void exec_plugins(struct packet_ptrs *pptrs, struct plugin_requests *req) { int saved_have_tag = FALSE, saved_have_tag2 = FALSE, saved_have_label = FALSE; pm_id_t saved_tag = 0, saved_tag2 = 0; pt_label_t saved_label; int num, ret, fixed_size, already_reprocessed = 0; u_int32_t savedptr; char *bptr; int index, got_tags = FALSE; pretag_init_label(&saved_label); #if defined WITH_GEOIPV2 if (reload_geoipv2_file && config.geoipv2_file) { pm_geoipv2_close(); pm_geoipv2_init(); reload_geoipv2_file = FALSE; } #endif for (index = 0; channels_list[index].aggregation || channels_list[index].aggregation_2; index++) { struct plugins_list_entry *p = channels_list[index].plugin; if (p->cfg.pre_tag_map && find_id_func) { if (p->cfg.ptm_global && got_tags) { pptrs->tag = saved_tag; pptrs->tag2 = saved_tag2; pretag_copy_label(&pptrs->label, &saved_label); pptrs->have_tag = saved_have_tag; pptrs->have_tag2 = saved_have_tag2; pptrs->have_label = saved_have_label; } else { find_id_func(&p->cfg.ptm, pptrs, &pptrs->tag, &pptrs->tag2); if (p->cfg.ptm_global) { saved_tag = pptrs->tag; saved_tag2 = pptrs->tag2; pretag_copy_label(&saved_label, &pptrs->label); saved_have_tag = pptrs->have_tag; saved_have_tag2 = pptrs->have_tag2; saved_have_label = pptrs->have_label; got_tags = TRUE; } } } if (evaluate_filters(&channels_list[index].agg_filter, pptrs->packet_ptr, pptrs->pkthdr) && !evaluate_tags(&channels_list[index].tag_filter, pptrs->tag) && !evaluate_tags(&channels_list[index].tag2_filter, pptrs->tag2) && !evaluate_labels(&channels_list[index].label_filter, &pptrs->label) && !check_shadow_status(pptrs, &channels_list[index])) { /* arranging buffer: supported primitives + packet total length */ reprocess: channels_list[index].reprocess = FALSE; num = 0; /* rg.ptr points to slot's base address into the ring (shared memory); bufptr works as a displacement into the slot to place sequentially packets */ bptr = channels_list[index].rg.ptr+ChBufHdrSz+channels_list[index].bufptr; fixed_size = (*channels_list[index].clean_func)(bptr, channels_list[index].datasize); channels_list[index].var_size = 0; savedptr = channels_list[index].bufptr; reset_fallback_status(pptrs); while (channels_list[index].phandler[num]) { (*channels_list[index].phandler[num])(&channels_list[index], pptrs, &bptr); num++; } if (channels_list[index].s.rate && !channels_list[index].s.sampled_pkts) { channels_list[index].reprocess = FALSE; channels_list[index].bufptr = savedptr; channels_list[index].hdr.num--; /* let's cheat this value as it will get increased later */ fixed_size = 0; channels_list[index].var_size = 0; } if (channels_list[index].reprocess) { /* Let's check if we have an issue with the buffer size */ if (already_reprocessed) { struct plugins_list_entry *list = channels_list[index].plugin; Log(LOG_ERR, "ERROR ( %s/%s ): plugin_buffer_size is too short.\n", list->name, list->type.string); exit_all(1); } already_reprocessed = TRUE; /* Let's cheat the size in order to send out the current buffer */ fixed_size = channels_list[index].plugin->cfg.pipe_size; } else { channels_list[index].hdr.num++; channels_list[index].bufptr += (fixed_size + channels_list[index].var_size); } if ((channels_list[index].bufptr+fixed_size) > channels_list[index].bufend || channels_list[index].hdr.num == INT_MAX) { channels_list[index].hdr.seq++; channels_list[index].hdr.seq %= MAX_SEQNUM; /* let's commit the buffer we just finished writing */ ((struct ch_buf_hdr *)channels_list[index].rg.ptr)->seq = channels_list[index].hdr.seq; ((struct ch_buf_hdr *)channels_list[index].rg.ptr)->num = channels_list[index].hdr.num; ((struct ch_buf_hdr *)channels_list[index].rg.ptr)->core_pid = channels_list[index].core_pid; if (config.debug_internal_msg) { struct plugins_list_entry *list = channels_list[index].plugin; Log(LOG_DEBUG, "DEBUG ( %s/%s ): buffer released cpid=%u seq=%u num_entries=%u\n", list->name, list->type.string, channels_list[index].core_pid, channels_list[index].hdr.seq, channels_list[index].hdr.num); } /* sending the buffer to the AMQP broker */ if (channels_list[index].plugin->cfg.pipe_amqp) { #ifdef WITH_RABBITMQ struct channels_list_entry *chptr = &channels_list[index]; plugin_pipe_amqp_sleeper_stop(chptr); if (!chptr->amqp_host_sleep) ret = p_amqp_publish_binary(&chptr->amqp_host, chptr->rg.ptr, chptr->bufsize); else ret = FALSE; if (ret) plugin_pipe_amqp_sleeper_start(chptr); #endif } /* sending the buffer to the Kafka broker */ else if (channels_list[index].plugin->cfg.pipe_kafka) { #ifdef WITH_KAFKA struct channels_list_entry *chptr = &channels_list[index]; /* XXX: no sleeper thread, trusting librdkafka */ ret = p_kafka_produce_data(&chptr->kafka_host, chptr->rg.ptr, chptr->bufsize); #endif } else { if (channels_list[index].status->wakeup) { channels_list[index].status->backlog++; if (channels_list[index].status->backlog > ((channels_list[index].plugin->cfg.pipe_size/channels_list[index].plugin->cfg.buffer_size) *channels_list[index].plugin->cfg.pipe_backlog)/100) { channels_list[index].status->wakeup = channels_list[index].request; if (write(channels_list[index].pipe, &channels_list[index].rg.ptr, CharPtrSz) != CharPtrSz) { struct plugins_list_entry *list = channels_list[index].plugin; Log(LOG_WARNING, "WARN ( %s/%s ): Failed during write: %s\n", list->name, list->type.string, strerror(errno)); } channels_list[index].status->backlog = 0; } } } channels_list[index].rg.ptr += channels_list[index].bufsize; if ((channels_list[index].rg.ptr+channels_list[index].bufsize) > channels_list[index].rg.end) channels_list[index].rg.ptr = channels_list[index].rg.base; /* let's protect the buffer we are going to write */ ((struct ch_buf_hdr *)channels_list[index].rg.ptr)->seq = -1; ((struct ch_buf_hdr *)channels_list[index].rg.ptr)->num = 0; ((struct ch_buf_hdr *)channels_list[index].rg.ptr)->core_pid = 0; /* rewind pointer */ channels_list[index].bufptr = channels_list[index].buf; channels_list[index].hdr.num = 0; if (channels_list[index].reprocess) goto reprocess; /* if reading from a savefile, let's sleep a bit after having sent over a buffer worth of data */ if (channels_list[index].plugin->cfg.pcap_savefile) usleep(1000); /* 1 msec */ } } pptrs->tag = 0; pptrs->tag2 = 0; pretag_free_label(&pptrs->label); } /* check if we have to reload the map: new loop is to ensure we reload it for all plugins and prevent any timing issues with pointers to labels */ if (reload_map_exec_plugins) { for (index = 0; channels_list[index].aggregation || channels_list[index].aggregation_2; index++) { struct plugins_list_entry *p = channels_list[index].plugin; if (p->cfg.pre_tag_map && find_id_func) { load_pre_tag_map(config.acct_type, p->cfg.pre_tag_map, &p->cfg.ptm, req, &p->cfg.ptm_alloc, p->cfg.maps_entries, p->cfg.maps_row_len); } } } /* cleanups */ reload_map_exec_plugins = FALSE; pretag_free_label(&saved_label); }
void tee_plugin(int pipe_fd, struct configuration *cfgptr, void *ptr) { struct pkt_msg *msg; unsigned char *pipebuf; struct pollfd pfd; int timeout, err; int ret, num, fd, pool_idx, recv_idx; struct ring *rg = &((struct channels_list_entry *)ptr)->rg; struct ch_status *status = ((struct channels_list_entry *)ptr)->status; u_int32_t bufsz = ((struct channels_list_entry *)ptr)->bufsize; char *dataptr, dest_addr[256], dest_serv[256]; struct tee_receiver *target = NULL; struct plugin_requests req; unsigned char *rgptr; int pollagain = TRUE; u_int32_t seq = 1, rg_err_count = 0; memcpy(&config, cfgptr, sizeof(struct configuration)); recollect_pipe_memory(ptr); pm_setproctitle("%s [%s]", "Tee Plugin", config.name); if (config.pidfile) write_pid_file_plugin(config.pidfile, config.type, config.name); if (config.logfile) { fclose(config.logfile_fd); config.logfile_fd = open_logfile(config.logfile, "a"); } if (config.proc_priority) { int ret; ret = setpriority(PRIO_PROCESS, 0, config.proc_priority); if (ret) Log(LOG_WARNING, "WARN ( %s/%s ): proc_priority failed (errno: %d)\n", config.name, config.type, errno); else Log(LOG_INFO, "INFO ( %s/%s ): proc_priority set to %d\n", config.name, config.type, getpriority(PRIO_PROCESS, 0)); } /* signal handling */ signal(SIGINT, Tee_exit_now); signal(SIGUSR1, SIG_IGN); signal(SIGUSR2, reload_maps); /* sets to true the reload_maps flag */ signal(SIGPIPE, SIG_IGN); signal(SIGCHLD, SIG_IGN); if (config.tee_transparent && getuid() != 0) { Log(LOG_ERR, "ERROR ( %s/%s ): Transparent mode requires super-user permissions. Exiting ...\n", config.name, config.type); exit_plugin(1); } if (config.nfprobe_receiver && config.tee_receivers) { Log(LOG_ERR, "ERROR ( %s/%s ): tee_receiver and tee_receivers are mutually exclusive. Exiting ...\n", config.name, config.type); exit_plugin(1); } else if (!config.nfprobe_receiver && !config.tee_receivers) { Log(LOG_ERR, "ERROR ( %s/%s ): No receivers specified: tee_receiver or tee_receivers is required. Exiting ...\n", config.name, config.type); exit_plugin(1); } memset(&receivers, 0, sizeof(receivers)); memset(&req, 0, sizeof(req)); reload_map = FALSE; /* Setting up pools */ if (!config.tee_max_receiver_pools) config.tee_max_receiver_pools = MAX_TEE_POOLS; receivers.pools = malloc((config.tee_max_receiver_pools+1)*sizeof(struct tee_receivers_pool)); if (!receivers.pools) { Log(LOG_ERR, "ERROR ( %s/%s ): unable to allocate receiver pools. Exiting ...\n", config.name, config.type); exit_plugin(1); } else memset(receivers.pools, 0, (config.tee_max_receiver_pools+1)*sizeof(struct tee_receivers_pool)); /* Setting up receivers per pool */ if (!config.tee_max_receivers) config.tee_max_receivers = MAX_TEE_RECEIVERS; for (pool_idx = 0; pool_idx < MAX_TEE_POOLS; pool_idx++) { receivers.pools[pool_idx].receivers = malloc(config.tee_max_receivers*sizeof(struct tee_receivers)); if (!receivers.pools[pool_idx].receivers) { Log(LOG_ERR, "ERROR ( %s/%s ): unable to allocate receivers for pool #%u. Exiting ...\n", config.name, config.type, pool_idx); exit_plugin(1); } else memset(receivers.pools[pool_idx].receivers, 0, config.tee_max_receivers*sizeof(struct tee_receivers)); } if (config.nfprobe_receiver) { pool_idx = 0; recv_idx = 0; target = &receivers.pools[pool_idx].receivers[recv_idx]; target->dest_len = sizeof(target->dest); if (Tee_parse_hostport(config.nfprobe_receiver, (struct sockaddr *) &target->dest, &target->dest_len)) { Log(LOG_ERR, "ERROR ( %s/%s ): Invalid receiver %s . ", config.name, config.type, config.nfprobe_receiver); exit_plugin(1); } else { recv_idx++; receivers.pools[pool_idx].num = recv_idx; pool_idx++; receivers.num = pool_idx; } } else if (config.tee_receivers) { int recvs_allocated = FALSE; req.key_value_table = (void *) &receivers; load_id_file(MAP_TEE_RECVS, config.tee_receivers, NULL, &req, &recvs_allocated); } config.sql_refresh_time = DEFAULT_TEE_REFRESH_TIME; timeout = config.sql_refresh_time*1000; pipebuf = (unsigned char *) Malloc(config.buffer_size); pfd.fd = pipe_fd; pfd.events = POLLIN; setnonblocking(pipe_fd); memset(pipebuf, 0, config.buffer_size); err_cant_bridge_af = 0; /* Arrange send socket */ Tee_init_socks(); /* plugin main loop */ for (;;) { poll_again: status->wakeup = TRUE; ret = poll(&pfd, 1, timeout); if (ret < 0) goto poll_again; if (reload_map) { int recvs_allocated = FALSE; Tee_destroy_recvs(); load_id_file(MAP_TEE_RECVS, config.tee_receivers, NULL, &req, &recvs_allocated); Tee_init_socks(); reload_map = FALSE; } switch (ret) { case 0: /* timeout */ /* reserved for future since we don't currently cache/batch/etc */ break; default: /* we received data */ read_data: if (!pollagain) { seq++; seq %= MAX_SEQNUM; if (seq == 0) rg_err_count = FALSE; } else { if ((ret = read(pipe_fd, &rgptr, sizeof(rgptr))) == 0) exit_plugin(1); /* we exit silently; something happened at the write end */ } if ((rg->ptr + bufsz) > rg->end) rg->ptr = rg->base; if (((struct ch_buf_hdr *)rg->ptr)->seq != seq) { if (!pollagain) { pollagain = TRUE; goto poll_again; } else { rg_err_count++; if (config.debug || (rg_err_count > MAX_RG_COUNT_ERR)) { Log(LOG_ERR, "ERROR ( %s/%s ): We are missing data.\n", config.name, config.type); Log(LOG_ERR, "If you see this message once in a while, discard it. Otherwise some solutions follow:\n"); Log(LOG_ERR, "- increase shared memory size, 'plugin_pipe_size'; now: '%u'.\n", config.pipe_size); Log(LOG_ERR, "- increase buffer size, 'plugin_buffer_size'; now: '%u'.\n", config.buffer_size); Log(LOG_ERR, "- increase system maximum socket size.\n\n"); } seq = ((struct ch_buf_hdr *)rg->ptr)->seq; } } pollagain = FALSE; memcpy(pipebuf, rg->ptr, bufsz); rg->ptr += bufsz; msg = (struct pkt_msg *) (pipebuf+sizeof(struct ch_buf_hdr)); Log(LOG_DEBUG, "DEBUG ( %s/%s ): buffer received seq=%u num_entries=%u\n", config.name, config.type, seq, ((struct ch_buf_hdr *)pipebuf)->num); while (((struct ch_buf_hdr *)pipebuf)->num > 0) { for (pool_idx = 0; pool_idx < receivers.num; pool_idx++) { if (!evaluate_tags(&receivers.pools[pool_idx].tag_filter, msg->tag)) { if (!receivers.pools[pool_idx].balance.func) { for (recv_idx = 0; recv_idx < receivers.pools[pool_idx].num; recv_idx++) { target = &receivers.pools[pool_idx].receivers[recv_idx]; Tee_send(msg, (struct sockaddr *) &target->dest, target->fd); } } else { target = receivers.pools[pool_idx].balance.func(&receivers.pools[pool_idx], msg); Tee_send(msg, (struct sockaddr *) &target->dest, target->fd); } } } ((struct ch_buf_hdr *)pipebuf)->num--; if (((struct ch_buf_hdr *)pipebuf)->num) { dataptr = (unsigned char *) msg; dataptr += PmsgSz; msg = (struct pkt_msg *) dataptr; } } goto read_data; } } }