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 : : #include <pcap.h>
32 : :
33 : : #include "fwknopd_common.h"
34 : : #include "pcap_capture.h"
35 : : #include "process_packet.h"
36 : : #include "sig_handler.h"
37 : : #include "fw_util.h"
38 : : #include "log_msg.h"
39 : : #include "fwknopd_errors.h"
40 : : #include "sig_handler.h"
41 : : #include "tcp_server.h"
42 : :
43 : : #if HAVE_SYS_WAIT_H
44 : : #include <sys/wait.h>
45 : : #endif
46 : :
47 : : /* The pcap capture routine.
48 : : */
49 : : int
50 : 463 : pcap_capture(fko_srv_options_t *opts)
51 : : {
52 : : pcap_t *pcap;
53 : 463 : char errstr[PCAP_ERRBUF_SIZE] = {0};
54 : : struct bpf_program fp;
55 : : int res;
56 : 463 : int pcap_errcnt = 0;
57 : 463 : int pending_break = 0;
58 : 463 : int promisc = 0;
59 : 463 : int set_direction = 1;
60 : 463 : int pcap_file_mode = 0;
61 : : int status;
62 : : int useconds;
63 : : int pcap_dispatch_count;
64 : : int max_sniff_bytes;
65 : : int is_err;
66 : : pid_t child_pid;
67 : :
68 : : #if FIREWALL_IPFW
69 : : time_t now;
70 : : #endif
71 : :
72 : 463 : useconds = strtol_wrapper(opts->config[CONF_PCAP_LOOP_SLEEP],
73 : : 0, RCHK_MAX_PCAP_LOOP_SLEEP, NO_EXIT_UPON_ERR, &is_err);
74 [ - + ]: 463 : if(is_err != FKO_SUCCESS)
75 : : {
76 : 0 : log_msg(LOG_ERR, "[*] invalid PCAP_LOOP_SLEEP_value");
77 : 0 : clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
78 : : }
79 : :
80 : 463 : max_sniff_bytes = strtol_wrapper(opts->config[CONF_MAX_SNIFF_BYTES],
81 : : 0, RCHK_MAX_SNIFF_BYTES, NO_EXIT_UPON_ERR, &is_err);
82 [ - + ]: 463 : if(is_err != FKO_SUCCESS)
83 : : {
84 : 0 : log_msg(LOG_ERR, "[*] invalid MAX_SNIFF_BYTES");
85 : 0 : clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
86 : : }
87 : :
88 : : /* Set promiscuous mode if ENABLE_PCAP_PROMISC is set to 'Y'.
89 : : */
90 [ + + ]: 463 : if(strncasecmp(opts->config[CONF_ENABLE_PCAP_PROMISC], "Y", 1) == 0)
91 : 1 : promisc = 1;
92 : :
93 [ + + ]: 463 : if(opts->config[CONF_PCAP_FILE] != NULL
94 [ + - ]: 114 : && opts->config[CONF_PCAP_FILE][0] != '\0')
95 : 114 : pcap_file_mode = 1;
96 : :
97 [ + + ]: 463 : if(pcap_file_mode == 1) {
98 : 114 : log_msg(LOG_INFO, "Reading pcap file: %s",
99 : : opts->config[CONF_PCAP_FILE]);
100 : :
101 : 114 : pcap = pcap_open_offline(opts->config[CONF_PCAP_FILE], errstr);
102 : :
103 [ + + ]: 114 : if(pcap == NULL)
104 : : {
105 : 18 : log_msg(LOG_ERR, "[*] pcap_open_offline() error: %s",
106 : : errstr);
107 : 18 : clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
108 : : }
109 : : }
110 : : else
111 : : {
112 : 349 : log_msg(LOG_INFO, "Sniffing interface: %s",
113 : : opts->config[CONF_PCAP_INTF]);
114 : :
115 : 349 : pcap = pcap_open_live(opts->config[CONF_PCAP_INTF],
116 : : max_sniff_bytes, promisc, 100, errstr
117 : : );
118 : :
119 [ + + ]: 349 : if(pcap == NULL)
120 : : {
121 : 1 : log_msg(LOG_ERR, "[*] pcap_open_live() error: %s", errstr);
122 : 1 : clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
123 : : }
124 : : }
125 : :
126 : : /* Set pcap filters, if any.
127 : : */
128 [ + - ]: 444 : if (opts->config[CONF_PCAP_FILTER][0] != '\0')
129 : : {
130 [ + + ]: 444 : if(pcap_compile(pcap, &fp, opts->config[CONF_PCAP_FILTER], 1, 0) == -1)
131 : : {
132 : 48 : log_msg(LOG_ERR, "[*] Error compiling pcap filter: %s",
133 : : pcap_geterr(pcap)
134 : : );
135 : 48 : clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
136 : : }
137 : :
138 [ - + ]: 385 : if(pcap_setfilter(pcap, &fp) == -1)
139 : : {
140 : 0 : log_msg(LOG_ERR, "[*] Error setting pcap filter: %s",
141 : : pcap_geterr(pcap)
142 : : );
143 : 0 : clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
144 : : }
145 : :
146 : 385 : log_msg(LOG_INFO, "PCAP filter is: '%s'", opts->config[CONF_PCAP_FILTER]);
147 : :
148 : 385 : pcap_freecode(&fp);
149 : : }
150 : :
151 : : /* Determine and set the data link encapsulation offset.
152 : : */
153 [ + - - - ]: 385 : switch(pcap_datalink(pcap)) {
154 : : case DLT_EN10MB:
155 : 385 : opts->data_link_offset = 14;
156 : 385 : break;
157 : : #if defined(__linux__)
158 : : case DLT_LINUX_SLL:
159 : 0 : opts->data_link_offset = 16;
160 : 0 : break;
161 : : #elif defined(__OpenBSD__)
162 : : case DLT_LOOP:
163 : : set_direction = 0;
164 : : opts->data_link_offset = 4;
165 : : break;
166 : : #endif
167 : : case DLT_NULL:
168 : 0 : set_direction = 0;
169 : 0 : opts->data_link_offset = 4;
170 : 0 : break;
171 : : default:
172 : 0 : opts->data_link_offset = 0;
173 : 0 : break;
174 : : }
175 : :
176 : : /* We are only interested on seeing packets coming into the interface.
177 : : */
178 [ + - ]: 385 : if ((opts->pcap_any_direction == 0)
179 [ + + ]: 385 : && (set_direction == 1) && (pcap_file_mode == 0)
180 [ - + ]: 347 : && (pcap_setdirection(pcap, PCAP_D_IN) < 0))
181 [ # # ]: 0 : if(opts->verbose)
182 : 0 : log_msg(LOG_WARNING, "[*] Warning: pcap error on setdirection: %s.",
183 : : pcap_geterr(pcap));
184 : :
185 : : /* Set our pcap handle nonblocking mode.
186 : : *
187 : : * NOTE: This is simply set to 0 for now until we find a need
188 : : * to actually use this mode (which when set on a FreeBSD
189 : : * system, it silently breaks the packet capture).
190 : : */
191 [ + + ]: 385 : if((pcap_file_mode == 0)
192 [ - + ]: 347 : && (pcap_setnonblock(pcap, DEF_PCAP_NONBLOCK, errstr)) == -1)
193 : : {
194 : 0 : log_msg(LOG_ERR, "[*] Error setting pcap nonblocking to %i: %s",
195 : : 0, errstr
196 : : );
197 : 0 : clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
198 : : }
199 : :
200 : 385 : pcap_dispatch_count = strtol_wrapper(opts->config[CONF_PCAP_DISPATCH_COUNT],
201 : : 0, RCHK_MAX_PCAP_DISPATCH_COUNT, NO_EXIT_UPON_ERR, &is_err);
202 [ + + ]: 385 : if(is_err != FKO_SUCCESS)
203 : : {
204 : 1 : log_msg(LOG_ERR, "[*] invalid PCAP_DISPATCH_COUNT");
205 : 1 : clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
206 : : }
207 : :
208 : : /* Initialize our signal handlers. You can check the return value for
209 : : * the number of signals that were *not* set. Those that were not set
210 : : * will be listed in the log/stderr output.
211 : : */
212 [ - + ]: 384 : if(set_sig_handlers() > 0)
213 : 0 : log_msg(LOG_ERR, "Errors encountered when setting signal handlers.");
214 : :
215 : 384 : log_msg(LOG_INFO, "Starting fwknopd main event loop.");
216 : :
217 : : /* Jump into our home-grown packet cature loop.
218 : : */
219 : : while(1)
220 : : {
221 : : /* If we got a SIGCHLD and it was the tcp server, then handle it here.
222 : : */
223 [ + + ]: 21003 : if(got_sigchld)
224 : : {
225 [ - + ]: 483 : if(opts->tcp_server_pid > 0)
226 : : {
227 : 0 : child_pid = waitpid(0, &status, WNOHANG);
228 : :
229 [ # # ]: 0 : if(child_pid == opts->tcp_server_pid)
230 : : {
231 [ # # ]: 0 : if(WIFSIGNALED(status))
232 : 0 : log_msg(LOG_WARNING, "TCP server got signal: %i", WTERMSIG(status));
233 : :
234 : 0 : log_msg(LOG_WARNING,
235 : : "TCP server exited with status of %i. Attempting restart.",
236 : 0 : WEXITSTATUS(status)
237 : : );
238 : :
239 : 0 : opts->tcp_server_pid = 0;
240 : :
241 : : /* Attempt to restart tcp server ? */
242 : 0 : usleep(1000000);
243 : 0 : run_tcp_server(opts);
244 : : }
245 : : }
246 : :
247 : 483 : got_sigchld = 0;
248 : : }
249 : :
250 : : /* Any signal except USR1, USR2, and SIGCHLD mean break the loop.
251 : : */
252 [ + + ]: 21003 : if(got_signal != 0)
253 : : {
254 [ + + ][ + + ]: 832 : if(got_sigint || got_sigterm || got_sighup)
[ + + ]
255 : : {
256 : 347 : pcap_breakloop(pcap);
257 : 347 : pending_break = 1;
258 : : }
259 [ + + ][ + + ]: 485 : else if(got_sigusr1 || got_sigusr2)
260 : : {
261 : : /* Not doing anything with these yet.
262 : : */
263 : 2 : got_sigusr1 = got_sigusr2 = 0;
264 : 2 : got_signal = 0;
265 : : }
266 : : else
267 : 483 : got_signal = 0;
268 : : }
269 : :
270 : 21003 : res = pcap_dispatch(pcap, pcap_dispatch_count,
271 : : (pcap_handler)&process_packet, (unsigned char *)opts);
272 : :
273 : : /* Count processed packets
274 : : */
275 [ + + ]: 21003 : if(res > 0)
276 : : {
277 [ + - ][ + + ]: 716 : if(opts->foreground == 1 && opts->verbose > 2)
278 : 1 : log_msg(LOG_DEBUG, "pcap_dispatch() processed: %d packets", res);
279 : :
280 : : /* Count the set of processed packets (pcap_dispatch() return
281 : : * value) - we use this as a comparison for --packet-limit regardless
282 : : * of SPA packet validity at this point.
283 : : */
284 : 716 : opts->packet_ctr += res;
285 [ + + ][ + - ]: 716 : if (opts->packet_ctr_limit && opts->packet_ctr >= opts->packet_ctr_limit)
286 : : {
287 : 37 : log_msg(LOG_WARNING,
288 : : "* Incoming packet count limit of %i reached",
289 : : opts->packet_ctr_limit
290 : : );
291 : :
292 : 37 : pcap_breakloop(pcap);
293 : 37 : pending_break = 1;
294 : : }
295 : : }
296 : : /* If there was an error, complain and go on (to an extent before
297 : : * giving up).
298 : : */
299 [ - + ]: 20287 : else if(res == -1)
300 : : {
301 : 0 : log_msg(LOG_ERR, "[*] Error from pcap_dispatch: %s",
302 : : pcap_geterr(pcap)
303 : : );
304 : :
305 [ # # ]: 0 : if(pcap_errcnt++ > MAX_PCAP_ERRORS_BEFORE_BAIL)
306 : : {
307 : 0 : log_msg(LOG_ERR, "[*] %i consecutive pcap errors. Giving up",
308 : : pcap_errcnt
309 : : );
310 : 0 : clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
311 : : }
312 : : }
313 [ + + ]: 20287 : else if(pending_break == 1 || res == -2)
314 : : {
315 : : /* pcap_breakloop was called, so we bail. */
316 : 384 : log_msg(LOG_INFO, "Gracefully leaving the fwknopd event loop.");
317 : : break;
318 : : }
319 : : else
320 : : pcap_errcnt = 0;
321 : :
322 : : /* Check for any expired firewall rules and deal with them.
323 : : */
324 [ + + ]: 20619 : if(!opts->test)
325 : 20288 : check_firewall_rules(opts);
326 : :
327 : : #if FIREWALL_IPFW
328 : : /* Purge expired rules that no longer have any corresponding
329 : : * dynamic rules.
330 : : */
331 : : if(opts->fw_config->total_rules > 0)
332 : : {
333 : : time(&now);
334 : : if(opts->fw_config->last_purge < (now - opts->fw_config->purge_interval))
335 : : {
336 : : ipfw_purge_expired_rules(opts);
337 : : opts->fw_config->last_purge = now;
338 : : }
339 : : }
340 : : #endif
341 : :
342 : 20619 : usleep(useconds);
343 : 20619 : }
344 : :
345 : 384 : pcap_close(pcap);
346 : :
347 : 384 : return(0);
348 : : }
349 : :
350 : : /***EOF***/
|