1 /*
2  *    jnettop, network online traffic visualiser
3  *    Copyright (C) 2002-2006 Jakub Skopal
4  *
5  *    This program is free software; you can redistribute it and/or modify
6  *    it under the terms of the GNU General Public License as published by
7  *    the Free Software Foundation; either version 2 of the License, or
8  *    (at your option) any later version.
9  *
10  *    This program is distributed in the hope that it will be useful,
11  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *    GNU General Public License for more details.
14  *
15  *    You should have received a copy of the GNU General Public License
16  *    along with this program; if not, write to the Free Software
17  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  *    $Header: /cvsroot/jnettop/jnettop/jnettop.c,v 1.38 2006/04/12 07:47:01 merunka Exp $
20  *
21  */
22 
23 #include "jbase.h"
24 #include "jdevice.h"
25 #include "jcapture.h"
26 #include "jprocessor.h"
27 #include "jresolver.h"
28 #include "jresolv.h"
29 #include "jfilter.h"
30 #include "jutil.h"
31 #include "jconfig.h"
32 #include "jcursesdisplay.h"
33 #include "jtxtdisplay.h"
34 #include "juiadisplay.h"
35 #include "jnetdisplay.h"
36 
37 #define			DEBUGOUT_NONE	0
38 #define			DEBUGOUT_SYSLOG	1
39 #define			DEBUGOUT_FILE	2
40 int			debugOut = DEBUGOUT_NONE;
41 FILE *			debugFile = NULL;
42 
43 volatile int		threadCount;
44 
45 jbase_display *		currentDisplay;
46 
debug(int priority,const char * format,...)47 void debug(int priority, const char *format, ...) {
48 	static char buffer[32768];
49 	va_list ap;
50 	va_start(ap, format);
51 	vsprintf(buffer, format, ap);
52 	va_end(ap);
53 
54 	switch (debugOut) {
55 		case DEBUGOUT_FILE:
56 			fprintf(debugFile, "%d - %d, %s\n", getpid(), priority, buffer);
57 			fflush(debugFile);
58 			break;
59 #ifdef SUPPORT_SYSLOG
60 		case DEBUGOUT_SYSLOG:
61 			syslog(priority, "%d - %d, %s\n", getpid(), priority, buffer);
62 #endif
63 	}
64 }
65 
jbase_cb_DrawStatus(const gchar * msg)66 void jbase_cb_DrawStatus(const gchar *msg) {
67 	currentDisplay->drawstatus(msg);
68 }
69 
parseCommandLineAndConfig(int argc,char ** argv)70 void parseCommandLineAndConfig(int argc, char ** argv) {
71 	char * configFileName = NULL;
72 	char * selectRuleName = NULL;
73 	int a;
74 
75 	jconfig_Setup();
76 
77 	for (a=1; a<argc; a++) {
78 		if (!strcmp(argv[a], "-v") || !strcmp(argv[a], "--version")) {
79 			printf(PACKAGE_STRING "\nWritten by Jakub Skopal <j@kubs.cz>\n\nSee copyright in the COPYING file.\n");
80 			exit(0);
81 		}
82 		if (!strcmp(argv[a], "-h") || !strcmp(argv[a], "--help")) {
83 			printf(	"Usage: jnettop [-hv] [-i interface] [-d filename]\n"
84 				"\n"
85 				"    -h, --help             display this help message\n"
86 				"    -v, --version          display version information\n\n"
87 				"    -b, --bit-units        show BPS in bits per second, not bytes per second\n"
88 				"    -c, --content-filter   disable content filtering\n"
89 				"    -d, --debug filename   write debug information into file (or syslog)\n"
90 				"    --display type         type of display (curses, text, uia)\n"
91 				"    -f, --config-file name reads configuration from file. defaults to ~/.jnettop\n"
92 				"    --format format        list of fields to list in text output\n"
93 				"    -i, --interface name   capture packets on specified interface\n"
94 				"    --local-aggr arg       set local aggregation to none/host/port\n"
95 				"    -n, --no-resolver      disable resolving of addresses\n"
96 				"    -p, --promiscuous      enable promisc mode on the devices\n"
97 				"    --remote-aggr arg      set remote aggregation to none/host/port\n"
98 				"    -s, --select-rule rule selects one of the rules defined in config file\n"
99 				"                           by it's name\n"
100 				"    -t, --timeout sec      timeout in seconds after which jnettop ends (text display)\n"
101 				"    -x, --filter rule      allows for specification of custom filtering rule\n"
102 				"                           this follows tcpdump(1) syntax. don't forget to\n"
103 				"                           enclose the filter into quotes when running from shell\n"
104 				"\n"
105 				"Report bugs to <j@kubs.cz>\n"
106 				"\n"
107 				"    Format variable can be CSV (comma separated values), TSV (tab separated values)\n"
108 				"    or completelly custom format string, where the following identifiers are subst-\n"
109 				"    ituted when surrounded by '$':\n"
110 				"       src, srcname, srcport, srcbytes, srcpackets, srcbps, srcpps,\n"
111 				"       dst, dstname, dstport, dstbytes, dstpackets, dstbps, dstpps,\n"
112 				"       proto, totalbytes, totalpackets, totalbps, totalpps, filterdata\n"
113 				"\n"
114 				"    example:\n"
115 				"       jnettop --display text -t 5 --format CSV\n"
116 				"       jnettop --display text -t 5 --format '$srcname$,$srcport$,$dstname$,$dstport$,$totalbps$'\n"
117 				"\n"
118 			);
119 			exit(0);
120 		}
121 		if (!strcmp(argv[a], "-c") || !strcmp(argv[a], "--content-filter")) {
122 			jconfig_Settings.onoffContentFiltering = FALSE;
123 			continue;
124 		}
125 		if (!strcmp(argv[a], "--display")) {
126 			if (a+1>=argc) {
127 				fprintf(stderr, "%s switch requires argument\n", argv[a]);
128 				exit(255);
129 			}
130 			++a;
131 			if (jcursesdisplay_Functions.supported && !strcmp(argv[a], "curses")) {
132 				currentDisplay = &jcursesdisplay_Functions;
133 			} else if (jtxtdisplay_Functions.supported && !strcmp(argv[a], "text")) {
134 				currentDisplay = &jtxtdisplay_Functions;
135 			} else if (jnetdisplay_Functions.supported && !strcmp(argv[a], "jnet")) {
136 				currentDisplay = &jnetdisplay_Functions;
137 			} else if (juiadisplay_Functions.supported && !strcmp(argv[a], "uia")) {
138 				currentDisplay = &juiadisplay_Functions;
139 			} else {
140 				fprintf(stderr, "display type %s is not supported.\n", argv[a]);
141 				exit(255);
142 			}
143 			continue;
144 		}
145 		if (!strcmp(argv[a], "-i") || !strcmp(argv[a], "--interface")) {
146 			if (a+1>=argc) {
147 				fprintf(stderr, "%s switch requires argument\n", argv[a]);
148 				exit(255);
149 			}
150 			jconfig_Settings.deviceName = argv[++a];
151 			continue;
152 		}
153 		if (!strcmp(argv[a], "-s") || !strcmp(argv[a], "--select-rule")) {
154 			if (a+1>=argc) {
155 				fprintf(stderr, "%s switch requires argument\n", argv[a]);
156 				exit(255);
157 			}
158 			selectRuleName = argv[++a];
159 			continue;
160 		}
161 		if (!strcmp(argv[a], "-d") || !strcmp(argv[a], "--debug")) {
162 			if (a+1>=argc) {
163 				fprintf(stderr, "%s switch requires filename to debug to as an argument\n", argv[a]);
164 				exit(255);
165 			}
166 			++a;
167 			if (!strcmp(argv[a], "syslog")) {
168 #ifdef SUPPORT_SYSLOG
169 				debugOut = DEBUGOUT_SYSLOG;
170 #else
171 				fprintf(stderr, "Syslog output not enabled in compilation\n");
172 				exit(255);
173 #endif
174 			} else {
175 				debugFile = fopen(argv[a], "w");
176 				if (!debugFile) {
177 					perror("Could not open debug file");
178 					exit(255);
179 				}
180 				debugOut = DEBUGOUT_FILE;
181 			}
182 			continue;
183 		}
184 		if (!strcmp(argv[a], "-f") || !strcmp(argv[a], "--config-file")) {
185 			if (a+1>=argc) {
186 				fprintf(stderr, "%s switch required argument\n", argv[a]);
187 				exit(255);
188 			}
189 			configFileName = argv[++a];
190 			continue;
191 		}
192 		if (!strcmp(argv[a], "-x") || !strcmp(argv[a], "--filter")) {
193 			const char *ret;
194 			char *commandLineRule;
195 			if (a+1>=argc) {
196 				fprintf(stderr, "%s switch requires argument\n", argv[a]);
197 				exit(255);
198 			}
199 			commandLineRule = argv[++a];
200 			ret = jutil_ValidateBPFFilter(commandLineRule);
201 			if (ret) {
202 				fprintf(stderr, "Error compiling rule: %s\n", ret);
203 				exit(255);
204 			}
205 			JCONFIG_BPFFILTERS_SETSELECTEDFILTER(JCONFIG_BPFFILTERS_LEN);
206 			jconfig_AddBpfFilter("<commandline>", commandLineRule);
207 			continue;
208 		}
209 		if (!strcmp(argv[a], "-p") || !strcmp(argv[a], "--promiscuous")) {
210 			jconfig_Settings.onoffPromisc = TRUE;
211 			continue;
212 		}
213 		if (!strcmp(argv[a], "-n") || !strcmp(argv[a], "--no-resolve")) {
214 			jconfig_Settings.onoffResolver = FALSE;
215 			continue;
216 		}
217 		if (!strcmp(argv[a], "--local-aggr")) {
218 			if (a+1>=argc || (jconfig_Settings.localAggregation = jutil_ParseAggregation(argv[++a]))==-1) {
219 				fprintf(stderr, "%s switch requires none, host or port as an argument\n", argv[a]);
220 				exit(255);
221 			}
222 			continue;
223 		}
224 		if (!strcmp(argv[a], "--remote-aggr")) {
225 			if (a+1>=argc || (jconfig_Settings.remoteAggregation = jutil_ParseAggregation(argv[++a]))==-1) {
226 				fprintf(stderr, "%s switch requires none, host or port as an argument\n", argv[a]);
227 				exit(255);
228 			}
229 			continue;
230 		}
231 		{
232 			int consumed = currentDisplay->processargument((const gchar **) argv+a, argc-a);
233 			if (consumed) {
234 				a += consumed - 1;
235 				continue;
236 			}
237 		}
238 		fprintf(stderr, "Unknown argument: %s\n", argv[a]);
239 		exit(255);
240 	}
241 
242 	if (!jconfig_ParseFile(configFileName)) {
243 		exit(255);
244 	}
245 
246 	jconfig_SetDefaults();
247 
248 	if (selectRuleName) {
249 		int i = jconfig_FindBpfFilterByName(selectRuleName);
250 		if (i == -1) {
251 			fprintf(stderr, "Rule '%s' specified on the command line is not defined.\n", selectRuleName);
252 			exit(255);
253 		}
254 		JCONFIG_BPFFILTERS_SETSELECTEDFILTER(i);
255 	}
256 }
257 
initializeDevices()258 void initializeDevices() {
259 	if (!jdevice_LookupDevices()) {
260 		exit(255);
261 	}
262 
263 	if (!jdevice_DevicesCount) {
264 			if (!jconfig_Settings.deviceName) {
265 				fprintf(stderr, "Autodiscovery found no devices. Specify device you want to watch with -i parameter\n");
266 				exit(255);
267 			}
268 			if (!(jconfig_Settings.device = jdevice_CreateSingleDevice(jconfig_Settings.deviceName))) {
269 				exit(255);
270 			}
271 	} else if (jconfig_Settings.deviceName) {
272 		jconfig_SelectDevice(jconfig_Settings.deviceName);
273 	}
274 
275 	if (!jconfig_Settings.device) {
276 		jconfig_Settings.deviceName = jdevice_Devices[0].name;
277 		jconfig_Settings.device = jdevice_Devices;
278 	}
279 
280 	if (!jdevice_CheckDevices()) {
281 		exit(255);
282 	}
283 }
284 
main(int argc,char ** argv)285 int main(int argc, char ** argv) {
286 	g_thread_init(NULL);
287 
288 	jcapture_Setup();
289 	jprocessor_Setup();
290 	jresolver_Setup();
291 
292 	if (jcursesdisplay_Functions.supported)
293 		currentDisplay = &jcursesdisplay_Functions;
294 	else
295 		currentDisplay = &jtxtdisplay_Functions;
296 
297 	parseCommandLineAndConfig(argc, argv);
298 
299 	if (!currentDisplay->presetup()) {
300 		return 0;
301 	}
302 
303 	jconfig_ConfigureModules();
304 	initializeDevices();
305 
306 	jresolver_Initialize();
307 	currentDisplay->setup();
308 
309 	while (TRUE) {
310 
311 		jprocessor_ResetStats();
312 
313 		if (!currentDisplay->prerunsetup()) {
314 			break;
315 		}
316 
317 		jcapture_SetDevice(jconfig_Settings.device);
318 		jcapture_SetBpfFilterText(jconfig_GetSelectedBpfFilterText());
319 
320 		currentDisplay->prerun();
321 
322 		jcapture_Start();
323 		jprocessor_Start();
324 
325 		if (!currentDisplay->run()) {
326 			// In case we're not switching to another device, we can happily finish
327 			// after our display thread dies. (mind the endwin())
328 			break;
329 		}
330 
331 		jcapture_Kill();
332 
333 		while (threadCount) {
334 			g_thread_yield();
335 		}
336 	}
337 
338 	if (debugFile) {
339 		fclose(debugFile);
340 	}
341 
342 	currentDisplay->shutdown();
343 
344 	jresolver_Shutdown();
345 
346 	return 0;
347 }
348