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