void handle_falling_child() { struct plugins_list_entry *list = NULL; int j, ret; /* we first scan failed_plugins[] array for plugins failed during the startup phase: when we are building plugins_list, we cannot arbitrarily delete nodes (plugins) from it */ for (j = 0; j < MAX_N_PLUGINS; j++) { if (failed_plugins[j]) { list = search_plugin_by_pid(failed_plugins[j]); if (list) { Log(LOG_WARNING, "WARN: connection lost to '%s-%s'; closing connection.\n", list->name, list->type.string); close(list->pipe[1]); delete_pipe_channel(list->pipe[1]); ret = delete_plugin_by_id(list->id); if (!ret) { Log(LOG_WARNING, "WARN: no more plugins active. Shutting down.\n"); if (config.pidfile) remove_pid_file(config.pidfile); exit(1); } } failed_plugins[j] = 0; } else break; } j = waitpid(-1, 0, WNOHANG); list = search_plugin_by_pid(j); if (list) { Log(LOG_WARNING, "WARN: connection lost to '%s-%s'; closing connection.\n", list->name, list->type.string); close(list->pipe[1]); delete_pipe_channel(list->pipe[1]); ret = delete_plugin_by_id(list->id); if (!ret) { Log(LOG_WARNING, "WARN: no more plugins active. Shutting down.\n"); if (config.pidfile) remove_pid_file(config.pidfile); exit(1); } } signal(SIGCHLD, handle_falling_child); }
/* no AMQP: when not using map_shared, 'pipe_size' is the size of the pipe created with socketpair(); when map_shared is enabled, it refers to the size of the shared memory area */ void load_plugins(struct plugin_requests *req) { u_int64_t buf_pipe_ratio_sz = 0, pipe_idx = 0; int snd_buflen = 0, rcv_buflen = 0, socklen = 0, target_buflen = 0, ret; int nfprobe_id = 0, min_sz = 0; struct plugins_list_entry *list = plugins_list; int l = sizeof(list->cfg.pipe_size), offset = 0; struct channels_list_entry *chptr = NULL; init_random_seed(); init_pipe_channels(); while (list) { if ((*list->type.func)) { if (list->cfg.data_type & (PIPE_TYPE_METADATA|PIPE_TYPE_PAYLOAD|PIPE_TYPE_MSG)); else { Log(LOG_ERR, "ERROR ( %s/%s ): Data type not supported: %d\n", list->name, list->type.string, list->cfg.data_type); exit(1); } min_sz = ChBufHdrSz; if (list->cfg.data_type & PIPE_TYPE_METADATA) min_sz += PdataSz; if (list->cfg.data_type & PIPE_TYPE_PAYLOAD) { if (list->cfg.acct_type == ACCT_PM && list->cfg.snaplen) min_sz += (PpayloadSz+list->cfg.snaplen); else min_sz += (PpayloadSz+DEFAULT_PLOAD_SIZE); } if (list->cfg.data_type & PIPE_TYPE_EXTRAS) min_sz += PextrasSz; if (list->cfg.data_type & PIPE_TYPE_MSG) min_sz += PmsgSz; if (list->cfg.data_type & PIPE_TYPE_BGP) min_sz += sizeof(struct pkt_bgp_primitives); if (list->cfg.data_type & PIPE_TYPE_NAT) min_sz += sizeof(struct pkt_nat_primitives); if (list->cfg.data_type & PIPE_TYPE_MPLS) min_sz += sizeof(struct pkt_mpls_primitives); if (list->cfg.cpptrs.len) min_sz += list->cfg.cpptrs.len; if (list->cfg.data_type & PIPE_TYPE_VLEN) min_sz += sizeof(struct pkt_vlen_hdr_primitives); /* If nothing is supplied, let's hint some working default values */ if (!list->cfg.pipe_size || !list->cfg.buffer_size) { if (!list->cfg.pipe_size) list->cfg.pipe_size = 4096000; /* 4Mb */ if (!list->cfg.buffer_size) { if (list->cfg.pcap_savefile) list->cfg.buffer_size = 10240; /* 10Kb */ else list->cfg.buffer_size = MIN(min_sz, 10240); } } /* some validations */ if (list->cfg.pipe_size < min_sz) list->cfg.pipe_size = min_sz; if (list->cfg.buffer_size < min_sz) list->cfg.buffer_size = min_sz; if (list->cfg.buffer_size > list->cfg.pipe_size) list->cfg.buffer_size = list->cfg.pipe_size; /* if required let's align plugin_buffer_size to 4 bytes boundary */ #if NEED_ALIGN while (list->cfg.buffer_size % 4 != 0) list->cfg.buffer_size--; #endif if (!list->cfg.pipe_amqp) { /* creating communication channel */ socketpair(AF_UNIX, SOCK_DGRAM, 0, list->pipe); /* checking SO_RCVBUF and SO_SNDBUF values; if different we take the smaller one */ getsockopt(list->pipe[0], SOL_SOCKET, SO_RCVBUF, &rcv_buflen, &l); getsockopt(list->pipe[1], SOL_SOCKET, SO_SNDBUF, &snd_buflen, &l); socklen = (rcv_buflen < snd_buflen) ? rcv_buflen : snd_buflen; buf_pipe_ratio_sz = (list->cfg.pipe_size/list->cfg.buffer_size)*sizeof(char *); if (buf_pipe_ratio_sz > INT_MAX) { Log(LOG_ERR, "ERROR ( %s/%s ): Current plugin_buffer_size elems per plugin_pipe_size: %d. Max: %d.\nExiting.\n", list->name, list->type.string, (list->cfg.pipe_size/list->cfg.buffer_size), (INT_MAX/sizeof(char *))); exit_all(1); } else target_buflen = buf_pipe_ratio_sz; if (target_buflen > socklen) { Setsocksize(list->pipe[0], SOL_SOCKET, SO_RCVBUF, &target_buflen, l); Setsocksize(list->pipe[1], SOL_SOCKET, SO_SNDBUF, &target_buflen, l); } getsockopt(list->pipe[0], SOL_SOCKET, SO_RCVBUF, &rcv_buflen, &l); getsockopt(list->pipe[1], SOL_SOCKET, SO_SNDBUF, &snd_buflen, &l); if (rcv_buflen < snd_buflen) snd_buflen = rcv_buflen; if (snd_buflen < socklen) { Setsocksize(list->pipe[0], SOL_SOCKET, SO_RCVBUF, &socklen, l); Setsocksize(list->pipe[1], SOL_SOCKET, SO_SNDBUF, &socklen, l); getsockopt(list->pipe[0], SOL_SOCKET, SO_RCVBUF, &rcv_buflen, &l); getsockopt(list->pipe[1], SOL_SOCKET, SO_SNDBUF, &snd_buflen, &l); if (rcv_buflen < snd_buflen) snd_buflen = rcv_buflen; } if (list->cfg.debug || (list->cfg.pipe_size > WARNING_PIPE_SIZE)) { Log(LOG_INFO, "INFO ( %s/%s ): plugin_pipe_size=%llu bytes plugin_buffer_size=%llu bytes\n", list->name, list->type.string, list->cfg.pipe_size, list->cfg.buffer_size); if (target_buflen <= snd_buflen) Log(LOG_INFO, "INFO ( %s/%s ): ctrl channel: obtained=%d bytes target=%d bytes\n", list->name, list->type.string, snd_buflen, target_buflen); else /* This should return an error and exit but we fallback to a warning in order to be backward compatible */ Log(LOG_WARNING, "WARN ( %s/%s ): ctrl channel: obtained=%d bytes target=%d bytes\n", list->name, list->type.string, snd_buflen, target_buflen); } } else { pipe_idx++; list->pipe[0] = list->pipe[1] = pipe_idx; } list->cfg.name = list->name; list->cfg.type = list->type.string; list->cfg.type_id = list->type.id; chptr = insert_pipe_channel(list->type.id, &list->cfg, list->pipe[1]); if (!chptr) { Log(LOG_ERR, "ERROR ( %s/%s ): Unable to setup a new Core Process <-> Plugin channel.\nExiting.\n", list->name, list->type.string); exit_all(1); } else chptr->plugin = list; /* sets new value to be assigned to 'wakeup'; 'TRUE' disables on-request wakeup */ if (list->type.id == PLUGIN_ID_MEMORY) chptr->request = TRUE; /* sets fixed/vlen offsets and cleaner routine; XXX: we should refine the cleaner part: 1) ie. extras assumes it's automagically piled with metadata; 2) what if multiple vlen components are stacked up? */ if (list->cfg.data_type & PIPE_TYPE_METADATA) { chptr->clean_func = pkt_data_clean; offset = sizeof(struct pkt_data); } if (list->cfg.data_type & PIPE_TYPE_PAYLOAD) chptr->clean_func = pkt_payload_clean; if (list->cfg.data_type & PIPE_TYPE_EXTRAS) { chptr->extras.off_pkt_extras = offset; offset += sizeof(struct pkt_extras); } if (list->cfg.data_type & PIPE_TYPE_MSG) chptr->clean_func = pkt_msg_clean; if (list->cfg.data_type & PIPE_TYPE_BGP) { chptr->extras.off_pkt_bgp_primitives = offset; offset += sizeof(struct pkt_bgp_primitives); } else chptr->extras.off_pkt_bgp_primitives = 0; if (list->cfg.data_type & PIPE_TYPE_NAT) { chptr->extras.off_pkt_nat_primitives = offset; offset += sizeof(struct pkt_nat_primitives); } else chptr->extras.off_pkt_nat_primitives = 0; if (list->cfg.data_type & PIPE_TYPE_MPLS) { chptr->extras.off_pkt_mpls_primitives = offset; offset += sizeof(struct pkt_mpls_primitives); } else chptr->extras.off_pkt_mpls_primitives = 0; if (list->cfg.cpptrs.len) { chptr->extras.off_custom_primitives = offset; offset += list->cfg.cpptrs.len; } /* PIPE_TYPE_VLEN at the end of the stack so to not make vlen other structures (although possible it would not make much sense) */ if (list->cfg.data_type & PIPE_TYPE_VLEN) { chptr->extras.off_pkt_vlen_hdr_primitives = offset; offset += sizeof(struct pkt_vlen_hdr_primitives); } else chptr->extras.off_pkt_vlen_hdr_primitives = 0; /* any further offset beyond this point must be set to PM_VARIABLE_LENGTH so to indicate plugins to resolve value at runtime. */ chptr->datasize = min_sz-ChBufHdrSz; /* sets nfprobe ID */ if (list->type.id == PLUGIN_ID_NFPROBE) { list->cfg.nfprobe_id = nfprobe_id; nfprobe_id++; } switch (list->pid = fork()) { case -1: /* Something went wrong */ Log(LOG_WARNING, "WARN ( %s/%s ): Unable to initialize plugin: %s\n", list->name, list->type.string, strerror(errno)); delete_pipe_channel(list->pipe[1]); break; case 0: /* Child */ /* SIGCHLD handling issue: SysV avoids zombies by ignoring SIGCHLD; to emulate such semantics on BSD systems, we need an handler like handle_falling_child() */ #if defined (IRIX) || (SOLARIS) signal(SIGCHLD, SIG_IGN); #else signal(SIGCHLD, ignore_falling_child); #endif #if defined HAVE_MALLOPT mallopt(M_CHECK_ACTION, 0); #endif close(config.sock); close(config.bgp_sock); if (!list->cfg.pipe_amqp) close(list->pipe[1]); (*list->type.func)(list->pipe[0], &list->cfg, chptr); exit(0); default: /* Parent */ if (!list->cfg.pipe_amqp) { close(list->pipe[0]); setnonblocking(list->pipe[1]); } break; } /* some residual check */ if (chptr && list->cfg.a_filter) req->bpf_filter = TRUE; } list = list->next; } sort_pipe_channels(); /* define pre_tag_map(s) now so that they don't finish unnecessarily in plugin memory space */ { int ptm_index = 0, ptm_global = FALSE; char *ptm_ptr = NULL; list = plugins_list; while (list) { if (list->cfg.pre_tag_map) { if (!ptm_index) { ptm_ptr = list->cfg.pre_tag_map; ptm_global = TRUE; } else { if (!ptm_ptr || strcmp(ptm_ptr, list->cfg.pre_tag_map)) ptm_global = FALSE; } load_pre_tag_map(config.acct_type, list->cfg.pre_tag_map, &list->cfg.ptm, req, &list->cfg.ptm_alloc, list->cfg.maps_entries, list->cfg.maps_row_len); } list = list->next; ptm_index++; } /* enforcing global flag */ list = plugins_list; while (list) { list->cfg.ptm_global = ptm_global; list = list->next; } } /* AMQP handling, if required */ #ifdef WITH_RABBITMQ { int ret, index, index2; for (index = 0; channels_list[index].aggregation || channels_list[index].aggregation_2; index++) { chptr = &channels_list[index]; list = chptr->plugin; if (list->cfg.pipe_amqp) { plugin_pipe_amqp_init_host(&chptr->amqp_host, list); ret = p_amqp_connect_to_publish(&chptr->amqp_host); if (ret) plugin_pipe_amqp_sleeper_start(chptr); } /* reset core process pipe AMQP routing key */ if (list->type.id == PLUGIN_ID_CORE) list->cfg.pipe_amqp_routing_key = NULL; } for (index = 0; channels_list[index].aggregation || channels_list[index].aggregation_2; index++) { struct plugins_list_entry *list2 = plugins_list; struct channels_list_entry *chptr2 = NULL; chptr = &channels_list[index]; list = chptr->plugin; for (index2 = index; channels_list[index2].aggregation || channels_list[index2].aggregation_2; index2++) { chptr2 = &channels_list[index2]; list2 = chptr2->plugin; if (index2 > index && list->cfg.pipe_amqp_exchange && list->cfg.pipe_amqp_routing_key) { if (!strcmp(list->cfg.pipe_amqp_exchange, list2->cfg.pipe_amqp_exchange) && !strcmp(list->cfg.pipe_amqp_routing_key, list2->cfg.pipe_amqp_routing_key)) { Log(LOG_ERR, "ERROR ( %s/%s ): Duplicated plugin_pipe_amqp_exchange, plugin_pipe_amqp_routing_key: %s, %s\nExiting.\n", list->name, list->type.string, list->cfg.pipe_amqp_exchange, list->cfg.pipe_amqp_routing_key); exit(1); } } } } } #endif /* Kafka handling, if required */ #ifdef WITH_KAFKA { int ret, index, index2; for (index = 0; channels_list[index].aggregation || channels_list[index].aggregation_2; index++) { chptr = &channels_list[index]; list = chptr->plugin; /* XXX: no sleeper thread, trusting librdkafka */ if (list->cfg.pipe_kafka) ret = plugin_pipe_kafka_init_host(&chptr->kafka_host, list, TRUE); /* reset core process pipe Kafka topic */ if (list->type.id == PLUGIN_ID_CORE) list->cfg.pipe_kafka_topic = NULL; } for (index = 0; channels_list[index].aggregation || channels_list[index].aggregation_2; index++) { struct plugins_list_entry *list2 = plugins_list; struct channels_list_entry *chptr2 = NULL; chptr = &channels_list[index]; list = chptr->plugin; for (index2 = index; channels_list[index2].aggregation || channels_list[index2].aggregation_2; index2++) { chptr2 = &channels_list[index2]; list2 = chptr2->plugin; if (index2 > index && list->cfg.pipe_kafka_broker_host && list->cfg.pipe_kafka_topic) { if (!strcmp(list->cfg.pipe_kafka_broker_host, list2->cfg.pipe_kafka_broker_host) && list->cfg.pipe_kafka_broker_port == list2->cfg.pipe_kafka_broker_port && !strcmp(list->cfg.pipe_kafka_topic, list2->cfg.pipe_kafka_topic) /* && XXX: topic partition too? */ ) { Log(LOG_ERR, "ERROR ( %s/%s ): Duplicated plugin_pipe_kafka_broker_*, plugin_pipe_kafka_topic: %s, %s, %s\nExiting.\n", list->name, list->type.string, list->cfg.pipe_kafka_broker_host, list->cfg.pipe_kafka_broker_port, list->cfg.pipe_kafka_topic); exit(1); } } } } } #endif }