1 /*
2  * Copyright (C) 2016 Jakub Kruszona-Zawadzki, Core Technology Sp. z o.o.
3  *
4  * This file is part of MooseFS.
5  *
6  * MooseFS is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, version 2 (only).
9  *
10  * MooseFS 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 MooseFS; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1301, USA
18  * or visit http://www.gnu.org/licenses/gpl-2.0.html
19  */
20 
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <inttypes.h>
26 #include <pcap.h>
27 
28 #include "MFSCommunication.h"
29 
30 typedef struct _userdata {
31 	uint8_t bytesskip;
32 } userdata;
33 
34 struct _mfscmd {
35 	uint32_t command;
36 	const char *commandstr;
37 } cmdtab[]={
38 #include "commands.h"
39 	{0,NULL}};
40 
41 typedef struct _mfscommand {
42 	uint32_t command;
43 	char *commandstr;
44 	uint8_t colorcode;
45 	uint8_t display;
46 } mfscommand;
47 
48 static mfscommand *mfscmdtab = NULL;
49 static uint32_t mfscmdtableng = 0;
50 
commands_cmp(const void * a,const void * b)51 int commands_cmp(const void *a,const void *b) {
52 	const mfscommand *aa = (const mfscommand*)a;
53 	const mfscommand *bb = (const mfscommand*)b;
54 	return (aa->command>bb->command)?1:(aa->command<bb->command)?-1:0;
55 }
56 
commands_convert(void)57 void commands_convert(void) {
58 	uint32_t i;
59 	uint8_t ccode;
60 	char *p;
61 	mfscmdtableng = 0;
62 	for (i=0 ; cmdtab[i].commandstr!=NULL ; i++) {
63 		mfscmdtableng++;
64 	}
65 	if (mfscmdtableng==0) {
66 		mfscmdtab = NULL;
67 		return;
68 	}
69 	mfscmdtab = malloc(sizeof(mfscommand)*mfscmdtableng);
70 	for (i=0 ; cmdtab[i].commandstr!=NULL ; i++) {
71 		mfscmdtab[i].command = cmdtab[i].command;
72 		mfscmdtab[i].commandstr = strdup(cmdtab[i].commandstr);
73 		p = mfscmdtab[i].commandstr;
74 		ccode = 0;
75 		if (p[0]!=0 && p[1]!=0 && p[2]=='T' && p[3]=='O') {
76 			if (p[0]=='A' && p[1]=='N') {
77 				if (p[4]=='A' && p[5]=='N') {
78 					ccode = 8; /* ANTOAN */
79 				} else if (p[4]=='C' && p[5]=='L') {
80 					ccode = 13; /* ANTOCL */
81 				} else if (p[4]=='C' && p[5]=='S') {
82 					ccode = 14; /* ANTOCS */
83 				} else if (p[4]=='M' && p[5]=='A') {
84 					ccode = 1; /* ANTOMA */
85 				}
86 			} else if (p[0]=='C' && p[1]=='L') {
87 				if (p[4]=='A' && p[5]=='N') {
88 					ccode = 5; /* CLTOAN */
89 				} else if (p[4]=='C' && p[5]=='S') {
90 					ccode = 11; /* CLTOCS */
91 				} else if (p[4]=='M' && p[5]=='A') {
92 					ccode = 10; /* CLTOMA */
93 				}
94 			} else if (p[0]=='C' && p[1]=='S') {
95 				if (p[4]=='A' && p[5]=='N') {
96 					ccode = 6; /* CSTOAN */
97 				} else if (p[4]=='C' && p[5]=='L') {
98 					ccode = 3; /* CSTOCL */
99 				} else if (p[4]=='M' && p[5]=='A') {
100 					ccode = 4; /* CSTOMA */
101 				}
102 			} else if (p[0]=='M' && p[1]=='A') {
103 				if (p[4]=='A' && p[5]=='N') {
104 					ccode = 9; /* MATOAN */
105 				} else if (p[4]=='C' && p[5]=='L') {
106 					ccode = 2; /* MATOCL */
107 				} else if (p[4]=='C' && p[5]=='S') {
108 					ccode = 12; /* MATOCS */
109 				}
110 			}
111 		}
112 		mfscmdtab[i].colorcode = ccode;
113 		mfscmdtab[i].display = 1;
114 	}
115 	qsort(mfscmdtab,mfscmdtableng,sizeof(mfscommand),commands_cmp);
116 }
117 
commands_find(uint32_t cmd,uint8_t * color,uint8_t * display)118 const char* commands_find(uint32_t cmd,uint8_t *color,uint8_t *display) {
119 	int32_t first,last,middle;
120 	first = 0;
121 	last = mfscmdtableng-1;
122 	middle = (first+last)/2;
123 
124 	while (first<=last) {
125 		if (mfscmdtab[middle].command<cmd) {
126 			first = middle + 1;
127 		} else if (mfscmdtab[middle].command>cmd)  {
128 			last = middle - 1;
129 		} else {
130 			*color = mfscmdtab[middle].colorcode;
131 			*display = mfscmdtab[middle].display;
132 			return mfscmdtab[middle].commandstr;
133 		}
134 		middle = (first+last)/2;
135 	}
136 	*color = 0;
137 	*display = 0;
138 	return NULL;
139 }
140 
commands_exclude(const char * opt)141 void commands_exclude(const char *opt) {
142 	char *str;
143 	char *p;
144 	uint32_t i;
145 
146 	str = strdup(opt);
147 	for (p=strtok(str," ,;") ; p!=NULL ; p=strtok(NULL," ,;")) {
148 		for (i=0 ; i<mfscmdtableng ; i++) {
149 			if (strcmp(p,mfscmdtab[i].commandstr)==0) {
150 				mfscmdtab[i].display = 0;
151 			}
152 		}
153 	}
154 	free(str);
155 }
156 
commands_onlyuse(const char * opt)157 void commands_onlyuse(const char *opt) {
158 	char *str;
159 	char *p;
160 	uint32_t i;
161 	static uint8_t ft = 1;
162 
163 	if (ft) { // do it once
164 		for (i=0 ; i<mfscmdtableng ; i++) {
165 			mfscmdtab[i].display = 0;
166 		}
167 		ft = 0;
168 	}
169 
170 	str = strdup(opt);
171 	for (p=strtok(str," ,;") ; p!=NULL ; p=strtok(NULL," ,;")) {
172 		for (i=0 ; i<mfscmdtableng ; i++) {
173 			if (strcmp(p,mfscmdtab[i].commandstr)==0) {
174 				mfscmdtab[i].display = 1;
175 			}
176 		}
177 	}
178 	free(str);
179 }
180 
181 
182 
183 
184 
185 #define COLOR_PAYLOAD_ADDR "\033[38;5;159m"
186 #define COLOR_PAYLOAD_HEX "\033[38;5;228m"
187 #define COLOR_TIMESTAMP "\033[38;5;231m"
188 #define COLOR_ADDR_SRCHI "\033[38;5;156m"
189 #define COLOR_ADDR_DSTHI "\033[38;5;217m"
190 #define COLOR_ADDR_SRCLO "\033[38;5;194m"
191 #define COLOR_ADDR_DSTLO "\033[38;5;224m"
192 #define COLOR_WRONGPACKET "\033[38;5;199m"
193 #define COLOR_NEWCONN "\033[38;5;116m"
194 #define COLOR_CLOSECONN "\033[38;5;100m"
195 #define COLOR_DATA "\033[38;5;180m"
196 #define COLOR_CLEAR "\033(B\033[m"
197 
198 static const char* color_tab[] = {
199 	"\033[30m",
200 	"\033[31m",
201 	"\033[32m",
202 	"\033[33m",
203 	"\033[34m",
204 	"\033[35m",
205 	"\033[36m",
206 	"\033[37m",
207 	"\033[90m",
208 	"\033[91m",
209 	"\033[92m",
210 	"\033[93m",
211 	"\033[94m",
212 	"\033[95m",
213 	"\033[96m",
214 	"\033[97m",
215 	NULL
216 };
217 
hexdump(const uint8_t * ptr,uint32_t len)218 void hexdump(const uint8_t *ptr,uint32_t len) {
219 	uint8_t eol;
220 	uint32_t i;
221 	eol = 0;
222 	for (i=0 ; i<len ; i++) {
223 		eol = 1;
224 		if ((i&0x1F)==0) {
225 			printf(COLOR_PAYLOAD_ADDR "\t0x%05X:" COLOR_PAYLOAD_HEX,i);
226 		}
227 		printf(" %02X",ptr[i]);
228 		if ((i&0x1F)==0x1F) {
229 			printf(COLOR_CLEAR "\n");
230 			eol = 0;
231 		}
232 	}
233 	if (eol) {
234 		printf(COLOR_CLEAR "\n");
235 	}
236 }
237 
238 // stupid implementation
239 typedef struct _connection {
240 	uint32_t srcip,dstip;
241 	uint16_t srcport,dstport;
242 	uint32_t seq;
243 	struct _connection *next;
244 } connection;
245 
246 static connection *connhead = NULL;
247 
packet_find(uint32_t srcip,uint16_t srcport,uint32_t dstip,uint16_t dstport)248 static inline connection** packet_find(uint32_t srcip,uint16_t srcport,uint32_t dstip,uint16_t dstport) {
249 	connection *c,**cp;
250 	cp = &connhead;
251 	while ((c = *cp)) {
252 		if (c->srcip==srcip && c->dstip==dstip && c->srcport==srcport && c->dstport==dstport) {
253 			return cp;
254 		}
255 		cp = &(c->next);
256 	}
257 	return NULL;
258 }
259 
print_info(const struct timeval * ts,const uint8_t * ip,uint16_t srcport,uint16_t dstport)260 static inline void print_info(const struct timeval *ts,const uint8_t *ip,uint16_t srcport,uint16_t dstport) {
261 	printf(COLOR_TIMESTAMP "%ld.%06u : ",(long)(ts->tv_sec),(unsigned)(ts->tv_usec));
262 	printf("%s%3u.%3u.%3u.%3u : %5" PRIu16 COLOR_CLEAR " -> %s%3u.%3u.%3u.%3u : %5"PRIu16 COLOR_CLEAR " ",(srcport<49152)?COLOR_ADDR_SRCLO:COLOR_ADDR_SRCHI,ip[12],ip[13],ip[14],ip[15],srcport,(dstport<49152)?COLOR_ADDR_DSTLO:COLOR_ADDR_DSTHI,ip[16],ip[17],ip[18],ip[19],dstport);
263 }
264 
parse_packet(u_char * args,const struct pcap_pkthdr * header,const u_char * packet)265 void parse_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet) {
266 	userdata *ud = (userdata*)args;
267 	const uint8_t *ip;
268 	const uint8_t *tcp;
269 	const uint8_t *payload;
270 	uint32_t iplen;
271 	uint32_t iphdrlen;
272 	uint32_t tcplen;
273 	uint32_t tcphdrlen;
274 	uint32_t payloadlen;
275 	uint32_t seqno,skip;
276 	uint32_t srcip,dstip;
277 	uint16_t srcport,dstport;
278 	uint32_t mfscmd,mfslen;
279 	uint8_t ccode,display;
280 	const char *commandstr;
281 	connection *c,**cp;
282 
283 //	printf("%ld.%06u: capleng = %u ; leng = %u (%u + %u)\n",(long)(header->ts.tv_sec),(unsigned)(header->ts.tv_usec),header->caplen,header->len,ud->bytesskip,header->len-ud->bytesskip);
284 	if (ud->bytesskip>=header->caplen) { // no data
285 		return;
286 	}
287 	ip = packet+ud->bytesskip;
288 	iplen = header->caplen-ud->bytesskip;
289 //	hexdump(ip,iplen);
290 //	printf("\n");
291 	if ((ip[0]&0xF0)!=0x40) {
292 		return;	// this is not IPv4 packet - ignore
293 	}
294 	if (iplen<20) {
295 		return; // packet truncated on IP header - ignore
296 	}
297 	if (ip[9]!=6) {
298 		return; // this is not TCP packet - ignore
299 	}
300 	iphdrlen = 4 * (ip[0] & 0xF);
301 	if (iplen < iphdrlen) {
302 		return; // packet truncated on IP header (options) - ignore
303 	}
304 	tcp = ip + iphdrlen;
305 	tcplen = iplen - iphdrlen;
306 //	hexdump(tcp,tcplen);
307 //	printf("\n");
308 	if (tcplen<20) {
309 		return; // packet truncated on TCP header - ignore
310 	}
311 	tcphdrlen = 4 * (tcp[12] >> 4);
312 	if (tcplen < tcphdrlen) {
313 		return; // packet truncated on TCP header (options) - ignore
314 	}
315 	srcip = ((ip[12]*256U+ip[13])*256U+ip[14])*256U+ip[15];
316 	dstip = ((ip[16]*256U+ip[17])*256U+ip[18])*256U+ip[19];
317 	srcport = tcp[0]*256U+tcp[1];
318 	dstport = tcp[2]*256U+tcp[3];
319 	seqno = ((tcp[4]*256U+tcp[5])*256U+tcp[6])*256U+tcp[7];
320 	cp = packet_find(srcip,srcport,dstip,dstport);
321 	if (tcp[13]&0x2) { // SYN
322 		print_info(&(header->ts),ip,srcport,dstport);
323 		printf(COLOR_NEWCONN "... new connection ..." COLOR_CLEAR "\n");
324 	}
325 	if (tcp[13]&0x5) { // RST | FIN
326 		print_info(&(header->ts),ip,srcport,dstport);
327 		printf(COLOR_CLOSECONN "... close connection ..." COLOR_CLEAR "\n");
328 		if (cp!=NULL) {
329 			c = *cp;
330 			*cp = c->next;
331 			free(c);
332 		}
333 		return;
334 	}
335 	payload = tcp + tcphdrlen;
336 	payloadlen = tcplen - tcphdrlen;
337 	if (cp!=NULL) {
338 		c = *cp;
339 		if (c->seq > seqno) {
340 			skip = c->seq - seqno;
341 			print_info(&(header->ts),ip,srcport,dstport);
342 			printf(COLOR_DATA "... data in packet ..." COLOR_CLEAR "\n");
343 		} else {
344 			skip = 0;
345 		}
346 		if (skip < payloadlen) {
347 			payload += skip;
348 			payloadlen -= skip;
349 		} else {
350 			return;
351 		}
352 	}
353 //	hexdump(payload,payloadlen);
354 	while (payloadlen>=8) {
355 		mfscmd = ((payload[0]*256U+payload[1])*256U+payload[2])*256U+payload[3];
356 		mfslen = ((payload[4]*256U+payload[5])*256U+payload[6])*256U+payload[7];
357 		commandstr = commands_find(mfscmd,&ccode,&display);
358 		if (commandstr && mfslen<=100000000) {
359 			if (display) {
360 				print_info(&(header->ts),ip,srcport,dstport);
361 				if (ccode>=1 && ccode<=15) {
362 					printf("%s",color_tab[ccode]);
363 				}
364 				printf("%s",commandstr);
365 				if (ccode) {
366 					printf(COLOR_CLEAR);
367 				}
368 				printf("\n");
369 				if (payloadlen-8<=128) {
370 					if (mfslen < payloadlen-8) {
371 						hexdump(payload+8,mfslen);
372 					} else {
373 						hexdump(payload+8,payloadlen-8);
374 					}
375 				} else {
376 					if (mfslen < 128) {
377 						hexdump(payload+8,mfslen);
378 					} else {
379 						hexdump(payload+8,128);
380 						printf("\t(...)\n");
381 					}
382 				}
383 			}
384 			if (mfslen + 8 == payloadlen) {
385 				if (cp!=NULL) {
386 					c = *cp;
387 					*cp = c->next;
388 					free(c);
389 				}
390 				payloadlen = 0;
391 				payload = NULL;
392 			} else if (mfslen + 8 < payloadlen) {
393 				payloadlen -= (mfslen + 8);
394 				payload += (mfslen + 8);
395 				seqno += (mfslen + 8);
396 			} else {
397 				if (cp!=NULL) {
398 					c = *cp;
399 					c->seq = seqno + mfslen + 8;
400 				} else {
401 					c = malloc(sizeof(connection));
402 					c->srcip = srcip;
403 					c->dstip = dstip;
404 					c->srcport = srcport;
405 					c->dstport = dstport;
406 					c->seq = seqno + mfslen + 8;
407 					c->next = connhead;
408 					connhead = c;
409 				}
410 				payloadlen = 0;
411 				payload = NULL;
412 			}
413 		} else {
414 			print_info(&(header->ts),ip,srcport,dstport);
415 			printf(COLOR_WRONGPACKET "... not mfs packet (%u:%u) ..." COLOR_CLEAR "\n",mfscmd,mfslen);
416 			if (cp!=NULL) {
417 				c = *cp;
418 				*cp = c->next;
419 				free(c);
420 			}
421 			return;
422 		}
423 	}
424 	return;
425 }
426 
usage(const char * appname)427 void usage(const char *appname) {
428 	fprintf(stderr,"usage: %s [-i interface] [-f pcap_filter] [-c packet count] [-e commands] [-o commands]\n\t-e: do not display this commands\n\t-o: when present only this commands will be displayed\n",appname);
429 }
430 
main(int argc,char ** argv)431 int main(int argc, char **argv) {
432 	char ch;
433 	char errbuf[PCAP_ERRBUF_SIZE];
434 	char *dev;
435 	char *filter;
436 	int32_t packetcnt;
437 	bpf_u_int32 devnet;
438 	bpf_u_int32 devmask;
439 	pcap_t *handle;
440 	int datalink;
441 	struct bpf_program fp;
442 	userdata udm;
443 
444 	commands_convert();
445 
446 	dev = NULL;
447 	filter = NULL;
448 	packetcnt = -1;
449 
450 	while ((ch = getopt(argc, argv, "si:f:c:e:o:?")) != -1) {
451 		switch (ch) {
452 			case 's':
453 				if (filter) {
454 					free(filter);
455 				}
456 				filter = strdup("port 9419 or port 9420 or port 9421 or port 9422");
457 				break;
458 			case 'i':
459 				if (dev) {
460 					free(dev);
461 				}
462 				dev = strdup(optarg);
463 				break;
464 			case 'f':
465 				if (filter) {
466 					free(filter);
467 				}
468 				filter = strdup(optarg);
469 				break;
470 			case 'c':
471 				packetcnt = strtol(optarg,NULL,0);
472 				break;
473 			case 'e':
474 				commands_exclude(optarg);
475 				break;
476 			case 'o':
477 				commands_onlyuse(optarg);
478 				break;
479 			default:
480 				usage(argv[0]);
481 				return 1;
482 		}
483 	}
484 
485 	if (dev==NULL) {
486 		dev = pcap_lookupdev(errbuf);
487 		if (dev == NULL) {
488 			fprintf(stderr, "Couldn't find default device: %s\n", errbuf);
489 			return 1;
490 		}
491 	}
492 
493 
494 	if (pcap_lookupnet(dev, &devnet, &devmask, errbuf)<0) {
495 		printf("Device: %s\n",dev);
496 		fprintf(stderr, "Couldn't get netmask for device %s: %s\n", dev, errbuf);
497 		devnet = 0;
498 		devmask = 0;
499 	} else {
500 //		printf("Device: %s (%u.%u.%u.%u/%u.%u.%u.%u)\n", dev, (devnet>>24)&0xFF,(devnet>>16)&0xFF,(devnet>>8)&0xFF,devnet&0xFF,(devmask>>24)&0xFF,(devmask>>16)&0xFF,(devmask>>8)&0xFF,devmask&0xFF);	// BIG/LITTLE ENDIAN ???
501 		printf("Device: %s (%u.%u.%u.%u/%u.%u.%u.%u)\n", dev, devnet&0xFF,(devnet>>8)&0xFF,(devnet>>16)&0xFF,(devnet>>24)&0xFF,devmask&0xFF,(devmask>>8)&0xFF,(devmask>>16)&0xFF,(devmask>>24)&0xFF);
502 	}
503 
504 	handle = pcap_open_live(dev, 100000, 1, 1000, errbuf);
505 	if (handle == NULL) {
506 		fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
507 		return 1;
508 	}
509 
510 	datalink = pcap_datalink(handle);
511 	if (datalink == DLT_EN10MB) {
512 		udm.bytesskip = 14;
513 	} else if (datalink == DLT_NULL) {
514 		udm.bytesskip = 4;
515 	} else if (datalink == DLT_RAW) {
516 		udm.bytesskip = 0;
517 	} else {
518 		fprintf(stderr, "%s is not an Ethernet (type: %s)\n", dev, pcap_datalink_val_to_name(datalink));
519 		return 1;
520 	}
521 
522 	if (filter!=NULL) {
523 		if (pcap_compile(handle, &fp, filter, 0, devnet)<0) {
524 			fprintf(stderr, "Couldn't parse filter %s: %s\n", filter, pcap_geterr(handle));
525 			return 1;
526 		}
527 
528 		if (pcap_setfilter(handle, &fp)<0) {
529 			fprintf(stderr, "Couldn't install filter %s: %s\n", filter, pcap_geterr(handle));
530 			return 1;
531 		}
532 	}
533 
534 	pcap_loop(handle,packetcnt,parse_packet,(void*)(&udm));
535 
536 	pcap_freecode(&fp);
537 	pcap_close(handle);
538 
539 	printf("\nCapture complete.\n");
540 	return 0;
541 }
542 
543