1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdarg.h>
4 #include <unistd.h>
5 #include <signal.h>
6 #include <string.h>
7 #include <sys/types.h>
8 #include <sys/socket.h>
9 #include <netinet/in.h>
10 #include <arpa/inet.h>
11 #include <syslog.h>
12 #include <netdb.h>
13 #include <errno.h>
14 #include <time.h>
15 #include <pcap.h>
16 #include "config.h"
17 #include "smurflog.h"
18 #include "hash.h"
19 
20 u_int  num	 = 0;
21 u_long pps	 = 0;
22 float  kbps	 = 0.0;
23 u_char smurfmode = 0;
24 u_char resetsecs = RESET_SECONDS;
25 #if LOGGING_OPTIONS >= 2
26 float maxkbps	 = 0.0;
27 u_long maxpps	 = 0;
28 #endif
29 
30 FILE *logfile;
31 struct hash hashtable[256];
32 struct bpf_program fcode;
33 char ebuf[PCAP_ERRBUF_SIZE];
34 struct in_addr localnet, netmask;
35 pcap_handler printer;
36 pcap_t *pd;
37 
38 u_char o_promisc	= 0;		/* promisc mode flag */
39 u_char o_syslog		= 1;		/* log to syslog or file */
40 char  *o_logfile	= NULL;		/* file to log to if not syslog */
41 char  *o_iface		= NULL;		/* interface */
42 
usage(char * progname)43 void usage(char *progname)
44 {
45    fprintf(stderr, "usage: %s [-hp] [-i iface] [-f logfile]\n", progname);
46    exit(1);
47 }
48 
log(char * format,...)49 inline void log(char *format, ...)
50 {
51    va_list args;
52 #if LOGGING_OPTIONS >= 1
53    time_t current;
54 #endif
55 
56    va_start(args, format);
57 
58    if (o_syslog)
59       vsyslog(LOG_NOTICE, format, args);
60    else {
61       time(&current);
62       fprintf(logfile, "%.15s ", ctime(&current) + 4);
63       vfprintf(logfile, format, args);
64       putc('\n', logfile);
65       fflush(logfile);
66    }
67 
68    va_end(args);
69 }
70 
alarm_handler(int status)71 inline RETSIGTYPE alarm_handler(int status)
72 {
73    if (!smurfmode) {
74       if (kbps >= THRESHOLD_KBPS && pps >= THRESHOLD_PPS) {
75          smurfmode = 1;
76 #if LOGGING_OPTIONS >= 1
77          log("Threshold reached, %4.02fkbps %ldpkt/s, Looks like a smurf.", kbps, pps);
78          fflush(stdout);
79 #endif
80       }
81    } else {
82 #if LOGGING_OPTIONS == 3
83       if (kbps || pps) {
84          log("Update %4.02fkbps %ldpkt/s", kbps, pps);
85          fflush(stdout);
86       }
87 #endif
88       if (kbps && pps) {
89          resetsecs = RESET_SECONDS;
90 #if LOGGING_OPTIONS >= 2
91          if (kbps > maxkbps)
92             maxkbps = kbps;
93          if (pps > maxpps)
94             maxpps = pps;
95 #endif
96       } else {
97          if (resetsecs == 0) {
98             resetsecs = RESET_SECONDS;
99             smurfmode = 0;
100 #if LOGGING_OPTIONS == 1
101             log("No longer in smurf mode.");
102             fflush(stdout);
103 #endif
104 #if LOGGING_OPTIONS >= 2
105             log("Leaving Smurf mode. Maximium seen %4.02fkbps %ldpkt/s.", maxkbps, maxpps);
106             fflush(stdout);
107 #endif
108             clear_hash();
109             num = 0;
110          } else
111             --resetsecs;
112       }
113    }
114 
115 #if LOGGING_OPTIONS >= 2
116    {
117 #else
118    if (!smurfmode) {
119 #endif
120       pps = 0;
121       kbps = 0.0;
122    }
123    alarm(1);
124 }
125 
126 inline void handlepkt(const u_char *packet)
127 {
128    u_short length;
129    u_long  srcip;
130    u_char  type;
131 
132    type   = *((u_char *)  (packet + 20));
133 
134    if (type != 0)			/* If not ICMP Echo Reply, skip it */
135       return;
136 
137    length = *((u_short *) (packet + 2));
138    srcip  = *((u_long *)  (packet + 12));
139    srcip &= 0x00FFFFFF;			/* Mask 255.255.255.0 */
140 
141 #if (LOGGING_OPTIONS == 0) || (LOGGING_OPTIONS >= 2)
142 #if LOGGING_OPTIONS >= 2
143    kbps += ntohs(length) / 1024.0;
144    pps  += 1;
145 #endif
146    if (!smurfmode)
147       return;
148 #else
149    if (!smurfmode) {
150       kbps += ntohs(length) / 1024.0;
151       pps  += 1;
152       return;
153    }
154 #endif
155 
156    if (num > MAX_LOGABLE)
157       return;
158    if (!unnamed(srcip))
159       return;
160 
161    num += 1;
162    log("#%d - Probable Smurf attack detected from %s/24 (%d bytes)",
163 	num, inet_ntoa(*((struct in_addr *) &srcip)), ntohs(length));
164 }
165 
166 inline void eth_printer(u_char *user, const struct pcap_pkthdr *h, const u_char *p)
167 {
168    handlepkt(p+14);
169 }
170 
171 inline void ppp_printer(u_char *user, const struct pcap_pkthdr *h, const u_char *p)
172 {
173    handlepkt(p+4);
174 }
175 
176 inline pcap_handler lookup_printer(int type)
177 {
178    if (type == DLT_EN10MB || type == DLT_IEEE802)
179       return eth_printer;
180    if (type == DLT_PPP);
181       return ppp_printer;
182 
183    fatal("unknown data link");
184 }
185 
186 int main(int argc, char *argv[])
187 {
188    int ch, i;
189 
190    printf("Smurflog v2.1 by Richard Steenbergen <humble@lightning.net>\n");
191 
192    while ((ch = getopt(argc, argv, "i:f:pu:h")) != EOF)
193       switch(ch) {
194          case 'i':
195             o_iface = optarg;
196             break;
197          case 'f':
198             o_syslog = 0;
199             o_logfile = optarg;
200             break;
201          case 'p':
202             o_promisc = 1;
203             break;
204          case 'h':
205          default:
206             usage(argv[0]);
207       }
208 
209    if (o_iface == NULL) {
210       o_iface = pcap_lookupdev(ebuf);
211       if (o_iface == NULL)
212          fatal(ebuf);
213    }
214 
215    if (o_syslog)
216       openlog("smurflog", LOG_PID, LOG_DAEMON);
217    else {
218       if ((logfile = fopen(o_logfile, "a")) == NULL)
219          fatal("Cannot open specified logfile.");
220    }
221 
222    if ((pd = pcap_open_live(o_iface, 128, o_promisc, 100, ebuf)) == NULL)
223       fatal(ebuf);
224    if (pcap_lookupnet(o_iface, &localnet.s_addr, &netmask.s_addr, ebuf) < 0)
225       fatal(ebuf);
226    if (pcap_compile(pd, &fcode, "icmp", 1, (u_long)netmask.s_addr) < 0)
227       fatal(pcap_geterr(pd));
228    if (pcap_setfilter(pd, &fcode) < 0)
229       fatal(pcap_geterr(pd));
230    if (o_iface) {
231       printf("Now monitoring %s for smurf attacks.\n", o_iface);
232 
233       if (o_syslog)
234          log("Now monitoring %s for smurf attacks.\n", o_iface);
235       else
236          fprintf(logfile, "Now monitoring %s for smurf attacks.\n", o_iface);
237    }
238 
239    i = fork();
240 
241    if (i) {
242       printf("Launching into background (pid: %d)\n", i); fflush(stdout);
243       exit(0);
244    }
245 
246    close(0); close(1); close(2);
247 
248    printer = lookup_printer(pcap_datalink(pd));
249 
250    fflush(stdout);
251    memset(hashtable, 0, sizeof(hashtable));
252    signal(SIGALRM, alarm_handler);
253    alarm(1);
254 
255    for(;;)
256       pcap_loop(pd, 0, printer, NULL);
257 
258    pcap_close(pd);
259 
260    if (o_syslog)
261       closelog();
262    else
263       fclose(logfile);
264 
265    exit(0);
266 }
267