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