Branch data Line data Source code
1 : : /*
2 : : *****************************************************************************
3 : : *
4 : : * File: pcap_capture.c
5 : : *
6 : : * Purpose: The pcap capture routines for fwknopd.
7 : : *
8 : : * Fwknop is developed primarily by the people listed in the file 'AUTHORS'.
9 : : * Copyright (C) 2009-2014 fwknop developers and contributors. For a full
10 : : * list of contributors, see the file 'CREDITS'.
11 : : *
12 : : * License (GNU General Public License):
13 : : *
14 : : * This program is free software; you can redistribute it and/or
15 : : * modify it under the terms of the GNU General Public License
16 : : * as published by the Free Software Foundation; either version 2
17 : : * of the License, or (at your option) any later version.
18 : : *
19 : : * This program is distributed in the hope that it will be useful,
20 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 : : * GNU General Public License for more details.
23 : : *
24 : : * You should have received a copy of the GNU General Public License
25 : : * along with this program; if not, write to the Free Software
26 : : * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 : : * USA
28 : : *
29 : : *****************************************************************************
30 : : */
31 : :
32 : : #if USE_LIBPCAP
33 : : #include <pcap.h>
34 : : #include <errno.h>
35 : : #endif
36 : :
37 : : #include "fwknopd_common.h"
38 : : #include "pcap_capture.h"
39 : : #include "process_packet.h"
40 : : #include "fw_util.h"
41 : : #include "log_msg.h"
42 : : #include "fwknopd_errors.h"
43 : : #include "sig_handler.h"
44 : : #include "tcp_server.h"
45 : :
46 : : #if HAVE_SYS_WAIT_H
47 : : #include <sys/wait.h>
48 : : #endif
49 : :
50 : : #if USE_LIBPCAP
51 : :
52 : : /* The pcap capture routine.
53 : : */
54 : : int
55 : 470 : pcap_capture(fko_srv_options_t *opts)
56 : : {
57 : : pcap_t *pcap;
58 : 470 : char errstr[PCAP_ERRBUF_SIZE] = {0};
59 : : struct bpf_program fp;
60 : : int res;
61 : 470 : int pcap_errcnt = 0;
62 : 470 : int pending_break = 0;
63 : 470 : int promisc = 0;
64 : 470 : int set_direction = 1;
65 : 470 : int pcap_file_mode = 0;
66 : : int status;
67 : : int useconds;
68 : : int rules_chk_threshold;
69 : : int pcap_dispatch_count;
70 : : int max_sniff_bytes;
71 : : int is_err;
72 : 470 : int chk_rm_all = 0;
73 : : pid_t child_pid;
74 : :
75 : : #if FIREWALL_IPFW
76 : : time_t now;
77 : : #endif
78 : :
79 : 470 : useconds = strtol_wrapper(opts->config[CONF_PCAP_LOOP_SLEEP],
80 : : 0, RCHK_MAX_PCAP_LOOP_SLEEP, NO_EXIT_UPON_ERR, &is_err);
81 [ - + ]: 470 : if(is_err != FKO_SUCCESS)
82 : : {
83 : 0 : log_msg(LOG_ERR, "[*] invalid PCAP_LOOP_SLEEP value");
84 : 0 : clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
85 : : }
86 : :
87 : 470 : max_sniff_bytes = strtol_wrapper(opts->config[CONF_MAX_SNIFF_BYTES],
88 : : 0, RCHK_MAX_SNIFF_BYTES, NO_EXIT_UPON_ERR, &is_err);
89 [ - + ]: 470 : if(is_err != FKO_SUCCESS)
90 : : {
91 : 0 : log_msg(LOG_ERR, "[*] invalid MAX_SNIFF_BYTES");
92 : 0 : clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
93 : : }
94 : :
95 : 470 : rules_chk_threshold = strtol_wrapper(opts->config[CONF_RULES_CHECK_THRESHOLD],
96 : : 0, RCHK_MAX_RULES_CHECK_THRESHOLD, NO_EXIT_UPON_ERR, &is_err);
97 [ - + ]: 470 : if(is_err != FKO_SUCCESS)
98 : : {
99 : 0 : log_msg(LOG_ERR, "[*] invalid RULES_CHECK_THRESHOLD");
100 : 0 : clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
101 : : }
102 : :
103 : : /* Set promiscuous mode if ENABLE_PCAP_PROMISC is set to 'Y'.
104 : : */
105 [ + + ]: 470 : if(strncasecmp(opts->config[CONF_ENABLE_PCAP_PROMISC], "Y", 1) == 0)
106 : 1 : promisc = 1;
107 : :
108 [ + + ]: 470 : if(opts->config[CONF_PCAP_FILE] != NULL
109 [ + - ]: 63 : && opts->config[CONF_PCAP_FILE][0] != '\0')
110 : 63 : pcap_file_mode = 1;
111 : :
112 [ + + ]: 470 : if(pcap_file_mode == 1) {
113 : 63 : log_msg(LOG_INFO, "Reading pcap file: %s",
114 : : opts->config[CONF_PCAP_FILE]);
115 : :
116 : 63 : pcap = pcap_open_offline(opts->config[CONF_PCAP_FILE], errstr);
117 : :
118 [ + + ]: 63 : if(pcap == NULL)
119 : : {
120 : 9 : log_msg(LOG_ERR, "[*] pcap_open_offline() error: %s",
121 : : errstr);
122 : 9 : clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
123 : : }
124 : : }
125 : : else
126 : : {
127 : 407 : log_msg(LOG_INFO, "Sniffing interface: %s",
128 : : opts->config[CONF_PCAP_INTF]);
129 : :
130 : 407 : pcap = pcap_open_live(opts->config[CONF_PCAP_INTF],
131 : : max_sniff_bytes, promisc, 100, errstr
132 : : );
133 : :
134 [ + + ]: 407 : if(pcap == NULL)
135 : : {
136 : 1 : log_msg(LOG_ERR, "[*] pcap_open_live() error: %s", errstr);
137 : 1 : clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
138 : : }
139 : : }
140 : :
141 : : /* Set pcap filters, if any.
142 : : */
143 [ + - ]: 460 : if (opts->config[CONF_PCAP_FILTER][0] != '\0')
144 : : {
145 [ + + ]: 460 : if(pcap_compile(pcap, &fp, opts->config[CONF_PCAP_FILTER], 1, 0) == -1)
146 : : {
147 : 20 : log_msg(LOG_ERR, "[*] Error compiling pcap filter: %s",
148 : : pcap_geterr(pcap)
149 : : );
150 : 20 : clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
151 : : }
152 : :
153 [ + + ]: 434 : if(pcap_setfilter(pcap, &fp) == -1)
154 : : {
155 : 1 : log_msg(LOG_ERR, "[*] Error setting pcap filter: %s",
156 : : pcap_geterr(pcap)
157 : : );
158 : 1 : clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
159 : : }
160 : :
161 : 433 : log_msg(LOG_INFO, "PCAP filter is: '%s'", opts->config[CONF_PCAP_FILTER]);
162 : :
163 : 433 : pcap_freecode(&fp);
164 : : }
165 : :
166 : : /* Determine and set the data link encapsulation offset.
167 : : */
168 [ + - - - ]: 433 : switch(pcap_datalink(pcap)) {
169 : : case DLT_EN10MB:
170 : 433 : opts->data_link_offset = 14;
171 : 433 : break;
172 : : #if defined(__linux__)
173 : : case DLT_LINUX_SLL:
174 : 0 : opts->data_link_offset = 16;
175 : 0 : break;
176 : : #elif defined(__OpenBSD__)
177 : : case DLT_LOOP:
178 : : set_direction = 0;
179 : : opts->data_link_offset = 4;
180 : : break;
181 : : #endif
182 : : case DLT_NULL:
183 : 0 : set_direction = 0;
184 : 0 : opts->data_link_offset = 4;
185 : 0 : break;
186 : : default:
187 : 0 : opts->data_link_offset = 0;
188 : 0 : break;
189 : : }
190 : :
191 : : /* We are only interested on seeing packets coming into the interface.
192 : : */
193 [ + - ]: 433 : if ((opts->pcap_any_direction == 0)
194 [ + + ]: 433 : && (set_direction == 1) && (pcap_file_mode == 0)
195 [ - + ]: 405 : && (pcap_setdirection(pcap, PCAP_D_IN) < 0))
196 [ # # ]: 0 : if(opts->verbose)
197 : 0 : log_msg(LOG_WARNING, "[*] Warning: pcap error on setdirection: %s.",
198 : : pcap_geterr(pcap));
199 : :
200 : : /* Set our pcap handle nonblocking mode.
201 : : *
202 : : * NOTE: This is simply set to 0 for now until we find a need
203 : : * to actually use this mode (which when set on a FreeBSD
204 : : * system, it silently breaks the packet capture).
205 : : */
206 [ + + ]: 433 : if((pcap_file_mode == 0)
207 [ - + ]: 405 : && (pcap_setnonblock(pcap, DEF_PCAP_NONBLOCK, errstr)) == -1)
208 : : {
209 : 0 : log_msg(LOG_ERR, "[*] Error setting pcap nonblocking to %i: %s",
210 : : 0, errstr
211 : : );
212 : 0 : clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
213 : : }
214 : :
215 : 433 : pcap_dispatch_count = strtol_wrapper(opts->config[CONF_PCAP_DISPATCH_COUNT],
216 : : 0, RCHK_MAX_PCAP_DISPATCH_COUNT, NO_EXIT_UPON_ERR, &is_err);
217 [ + + ]: 433 : if(is_err != FKO_SUCCESS)
218 : : {
219 : 1 : log_msg(LOG_ERR, "[*] invalid PCAP_DISPATCH_COUNT");
220 : 1 : clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
221 : : }
222 : :
223 : : /* Initialize our signal handlers. You can check the return value for
224 : : * the number of signals that were *not* set. Those that were not set
225 : : * will be listed in the log/stderr output.
226 : : */
227 [ - + ]: 432 : if(set_sig_handlers() > 0)
228 : 0 : log_msg(LOG_ERR, "Errors encountered when setting signal handlers.");
229 : :
230 : 432 : log_msg(LOG_INFO, "Starting fwknopd main event loop.");
231 : :
232 : : /* Jump into our home-grown packet cature loop.
233 : : */
234 : : while(1)
235 : : {
236 : : /* If we got a SIGCHLD and it was the tcp server, then handle it here.
237 : : */
238 [ + + ]: 20495 : if(got_sigchld)
239 : : {
240 [ - + ]: 1389 : if(opts->tcp_server_pid > 0)
241 : : {
242 : 0 : child_pid = waitpid(0, &status, WNOHANG);
243 : :
244 [ # # ]: 0 : if(child_pid == opts->tcp_server_pid)
245 : : {
246 [ # # ]: 0 : if(WIFSIGNALED(status))
247 : 0 : log_msg(LOG_WARNING, "TCP server got signal: %i", WTERMSIG(status));
248 : :
249 : 0 : log_msg(LOG_WARNING,
250 : : "TCP server exited with status of %i. Attempting restart.",
251 : 0 : WEXITSTATUS(status)
252 : : );
253 : :
254 : 0 : opts->tcp_server_pid = 0;
255 : :
256 : : /* Attempt to restart tcp server ? */
257 : 0 : usleep(1000000);
258 : 0 : run_tcp_server(opts);
259 : : }
260 : : }
261 : :
262 : 1389 : got_sigchld = 0;
263 : : }
264 : :
265 [ + + ]: 20495 : if(sig_do_stop())
266 : : {
267 : 405 : pcap_breakloop(pcap);
268 : 405 : pending_break = 1;
269 : : }
270 : :
271 : 20495 : res = pcap_dispatch(pcap, pcap_dispatch_count,
272 : : (pcap_handler)&process_packet, (unsigned char *)opts);
273 : :
274 : : /* Count processed packets
275 : : */
276 [ + + ]: 20495 : if(res > 0)
277 : : {
278 [ + - ][ + + ]: 775 : if(opts->foreground == 1 && opts->verbose > 2)
279 : 1 : log_msg(LOG_DEBUG, "pcap_dispatch() processed: %d packets", res);
280 : :
281 : : /* Count the set of processed packets (pcap_dispatch() return
282 : : * value) - we use this as a comparison for --packet-limit regardless
283 : : * of SPA packet validity at this point.
284 : : */
285 : 775 : opts->packet_ctr += res;
286 [ + + ][ + - ]: 775 : if (opts->packet_ctr_limit && opts->packet_ctr >= opts->packet_ctr_limit)
287 : : {
288 : 26 : log_msg(LOG_WARNING,
289 : : "* Incoming packet count limit of %i reached",
290 : : opts->packet_ctr_limit
291 : : );
292 : :
293 : 26 : pcap_breakloop(pcap);
294 : 26 : pending_break = 1;
295 : : }
296 : : }
297 : : /* If there was an error, complain and go on (to an extent before
298 : : * giving up).
299 : : */
300 [ + + ]: 19720 : else if(res == -1)
301 : : {
302 [ + + ]: 2 : if((strncasecmp(opts->config[CONF_EXIT_AT_INTF_DOWN], "Y", 1) == 0)
303 [ + - ]: 1 : && errno == ENETDOWN)
304 : : {
305 : 1 : log_msg(LOG_ERR, "[*] Fatal error from pcap_dispatch: %s",
306 : : pcap_geterr(pcap)
307 : : );
308 : 1 : clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
309 : : }
310 : : else
311 : : {
312 : 1 : log_msg(LOG_ERR, "[*] Error from pcap_dispatch: %s",
313 : : pcap_geterr(pcap)
314 : : );
315 : : }
316 : :
317 [ - + ]: 1 : if(pcap_errcnt++ > MAX_PCAP_ERRORS_BEFORE_BAIL)
318 : : {
319 : 0 : log_msg(LOG_ERR, "[*] %i consecutive pcap errors. Giving up",
320 : : pcap_errcnt
321 : : );
322 : 0 : clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
323 : : }
324 : : }
325 [ + + ]: 19718 : else if(pending_break == 1 || res == -2)
326 : : {
327 : : /* pcap_breakloop was called, so we bail. */
328 : 431 : log_msg(LOG_INFO, "Gracefully leaving the fwknopd event loop.");
329 : : break;
330 : : }
331 : : else
332 : : pcap_errcnt = 0;
333 : :
334 : : /* Check for any expired firewall rules and deal with them.
335 : : */
336 [ + + ]: 20063 : if(!opts->test)
337 : : {
338 [ + - ]: 19674 : if(rules_chk_threshold > 0)
339 : : {
340 : 19674 : opts->check_rules_ctr++;
341 [ + + ]: 19674 : if ((opts->check_rules_ctr % rules_chk_threshold) == 0)
342 : : {
343 : 813 : chk_rm_all = 1;
344 : 813 : opts->check_rules_ctr = 0;
345 : : }
346 : : }
347 : 19674 : check_firewall_rules(opts, chk_rm_all);
348 : 19674 : chk_rm_all = 0;
349 : : }
350 : :
351 : : #if FIREWALL_IPFW
352 : : /* Purge expired rules that no longer have any corresponding
353 : : * dynamic rules.
354 : : */
355 : : if(opts->fw_config->total_rules > 0)
356 : : {
357 : : time(&now);
358 : : if(opts->fw_config->last_purge < (now - opts->fw_config->purge_interval))
359 : : {
360 : : ipfw_purge_expired_rules(opts);
361 : : opts->fw_config->last_purge = now;
362 : : }
363 : : }
364 : : #endif
365 : :
366 : 20063 : usleep(useconds);
367 : 20063 : }
368 : :
369 : 431 : pcap_close(pcap);
370 : :
371 : 431 : return(0);
372 : : }
373 : :
374 : : #endif /* USE_LIBPCAP */
375 : :
376 : : /***EOF***/
|