1 /*
2 ** Copyright (C) 2018-2021 Cisco and/or its affiliates. All rights reserved.
3 ** Author: Michael R. Altizer <mialtize@cisco.com>
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License Version 2 as
7 ** published by the Free Software Foundation. You may not use, modify or
8 ** distribute this program under any other version of the GNU General
9 ** Public License.
10 **
11 ** This program is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ** GNU General Public License for more details.
15 **
16 ** You should have received a copy of the GNU General Public License
17 ** along with this program; if not, write to the Free Software
18 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <errno.h>
26 #include <stdbool.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/socket.h>
30
31 #include "daq_dlt.h"
32 #include "daq_module_api.h"
33
34 #define DAQ_DIVERT_VERSION 1
35
36 #define DIVERT_DEFAULT_POOL_SIZE 64
37
38 #define SET_ERROR(modinst, ...) daq_base_api.set_errbuf(modinst, __VA_ARGS__)
39
40 typedef struct _divert_pkt_desc
41 {
42 DAQ_Msg_t msg;
43 DAQ_PktHdr_t pkthdr;
44 uint8_t *data;
45 struct sockaddr_in addr;
46 struct _divert_pkt_desc *next;
47 } DivertPktDesc;
48
49 typedef struct _divert_msg_pool
50 {
51 DivertPktDesc *pool;
52 DivertPktDesc *freelist;
53 DAQ_MsgPoolInfo_t info;
54 } DivertMsgPool;
55
56 typedef struct _divert_context {
57 /* Configuration */
58 int port;
59 bool passive;
60 unsigned timeout;
61 unsigned snaplen;
62 /* State */
63 int sock;
64 DAQ_ModuleInstance_h modinst;
65 DivertMsgPool pool;
66 volatile bool interrupted;
67 DAQ_Stats_t stats;
68 } Divert_Context_t;
69
70 static void divert_daq_destroy(void *);
71
72 static DAQ_BaseAPI_t daq_base_api;
73
74
destroy_packet_pool(Divert_Context_t * dc)75 static void destroy_packet_pool(Divert_Context_t *dc)
76 {
77 DivertMsgPool *pool = &dc->pool;
78 if (pool->pool)
79 {
80 while (pool->info.size > 0)
81 free(pool->pool[--pool->info.size].data);
82 free(pool->pool);
83 pool->pool = NULL;
84 }
85 pool->freelist = NULL;
86 pool->info.available = 0;
87 pool->info.mem_size = 0;
88 }
89
create_packet_pool(Divert_Context_t * dc,unsigned size)90 static int create_packet_pool(Divert_Context_t *dc, unsigned size)
91 {
92 DivertMsgPool *pool = &dc->pool;
93 pool->pool = calloc(sizeof(DivertPktDesc), size);
94 if (!pool->pool)
95 {
96 SET_ERROR(dc->modinst, "%s: Couldn't allocate %zu bytes for a packet descriptor pool!",
97 __func__, sizeof(DivertPktDesc) * size);
98 return DAQ_ERROR_NOMEM;
99 }
100 pool->info.mem_size = sizeof(DivertPktDesc) * size;
101 while (pool->info.size < size)
102 {
103 /* Allocate packet data and set up descriptor */
104 DivertPktDesc *desc = &pool->pool[pool->info.size];
105 desc->data = malloc(dc->snaplen);
106 if (!desc->data)
107 {
108 SET_ERROR(dc->modinst, "%s: Couldn't allocate %d bytes for a packet descriptor message buffer!",
109 __func__, dc->snaplen);
110 return DAQ_ERROR_NOMEM;
111 }
112 pool->info.mem_size += dc->snaplen;
113
114 /* Initialize non-zero invariant packet header fields. */
115 DAQ_PktHdr_t *pkthdr = &desc->pkthdr;
116 pkthdr->ingress_index = DAQ_PKTHDR_UNKNOWN;
117 pkthdr->ingress_group = DAQ_PKTHDR_UNKNOWN;
118 pkthdr->egress_index = DAQ_PKTHDR_UNKNOWN;
119 pkthdr->egress_group = DAQ_PKTHDR_UNKNOWN;
120
121 /* Initialize non-zero invariant message header fields. */
122 DAQ_Msg_t *msg = &desc->msg;
123 msg->type = DAQ_MSG_TYPE_PACKET;
124 msg->hdr_len = sizeof(desc->pkthdr);
125 msg->hdr = &desc->pkthdr;
126 msg->data = desc->data;
127 msg->owner = dc->modinst;
128 msg->priv = desc;
129
130 /* Place it on the free list */
131 desc->next = pool->freelist;
132 pool->freelist = desc;
133
134 pool->info.size++;
135 }
136 pool->info.available = pool->info.size;
137 return DAQ_SUCCESS;
138 }
139
divert_daq_module_load(const DAQ_BaseAPI_t * base_api)140 static int divert_daq_module_load(const DAQ_BaseAPI_t *base_api)
141 {
142 if (base_api->api_version != DAQ_BASE_API_VERSION || base_api->api_size != sizeof(DAQ_BaseAPI_t))
143 return DAQ_ERROR;
144
145 daq_base_api = *base_api;
146
147 return DAQ_SUCCESS;
148 }
149
divert_daq_module_unload(void)150 static int divert_daq_module_unload(void)
151 {
152 memset(&daq_base_api, 0, sizeof(daq_base_api));
153 return DAQ_SUCCESS;
154 }
155
divert_daq_instantiate(const DAQ_ModuleConfig_h modcfg,DAQ_ModuleInstance_h modinst,void ** ctxt_ptr)156 static int divert_daq_instantiate(const DAQ_ModuleConfig_h modcfg, DAQ_ModuleInstance_h modinst, void **ctxt_ptr)
157 {
158 Divert_Context_t *dc = calloc(1, sizeof(*dc));
159
160 if (!dc)
161 {
162 SET_ERROR(modinst, "%s: Couldn't allocate memory for the new Divert context!", __func__);
163 return DAQ_ERROR_NOMEM;
164 }
165 dc->sock = -1;
166 dc->modinst = modinst;
167
168 char *endptr;
169 errno = 0;
170 dc->port = strtoul(daq_base_api.config_get_input(modcfg), &endptr, 10);
171 if (*endptr != '\0' || errno != 0 || dc->port > 65535)
172 {
173 SET_ERROR(modinst, "%s: Invalid divert port number specified: '%s'",
174 __func__, daq_base_api.config_get_input(modcfg));
175 divert_daq_destroy(dc);
176 return DAQ_ERROR_INVAL;
177 }
178
179 dc->snaplen = daq_base_api.config_get_snaplen(modcfg);
180 dc->timeout = daq_base_api.config_get_timeout(modcfg);
181 if (dc->timeout == 0)
182 dc->timeout = -1;
183 dc->passive = (daq_base_api.config_get_mode(modcfg) == DAQ_MODE_PASSIVE);
184
185 /* Open the divert socket. Traffic will not start going to it until we bind it in start(). */
186 if ((dc->sock = socket(PF_INET, SOCK_RAW, IPPROTO_DIVERT)) == -1)
187 {
188 SET_ERROR(modinst, "%s: Couldn't open the DIVERT socket: %s", __func__, strerror(errno));
189 divert_daq_destroy(dc);
190 return DAQ_ERROR;
191 }
192
193 /* Finally, create the message buffer pool. */
194 uint32_t pool_size = daq_base_api.config_get_msg_pool_size(modcfg);
195 int rval;
196 if ((rval = create_packet_pool(dc, pool_size ? pool_size : DIVERT_DEFAULT_POOL_SIZE)) != DAQ_SUCCESS)
197 {
198 divert_daq_destroy(dc);
199 return rval;
200 }
201
202 *ctxt_ptr = dc;
203
204 return DAQ_SUCCESS;
205 }
206
divert_daq_destroy(void * handle)207 static void divert_daq_destroy(void* handle)
208 {
209 Divert_Context_t *dc = (Divert_Context_t *) handle;
210
211 if (dc->sock != -1)
212 close(dc->sock);
213 destroy_packet_pool(dc);
214 free(dc);
215 }
216
divert_daq_start(void * handle)217 static int divert_daq_start(void *handle)
218 {
219 Divert_Context_t *dc = (Divert_Context_t *) handle;
220
221 struct sockaddr_in sin;
222 memset(&sin, 0, sizeof(sin));
223 sin.sin_family = PF_INET;
224 sin.sin_addr.s_addr = INADDR_ANY;
225 sin.sin_port = htons(dc->port);
226
227 if (bind(dc->sock, (struct sockaddr *) &sin, sizeof(sin)) == -1)
228 {
229 SET_ERROR(dc->modinst, "%s: Couldn't bind to port %d on the DIVERT socket: %s",
230 __func__, dc->port, strerror(errno));
231 return DAQ_ERROR;
232 }
233
234 return DAQ_SUCCESS;
235 }
236
divert_daq_stop(void * handle)237 static int divert_daq_stop(void *handle)
238 {
239 Divert_Context_t *dc = (Divert_Context_t *) handle;
240 close(dc->sock);
241 dc->sock = -1;
242 return DAQ_SUCCESS;
243 }
244
divert_daq_inject_relative(void * handle,const DAQ_Msg_t * msg,const uint8_t * data,uint32_t data_len,int reverse)245 static int divert_daq_inject_relative(void *handle, const DAQ_Msg_t *msg, const uint8_t *data, uint32_t data_len, int reverse)
246 {
247 Divert_Context_t *dc = (Divert_Context_t *) handle;
248 DivertPktDesc *desc = (DivertPktDesc *) msg->priv;
249
250 /* We don't appear to need to respect the reverse aspect as long as the packet is well-formed enough to be
251 routed successfully. */
252 ssize_t wrote = sendto(dc->sock, data, data_len, 0, (struct sockaddr*) &desc->addr, sizeof(desc->addr));
253 if (wrote < 0 || (unsigned) wrote != data_len)
254 {
255 SET_ERROR(dc->modinst, "%s: Couldn't send to the DIVERT socket: %s", __func__, strerror(errno));
256 return DAQ_ERROR;
257 }
258 dc->stats.packets_injected++;
259
260 return DAQ_SUCCESS;
261 }
262
divert_daq_msg_receive(void * handle,const unsigned max_recv,const DAQ_Msg_t * msgs[],DAQ_RecvStatus * rstat)263 static unsigned divert_daq_msg_receive(void *handle, const unsigned max_recv, const DAQ_Msg_t *msgs[], DAQ_RecvStatus *rstat)
264 {
265 Divert_Context_t *dc = (Divert_Context_t *) handle;
266 DAQ_RecvStatus status = DAQ_RSTAT_OK;
267 struct timeval tv;
268 unsigned idx = 0;
269
270 while (idx < max_recv)
271 {
272 /* Make sure that we have a packet descriptor available to populate. */
273 DivertPktDesc *desc = dc->pool.freelist;
274 if (!desc)
275 {
276 status = DAQ_RSTAT_NOBUF;
277 break;
278 }
279
280 fd_set fdset;
281
282 int rval;
283 if (idx == 0)
284 {
285 int timeout = dc->timeout;
286 rval = 0;
287 while (timeout != 0 && rval == 0)
288 {
289 if (dc->interrupted)
290 {
291 dc->interrupted = false;
292 *rstat = DAQ_RSTAT_INTERRUPTED;
293 return 0;
294 }
295 if (timeout >= 1000)
296 {
297 tv.tv_sec = 1;
298 tv.tv_usec = 0;
299 timeout -= 1000;
300 }
301 else if (timeout > 0)
302 {
303 tv.tv_sec = 0;
304 tv.tv_usec = timeout * 1000;
305 timeout = 0;
306 }
307 else
308 {
309 tv.tv_sec = 1;
310 tv.tv_usec = 0;
311 }
312 FD_ZERO(&fdset);
313 FD_SET(dc->sock, &fdset);
314 rval = select(dc->sock + 1, &fdset, NULL, NULL, &tv);
315 }
316 }
317 else
318 {
319 tv.tv_sec = 0;
320 tv.tv_usec = 0;
321 FD_ZERO(&fdset);
322 FD_SET(dc->sock, &fdset);
323 rval = select(dc->sock + 1, &fdset, NULL, NULL, &tv);
324 }
325
326 if (rval == -1)
327 {
328 SET_ERROR(dc->modinst, "%s: Couldn't select on the DIVERT socket: %s", __func__, strerror(errno));
329 status = DAQ_RSTAT_ERROR;
330 break;
331 }
332
333 if (rval == 0)
334 {
335 status = (idx == 0) ? DAQ_RSTAT_TIMEOUT : DAQ_RSTAT_WOULD_BLOCK;
336 break;
337 }
338
339 if (FD_ISSET(dc->sock, &fdset))
340 {
341 socklen_t addrlen = sizeof(desc->addr);
342 ssize_t pktlen;
343
344 if ((pktlen = recvfrom(dc->sock, desc->data, dc->snaplen, 0,
345 (struct sockaddr *) &desc->addr, &addrlen)) == -1)
346 {
347 if (errno != EINTR)
348 {
349 SET_ERROR(dc->modinst, "%s: Couldn't receive from the DIVERT socket: %s", __func__, strerror(errno));
350 status = DAQ_RSTAT_ERROR;
351 break;
352 }
353 }
354
355 /*
356 On FreeBSD with IPFW divert, the socket address structure returned will look like this:
357 sin_len = 16 AKA sizeof(struct sockaddr_in)
358 sin_family = 2 AKA AF_INET
359 sin_port = <ipfw rule ID>
360 sin_addr = incoming ? first IP of ingress interface : 0.0.0.0
361 sin_zero[8] = name of interface (up to 8 characters)
362 */
363
364 /* Next, set up the DAQ message. Most fields are prepopulated and unchanging. */
365 DAQ_Msg_t *msg = &desc->msg;
366 msg->data_len = pktlen;
367
368 /* Then, set up the DAQ packet header. */
369 DAQ_PktHdr_t *pkthdr = &desc->pkthdr;
370 gettimeofday(&pkthdr->ts, NULL);
371 pkthdr->pktlen = pktlen;
372
373 /* Last, but not least, extract this descriptor from the free list and
374 place the message in the return vector. */
375 dc->pool.freelist = desc->next;
376 desc->next = NULL;
377 dc->pool.info.available--;
378 msgs[idx] = &desc->msg;
379
380 dc->stats.hw_packets_received++;
381 dc->stats.packets_received++;
382
383 idx++;
384 }
385 }
386
387 *rstat = status;
388
389 return idx;
390 }
391
392 static const DAQ_Verdict verdict_translation_table[MAX_DAQ_VERDICT] = {
393 DAQ_VERDICT_PASS, /* DAQ_VERDICT_PASS */
394 DAQ_VERDICT_BLOCK, /* DAQ_VERDICT_BLOCK */
395 DAQ_VERDICT_PASS, /* DAQ_VERDICT_REPLACE */
396 DAQ_VERDICT_PASS, /* DAQ_VERDICT_WHITELIST */
397 DAQ_VERDICT_BLOCK, /* DAQ_VERDICT_BLACKLIST */
398 DAQ_VERDICT_PASS /* DAQ_VERDICT_IGNORE */
399 };
400
divert_daq_msg_finalize(void * handle,const DAQ_Msg_t * msg,DAQ_Verdict verdict)401 static int divert_daq_msg_finalize(void *handle, const DAQ_Msg_t *msg, DAQ_Verdict verdict)
402 {
403 Divert_Context_t *dc = (Divert_Context_t *) handle;
404 DivertPktDesc *desc = (DivertPktDesc *) msg->priv;
405
406 /* Sanitize and enact the verdict. */
407 if (verdict >= MAX_DAQ_VERDICT)
408 verdict = DAQ_VERDICT_BLOCK;
409 dc->stats.verdicts[verdict]++;
410 verdict = verdict_translation_table[verdict];
411 if (dc->passive || verdict == DAQ_VERDICT_PASS)
412 {
413 ssize_t wrote = sendto(dc->sock, msg->data, msg->data_len, 0,
414 (struct sockaddr*) &desc->addr, sizeof(desc->addr));
415 if (wrote < 0 || (unsigned) wrote != msg->data_len)
416 {
417 SET_ERROR(dc->modinst, "%s: Couldn't send to the DIVERT socket: %s", __func__, strerror(errno));
418 return DAQ_ERROR;
419 }
420 }
421
422 /* Toss the descriptor back on the free list for reuse. */
423 desc->next = dc->pool.freelist;
424 dc->pool.freelist = desc;
425 dc->pool.info.available++;
426
427 return DAQ_SUCCESS;
428 }
429
divert_daq_interrupt(void * handle)430 static int divert_daq_interrupt(void *handle)
431 {
432 Divert_Context_t *dc = (Divert_Context_t *) handle;
433
434 dc->interrupted = true;
435
436 return DAQ_SUCCESS;
437 }
438
divert_daq_get_stats(void * handle,DAQ_Stats_t * stats)439 static int divert_daq_get_stats(void *handle, DAQ_Stats_t* stats)
440 {
441 Divert_Context_t *dc = (Divert_Context_t *) handle;
442
443 *stats = dc->stats;
444
445 return DAQ_SUCCESS;
446 }
447
divert_daq_reset_stats(void * handle)448 static void divert_daq_reset_stats(void *handle)
449 {
450 Divert_Context_t *dc = (Divert_Context_t *) handle;
451 memset(&dc->stats, 0, sizeof(dc->stats));
452 }
453
divert_daq_get_snaplen(void * handle)454 static int divert_daq_get_snaplen(void *handle)
455 {
456 Divert_Context_t *dc = (Divert_Context_t *) handle;
457 return dc->snaplen;
458 }
459
divert_daq_get_capabilities(void * handle)460 static uint32_t divert_daq_get_capabilities(void *handle)
461 {
462 return DAQ_CAPA_BLOCK | DAQ_CAPA_REPLACE | DAQ_CAPA_INJECT | DAQ_CAPA_INJECT_RAW
463 | DAQ_CAPA_INTERRUPT | DAQ_CAPA_UNPRIV_START;
464 }
465
divert_daq_get_datalink_type(void * handle)466 static int divert_daq_get_datalink_type(void *handle)
467 {
468 return DLT_RAW;
469 }
470
divert_daq_get_msg_pool_info(void * handle,DAQ_MsgPoolInfo_t * info)471 static int divert_daq_get_msg_pool_info(void *handle, DAQ_MsgPoolInfo_t *info)
472 {
473 Divert_Context_t *dc = (Divert_Context_t *) handle;
474
475 *info = dc->pool.info;
476
477 return DAQ_SUCCESS;
478 }
479
480 #ifdef BUILDING_SO
481 DAQ_SO_PUBLIC const DAQ_ModuleAPI_t DAQ_MODULE_DATA =
482 #else
483 const DAQ_ModuleAPI_t divert_daq_module_data =
484 #endif
485 {
486 /* .api_version = */ DAQ_MODULE_API_VERSION,
487 /* .api_size = */ sizeof(DAQ_ModuleAPI_t),
488 /* .module_version = */ DAQ_DIVERT_VERSION,
489 /* .name = */ "divert",
490 /* .type = */ DAQ_TYPE_INTF_CAPABLE | DAQ_TYPE_INLINE_CAPABLE | DAQ_TYPE_MULTI_INSTANCE,
491 /* .load = */ divert_daq_module_load,
492 /* .unload = */ divert_daq_module_unload,
493 /* .get_variable_descs = */ NULL,
494 /* .instantiate = */ divert_daq_instantiate,
495 /* .destroy = */ divert_daq_destroy,
496 /* .set_filter = */ NULL,
497 /* .start = */ divert_daq_start,
498 /* .inject = */ NULL,
499 /* .inject_relative = */ divert_daq_inject_relative,
500 /* .interrupt = */ divert_daq_interrupt,
501 /* .stop = */ divert_daq_stop,
502 /* .ioctl = */ NULL,
503 /* .get_stats = */ divert_daq_get_stats,
504 /* .reset_stats = */ divert_daq_reset_stats,
505 /* .get_snaplen = */ divert_daq_get_snaplen,
506 /* .get_capabilities = */ divert_daq_get_capabilities,
507 /* .get_datalink_type = */ divert_daq_get_datalink_type,
508 /* .config_load = */ NULL,
509 /* .config_swap = */ NULL,
510 /* .config_free = */ NULL,
511 /* .msg_receive = */ divert_daq_msg_receive,
512 /* .msg_finalize = */ divert_daq_msg_finalize,
513 /* .get_msg_pool_info = */ divert_daq_get_msg_pool_info,
514 };
515