]>
Commit | Line | Data |
---|---|---|
c45d8985 EL |
1 | /* Copyright (C) 2011 Open Information Security Foundation |
2 | * | |
3 | * You can copy, redistribute or modify this Program under the terms of | |
4 | * the GNU General Public License version 2 as published by the Free | |
5 | * Software Foundation. | |
6 | * | |
7 | * This program is distributed in the hope that it will be useful, | |
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | * GNU General Public License for more details. | |
11 | * | |
12 | * You should have received a copy of the GNU General Public License | |
13 | * version 2 along with this program; if not, write to the Free Software | |
14 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
15 | * 02110-1301, USA. | |
16 | */ | |
17 | ||
18 | /** | |
19 | * \file | |
20 | * | |
21 | * \author Eric Leblond <eric@regit.org> | |
22 | * | |
23 | * AF_PACKET socket acquisition support | |
24 | * | |
25 | * Fanouts socket from David Miller: | |
26 | * we need to support the split of flow in different socket | |
27 | * option: | |
e80b30c0 EL |
28 | * - packet_fanout type |
29 | * - fanout ID ?? seems it could be useful | |
30 | * - protocol is the IEEE 802.3 protocol number in network order (filtering | |
31 | * is great) | |
32 | * - runmode -> family of threads in parallel (acccount) | |
33 | * - add a new ratio or threads number (overwritten by cpu_affinity) | |
34 | * - add af_max_read_packets for batched reading | |
c45d8985 EL |
35 | * |
36 | * architecture | |
37 | * loop with read | |
38 | * code needed for iface name to int mapping | |
39 | * socket opening | |
40 | * socket call | |
41 | * bind | |
42 | * must switch to promiscous mode -> use PACKET_MR_PROMISC socket option | |
43 | * | |
e80b30c0 EL |
44 | * \todo watch other interface event to detect suppression of the monitored |
45 | * interface | |
c45d8985 EL |
46 | */ |
47 | ||
48 | #include "suricata-common.h" | |
e80b30c0 | 49 | #include "config.h" |
c45d8985 EL |
50 | #include "suricata.h" |
51 | #include "decode.h" | |
52 | #include "packet-queue.h" | |
53 | #include "threads.h" | |
54 | #include "threadvars.h" | |
55 | #include "tm-queuehandlers.h" | |
56 | #include "tm-modules.h" | |
57 | #include "tm-threads.h" | |
58 | #include "tm-threads-common.h" | |
59 | #include "conf.h" | |
60 | #include "util-debug.h" | |
61 | #include "util-error.h" | |
62 | #include "util-privs.h" | |
e80b30c0 | 63 | #include "util-optimize.h" |
c45d8985 EL |
64 | #include "tmqh-packetpool.h" |
65 | #include "source-af-packet.h" | |
66 | ||
e80b30c0 | 67 | #ifdef HAVE_AF_PACKET |
2bc0be6e | 68 | #include <sys/ioctl.h> |
c45d8985 EL |
69 | #include <linux/if_ether.h> |
70 | #include <linux/if_packet.h> | |
71 | #include <linux/if_arp.h> | |
e80b30c0 | 72 | #endif |
c45d8985 EL |
73 | |
74 | extern uint8_t suricata_ctl_flags; | |
75 | extern int max_pending_packets; | |
76 | ||
e80b30c0 EL |
77 | #ifndef HAVE_AF_PACKET |
78 | ||
79 | TmEcode NoAFPSupportExit(ThreadVars *, void *, void **); | |
80 | ||
81 | void TmModuleReceiveAFPRegister (void) { | |
82 | tmm_modules[TMM_RECEIVEAFP].name = "ReceiveAFP"; | |
83 | tmm_modules[TMM_RECEIVEAFP].ThreadInit = NoAFPSupportExit; | |
84 | tmm_modules[TMM_RECEIVEAFP].Func = NULL; | |
85 | tmm_modules[TMM_RECEIVEAFP].ThreadExitPrintStats = NULL; | |
86 | tmm_modules[TMM_RECEIVEAFP].ThreadDeinit = NULL; | |
87 | tmm_modules[TMM_RECEIVEAFP].RegisterTests = NULL; | |
88 | tmm_modules[TMM_RECEIVEAFP].cap_flags = 0; | |
3f1c4efc | 89 | tmm_modules[TMM_RECEIVEAFP].flags = TM_FLAG_RECEIVE_TM; |
e80b30c0 EL |
90 | } |
91 | ||
92 | /** | |
93 | * \brief Registration Function for DecodeAFP. | |
94 | * \todo Unit tests are needed for this module. | |
95 | */ | |
96 | void TmModuleDecodeAFPRegister (void) { | |
97 | tmm_modules[TMM_DECODEAFP].name = "DecodeAFP"; | |
98 | tmm_modules[TMM_DECODEAFP].ThreadInit = NoAFPSupportExit; | |
99 | tmm_modules[TMM_DECODEAFP].Func = NULL; | |
100 | tmm_modules[TMM_DECODEAFP].ThreadExitPrintStats = NULL; | |
101 | tmm_modules[TMM_DECODEAFP].ThreadDeinit = NULL; | |
102 | tmm_modules[TMM_DECODEAFP].RegisterTests = NULL; | |
103 | tmm_modules[TMM_DECODEAFP].cap_flags = 0; | |
104 | } | |
105 | ||
106 | /** | |
107 | * \brief this function prints an error message and exits. | |
108 | */ | |
109 | TmEcode NoAFPSupportExit(ThreadVars *tv, void *initdata, void **data) | |
110 | { | |
111 | SCLogError(SC_ERR_NO_AF_PACKET,"Error creating thread %s: you do not have " | |
112 | "support for AF_PACKET enabled, on Linux host please recompile " | |
113 | "with --enable-af-packet", tv->name); | |
114 | exit(EXIT_FAILURE); | |
115 | } | |
116 | ||
117 | #else /* We have AF_PACKET support */ | |
118 | ||
c45d8985 EL |
119 | #define AFP_IFACE_NAME_LENGTH 48 |
120 | ||
121 | #define AFP_STATE_DOWN 0 | |
122 | #define AFP_STATE_UP 1 | |
123 | ||
124 | #define AFP_RECONNECT_TIMEOUT 500000 | |
125 | ||
126 | #define POLL_TIMEOUT 100 | |
127 | ||
62e63e3f EL |
128 | enum { |
129 | AFP_READ_OK, | |
130 | AFP_READ_FAILURE, | |
131 | AFP_FAILURE, | |
132 | }; | |
133 | ||
c45d8985 EL |
134 | /** |
135 | * \brief Structure to hold thread specific variables. | |
136 | */ | |
137 | typedef struct AFPThreadVars_ | |
138 | { | |
139 | /* thread specific socket */ | |
140 | int socket; | |
141 | /* handle state */ | |
142 | unsigned char afp_state; | |
c45d8985 EL |
143 | |
144 | /* data link type for the thread */ | |
145 | int datalink; | |
146 | int cooked; | |
147 | ||
148 | /* counters */ | |
149 | uint32_t pkts; | |
150 | uint64_t bytes; | |
151 | uint32_t errs; | |
152 | ||
ff6365dd EL |
153 | ThreadVars *tv; |
154 | TmSlot *slot; | |
155 | ||
156 | uint8_t *data; /** Per function and thread data */ | |
157 | int datalen; /** Length of per function and thread data */ | |
158 | ||
159 | char iface[AFP_IFACE_NAME_LENGTH]; | |
e80b30c0 EL |
160 | /* socket buffer size */ |
161 | int buffer_size; | |
df7dbe36 | 162 | int promisc; |
67f791e8 | 163 | int detect_offload; |
e80b30c0 EL |
164 | |
165 | int cluster_id; | |
166 | int cluster_type; | |
c45d8985 | 167 | |
fbca1a4e EL |
168 | int threads; |
169 | ||
c45d8985 | 170 | |
c45d8985 EL |
171 | } AFPThreadVars; |
172 | ||
173 | TmEcode ReceiveAFP(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *); | |
174 | TmEcode ReceiveAFPThreadInit(ThreadVars *, void *, void **); | |
175 | void ReceiveAFPThreadExitStats(ThreadVars *, void *); | |
176 | TmEcode ReceiveAFPThreadDeinit(ThreadVars *, void *); | |
e80b30c0 | 177 | TmEcode ReceiveAFPLoop(ThreadVars *tv, void *data, void *slot); |
c45d8985 EL |
178 | |
179 | TmEcode DecodeAFPThreadInit(ThreadVars *, void *, void **); | |
180 | TmEcode DecodeAFP(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *); | |
181 | ||
182 | /** | |
183 | * \brief Registration Function for RecieveAFP. | |
184 | * \todo Unit tests are needed for this module. | |
185 | */ | |
186 | void TmModuleReceiveAFPRegister (void) { | |
187 | tmm_modules[TMM_RECEIVEAFP].name = "ReceiveAFP"; | |
188 | tmm_modules[TMM_RECEIVEAFP].ThreadInit = ReceiveAFPThreadInit; | |
ff6365dd | 189 | tmm_modules[TMM_RECEIVEAFP].Func = NULL; |
e80b30c0 | 190 | tmm_modules[TMM_RECEIVEAFP].PktAcqLoop = ReceiveAFPLoop; |
c45d8985 EL |
191 | tmm_modules[TMM_RECEIVEAFP].ThreadExitPrintStats = ReceiveAFPThreadExitStats; |
192 | tmm_modules[TMM_RECEIVEAFP].ThreadDeinit = NULL; | |
193 | tmm_modules[TMM_RECEIVEAFP].RegisterTests = NULL; | |
194 | tmm_modules[TMM_RECEIVEAFP].cap_flags = SC_CAP_NET_RAW; | |
195 | } | |
196 | ||
197 | /** | |
198 | * \brief Registration Function for DecodeAFP. | |
199 | * \todo Unit tests are needed for this module. | |
200 | */ | |
201 | void TmModuleDecodeAFPRegister (void) { | |
202 | tmm_modules[TMM_DECODEAFP].name = "DecodeAFP"; | |
203 | tmm_modules[TMM_DECODEAFP].ThreadInit = DecodeAFPThreadInit; | |
204 | tmm_modules[TMM_DECODEAFP].Func = DecodeAFP; | |
205 | tmm_modules[TMM_DECODEAFP].ThreadExitPrintStats = NULL; | |
206 | tmm_modules[TMM_DECODEAFP].ThreadDeinit = NULL; | |
207 | tmm_modules[TMM_DECODEAFP].RegisterTests = NULL; | |
208 | tmm_modules[TMM_DECODEAFP].cap_flags = 0; | |
209 | } | |
210 | ||
e80b30c0 EL |
211 | static int AFPCreateSocket(AFPThreadVars *ptv, char *devname, int verbose); |
212 | ||
c45d8985 EL |
213 | |
214 | /** | |
215 | * \brief AF packet read function. | |
216 | * | |
217 | * This function fills | |
218 | * From here the packets are picked up by the DecodeAFP thread. | |
219 | * | |
220 | * \param user pointer to AFPThreadVars | |
221 | * \retval TM_ECODE_FAILED on failure and TM_ECODE_OK on success | |
222 | */ | |
62e63e3f | 223 | int AFPRead(AFPThreadVars *ptv) |
c45d8985 EL |
224 | { |
225 | Packet *p = NULL; | |
226 | /* XXX should try to use read that get directly to packet */ | |
c45d8985 EL |
227 | int offset = 0; |
228 | int caplen; | |
229 | struct sockaddr_ll from; | |
230 | struct iovec iov; | |
231 | struct msghdr msg; | |
c45d8985 EL |
232 | struct cmsghdr *cmsg; |
233 | union { | |
234 | struct cmsghdr cmsg; | |
235 | char buf[CMSG_SPACE(sizeof(struct tpacket_auxdata))]; | |
236 | } cmsg_buf; | |
c45d8985 EL |
237 | |
238 | msg.msg_name = &from; | |
239 | msg.msg_namelen = sizeof(from); | |
240 | msg.msg_iov = &iov; | |
241 | msg.msg_iovlen = 1; | |
c45d8985 EL |
242 | msg.msg_control = &cmsg_buf; |
243 | msg.msg_controllen = sizeof(cmsg_buf); | |
c45d8985 EL |
244 | msg.msg_flags = 0; |
245 | ||
246 | if (ptv->cooked) | |
247 | offset = SLL_HEADER_LEN; | |
248 | else | |
249 | offset = 0; | |
e80b30c0 EL |
250 | iov.iov_len = ptv->datalen - offset; |
251 | iov.iov_base = ptv->data + offset; | |
c45d8985 EL |
252 | |
253 | caplen = recvmsg(ptv->socket, &msg, MSG_TRUNC); | |
254 | ||
255 | if (caplen < 0) { | |
256 | SCLogWarning(SC_ERR_AFP_READ, "recvmsg failed with error code %" PRId32, | |
257 | errno); | |
62e63e3f | 258 | SCReturnInt(AFP_READ_FAILURE); |
c45d8985 | 259 | } |
ff6365dd EL |
260 | |
261 | p = PacketGetFromQueueOrAlloc(); | |
c45d8985 | 262 | if (p == NULL) { |
62e63e3f | 263 | SCReturnInt(AFP_FAILURE); |
c45d8985 EL |
264 | } |
265 | ||
266 | /* get timestamp of packet via ioctl */ | |
267 | if (ioctl(ptv->socket, SIOCGSTAMP, &p->ts) == -1) { | |
268 | SCLogWarning(SC_ERR_AFP_READ, "recvmsg failed with error code %" PRId32, | |
269 | errno); | |
270 | TmqhOutputPacketpool(ptv->tv, p); | |
62e63e3f | 271 | SCReturnInt(AFP_READ_FAILURE); |
c45d8985 EL |
272 | } |
273 | ||
274 | ptv->pkts++; | |
275 | ptv->bytes += caplen + offset; | |
276 | ||
277 | /* add forged header */ | |
278 | if (ptv->cooked) { | |
e80b30c0 | 279 | SllHdr * hdrp = (SllHdr *)ptv->data; |
c45d8985 EL |
280 | /* XXX this is minimalist, but this seems enough */ |
281 | hdrp->sll_protocol = from.sll_protocol; | |
282 | } | |
283 | ||
284 | p->datalink = ptv->datalink; | |
285 | SET_PKT_LEN(p, caplen + offset); | |
e80b30c0 | 286 | if (PacketCopyData(p, ptv->data, GET_PKT_LEN(p)) == -1) { |
c45d8985 | 287 | TmqhOutputPacketpool(ptv->tv, p); |
62e63e3f | 288 | SCReturnInt(AFP_FAILURE); |
c45d8985 | 289 | } |
e80b30c0 EL |
290 | SCLogDebug("pktlen: %" PRIu32 " (pkt %p, pkt data %p)", |
291 | GET_PKT_LEN(p), p, GET_PKT_DATA(p)); | |
292 | ||
f6ddaf33 EL |
293 | for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { |
294 | struct tpacket_auxdata *aux; | |
295 | ||
296 | if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct tpacket_auxdata)) || | |
297 | cmsg->cmsg_level != SOL_PACKET || | |
298 | cmsg->cmsg_type != PACKET_AUXDATA) | |
299 | continue; | |
300 | ||
301 | aux = (struct tpacket_auxdata *)CMSG_DATA(cmsg); | |
302 | ||
303 | if (aux->tp_status & TP_STATUS_CSUMNOTREADY) { | |
304 | p->flags |= PKT_IGNORE_CHECKSUM; | |
305 | } | |
306 | } | |
307 | ||
c469824b EL |
308 | if (TmThreadsSlotProcessPkt(ptv->tv, ptv->slot, p) != TM_ECODE_OK) { |
309 | TmqhOutputPacketpool(ptv->tv, p); | |
62e63e3f | 310 | SCReturnInt(AFP_FAILURE); |
c469824b | 311 | } |
62e63e3f | 312 | SCReturnInt(AFP_READ_OK); |
c45d8985 EL |
313 | } |
314 | ||
315 | static int AFPTryReopen(AFPThreadVars *ptv) | |
316 | { | |
317 | int afp_activate_r; | |
318 | ||
319 | ptv->afp_state = AFP_STATE_DOWN; | |
320 | ||
e80b30c0 | 321 | afp_activate_r = AFPCreateSocket(ptv, ptv->iface, 0); |
c45d8985 EL |
322 | if (afp_activate_r != 0) { |
323 | return afp_activate_r; | |
324 | } | |
325 | ||
326 | SCLogInfo("Recovering interface listening"); | |
327 | ptv->afp_state = AFP_STATE_UP; | |
328 | return 0; | |
329 | } | |
330 | ||
e80b30c0 EL |
331 | /** |
332 | * \brief Main AF_PACKET reading Loop function | |
333 | */ | |
334 | TmEcode ReceiveAFPLoop(ThreadVars *tv, void *data, void *slot) | |
335 | { | |
336 | uint16_t packet_q_len = 0; | |
337 | AFPThreadVars *ptv = (AFPThreadVars *)data; | |
338 | TmSlot *s = (TmSlot *)slot; | |
339 | ptv->slot = s->slot_next; | |
340 | struct pollfd fds; | |
341 | int r; | |
342 | ||
343 | SCEnter(); | |
344 | ||
345 | fds.fd = ptv->socket; | |
346 | fds.events = POLLIN; | |
347 | ||
348 | while (1) { | |
349 | /* Start by checking the state of our interface */ | |
350 | if (unlikely(ptv->afp_state == AFP_STATE_DOWN)) { | |
351 | int dbreak = 0; | |
352 | do { | |
353 | usleep(AFP_RECONNECT_TIMEOUT); | |
354 | if (suricata_ctl_flags != 0) { | |
355 | dbreak = 1; | |
356 | break; | |
357 | } | |
358 | r = AFPTryReopen(ptv); | |
359 | } while (r < 0); | |
360 | if (dbreak == 1) | |
361 | break; | |
362 | } | |
363 | ||
364 | /* make sure we have at least one packet in the packet pool, to prevent | |
365 | * us from alloc'ing packets at line rate */ | |
366 | do { | |
367 | packet_q_len = PacketPoolSize(); | |
368 | if (unlikely(packet_q_len == 0)) { | |
369 | PacketPoolWait(); | |
370 | } | |
371 | } while (packet_q_len == 0); | |
372 | ||
373 | r = poll(&fds, 1, POLL_TIMEOUT); | |
374 | ||
375 | if (suricata_ctl_flags != 0) { | |
376 | break; | |
377 | } | |
378 | ||
379 | if (r > 0 && | |
380 | (fds.revents & (POLLHUP|POLLRDHUP|POLLERR|POLLNVAL))) { | |
381 | if (fds.revents & (POLLHUP | POLLRDHUP)) { | |
382 | close(ptv->socket); | |
383 | ptv->afp_state = AFP_STATE_DOWN; | |
384 | continue; | |
ff6365dd | 385 | } else if (fds.revents & POLLERR) { |
e80b30c0 EL |
386 | char c; |
387 | /* Do a recv to get errno */ | |
388 | if (recv(ptv->socket, &c, sizeof c, MSG_PEEK) != -1) | |
389 | continue; /* what, no error? */ | |
390 | SCLogError(SC_ERR_AFP_READ, "Error reading data from socket: (%d" PRIu32 ") %s", | |
391 | errno, strerror(errno)); | |
392 | close(ptv->socket); | |
393 | ptv->afp_state = AFP_STATE_DOWN; | |
394 | continue; | |
ff6365dd | 395 | } else if (fds.revents & POLLNVAL) { |
e80b30c0 EL |
396 | SCLogError(SC_ERR_AFP_READ, "Invalid polling request"); |
397 | close(ptv->socket); | |
398 | ptv->afp_state = AFP_STATE_DOWN; | |
399 | continue; | |
400 | } | |
401 | } else if (r > 0) { | |
402 | /* AFPRead will call TmThreadsSlotProcessPkt on read packets */ | |
ff6365dd | 403 | r = AFPRead(ptv); |
62e63e3f EL |
404 | switch (r) { |
405 | case AFP_READ_FAILURE: | |
406 | /* AFPRead in error: best to reset the socket */ | |
407 | SCLogError(SC_ERR_AFP_READ, "AFPRead error reading data from socket: (%d" PRIu32 ") %s", | |
408 | errno, strerror(errno)); | |
409 | close(ptv->socket); | |
410 | ptv->afp_state = AFP_STATE_DOWN; | |
411 | continue; | |
412 | case AFP_FAILURE: | |
413 | SCReturnInt(TM_ECODE_FAILED); | |
414 | break; | |
415 | case AFP_READ_OK: | |
416 | break; | |
e80b30c0 EL |
417 | } |
418 | } else if ((r < 0) && (errno != EINTR)) { | |
419 | SCLogError(SC_ERR_AFP_READ, "Error reading data from socket: (%d" PRIu32 ") %s", | |
420 | errno, strerror(errno)); | |
421 | close(ptv->socket); | |
422 | ptv->afp_state = AFP_STATE_DOWN; | |
423 | continue; | |
424 | } | |
d68f182e | 425 | SCPerfSyncCountersIfSignalled(tv, 0); |
e80b30c0 EL |
426 | } |
427 | ||
e80b30c0 EL |
428 | SCReturnInt(TM_ECODE_OK); |
429 | } | |
430 | ||
e80b30c0 | 431 | static int AFPGetIfnumByDev(int fd, const char *ifname, int verbose) |
c45d8985 EL |
432 | { |
433 | struct ifreq ifr; | |
434 | ||
435 | memset(&ifr, 0, sizeof(ifr)); | |
e80b30c0 | 436 | strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); |
c45d8985 EL |
437 | |
438 | if (ioctl(fd, SIOCGIFINDEX, &ifr) == -1) { | |
439 | if (verbose) | |
440 | SCLogError(SC_ERR_AFP_CREATE, "Unable to find iface %s: %s", | |
441 | ifname, strerror(errno)); | |
442 | return -1; | |
443 | } | |
444 | ||
445 | return ifr.ifr_ifindex; | |
446 | } | |
447 | ||
e80b30c0 | 448 | static int AFPGetDevLinktype(int fd, const char *ifname) |
c45d8985 EL |
449 | { |
450 | struct ifreq ifr; | |
451 | ||
452 | memset(&ifr, 0, sizeof(ifr)); | |
e80b30c0 | 453 | strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); |
c45d8985 EL |
454 | |
455 | if (ioctl(fd, SIOCGIFHWADDR, &ifr) == -1) { | |
456 | SCLogError(SC_ERR_AFP_CREATE, "Unable to find type for iface \"%s\": %s", | |
457 | ifname, strerror(errno)); | |
458 | return -1; | |
459 | } | |
460 | ||
e80b30c0 EL |
461 | switch (ifr.ifr_hwaddr.sa_family) { |
462 | case ARPHRD_LOOPBACK: | |
463 | return LINKTYPE_ETHERNET; | |
464 | case ARPHRD_PPP: | |
465 | return LINKTYPE_RAW; | |
466 | default: | |
467 | return ifr.ifr_hwaddr.sa_family; | |
468 | } | |
c45d8985 EL |
469 | } |
470 | ||
e80b30c0 | 471 | static int AFPCreateSocket(AFPThreadVars *ptv, char *devname, int verbose) |
c45d8985 EL |
472 | { |
473 | int r; | |
474 | struct packet_mreq sock_params; | |
475 | struct sockaddr_ll bind_address; | |
476 | /* open socket */ | |
477 | ptv->socket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); | |
478 | if (ptv->socket == -1) { | |
e80b30c0 | 479 | SCLogError(SC_ERR_AFP_CREATE, "Couldn't create a AF_PACKET socket, error %s", strerror(errno)); |
c45d8985 EL |
480 | return -1; |
481 | } | |
361bf221 | 482 | SCLogDebug("using interface %s", (char *)devname); |
c45d8985 EL |
483 | /* bind socket */ |
484 | memset(&bind_address, 0, sizeof(bind_address)); | |
485 | bind_address.sll_family = AF_PACKET; | |
486 | bind_address.sll_protocol = htons(ETH_P_ALL); | |
e80b30c0 | 487 | bind_address.sll_ifindex = AFPGetIfnumByDev(ptv->socket, devname, verbose); |
c45d8985 EL |
488 | if (bind_address.sll_ifindex == -1) { |
489 | if (verbose) | |
e80b30c0 | 490 | SCLogError(SC_ERR_AFP_CREATE, "Couldn't find iface %s", devname); |
c45d8985 EL |
491 | return -1; |
492 | } | |
493 | r = bind(ptv->socket, (struct sockaddr *)&bind_address, sizeof(bind_address)); | |
494 | if (r < 0) { | |
495 | if (verbose) { | |
496 | if (errno == ENETDOWN) { | |
497 | SCLogError(SC_ERR_AFP_CREATE, | |
498 | "Couldn't bind AF_PACKET socket, iface %s is down", | |
499 | devname); | |
500 | } else { | |
501 | SCLogError(SC_ERR_AFP_CREATE, | |
502 | "Couldn't bind AF_PACKET socket to iface %s, error %s", | |
503 | devname, | |
504 | strerror(errno)); | |
505 | } | |
506 | } | |
507 | close(ptv->socket); | |
508 | return -1; | |
509 | } | |
df7dbe36 EL |
510 | if (ptv->promisc != 0) { |
511 | /* Force promiscuous mode */ | |
512 | memset(&sock_params, 0, sizeof(sock_params)); | |
513 | sock_params.mr_type = PACKET_MR_PROMISC; | |
514 | sock_params.mr_ifindex = bind_address.sll_ifindex; | |
515 | r = setsockopt(ptv->socket, SOL_PACKET, PACKET_ADD_MEMBERSHIP,(void *)&sock_params, sizeof(sock_params)); | |
516 | if (r < 0) { | |
517 | SCLogError(SC_ERR_AFP_CREATE, | |
518 | "Couldn't switch iface %s to promiscuous, error %s", | |
519 | devname, | |
520 | strerror(errno)); | |
521 | close(ptv->socket); | |
522 | return -1; | |
523 | } | |
c45d8985 | 524 | } |
f6ddaf33 | 525 | |
67f791e8 EL |
526 | if (ptv->detect_offload) { |
527 | int val = 1; | |
528 | if (setsockopt(ptv->socket, SOL_PACKET, PACKET_AUXDATA, &val, | |
529 | sizeof(val)) == -1 && errno != ENOPROTOOPT) { | |
530 | SCLogError(SC_ERR_AFP_CREATE, | |
531 | "Couldn't active auxdata on iface %s, error %s", | |
532 | devname, | |
533 | strerror(errno)); | |
534 | close(ptv->socket); | |
535 | return -1; | |
536 | } | |
f6ddaf33 | 537 | } |
f6ddaf33 | 538 | |
e80b30c0 EL |
539 | /* set socket recv buffer size */ |
540 | if (ptv->buffer_size != 0) { | |
541 | /* | |
542 | * Set the socket buffer size to the specified value. | |
543 | */ | |
544 | SCLogInfo("Setting AF_PACKET socket buffer to %d", ptv->buffer_size); | |
545 | if (setsockopt(ptv->socket, SOL_SOCKET, SO_RCVBUF, | |
546 | &ptv->buffer_size, | |
547 | sizeof(ptv->buffer_size)) == -1) { | |
548 | SCLogError(SC_ERR_AFP_CREATE, | |
549 | "Couldn't set buffer size to %d on iface %s, error %s", | |
550 | ptv->buffer_size, | |
551 | devname, | |
552 | strerror(errno)); | |
553 | close(ptv->socket); | |
554 | return -1; | |
555 | } | |
556 | } | |
557 | ||
c45d8985 EL |
558 | #ifdef HAVE_PACKET_FANOUT |
559 | /* add binded socket to fanout group */ | |
fbca1a4e | 560 | if (ptv->threads > 1) { |
c45d8985 | 561 | uint32_t option = 0; |
e80b30c0 EL |
562 | uint16_t mode = ptv->cluster_type; |
563 | uint16_t id = ptv->cluster_id; | |
c45d8985 EL |
564 | option = (mode << 16) | (id & 0xffff); |
565 | r = setsockopt(ptv->socket, SOL_PACKET, PACKET_FANOUT,(void *)&option, sizeof(option)); | |
566 | if (r < 0) { | |
567 | SCLogError(SC_ERR_AFP_CREATE, | |
568 | "Coudn't set fanout mode, error %s", | |
569 | strerror(errno)); | |
e80b30c0 | 570 | close(ptv->socket); |
c45d8985 EL |
571 | return -1; |
572 | } | |
573 | } | |
574 | #endif | |
575 | ||
576 | ptv->afp_state = AFP_STATE_UP; | |
577 | return 0; | |
578 | } | |
579 | ||
580 | ||
581 | /** | |
582 | * \brief Init function for ReceiveAFP. | |
583 | * | |
584 | * \param tv pointer to ThreadVars | |
585 | * \param initdata pointer to the interface passed from the user | |
586 | * \param data pointer gets populated with AFPThreadVars | |
587 | * | |
588 | * \todo Create a general AFP setup function. | |
589 | */ | |
590 | TmEcode ReceiveAFPThreadInit(ThreadVars *tv, void *initdata, void **data) { | |
591 | SCEnter(); | |
592 | int r; | |
fbca1a4e | 593 | AFPIfaceConfig *afpconfig = initdata; |
c45d8985 | 594 | |
c45d8985 EL |
595 | if (initdata == NULL) { |
596 | SCLogError(SC_ERR_INVALID_ARGUMENT, "initdata == NULL"); | |
597 | SCReturnInt(TM_ECODE_FAILED); | |
598 | } | |
599 | ||
600 | AFPThreadVars *ptv = SCMalloc(sizeof(AFPThreadVars)); | |
45d5c3ca EL |
601 | if (ptv == NULL) { |
602 | afpconfig->DerefFunc(afpconfig); | |
c45d8985 | 603 | SCReturnInt(TM_ECODE_FAILED); |
45d5c3ca | 604 | } |
c45d8985 EL |
605 | memset(ptv, 0, sizeof(AFPThreadVars)); |
606 | ||
607 | ptv->tv = tv; | |
608 | ptv->cooked = 0; | |
609 | ||
fbca1a4e | 610 | strlcpy(ptv->iface, afpconfig->iface, AFP_IFACE_NAME_LENGTH); |
c45d8985 EL |
611 | ptv->iface[AFP_IFACE_NAME_LENGTH - 1]= '\0'; |
612 | ||
fbca1a4e | 613 | ptv->buffer_size = afpconfig->buffer_size; |
e80b30c0 | 614 | |
df7dbe36 | 615 | ptv->promisc = afpconfig->promisc; |
67f791e8 | 616 | ptv->detect_offload = afpconfig->detect_offload; |
df7dbe36 | 617 | |
fbca1a4e | 618 | ptv->threads = 1; |
e80b30c0 EL |
619 | #ifdef HAVE_PACKET_FANOUT |
620 | ptv->cluster_type = PACKET_FANOUT_LB; | |
621 | ptv->cluster_id = 1; | |
622 | /* We only set cluster info if the number of reader threads is greater than 1 */ | |
fbca1a4e EL |
623 | if (afpconfig->threads > 1) { |
624 | ptv->cluster_id = afpconfig->cluster_id; | |
625 | ptv->cluster_type = afpconfig->cluster_type; | |
626 | ptv->threads = afpconfig->threads; | |
e80b30c0 EL |
627 | } |
628 | #endif | |
629 | ||
fbca1a4e | 630 | r = AFPCreateSocket(ptv, ptv->iface, 1); |
c45d8985 | 631 | if (r < 0) { |
e80b30c0 | 632 | SCLogError(SC_ERR_AFP_CREATE, "Couldn't init AF_PACKET socket"); |
c45d8985 | 633 | SCFree(ptv); |
45d5c3ca | 634 | afpconfig->DerefFunc(afpconfig); |
c45d8985 EL |
635 | SCReturnInt(TM_ECODE_FAILED); |
636 | } | |
637 | ||
e80b30c0 | 638 | ptv->datalink = AFPGetDevLinktype(ptv->socket, ptv->iface); |
c45d8985 EL |
639 | switch (ptv->datalink) { |
640 | case ARPHRD_PPP: | |
641 | case ARPHRD_ATM: | |
642 | ptv->cooked = 1; | |
643 | } | |
644 | ||
e80b30c0 EL |
645 | #define T_DATA_SIZE 70000 |
646 | ptv->data = SCMalloc(T_DATA_SIZE); | |
647 | if (ptv->data == NULL) { | |
45d5c3ca | 648 | afpconfig->DerefFunc(afpconfig); |
e80b30c0 | 649 | SCReturnInt(TM_ECODE_FAILED); |
c45d8985 | 650 | } |
e80b30c0 EL |
651 | ptv->datalen = T_DATA_SIZE; |
652 | #undef T_DATA_SIZE | |
653 | ||
c45d8985 EL |
654 | |
655 | *data = (void *)ptv; | |
fbca1a4e | 656 | |
45d5c3ca | 657 | afpconfig->DerefFunc(afpconfig); |
c45d8985 EL |
658 | SCReturnInt(TM_ECODE_OK); |
659 | } | |
660 | ||
661 | /** | |
662 | * \brief This function prints stats to the screen at exit. | |
663 | * \param tv pointer to ThreadVars | |
664 | * \param data pointer that gets cast into AFPThreadVars for ptv | |
665 | */ | |
666 | void ReceiveAFPThreadExitStats(ThreadVars *tv, void *data) { | |
667 | SCEnter(); | |
668 | AFPThreadVars *ptv = (AFPThreadVars *)data; | |
9549faae EL |
669 | #ifdef PACKET_STATISTICS |
670 | struct tpacket_stats kstats; | |
671 | socklen_t len = sizeof (struct tpacket_stats); | |
672 | #endif | |
673 | ||
674 | #ifdef PACKET_STATISTICS | |
675 | if (getsockopt(ptv->socket, SOL_PACKET, PACKET_STATISTICS, | |
676 | &kstats, &len) > -1) { | |
677 | SCLogInfo("(%s) Kernel: Packets %" PRIu32 ", dropped %" PRIu32 "", | |
678 | tv->name, | |
679 | kstats.tp_packets, kstats.tp_drops); | |
680 | } | |
681 | #endif | |
e80b30c0 EL |
682 | |
683 | SCLogInfo("(%s) Packets %" PRIu32 ", bytes %" PRIu64 "", tv->name, ptv->pkts, ptv->bytes); | |
c45d8985 EL |
684 | } |
685 | ||
686 | /** | |
687 | * \brief DeInit function closes af packet socket at exit. | |
688 | * \param tv pointer to ThreadVars | |
689 | * \param data pointer that gets cast into AFPThreadVars for ptv | |
690 | */ | |
691 | TmEcode ReceiveAFPThreadDeinit(ThreadVars *tv, void *data) { | |
692 | AFPThreadVars *ptv = (AFPThreadVars *)data; | |
693 | ||
e80b30c0 EL |
694 | if (ptv->data != NULL) { |
695 | SCFree(ptv->data); | |
696 | ptv->data = NULL; | |
697 | } | |
698 | ptv->datalen = 0; | |
699 | ||
c45d8985 EL |
700 | close(ptv->socket); |
701 | SCReturnInt(TM_ECODE_OK); | |
702 | } | |
703 | ||
704 | /** | |
705 | * \brief This function passes off to link type decoders. | |
706 | * | |
707 | * DecodeAFP reads packets from the PacketQueue and passes | |
708 | * them off to the proper link type decoder. | |
709 | * | |
710 | * \param t pointer to ThreadVars | |
711 | * \param p pointer to the current packet | |
712 | * \param data pointer that gets cast into AFPThreadVars for ptv | |
713 | * \param pq pointer to the current PacketQueue | |
714 | */ | |
715 | TmEcode DecodeAFP(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq) | |
716 | { | |
717 | SCEnter(); | |
718 | DecodeThreadVars *dtv = (DecodeThreadVars *)data; | |
719 | ||
720 | /* update counters */ | |
721 | SCPerfCounterIncr(dtv->counter_pkts, tv->sc_perf_pca); | |
722 | SCPerfCounterIncr(dtv->counter_pkts_per_sec, tv->sc_perf_pca); | |
723 | ||
724 | SCPerfCounterAddUI64(dtv->counter_bytes, tv->sc_perf_pca, GET_PKT_LEN(p)); | |
725 | #if 0 | |
726 | SCPerfCounterAddDouble(dtv->counter_bytes_per_sec, tv->sc_perf_pca, GET_PKT_LEN(p)); | |
727 | SCPerfCounterAddDouble(dtv->counter_mbit_per_sec, tv->sc_perf_pca, | |
728 | (GET_PKT_LEN(p) * 8)/1000000.0); | |
729 | #endif | |
730 | ||
731 | SCPerfCounterAddUI64(dtv->counter_avg_pkt_size, tv->sc_perf_pca, GET_PKT_LEN(p)); | |
732 | SCPerfCounterSetUI64(dtv->counter_max_pkt_size, tv->sc_perf_pca, GET_PKT_LEN(p)); | |
733 | ||
734 | /* call the decoder */ | |
735 | switch(p->datalink) { | |
736 | case LINKTYPE_LINUX_SLL: | |
737 | DecodeSll(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq); | |
738 | break; | |
739 | case LINKTYPE_ETHERNET: | |
740 | DecodeEthernet(tv, dtv, p,GET_PKT_DATA(p), GET_PKT_LEN(p), pq); | |
741 | break; | |
742 | case LINKTYPE_PPP: | |
743 | DecodePPP(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq); | |
744 | break; | |
745 | case LINKTYPE_RAW: | |
746 | DecodeRaw(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq); | |
747 | break; | |
748 | default: | |
749 | SCLogError(SC_ERR_DATALINK_UNIMPLEMENTED, "Error: datalink type %" PRId32 " not yet supported in module DecodeAFP", p->datalink); | |
750 | break; | |
751 | } | |
752 | ||
753 | SCReturnInt(TM_ECODE_OK); | |
754 | } | |
755 | ||
756 | TmEcode DecodeAFPThreadInit(ThreadVars *tv, void *initdata, void **data) | |
757 | { | |
758 | SCEnter(); | |
759 | DecodeThreadVars *dtv = NULL; | |
760 | ||
761 | dtv = DecodeThreadVarsAlloc(); | |
762 | ||
763 | if (dtv == NULL) | |
764 | SCReturnInt(TM_ECODE_FAILED); | |
765 | ||
766 | DecodeRegisterPerfCounters(dtv, tv); | |
767 | ||
768 | *data = (void *)dtv; | |
769 | ||
770 | SCReturnInt(TM_ECODE_OK); | |
771 | } | |
772 | ||
e80b30c0 | 773 | #endif /* HAVE_AF_PACKET */ |
c45d8985 | 774 | /* eof */ |