]>
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; |
6062e00c | 163 | ChecksumValidationMode checksum_mode; |
e80b30c0 EL |
164 | |
165 | int cluster_id; | |
166 | int cluster_type; | |
c45d8985 | 167 | |
fbca1a4e EL |
168 | int threads; |
169 | ||
c45d8985 EL |
170 | } AFPThreadVars; |
171 | ||
172 | TmEcode ReceiveAFP(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *); | |
173 | TmEcode ReceiveAFPThreadInit(ThreadVars *, void *, void **); | |
174 | void ReceiveAFPThreadExitStats(ThreadVars *, void *); | |
175 | TmEcode ReceiveAFPThreadDeinit(ThreadVars *, void *); | |
e80b30c0 | 176 | TmEcode ReceiveAFPLoop(ThreadVars *tv, void *data, void *slot); |
c45d8985 EL |
177 | |
178 | TmEcode DecodeAFPThreadInit(ThreadVars *, void *, void **); | |
179 | TmEcode DecodeAFP(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *); | |
180 | ||
181 | /** | |
182 | * \brief Registration Function for RecieveAFP. | |
183 | * \todo Unit tests are needed for this module. | |
184 | */ | |
185 | void TmModuleReceiveAFPRegister (void) { | |
186 | tmm_modules[TMM_RECEIVEAFP].name = "ReceiveAFP"; | |
187 | tmm_modules[TMM_RECEIVEAFP].ThreadInit = ReceiveAFPThreadInit; | |
ff6365dd | 188 | tmm_modules[TMM_RECEIVEAFP].Func = NULL; |
e80b30c0 | 189 | tmm_modules[TMM_RECEIVEAFP].PktAcqLoop = ReceiveAFPLoop; |
c45d8985 EL |
190 | tmm_modules[TMM_RECEIVEAFP].ThreadExitPrintStats = ReceiveAFPThreadExitStats; |
191 | tmm_modules[TMM_RECEIVEAFP].ThreadDeinit = NULL; | |
192 | tmm_modules[TMM_RECEIVEAFP].RegisterTests = NULL; | |
193 | tmm_modules[TMM_RECEIVEAFP].cap_flags = SC_CAP_NET_RAW; | |
194 | } | |
195 | ||
196 | /** | |
197 | * \brief Registration Function for DecodeAFP. | |
198 | * \todo Unit tests are needed for this module. | |
199 | */ | |
200 | void TmModuleDecodeAFPRegister (void) { | |
201 | tmm_modules[TMM_DECODEAFP].name = "DecodeAFP"; | |
202 | tmm_modules[TMM_DECODEAFP].ThreadInit = DecodeAFPThreadInit; | |
203 | tmm_modules[TMM_DECODEAFP].Func = DecodeAFP; | |
204 | tmm_modules[TMM_DECODEAFP].ThreadExitPrintStats = NULL; | |
205 | tmm_modules[TMM_DECODEAFP].ThreadDeinit = NULL; | |
206 | tmm_modules[TMM_DECODEAFP].RegisterTests = NULL; | |
207 | tmm_modules[TMM_DECODEAFP].cap_flags = 0; | |
208 | } | |
209 | ||
e80b30c0 EL |
210 | static int AFPCreateSocket(AFPThreadVars *ptv, char *devname, int verbose); |
211 | ||
c45d8985 EL |
212 | |
213 | /** | |
214 | * \brief AF packet read function. | |
215 | * | |
216 | * This function fills | |
217 | * From here the packets are picked up by the DecodeAFP thread. | |
218 | * | |
219 | * \param user pointer to AFPThreadVars | |
220 | * \retval TM_ECODE_FAILED on failure and TM_ECODE_OK on success | |
221 | */ | |
62e63e3f | 222 | int AFPRead(AFPThreadVars *ptv) |
c45d8985 EL |
223 | { |
224 | Packet *p = NULL; | |
225 | /* XXX should try to use read that get directly to packet */ | |
c45d8985 EL |
226 | int offset = 0; |
227 | int caplen; | |
228 | struct sockaddr_ll from; | |
229 | struct iovec iov; | |
230 | struct msghdr msg; | |
c45d8985 EL |
231 | struct cmsghdr *cmsg; |
232 | union { | |
233 | struct cmsghdr cmsg; | |
234 | char buf[CMSG_SPACE(sizeof(struct tpacket_auxdata))]; | |
235 | } cmsg_buf; | |
c45d8985 EL |
236 | |
237 | msg.msg_name = &from; | |
238 | msg.msg_namelen = sizeof(from); | |
239 | msg.msg_iov = &iov; | |
240 | msg.msg_iovlen = 1; | |
c45d8985 EL |
241 | msg.msg_control = &cmsg_buf; |
242 | msg.msg_controllen = sizeof(cmsg_buf); | |
c45d8985 EL |
243 | msg.msg_flags = 0; |
244 | ||
245 | if (ptv->cooked) | |
246 | offset = SLL_HEADER_LEN; | |
247 | else | |
248 | offset = 0; | |
e80b30c0 EL |
249 | iov.iov_len = ptv->datalen - offset; |
250 | iov.iov_base = ptv->data + offset; | |
c45d8985 EL |
251 | |
252 | caplen = recvmsg(ptv->socket, &msg, MSG_TRUNC); | |
253 | ||
254 | if (caplen < 0) { | |
255 | SCLogWarning(SC_ERR_AFP_READ, "recvmsg failed with error code %" PRId32, | |
256 | errno); | |
62e63e3f | 257 | SCReturnInt(AFP_READ_FAILURE); |
c45d8985 | 258 | } |
ff6365dd EL |
259 | |
260 | p = PacketGetFromQueueOrAlloc(); | |
c45d8985 | 261 | if (p == NULL) { |
62e63e3f | 262 | SCReturnInt(AFP_FAILURE); |
c45d8985 EL |
263 | } |
264 | ||
265 | /* get timestamp of packet via ioctl */ | |
266 | if (ioctl(ptv->socket, SIOCGSTAMP, &p->ts) == -1) { | |
267 | SCLogWarning(SC_ERR_AFP_READ, "recvmsg failed with error code %" PRId32, | |
268 | errno); | |
269 | TmqhOutputPacketpool(ptv->tv, p); | |
62e63e3f | 270 | SCReturnInt(AFP_READ_FAILURE); |
c45d8985 EL |
271 | } |
272 | ||
273 | ptv->pkts++; | |
274 | ptv->bytes += caplen + offset; | |
275 | ||
276 | /* add forged header */ | |
277 | if (ptv->cooked) { | |
e80b30c0 | 278 | SllHdr * hdrp = (SllHdr *)ptv->data; |
c45d8985 EL |
279 | /* XXX this is minimalist, but this seems enough */ |
280 | hdrp->sll_protocol = from.sll_protocol; | |
281 | } | |
282 | ||
283 | p->datalink = ptv->datalink; | |
284 | SET_PKT_LEN(p, caplen + offset); | |
e80b30c0 | 285 | if (PacketCopyData(p, ptv->data, GET_PKT_LEN(p)) == -1) { |
c45d8985 | 286 | TmqhOutputPacketpool(ptv->tv, p); |
62e63e3f | 287 | SCReturnInt(AFP_FAILURE); |
c45d8985 | 288 | } |
e80b30c0 EL |
289 | SCLogDebug("pktlen: %" PRIu32 " (pkt %p, pkt data %p)", |
290 | GET_PKT_LEN(p), p, GET_PKT_DATA(p)); | |
291 | ||
6062e00c EL |
292 | /* We only check for checksum disable */ |
293 | if (ptv->checksum_mode == CHECKSUM_VALIDATION_DISABLE) { | |
294 | p->flags |= PKT_IGNORE_CHECKSUM; | |
295 | } else { | |
296 | /* List is NULL if we don't have activated auxiliary data */ | |
297 | for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { | |
298 | struct tpacket_auxdata *aux; | |
299 | ||
300 | if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct tpacket_auxdata)) || | |
301 | cmsg->cmsg_level != SOL_PACKET || | |
302 | cmsg->cmsg_type != PACKET_AUXDATA) | |
303 | continue; | |
f6ddaf33 | 304 | |
6062e00c | 305 | aux = (struct tpacket_auxdata *)CMSG_DATA(cmsg); |
f6ddaf33 | 306 | |
6062e00c EL |
307 | if (aux->tp_status & TP_STATUS_CSUMNOTREADY) { |
308 | p->flags |= PKT_IGNORE_CHECKSUM; | |
309 | } | |
310 | break; | |
f6ddaf33 EL |
311 | } |
312 | } | |
313 | ||
6062e00c | 314 | |
c469824b EL |
315 | if (TmThreadsSlotProcessPkt(ptv->tv, ptv->slot, p) != TM_ECODE_OK) { |
316 | TmqhOutputPacketpool(ptv->tv, p); | |
62e63e3f | 317 | SCReturnInt(AFP_FAILURE); |
c469824b | 318 | } |
62e63e3f | 319 | SCReturnInt(AFP_READ_OK); |
c45d8985 EL |
320 | } |
321 | ||
322 | static int AFPTryReopen(AFPThreadVars *ptv) | |
323 | { | |
324 | int afp_activate_r; | |
325 | ||
326 | ptv->afp_state = AFP_STATE_DOWN; | |
327 | ||
e80b30c0 | 328 | afp_activate_r = AFPCreateSocket(ptv, ptv->iface, 0); |
c45d8985 EL |
329 | if (afp_activate_r != 0) { |
330 | return afp_activate_r; | |
331 | } | |
332 | ||
333 | SCLogInfo("Recovering interface listening"); | |
334 | ptv->afp_state = AFP_STATE_UP; | |
335 | return 0; | |
336 | } | |
337 | ||
e80b30c0 EL |
338 | /** |
339 | * \brief Main AF_PACKET reading Loop function | |
340 | */ | |
341 | TmEcode ReceiveAFPLoop(ThreadVars *tv, void *data, void *slot) | |
342 | { | |
343 | uint16_t packet_q_len = 0; | |
344 | AFPThreadVars *ptv = (AFPThreadVars *)data; | |
345 | TmSlot *s = (TmSlot *)slot; | |
346 | ptv->slot = s->slot_next; | |
347 | struct pollfd fds; | |
348 | int r; | |
349 | ||
350 | SCEnter(); | |
351 | ||
352 | fds.fd = ptv->socket; | |
353 | fds.events = POLLIN; | |
354 | ||
355 | while (1) { | |
356 | /* Start by checking the state of our interface */ | |
357 | if (unlikely(ptv->afp_state == AFP_STATE_DOWN)) { | |
358 | int dbreak = 0; | |
359 | do { | |
360 | usleep(AFP_RECONNECT_TIMEOUT); | |
361 | if (suricata_ctl_flags != 0) { | |
362 | dbreak = 1; | |
363 | break; | |
364 | } | |
365 | r = AFPTryReopen(ptv); | |
366 | } while (r < 0); | |
367 | if (dbreak == 1) | |
368 | break; | |
369 | } | |
370 | ||
371 | /* make sure we have at least one packet in the packet pool, to prevent | |
372 | * us from alloc'ing packets at line rate */ | |
373 | do { | |
374 | packet_q_len = PacketPoolSize(); | |
375 | if (unlikely(packet_q_len == 0)) { | |
376 | PacketPoolWait(); | |
377 | } | |
378 | } while (packet_q_len == 0); | |
379 | ||
380 | r = poll(&fds, 1, POLL_TIMEOUT); | |
381 | ||
382 | if (suricata_ctl_flags != 0) { | |
383 | break; | |
384 | } | |
385 | ||
386 | if (r > 0 && | |
387 | (fds.revents & (POLLHUP|POLLRDHUP|POLLERR|POLLNVAL))) { | |
388 | if (fds.revents & (POLLHUP | POLLRDHUP)) { | |
389 | close(ptv->socket); | |
390 | ptv->afp_state = AFP_STATE_DOWN; | |
391 | continue; | |
ff6365dd | 392 | } else if (fds.revents & POLLERR) { |
e80b30c0 EL |
393 | char c; |
394 | /* Do a recv to get errno */ | |
395 | if (recv(ptv->socket, &c, sizeof c, MSG_PEEK) != -1) | |
396 | continue; /* what, no error? */ | |
397 | SCLogError(SC_ERR_AFP_READ, "Error reading data from socket: (%d" PRIu32 ") %s", | |
398 | errno, strerror(errno)); | |
399 | close(ptv->socket); | |
400 | ptv->afp_state = AFP_STATE_DOWN; | |
401 | continue; | |
ff6365dd | 402 | } else if (fds.revents & POLLNVAL) { |
e80b30c0 EL |
403 | SCLogError(SC_ERR_AFP_READ, "Invalid polling request"); |
404 | close(ptv->socket); | |
405 | ptv->afp_state = AFP_STATE_DOWN; | |
406 | continue; | |
407 | } | |
408 | } else if (r > 0) { | |
409 | /* AFPRead will call TmThreadsSlotProcessPkt on read packets */ | |
ff6365dd | 410 | r = AFPRead(ptv); |
62e63e3f EL |
411 | switch (r) { |
412 | case AFP_READ_FAILURE: | |
413 | /* AFPRead in error: best to reset the socket */ | |
414 | SCLogError(SC_ERR_AFP_READ, "AFPRead error reading data from socket: (%d" PRIu32 ") %s", | |
415 | errno, strerror(errno)); | |
416 | close(ptv->socket); | |
417 | ptv->afp_state = AFP_STATE_DOWN; | |
418 | continue; | |
419 | case AFP_FAILURE: | |
420 | SCReturnInt(TM_ECODE_FAILED); | |
421 | break; | |
422 | case AFP_READ_OK: | |
423 | break; | |
e80b30c0 EL |
424 | } |
425 | } else if ((r < 0) && (errno != EINTR)) { | |
426 | SCLogError(SC_ERR_AFP_READ, "Error reading data from socket: (%d" PRIu32 ") %s", | |
427 | errno, strerror(errno)); | |
428 | close(ptv->socket); | |
429 | ptv->afp_state = AFP_STATE_DOWN; | |
430 | continue; | |
431 | } | |
d68f182e | 432 | SCPerfSyncCountersIfSignalled(tv, 0); |
e80b30c0 EL |
433 | } |
434 | ||
e80b30c0 EL |
435 | SCReturnInt(TM_ECODE_OK); |
436 | } | |
437 | ||
e80b30c0 | 438 | static int AFPGetIfnumByDev(int fd, const char *ifname, int verbose) |
c45d8985 EL |
439 | { |
440 | struct ifreq ifr; | |
441 | ||
442 | memset(&ifr, 0, sizeof(ifr)); | |
e80b30c0 | 443 | strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); |
c45d8985 EL |
444 | |
445 | if (ioctl(fd, SIOCGIFINDEX, &ifr) == -1) { | |
446 | if (verbose) | |
447 | SCLogError(SC_ERR_AFP_CREATE, "Unable to find iface %s: %s", | |
448 | ifname, strerror(errno)); | |
449 | return -1; | |
450 | } | |
451 | ||
452 | return ifr.ifr_ifindex; | |
453 | } | |
454 | ||
e80b30c0 | 455 | static int AFPGetDevLinktype(int fd, const char *ifname) |
c45d8985 EL |
456 | { |
457 | struct ifreq ifr; | |
458 | ||
459 | memset(&ifr, 0, sizeof(ifr)); | |
e80b30c0 | 460 | strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); |
c45d8985 EL |
461 | |
462 | if (ioctl(fd, SIOCGIFHWADDR, &ifr) == -1) { | |
463 | SCLogError(SC_ERR_AFP_CREATE, "Unable to find type for iface \"%s\": %s", | |
464 | ifname, strerror(errno)); | |
465 | return -1; | |
466 | } | |
467 | ||
e80b30c0 EL |
468 | switch (ifr.ifr_hwaddr.sa_family) { |
469 | case ARPHRD_LOOPBACK: | |
470 | return LINKTYPE_ETHERNET; | |
471 | case ARPHRD_PPP: | |
472 | return LINKTYPE_RAW; | |
473 | default: | |
474 | return ifr.ifr_hwaddr.sa_family; | |
475 | } | |
c45d8985 EL |
476 | } |
477 | ||
e80b30c0 | 478 | static int AFPCreateSocket(AFPThreadVars *ptv, char *devname, int verbose) |
c45d8985 EL |
479 | { |
480 | int r; | |
481 | struct packet_mreq sock_params; | |
482 | struct sockaddr_ll bind_address; | |
483 | /* open socket */ | |
484 | ptv->socket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); | |
485 | if (ptv->socket == -1) { | |
e80b30c0 | 486 | SCLogError(SC_ERR_AFP_CREATE, "Couldn't create a AF_PACKET socket, error %s", strerror(errno)); |
c45d8985 EL |
487 | return -1; |
488 | } | |
361bf221 | 489 | SCLogDebug("using interface %s", (char *)devname); |
c45d8985 EL |
490 | /* bind socket */ |
491 | memset(&bind_address, 0, sizeof(bind_address)); | |
492 | bind_address.sll_family = AF_PACKET; | |
493 | bind_address.sll_protocol = htons(ETH_P_ALL); | |
e80b30c0 | 494 | bind_address.sll_ifindex = AFPGetIfnumByDev(ptv->socket, devname, verbose); |
c45d8985 EL |
495 | if (bind_address.sll_ifindex == -1) { |
496 | if (verbose) | |
e80b30c0 | 497 | SCLogError(SC_ERR_AFP_CREATE, "Couldn't find iface %s", devname); |
c45d8985 EL |
498 | return -1; |
499 | } | |
500 | r = bind(ptv->socket, (struct sockaddr *)&bind_address, sizeof(bind_address)); | |
501 | if (r < 0) { | |
502 | if (verbose) { | |
503 | if (errno == ENETDOWN) { | |
504 | SCLogError(SC_ERR_AFP_CREATE, | |
505 | "Couldn't bind AF_PACKET socket, iface %s is down", | |
506 | devname); | |
507 | } else { | |
508 | SCLogError(SC_ERR_AFP_CREATE, | |
509 | "Couldn't bind AF_PACKET socket to iface %s, error %s", | |
510 | devname, | |
511 | strerror(errno)); | |
512 | } | |
513 | } | |
514 | close(ptv->socket); | |
515 | return -1; | |
516 | } | |
df7dbe36 EL |
517 | if (ptv->promisc != 0) { |
518 | /* Force promiscuous mode */ | |
519 | memset(&sock_params, 0, sizeof(sock_params)); | |
520 | sock_params.mr_type = PACKET_MR_PROMISC; | |
521 | sock_params.mr_ifindex = bind_address.sll_ifindex; | |
522 | r = setsockopt(ptv->socket, SOL_PACKET, PACKET_ADD_MEMBERSHIP,(void *)&sock_params, sizeof(sock_params)); | |
523 | if (r < 0) { | |
524 | SCLogError(SC_ERR_AFP_CREATE, | |
525 | "Couldn't switch iface %s to promiscuous, error %s", | |
526 | devname, | |
527 | strerror(errno)); | |
528 | close(ptv->socket); | |
529 | return -1; | |
530 | } | |
c45d8985 | 531 | } |
f6ddaf33 | 532 | |
6062e00c | 533 | if (ptv->checksum_mode == CHECKSUM_VALIDATION_KERNEL) { |
67f791e8 EL |
534 | int val = 1; |
535 | if (setsockopt(ptv->socket, SOL_PACKET, PACKET_AUXDATA, &val, | |
536 | sizeof(val)) == -1 && errno != ENOPROTOOPT) { | |
537 | SCLogError(SC_ERR_AFP_CREATE, | |
538 | "Couldn't active auxdata on iface %s, error %s", | |
539 | devname, | |
540 | strerror(errno)); | |
541 | close(ptv->socket); | |
542 | return -1; | |
543 | } | |
f6ddaf33 | 544 | } |
f6ddaf33 | 545 | |
e80b30c0 EL |
546 | /* set socket recv buffer size */ |
547 | if (ptv->buffer_size != 0) { | |
548 | /* | |
549 | * Set the socket buffer size to the specified value. | |
550 | */ | |
551 | SCLogInfo("Setting AF_PACKET socket buffer to %d", ptv->buffer_size); | |
552 | if (setsockopt(ptv->socket, SOL_SOCKET, SO_RCVBUF, | |
553 | &ptv->buffer_size, | |
554 | sizeof(ptv->buffer_size)) == -1) { | |
555 | SCLogError(SC_ERR_AFP_CREATE, | |
556 | "Couldn't set buffer size to %d on iface %s, error %s", | |
557 | ptv->buffer_size, | |
558 | devname, | |
559 | strerror(errno)); | |
560 | close(ptv->socket); | |
561 | return -1; | |
562 | } | |
563 | } | |
564 | ||
c45d8985 EL |
565 | #ifdef HAVE_PACKET_FANOUT |
566 | /* add binded socket to fanout group */ | |
fbca1a4e | 567 | if (ptv->threads > 1) { |
c45d8985 | 568 | uint32_t option = 0; |
e80b30c0 EL |
569 | uint16_t mode = ptv->cluster_type; |
570 | uint16_t id = ptv->cluster_id; | |
c45d8985 EL |
571 | option = (mode << 16) | (id & 0xffff); |
572 | r = setsockopt(ptv->socket, SOL_PACKET, PACKET_FANOUT,(void *)&option, sizeof(option)); | |
573 | if (r < 0) { | |
574 | SCLogError(SC_ERR_AFP_CREATE, | |
575 | "Coudn't set fanout mode, error %s", | |
576 | strerror(errno)); | |
e80b30c0 | 577 | close(ptv->socket); |
c45d8985 EL |
578 | return -1; |
579 | } | |
580 | } | |
581 | #endif | |
582 | ||
583 | ptv->afp_state = AFP_STATE_UP; | |
584 | return 0; | |
585 | } | |
586 | ||
587 | ||
588 | /** | |
589 | * \brief Init function for ReceiveAFP. | |
590 | * | |
591 | * \param tv pointer to ThreadVars | |
592 | * \param initdata pointer to the interface passed from the user | |
593 | * \param data pointer gets populated with AFPThreadVars | |
594 | * | |
595 | * \todo Create a general AFP setup function. | |
596 | */ | |
597 | TmEcode ReceiveAFPThreadInit(ThreadVars *tv, void *initdata, void **data) { | |
598 | SCEnter(); | |
599 | int r; | |
fbca1a4e | 600 | AFPIfaceConfig *afpconfig = initdata; |
c45d8985 | 601 | |
c45d8985 EL |
602 | if (initdata == NULL) { |
603 | SCLogError(SC_ERR_INVALID_ARGUMENT, "initdata == NULL"); | |
604 | SCReturnInt(TM_ECODE_FAILED); | |
605 | } | |
606 | ||
607 | AFPThreadVars *ptv = SCMalloc(sizeof(AFPThreadVars)); | |
45d5c3ca EL |
608 | if (ptv == NULL) { |
609 | afpconfig->DerefFunc(afpconfig); | |
c45d8985 | 610 | SCReturnInt(TM_ECODE_FAILED); |
45d5c3ca | 611 | } |
c45d8985 EL |
612 | memset(ptv, 0, sizeof(AFPThreadVars)); |
613 | ||
614 | ptv->tv = tv; | |
615 | ptv->cooked = 0; | |
616 | ||
fbca1a4e | 617 | strlcpy(ptv->iface, afpconfig->iface, AFP_IFACE_NAME_LENGTH); |
c45d8985 EL |
618 | ptv->iface[AFP_IFACE_NAME_LENGTH - 1]= '\0'; |
619 | ||
fbca1a4e | 620 | ptv->buffer_size = afpconfig->buffer_size; |
e80b30c0 | 621 | |
df7dbe36 | 622 | ptv->promisc = afpconfig->promisc; |
6062e00c | 623 | ptv->checksum_mode = afpconfig->checksum_mode; |
df7dbe36 | 624 | |
fbca1a4e | 625 | ptv->threads = 1; |
e80b30c0 EL |
626 | #ifdef HAVE_PACKET_FANOUT |
627 | ptv->cluster_type = PACKET_FANOUT_LB; | |
628 | ptv->cluster_id = 1; | |
629 | /* We only set cluster info if the number of reader threads is greater than 1 */ | |
fbca1a4e EL |
630 | if (afpconfig->threads > 1) { |
631 | ptv->cluster_id = afpconfig->cluster_id; | |
632 | ptv->cluster_type = afpconfig->cluster_type; | |
633 | ptv->threads = afpconfig->threads; | |
e80b30c0 EL |
634 | } |
635 | #endif | |
636 | ||
fbca1a4e | 637 | r = AFPCreateSocket(ptv, ptv->iface, 1); |
c45d8985 | 638 | if (r < 0) { |
e80b30c0 | 639 | SCLogError(SC_ERR_AFP_CREATE, "Couldn't init AF_PACKET socket"); |
c45d8985 | 640 | SCFree(ptv); |
45d5c3ca | 641 | afpconfig->DerefFunc(afpconfig); |
c45d8985 EL |
642 | SCReturnInt(TM_ECODE_FAILED); |
643 | } | |
644 | ||
e80b30c0 | 645 | ptv->datalink = AFPGetDevLinktype(ptv->socket, ptv->iface); |
c45d8985 EL |
646 | switch (ptv->datalink) { |
647 | case ARPHRD_PPP: | |
648 | case ARPHRD_ATM: | |
649 | ptv->cooked = 1; | |
650 | } | |
651 | ||
e80b30c0 EL |
652 | #define T_DATA_SIZE 70000 |
653 | ptv->data = SCMalloc(T_DATA_SIZE); | |
654 | if (ptv->data == NULL) { | |
45d5c3ca | 655 | afpconfig->DerefFunc(afpconfig); |
e80b30c0 | 656 | SCReturnInt(TM_ECODE_FAILED); |
c45d8985 | 657 | } |
e80b30c0 EL |
658 | ptv->datalen = T_DATA_SIZE; |
659 | #undef T_DATA_SIZE | |
660 | ||
c45d8985 EL |
661 | |
662 | *data = (void *)ptv; | |
fbca1a4e | 663 | |
45d5c3ca | 664 | afpconfig->DerefFunc(afpconfig); |
c45d8985 EL |
665 | SCReturnInt(TM_ECODE_OK); |
666 | } | |
667 | ||
668 | /** | |
669 | * \brief This function prints stats to the screen at exit. | |
670 | * \param tv pointer to ThreadVars | |
671 | * \param data pointer that gets cast into AFPThreadVars for ptv | |
672 | */ | |
673 | void ReceiveAFPThreadExitStats(ThreadVars *tv, void *data) { | |
674 | SCEnter(); | |
675 | AFPThreadVars *ptv = (AFPThreadVars *)data; | |
9549faae EL |
676 | #ifdef PACKET_STATISTICS |
677 | struct tpacket_stats kstats; | |
678 | socklen_t len = sizeof (struct tpacket_stats); | |
679 | #endif | |
680 | ||
681 | #ifdef PACKET_STATISTICS | |
682 | if (getsockopt(ptv->socket, SOL_PACKET, PACKET_STATISTICS, | |
683 | &kstats, &len) > -1) { | |
684 | SCLogInfo("(%s) Kernel: Packets %" PRIu32 ", dropped %" PRIu32 "", | |
685 | tv->name, | |
686 | kstats.tp_packets, kstats.tp_drops); | |
687 | } | |
688 | #endif | |
e80b30c0 EL |
689 | |
690 | SCLogInfo("(%s) Packets %" PRIu32 ", bytes %" PRIu64 "", tv->name, ptv->pkts, ptv->bytes); | |
c45d8985 EL |
691 | } |
692 | ||
693 | /** | |
694 | * \brief DeInit function closes af packet socket at exit. | |
695 | * \param tv pointer to ThreadVars | |
696 | * \param data pointer that gets cast into AFPThreadVars for ptv | |
697 | */ | |
698 | TmEcode ReceiveAFPThreadDeinit(ThreadVars *tv, void *data) { | |
699 | AFPThreadVars *ptv = (AFPThreadVars *)data; | |
700 | ||
e80b30c0 EL |
701 | if (ptv->data != NULL) { |
702 | SCFree(ptv->data); | |
703 | ptv->data = NULL; | |
704 | } | |
705 | ptv->datalen = 0; | |
706 | ||
c45d8985 EL |
707 | close(ptv->socket); |
708 | SCReturnInt(TM_ECODE_OK); | |
709 | } | |
710 | ||
711 | /** | |
712 | * \brief This function passes off to link type decoders. | |
713 | * | |
714 | * DecodeAFP reads packets from the PacketQueue and passes | |
715 | * them off to the proper link type decoder. | |
716 | * | |
717 | * \param t pointer to ThreadVars | |
718 | * \param p pointer to the current packet | |
719 | * \param data pointer that gets cast into AFPThreadVars for ptv | |
720 | * \param pq pointer to the current PacketQueue | |
721 | */ | |
722 | TmEcode DecodeAFP(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq) | |
723 | { | |
724 | SCEnter(); | |
725 | DecodeThreadVars *dtv = (DecodeThreadVars *)data; | |
726 | ||
727 | /* update counters */ | |
728 | SCPerfCounterIncr(dtv->counter_pkts, tv->sc_perf_pca); | |
729 | SCPerfCounterIncr(dtv->counter_pkts_per_sec, tv->sc_perf_pca); | |
730 | ||
731 | SCPerfCounterAddUI64(dtv->counter_bytes, tv->sc_perf_pca, GET_PKT_LEN(p)); | |
732 | #if 0 | |
733 | SCPerfCounterAddDouble(dtv->counter_bytes_per_sec, tv->sc_perf_pca, GET_PKT_LEN(p)); | |
734 | SCPerfCounterAddDouble(dtv->counter_mbit_per_sec, tv->sc_perf_pca, | |
735 | (GET_PKT_LEN(p) * 8)/1000000.0); | |
736 | #endif | |
737 | ||
738 | SCPerfCounterAddUI64(dtv->counter_avg_pkt_size, tv->sc_perf_pca, GET_PKT_LEN(p)); | |
739 | SCPerfCounterSetUI64(dtv->counter_max_pkt_size, tv->sc_perf_pca, GET_PKT_LEN(p)); | |
740 | ||
741 | /* call the decoder */ | |
742 | switch(p->datalink) { | |
743 | case LINKTYPE_LINUX_SLL: | |
744 | DecodeSll(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq); | |
745 | break; | |
746 | case LINKTYPE_ETHERNET: | |
747 | DecodeEthernet(tv, dtv, p,GET_PKT_DATA(p), GET_PKT_LEN(p), pq); | |
748 | break; | |
749 | case LINKTYPE_PPP: | |
750 | DecodePPP(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq); | |
751 | break; | |
752 | case LINKTYPE_RAW: | |
753 | DecodeRaw(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq); | |
754 | break; | |
755 | default: | |
756 | SCLogError(SC_ERR_DATALINK_UNIMPLEMENTED, "Error: datalink type %" PRId32 " not yet supported in module DecodeAFP", p->datalink); | |
757 | break; | |
758 | } | |
759 | ||
760 | SCReturnInt(TM_ECODE_OK); | |
761 | } | |
762 | ||
763 | TmEcode DecodeAFPThreadInit(ThreadVars *tv, void *initdata, void **data) | |
764 | { | |
765 | SCEnter(); | |
766 | DecodeThreadVars *dtv = NULL; | |
767 | ||
768 | dtv = DecodeThreadVarsAlloc(); | |
769 | ||
770 | if (dtv == NULL) | |
771 | SCReturnInt(TM_ECODE_FAILED); | |
772 | ||
773 | DecodeRegisterPerfCounters(dtv, tv); | |
774 | ||
775 | *data = (void *)dtv; | |
776 | ||
777 | SCReturnInt(TM_ECODE_OK); | |
778 | } | |
779 | ||
e80b30c0 | 780 | #endif /* HAVE_AF_PACKET */ |
c45d8985 | 781 | /* eof */ |