void amqp_avro_schema_purge(char *avro_schema_str) { struct p_amqp_host amqp_avro_schema_host; int ret; if (!avro_schema_str || !config.amqp_avro_schema_routing_key) return; /* setting some defaults */ if (!config.sql_host) config.sql_host = default_amqp_host; if (!config.sql_db) config.sql_db = default_amqp_exchange; if (!config.amqp_exchange_type) config.amqp_exchange_type = default_amqp_exchange_type; if (!config.amqp_vhost) config.amqp_vhost = default_amqp_vhost; p_amqp_init_host(&amqp_avro_schema_host); p_amqp_set_user(&amqp_avro_schema_host, config.sql_user); p_amqp_set_passwd(&amqp_avro_schema_host, config.sql_passwd); p_amqp_set_exchange(&amqp_avro_schema_host, config.sql_db); p_amqp_set_routing_key(&amqp_avro_schema_host, config.amqp_avro_schema_routing_key); p_amqp_set_exchange_type(&amqp_avro_schema_host, config.amqp_exchange_type); p_amqp_set_host(&amqp_avro_schema_host, config.sql_host); p_amqp_set_vhost(&amqp_avro_schema_host, config.amqp_vhost); p_amqp_set_persistent_msg(&amqp_avro_schema_host, config.amqp_persistent_msg); p_amqp_set_frame_max(&amqp_avro_schema_host, config.amqp_frame_max); p_amqp_set_content_type_json(&amqp_avro_schema_host); ret = p_amqp_connect_to_publish(&amqp_avro_schema_host); if (ret) return; ret = p_amqp_publish_string(&amqp_avro_schema_host, avro_schema_str); p_amqp_close(&amqp_avro_schema_host, FALSE); }
/* 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 }
void amqp_cache_purge(struct chained_cache *queue[], int index, int safe_action) { struct pkt_primitives *data = NULL; struct pkt_bgp_primitives *pbgp = NULL; struct pkt_nat_primitives *pnat = NULL; struct pkt_mpls_primitives *pmpls = NULL; struct pkt_tunnel_primitives *ptun = NULL; char *pcust = NULL; struct pkt_vlen_hdr_primitives *pvlen = NULL; struct pkt_bgp_primitives empty_pbgp; struct pkt_nat_primitives empty_pnat; struct pkt_mpls_primitives empty_pmpls; struct pkt_tunnel_primitives empty_ptun; char *empty_pcust = NULL; char src_mac[18], dst_mac[18], src_host[INET6_ADDRSTRLEN], dst_host[INET6_ADDRSTRLEN], ip_address[INET6_ADDRSTRLEN]; char rd_str[SRVBUFLEN], misc_str[SRVBUFLEN], dyn_amqp_routing_key[SRVBUFLEN], *orig_amqp_routing_key = NULL; int i, j, stop, batch_idx, is_routing_key_dyn = FALSE, qn = 0, ret, saved_index = index; int mv_num = 0, mv_num_save = 0; time_t start, duration; pid_t writer_pid = getpid(); char *json_buf = NULL; int json_buf_off = 0; #ifdef WITH_AVRO avro_writer_t avro_writer; char *avro_buf = NULL; int avro_buffer_full = FALSE; #endif /* setting some defaults */ if (!config.sql_host) config.sql_host = default_amqp_host; if (!config.sql_db) config.sql_db = default_amqp_exchange; if (!config.amqp_exchange_type) config.amqp_exchange_type = default_amqp_exchange_type; if (!config.amqp_vhost) config.amqp_vhost = default_amqp_vhost; if (!config.sql_table) config.sql_table = default_amqp_routing_key; else { if (strchr(config.sql_table, '$')) { is_routing_key_dyn = TRUE; orig_amqp_routing_key = config.sql_table; config.sql_table = dyn_amqp_routing_key; } } if (config.amqp_routing_key_rr) { orig_amqp_routing_key = config.sql_table; config.sql_table = dyn_amqp_routing_key; } p_amqp_set_exchange(&amqpp_amqp_host, config.sql_db); p_amqp_set_routing_key(&amqpp_amqp_host, config.sql_table); p_amqp_set_exchange_type(&amqpp_amqp_host, config.amqp_exchange_type); p_amqp_set_host(&amqpp_amqp_host, config.sql_host); p_amqp_set_vhost(&amqpp_amqp_host, config.amqp_vhost); p_amqp_set_persistent_msg(&amqpp_amqp_host, config.amqp_persistent_msg); p_amqp_set_frame_max(&amqpp_amqp_host, config.amqp_frame_max); if (config.message_broker_output & PRINT_OUTPUT_JSON) p_amqp_set_content_type_json(&amqpp_amqp_host); else if (config.message_broker_output & PRINT_OUTPUT_AVRO) p_amqp_set_content_type_binary(&amqpp_amqp_host); else { Log(LOG_ERR, "ERROR ( %s/%s ): Unsupported amqp_output value specified. Exiting.\n", config.name, config.type); exit_plugin(1); } p_amqp_init_routing_key_rr(&amqpp_amqp_host); p_amqp_set_routing_key_rr(&amqpp_amqp_host, config.amqp_routing_key_rr); empty_pcust = malloc(config.cpptrs.len); if (!empty_pcust) { Log(LOG_ERR, "ERROR ( %s/%s ): Unable to malloc() empty_pcust. Exiting.\n", config.name, config.type); exit_plugin(1); } memset(&empty_pbgp, 0, sizeof(struct pkt_bgp_primitives)); memset(&empty_pnat, 0, sizeof(struct pkt_nat_primitives)); memset(&empty_pmpls, 0, sizeof(struct pkt_mpls_primitives)); memset(&empty_ptun, 0, sizeof(struct pkt_tunnel_primitives)); memset(empty_pcust, 0, config.cpptrs.len); ret = p_amqp_connect_to_publish(&amqpp_amqp_host); if (ret) return; for (j = 0, stop = 0; (!stop) && P_preprocess_funcs[j]; j++) stop = P_preprocess_funcs[j](queue, &index, j); Log(LOG_INFO, "INFO ( %s/%s ): *** Purging cache - START (PID: %u) ***\n", config.name, config.type, writer_pid); start = time(NULL); if (config.print_markers) { if (config.message_broker_output & PRINT_OUTPUT_JSON || config.message_broker_output & PRINT_OUTPUT_AVRO) { void *json_obj; char *json_str; json_obj = compose_purge_init_json(config.name, writer_pid); if (json_obj) json_str = compose_json_str(json_obj); if (json_str) { Log(LOG_DEBUG, "DEBUG ( %s/%s ): %s\n\n", config.name, config.type, json_str); ret = p_amqp_publish_string(&amqpp_amqp_host, json_str); free(json_str); json_str = NULL; } } } if (config.message_broker_output & PRINT_OUTPUT_JSON) { if (config.sql_multi_values) { json_buf = malloc(config.sql_multi_values); if (!json_buf) { Log(LOG_ERR, "ERROR ( %s/%s ): malloc() failed (json_buf). Exiting ..\n", config.name, config.type); exit_plugin(1); } else memset(json_buf, 0, config.sql_multi_values); } } else if (config.message_broker_output & PRINT_OUTPUT_AVRO) { #ifdef WITH_AVRO if (!config.avro_buffer_size) config.avro_buffer_size = LARGEBUFLEN; avro_buf = malloc(config.avro_buffer_size); if (!avro_buf) { Log(LOG_ERR, "ERROR ( %s/%s ): malloc() failed (avro_buf). Exiting ..\n", config.name, config.type); exit_plugin(1); } avro_writer = avro_writer_memory(avro_buf, config.avro_buffer_size); #endif } for (j = 0; j < index; j++) { void *json_obj; char *json_str; if (queue[j]->valid != PRINT_CACHE_COMMITTED) continue; data = &queue[j]->primitives; if (queue[j]->pbgp) pbgp = queue[j]->pbgp; else pbgp = &empty_pbgp; if (queue[j]->pnat) pnat = queue[j]->pnat; else pnat = &empty_pnat; if (queue[j]->pmpls) pmpls = queue[j]->pmpls; else pmpls = &empty_pmpls; if (queue[j]->ptun) ptun = queue[j]->ptun; else ptun = &empty_ptun; if (queue[j]->pcust) pcust = queue[j]->pcust; else pcust = empty_pcust; if (queue[j]->pvlen) pvlen = queue[j]->pvlen; else pvlen = NULL; if (queue[j]->valid == PRINT_CACHE_FREE) continue; if (config.message_broker_output & PRINT_OUTPUT_JSON) { #ifdef WITH_JANSSON json_t *json_obj = json_object(); int idx; for (idx = 0; idx < N_PRIMITIVES && cjhandler[idx]; idx++) cjhandler[idx](json_obj, queue[j]); add_writer_name_and_pid_json(json_obj, config.name, writer_pid); json_str = compose_json_str(json_obj); #endif } else if (config.message_broker_output & PRINT_OUTPUT_AVRO) { #ifdef WITH_AVRO avro_value_iface_t *avro_iface = avro_generic_class_from_schema(avro_acct_schema); avro_value_t avro_value = compose_avro(config.what_to_count, config.what_to_count_2, queue[j]->flow_type, &queue[j]->primitives, pbgp, pnat, pmpls, ptun, pcust, pvlen, queue[j]->bytes_counter, queue[j]->packet_counter, queue[j]->flow_counter, queue[j]->tcp_flags, &queue[j]->basetime, queue[j]->stitch, avro_iface); size_t avro_value_size; add_writer_name_and_pid_avro(avro_value, config.name, writer_pid); avro_value_sizeof(&avro_value, &avro_value_size); if (avro_value_size > config.avro_buffer_size) { Log(LOG_ERR, "ERROR ( %s/%s ): AVRO; insufficient buffer size (avro_buffer_size=%u)\n", config.name, config.type, config.avro_buffer_size); Log(LOG_ERR, "ERROR ( %s/%s ): AVRO: increase value or look for avro_buffer_size in CONFIG-KEYS document.\n\n", config.name, config.type); exit_plugin(1); } else if (avro_value_size >= (config.avro_buffer_size - avro_writer_tell(avro_writer))) { avro_buffer_full = TRUE; j--; } else if (avro_value_write(avro_writer, &avro_value)) { Log(LOG_ERR, "ERROR ( %s/%s ): ARVO: unable to write value: %s\n", config.name, config.type, avro_strerror()); exit_plugin(1); } else { mv_num++; } avro_value_decref(&avro_value); avro_value_iface_decref(avro_iface); #else if (config.debug) Log(LOG_DEBUG, "DEBUG ( %s/%s ): compose_avro(): AVRO object not created due to missing --enable-avro\n", config.name, config.type); #endif } if (config.message_broker_output & PRINT_OUTPUT_JSON) { char *tmp_str = NULL; if (json_str && config.sql_multi_values) { int json_strlen = (strlen(json_str) ? (strlen(json_str) + 1) : 0); if (json_strlen >= (config.sql_multi_values - json_buf_off)) { if (json_strlen >= config.sql_multi_values) { Log(LOG_ERR, "ERROR ( %s/%s ): amqp_multi_values not large enough to store JSON elements. Exiting ..\n", config.name, config.type); exit(1); } tmp_str = json_str; json_str = json_buf; } else { strcat(json_buf, json_str); mv_num++; string_add_newline(json_buf); json_buf_off = strlen(json_buf); free(json_str); json_str = NULL; } } if (json_str) { if (is_routing_key_dyn) { P_handle_table_dyn_strings(dyn_amqp_routing_key, SRVBUFLEN, orig_amqp_routing_key, queue[j]); p_amqp_set_routing_key(&amqpp_amqp_host, dyn_amqp_routing_key); } if (config.amqp_routing_key_rr) { P_handle_table_dyn_rr(dyn_amqp_routing_key, SRVBUFLEN, orig_amqp_routing_key, &amqpp_amqp_host.rk_rr); p_amqp_set_routing_key(&amqpp_amqp_host, dyn_amqp_routing_key); } Log(LOG_DEBUG, "DEBUG ( %s/%s ): %s\n\n", config.name, config.type, json_str); ret = p_amqp_publish_string(&amqpp_amqp_host, json_str); if (config.sql_multi_values) { json_str = tmp_str; strcpy(json_buf, json_str); mv_num_save = mv_num; mv_num = 1; string_add_newline(json_buf); json_buf_off = strlen(json_buf); } free(json_str); json_str = NULL; if (!ret) { if (!config.sql_multi_values) qn++; else qn += mv_num_save; } else break; } } else if (config.message_broker_output & PRINT_OUTPUT_AVRO) { #ifdef WITH_AVRO if (!config.sql_multi_values || (mv_num >= config.sql_multi_values) || avro_buffer_full) { if (is_routing_key_dyn) { P_handle_table_dyn_strings(dyn_amqp_routing_key, SRVBUFLEN, orig_amqp_routing_key, queue[j]); p_amqp_set_routing_key(&amqpp_amqp_host, dyn_amqp_routing_key); } if (config.amqp_routing_key_rr) { P_handle_table_dyn_rr(dyn_amqp_routing_key, SRVBUFLEN, orig_amqp_routing_key, &amqpp_amqp_host.rk_rr); p_amqp_set_routing_key(&amqpp_amqp_host, dyn_amqp_routing_key); } ret = p_amqp_publish_binary(&amqpp_amqp_host, avro_buf, avro_writer_tell(avro_writer)); avro_writer_reset(avro_writer); avro_buffer_full = FALSE; mv_num_save = mv_num; mv_num = 0; if (!ret) qn += mv_num_save; else break; } #endif } } if (config.sql_multi_values) { if (config.message_broker_output & PRINT_OUTPUT_JSON) { if (json_buf && json_buf_off) { /* no handling of dyn routing keys here: not compatible */ Log(LOG_DEBUG, "DEBUG ( %s/%s ): %s\n\n", config.name, config.type, json_buf); ret = p_amqp_publish_string(&amqpp_amqp_host, json_buf); if (!ret) qn += mv_num; } } else if (config.message_broker_output & PRINT_OUTPUT_AVRO) { #ifdef WITH_AVRO if (avro_writer_tell(avro_writer)) { ret = p_amqp_publish_binary(&amqpp_amqp_host, avro_buf, avro_writer_tell(avro_writer)); avro_writer_free(avro_writer); if (!ret) qn += mv_num; } #endif } } duration = time(NULL)-start; if (config.print_markers) { if (config.message_broker_output & PRINT_OUTPUT_JSON || config.message_broker_output & PRINT_OUTPUT_AVRO) { void *json_obj; char *json_str; json_obj = compose_purge_close_json(config.name, writer_pid, qn, saved_index, duration); if (json_obj) json_str = compose_json_str(json_obj); if (json_str) { Log(LOG_DEBUG, "DEBUG ( %s/%s ): %s\n\n", config.name, config.type, json_str); ret = p_amqp_publish_string(&amqpp_amqp_host, json_str); free(json_str); json_str = NULL; } } } p_amqp_close(&amqpp_amqp_host, FALSE); Log(LOG_INFO, "INFO ( %s/%s ): *** Purging cache - END (PID: %u, QN: %u/%u, ET: %u) ***\n", config.name, config.type, writer_pid, qn, saved_index, duration); if (config.sql_trigger_exec && !safe_action) P_trigger_exec(config.sql_trigger_exec); if (empty_pcust) free(empty_pcust); if (json_buf) free(json_buf); #ifdef WITH_AVRO if (avro_buf) free(avro_buf); #endif }
void skinny_bmp_daemon() { int slen, clen, ret, rc, peers_idx, allowed, yes=1, no=0; int peers_idx_rr = 0, max_peers_idx = 0; u_int32_t pkt_remaining_len=0; time_t now; afi_t afi; safi_t safi; struct bmp_peer *bmpp = NULL; struct bgp_peer *peer = NULL; #if defined ENABLE_IPV6 struct sockaddr_storage server, client; #else struct sockaddr server, client; #endif struct hosts_table allow; struct host_addr addr; struct bgp_peer_batch bp_batch; /* select() stuff */ fd_set read_descs, bkp_read_descs; int fd, select_fd, bkp_select_fd, recalc_fds, select_num; /* logdump time management */ time_t dump_refresh_deadline; struct timeval dump_refresh_timeout, *drt_ptr; /* initial cleanups */ reload_map_bmp_thread = FALSE; reload_log_bmp_thread = FALSE; memset(&server, 0, sizeof(server)); memset(&client, 0, sizeof(client)); memset(&allow, 0, sizeof(struct hosts_table)); clen = sizeof(client); bmp_routing_db = &inter_domain_routing_dbs[FUNC_TYPE_BMP]; memset(bmp_routing_db, 0, sizeof(struct bgp_rt_structs)); /* socket creation for BMP server: IPv4 only */ #if (defined ENABLE_IPV6) if (!config.nfacctd_bmp_ip) { struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&server; sa6->sin6_family = AF_INET6; sa6->sin6_port = htons(config.nfacctd_bmp_port); slen = sizeof(struct sockaddr_in6); } #else if (!config.nfacctd_bmp_ip) { struct sockaddr_in *sa4 = (struct sockaddr_in *)&server; sa4->sin_family = AF_INET; sa4->sin_addr.s_addr = htonl(0); sa4->sin_port = htons(config.nfacctd_bmp_port); slen = sizeof(struct sockaddr_in); } #endif else { trim_spaces(config.nfacctd_bmp_ip); ret = str_to_addr(config.nfacctd_bmp_ip, &addr); if (!ret) { Log(LOG_ERR, "ERROR ( %s/%s ): 'bmp_daemon_ip' value is not a valid IPv4/IPv6 address. Terminating thread.\n", config.name, bmp_misc_db->log_str); exit_all(1); } slen = addr_to_sa((struct sockaddr *)&server, &addr, config.nfacctd_bmp_port); } if (!config.nfacctd_bmp_max_peers) config.nfacctd_bmp_max_peers = BMP_MAX_PEERS_DEFAULT; Log(LOG_INFO, "INFO ( %s/%s ): maximum BMP peers allowed: %d\n", config.name, bmp_misc_db->log_str, config.nfacctd_bmp_max_peers); bmp_peers = malloc(config.nfacctd_bmp_max_peers*sizeof(struct bmp_peer)); if (!bmp_peers) { Log(LOG_ERR, "ERROR ( %s/%s ): Unable to malloc() BMP peers structure. Terminating thread.\n", config.name, bmp_misc_db->log_str); exit_all(1); } memset(bmp_peers, 0, config.nfacctd_bmp_max_peers*sizeof(struct bmp_peer)); if (config.nfacctd_bmp_msglog_file || config.nfacctd_bmp_msglog_amqp_routing_key || config.nfacctd_bmp_msglog_kafka_topic) { if (config.nfacctd_bmp_msglog_file) bmp_misc_db->msglog_backend_methods++; if (config.nfacctd_bmp_msglog_amqp_routing_key) bmp_misc_db->msglog_backend_methods++; if (config.nfacctd_bmp_msglog_kafka_topic) bmp_misc_db->msglog_backend_methods++; if (bmp_misc_db->msglog_backend_methods > 1) { Log(LOG_ERR, "ERROR ( %s/%s ): bmp_daemon_msglog_file, bmp_daemon_msglog_amqp_routing_key and bmp_daemon_msglog_kafka_topic are mutually exclusive. Terminating thread.\n", config.name, bmp_misc_db->log_str); exit_all(1); } } if (config.bmp_dump_file || config.bmp_dump_amqp_routing_key || config.bmp_dump_kafka_topic) { if (config.bmp_dump_file) bmp_misc_db->dump_backend_methods++; if (config.bmp_dump_amqp_routing_key) bmp_misc_db->dump_backend_methods++; if (config.bmp_dump_kafka_topic) bmp_misc_db->dump_backend_methods++; if (bmp_misc_db->dump_backend_methods > 1) { Log(LOG_ERR, "ERROR ( %s/%s ): bmp_dump_file, bmp_dump_amqp_routing_key and bmp_dump_kafka_topic are mutually exclusive. Terminating thread.\n", config.name, bmp_misc_db->log_str); exit_all(1); } } if (bmp_misc_db->msglog_backend_methods || bmp_misc_db->dump_backend_methods) bgp_peer_log_seq_init(&bmp_misc_db->log_seq); if (bmp_misc_db->msglog_backend_methods) { bmp_misc_db->peers_log = malloc(config.nfacctd_bmp_max_peers*sizeof(struct bgp_peer_log)); if (!bmp_misc_db->peers_log) { Log(LOG_ERR, "ERROR ( %s/%s ): Unable to malloc() BMP peers log structure. Terminating thread.\n", config.name, bmp_misc_db->log_str); exit_all(1); } memset(bmp_misc_db->peers_log, 0, config.nfacctd_bmp_max_peers*sizeof(struct bgp_peer_log)); if (config.nfacctd_bmp_msglog_amqp_routing_key) { #ifdef WITH_RABBITMQ bmp_daemon_msglog_init_amqp_host(); p_amqp_connect_to_publish(&bmp_daemon_msglog_amqp_host); if (!config.nfacctd_bmp_msglog_amqp_retry) config.nfacctd_bmp_msglog_amqp_retry = AMQP_DEFAULT_RETRY; #else Log(LOG_WARNING, "WARN ( %s/%s ): p_amqp_connect_to_publish() not possible due to missing --enable-rabbitmq\n", config.name, bmp_misc_db->log_str); #endif } if (config.nfacctd_bmp_msglog_kafka_topic) { #ifdef WITH_KAFKA bmp_daemon_msglog_init_kafka_host(); #else Log(LOG_WARNING, "WARN ( %s/%s ): p_kafka_connect_to_produce() not possible due to missing --enable-kafka\n", config.name, bmp_misc_db->log_str); #endif } } if (!config.bmp_table_attr_hash_buckets) config.bmp_table_attr_hash_buckets = HASHTABSIZE; bgp_attr_init(config.bmp_table_attr_hash_buckets, bmp_routing_db); if (!config.bmp_table_peer_buckets) config.bmp_table_peer_buckets = DEFAULT_BGP_INFO_HASH; if (!config.bmp_table_per_peer_buckets) config.bmp_table_per_peer_buckets = DEFAULT_BGP_INFO_PER_PEER_HASH; if (config.bmp_table_per_peer_hash == BGP_ASPATH_HASH_PATHID) bmp_route_info_modulo = bmp_route_info_modulo_pathid; else { Log(LOG_ERR, "ERROR ( %s/%s ): Unknown 'bmp_table_per_peer_hash' value. Terminating thread.\n", config.name, bmp_misc_db->log_str); exit_all(1); } config.bmp_sock = socket(((struct sockaddr *)&server)->sa_family, SOCK_STREAM, 0); if (config.bmp_sock < 0) { #if (defined ENABLE_IPV6) /* retry with IPv4 */ if (!config.nfacctd_bmp_ip) { struct sockaddr_in *sa4 = (struct sockaddr_in *)&server; sa4->sin_family = AF_INET; sa4->sin_addr.s_addr = htonl(0); sa4->sin_port = htons(config.nfacctd_bmp_port); slen = sizeof(struct sockaddr_in); config.bmp_sock = socket(((struct sockaddr *)&server)->sa_family, SOCK_STREAM, 0); } #endif if (config.bmp_sock < 0) { Log(LOG_ERR, "ERROR ( %s/%s ): thread socket() failed. Terminating thread.\n", config.name, bmp_misc_db->log_str); exit_all(1); } } if (config.nfacctd_bmp_ipprec) { int opt = config.nfacctd_bmp_ipprec << 5; rc = setsockopt(config.bmp_sock, IPPROTO_IP, IP_TOS, &opt, sizeof(opt)); if (rc < 0) Log(LOG_ERR, "WARN ( %s/%s ): setsockopt() failed for IP_TOS (errno: %d).\n", config.name, bmp_misc_db->log_str, errno); } rc = setsockopt(config.bmp_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)); if (rc < 0) Log(LOG_ERR, "WARN ( %s/%s ): setsockopt() failed for SO_REUSEADDR (errno: %d).\n", config.name, bmp_misc_db->log_str, errno); #if (defined ENABLE_IPV6) && (defined IPV6_BINDV6ONLY) rc = setsockopt(config.bmp_sock, IPPROTO_IPV6, IPV6_BINDV6ONLY, (char *) &no, (socklen_t) sizeof(no)); if (rc < 0) Log(LOG_ERR, "WARN ( %s/%s ): setsockopt() failed for IPV6_BINDV6ONLY (errno: %d).\n", config.name, bmp_misc_db->log_str, errno); #endif if (config.nfacctd_bmp_pipe_size) { int l = sizeof(config.nfacctd_bmp_pipe_size); int saved = 0, obtained = 0; getsockopt(config.bmp_sock, SOL_SOCKET, SO_RCVBUF, &saved, &l); Setsocksize(config.bmp_sock, SOL_SOCKET, SO_RCVBUF, &config.nfacctd_bmp_pipe_size, sizeof(config.nfacctd_bmp_pipe_size)); getsockopt(config.bmp_sock, SOL_SOCKET, SO_RCVBUF, &obtained, &l); Setsocksize(config.bmp_sock, SOL_SOCKET, SO_RCVBUF, &saved, l); getsockopt(config.bmp_sock, SOL_SOCKET, SO_RCVBUF, &obtained, &l); Log(LOG_INFO, "INFO ( %s/%s ): bmp_daemon_pipe_size: obtained=%d target=%d.\n", config.name, bmp_misc_db->log_str, obtained, config.nfacctd_bmp_pipe_size); } rc = bind(config.bmp_sock, (struct sockaddr *) &server, slen); if (rc < 0) { char null_ip_address[] = "0.0.0.0"; char *ip_address; ip_address = config.nfacctd_bmp_ip ? config.nfacctd_bmp_ip : null_ip_address; Log(LOG_ERR, "ERROR ( %s/%s ): bind() to ip=%s port=%d/tcp failed (errno: %d).\n", config.name, bmp_misc_db->log_str, ip_address, config.nfacctd_bmp_port, errno); exit_all(1); } rc = listen(config.bmp_sock, 1); if (rc < 0) { Log(LOG_ERR, "ERROR ( %s/%s ): listen() failed (errno: %d).\n", config.name, bmp_misc_db->log_str, errno); exit_all(1); } /* Preparing for syncronous I/O multiplexing */ select_fd = 0; FD_ZERO(&bkp_read_descs); FD_SET(config.bmp_sock, &bkp_read_descs); { char srv_string[INET6_ADDRSTRLEN]; struct host_addr srv_addr; u_int16_t srv_port; sa_to_addr((struct sockaddr *)&server, &srv_addr, &srv_port); addr_to_str(srv_string, &srv_addr); Log(LOG_INFO, "INFO ( %s/%s ): waiting for BMP data on %s:%u\n", config.name, bmp_misc_db->log_str, srv_string, srv_port); } /* Preparing ACL, if any */ if (config.nfacctd_bmp_allow_file) load_allow_file(config.nfacctd_bmp_allow_file, &allow); /* Let's initialize clean shared RIB */ for (afi = AFI_IP; afi < AFI_MAX; afi++) { for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) { bmp_routing_db->rib[afi][safi] = bgp_table_init(afi, safi); } } /* BMP peers batching checks */ if ((config.nfacctd_bmp_batch && !config.nfacctd_bmp_batch_interval) || (config.nfacctd_bmp_batch_interval && !config.nfacctd_bmp_batch)) { Log(LOG_WARNING, "WARN ( %s/%s ): 'bmp_daemon_batch_interval' and 'bmp_daemon_batch' both set to zero.\n", config.name, bmp_misc_db->log_str); config.nfacctd_bmp_batch = 0; config.nfacctd_bmp_batch_interval = 0; } else bgp_batch_init(&bp_batch, config.nfacctd_bmp_batch, config.nfacctd_bmp_batch_interval); if (bmp_misc_db->msglog_backend_methods) { #ifdef WITH_JANSSON if (!config.nfacctd_bmp_msglog_output) config.nfacctd_bmp_msglog_output = PRINT_OUTPUT_JSON; #else Log(LOG_WARNING, "WARN ( %s/%s ): bmp_daemon_msglog_output set to json but will produce no output (missing --enable-jansson).\n", config.name, bmp_misc_db->log_str); #endif } if (bmp_misc_db->dump_backend_methods) { #ifdef WITH_JANSSON if (!config.bmp_dump_output) config.bmp_dump_output = PRINT_OUTPUT_JSON; #else Log(LOG_WARNING, "WARN ( %s/%s ): bmp_table_dump_output set to json but will produce no output (missing --enable-jansson).\n", config.name, bmp_misc_db->log_str); #endif } if (bmp_misc_db->dump_backend_methods) { char dump_roundoff[] = "m"; time_t tmp_time; if (config.bmp_dump_refresh_time) { gettimeofday(&bmp_misc_db->log_tstamp, NULL); dump_refresh_deadline = bmp_misc_db->log_tstamp.tv_sec; tmp_time = roundoff_time(dump_refresh_deadline, dump_roundoff); while ((tmp_time+config.bmp_dump_refresh_time) < dump_refresh_deadline) { tmp_time += config.bmp_dump_refresh_time; } dump_refresh_deadline = tmp_time; dump_refresh_deadline += config.bmp_dump_refresh_time; /* it's a deadline not a basetime */ } else { config.bmp_dump_file = NULL; bmp_misc_db->dump_backend_methods = FALSE; Log(LOG_WARNING, "WARN ( %s/%s ): Invalid 'bmp_dump_refresh_time'.\n", config.name, bmp_misc_db->log_str); } if (config.bmp_dump_amqp_routing_key) bmp_dump_init_amqp_host(); if (config.bmp_dump_kafka_topic) bmp_dump_init_kafka_host(); } select_fd = bkp_select_fd = (config.bmp_sock + 1); recalc_fds = FALSE; bmp_link_misc_structs(bmp_misc_db); for (;;) { select_again: if (recalc_fds) { select_fd = config.bmp_sock; max_peers_idx = -1; /* .. since valid indexes include 0 */ for (peers_idx = 0; peers_idx < config.nfacctd_bmp_max_peers; peers_idx++) { if (select_fd < bmp_peers[peers_idx].self.fd) select_fd = bmp_peers[peers_idx].self.fd; if (bmp_peers[peers_idx].self.fd) max_peers_idx = peers_idx; } select_fd++; max_peers_idx++; bkp_select_fd = select_fd; recalc_fds = FALSE; } else select_fd = bkp_select_fd; memcpy(&read_descs, &bkp_read_descs, sizeof(bkp_read_descs)); if (bmp_misc_db->dump_backend_methods) { int delta; calc_refresh_timeout_sec(dump_refresh_deadline, bmp_misc_db->log_tstamp.tv_sec, &delta); dump_refresh_timeout.tv_sec = delta; dump_refresh_timeout.tv_usec = 0; drt_ptr = &dump_refresh_timeout; } else drt_ptr = NULL; select_num = select(select_fd, &read_descs, NULL, NULL, drt_ptr); if (select_num < 0) goto select_again; if (reload_map_bmp_thread) { if (config.nfacctd_bmp_allow_file) load_allow_file(config.nfacctd_bmp_allow_file, &allow); reload_map_bmp_thread = FALSE; } if (reload_log_bmp_thread) { for (peers_idx = 0; peers_idx < config.nfacctd_bmp_max_peers; peers_idx++) { if (bmp_misc_db->peers_log[peers_idx].fd) { fclose(bmp_misc_db->peers_log[peers_idx].fd); bmp_misc_db->peers_log[peers_idx].fd = open_output_file(bmp_misc_db->peers_log[peers_idx].filename, "a", FALSE); setlinebuf(bmp_misc_db->peers_log[peers_idx].fd); } else break; } reload_log_bmp_thread = FALSE; } if (bmp_misc_db->msglog_backend_methods || bmp_misc_db->dump_backend_methods) { gettimeofday(&bmp_misc_db->log_tstamp, NULL); compose_timestamp(bmp_misc_db->log_tstamp_str, SRVBUFLEN, &bmp_misc_db->log_tstamp, TRUE, config.timestamps_since_epoch, config.timestamps_rfc3339, config.timestamps_utc); if (bmp_misc_db->dump_backend_methods) { while (bmp_misc_db->log_tstamp.tv_sec > dump_refresh_deadline) { bmp_misc_db->dump.tstamp.tv_sec = dump_refresh_deadline; bmp_misc_db->dump.tstamp.tv_usec = 0; compose_timestamp(bmp_misc_db->dump.tstamp_str, SRVBUFLEN, &bmp_misc_db->dump.tstamp, FALSE, config.timestamps_since_epoch, config.timestamps_rfc3339, config.timestamps_utc); bmp_misc_db->dump.period = config.bmp_dump_refresh_time; bmp_handle_dump_event(); dump_refresh_deadline += config.bmp_dump_refresh_time; } } #ifdef WITH_RABBITMQ if (config.nfacctd_bmp_msglog_amqp_routing_key) { time_t last_fail = P_broker_timers_get_last_fail(&bmp_daemon_msglog_amqp_host.btimers); if (last_fail && ((last_fail + P_broker_timers_get_retry_interval(&bmp_daemon_msglog_amqp_host.btimers)) <= bmp_misc_db->log_tstamp.tv_sec)) { bmp_daemon_msglog_init_amqp_host(); p_amqp_connect_to_publish(&bmp_daemon_msglog_amqp_host); } } #endif #ifdef WITH_KAFKA if (config.nfacctd_bmp_msglog_kafka_topic) { time_t last_fail = P_broker_timers_get_last_fail(&bmp_daemon_msglog_kafka_host.btimers); if (last_fail && ((last_fail + P_broker_timers_get_retry_interval(&bmp_daemon_msglog_kafka_host.btimers)) <= bmp_misc_db->log_tstamp.tv_sec)) bmp_daemon_msglog_init_kafka_host(); } #endif } /* If select_num == 0 then we got out of select() due to a timeout rather than because we had a message from a peer to handle. By now we did all routine checks and can happily return to select() again. */ if (!select_num) goto select_again; /* New connection is coming in */ if (FD_ISSET(config.bmp_sock, &read_descs)) { int peers_check_idx, peers_num; fd = accept(config.bmp_sock, (struct sockaddr *) &client, &clen); if (fd == ERR) goto read_data; #if defined ENABLE_IPV6 ipv4_mapped_to_ipv4(&client); #endif /* If an ACL is defined, here we check against and enforce it */ if (allow.num) allowed = check_allow(&allow, (struct sockaddr *)&client); else allowed = TRUE; if (!allowed) { close(fd); goto read_data; } for (peer = NULL, peers_idx = 0; peers_idx < config.nfacctd_bmp_max_peers; peers_idx++) { if (!bmp_peers[peers_idx].self.fd) { now = time(NULL); /* Admitted if: * batching feature is disabled or * we have room in the current batch or * we can start a new batch */ if (bgp_batch_is_admitted(&bp_batch, now)) { peer = &bmp_peers[peers_idx].self; bmpp = &bmp_peers[peers_idx]; if (bmp_peer_init(bmpp, FUNC_TYPE_BMP)) { peer = NULL; bmpp = NULL; } else recalc_fds = TRUE; log_notification_unset(&log_notifications.bgp_peers_throttling); if (bgp_batch_is_enabled(&bp_batch) && peer) { if (bgp_batch_is_expired(&bp_batch, now)) bgp_batch_reset(&bp_batch, now); if (bgp_batch_is_not_empty(&bp_batch)) bgp_batch_decrease_counter(&bp_batch); } break; } else { /* throttle */ /* We briefly accept the new connection to be able to drop it */ if (!log_notification_isset(&log_notifications.bmp_peers_throttling, now)) { Log(LOG_INFO, "INFO ( %s/%s ): throttling at BMP peer #%u\n", config.name, bmp_misc_db->log_str, peers_idx); log_notification_set(&log_notifications.bmp_peers_throttling, now, FALSE); } close(fd); goto read_data; } } } if (!peer) { int fd; /* We briefly accept the new connection to be able to drop it */ Log(LOG_ERR, "ERROR ( %s/%s ): Insufficient number of BMP peers has been configured by 'bmp_daemon_max_peers' (%d).\n", config.name, bmp_misc_db->log_str, config.nfacctd_bmp_max_peers); close(fd); goto read_data; } peer->fd = fd; FD_SET(peer->fd, &bkp_read_descs); peer->addr.family = ((struct sockaddr *)&client)->sa_family; if (peer->addr.family == AF_INET) { peer->addr.address.ipv4.s_addr = ((struct sockaddr_in *)&client)->sin_addr.s_addr; peer->tcp_port = ntohs(((struct sockaddr_in *)&client)->sin_port); } #if defined ENABLE_IPV6 else if (peer->addr.family == AF_INET6) { memcpy(&peer->addr.address.ipv6, &((struct sockaddr_in6 *)&client)->sin6_addr, 16); peer->tcp_port = ntohs(((struct sockaddr_in6 *)&client)->sin6_port); } #endif addr_to_str(peer->addr_str, &peer->addr); memcpy(&peer->id, &peer->addr, sizeof(struct host_addr)); /* XXX: some inet_ntoa()'s could be around against peer->id */ if (bmp_misc_db->msglog_backend_methods) bgp_peer_log_init(peer, config.nfacctd_bmp_msglog_output, FUNC_TYPE_BMP); if (bmp_misc_db->dump_backend_methods) bmp_dump_init_peer(peer); /* Check: multiple TCP connections per peer */ for (peers_check_idx = 0, peers_num = 0; peers_check_idx < config.nfacctd_bmp_max_peers; peers_check_idx++) { if (peers_idx != peers_check_idx && !memcmp(&bmp_peers[peers_check_idx].self.addr, &peer->addr, sizeof(bmp_peers[peers_check_idx].self.addr))) { if (bmp_misc_db->is_thread && !config.nfacctd_bgp_to_agent_map) { Log(LOG_WARNING, "WARN ( %s/%s ): [%s] Multiple connections from peer and no bgp_agent_map defined.\n", config.name, bmp_misc_db->log_str, bmp_peers[peers_check_idx].self.addr_str); } } else { if (bmp_peers[peers_check_idx].self.fd) peers_num++; } } Log(LOG_INFO, "INFO ( %s/%s ): [%s] BMP peers usage: %u/%u\n", config.name, bmp_misc_db->log_str, peer->addr_str, peers_num, config.nfacctd_bmp_max_peers); } read_data: /* We have something coming in: let's lookup which peer is that. FvD: To avoid starvation of the "later established" peers, we offset the start of the search in a round-robin style. */ for (peer = NULL, peers_idx = 0; peers_idx < max_peers_idx; peers_idx++) { int loc_idx = (peers_idx + peers_idx_rr) % max_peers_idx; if (bmp_peers[loc_idx].self.fd && FD_ISSET(bmp_peers[loc_idx].self.fd, &read_descs)) { peer = &bmp_peers[loc_idx].self; bmpp = &bmp_peers[loc_idx]; peers_idx_rr = (peers_idx_rr + 1) % max_peers_idx; break; } } if (!peer) goto select_again; ret = recv(peer->fd, &peer->buf.base[peer->buf.truncated_len], (peer->buf.len - peer->buf.truncated_len), 0); peer->msglen = (ret + peer->buf.truncated_len); if (ret <= 0) { Log(LOG_INFO, "INFO ( %s/%s ): [%s] BMP connection reset by peer (%d).\n", config.name, bmp_misc_db->log_str, peer->addr_str, errno); FD_CLR(peer->fd, &bkp_read_descs); bmp_peer_close(bmpp, FUNC_TYPE_BMP); recalc_fds = TRUE; goto select_again; } else { pkt_remaining_len = bmp_process_packet(peer->buf.base, peer->msglen, bmpp); /* handling offset for TCP segment reassembly */ if (pkt_remaining_len) peer->buf.truncated_len = bmp_packet_adj_offset(peer->buf.base, peer->buf.len, peer->msglen, pkt_remaining_len, peer->addr_str); else peer->buf.truncated_len = 0; } } }
void skinny_bmp_daemon() { int slen, clen, ret, rc, peers_idx, allowed, yes=1; char bmp_packet[BMP_MAX_PACKET_SIZE], *bmp_packet_ptr; time_t now; afi_t afi; safi_t safi; struct bgp_peer *peer; #if defined ENABLE_IPV6 struct sockaddr_storage server, client; struct ipv6_mreq multi_req6; #else struct sockaddr server, client; #endif struct hosts_table allow; struct host_addr addr; /* BMP peer batching vars */ int bmp_current_batch_elem = 0; time_t bmp_current_batch_stamp_base = 0; /* select() stuff */ fd_set read_descs, bkp_read_descs; int select_fd, select_num; /* logdump time management */ time_t dump_refresh_deadline; struct timeval dump_refresh_timeout, *drt_ptr; /* initial cleanups */ reload_log_bmp_thread = FALSE; memset(&server, 0, sizeof(server)); memset(&client, 0, sizeof(client)); memset(bmp_packet, 0, BMP_MAX_PACKET_SIZE); memset(&allow, 0, sizeof(struct hosts_table)); clen = sizeof(client); /* socket creation for BMP server: IPv4 only */ #if (defined ENABLE_IPV6) if (!config.nfacctd_bmp_ip) { struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&server; sa6->sin6_family = AF_INET6; sa6->sin6_port = htons(config.nfacctd_bmp_port); slen = sizeof(struct sockaddr_in6); } #else if (!config.nfacctd_bmp_ip) { struct sockaddr_in *sa4 = (struct sockaddr_in *)&server; sa4->sin_family = AF_INET; sa4->sin_addr.s_addr = htonl(0); sa4->sin_port = htons(config.nfacctd_bmp_port); slen = sizeof(struct sockaddr_in); } #endif else { trim_spaces(config.nfacctd_bmp_ip); ret = str_to_addr(config.nfacctd_bmp_ip, &addr); if (!ret) { Log(LOG_ERR, "ERROR ( %s/core/BMP ): 'bmp_daemon_ip' value is not a valid IPv4/IPv6 address. Terminating thread.\n", config.name); exit_all(1); } slen = addr_to_sa((struct sockaddr *)&server, &addr, config.nfacctd_bmp_port); } if (!config.nfacctd_bmp_max_peers) config.nfacctd_bmp_max_peers = BMP_MAX_PEERS_DEFAULT; Log(LOG_INFO, "INFO ( %s/core/BMP ): maximum BMP peers allowed: %d\n", config.name, config.nfacctd_bmp_max_peers); bmp_peers = malloc(config.nfacctd_bmp_max_peers*sizeof(struct bgp_peer)); if (!bmp_peers) { Log(LOG_ERR, "ERROR ( %s/core/BMP ): Unable to malloc() BMP peers structure. Terminating thread.\n", config.name); exit_all(1); } memset(bmp_peers, 0, config.nfacctd_bmp_max_peers*sizeof(struct bgp_peer)); if (config.nfacctd_bmp_msglog_file && config.nfacctd_bmp_msglog_amqp_routing_key) { Log(LOG_ERR, "ERROR ( %s/core/BMP ): bmp_daemon_msglog_file and bmp_daemon_msglog_amqp_routing_key are mutually exclusive. Terminating thread.\n", config.name); exit_all(1); } if (config.bmp_dump_file && config.bmp_dump_amqp_routing_key) { Log(LOG_ERR, "ERROR ( %s/core/BMP ): bmp_dump_file and bmp_dump_amqp_routing_key are mutually exclusive. Terminating thread.\n", config.name); exit_all(1); } if (config.nfacctd_bmp_msglog_file || config.nfacctd_bmp_msglog_amqp_routing_key) { bmp_peers_log = malloc(config.nfacctd_bmp_max_peers*sizeof(struct bgp_peer_log)); if (!bmp_peers_log) { Log(LOG_ERR, "ERROR ( %s/core/BMP ): Unable to malloc() BMP peers log structure. Terminating thread.\n", config.name); exit_all(1); } memset(bmp_peers_log, 0, config.nfacctd_bmp_max_peers*sizeof(struct bgp_peer_log)); bgp_peer_log_seq_init(&bmp_log_seq); if (config.nfacctd_bmp_msglog_amqp_routing_key) { #ifdef WITH_RABBITMQ bmp_daemon_msglog_init_amqp_host(); p_amqp_connect_to_publish(&bmp_daemon_msglog_amqp_host); if (!config.nfacctd_bmp_msglog_amqp_retry) config.nfacctd_bmp_msglog_amqp_retry = AMQP_DEFAULT_RETRY; #else Log(LOG_WARNING, "WARN ( %s/core/BMP ): p_amqp_connect_to_publish() not possible due to missing --enable-rabbitmq\n", config.name); #endif } } if (!config.bmp_table_attr_hash_buckets) config.bmp_table_attr_hash_buckets = HASHTABSIZE; bmp_attr_init(); if (!config.bmp_table_peer_buckets) config.bmp_table_peer_buckets = DEFAULT_BGP_INFO_HASH; if (!config.bmp_table_per_peer_buckets) config.bmp_table_per_peer_buckets = DEFAULT_BGP_INFO_PER_PEER_HASH; if (config.bmp_table_per_peer_hash == BGP_ASPATH_HASH_PATHID) bmp_route_info_modulo = bgp_route_info_modulo_pathid; else { Log(LOG_ERR, "ERROR ( %s/core/BMP ): Unknown 'bmp_table_per_peer_hash' value. Terminating thread.\n", config.name); exit_all(1); } config.bmp_sock = socket(((struct sockaddr *)&server)->sa_family, SOCK_STREAM, 0); if (config.bmp_sock < 0) { #if (defined ENABLE_IPV6) /* retry with IPv4 */ if (!config.nfacctd_bmp_ip) { struct sockaddr_in *sa4 = (struct sockaddr_in *)&server; sa4->sin_family = AF_INET; sa4->sin_addr.s_addr = htonl(0); sa4->sin_port = htons(config.nfacctd_bmp_port); slen = sizeof(struct sockaddr_in); config.bmp_sock = socket(((struct sockaddr *)&server)->sa_family, SOCK_STREAM, 0); } #endif if (config.bmp_sock < 0) { Log(LOG_ERR, "ERROR ( %s/core/BMP ): thread socket() failed. Terminating thread.\n", config.name); exit_all(1); } } if (config.nfacctd_bmp_ipprec) { int opt = config.nfacctd_bmp_ipprec << 5; rc = setsockopt(config.bmp_sock, IPPROTO_IP, IP_TOS, &opt, sizeof(opt)); if (rc < 0) Log(LOG_ERR, "WARN ( %s/core/BMP ): setsockopt() failed for IP_TOS (errno: %d).\n", config.name, errno); } rc = setsockopt(config.bmp_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)); if (rc < 0) Log(LOG_ERR, "WARN ( %s/core/BMP ): setsockopt() failed for SO_REUSEADDR (errno: %d).\n", config.name, errno); if (config.nfacctd_bmp_pipe_size) { int l = sizeof(config.nfacctd_bmp_pipe_size); int saved = 0, obtained = 0; getsockopt(config.bmp_sock, SOL_SOCKET, SO_RCVBUF, &saved, &l); Setsocksize(config.bmp_sock, SOL_SOCKET, SO_RCVBUF, &config.nfacctd_bmp_pipe_size, sizeof(config.nfacctd_bmp_pipe_size)); getsockopt(config.bmp_sock, SOL_SOCKET, SO_RCVBUF, &obtained, &l); Setsocksize(config.bmp_sock, SOL_SOCKET, SO_RCVBUF, &saved, l); getsockopt(config.bmp_sock, SOL_SOCKET, SO_RCVBUF, &obtained, &l); Log(LOG_INFO, "INFO ( %s/core/BMP ): bmp_daemon_pipe_size: obtained=%d target=%d.\n", config.name, obtained, config.nfacctd_bmp_pipe_size); } rc = bind(config.bmp_sock, (struct sockaddr *) &server, slen); if (rc < 0) { char null_ip_address[] = "0.0.0.0"; char *ip_address; ip_address = config.nfacctd_bmp_ip ? config.nfacctd_bmp_ip : null_ip_address; Log(LOG_ERR, "ERROR ( %s/core/BMP ): bind() to ip=%s port=%d/tcp failed (errno: %d).\n", config.name, ip_address, config.nfacctd_bmp_port, errno); exit_all(1); } rc = listen(config.bmp_sock, 1); if (rc < 0) { Log(LOG_ERR, "ERROR ( %s/core/BMP ): listen() failed (errno: %d).\n", config.name, errno); exit_all(1); } /* Preparing for syncronous I/O multiplexing */ select_fd = 0; FD_ZERO(&bkp_read_descs); FD_SET(config.bmp_sock, &bkp_read_descs); { char srv_string[INET6_ADDRSTRLEN]; struct host_addr srv_addr; u_int16_t srv_port; sa_to_addr(&server, &srv_addr, &srv_port); addr_to_str(srv_string, &srv_addr); Log(LOG_INFO, "INFO ( %s/core/BMP ): waiting for BMP data on %s:%u\n", config.name, srv_string, srv_port); } /* Preparing ACL, if any */ if (config.nfacctd_bmp_allow_file) load_allow_file(config.nfacctd_bmp_allow_file, &allow); /* Let's initialize clean shared RIB */ for (afi = AFI_IP; afi < AFI_MAX; afi++) { for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) { bmp_rib[afi][safi] = bgp_table_init(afi, safi); } } /* BMP peers batching checks */ if ((config.nfacctd_bmp_batch && !config.nfacctd_bmp_batch_interval) || (config.nfacctd_bmp_batch_interval && !config.nfacctd_bmp_batch)) { Log(LOG_WARNING, "WARN ( %s/core/BMP ): 'bmp_daemon_batch_interval' and 'bmp_daemon_batch' both set to zero.\n", config.name); config.nfacctd_bmp_batch = 0; config.nfacctd_bmp_batch_interval = 0; } if (!config.nfacctd_bmp_msglog_output && (config.nfacctd_bmp_msglog_file || config.nfacctd_bmp_msglog_amqp_routing_key)) #ifdef WITH_JANSSON config.nfacctd_bmp_msglog_output = PRINT_OUTPUT_JSON; #else Log(LOG_WARNING, "WARN ( %s/core/BMP ): bmp_daemon_msglog_output set to json but will produce no output (missing --enable-jansson).\n", config.name); #endif if (!config.bmp_dump_output && (config.bmp_dump_file || config.bmp_dump_amqp_routing_key)) #ifdef WITH_JANSSON config.bmp_dump_output = PRINT_OUTPUT_JSON; #else Log(LOG_WARNING, "WARN ( %s/core/BMP ): bmp_table_dump_output set to json but will produce no output (missing --enable-jansson).\n", config.name); #endif if (config.bmp_dump_file || config.bmp_dump_amqp_routing_key) { char dump_roundoff[] = "m"; time_t tmp_time; if (config.bmp_dump_refresh_time) { gettimeofday(&bmp_log_tstamp, NULL); dump_refresh_deadline = bmp_log_tstamp.tv_sec; tmp_time = roundoff_time(dump_refresh_deadline, dump_roundoff); while ((tmp_time+config.bmp_dump_refresh_time) < dump_refresh_deadline) { tmp_time += config.bmp_dump_refresh_time; } dump_refresh_deadline = tmp_time; dump_refresh_deadline += config.bmp_dump_refresh_time; /* it's a deadline not a basetime */ } else { config.bmp_dump_file = NULL; Log(LOG_WARNING, "WARN ( %s/core/BMP ): Invalid 'bmp_dump_refresh_time'.\n", config.name); } bmp_dump_init_amqp_host(); } for (;;) { select_again: select_fd = config.bmp_sock; for (peers_idx = 0; peers_idx < config.nfacctd_bmp_max_peers; peers_idx++) if (select_fd < bmp_peers[peers_idx].fd) select_fd = bmp_peers[peers_idx].fd; select_fd++; memcpy(&read_descs, &bkp_read_descs, sizeof(bkp_read_descs)); if (config.bmp_dump_file || config.bmp_dump_amqp_routing_key) { int delta; calc_refresh_timeout_sec(dump_refresh_deadline, bmp_log_tstamp.tv_sec, &delta); dump_refresh_timeout.tv_sec = delta; dump_refresh_timeout.tv_usec = 0; drt_ptr = &dump_refresh_timeout; } else drt_ptr = NULL; select_num = select(select_fd, &read_descs, NULL, NULL, drt_ptr); if (select_num < 0) goto select_again; if (reload_log_bmp_thread) { for (peers_idx = 0; peers_idx < config.nfacctd_bmp_max_peers; peers_idx++) { if (bmp_peers_log[peers_idx].fd) { fclose(bmp_peers_log[peers_idx].fd); bmp_peers_log[peers_idx].fd = open_logfile(bmp_peers_log[peers_idx].filename, "a"); } else break; } } if (config.nfacctd_bmp_msglog_file || config.nfacctd_bmp_msglog_amqp_routing_key || config.bmp_dump_file || config.bmp_dump_amqp_routing_key) { gettimeofday(&bmp_log_tstamp, NULL); compose_timestamp(bmp_log_tstamp_str, SRVBUFLEN, &bmp_log_tstamp, TRUE, config.sql_history_since_epoch); if (config.bmp_dump_file || config.bmp_dump_amqp_routing_key) { while (bmp_log_tstamp.tv_sec > dump_refresh_deadline) { bmp_handle_dump_event(); dump_refresh_deadline += config.bmp_dump_refresh_time; } } #ifdef WITH_RABBITMQ if (config.nfacctd_bmp_msglog_amqp_routing_key) { time_t last_fail = p_amqp_get_last_fail(&bmp_daemon_msglog_amqp_host); if (last_fail && ((last_fail + p_amqp_get_retry_interval(&bmp_daemon_msglog_amqp_host)) <= log_tstamp.tv_sec)) { bmp_daemon_msglog_init_amqp_host(); p_amqp_connect_to_publish(&bmp_daemon_msglog_amqp_host); } } #endif } /* If select_num == 0 then we got out of select() due to a timeout rather than because we had a message from a peeer to handle. By now we did all routine checks and can happily return to selet() again. */ if (!select_num) goto select_again; /* New connection is coming in */ if (FD_ISSET(config.bmp_sock, &read_descs)) { int peers_check_idx, peers_num; for (peer = NULL, peers_idx = 0; peers_idx < config.nfacctd_bmp_max_peers; peers_idx++) { if (bmp_peers[peers_idx].fd == 0) { now = time(NULL); if (bmp_current_batch_elem > 0 || now > (bmp_current_batch_stamp_base + config.nfacctd_bmp_batch_interval)) { peer = &bmp_peers[peers_idx]; if (bgp_peer_init(peer)) peer = NULL; log_notification_unset(&log_notifications.bmp_peers_throttling); if (config.nfacctd_bmp_batch && peer) { if (now > (bmp_current_batch_stamp_base + config.nfacctd_bmp_batch_interval)) { bmp_current_batch_elem = config.nfacctd_bmp_batch; bmp_current_batch_stamp_base = now; } if (bmp_current_batch_elem > 0) bmp_current_batch_elem--; } break; } else { /* throttle */ int fd = 0; /* We briefly accept the new connection to be able to drop it */ if (!log_notification_isset(log_notifications.bmp_peers_throttling)) { Log(LOG_INFO, "INFO ( %s/core/BMP ): throttling at BMP peer #%u\n", config.name, peers_idx); log_notification_set(&log_notifications.bmp_peers_throttling); } fd = accept(config.bmp_sock, (struct sockaddr *) &client, &clen); close(fd); goto select_again; } } } if (!peer) { int fd; /* We briefly accept the new connection to be able to drop it */ Log(LOG_ERR, "ERROR ( %s/core/BMP ): Insufficient number of BMP peers has been configured by 'bmp_daemon_max_peers' (%d).\n", config.name, config.nfacctd_bmp_max_peers); fd = accept(config.bmp_sock, (struct sockaddr *) &client, &clen); close(fd); goto select_again; } peer->fd = accept(config.bmp_sock, (struct sockaddr *) &client, &clen); #if defined ENABLE_IPV6 ipv4_mapped_to_ipv4(&client); #endif /* If an ACL is defined, here we check against and enforce it */ if (allow.num) allowed = check_allow(&allow, (struct sockaddr *)&client); else allowed = TRUE; if (!allowed) { bgp_peer_close(peer, FUNC_TYPE_BMP); goto select_again; } FD_SET(peer->fd, &bkp_read_descs); peer->addr.family = ((struct sockaddr *)&client)->sa_family; if (peer->addr.family == AF_INET) { peer->addr.address.ipv4.s_addr = ((struct sockaddr_in *)&client)->sin_addr.s_addr; peer->tcp_port = ntohs(((struct sockaddr_in *)&client)->sin_port); } #if defined ENABLE_IPV6 else if (peer->addr.family == AF_INET6) { memcpy(&peer->addr.address.ipv6, &((struct sockaddr_in6 *)&client)->sin6_addr, 16); peer->tcp_port = ntohs(((struct sockaddr_in6 *)&client)->sin6_port); } #endif addr_to_str(peer->addr_str, &peer->addr); memcpy(&peer->id, &peer->addr, sizeof(struct host_addr)); /* XXX: some inet_ntoa()'s could be around against peer->id */ if (config.nfacctd_bmp_msglog_file || config.nfacctd_bmp_msglog_amqp_routing_key) bgp_peer_log_init(peer, config.nfacctd_bmp_msglog_output, FUNC_TYPE_BMP); if (config.bmp_dump_file || config.bmp_dump_amqp_routing_key) bmp_dump_init_peer(peer); /* Check: only one TCP connection is allowed per peer */ for (peers_check_idx = 0, peers_num = 0; peers_check_idx < config.nfacctd_bmp_max_peers; peers_check_idx++) { if (peers_idx != peers_check_idx && !memcmp(&bmp_peers[peers_check_idx].addr, &peer->addr, sizeof(bmp_peers[peers_check_idx].addr))) { Log(LOG_ERR, "ERROR ( %s/core/BMP ): [Id: %s] Refusing new connection from existing peer.\n", config.name, bmp_peers[peers_check_idx].addr_str); FD_CLR(peer->fd, &bkp_read_descs); bgp_peer_close(peer, FUNC_TYPE_BMP); goto select_again; } else { if (bmp_peers[peers_check_idx].fd) peers_num++; } } Log(LOG_INFO, "INFO ( %s/core/BMP ): BMP peers usage: %u/%u\n", config.name, peers_num, config.nfacctd_bmp_max_peers); if (config.nfacctd_bmp_neighbors_file) write_neighbors_file(config.nfacctd_bmp_neighbors_file); goto select_again; } /* We have something coming in: let's lookup which peer is that; XXX old: to be optimized */ for (peer = NULL, peers_idx = 0; peers_idx < config.nfacctd_bmp_max_peers; peers_idx++) { if (bmp_peers[peers_idx].fd && FD_ISSET(bmp_peers[peers_idx].fd, &read_descs)) { peer = &bmp_peers[peers_idx]; break; } } if (!peer) { Log(LOG_ERR, "ERROR ( %s/core/BMP ): message delivered to an unknown peer (FD bits: %d; FD max: %d)\n", config.name, select_num, select_fd); goto select_again; } peer->msglen = ret = recv(peer->fd, bmp_packet, BMP_MAX_PACKET_SIZE, 0); if (ret <= 0) { Log(LOG_INFO, "INFO ( %s/core/BMP ): [Id: %s] Existing BMP connection was reset (%d).\n", config.name, peer->addr_str, errno); FD_CLR(peer->fd, &bkp_read_descs); bgp_peer_close(peer, FUNC_TYPE_BMP); goto select_again; } else bmp_process_packet(bmp_packet, peer->msglen, peer); } }
void telemetry_daemon(void *t_data_void) { struct telemetry_data *t_data = t_data_void; telemetry_peer_udp_cache tpuc; int slen, clen, ret, rc, peers_idx, allowed, yes=1, no=0; int peers_idx_rr = 0, max_peers_idx = 0, peers_num = 0; int decoder = 0, data_decoder = 0, recv_flags = 0; u_int16_t port = 0; char *srv_proto = NULL; time_t now, last_udp_timeout_check; telemetry_peer *peer = NULL; telemetry_peer_z *peer_z = NULL; #if defined ENABLE_IPV6 struct sockaddr_storage server, client; #else struct sockaddr server, client; #endif struct hosts_table allow; struct host_addr addr; /* select() stuff */ fd_set read_descs, bkp_read_descs; int fd, select_fd, bkp_select_fd, recalc_fds, select_num; /* logdump time management */ time_t dump_refresh_deadline; struct timeval dump_refresh_timeout, *drt_ptr; if (!t_data) { Log(LOG_ERR, "ERROR ( %s/%s ): telemetry_daemon(): missing telemetry data. Terminating.\n", config.name, t_data->log_str); exit_all(1); } /* initial cleanups */ reload_log_telemetry_thread = FALSE; memset(&server, 0, sizeof(server)); memset(&client, 0, sizeof(client)); memset(&allow, 0, sizeof(struct hosts_table)); clen = sizeof(client); telemetry_peers_udp_cache = NULL; last_udp_timeout_check = FALSE; telemetry_misc_db = &inter_domain_misc_dbs[FUNC_TYPE_TELEMETRY]; memset(telemetry_misc_db, 0, sizeof(telemetry_misc_structs)); /* initialize variables */ if (config.telemetry_port_tcp && config.telemetry_port_udp) { Log(LOG_ERR, "ERROR ( %s/%s ): telemetry_daemon_port_tcp and telemetry_daemon_port_udp are mutually exclusive. Terminating.\n", config.name, t_data->log_str); exit_all(1); } else if (!config.telemetry_port_tcp && !config.telemetry_port_udp) { /* defaulting to TCP */ port = config.telemetry_port_tcp = TELEMETRY_TCP_PORT; srv_proto = malloc(strlen("tcp") + 1); strcpy(srv_proto, "tcp"); } else { if (config.telemetry_port_tcp) { port = config.telemetry_port_tcp; srv_proto = malloc(strlen("tcp") + 1); strcpy(srv_proto, "tcp"); } if (config.telemetry_port_udp) { port = config.telemetry_port_udp; srv_proto = malloc(strlen("udp") + 1); strcpy(srv_proto, "udp"); } } /* socket creation for telemetry server: IPv4 only */ #if (defined ENABLE_IPV6) if (!config.telemetry_ip) { struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&server; sa6->sin6_family = AF_INET6; sa6->sin6_port = htons(port); slen = sizeof(struct sockaddr_in6); } #else if (!config.telemetry_ip) { struct sockaddr_in *sa4 = (struct sockaddr_in *)&server; sa4->sin_family = AF_INET; sa4->sin_addr.s_addr = htonl(0); sa4->sin_port = htons(port); slen = sizeof(struct sockaddr_in); } #endif else { trim_spaces(config.telemetry_ip); ret = str_to_addr(config.telemetry_ip, &addr); if (!ret) { Log(LOG_ERR, "ERROR ( %s/%s ): telemetry_daemon_ip value is not a valid IPv4/IPv6 address. Terminating.\n", config.name, t_data->log_str); exit_all(1); } slen = addr_to_sa((struct sockaddr *)&server, &addr, port); } if (!config.telemetry_decoder) { Log(LOG_ERR, "ERROR ( %s/%s ): telemetry_daemon_decoder is not specified. Terminating.\n", config.name, t_data->log_str); exit_all(1); } else { if (!strcmp(config.telemetry_decoder, "json")) decoder = TELEMETRY_DECODER_JSON; else if (!strcmp(config.telemetry_decoder, "zjson")) { #if defined (HAVE_ZLIB) decoder = TELEMETRY_DECODER_ZJSON; #else Log(LOG_ERR, "ERROR ( %s/%s ): telemetry_daemon_decoder set to 'zjson' but zlib not available. Terminating.\n", config.name, t_data->log_str); exit_all(1); #endif } else if (!strcmp(config.telemetry_decoder, "cisco_json")) decoder = TELEMETRY_DECODER_CISCO_JSON; else if (!strcmp(config.telemetry_decoder, "cisco_zjson")) { #if defined (HAVE_ZLIB) decoder = TELEMETRY_DECODER_CISCO_ZJSON; #else Log(LOG_ERR, "ERROR ( %s/%s ): telemetry_daemon_decoder set to 'cisco_zjson' but zlib not available. Terminating.\n", config.name, t_data->log_str); exit_all(1); #endif } else if (!strcmp(config.telemetry_decoder, "cisco")) decoder = TELEMETRY_DECODER_CISCO; else if (!strcmp(config.telemetry_decoder, "cisco_gpb")) decoder = TELEMETRY_DECODER_CISCO_GPB; else if (!strcmp(config.telemetry_decoder, "cisco_gpb_kv")) decoder = TELEMETRY_DECODER_CISCO_GPB_KV; else { Log(LOG_ERR, "ERROR ( %s/%s ): telemetry_daemon_decoder set to unknown value. Terminating.\n", config.name, t_data->log_str); exit_all(1); } } if (!config.telemetry_max_peers) config.telemetry_max_peers = TELEMETRY_MAX_PEERS_DEFAULT; Log(LOG_INFO, "INFO ( %s/%s ): maximum telemetry peers allowed: %d\n", config.name, t_data->log_str, config.telemetry_max_peers); if (config.telemetry_port_udp) { if (!config.telemetry_udp_timeout) config.telemetry_udp_timeout = TELEMETRY_UDP_TIMEOUT_DEFAULT; Log(LOG_INFO, "INFO ( %s/%s ): telemetry UDP peers timeout: %u\n", config.name, t_data->log_str, config.telemetry_udp_timeout); } telemetry_peers = malloc(config.telemetry_max_peers*sizeof(telemetry_peer)); if (!telemetry_peers) { Log(LOG_ERR, "ERROR ( %s/%s ): Unable to malloc() telemetry_peers structure. Terminating.\n", config.name, t_data->log_str); exit_all(1); } memset(telemetry_peers, 0, config.telemetry_max_peers*sizeof(telemetry_peer)); if (telemetry_is_zjson(decoder)) { telemetry_peers_z = malloc(config.telemetry_max_peers*sizeof(telemetry_peer_z)); if (!telemetry_peers_z) { Log(LOG_ERR, "ERROR ( %s/%s ): Unable to malloc() telemetry_peers_z structure. Terminating.\n", config.name, t_data->log_str); exit_all(1); } memset(telemetry_peers_z, 0, config.telemetry_max_peers*sizeof(telemetry_peer_z)); } if (config.telemetry_port_udp) { telemetry_peers_udp_timeout = malloc(config.telemetry_max_peers*sizeof(telemetry_peer_udp_timeout)); if (!telemetry_peers_udp_timeout) { Log(LOG_ERR, "ERROR ( %s/%s ): Unable to malloc() telemetry_peers_udp_timeout structure. Terminating.\n", config.name, t_data->log_str); exit_all(1); } memset(telemetry_peers_udp_timeout, 0, config.telemetry_max_peers*sizeof(telemetry_peer_udp_timeout)); } if (config.telemetry_msglog_file || config.telemetry_msglog_amqp_routing_key || config.telemetry_msglog_kafka_topic) { if (config.telemetry_msglog_file) telemetry_misc_db->msglog_backend_methods++; if (config.telemetry_msglog_amqp_routing_key) telemetry_misc_db->msglog_backend_methods++; if (config.telemetry_msglog_kafka_topic) telemetry_misc_db->msglog_backend_methods++; if (telemetry_misc_db->msglog_backend_methods > 1) { Log(LOG_ERR, "ERROR ( %s/%s ): telemetry_daemon_msglog_file, telemetry_daemon_msglog_amqp_routing_key and telemetry_daemon_msglog_kafka_topic are mutually exclusive. Terminating.\n", config.name, t_data->log_str); exit_all(1); } } if (config.telemetry_dump_file || config.telemetry_dump_amqp_routing_key || config.telemetry_dump_kafka_topic) { if (config.telemetry_dump_file) telemetry_misc_db->dump_backend_methods++; if (config.telemetry_dump_amqp_routing_key) telemetry_misc_db->dump_backend_methods++; if (config.telemetry_dump_kafka_topic) telemetry_misc_db->dump_backend_methods++; if (telemetry_misc_db->dump_backend_methods > 1) { Log(LOG_ERR, "ERROR ( %s/%s ): telemetry_dump_file, telemetry_dump_amqp_routing_key and telemetry_dump_kafka_topic are mutually exclusive. Terminating.\n", config.name, t_data->log_str); exit_all(1); } } if (telemetry_misc_db->msglog_backend_methods) { telemetry_misc_db->peers_log = malloc(config.telemetry_max_peers*sizeof(telemetry_peer_log)); if (!telemetry_misc_db->peers_log) { Log(LOG_ERR, "ERROR ( %s/%s ): Unable to malloc() telemetry peers_log structure. Terminating.\n", config.name, t_data->log_str); exit_all(1); } memset(telemetry_misc_db->peers_log, 0, config.telemetry_max_peers*sizeof(telemetry_peer_log)); telemetry_peer_log_seq_init(&telemetry_misc_db->log_seq); if (config.telemetry_msglog_amqp_routing_key) { #ifdef WITH_RABBITMQ telemetry_daemon_msglog_init_amqp_host(); p_amqp_connect_to_publish(&telemetry_daemon_msglog_amqp_host); if (!config.telemetry_msglog_amqp_retry) config.telemetry_msglog_amqp_retry = AMQP_DEFAULT_RETRY; #else Log(LOG_WARNING, "WARN ( %s/%s ): p_amqp_connect_to_publish() not possible due to missing --enable-rabbitmq\n", config.name, t_data->log_str); #endif } if (config.telemetry_msglog_kafka_topic) { #ifdef WITH_KAFKA telemetry_daemon_msglog_init_kafka_host(); #else Log(LOG_WARNING, "WARN ( %s/%s ): p_kafka_connect_to_produce() not possible due to missing --enable-kafka\n", config.name, t_data->log_str); #endif } } if (config.telemetry_port_tcp) config.telemetry_sock = socket(((struct sockaddr *)&server)->sa_family, SOCK_STREAM, 0); else if (config.telemetry_port_udp) config.telemetry_sock = socket(((struct sockaddr *)&server)->sa_family, SOCK_DGRAM, 0); if (config.telemetry_sock < 0) { #if (defined ENABLE_IPV6) /* retry with IPv4 */ if (!config.telemetry_ip) { struct sockaddr_in *sa4 = (struct sockaddr_in *)&server; sa4->sin_family = AF_INET; sa4->sin_addr.s_addr = htonl(0); sa4->sin_port = htons(port); slen = sizeof(struct sockaddr_in); if (config.telemetry_port_tcp) config.telemetry_sock = socket(((struct sockaddr *)&server)->sa_family, SOCK_STREAM, 0); else if (config.telemetry_port_udp) config.telemetry_sock = socket(((struct sockaddr *)&server)->sa_family, SOCK_DGRAM, 0); } #endif if (config.telemetry_sock < 0) { Log(LOG_ERR, "ERROR ( %s/%s ): socket() failed. Terminating.\n", config.name, t_data->log_str); exit_all(1); } } if (config.telemetry_ipprec) { int opt = config.telemetry_ipprec << 5; rc = setsockopt(config.telemetry_sock, IPPROTO_IP, IP_TOS, &opt, sizeof(opt)); if (rc < 0) Log(LOG_ERR, "WARN ( %s/%s ): setsockopt() failed for IP_TOS (errno: %d).\n", config.name, t_data->log_str, errno); } rc = setsockopt(config.telemetry_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)); if (rc < 0) Log(LOG_ERR, "WARN ( %s/%s ): setsockopt() failed for SO_REUSEADDR (errno: %d).\n", config.name, t_data->log_str, errno); #if (defined ENABLE_IPV6) && (defined IPV6_BINDV6ONLY) rc = setsockopt(config.telemetry_sock, IPPROTO_IPV6, IPV6_BINDV6ONLY, (char *) &no, (socklen_t) sizeof(no)); if (rc < 0) Log(LOG_ERR, "WARN ( %s/%s ): setsockopt() failed for IPV6_BINDV6ONLY (errno: %d).\n", config.name, t_data->log_str, errno); #endif if (config.telemetry_pipe_size) { int l = sizeof(config.telemetry_pipe_size); int saved = 0, obtained = 0; getsockopt(config.telemetry_sock, SOL_SOCKET, SO_RCVBUF, &saved, &l); Setsocksize(config.telemetry_sock, SOL_SOCKET, SO_RCVBUF, &config.telemetry_pipe_size, sizeof(config.telemetry_pipe_size)); getsockopt(config.telemetry_sock, SOL_SOCKET, SO_RCVBUF, &obtained, &l); Setsocksize(config.telemetry_sock, SOL_SOCKET, SO_RCVBUF, &saved, l); getsockopt(config.telemetry_sock, SOL_SOCKET, SO_RCVBUF, &obtained, &l); Log(LOG_INFO, "INFO ( %s/%s ): telemetry_daemon_pipe_size: obtained=%d target=%d.\n", config.name, t_data->log_str, obtained, config.telemetry_pipe_size); } rc = bind(config.telemetry_sock, (struct sockaddr *) &server, slen); if (rc < 0) { char null_ip_address[] = "0.0.0.0"; char *ip_address; ip_address = config.telemetry_ip ? config.telemetry_ip : null_ip_address; Log(LOG_ERR, "ERROR ( %s/%s ): bind() to ip=%s port=%u/%s failed (errno: %d).\n", config.name, t_data->log_str, ip_address, port, srv_proto, errno); exit_all(1); } if (config.telemetry_port_tcp) { rc = listen(config.telemetry_sock, 1); if (rc < 0) { Log(LOG_ERR, "ERROR ( %s/%s ): listen() failed (errno: %d).\n", config.name, t_data->log_str, errno); exit_all(1); } } /* Preparing for syncronous I/O multiplexing */ select_fd = 0; FD_ZERO(&bkp_read_descs); FD_SET(config.telemetry_sock, &bkp_read_descs); { char srv_string[INET6_ADDRSTRLEN]; struct host_addr srv_addr; u_int16_t srv_port; sa_to_addr(&server, &srv_addr, &srv_port); addr_to_str(srv_string, &srv_addr); Log(LOG_INFO, "INFO ( %s/%s ): waiting for telemetry data on %s:%u/%s\n", config.name, t_data->log_str, srv_string, srv_port, srv_proto); } /* Preparing ACL, if any */ if (config.telemetry_allow_file) load_allow_file(config.telemetry_allow_file, &allow); if (telemetry_misc_db->msglog_backend_methods) { #ifdef WITH_JANSSON if (!config.telemetry_msglog_output) config.telemetry_msglog_output = PRINT_OUTPUT_JSON; #else Log(LOG_WARNING, "WARN ( %s/%s ): telemetry_daemon_msglog_output set to json but will produce no output (missing --enable-jansson).\n", config.name, t_data->log_str); #endif } if (telemetry_misc_db->dump_backend_methods) { #ifdef WITH_JANSSON if (!config.telemetry_dump_output) config.telemetry_dump_output = PRINT_OUTPUT_JSON; #else Log(LOG_WARNING, "WARN ( %s/%s ): telemetry_table_dump_output set to json but will produce no output (missing --enable-jansson).\n", config.name, t_data->log_str); #endif } if (telemetry_misc_db->dump_backend_methods) { char dump_roundoff[] = "m"; time_t tmp_time; if (config.telemetry_dump_refresh_time) { gettimeofday(&telemetry_misc_db->log_tstamp, NULL); dump_refresh_deadline = telemetry_misc_db->log_tstamp.tv_sec; tmp_time = roundoff_time(dump_refresh_deadline, dump_roundoff); while ((tmp_time+config.telemetry_dump_refresh_time) < dump_refresh_deadline) { tmp_time += config.telemetry_dump_refresh_time; } dump_refresh_deadline = tmp_time; dump_refresh_deadline += config.telemetry_dump_refresh_time; /* it's a deadline not a basetime */ } else { config.telemetry_dump_file = NULL; telemetry_misc_db->dump_backend_methods = FALSE; Log(LOG_WARNING, "WARN ( %s/%s ): Invalid 'telemetry_dump_refresh_time'.\n", config.name, t_data->log_str); } if (config.telemetry_dump_amqp_routing_key) telemetry_dump_init_amqp_host(); if (config.telemetry_dump_kafka_topic) telemetry_dump_init_kafka_host(); } select_fd = bkp_select_fd = (config.telemetry_sock + 1); recalc_fds = FALSE; telemetry_link_misc_structs(telemetry_misc_db); for (;;) { select_again: if (recalc_fds) { select_fd = config.telemetry_sock; max_peers_idx = -1; /* .. since valid indexes include 0 */ for (peers_idx = 0, peers_num = 0; peers_idx < config.telemetry_max_peers; peers_idx++) { if (select_fd < telemetry_peers[peers_idx].fd) select_fd = telemetry_peers[peers_idx].fd; if (telemetry_peers[peers_idx].fd) { max_peers_idx = peers_idx; peers_num++; } } select_fd++; max_peers_idx++; bkp_select_fd = select_fd; recalc_fds = FALSE; } else select_fd = bkp_select_fd; memcpy(&read_descs, &bkp_read_descs, sizeof(bkp_read_descs)); if (telemetry_misc_db->dump_backend_methods) { int delta; calc_refresh_timeout_sec(dump_refresh_deadline, telemetry_misc_db->log_tstamp.tv_sec, &delta); dump_refresh_timeout.tv_sec = delta; dump_refresh_timeout.tv_usec = 0; drt_ptr = &dump_refresh_timeout; } else drt_ptr = NULL; select_num = select(select_fd, &read_descs, NULL, NULL, drt_ptr); if (select_num < 0) goto select_again; // XXX: UDP case: timeout handling (to be tested) if (config.telemetry_port_udp) { now = time(NULL); if (now > (last_udp_timeout_check + TELEMETRY_UDP_TIMEOUT_INTERVAL)) { for (peers_idx = 0; peers_idx < config.telemetry_max_peers; peers_idx++) { telemetry_peer_udp_timeout *peer_udp_timeout; peer = &telemetry_peers[peers_idx]; peer_z = &telemetry_peers_z[peers_idx]; peer_udp_timeout = &telemetry_peers_udp_timeout[peers_idx]; if (peer->fd) { if (now > (peer_udp_timeout->last_msg + config.telemetry_udp_timeout)) { Log(LOG_INFO, "INFO ( %s/%s ): [%s] telemetry UDP peer removed (timeout).\n", config.name, t_data->log_str, peer->addr_str); telemetry_peer_close(peer, FUNC_TYPE_TELEMETRY); if (telemetry_is_zjson(decoder)) telemetry_peer_z_close(peer_z); recalc_fds = TRUE; } } } } } if (reload_log_telemetry_thread) { for (peers_idx = 0; peers_idx < config.telemetry_max_peers; peers_idx++) { if (telemetry_misc_db->peers_log[peers_idx].fd) { fclose(telemetry_misc_db->peers_log[peers_idx].fd); telemetry_misc_db->peers_log[peers_idx].fd = open_output_file(telemetry_misc_db->peers_log[peers_idx].filename, "a", FALSE); setlinebuf(telemetry_misc_db->peers_log[peers_idx].fd); } else break; } } if (telemetry_misc_db->msglog_backend_methods || telemetry_misc_db->dump_backend_methods) { gettimeofday(&telemetry_misc_db->log_tstamp, NULL); compose_timestamp(telemetry_misc_db->log_tstamp_str, SRVBUFLEN, &telemetry_misc_db->log_tstamp, TRUE, config.timestamps_since_epoch); if (telemetry_misc_db->dump_backend_methods) { while (telemetry_misc_db->log_tstamp.tv_sec > dump_refresh_deadline) { telemetry_handle_dump_event(t_data); dump_refresh_deadline += config.telemetry_dump_refresh_time; } } #ifdef WITH_RABBITMQ if (config.telemetry_msglog_amqp_routing_key) { time_t last_fail = P_broker_timers_get_last_fail(&telemetry_daemon_msglog_amqp_host.btimers); if (last_fail && ((last_fail + P_broker_timers_get_retry_interval(&telemetry_daemon_msglog_amqp_host.btimers)) <= telemetry_misc_db->log_tstamp.tv_sec)) { telemetry_daemon_msglog_init_amqp_host(); p_amqp_connect_to_publish(&telemetry_daemon_msglog_amqp_host); } } #endif #ifdef WITH_KAFKA if (config.telemetry_msglog_kafka_topic) { time_t last_fail = P_broker_timers_get_last_fail(&telemetry_daemon_msglog_kafka_host.btimers); if (last_fail && ((last_fail + P_broker_timers_get_retry_interval(&telemetry_daemon_msglog_kafka_host.btimers)) <= telemetry_misc_db->log_tstamp.tv_sec)) telemetry_daemon_msglog_init_kafka_host(); } #endif } /* If select_num == 0 then we got out of select() due to a timeout rather than because we had a message from a peeer to handle. By now we did all routine checks and can happily return to selet() again. */ if (!select_num) goto select_again; /* New connection is coming in */ if (FD_ISSET(config.telemetry_sock, &read_descs)) { if (config.telemetry_port_tcp) { fd = accept(config.telemetry_sock, (struct sockaddr *) &client, &clen); if (fd == ERR) goto read_data; } else if (config.telemetry_port_udp) { char dummy_local_buf[TRUE]; ret = recvfrom(config.telemetry_sock, dummy_local_buf, TRUE, MSG_PEEK, (struct sockaddr *) &client, &clen); if (ret <= 0) goto select_again; else fd = config.telemetry_sock; } #if defined ENABLE_IPV6 ipv4_mapped_to_ipv4(&client); #endif /* If an ACL is defined, here we check against and enforce it */ if (allow.num) allowed = check_allow(&allow, (struct sockaddr *)&client); else allowed = TRUE; if (!allowed) { if (config.telemetry_port_tcp) close(fd); goto read_data; } /* XXX: UDP case may be optimized further */ if (config.telemetry_port_udp) { telemetry_peer_udp_cache *tpuc_ret; u_int16_t client_port; sa_to_addr(&client, &tpuc.addr, &client_port); tpuc_ret = pm_tfind(&tpuc, &telemetry_peers_udp_cache, telemetry_tpuc_addr_cmp); if (tpuc_ret) { peer = &telemetry_peers[tpuc_ret->index]; telemetry_peers_udp_timeout[tpuc_ret->index].last_msg = now; goto read_data; } } for (peer = NULL, peers_idx = 0; peers_idx < config.telemetry_max_peers; peers_idx++) { if (!telemetry_peers[peers_idx].fd) { peer = &telemetry_peers[peers_idx]; if (telemetry_peer_init(peer, FUNC_TYPE_TELEMETRY)) peer = NULL; if (telemetry_is_zjson(decoder)) { peer_z = &telemetry_peers_z[peers_idx]; if (telemetry_peer_z_init(peer_z)) { peer = NULL; peer_z = NULL; } } if (peer) { recalc_fds = TRUE; if (config.telemetry_port_udp) { tpuc.index = peers_idx; telemetry_peers_udp_timeout[peers_idx].last_msg = now; if (!pm_tsearch(&tpuc, &telemetry_peers_udp_cache, telemetry_tpuc_addr_cmp, sizeof(telemetry_peer_udp_cache))) Log(LOG_WARNING, "WARN ( %s/%s ): tsearch() unable to insert in UDP peers cache.\n", config.name, t_data->log_str); } } break; } } if (!peer) { /* We briefly accept the new connection to be able to drop it */ Log(LOG_ERR, "ERROR ( %s/%s ): Insufficient number of telemetry peers has been configured by telemetry_max_peers (%d).\n", config.name, t_data->log_str, config.telemetry_max_peers); if (config.telemetry_port_tcp) close(fd); goto read_data; } peer->fd = fd; if (config.telemetry_port_tcp) FD_SET(peer->fd, &bkp_read_descs); peer->addr.family = ((struct sockaddr *)&client)->sa_family; if (peer->addr.family == AF_INET) { peer->addr.address.ipv4.s_addr = ((struct sockaddr_in *)&client)->sin_addr.s_addr; peer->tcp_port = ntohs(((struct sockaddr_in *)&client)->sin_port); } #if defined ENABLE_IPV6 else if (peer->addr.family == AF_INET6) { memcpy(&peer->addr.address.ipv6, &((struct sockaddr_in6 *)&client)->sin6_addr, 16); peer->tcp_port = ntohs(((struct sockaddr_in6 *)&client)->sin6_port); } #endif addr_to_str(peer->addr_str, &peer->addr); if (telemetry_misc_db->msglog_backend_methods) telemetry_peer_log_init(peer, config.telemetry_msglog_output, FUNC_TYPE_TELEMETRY); if (telemetry_misc_db->dump_backend_methods) telemetry_dump_init_peer(peer); peers_num++; Log(LOG_INFO, "INFO ( %s/%s ): [%s] telemetry peers usage: %u/%u\n", config.name, t_data->log_str, peer->addr_str, peers_num, config.telemetry_max_peers); } read_data: /* We have something coming in: let's lookup which peer is that. FvD: To avoid starvation of the "later established" peers, we offset the start of the search in a round-robin style. */ if (config.telemetry_port_tcp) { for (peer = NULL, peers_idx = 0; peers_idx < max_peers_idx; peers_idx++) { int loc_idx = (peers_idx + peers_idx_rr) % max_peers_idx; if (telemetry_peers[loc_idx].fd && FD_ISSET(telemetry_peers[loc_idx].fd, &read_descs)) { peer = &telemetry_peers[loc_idx]; if (telemetry_is_zjson(decoder)) peer_z = &telemetry_peers_z[loc_idx]; peers_idx_rr = (peers_idx_rr + 1) % max_peers_idx; break; } } } if (!peer) goto select_again; recv_flags = 0; switch (decoder) { case TELEMETRY_DECODER_JSON: ret = telemetry_recv_json(peer, 0, &recv_flags); data_decoder = TELEMETRY_DATA_DECODER_JSON; break; case TELEMETRY_DECODER_ZJSON: ret = telemetry_recv_zjson(peer, peer_z, 0, &recv_flags); data_decoder = TELEMETRY_DATA_DECODER_JSON; break; case TELEMETRY_DECODER_CISCO: ret = telemetry_recv_cisco(peer, &recv_flags, &data_decoder); break; case TELEMETRY_DECODER_CISCO_JSON: ret = telemetry_recv_cisco_json(peer, &recv_flags); data_decoder = TELEMETRY_DATA_DECODER_JSON; break; case TELEMETRY_DECODER_CISCO_ZJSON: ret = telemetry_recv_cisco_zjson(peer, peer_z, &recv_flags); data_decoder = TELEMETRY_DATA_DECODER_JSON; break; case TELEMETRY_DECODER_CISCO_GPB: ret = telemetry_recv_cisco_gpb(peer, &recv_flags); data_decoder = TELEMETRY_DATA_DECODER_GPB; break; case TELEMETRY_DECODER_CISCO_GPB_KV: ret = telemetry_recv_cisco_gpb_kv(peer, &recv_flags); data_decoder = TELEMETRY_DATA_DECODER_GPB; break; default: ret = TRUE; recv_flags = ERR; data_decoder = TELEMETRY_DATA_DECODER_UNKNOWN; break; } if (ret <= 0) { Log(LOG_INFO, "INFO ( %s/%s ): [%s] connection reset by peer (%d).\n", config.name, t_data->log_str, peer->addr_str, errno); FD_CLR(peer->fd, &bkp_read_descs); telemetry_peer_close(peer, FUNC_TYPE_TELEMETRY); if (telemetry_is_zjson(decoder)) telemetry_peer_z_close(peer_z); recalc_fds = TRUE; } else { if (recv_flags != ERR) telemetry_process_data(peer, t_data, data_decoder); } } }
void amqp_cache_purge(struct chained_cache *queue[], int index) { struct pkt_primitives *data = NULL; struct pkt_bgp_primitives *pbgp = NULL; struct pkt_nat_primitives *pnat = NULL; struct pkt_mpls_primitives *pmpls = NULL; char *pcust = NULL; struct pkt_vlen_hdr_primitives *pvlen = NULL; struct pkt_bgp_primitives empty_pbgp; struct pkt_nat_primitives empty_pnat; struct pkt_mpls_primitives empty_pmpls; char *empty_pcust = NULL; char src_mac[18], dst_mac[18], src_host[INET6_ADDRSTRLEN], dst_host[INET6_ADDRSTRLEN], ip_address[INET6_ADDRSTRLEN]; char rd_str[SRVBUFLEN], misc_str[SRVBUFLEN], dyn_amqp_routing_key[SRVBUFLEN], *orig_amqp_routing_key = NULL; int i, j, stop, batch_idx, is_routing_key_dyn = FALSE, qn = 0, ret, saved_index = index; int mv_num = 0, mv_num_save = 0; time_t start, duration; pid_t writer_pid = getpid(); #ifdef WITH_JANSSON json_t *array = json_array(); #endif /* setting some defaults */ if (!config.sql_host) config.sql_host = default_amqp_host; if (!config.sql_db) config.sql_db = default_amqp_exchange; if (!config.amqp_exchange_type) config.amqp_exchange_type = default_amqp_exchange_type; if (!config.amqp_vhost) config.amqp_vhost = default_amqp_vhost; if (!config.sql_table) config.sql_table = default_amqp_routing_key; else { if (strchr(config.sql_table, '$')) { is_routing_key_dyn = TRUE; orig_amqp_routing_key = config.sql_table; config.sql_table = dyn_amqp_routing_key; } } if (config.amqp_routing_key_rr) { orig_amqp_routing_key = config.sql_table; config.sql_table = dyn_amqp_routing_key; } p_amqp_set_exchange(&amqpp_amqp_host, config.sql_db); p_amqp_set_routing_key(&amqpp_amqp_host, config.sql_table); p_amqp_set_exchange_type(&amqpp_amqp_host, config.amqp_exchange_type); p_amqp_set_host(&amqpp_amqp_host, config.sql_host); p_amqp_set_vhost(&amqpp_amqp_host, config.amqp_vhost); p_amqp_set_persistent_msg(&amqpp_amqp_host, config.amqp_persistent_msg); p_amqp_set_frame_max(&amqpp_amqp_host, config.amqp_frame_max); p_amqp_set_content_type_json(&amqpp_amqp_host); p_amqp_init_routing_key_rr(&amqpp_amqp_host); p_amqp_set_routing_key_rr(&amqpp_amqp_host, config.amqp_routing_key_rr); empty_pcust = malloc(config.cpptrs.len); if (!empty_pcust) { Log(LOG_ERR, "ERROR ( %s/%s ): Unable to malloc() empty_pcust. Exiting.\n", config.name, config.type); exit_plugin(1); } memset(&empty_pbgp, 0, sizeof(struct pkt_bgp_primitives)); memset(&empty_pnat, 0, sizeof(struct pkt_nat_primitives)); memset(&empty_pmpls, 0, sizeof(struct pkt_mpls_primitives)); memset(empty_pcust, 0, config.cpptrs.len); ret = p_amqp_connect_to_publish(&amqpp_amqp_host); if (ret) return; for (j = 0, stop = 0; (!stop) && P_preprocess_funcs[j]; j++) stop = P_preprocess_funcs[j](queue, &index, j); Log(LOG_INFO, "INFO ( %s/%s ): *** Purging cache - START (PID: %u) ***\n", config.name, config.type, writer_pid); start = time(NULL); for (j = 0; j < index; j++) { void *json_obj; char *json_str; if (queue[j]->valid != PRINT_CACHE_COMMITTED) continue; data = &queue[j]->primitives; if (queue[j]->pbgp) pbgp = queue[j]->pbgp; else pbgp = &empty_pbgp; if (queue[j]->pnat) pnat = queue[j]->pnat; else pnat = &empty_pnat; if (queue[j]->pmpls) pmpls = queue[j]->pmpls; else pmpls = &empty_pmpls; if (queue[j]->pcust) pcust = queue[j]->pcust; else pcust = empty_pcust; if (queue[j]->pvlen) pvlen = queue[j]->pvlen; else pvlen = NULL; if (queue[j]->valid == PRINT_CACHE_FREE) continue; json_obj = compose_json(config.what_to_count, config.what_to_count_2, queue[j]->flow_type, &queue[j]->primitives, pbgp, pnat, pmpls, pcust, pvlen, queue[j]->bytes_counter, queue[j]->packet_counter, queue[j]->flow_counter, queue[j]->tcp_flags, &queue[j]->basetime, queue[j]->stitch); json_str = compose_json_str(json_obj); #ifdef WITH_JANSSON if (json_str && config.sql_multi_values) { json_t *elem = NULL; char *tmp_str = json_str; int do_free = FALSE; if (json_array_size(array) >= config.sql_multi_values) { json_str = json_dumps(array, 0); json_array_clear(array); mv_num_save = mv_num; mv_num = 0; } else do_free = TRUE; elem = json_loads(tmp_str, 0, NULL); json_array_append_new(array, elem); mv_num++; if (do_free) { free(json_str); json_str = NULL; } } #endif if (json_str) { if (is_routing_key_dyn) { P_handle_table_dyn_strings(dyn_amqp_routing_key, SRVBUFLEN, orig_amqp_routing_key, queue[j]); p_amqp_set_routing_key(&amqpp_amqp_host, dyn_amqp_routing_key); } if (config.amqp_routing_key_rr) { P_handle_table_dyn_rr(dyn_amqp_routing_key, SRVBUFLEN, orig_amqp_routing_key, &amqpp_amqp_host.rk_rr); p_amqp_set_routing_key(&amqpp_amqp_host, dyn_amqp_routing_key); } ret = p_amqp_publish_string(&amqpp_amqp_host, json_str); free(json_str); json_str = NULL; if (!ret) { if (!config.sql_multi_values) qn++; else qn += mv_num_save; } else break; } } #ifdef WITH_JANSSON if (config.sql_multi_values && json_array_size(array)) { char *json_str; json_str = json_dumps(array, 0); json_array_clear(array); json_decref(array); if (json_str) { /* no handling of dyn routing keys here: not compatible */ ret = p_amqp_publish_string(&amqpp_amqp_host, json_str); free(json_str); json_str = NULL; if (!ret) qn += mv_num; } } #endif p_amqp_close(&amqpp_amqp_host, FALSE); duration = time(NULL)-start; Log(LOG_INFO, "INFO ( %s/%s ): *** Purging cache - END (PID: %u, QN: %u/%u, ET: %u) ***\n", config.name, config.type, writer_pid, qn, saved_index, duration); if (config.sql_trigger_exec) P_trigger_exec(config.sql_trigger_exec); if (empty_pcust) free(empty_pcust); }