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(¤t);
62 fprintf(logfile, "%.15s ", ctime(¤t) + 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