1 /*
2  *	Copyright (c) 1999 RISS-Telecom Networking Center
3  *
4  *	Copyright (c) 1993 The CAD lab of the
5  *	Novosibirsk Institute of Broadcasting and Telecommunication
6  *
7  *	BPFT $Id: main.c,v 1.9 2004/05/05 18:06:51 stas_degteff Exp $
8  *
9  *
10  * Redistribution and use in source forms, with and without modification,
11  * are permitted provided that this entire comment appears intact.
12  * Redistribution in binary form may occur without any restrictions.
13  *
14  * THIS SOFTWARE IS PROVIDED ``AS IS'' WITHOUT ANY WARRANTIES OF ANY KIND.
15  */
16 
17 /*	main.c - tcp/udp data traffic collector daemon	*/
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <signal.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <syslog.h>
25 #include <paths.h>
26 #include <errno.h>
27 #include <sys/types.h>
28 #include <sys/time.h>
29 #include <sys/wait.h>
30 #ifndef __linux__
31 #include <sys/syslimits.h>
32 #endif
33 #include <sys/stat.h>
34 #include <netinet/in.h>
35 #include <net/bpf.h>
36 #include <pcap.h>
37 
38 #include "../include/interface.h"
39 #include "../include/pathnames.h"
40 #include "../include/traffic.h"
41 #include "../include/addrtoname.h"
42 #include "trafd.h"
43 
44 #define USE_EXIT 0	/* Use exit() function or use return operator
45 			   in the 'main' function */
46 
47 /*
48  * Command line switches.
49  * Left - default value, right - action, if switch has been used.
50  */
51 int dflag = 0;	/* dump to compiled packet-matching code to stdout and stop */
52 int Oflag = 1;	/* don't run the packet-matching code optimizer if have bug */
53 int pflag = 0;	/* don't put the interface into promiscuous mode */
54 int rflag = 0;	/* attempt to resume data from safe file if exist */
55 int nflag = 0;
56 int Nflag = 0;
57 int fflag = 0;
58 int Xflag = 1;	/* collect 'extended' traffic info (hosts-protocol-ports) */
59 
60 /* Global interrupts flags */
61 int flag_hup;	/* SIGHUP - drop collected data to tempfile */
62 int flag_int;	/* SIGINT - append collected data to savefile */
63 int flag_usr1;
64 
65 /* Global variables */
66 char *program_name;	/* myself */
67 char *device_name = 0;	/* interface name */
68 char file_pid[PATH_MAX];	/* pid file */
69 char file_dump[PATH_MAX];	/* dump file */
70 char file_save[PATH_MAX];	/* save file */
71 char file_tmp[PATH_MAX];	/* temp file */
72 char file_fifo[PATH_MAX];	/* fifo file */
73 unsigned long minsize=MIN_SIZE_TO_SAVE;
74 
75 /* Block of the interrupt drivers */
76 static void cleanup();
77 static void onhup();
78 static void onint();
79 static void onusr1();
80 static void onusr2();
81 static void onalarm();
82 static void onchld();
83 
84 int is_symlink(const char*);
85 void check_stats();
86 char *version();
87 
88 /* Length of saved portion of packet */
89 int snaplen = DEFAULT_SNAPLEN;
90 
91 /*static*/ pcap_t *pd;
92 
93 int
main(argc,argv)94 main(argc, argv)
95 	int argc;
96 	char **argv;
97 {
98 	struct bpf_program *parse();
99 	void bpf_dump(), usage(), printver(), check_stats();
100 
101 	register int op, cnt = -1, i;
102 	struct bpf_program fcode;
103 	register char *cp, *infile = NULL, *cmdbuf, *DevFileName = NULL;
104 	FILE *fd;
105 	pcap_handler printer;
106 	u_char *pcap_userdata;
107 	char ebuf[PCAP_ERRBUF_SIZE];
108 	extern char *optarg;
109 	extern int optind, opterr;
110 	bpf_u_int32 localnet, netmask;
111 
112 	if ((cp = strrchr(argv[0], '/')) != NULL)
113 			program_name = cp + 1;
114 	else
115 			program_name = argv[0];
116 
117 	opterr = 0;
118 	while ((op = getopt(argc, argv, "c:df:F:i:OprVXm:")) != EOF)
119 		switch (op) {
120 		case 'c':
121 			cnt = atoi(optarg);
122 			break;
123 		case 'd':
124 			++dflag;
125 			break;
126 		case 'f':
127 			DevFileName = optarg;
128 			break;
129 		case 'F':
130 			infile = optarg;
131 			break;
132 		case 'i':
133 			device_name = optarg;
134 			break;
135 		case 'm':
136 			minsize = strtoul(optarg, NULL,10);
137 			break;
138 		case 'O':
139 			Oflag = 0;
140 			break;
141 		case 'p':
142 			++pflag;
143 			break;
144 		case 'r':
145 			++rflag;
146 			break;
147 		case 'X':
148 			Xflag=0;
149 			break;
150 		case 'V':
151 			printver();
152 			return 0;
153 			break;
154 		default:
155 			usage();
156 		}
157 
158 	if (access(_PATH_VARRUN, R_OK|W_OK|X_OK) < 0) {  /* ITS4: ignore */
159 		snprintf(file_pid, PATH_MAX,"%s: access to %s", program_name,
160 			_PATH_VARRUN);
161 		perror(file_pid);
162 		exit(1);
163 	}
164 	if (access(PATH_TOSAVE, R_OK|W_OK|X_OK) < 0) {   /* ITS4: ignore */
165 		snprintf(file_save, PATH_MAX, "%s: access to %s", program_name,
166 			PATH_TOSAVE);
167 		perror(file_save);
168 		exit(1);
169 	}
170 	if (device_name == 0)
171 		if ((device_name = getenv("IFF_LISTEN")) == NULL)
172 			if ((device_name = pcap_lookupdev(ebuf)) == 0)
173 				error("%s", ebuf);
174 	if (DevFileName == NULL)
175 		DevFileName = device_name;
176 
177 	/* Attach bpf interface to network interface */
178 	pd = pcap_open_live(device_name, snaplen, !pflag, 1000, ebuf);
179 	if (pd == NULL)
180 			error("%s", ebuf);
181 
182 	i = pcap_snapshot(pd);
183 	if (snaplen < i) {
184 		warning("snaplen raised from %d to %d", snaplen, i);
185 		snaplen = i;
186 	}
187 
188 	if (pcap_lookupnet(device_name, &localnet, &netmask, ebuf) < 0) {
189 		localnet = 0;
190 		netmask = 0;
191 		warning("%s", ebuf);
192 	}
193 
194 	if (infile!=NULL)
195 		cmdbuf = read_infile(infile);
196 	else
197 		cmdbuf = copy_argv(&argv[optind]);
198 
199 	if(pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) < 0)
200 			error("%s", pcap_geterr(pd));
201 
202 	if (dflag) {
203 		bpf_dump(&fcode, dflag);
204 #if USE_EXIT
205 		exit(0);
206 #else
207 		return 0;
208 #endif
209 	}
210 	init_addrtoname(localnet, netmask);
211 
212 	/* Catch signals */
213 	flag_int = flag_hup = flag_usr1 = 0;
214 	(void)signal(SIGTERM, cleanup);
215 	(void)signal(SIGQUIT, cleanup);
216 	(void)signal(SIGPIPE, SIG_IGN);
217 	(void)signal(SIGINT,  onint);
218 	(void)signal(SIGHUP,  onhup);
219 	(void)signal(SIGUSR1, onusr1);
220 	(void)signal(SIGUSR2, onusr2);
221 	(void)signal(SIGALRM, onalarm);
222 	(void)signal(SIGCHLD, onchld);
223 
224 	/* Initialize file names */
225 	snprintf(file_pid, PATH_MAX, "%s%s.%s",  _PATH_VARRUN, program_name, device_name);
226 	snprintf(file_dump, PATH_MAX, "%s%s.%s",  _PATH_VARTMP, program_name, DevFileName);
227 	snprintf(file_save, PATH_MAX, "%s%s.%s", PATH_TOSAVE, program_name, DevFileName);
228 	snprintf(file_tmp, PATH_MAX, "%stmp.%s", PATH_TOSAVE, DevFileName);
229 	snprintf(file_fifo, PATH_MAX, "%s%s.%s", _PATH_TMP,    program_name, device_name);
230 
231 	/* Check for symlinks */
232 	if( access(file_dump,F_OK) ){
233 		/* create file & set access mask */
234 /*		fd = fopen(file_dump,"w");
235 		fchmod(fd,TMP_UMASK);
236 		fclose(fd);
237 */	}
238 	else if( is_symlink(file_dump) )		/*ITS4: ignore*/
239 		error("%s is symlink", file_dump);
240 	if( !access(file_save,F_OK) && is_symlink(file_save) )	/*ITS4: ignore*/
241 		error("%s is symlink", file_save);
242 	if( !access(file_fifo,F_OK) && is_symlink(file_fifo) )		/*ITS4: ignore*/
243 		error("%s is symlink", file_fifo);
244 
245 	/* Todo: Need check for old pidfile */
246 	if (access(file_pid, F_OK) == 0)  /* ITS4: ignore */
247 		error("unexpected pidfile, probable interface '%s' already listen",
248 		      device_name);
249 
250 	check_stats();
251 
252 	/* Jump to background */
253 	daemon(1, 0);
254 	if ((fd = fopen(file_pid, "w")) == NULL)	/* ITS4: ignore */
255 #if USE_EXIT
256 		exit(1);
257 #else
258 		return 1;
259 #endif
260 
261 	fprintf(fd,"%d\n", getpid());	/* ITS4: ignore */
262 	fclose(fd);
263 
264 /*	openlog(program_name, LOG_PID|LOG_CONS, LOG_DAEMON);*/
265 	openlog(program_name, LOG_PID|LOG_CONS, SYSLOG_FACILITY);	/*ITS4: ignore*/
266 	syslog(LOG_NOTICE, "(%s) traffic collector started", device_name);	/* ITS4: ignore */
267 
268 	if (pcap_setfilter(pd, &fcode) < 0)
269 		error("%s", pcap_geterr(pd));
270 
271 	printer = lookup_printer(pcap_datalink(pd));
272 	pcap_userdata = 0;
273 	/* Start read from bpf */
274 	if (!traf_init(rflag)) {
275 		if (pcap_loop(pd, cnt, printer, pcap_userdata))
276 			/* Clear exit */
277 			cleanup();
278 	} else
279 		errno = ENOMEM;
280 
281 	pcap_close(pd);
282 	/* Abnormal termination */
283 	(void)syslog(LOG_ERR, "(%s) traffic collector aborted: %m",	/* ITS4: ignore */
284 		     device_name);
285 #if USE_EXIT
286 	exit(1);
287 #endif
288 	return 1;
289 }
290 
291 /* make a clean exit on interrupts */
292 static void
cleanup()293 cleanup()
294 {
295 
296 	struct pcap_stat stat;
297 
298 	traf_save(file_dump, "w");
299 	if (!pcap_stats(pd, &stat))
300 		(void)syslog(LOG_INFO, "(%s) packets: received %d, dropped %d",	/* ITS4: ignore */
301 			     device_name, stat.ps_recv, stat.ps_drop);
302 	pcap_close(pd);
303 	unlink(file_pid);	/* ITS4: ignore */
304 	pd = NULL;
305 	(void)syslog(LOG_NOTICE, "(%s) traffic collector stopped", device_name); /* ITS4: ignore */
306 	exit(0);
307 }
308 
309 #define	WAIT_ACTION	5
310 
311 static void
onhup()312 onhup()
313 {
314 	alarm(WAIT_ACTION);
315 	flag_hup++;
316 }
317 
318 static void
onint()319 onint()
320 {
321 	alarm(WAIT_ACTION);
322 	flag_int++;
323 }
324 
325 static void
onusr1()326 onusr1()
327 {
328 	flag_usr1++;
329 }
330 
331 static void
onusr2()332 onusr2()
333 {
334 	flag_usr1 = 0;
335 	traf_pipe();
336 }
337 
338 static void
onalarm()339 onalarm()
340 {
341 	alarm(0);
342 	if (flag_hup) {
343 		flag_hup = 0;
344 		traf_save(file_dump, "w");
345 	}
346 	if (flag_int) {
347 		flag_int = 0;
348 		traf_save(file_save, "a");
349 		traf_clear();
350 	}
351 }
352 
353 static void
onchld()354 onchld()
355 {
356 	if (wait(0) == -1)
357 		(void)syslog(LOG_WARNING, "(%s) wait: %m", device_name); /* ITS4: ignore */
358 }
359 
360 
361 void
printver()362 printver()
363 {
364 
365 	fprintf(stderr,	/* ITS4: ignore */
366 		"trafd v%s - tcp/udp data traffic collector daemon (BPFT project)\n\n",
367 		version() );
368 }
369 
370 void
usage()371 usage()
372 {
373 	printver();
374 	fprintf(stderr,	/* ITS4: ignore */
375 "Usage: %s [-dOprVX] [-c count] [-i iface] [-f ext] [-m minsize] [-F file | expr]\n", program_name);
376 	exit(-1);
377 }
378 
379 
380 /* Check status of the interface and print warnings if needed */
check_stats()381 void check_stats()
382 {	struct pcap_stat pcap_status={0,0,0};
383 
384 	if( pcap_stats(pd, &pcap_status) )
385 		warning("(%s) packets dropping checks not avaiable (pcap_stats(): %s)",
386 			     device_name, pcap_geterr(pd) );
387 	else if( pcap_status.ps_ifdrop )
388 		warning("(%d) drops by interface %s not yet supported, data loss is possible",
389 			     pcap_status.ps_ifdrop, device_name );
390 }
391 
is_symlink(path)392 int is_symlink(path)
393 	const char *path;
394 {
395 	struct stat st,lst;
396 
397 	lstat( path, &lst );	/*ITS4: ignore*/
398 	stat( path, &st );	/*ITS4: ignore*/
399 	return ( lst.st_dev!=st.st_dev || lst.st_ino!=st.st_ino );
400 }
401