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