1 /*
2 ** Copyright (C) 2014-2015 Cisco and/or its affiliates. All rights reserved.
3 ** Copyright (C) 2010-2013 Sourcefire, Inc.
4 ** Author: Michael R. Altizer <mialtize@cisco.com>
5 **
6 ** This program is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU General Public License Version 2 as
8 ** published by the Free Software Foundation. You may not use, modify or
9 ** distribute this program under any other version of the GNU General
10 ** Public License.
11 **
12 ** This program is distributed in the hope that it will be useful,
13 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ** GNU General Public License for more details.
16 **
17 ** You should have received a copy of the GNU General Public License
18 ** along with this program; if not, write to the Free Software
19 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <errno.h>
27 #include <limits.h>
28 #include <linux/if_ether.h>
29 #include <linux/if_packet.h>
30 #include <net/if.h>
31 #include <net/if_arp.h>
32 #include <netinet/in.h>
33 #include <stdbool.h>
34 #include <stdint.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sys/ioctl.h>
39 #include <sys/mman.h>
40 #include <sys/poll.h>
41 #include <sys/socket.h>
42 #include <unistd.h>
43
44 #include "daq_api.h"
45 #include "sfbpf.h"
46
47 #define DAQ_AFPACKET_VERSION 6
48
49 #define AF_PACKET_DEFAULT_BUFFER_SIZE 128
50 #define AF_PACKET_MAX_INTERFACES 32
51
52 union thdr
53 {
54 struct tpacket2_hdr *h2;
55 uint8_t *raw;
56 };
57
58 typedef struct _af_packet_entry
59 {
60 struct _af_packet_entry *next;
61 union thdr hdr;
62 } AFPacketEntry;
63
64 typedef struct _af_packet_ring
65 {
66 struct tpacket_req layout;
67 unsigned int size;
68 void *start;
69 AFPacketEntry *entries;
70 AFPacketEntry *cursor;
71 } AFPacketRing;
72
73 typedef struct _af_packet_instance
74 {
75 struct _af_packet_instance *next;
76 int fd;
77 unsigned tp_version;
78 unsigned tp_hdrlen;
79 void *buffer;
80 AFPacketRing rx_ring;
81 AFPacketRing tx_ring;
82 char *name;
83 int index;
84 struct _af_packet_instance *peer;
85 struct sockaddr_ll sll;
86 } AFPacketInstance;
87
88 #ifdef PACKET_FANOUT
89 typedef struct _af_packet_fanout_cfg
90 {
91 uint16_t fanout_flags;
92 uint16_t fanout_type;
93 bool enabled;
94 } AFPacketFanoutCfg;
95 #endif
96
97 typedef struct _afpacket_context
98 {
99 char *device;
100 char *filter;
101 int snaplen;
102 int timeout;
103 uint32_t size;
104 int debug;
105 AFPacketInstance *instances;
106 uint32_t intf_count;
107 struct sfbpf_program fcode;
108 volatile int break_loop;
109 DAQ_Stats_t stats;
110 DAQ_State state;
111 char errbuf[256];
112 #ifdef PACKET_FANOUT
113 AFPacketFanoutCfg fanout_cfg;
114 #endif
115 } AFPacket_Context_t;
116
117 /* VLAN defintions stolen from LibPCAP's vlan.h. */
118 struct vlan_tag {
119 u_int16_t vlan_tpid; /* ETH_P_8021Q */
120 u_int16_t vlan_tci; /* VLAN TCI */
121 };
122 #define VLAN_TAG_LEN 4
123
124 static const int vlan_offset = 2 * ETH_ALEN;
125
bind_instance_interface(AFPacket_Context_t * afpc,AFPacketInstance * instance)126 static int bind_instance_interface(AFPacket_Context_t *afpc, AFPacketInstance *instance)
127 {
128 struct sockaddr_ll sll;
129 int err;
130 socklen_t errlen = sizeof(err);
131
132 /* Bind to the specified device so we only see packets from it. */
133 memset(&sll, 0, sizeof(struct sockaddr_ll));
134 sll.sll_family = AF_PACKET;
135 sll.sll_ifindex = instance->index;
136 sll.sll_protocol = htons(ETH_P_ALL);
137
138 if (bind(instance->fd, (struct sockaddr *) &sll, sizeof(sll)) == -1)
139 {
140 DPE(afpc->errbuf, "%s: bind(%s): %s\n", __func__, instance->name, strerror(errno));
141 return DAQ_ERROR;
142 }
143
144 /* Any pending errors, e.g., network is down? */
145 if (getsockopt(instance->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) || err)
146 {
147 DPE(afpc->errbuf, "%s: getsockopt: %s", __func__, err ? strerror(err) : strerror(errno));
148 return DAQ_ERROR;
149 }
150
151 return DAQ_SUCCESS;
152 }
153
set_up_ring(AFPacket_Context_t * afpc,AFPacketInstance * instance,AFPacketRing * ring)154 static int set_up_ring(AFPacket_Context_t *afpc, AFPacketInstance *instance, AFPacketRing *ring)
155 {
156 unsigned int idx, block, block_offset, frame, frame_offset;
157
158 /* Allocate a ring to hold packet pointers. */
159 ring->entries = calloc(ring->layout.tp_frame_nr, sizeof(AFPacketEntry));
160 if (!ring->entries)
161 {
162 DPE(afpc->errbuf, "%s: Could not allocate ring buffer entries for device %s!", __func__, instance->name);
163 return DAQ_ERROR_NOMEM;
164 }
165
166 /* Set up the buffer entry pointers in the ring. */
167 idx = 0;
168 for (block = 0; block < ring->layout.tp_block_nr; block++)
169 {
170 block_offset = block * ring->layout.tp_block_size;
171 for (frame = 0; frame < (ring->layout.tp_block_size / ring->layout.tp_frame_size) && idx < ring->layout.tp_frame_nr; frame++)
172 {
173 frame_offset = frame * ring->layout.tp_frame_size;
174 ring->entries[idx].hdr.raw = (uint8_t *) ring->start + block_offset + frame_offset;
175 ring->entries[idx].next = &ring->entries[idx + 1];
176 idx++;
177 }
178 }
179 /* Make this a circular buffer ... a RING if you will! */
180 ring->entries[ring->layout.tp_frame_nr - 1].next = &ring->entries[0];
181 /* Initialize our entry point into the ring as the first buffer entry. */
182 ring->cursor = &ring->entries[0];
183
184 return DAQ_SUCCESS;
185 }
186
destroy_instance(AFPacketInstance * instance)187 static void destroy_instance(AFPacketInstance *instance)
188 {
189 unsigned int ringsize;
190 struct tpacket_req req;
191
192 if (instance)
193 {
194 if (instance->fd != -1)
195 {
196 /* Destroy the userspace RX ring. */
197 if (instance->rx_ring.entries)
198 {
199 free(instance->rx_ring.entries);
200 instance->rx_ring.entries = NULL;
201 }
202 /* Destroy the userspace TX ring. */
203 if (instance->tx_ring.entries)
204 {
205 free(instance->tx_ring.entries);
206 instance->tx_ring.entries = NULL;
207 }
208 /* Unmap the kernel packet ring. */
209 if (instance->buffer != MAP_FAILED)
210 {
211 ringsize = instance->rx_ring.size + instance->tx_ring.size;
212 munmap(instance->buffer, ringsize);
213 instance->buffer = MAP_FAILED;
214 }
215 /* Tell the kernel to destroy the rings. */
216 memset(&req, 0, sizeof(req));
217 setsockopt(instance->fd, SOL_PACKET, PACKET_RX_RING, (void *) &req, sizeof(req));
218 setsockopt(instance->fd, SOL_PACKET, PACKET_TX_RING, (void *) &req, sizeof(req));
219 close(instance->fd);
220 }
221 if (instance->name)
222 {
223 free(instance->name);
224 instance->name = NULL;
225 }
226 free(instance);
227 }
228 }
229
230
iface_get_arptype(AFPacketInstance * instance)231 static int iface_get_arptype(AFPacketInstance *instance)
232 {
233 struct ifreq ifr;
234
235 memset(&ifr, 0, sizeof(ifr));
236 strncpy(ifr.ifr_name, instance->name, sizeof(ifr.ifr_name));
237
238 if (ioctl(instance->fd, SIOCGIFHWADDR, &ifr) == -1)
239 {
240 if (errno == ENODEV)
241 {
242 return DAQ_ERROR_NODEV;
243 }
244 return DAQ_ERROR;
245 }
246
247 return ifr.ifr_hwaddr.sa_family;
248 }
249
create_instance(const char * device,char * errbuf,size_t errlen)250 static AFPacketInstance *create_instance(const char *device, char *errbuf, size_t errlen)
251 {
252 AFPacketInstance *instance = NULL;
253 struct ifreq ifr;
254
255 instance = calloc(1, sizeof(AFPacketInstance));
256 if (!instance)
257 {
258 snprintf(errbuf, errlen, "%s: Could not allocate a new instance structure.", __func__);
259 goto err;
260 }
261 instance->buffer = MAP_FAILED;
262
263 if ((instance->name = strdup(device)) == NULL)
264 {
265 snprintf(errbuf, errlen, "%s: Could not allocate a copy of the device name.", __func__);
266 goto err;;
267 }
268
269 /* Open the PF_PACKET raw socket to receive all network traffic completely unmodified. */
270 instance->fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
271 if (instance->fd == -1)
272 {
273 snprintf(errbuf, errlen, "%s: Could not open the PF_PACKET socket: %s", __func__, strerror(errno));
274 goto err;
275 }
276
277 /* Find the device index of the specified interface. */
278 memset(&ifr, 0, sizeof(ifr));
279 strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
280 if (ioctl(instance->fd, SIOCGIFINDEX, &ifr) == -1)
281 {
282 snprintf(errbuf, errlen, "%s: Could not find index for device %s", __func__, instance->name);
283 goto err;
284 }
285 instance->index = ifr.ifr_ifindex;
286
287 /* Initialize the sockaddr for this instance's interface for later injection/forwarding use. */
288 instance->sll.sll_family = AF_PACKET;
289 instance->sll.sll_ifindex = instance->index;
290 instance->sll.sll_protocol = htons(ETH_P_ALL);
291
292 return instance;
293
294 err:
295 destroy_instance(instance);
296 return NULL;
297 }
298
299 /* The function below was heavily influenced by LibPCAP's pcap-linux.c. Thanks! */
determine_version(AFPacket_Context_t * afpc,AFPacketInstance * instance)300 static int determine_version(AFPacket_Context_t *afpc, AFPacketInstance *instance)
301 {
302 socklen_t len;
303 int val;
304
305 /* Probe whether kernel supports TPACKET_V2 */
306 val = TPACKET_V2;
307 len = sizeof(val);
308 if (getsockopt(instance->fd, SOL_PACKET, PACKET_HDRLEN, &val, &len) < 0)
309 {
310 DPE(afpc->errbuf, "Couldn't retrieve TPACKET_V2 header length: %s", strerror(errno));
311 return -1;
312 }
313 instance->tp_hdrlen = val;
314
315 /* Tell the kernel to use TPACKET_V2 */
316 val = TPACKET_V2;
317 if (setsockopt(instance->fd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val)) < 0)
318 {
319 DPE(afpc->errbuf, "Couldn't activate TPACKET_V2 on packet socket: %s", strerror(errno));
320 return -1;
321 }
322 instance->tp_version = TPACKET_V2;
323
324 /* Reserve space for VLAN tag reconstruction */
325 val = VLAN_TAG_LEN;
326 if (setsockopt(instance->fd, SOL_PACKET, PACKET_RESERVE, &val, sizeof(val)) < 0)
327 {
328 DPE(afpc->errbuf, "Couldn't set up a %d-byte reservation packet socket: %s", val, strerror(errno));
329 return -1;
330 }
331
332 if (afpc->debug)
333 {
334 printf("Version: %u\n", instance->tp_version);
335 printf("Header Length: %u\n", instance->tp_hdrlen);
336 }
337
338 return DAQ_SUCCESS;
339 }
340
calculate_layout(AFPacket_Context_t * afpc,struct tpacket_req * layout,unsigned int tp_hdrlen,int order)341 static int calculate_layout(AFPacket_Context_t *afpc, struct tpacket_req *layout, unsigned int tp_hdrlen, int order)
342 {
343 unsigned int tp_hdrlen_sll, netoff, frames_per_block;
344
345 /* Calculate the frame size and minimum block size required. */
346 tp_hdrlen_sll = TPACKET_ALIGN(tp_hdrlen) + sizeof(struct sockaddr_ll);
347 netoff = TPACKET_ALIGN(tp_hdrlen_sll + ETH_HLEN) + VLAN_TAG_LEN;
348 layout->tp_frame_size = TPACKET_ALIGN(netoff - ETH_HLEN + afpc->snaplen);
349 layout->tp_block_size = getpagesize() << order;
350 while (layout->tp_block_size < layout->tp_frame_size)
351 layout->tp_block_size <<= 1;
352 frames_per_block = layout->tp_block_size / layout->tp_frame_size;
353 if (frames_per_block == 0)
354 {
355 DPE(afpc->errbuf, "%s: Invalid frames per block (%u/%u) for %s",
356 __func__, layout->tp_block_size, layout->tp_frame_size, afpc->device);
357 return DAQ_ERROR;
358 }
359
360 /* Find the total number of frames required to amount to the requested per-interface memory.
361 Then find the number of blocks required to hold those packet buffer frames. */
362 layout->tp_frame_nr = afpc->size / layout->tp_frame_size;
363 layout->tp_block_nr = layout->tp_frame_nr / frames_per_block;
364 /* afpc->layout.tp_frame_nr is requested to match frames_per_block*n_blocks */
365 layout->tp_frame_nr = layout->tp_block_nr * frames_per_block;
366 if (afpc->debug)
367 {
368 printf("AFPacket Layout:\n");
369 printf(" Frame Size: %u\n", layout->tp_frame_size);
370 printf(" Frames: %u\n", layout->tp_frame_nr);
371 printf(" Block Size: %u (Order %d)\n", layout->tp_block_size, order);
372 printf(" Blocks: %u\n", layout->tp_block_nr);
373 }
374
375 return DAQ_SUCCESS;
376 }
377
378 #define DEFAULT_ORDER 3
create_ring(AFPacket_Context_t * afpc,AFPacketInstance * instance,AFPacketRing * ring,int optname)379 static int create_ring(AFPacket_Context_t *afpc, AFPacketInstance *instance, AFPacketRing *ring, int optname)
380 {
381 int rc, order;
382
383 /* Starting with page allocations of order 3, try to allocate an RX ring in the kernel. */
384 for (order = DEFAULT_ORDER; order >= 0; order--)
385 {
386 if (calculate_layout(afpc, &ring->layout, instance->tp_hdrlen, order))
387 return DAQ_ERROR;
388
389 /* Ask the kernel to create the ring. */
390 rc = setsockopt(instance->fd, SOL_PACKET, optname, (void*) &ring->layout, sizeof(struct tpacket_req));
391 if (rc)
392 {
393 if (errno == ENOMEM)
394 {
395 if (afpc->debug)
396 printf("%s: Allocation of kernel packet ring failed with order %d, retrying...\n", instance->name, order);
397 continue;
398 }
399 DPE(afpc->errbuf, "%s: Couldn't create kernel ring on packet socket: %s",
400 __func__, strerror(errno));
401 return DAQ_ERROR;
402 }
403 /* Store the total ring size for later. */
404 ring->size = ring->layout.tp_block_size * ring->layout.tp_block_nr;
405 if (afpc->debug)
406 printf("Created a ring of type %d with total size of %u\n", optname, ring->size);
407 return DAQ_SUCCESS;
408 }
409
410 /* If we got here, it means we failed allocation on order 0. */
411 DPE(afpc->errbuf, "%s: Couldn't allocate enough memory for the kernel packet ring!", instance->name);
412 return DAQ_ERROR;
413 }
414
mmap_rings(AFPacket_Context_t * afpc,AFPacketInstance * instance)415 static int mmap_rings(AFPacket_Context_t *afpc, AFPacketInstance *instance)
416 {
417 unsigned int ringsize;
418
419 /* Map the ring into userspace. */
420 ringsize = instance->rx_ring.size + instance->tx_ring.size;
421 instance->buffer = mmap(0, ringsize, PROT_READ | PROT_WRITE, MAP_SHARED, instance->fd, 0);
422 if (instance->buffer == MAP_FAILED)
423 {
424 DPE(afpc->errbuf, "%s: Could not MMAP the ring: %s", __func__, strerror(errno));
425 return DAQ_ERROR;
426 }
427 instance->rx_ring.start = instance->buffer;
428 instance->tx_ring.start = (uint8_t *) instance->buffer + instance->rx_ring.size;
429
430 return DAQ_SUCCESS;
431 }
432
433 #ifdef PACKET_FANOUT
configure_fanout(AFPacket_Context_t * afpc,AFPacketInstance * instance)434 static int configure_fanout(AFPacket_Context_t *afpc, AFPacketInstance *instance)
435 {
436 int fanout_arg;
437
438 fanout_arg = ((afpc->fanout_cfg.fanout_type | afpc->fanout_cfg.fanout_flags)) << 16 | instance->index;
439 if (setsockopt(instance->fd, SOL_PACKET, PACKET_FANOUT, &fanout_arg, sizeof(fanout_arg)) == -1)
440 {
441 DPE(afpc->errbuf, "%s: Could not configure packet fanout: %s", __func__, strerror(errno));
442 return DAQ_ERROR;
443 }
444
445 return DAQ_SUCCESS;
446 }
447 #endif
448
start_instance(AFPacket_Context_t * afpc,AFPacketInstance * instance)449 static int start_instance(AFPacket_Context_t *afpc, AFPacketInstance *instance)
450 {
451 struct packet_mreq mr;
452 int arptype;
453
454 /* Bind the RX ring to this interface. */
455 if (bind_instance_interface(afpc, instance) != 0)
456 return -1;
457
458 /* Turn on promiscuous mode for the device. */
459 memset(&mr, 0, sizeof(mr));
460 mr.mr_ifindex = instance->index;
461 mr.mr_type = PACKET_MR_PROMISC;
462 if (setsockopt(instance->fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) == -1)
463 {
464 DPE(afpc->errbuf, "%s: setsockopt: %s", __func__, strerror(errno));
465 return -1;
466 }
467
468 /* Get the link-layer type. */
469 arptype = iface_get_arptype(instance);
470 if (arptype < 0)
471 {
472 DPE(afpc->errbuf, "%s: failed to get interface type for device %s: (%d) %s",
473 __func__, instance->name, errno, strerror(errno));
474 return -1;
475 }
476
477 if (arptype != ARPHRD_ETHER)
478 {
479 DPE(afpc->errbuf, "%s: invalid interface type for device %s: %d != %d",
480 __func__, instance->name, arptype, ARPHRD_ETHER);
481 return -1;
482 }
483
484 /* Determine which versions of TPACKET the socket supports. */
485 if (determine_version(afpc, instance) != DAQ_SUCCESS)
486 return -1;
487
488 /* Request the kernel RX ring from af_packet... */
489 if (create_ring(afpc, instance, &instance->rx_ring, PACKET_RX_RING) != DAQ_SUCCESS)
490 return -1;
491 /* ...request the kernel TX ring from af_packet if we're in inline mode... */
492 if (instance->peer && create_ring(afpc, instance, &instance->tx_ring, PACKET_TX_RING) != DAQ_SUCCESS)
493 return -1;
494 /* ...map the memory for the kernel ring(s) into userspace... */
495 if (mmap_rings(afpc, instance) != DAQ_SUCCESS)
496 return -1;
497 /* ...and, finally, set up a userspace ring buffer to represent the kernel RX ring... */
498 if (set_up_ring(afpc, instance, &instance->rx_ring) != DAQ_SUCCESS)
499 return -1;
500 /* ...as well as one for the TX ring if we're in inline mode... */
501 if (instance->peer && set_up_ring(afpc, instance, &instance->tx_ring) != DAQ_SUCCESS)
502 return -1;
503 #ifdef PACKET_FANOUT
504 /* ...and configure packet fanout if requested. */
505 if (afpc->fanout_cfg.enabled && configure_fanout(afpc, instance) != DAQ_SUCCESS)
506 return -1;
507 #endif
508
509 return 0;
510 }
511
update_hw_stats(AFPacket_Context_t * afpc)512 static void update_hw_stats(AFPacket_Context_t *afpc)
513 {
514 AFPacketInstance *instance;
515 struct tpacket_stats kstats;
516 socklen_t len = sizeof (struct tpacket_stats);
517
518 if (afpc->state != DAQ_STATE_STARTED)
519 return;
520
521 for (instance = afpc->instances; instance; instance = instance->next)
522 {
523 memset(&kstats, 0, len);
524 if (getsockopt(instance->fd, SOL_PACKET, PACKET_STATISTICS, &kstats, &len) > -1)
525 {
526 /* The IOCTL adds tp_drops to tp_packets in the returned structure for some mind-boggling reason... */
527 afpc->stats.hw_packets_received += kstats.tp_packets - kstats.tp_drops;
528 afpc->stats.hw_packets_dropped += kstats.tp_drops;
529 }
530 else
531 fprintf(stderr, "Failed to get stats for %s: %d %s\n", instance->name, errno, strerror(errno));
532 }
533 }
534
af_packet_close(AFPacket_Context_t * afpc)535 static int af_packet_close(AFPacket_Context_t *afpc)
536 {
537 AFPacketInstance *instance;
538
539 if (!afpc)
540 return -1;
541
542 /* Cache the latest hardware stats before stopping. */
543 update_hw_stats(afpc);
544
545 while ((instance = afpc->instances) != NULL)
546 {
547 afpc->instances = instance->next;
548 destroy_instance(instance);
549 }
550
551 sfbpf_freecode(&afpc->fcode);
552
553 afpc->state = DAQ_STATE_STOPPED;
554
555 return 0;
556 }
557
create_bridge(AFPacket_Context_t * afpc,const char * device_name1,const char * device_name2)558 static int create_bridge(AFPacket_Context_t *afpc, const char *device_name1, const char *device_name2)
559 {
560 AFPacketInstance *instance, *peer1, *peer2;
561
562 peer1 = peer2 = NULL;
563 for (instance = afpc->instances; instance; instance = instance->next)
564 {
565 if (!strcmp(instance->name, device_name1))
566 peer1 = instance;
567 else if (!strcmp(instance->name, device_name2))
568 peer2 = instance;
569 }
570
571 if (!peer1 || !peer2)
572 return DAQ_ERROR_NODEV;
573
574 peer1->peer = peer2;
575 peer2->peer = peer1;
576
577 return DAQ_SUCCESS;
578 }
579
reset_stats(AFPacket_Context_t * afpc)580 static void reset_stats(AFPacket_Context_t *afpc)
581 {
582 AFPacketInstance *instance;
583 struct tpacket_stats kstats;
584 socklen_t len = sizeof (struct tpacket_stats);
585
586 memset(&afpc->stats, 0, sizeof(DAQ_Stats_t));
587 /* Just call PACKET_STATISTICS to clear each instance's stats. */
588 for (instance = afpc->instances; instance; instance = instance->next)
589 getsockopt(instance->fd, SOL_PACKET, PACKET_STATISTICS, &kstats, &len);
590 }
591
afpacket_daq_initialize(const DAQ_Config_t * config,void ** ctxt_ptr,char * errbuf,size_t errlen)592 static int afpacket_daq_initialize(const DAQ_Config_t *config, void **ctxt_ptr, char *errbuf, size_t errlen)
593 {
594 AFPacket_Context_t *afpc;
595 AFPacketInstance *instance;
596 const char *size_str = NULL;
597 char *name1, *name2, *dev;
598 char intf[IFNAMSIZ];
599 uint32_t size;
600 size_t len;
601 int num_rings, num_intfs = 0;
602 int rval = DAQ_ERROR;
603 DAQ_Dict *entry;
604
605 afpc = calloc(1, sizeof(AFPacket_Context_t));
606 if (!afpc)
607 {
608 snprintf(errbuf, errlen, "%s: Couldn't allocate memory for the new AFPacket context!", __func__);
609 rval = DAQ_ERROR_NOMEM;
610 goto err;
611 }
612
613 afpc->device = strdup(config->name);
614 if (!afpc->device)
615 {
616 snprintf(errbuf, errlen, "%s: Couldn't allocate memory for the device string!", __func__);
617 rval = DAQ_ERROR_NOMEM;
618 goto err;
619 }
620
621 afpc->snaplen = config->snaplen;
622 afpc->timeout = (config->timeout > 0) ? (int) config->timeout : -1;
623
624 dev = afpc->device;
625 if (*dev == ':' || ((len = strlen(dev)) > 0 && *(dev + len - 1) == ':') || (config->mode == DAQ_MODE_PASSIVE && strstr(dev, "::")))
626 {
627 snprintf(errbuf, errlen, "%s: Invalid interface specification: '%s'!", __func__, afpc->device);
628 goto err;
629 }
630
631 while (*dev != '\0')
632 {
633 len = strcspn(dev, ":");
634 if (len >= IFNAMSIZ)
635 {
636 snprintf(errbuf, errlen, "%s: Interface name too long! (%zu)", __func__, len);
637 goto err;
638 }
639 if (len != 0)
640 {
641 afpc->intf_count++;
642 if (afpc->intf_count >= AF_PACKET_MAX_INTERFACES)
643 {
644 snprintf(errbuf, errlen, "%s: Using more than %d interfaces is not supported!", __func__, AF_PACKET_MAX_INTERFACES);
645 goto err;
646 }
647 snprintf(intf, len + 1, "%s", dev);
648 instance = create_instance(intf, errbuf, errlen);
649 if (!instance)
650 goto err;
651
652 instance->next = afpc->instances;
653 afpc->instances = instance;
654 num_intfs++;
655 if (config->mode != DAQ_MODE_PASSIVE)
656 {
657 if (num_intfs == 2)
658 {
659 name1 = afpc->instances->next->name;
660 name2 = afpc->instances->name;
661
662 if (create_bridge(afpc, name1, name2) != DAQ_SUCCESS)
663 {
664 snprintf(errbuf, errlen, "%s: Couldn't create the bridge between %s and %s!", __func__, name1, name2);
665 goto err;
666 }
667 num_intfs = 0;
668 }
669 else if (num_intfs > 2)
670 break;
671 }
672 }
673 else
674 len = 1;
675 dev += len;
676 }
677
678 /* If there are any leftover unbridged interfaces and we're not in Passive mode, error out. */
679 if (!afpc->instances || (config->mode != DAQ_MODE_PASSIVE && num_intfs != 0))
680 {
681 snprintf(errbuf, errlen, "%s: Invalid interface specification: '%s'!", __func__, afpc->device);
682 goto err;
683 }
684
685 /*
686 * Determine the dimensions of the kernel RX ring(s) to request.
687 */
688 /* 1. Find the total desired packet buffer memory for all instances. */
689 for (entry = config->values; entry; entry = entry->next)
690 {
691 if (!strcmp(entry->key, "buffer_size_mb"))
692 size_str = entry->value;
693 else if (!strcmp(entry->key, "debug"))
694 afpc->debug = 1;
695 #ifdef PACKET_FANOUT
696 else if (!strcmp(entry->key, "fanout_type"))
697 {
698 if (!entry->value)
699 {
700 snprintf(errbuf, errlen, "%s: %s requires an argument!", __func__, entry->key);
701 goto err;
702 }
703 /* Using anything other than 'hash' is probably asking for trouble, but
704 I'll never stop you from shooting yourself in the foot. */
705 if (!strcmp(entry->value, "hash"))
706 afpc->fanout_cfg.fanout_type = PACKET_FANOUT_HASH;
707 else if (!strcmp(entry->value, "lb"))
708 afpc->fanout_cfg.fanout_type = PACKET_FANOUT_LB;
709 else if (!strcmp(entry->value, "cpu"))
710 afpc->fanout_cfg.fanout_type = PACKET_FANOUT_CPU;
711 else if (!strcmp(entry->value, "rollover"))
712 afpc->fanout_cfg.fanout_type = PACKET_FANOUT_ROLLOVER;
713 else if (!strcmp(entry->value, "rnd"))
714 afpc->fanout_cfg.fanout_type = PACKET_FANOUT_RND;
715 #ifdef PACKET_FANOUT_QM
716 else if (!strcmp(entry->value, "qm"))
717 afpc->fanout_cfg.fanout_type = PACKET_FANOUT_QM;
718 #endif
719 else
720 {
721 snprintf(errbuf, errlen, "%s: Unrecognized argument for %s: '%s'!", __func__, entry->key, entry->value);
722 goto err;
723 }
724 afpc->fanout_cfg.enabled = true;
725 }
726 else if (!strcmp(entry->key, "fanout_flag"))
727 {
728 if (!entry->value)
729 {
730 snprintf(errbuf, errlen, "%s: %s requires an argument!", __func__, entry->key);
731 goto err;
732 }
733 if (!strcmp(entry->value, "rollover"))
734 afpc->fanout_cfg.fanout_flags |= PACKET_FANOUT_FLAG_ROLLOVER;
735 else if (!strcmp(entry->value, "defrag"))
736 afpc->fanout_cfg.fanout_flags |= PACKET_FANOUT_FLAG_DEFRAG;
737 else
738 {
739 snprintf(errbuf, errlen, "%s: Unrecognized argument for %s: '%s'!", __func__, entry->key, entry->value);
740 goto err;
741 }
742 }
743 #endif /* PACKET_FANOUT */
744 }
745
746 /* Fall back to the environment variable. */
747 if (!size_str)
748 size_str = getenv("AF_PACKET_BUFFER_SIZE");
749 if (size_str && strcmp("max", size_str) != 0)
750 size = strtoul(size_str, NULL, 10);
751 else
752 size = AF_PACKET_DEFAULT_BUFFER_SIZE;
753 /* The size is specified in megabytes. */
754 size = size * 1024 * 1024;
755
756 /* 2. Divide it evenly across the number of rings. (One per passive interface, two per inline.) */
757 num_rings = 0;
758 for (instance = afpc->instances; instance; instance = instance->next)
759 num_rings += instance->peer ? 2 : 1;
760 afpc->size = size / num_rings;
761
762 afpc->state = DAQ_STATE_INITIALIZED;
763
764 *ctxt_ptr = afpc;
765 return DAQ_SUCCESS;
766
767 err:
768 if (afpc)
769 {
770 af_packet_close(afpc);
771 if (afpc->device)
772 free(afpc->device);
773 free(afpc);
774 }
775 return rval;
776 }
777
afpacket_daq_set_filter(void * handle,const char * filter)778 static int afpacket_daq_set_filter(void *handle, const char *filter)
779 {
780 AFPacket_Context_t *afpc = (AFPacket_Context_t *) handle;
781 struct sfbpf_program fcode;
782
783 if (afpc->filter)
784 free(afpc->filter);
785
786 afpc->filter = strdup(filter);
787 if (!afpc->filter)
788 {
789 DPE(afpc->errbuf, "%s: Couldn't allocate memory for the filter string!", __func__);
790 return DAQ_ERROR;
791 }
792
793 if (sfbpf_compile(afpc->snaplen, DLT_EN10MB, &fcode, afpc->filter, 1, 0) < 0)
794 {
795 DPE(afpc->errbuf, "%s: BPF state machine compilation failed!", __func__);
796 return DAQ_ERROR;
797 }
798
799 sfbpf_freecode(&afpc->fcode);
800 afpc->fcode.bf_len = fcode.bf_len;
801 afpc->fcode.bf_insns = fcode.bf_insns;
802
803 return DAQ_SUCCESS;
804 }
805
afpacket_daq_start(void * handle)806 static int afpacket_daq_start(void *handle)
807 {
808 AFPacket_Context_t *afpc = (AFPacket_Context_t *) handle;
809 AFPacketInstance *instance;
810
811 for (instance = afpc->instances; instance; instance = instance->next)
812 {
813 if (start_instance(afpc, instance) != 0)
814 return DAQ_ERROR;
815 }
816
817 reset_stats(afpc);
818
819 afpc->state = DAQ_STATE_STARTED;
820
821 return DAQ_SUCCESS;
822 }
823
824 static const DAQ_Verdict verdict_translation_table[MAX_DAQ_VERDICT] = {
825 DAQ_VERDICT_PASS, /* DAQ_VERDICT_PASS */
826 DAQ_VERDICT_BLOCK, /* DAQ_VERDICT_BLOCK */
827 DAQ_VERDICT_PASS, /* DAQ_VERDICT_REPLACE */
828 DAQ_VERDICT_PASS, /* DAQ_VERDICT_WHITELIST */
829 DAQ_VERDICT_BLOCK, /* DAQ_VERDICT_BLACKLIST */
830 DAQ_VERDICT_PASS, /* DAQ_VERDICT_IGNORE */
831 DAQ_VERDICT_BLOCK /* DAQ_VERDICT_RETRY */
832 };
833
afpacket_daq_acquire(void * handle,int cnt,DAQ_Analysis_Func_t callback,DAQ_Meta_Func_t metaback,void * user)834 static int afpacket_daq_acquire(void *handle, int cnt, DAQ_Analysis_Func_t callback, DAQ_Meta_Func_t metaback, void *user)
835 {
836 AFPacket_Context_t *afpc = (AFPacket_Context_t *) handle;
837 AFPacketInstance *instance;
838 DAQ_PktHdr_t daqhdr;
839 DAQ_Verdict verdict;
840 union thdr hdr;
841 struct pollfd pfd[AF_PACKET_MAX_INTERFACES];
842 const uint8_t *data;
843 uint32_t i;
844 int got_one, ignored_one;
845 int ret, c = 0;
846 unsigned int tp_len, tp_mac, tp_snaplen, tp_sec, tp_usec;
847
848 while (c < cnt || cnt <= 0)
849 {
850 got_one = 0;
851 ignored_one = 0;
852 for (instance = afpc->instances; instance; instance = instance->next)
853 {
854 /* Has breakloop() been called? */
855 if (afpc->break_loop)
856 {
857 afpc->break_loop = 0;
858 return 0;
859 }
860
861 hdr = instance->rx_ring.cursor->hdr;
862 if (instance->tp_version == TPACKET_V2 && (hdr.h2->tp_status & TP_STATUS_USER))
863 {
864 switch (instance->tp_version)
865 {
866 case TPACKET_V2:
867 tp_len = hdr.h2->tp_len;
868 tp_mac = hdr.h2->tp_mac;
869 tp_snaplen = hdr.h2->tp_snaplen;
870 tp_sec = hdr.h2->tp_sec;
871 tp_usec = hdr.h2->tp_nsec / 1000;
872 break;
873
874 default:
875 DPE(afpc->errbuf, "%s: Unknown TPACKET version: %u!", __func__, instance->tp_version);
876 return DAQ_ERROR;
877 }
878 if (tp_mac + tp_snaplen > instance->rx_ring.layout.tp_frame_size)
879 {
880 DPE(afpc->errbuf, "%s: Corrupted frame on kernel ring (MAC offset %u + CapLen %u > FrameSize %d)",
881 __func__, tp_mac, tp_snaplen, instance->rx_ring.layout.tp_frame_size);
882 return DAQ_ERROR;
883 }
884 data = instance->rx_ring.cursor->hdr.raw + tp_mac;
885
886 /* Make a valiant attempt at reconstructing the VLAN tag if it has been stripped. This really sucks. :( */
887 if ((instance->tp_version == TPACKET_V2) &&
888 #if defined(TP_STATUS_VLAN_VALID)
889 (hdr.h2->tp_vlan_tci || (hdr.h2->tp_status & TP_STATUS_VLAN_VALID)) &&
890 #else
891 hdr.h2->tp_vlan_tci &&
892 #endif
893 tp_snaplen >= (unsigned int) vlan_offset)
894 {
895 struct vlan_tag *tag;
896
897 data -= VLAN_TAG_LEN;
898 memmove((void *) data, data + VLAN_TAG_LEN, vlan_offset);
899
900 tag = (struct vlan_tag *) (data + vlan_offset);
901 #if defined(TP_STATUS_VLAN_TPID_VALID)
902 if (hdr.h2->tp_vlan_tpid && (hdr.h2->tp_status & TP_STATUS_VLAN_TPID_VALID))
903 tag->vlan_tpid = htons(hdr.h2->tp_vlan_tpid);
904 else
905 #endif
906 tag->vlan_tpid = htons(ETH_P_8021Q);
907 tag->vlan_tci = htons(hdr.h2->tp_vlan_tci);
908
909 tp_snaplen += VLAN_TAG_LEN;
910 tp_len += VLAN_TAG_LEN;
911 }
912
913 verdict = DAQ_VERDICT_PASS;
914 if (afpc->fcode.bf_insns && sfbpf_filter(afpc->fcode.bf_insns, data, tp_len, tp_snaplen) == 0)
915 {
916 ignored_one = 1;
917 afpc->stats.packets_filtered++;
918 goto send_packet;
919 }
920 got_one = 1;
921
922 daqhdr.ts.tv_sec = tp_sec;
923 daqhdr.ts.tv_usec = tp_usec;
924 daqhdr.caplen = tp_snaplen;
925 daqhdr.pktlen = tp_len;
926 daqhdr.ingress_index = instance->index;
927 daqhdr.egress_index = instance->peer ? instance->peer->index : DAQ_PKTHDR_UNKNOWN;
928 daqhdr.ingress_group = DAQ_PKTHDR_UNKNOWN;
929 daqhdr.egress_group = DAQ_PKTHDR_UNKNOWN;
930 daqhdr.flags = 0;
931 daqhdr.opaque = 0;
932 daqhdr.priv_ptr = NULL;
933 daqhdr.address_space_id = 0;
934
935 if (callback)
936 {
937 verdict = callback(user, &daqhdr, data);
938 if (verdict >= MAX_DAQ_VERDICT)
939 verdict = DAQ_VERDICT_PASS;
940 afpc->stats.verdicts[verdict]++;
941 verdict = verdict_translation_table[verdict];
942 }
943 afpc->stats.packets_received++;
944 c++;
945 send_packet:
946 if (verdict == DAQ_VERDICT_PASS && instance->peer)
947 {
948 AFPacketEntry *entry = instance->peer->tx_ring.cursor;
949 int rc;
950
951 if (entry->hdr.h2->tp_status == TP_STATUS_AVAILABLE)
952 {
953 memcpy(entry->hdr.raw + TPACKET_ALIGN(instance->peer->tp_hdrlen), data, tp_snaplen);
954 entry->hdr.h2->tp_len = tp_snaplen;
955 entry->hdr.h2->tp_status = TP_STATUS_SEND_REQUEST;
956 rc = send(instance->peer->fd, NULL, 0, 0);
957 instance->peer->tx_ring.cursor = entry->next;
958 }
959 /* Else, don't forward the packet... */
960 }
961 /* Release the TPACKET buffer back to the kernel. */
962 switch (instance->tp_version)
963 {
964 case TPACKET_V2:
965 hdr.h2->tp_status = TP_STATUS_KERNEL;
966 break;
967 }
968 instance->rx_ring.cursor = instance->rx_ring.cursor->next;
969 }
970 }
971 if (!got_one && !ignored_one)
972 {
973 for (i = 0, instance = afpc->instances; instance; i++, instance = instance->next)
974 {
975 pfd[i].fd = instance->fd;
976 pfd[i].revents = 0;
977 pfd[i].events = POLLIN;
978 }
979 ret = poll(pfd, afpc->intf_count, afpc->timeout);
980 /* If we were interrupted by a signal, start the loop over. The user should call daq_breakloop to actually exit. */
981 if (ret < 0 && errno != EINTR)
982 {
983 DPE(afpc->errbuf, "%s: Poll failed: %s (%d)", __func__, strerror(errno), errno);
984 return DAQ_ERROR;
985 }
986 /* If the poll times out, return control to the caller. */
987 if (ret == 0)
988 break;
989 /* If some number of of sockets have events returned, check them all for badness. */
990 if (ret > 0)
991 {
992 for (i = 0; i < afpc->intf_count; i++)
993 {
994 if (pfd[i].revents & (POLLHUP | POLLRDHUP | POLLERR | POLLNVAL))
995 {
996 if (pfd[i].revents & (POLLHUP | POLLRDHUP))
997 DPE(afpc->errbuf, "%s: Hang-up on a packet socket", __func__);
998 else if (pfd[i].revents & POLLERR)
999 DPE(afpc->errbuf, "%s: Encountered error condition on a packet socket", __func__);
1000 else if (pfd[i].revents & POLLNVAL)
1001 DPE(afpc->errbuf, "%s: Invalid polling request on a packet socket", __func__);
1002 return DAQ_ERROR;
1003 }
1004 }
1005 }
1006 }
1007 }
1008 return 0;
1009 }
1010
afpacket_daq_inject(void * handle,const DAQ_PktHdr_t * hdr,const uint8_t * packet_data,uint32_t len,int reverse)1011 static int afpacket_daq_inject(void *handle, const DAQ_PktHdr_t *hdr, const uint8_t *packet_data, uint32_t len, int reverse)
1012 {
1013 AFPacket_Context_t *afpc = (AFPacket_Context_t *) handle;
1014 AFPacketInstance *instance;
1015 AFPacketEntry *entry;
1016
1017 /* Find the instance that the packet was received on. */
1018 for (instance = afpc->instances; instance; instance = instance->next)
1019 {
1020 if (instance->index == hdr->ingress_index)
1021 break;
1022 }
1023
1024 if (!instance || (!reverse && !(instance = instance->peer)))
1025 return DAQ_ERROR;
1026
1027 if (instance->tx_ring.entries)
1028 {
1029 entry = instance->tx_ring.cursor;
1030 if (entry->hdr.h2->tp_status == TP_STATUS_AVAILABLE)
1031 {
1032 memcpy(entry->hdr.raw + TPACKET_ALIGN(instance->tp_hdrlen), packet_data, len);
1033 entry->hdr.h2->tp_len = len;
1034 entry->hdr.h2->tp_status = TP_STATUS_SEND_REQUEST;
1035 instance->tx_ring.cursor = entry->next;
1036 if (send(instance->fd, NULL, 0, 0) < 0)
1037 {
1038 DPE(afpc->errbuf, "%s: Error sending packet: %s (%d)", __func__, strerror(errno), errno);
1039 return DAQ_ERROR;
1040 }
1041 afpc->stats.packets_injected++;
1042 }
1043 }
1044 else
1045 {
1046 const struct ethhdr *eth;
1047 struct sockaddr_ll *sll;
1048
1049 eth = (const struct ethhdr *)packet_data;
1050 sll = &instance->sll;
1051 sll->sll_protocol = eth->h_proto;
1052
1053 if (sendto(instance->fd, packet_data, len, 0, (struct sockaddr *) sll, sizeof(*sll)) < 0)
1054 {
1055 DPE(afpc->errbuf, "%s: Error sending packet: %s (%d)", __func__, strerror(errno), errno);
1056 return DAQ_ERROR;
1057 }
1058 afpc->stats.packets_injected++;
1059 }
1060
1061 return DAQ_SUCCESS;
1062 }
1063
afpacket_daq_breakloop(void * handle)1064 static int afpacket_daq_breakloop(void *handle)
1065 {
1066 AFPacket_Context_t *afpc = (AFPacket_Context_t *) handle;
1067
1068 afpc->break_loop = 1;
1069
1070 return DAQ_SUCCESS;
1071 }
1072
afpacket_daq_stop(void * handle)1073 static int afpacket_daq_stop(void *handle)
1074 {
1075 AFPacket_Context_t *afpc = (AFPacket_Context_t *) handle;
1076
1077 af_packet_close(afpc);
1078
1079 return DAQ_SUCCESS;
1080 }
1081
afpacket_daq_shutdown(void * handle)1082 static void afpacket_daq_shutdown(void *handle)
1083 {
1084 AFPacket_Context_t *afpc = (AFPacket_Context_t *) handle;
1085
1086 af_packet_close(afpc);
1087 if (afpc->device)
1088 free(afpc->device);
1089 if (afpc->filter)
1090 free(afpc->filter);
1091 free(afpc);
1092 }
1093
afpacket_daq_check_status(void * handle)1094 static DAQ_State afpacket_daq_check_status(void *handle)
1095 {
1096 AFPacket_Context_t *afpc = (AFPacket_Context_t *) handle;
1097
1098 return afpc->state;
1099 }
1100
afpacket_daq_get_stats(void * handle,DAQ_Stats_t * stats)1101 static int afpacket_daq_get_stats(void *handle, DAQ_Stats_t *stats)
1102 {
1103 AFPacket_Context_t *afpc = (AFPacket_Context_t *) handle;
1104
1105 update_hw_stats(afpc);
1106 memcpy(stats, &afpc->stats, sizeof(DAQ_Stats_t));
1107
1108 return DAQ_SUCCESS;
1109 }
1110
afpacket_daq_reset_stats(void * handle)1111 static void afpacket_daq_reset_stats(void *handle)
1112 {
1113 AFPacket_Context_t *afpc = (AFPacket_Context_t *) handle;
1114
1115 reset_stats(afpc);
1116 }
1117
afpacket_daq_get_snaplen(void * handle)1118 static int afpacket_daq_get_snaplen(void *handle)
1119 {
1120 AFPacket_Context_t *afpc = (AFPacket_Context_t *) handle;
1121
1122 return afpc->snaplen;
1123 }
1124
afpacket_daq_get_capabilities(void * handle)1125 static uint32_t afpacket_daq_get_capabilities(void *handle)
1126 {
1127 return DAQ_CAPA_BLOCK | DAQ_CAPA_REPLACE | DAQ_CAPA_INJECT | DAQ_CAPA_UNPRIV_START | DAQ_CAPA_BREAKLOOP | DAQ_CAPA_BPF | DAQ_CAPA_DEVICE_INDEX;
1128 }
1129
afpacket_daq_get_datalink_type(void * handle)1130 static int afpacket_daq_get_datalink_type(void *handle)
1131 {
1132 return DLT_EN10MB;
1133 }
1134
afpacket_daq_get_errbuf(void * handle)1135 static const char *afpacket_daq_get_errbuf(void *handle)
1136 {
1137 AFPacket_Context_t *afpc = (AFPacket_Context_t *) handle;
1138
1139 return afpc->errbuf;
1140 }
1141
afpacket_daq_set_errbuf(void * handle,const char * string)1142 static void afpacket_daq_set_errbuf(void *handle, const char *string)
1143 {
1144 AFPacket_Context_t *afpc = (AFPacket_Context_t *) handle;
1145
1146 if (!string)
1147 return;
1148
1149 DPE(afpc->errbuf, "%s", string);
1150 }
1151
afpacket_daq_get_device_index(void * handle,const char * device)1152 static int afpacket_daq_get_device_index(void *handle, const char *device)
1153 {
1154 AFPacket_Context_t *afpc = (AFPacket_Context_t *) handle;
1155 AFPacketInstance *instance;
1156
1157 for (instance = afpc->instances; instance; instance = instance->next)
1158 {
1159 if (!strcmp(device, instance->name))
1160 return instance->index;
1161 }
1162
1163 return DAQ_ERROR_NODEV;
1164 }
1165
1166 #ifdef BUILDING_SO
1167 DAQ_SO_PUBLIC const DAQ_Module_t DAQ_MODULE_DATA =
1168 #else
1169 const DAQ_Module_t afpacket_daq_module_data =
1170 #endif
1171 {
1172 .api_version = DAQ_API_VERSION,
1173 .module_version = DAQ_AFPACKET_VERSION,
1174 .name = "afpacket",
1175 .type = DAQ_TYPE_INTF_CAPABLE | DAQ_TYPE_INLINE_CAPABLE | DAQ_TYPE_MULTI_INSTANCE,
1176 .initialize = afpacket_daq_initialize,
1177 .set_filter = afpacket_daq_set_filter,
1178 .start = afpacket_daq_start,
1179 .acquire = afpacket_daq_acquire,
1180 .inject = afpacket_daq_inject,
1181 .breakloop = afpacket_daq_breakloop,
1182 .stop = afpacket_daq_stop,
1183 .shutdown = afpacket_daq_shutdown,
1184 .check_status = afpacket_daq_check_status,
1185 .get_stats = afpacket_daq_get_stats,
1186 .reset_stats = afpacket_daq_reset_stats,
1187 .get_snaplen = afpacket_daq_get_snaplen,
1188 .get_capabilities = afpacket_daq_get_capabilities,
1189 .get_datalink_type = afpacket_daq_get_datalink_type,
1190 .get_errbuf = afpacket_daq_get_errbuf,
1191 .set_errbuf = afpacket_daq_set_errbuf,
1192 .get_device_index = afpacket_daq_get_device_index,
1193 .modify_flow = NULL,
1194 .hup_prep = NULL,
1195 .hup_apply = NULL,
1196 .hup_post = NULL,
1197 };
1198