htable: start empty.
[ccan] / junkcode / swehack@gmail.com-snifstat / snifstat.c
1 /* this application captures packets destined to and from 
2  * a specified host, it then tries to calculate in and out 
3  * traffic statistics and display it like ifstat 
4  * by nocturnal [at] swehack [dot] se */
5
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <unistd.h>
9 #include <string.h>
10
11 /* gethostbyname(3) */
12 #include <netdb.h>
13
14 /* networking(4) */
15 #include <sys/types.h>
16 #include <sys/socket.h>
17 #include <net/if.h>
18 #include <net/route.h>
19 #include <net/if_dl.h>
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
22 #include <netinet/if_ether.h>
23 #include <ifaddrs.h>
24
25 /* setitimer(2) */
26 #include <sys/time.h>
27
28 /* signal(3) */
29 #include <signal.h>
30
31 /* get_windowsize */
32 #include <termios.h>
33 #ifndef TIOCGWINSZ
34 #include <sys/ioctl.h>
35 #endif
36
37 /* duh -lpcap */
38 #include <pcap.h>
39
40 #define APP_NAME        "snifstat"
41 #define APP_VERSION     0.2
42
43 unsigned int calc_traf_io(char *, struct ether_header *);
44 int ethaddrsncmp(const char *, const char *, size_t);
45 void reset_count(int);
46 unsigned short get_windowsize(void);
47 void usage(const char *);
48
49 unsigned int reset = 0;
50
51 int main(int argc, char **argv) {
52         char *ifname = NULL;
53         int argch;
54         unsigned int show_in_bits = 0;
55         char bits_prefix[] = "Kbps";
56         char bytes_prefix[] = "KB/s";
57         unsigned int max_iteration = 0;
58
59         char errbuf[PCAP_ERRBUF_SIZE];
60         pcap_t *pcap = NULL;
61         struct bpf_program filterd;
62         bpf_u_int32 netp, netmask;
63         char *filter = NULL;
64         const u_char *packet = NULL;
65         struct pcap_pkthdr header;
66         
67         unsigned char *ether_addrs = NULL;
68         struct ifaddrs *ifa = NULL;
69         struct sockaddr_dl *sdl = NULL;
70
71         struct itimerval itv, oitv;
72         struct itimerval *itvp = &itv;
73
74
75         struct ether_header *ethernet = NULL;
76         
77         unsigned int iteration = 0, total_iteration = 0;
78         double cur_in, cur_out;
79         unsigned short cur_ws = 0, new_ws, old_ws, ws_change;
80         unsigned int traf_io = 2;
81
82         unsigned int *resetp = &reset;
83
84         if(argc < 4) {
85                 usage(argv[0]);
86                 exit(-1);
87         }
88
89         while((argch = getopt(argc, argv, "i:bc:hv")) != -1) {
90                 switch(argch) {
91                         case 'i':
92                                 if(strlen(optarg) < 16) {
93                                         ifname = optarg;
94                                         optreset = 1;
95                                 } else {
96                                         usage(argv[0]);
97                                         exit(-1);
98                                 }
99                                 break;
100                         case 'b':
101                                 show_in_bits = 1;
102                                 optreset = 1;
103                                 break;
104                         case 'c':
105                                 max_iteration = (unsigned int)atoi(optarg);
106                                 break;
107                         case 'h':
108                                 usage(argv[0]);
109                                 exit(-1);
110                         case 'v': /* LOL WUT?! */
111                                 printf("%s v%.1f by nocturnal [at] swehack [dot] se\n", APP_NAME, APP_VERSION);
112                                 exit(-1);
113                 }
114         }
115
116         if(argc - optind < 1) {
117                 usage(argv[0]);
118                 exit(-1);
119         }
120
121         filter = argv[optind];
122
123         if(pcap_lookupnet(ifname, &netp, &netmask, errbuf) == -1) {
124                 fprintf(stderr, "pcap_lookupnet: %s\n", errbuf);
125                 exit(-1);
126         }
127
128         if((pcap = pcap_open_live(ifname, 65535, 1, 10, errbuf)) == NULL) {
129                 fprintf(stderr, "pcap_open_live: %s\n", errbuf);
130                 exit(-1);
131         }
132
133         if(pcap_compile(pcap, &filterd, filter, 1, netmask) == -1) {
134                 fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(pcap));
135                 exit(-1);
136         }
137
138         if(pcap_setfilter(pcap, &filterd) == -1) {
139                 fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(pcap));
140                 exit(-1);
141         }
142
143         if(signal(SIGALRM, reset_count) == SIG_ERR) {
144                 perror("signal: ");
145                 exit(-1);
146         }
147
148         if(getifaddrs(&ifa) == -1) {
149                 perror("getifaddrs: ");
150                 exit(-1);
151         }
152
153         for(;ifa;ifa = ifa->ifa_next) {
154                 if(strncmp(ifname, ifa->ifa_name, sizeof(ifa->ifa_name)) == 0) {
155                         sdl = (struct sockaddr_dl *)ifa->ifa_addr;
156                         if((ether_addrs = malloc(sdl->sdl_alen)) == NULL) {
157                                 perror("malloc: ");
158                                 exit(-1);
159                         }
160                         memcpy(ether_addrs, LLADDR(sdl), sdl->sdl_alen);
161                         break;
162                 }
163         }
164
165         timerclear(&itvp->it_interval);
166         itvp->it_value.tv_sec = 1;
167         itvp->it_value.tv_usec = 0;
168
169         while(1) {
170                 *resetp = 0;
171
172                 old_ws = cur_ws;
173                 new_ws = get_windowsize();
174                 if(new_ws != old_ws) {
175                         cur_ws = new_ws;
176                 }
177                 ws_change = cur_ws-2;
178
179                 if(setitimer(ITIMER_REAL, itvp, &oitv) < 0) {
180                         fprintf(stderr, "setitimer: \n");
181                         exit(-1);
182                 }
183
184                 cur_in = 0.0;
185                 cur_out = 0.0;
186                 while(*resetp == 0) {
187                         if((packet = pcap_next(pcap, &header)) != NULL) {
188                                 ethernet = (struct ether_header *)packet;
189
190                                 if(header.len == 671429858) {
191                                         cur_in += 0.0;
192                                         cur_out += 0.0;
193                                 } else {
194                                         traf_io = calc_traf_io(ether_addrs, ethernet);
195                                         if(traf_io == 0) {
196                                                 cur_in += header.len;
197                                         } else if(traf_io == 1) {
198                                                 cur_out += header.len;
199                                         }
200                                         traf_io = 2;
201                                 }
202                         }
203                 }
204
205                 cur_in /= 1024;
206                 cur_out /= 1024;
207
208                 if(show_in_bits == 1) {
209                         cur_in *= 8;
210                         cur_out *= 8;
211                 }
212                 
213                 if(iteration >= ws_change || total_iteration == 0) {
214                         printf("%11s\n%5s in %5s out\n", ifname, (show_in_bits == 1) ? bits_prefix : bytes_prefix, (show_in_bits == 1) ? bits_prefix : bytes_prefix);
215                         if(iteration > 1) {
216                                 iteration = 1;
217                         }
218                 }
219
220                 if(total_iteration > 0) {
221                         printf("%8.2lf %9.2lf\n", cur_in, cur_out);
222                 }
223
224                 if(max_iteration > 0 && max_iteration == total_iteration) {
225                         break;
226                 }
227
228                 iteration++;
229                 total_iteration++;
230         }
231
232         pcap_close(pcap);
233
234         exit(0);
235 }
236
237 /* calculate if the packet is going in or out */
238 unsigned int calc_traf_io(char *ether_addrs, struct ether_header *ethernet) {
239         /* 0 = in
240          * 1 = out
241          * 2 = error
242          * GET IT!? GET IT?!?!??!!? :/ */
243         if(ethaddrsncmp(ether_addrs, ethernet->ether_shost, sizeof(ether_addrs)) == 0) {
244                 return(1);
245         }
246         if(ethaddrsncmp(ether_addrs, ethernet->ether_dhost, sizeof(ether_addrs)) == 0) {
247                 return(0);
248         }
249         return(2);
250 }
251
252 /* compare ethernet addresses */
253 int ethaddrsncmp(const char *s1, const char *s2, size_t len) {
254         if(len == 0) {
255                 return(0);
256         }
257
258         do {
259                 if(*s1++ != *s2++) {
260                         return(*(const unsigned char *)s1 - *(const unsigned char *)(s2 - 1));
261                 }
262         } while(--len != 0);
263
264         return(0);
265 }
266
267 /* resets a global value */
268 void reset_count(int signal) {
269         int *resetp = NULL;
270         resetp = &reset;
271
272         *resetp = 1;
273
274         return;
275 }
276
277 /* get windowsize */
278 unsigned short get_windowsize(void) {
279         struct winsize ws;
280
281         if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0) {
282                 return(-1);
283         }
284
285         return(ws.ws_row);
286 }
287
288 void usage(const char *appname) {
289         printf("Usage: %s -i <interface> [-bhv] <filter>\n", appname);
290         printf("\t-i <interface>\t Specify interface to capture from\n"
291                         "\t-b\t\t Show values in bits instead of bytes\n"
292                         "\t-h\t\t Show this help text\n"
293                         "\t-v\t\t Show version\n");
294
295         return;
296 }