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