/*
 * Copyright (c) 2000, Ping Pan (Columbia University/Bell Labs)
 *      All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * The author acknowledges Bell Labs for providing time. Please forward 
 * bug fixes, enhancements and questions to pingpan@cs.columbia.edu.
 */

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <syslog.h>
#include <signal.h>
#include <paths.h>
#include <fcntl.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/uio.h>
#include <net/if_dl.h>
#define _KERNEL
#define KERNEL
#include <netinet/in.h>
#include <netinet/in_systm.h> 
#include <netinet/ip.h>

extern int optind;
extern int opterr;
extern char *optarg;

void	sock_init(int);
void	sockwriter(int);
int	reg_sockread(int, int);	
void	logbad(int dump, char *p, ...);
void	usage();
void	stopit(int);
void	finish();
void	print_raw(u_char *, int, int);
void	print_rr(int, int);
void	print_ts(int, int);
void	print_sec(int, int);
void	print_lsrr(int, int);
void	print_ssrr(int, int);
void	print_ra(int, int);
void	print_rsvp(int, int);
void	print_rtcp(int, int);

/* the type of message that we are trying to capture:
 * the first 8-bit is the actual option value; the second 8-bit is 
 * option-specific.
 */

#define IPOPT_ANY	-1
#define IPOPT_RRANY	(IPOPT_RR << 8)
#define IPOPT_TSANY	(IPOPT_TS << 8)
#define IPOPT_SECANY	(IPOPT_SECURITY << 8)
#define IPOPT_LSRRANY	(IPOPT_LSRR << 8)
#define IPOPT_SSRRANY	(IPOPT_SSRR << 8)
#define IPOPT_RAANY	(IPOPT_RA << 8)
#define IPOPT_RARSVP	((IPOPT_RA << 8) | IPOPT_RECVRSVP)
#define IPOPT_RARTCP	((IPOPT_RA << 8) | IPOPT_RECVRTCP)

#define DEFAULT_SNAPLEN	128		/* default snap packet length */

/* max. control info size... plus some safe guard space 
 */
#define MAXCTRLSIZE						\
	(sizeof(struct cmsghdr) + sizeof(struct sockaddr_dl) +	\
	sizeof(struct cmsghdr) + sizeof(int) + 32)

#define MAXPKTSIZE	9126		/* don't ask */

#define CMSG_IFINDEX(cmsg) 				\
	(((struct sockaddr_dl*)(cmsg + 1))->sdl_index)	\

#define CMSG_LOCAL(cmsg) 	(*(int *)(cmsg + 1))

struct sockread {
	int opt;			/* option type */
	int sock;			/* socket num */
	void (*readfunc)(int, int);	/* printer func */
};

static struct sockread sockreader[] = {
	{ IPOPT_RRANY,		-1,	print_rr, },
	{ IPOPT_TSANY,		-1,	print_ts, },
	{ IPOPT_SECANY,		-1,	print_sec, },
	{ IPOPT_LSRRANY,	-1,	print_lsrr, },
	{ IPOPT_SSRRANY,	-1,	print_ssrr, },
	{ IPOPT_RAANY,		-1,	print_ra, },
	{ IPOPT_RARSVP,		-1,	print_rsvp, },
	{ IPOPT_RARTCP,		-1,	print_rtcp, },
};

#define sizeof_sockread_tab \
	(sizeof (sockreader) / sizeof sockreader[0])

fd_set opt_fdset;			/* option fd's */
int sock_max;				/* max. sockets per process */
int eflag;				/* error flag */
int iflag;				/* include-interface-index flag */
int xflag;				/* print-in-hex flag */
int packetype;				/* capture pkt type */
int snaplen;				/* snap length */
int tx_sock;				/* transmit socket */
volatile sig_atomic_t finish_up;	/* nonzero if told to finish up */
u_char *packet;				/* pointer to the recv pkt */

int ncapture;				/* number of captured packets */
int ntransmit;				/* number of transmitted packets */
int nra;				/* number of router-alert pkts */
int nrsvp;				/* number of router-alert rsvp pkts */
int nrtcp;				/* number of router-alert rtcp pkts */
int nrr;				/* number of route-record pkts */
int nts;				/* number of time-stamp pkts */
int nsec;				/* number of security pkts */
int nlsrr;				/* number of loose-source-routing */
int nssrr;				/* number of strict-source-routing */

char *program_name;			/* what do you think ? */


int
main(int argc, char *argv[])
{
	register int uid, n, cnt;
	struct sigaction si_sa;
	struct msghdr msg;
	struct sockaddr_in from;
	struct iovec iov;
	struct cmsghdr *cmsg;
	char *ctrl, *cp;
	int on=1;

	setlinebuf(stdout);

	setuid(getuid());
	if ((uid = getuid())) {
		perror("need to be a superuser");
		exit(1);
	} 

	/* init. all parameters */
	packetype = IPOPT_ANY;
	snaplen = DEFAULT_SNAPLEN;
	eflag = 0;
	iflag = 0;
	xflag = 0;
	cnt = -1;

	ncapture = 0;
	nra = 0;
	nrsvp = 0;
	nrtcp = 0;
	nrr = 0;
	nts = 0;
	nsec = 0;
	nlsrr = 0;
	nssrr = 0;

	if ((cp = strrchr(argv[0], '/')) != NULL)
		program_name = cp + 1;
	else
		program_name = argv[0];

	while ((n = getopt(argc, argv, "c:ixT:s:")) != EOF) {
		switch (n) {

		case 'c':
			cnt = atoi(optarg);
			if (cnt < 0) {
				fprintf(stderr, "bad packet count %s", optarg);
				eflag++;
			}
			break;

		case 'i':
			iflag++;
			break;

		case 'x':
			xflag++;
			break;

		case 'T':
			if (strcasecmp(optarg, "rr") == 0)
				packetype = IPOPT_RRANY;
			else if (strcasecmp(optarg, "ts") == 0)
				packetype = IPOPT_TSANY;
			else if (strcasecmp(optarg, "sec") == 0)
				packetype = IPOPT_SECANY;
			else if (strcasecmp(optarg, "lsrr") == 0)
				packetype = IPOPT_LSRRANY;
			else if (strcasecmp(optarg, "ssrr") == 0)
				packetype = IPOPT_SSRRANY;
			else if (strcasecmp(optarg, "ra") == 0)
				packetype = IPOPT_RAANY;
			else if (strcasecmp(optarg, "rsvp") == 0)
				packetype = IPOPT_RARSVP;
			else if (strcasecmp(optarg, "rtcp") == 0)
				packetype = IPOPT_RARTCP;
			else {
				fprintf(stderr, "invalid type %s", optarg);
				eflag++;
			}
			break;

		case 's':
			snaplen = atoi(optarg);
			if (snaplen <= 0) {
				fprintf(stderr, "bad snap length %s", optarg);
				eflag++;
			}
			break;

		default:
			usage();
		}
	}

	if (eflag)
		usage();

	/* socket and buffer setup:
	 */
	sock_max = getdtablesize();
	FD_ZERO(&opt_fdset);

	if (packetype == IPOPT_ANY)  {
		sock_init(IPOPT_RRANY);
		sock_init(IPOPT_TSANY);
		sock_init(IPOPT_SECANY);
		sock_init(IPOPT_LSRRANY);
		sock_init(IPOPT_SSRRANY);
		sock_init(IPOPT_RAANY);
		sock_init(IPOPT_RARSVP);
		sock_init(IPOPT_RARTCP);
	}
	else
		sock_init(packetype);

	if (!(packet = (char *)malloc(MAXPKTSIZE)))
		logbad(0, "cannot get buff space");

	/* setup tx. socket */
	if ((tx_sock = socket(PF_INET, SOCK_RAW, 0)) < 0)
		logbad(0, "tx_sock: socket() < 0");

	if (setsockopt(tx_sock, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0)
		logbad(0, "setsockopt() for IP_HDRINCL < 0");

	/* signal setup */
	sigemptyset(&si_sa.sa_mask);
	si_sa.sa_flags = 0;

	si_sa.sa_handler = stopit;
	if (sigaction(SIGINT, &si_sa, 0) == -1)
		logbad(0, "sigaction SIGINT");

	si_sa.sa_handler = stopit;
	if (sigaction(SIGTERM, &si_sa, 0) == -1)
		logbad(0, "sigaction SIGTERM");

	/* read from sockets: 
	 */
	if (!(ctrl = (char *)malloc(MAXCTRLSIZE)))
		logbad(0, "cannot get control data space");

	bzero(&msg, sizeof(msg));
	msg.msg_name = (caddr_t)&from;
	msg.msg_namelen = sizeof(from);
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	msg.msg_control = ctrl;
	msg.msg_controllen = MAXCTRLSIZE;

	iov.iov_base = (char *)packet;
	iov.iov_len = MAXPKTSIZE;

	while (!finish_up) {
		struct timeval timeout;
		fd_set curr_fdset;
		int s, cc, if_index, local;

		if_index = -1;
		local = 0;
		timeout.tv_sec = 1;
		timeout.tv_usec = 0;
		curr_fdset = opt_fdset;

		n = select(sock_max, &curr_fdset, 0, 0, &timeout);
		if (n <= 0) {
			if (n < 0 && errno != EINTR && errno != EAGAIN)
				logbad(0, "bad select");

			continue;
		}

		for (n=0; n < (int)sizeof_sockread_tab; n++) {
			if ((s = sockreader[n].sock) == -1)
				continue;

			if (!(FD_ISSET(s, &curr_fdset)))
				continue;

			for (;;) {
				if ((cc = recvmsg(s, &msg, 0)) < 0)
					break;

				if (cc > MAXPKTSIZE) {
					fprintf(stderr, "pkt size %d", cc);
					continue;
				}

				for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; 
				   cmsg = CMSG_NXTHDR(&msg, cmsg)) {

					if (cmsg->cmsg_type == IP_RECVIF)
						if_index = CMSG_IFINDEX(cmsg);
					if (cmsg->cmsg_type == IPOPT_RECVLOCAL)
						local = CMSG_LOCAL(cmsg);
				}

				ncapture++;

				if ((cnt > 0) && (ncapture > cnt))
					finish_up = 1;

				if (xflag)
					print_raw(packet, cc, if_index);
				else
					sockreader[n].readfunc(cc, if_index);

				/* continue to forward the message */
				if (!local) {
					sockwriter(cc);
					local = 0;
				}
			}
		}
	}
	finish();

	exit(0);
}


/* init a socket for option type <packetype>: create and set socket;
 * register the socket to the reader table.
 */

void
sock_init(int packetype)
{
	int sock, on=1;

	if ((sock = socket(PF_IPOPTION, SOCK_RAW, (packetype >> 8))) < 0) {
		fprintf(stderr, "Need to install PF_IPOPTION kernel option\n");
		logbad(0, "PF_IPOPTION error: socket() < 0");
	}

	if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1)
		logbad(0, "fcntl failed to O_NONBLOCK: %s", errno);

	if (packetype == IPOPT_RARSVP) {
		if (setsockopt(sock, IPPROTO_IP, IPOPT_RECVRSVP, 
		  &on, sizeof(on)) < 0)
			logbad(0, "bad setsockopt() on RSVP");
	}
	else if (packetype == IPOPT_RARTCP) {
		if (setsockopt(sock, IPPROTO_IP, IPOPT_RECVRTCP, 
		  &on, sizeof(on)) < 0)
			logbad(0, "bad setsockopt() on RTCP");
	}
	else if (packetype == IPOPT_RAANY) {
		if (setsockopt(sock, IPPROTO_IP, IPOPT_RECVRA, 
		  &on, sizeof(on)) < 0)
			logbad(0, "bad setsockopt() on RECVRA");
	}

	if (iflag) {		/* include the ingrss if-index */
		if (setsockopt(sock, IPPROTO_IP, IP_RECVIF, 
		  &on, sizeof(on)) < 0)
			logbad(0, "bad setsockopt() on RECVIF < 0");
	}

	/* add the socket number into the readsock table */
	if (!reg_sockread(sock, packetype))
		logbad(0, "cannot register the socket");

	FD_SET(sock, &opt_fdset);

	return;
}


/* inject the captued packet back to the network
 * Note: IPOPTION sockets are in front of INET sockets in the ip_input().
 *  So if the captured packet is doing or has the same source and 
 *  destination addresses for some reason, the same packet will be captued 
 *  and transmitted forever. So we'd better do some checking before it hits
 *  the fan. 
 */

void
sockwriter(int len)
{
	struct sockaddr whereto;
	struct sockaddr_in *to;
	struct ip *ip;
	struct in_addr *dst, *src;

	ip = (struct ip *)packet;
	src = (struct in_addr *)&(ip->ip_src);
	dst = (struct in_addr *)&(ip->ip_dst);

	/* loopback already ? */
	if (src->s_addr == dst->s_addr)
		return;

	ntransmit++;

	to = (struct sockaddr_in *)&whereto;
	to->sin_len = sizeof *to;
	to->sin_family = PF_INET;
	to->sin_addr.s_addr = dst->s_addr;

	if (sendto(tx_sock, packet, len, 0, &whereto, sizeof(whereto)) < 0)
		logbad(0, "cannot transmit a captured message, oops!");

	return;
}


/* insert the socket number 'sock' that is meant for option 'packetype'
 * into lookup table, sockreader[].
 */

int
reg_sockread(int sock, int packetype)
{
	int i;

	for (i=0; i < (int)sizeof_sockread_tab; i++) {
		if (packetype == sockreader[i].opt) {
			sockreader[i].sock = sock;
			return -1;
		}
	}
	return 0;
}


/* display the bad news on console and quit
 */
void
logbad(int dump, char *p, ...)
{
	va_list args;

	(void)fprintf(stderr, "%s: ", program_name);

	va_start(args, p);
	(void)vfprintf(stderr, p, args);
	va_end(args);

	if (*p) {
		p += strlen(p);
		if (p[-1] != 'n')
			(void)fputc('\n', stderr);
	}

	if (dump)
		abort();

	exit(-1);
}


void
stopit(int sig)
{
	finish_up = 1;
}


void
finish()
{
	(void)signal(SIGINT, SIG_IGN);
	(void)signal(SIGTERM, SIG_IGN);
	(void)putchar('\n');
	(void)fflush(stdout);

	printf("--- %s statistics ---\n", program_name);
	printf("%d received packets.\n", ncapture);
	printf("%d transmited packets.\n", ntransmit);
	printf("%d received Route Record packets.\n", nrr);
	printf("%d received Timestamp packets.\n", nts);
	printf("%d received Security packets.\n", nsec);
	printf("%d received Loose-Sourced Routing packets.\n", nlsrr);
	printf("%d received Struct-Sourced Routing packets.\n", nssrr);
	printf("%d received Router Alert packets.\n", nra);
	printf("%d received RSVP (raw mode) packets.\n", nrsvp);
	printf("%d received RTCP packets.\n", nrtcp);

	exit(0);
}


void
usage()
{
	fprintf(stderr, 
		"%s [-c count] [-ix] [-T type] [-s snaplen]\n", 
		program_name);

	exit(-1);
}


#define BYTES_PER_LINE	16

void
print_raw(u_char *msg, int len, int if_index)
{
	int i, j, line_cnt;

	if (len > snaplen)
		len = snaplen;

	if (if_index != -1)
		printf("\nReceived from if-index %d\n", if_index);

	line_cnt = (len / BYTES_PER_LINE) + (len % BYTES_PER_LINE ? 1 : 0);

	printf("\r\n");
	for (i=0; i < line_cnt; i++) {

		j = i * BYTES_PER_LINE;
		for (; ((j < (i+1) * BYTES_PER_LINE) && (j < len)); j++)
			printf("%02X:", msg[j]);

		printf("\r\n");
	}
}


/* my version of inet_aton */
char
*myinet_aton(struct in_addr addr)
{
	char *host;
	struct hostent *hp;
	
	host = inet_ntoa(addr);

	/* get from DNS */
	hp = gethostbyaddr((char *)&addr, sizeof(struct in_addr), AF_INET);
	if (hp == NULL)		/* unknown host */
		return host;

	if (hp->h_addrtype != AF_INET || hp->h_length != 4)
		return host;		/* bad name */

	return (hp->h_name);
}


void
print_ip_hdr(u_char *msg, int len)
{
	struct {
		char *name;
		int ptype;
	} ptype_map[] = {
		{ "TCP", IPPROTO_TCP },
		{ "UDP", IPPROTO_UDP },
		{ "ICMP", IPPROTO_ICMP },
		{ "IGMP", IPPROTO_IGRP },
		{ "RSVP", IPPROTO_RSVP },
	};
	struct ip *ip;
	int hlen, max, i;

	ip = (struct ip *)msg;
	hlen = ip->ip_hl * 4;
	max = sizeof(ptype_map) / sizeof(ptype_map[0]);

	if (hlen > len) {
		print_raw(msg, len, -1);
		return;
	}

	printf("\r\n");

	printf("IP: ");
	printf("[len %d] ", ip->ip_len);

	if (ip->ip_tos)
		printf("[tos 0x%x] ", (int)ip->ip_tos);

	printf("[ttl %d] ", (int)ip->ip_ttl);

	for (i=0; i < max; i++) {
		if (ip->ip_p == ptype_map[i].ptype) {
			printf("[%s] ", ptype_map[i].name);
			break;
		}
	}

	if (i == max)
		printf("[ptype %d] ", ip->ip_p);

	printf("%s -> ", myinet_aton(ip->ip_src));
	printf("%s", myinet_aton(ip->ip_dst));
	printf("\r\n");

	return;
}


void
print_rr(int len, int if_index)
{
	nrr++;

	if (len > snaplen)
		len = snaplen;

	if (if_index != -1)
		printf("\nIngress if-index: %d\n", if_index);

	print_ip_hdr(packet, len);

	printf("IP Record Route option");
	printf("\r\n");

	return;
}

void
print_ts(int len, int if_index)
{
	nts++;

	if (len > snaplen)
		len = snaplen;

	if (if_index != -1)
		printf("\nIngress if-index: %d\n", if_index);

	print_ip_hdr(packet, len);

	return;
}

void
print_sec(int len, int if_index)
{
	nsec++;

	if (len > snaplen)
		len = snaplen;

	if (if_index != -1)
		printf("\nIngress if-index: %d\n", if_index);

	print_ip_hdr(packet, len);

	return;
}

void
print_lsrr(int len, int if_index)
{
	nlsrr++;

	if (len > snaplen)
		len = snaplen;

	if (if_index != -1)
		printf("\nIngress if-index: %d\n", if_index);

	print_ip_hdr(packet, len);

	return;
}

void
print_ssrr(int len, int if_index)
{
	nssrr++;

	if (len > snaplen)
		len = snaplen;

	if (if_index != -1)
		printf("\nIngress if-index: %d\n", if_index);

	print_ip_hdr(packet, len);

	return;
}

void
print_ra(int len, int if_index)
{
	struct ip *ip;
	struct ip_routeralert *alert;

	ip = (struct ip *)packet;
	alert = (struct ip_routeralert *)(ip + 1);

	if (len > snaplen)
		len = snaplen;

	if (ntohs(alert->ipa_val) == IPOPT_RA_RTCP) {
		print_rtcp(len, if_index);
		return;
	}
	else if (ntohs(alert->ipa_val) == IPOPT_RA_EXAM && 
	  ip->ip_p == IPPROTO_RSVP) {
		print_rsvp(len, if_index);
		return;
	}

	nra++;
	if (if_index != -1)
		printf("\nReceiving interface index: %d\n", if_index);

	if(((ip->ip_hl * 4) - sizeof(struct ip)) != alert->ipa_len) {
		fprintf(stderr, "bad alert message length\n");
		return;
	}

	printf("IP Router-Alert: ");
	printf("[value %d] ", ntohs(alert->ipa_val));
	printf("\r\n");

	return;
}


struct rsvphdr {
	u_char	ver;
	u_char	type;
	u_short	chksum;
	u_char	ttl;
	u_char	rsvd;
	u_short	len;

	/*
	 * a blub number of objects
	 */
};


#define RSVP_PATH	1
#define RSVP_RESV	2
#define RSVP_PERR	3
#define RSVP_RERR	4
#define RSVP_PTEAR	5
#define RSVP_RTEAR	6
#define RSVP_RCONF	7


void
print_rsvp(int len, int if_index)
{
	struct rsvphdr	*rhdr;
	struct ip *ip;
	int hlen;

	nrsvp++;

	if (len > snaplen)
		len = snaplen;

	if (if_index != -1)
		printf("\nIngress if-index: %d\n", if_index);

	print_ip_hdr(packet, len);

	/* print RSVP header */
	printf("RSVP: ");
	ip = (struct ip *)packet;
	hlen = ip->ip_hl * 4;

	rhdr = (struct rsvphdr *)((char *)ip + hlen);
	switch (rhdr->type) {

		case RSVP_PATH:
			printf("[PATH] ");
			break;

		case RSVP_RESV:
			printf("[RESV] ");
			break;

		case RSVP_PERR:
			printf("[PATH ERR] ");
			break;

		case RSVP_RERR:
			printf("[RESV ERR] ");
			break;

		case RSVP_PTEAR:
			printf("[PATH TEAR] ");
			break;

		case RSVP_RTEAR:
			printf("[RESV TEAR] ");
			break;

		case RSVP_RCONF:
			printf("[RESV CONF] ");
			break;

		default:
			printf("[type %d] ", rhdr->type);
			break;
	}

	printf("[ttl %d] ", (int)rhdr->ttl);
	printf("[len %d] ", ntohs(rhdr->len));

	/* well... will add the detailed parsing stuff here later
	 * For impatient people, copy rsvp_map_packet() from the ISI
	 * RSVP release and display all the objects one by one,
	 */

	printf("\r\n");
	return;
}

void
print_rtcp(int len, int if_index)
{
	struct ip *ip;
	struct ip_routeralert *alert;

	nra++;
	nrtcp++;

	ip = (struct ip *)packet;
	alert = (struct ip_routeralert *)(ip + 1);

	if (len > snaplen)
		len = snaplen;

	if (if_index != -1)
		printf("\nIngress if-index: %d\n", if_index);

	print_ip_hdr(packet, len);

	printf("IP Router-Alert: [RTCP] ");

	if(((ip->ip_hl * 4) - sizeof(struct ip)) != alert->ipa_len) {
		fprintf(stderr, "bad alert message length\n");
		return;
	}

	if (ntohs(alert->ipa_val) != IPOPT_RA_RTCP) {
		fprintf(stderr, "not RTCP code, %d\n", alert->ipa_val);
		return;
	}

	printf("\r\n");

	return;
}
