/* Functions */ void imt_plugin(int pipe_fd, struct configuration *cfgptr, void *ptr) { int maxqsize = (MAX_QUERIES*sizeof(struct pkt_primitives))+sizeof(struct query_header)+2; struct sockaddr cAddr; struct pkt_data *data; struct ports_table pt; unsigned char srvbuf[maxqsize]; unsigned char *srvbufptr; struct query_header *qh; unsigned char *pipebuf; char path[] = "/tmp/collect.pipe"; short int go_to_clear = FALSE; u_int32_t request, sz; struct ch_status *status = ((struct channels_list_entry *)ptr)->status; int datasize = ((struct channels_list_entry *)ptr)->datasize; struct extra_primitives extras; unsigned char *rgptr; int pollagain = 0; u_int32_t seq = 0; int rg_err_count = 0; int ret; struct pkt_bgp_primitives *pbgp, empty_pbgp; struct pkt_nat_primitives *pnat, empty_pnat; struct pkt_mpls_primitives *pmpls, empty_pmpls; char *pcust, empty_pcust[] = ""; struct pkt_vlen_hdr_primitives *pvlen, empty_pvlen; struct networks_file_data nfd; struct timeval select_timeout; struct primitives_ptrs prim_ptrs; fd_set read_descs, bkp_read_descs; /* select() stuff */ int select_fd, lock = FALSE; int cLen, num, sd, sd2; char *dataptr; memcpy(&config, cfgptr, sizeof(struct configuration)); memcpy(&extras, &((struct channels_list_entry *)ptr)->extras, sizeof(struct extra_primitives)); recollect_pipe_memory(ptr); pm_setproctitle("%s [%s]", "IMT Plugin", config.name); if (config.pidfile) write_pid_file_plugin(config.pidfile, config.type, config.name); if (config.logfile) { fclose(config.logfile_fd); config.logfile_fd = open_logfile(config.logfile, "a"); } if (extras.off_pkt_vlen_hdr_primitives) { Log(LOG_ERR, "ERROR ( %s/%s ): variable-length primitives, ie. label, are not supported in IMT plugin. Exiting ..\n", config.name, config.type); exit_plugin(1); } reload_map = FALSE; status->wakeup = TRUE; /* a bunch of default definitions and post-checks */ pipebuf = (unsigned char *) malloc(config.buffer_size); if (!pipebuf) { Log(LOG_ERR, "ERROR ( %s/%s ): malloc() failed (pipebuf). Exiting ..\n", config.name, config.type); exit_plugin(1); } setnonblocking(pipe_fd); memset(pipebuf, 0, config.buffer_size); no_more_space = FALSE; if (config.what_to_count & (COUNT_SUM_HOST|COUNT_SUM_NET)) insert_func = sum_host_insert; else if (config.what_to_count & COUNT_SUM_PORT) insert_func = sum_port_insert; else if (config.what_to_count & COUNT_SUM_AS) insert_func = sum_as_insert; #if defined (HAVE_L2) else if (config.what_to_count & COUNT_SUM_MAC) insert_func = sum_mac_insert; #endif else insert_func = insert_accounting_structure; memset(&nt, 0, sizeof(nt)); memset(&nc, 0, sizeof(nc)); memset(&pt, 0, sizeof(pt)); load_networks(config.networks_file, &nt, &nc); set_net_funcs(&nt); if (config.ports_file) load_ports(config.ports_file, &pt); if (config.pkt_len_distrib_bins_str) load_pkt_len_distrib_bins(); else { if (config.what_to_count_2 & COUNT_PKT_LEN_DISTRIB) { Log(LOG_ERR, "ERROR ( %s/%s ): 'aggregate' contains pkt_len_distrib but no 'pkt_len_distrib_bins' defined. Exiting.\n", config.name, config.type); exit_plugin(1); } } if ((!config.num_memory_pools) && (!have_num_memory_pools)) config.num_memory_pools = NUM_MEMORY_POOLS; if (!config.memory_pool_size) config.memory_pool_size = MEMORY_POOL_SIZE; else { if (config.memory_pool_size < sizeof(struct acc)) { Log(LOG_WARNING, "WARN ( %s/%s ): enforcing memory pool's minimum size, %d bytes.\n", config.name, config.type, sizeof(struct acc)); config.memory_pool_size = MEMORY_POOL_SIZE; } } if (!config.imt_plugin_path) config.imt_plugin_path = path; if (!config.buckets) config.buckets = MAX_HOSTS; init_memory_pool_table(config); if (mpd == NULL) { Log(LOG_ERR, "ERROR ( %s/%s ): unable to allocate memory pools table\n", config.name, config.type); exit_plugin(1); } current_pool = request_memory_pool(config.buckets*sizeof(struct acc)); if (current_pool == NULL) { Log(LOG_ERR, "ERROR ( %s/%s ): unable to allocate first memory pool, try with larger value.\n", config.name, config.type); exit_plugin(1); } a = current_pool->base_ptr; lru_elem_ptr = malloc(config.buckets*sizeof(struct acc *)); if (lru_elem_ptr == NULL) { Log(LOG_ERR, "ERROR ( %s/%s ): unable to allocate LRU element pointers.\n", config.name, config.type); exit_plugin(1); } else memset(lru_elem_ptr, 0, config.buckets*sizeof(struct acc *)); current_pool = request_memory_pool(config.memory_pool_size); if (current_pool == NULL) { Log(LOG_ERR, "ERROR ( %s/%s ): unable to allocate more memory pools, try with larger value.\n", config.name, config.type); exit_plugin(1); } signal(SIGHUP, reload); /* handles reopening of syslog channel */ signal(SIGINT, exit_now); /* exit lane */ signal(SIGUSR1, SIG_IGN); signal(SIGUSR2, reload_maps); signal(SIGPIPE, SIG_IGN); signal(SIGCHLD, SIG_IGN); memset(&empty_pbgp, 0, sizeof(empty_pbgp)); memset(&empty_pnat, 0, sizeof(empty_pnat)); memset(&empty_pmpls, 0, sizeof(empty_pmpls)); memset(&empty_pvlen, 0, sizeof(empty_pvlen)); /* building a server for interrogations by clients */ sd = build_query_server(config.imt_plugin_path); cLen = sizeof(cAddr); /* preparing for synchronous I/O multiplexing */ select_fd = 0; FD_ZERO(&read_descs); FD_SET(sd, &read_descs); if (sd > select_fd) select_fd = sd; FD_SET(pipe_fd, &read_descs); if (pipe_fd > select_fd) select_fd = pipe_fd; select_fd++; memcpy(&bkp_read_descs, &read_descs, sizeof(read_descs)); qh = (struct query_header *) srvbuf; /* plugin main loop */ for(;;) { select_again: select_timeout.tv_sec = DEFAULT_IMT_PLUGIN_SELECT_TIMEOUT; select_timeout.tv_usec = 0; memcpy(&read_descs, &bkp_read_descs, sizeof(bkp_read_descs)); num = select(select_fd, &read_descs, NULL, NULL, &select_timeout); if (num <= 0) { if (getppid() == 1) { Log(LOG_ERR, "ERROR ( %s/%s ): Core process *seems* gone. Exiting.\n", config.name, config.type); exit_plugin(1); } goto select_again; } gettimeofday(&cycle_stamp, NULL); /* doing server tasks */ if (FD_ISSET(sd, &read_descs)) { struct pollfd pfd; int ret; sd2 = accept(sd, &cAddr, &cLen); setblocking(sd2); srvbufptr = srvbuf; sz = maxqsize; pfd.fd = sd2; pfd.events = POLLIN; recv_again: ret = poll(&pfd, 1, 1000); if (ret == 0) { Log(LOG_WARNING, "WARN ( %s/%s ): Timed out while processing fragmented query.\n", config.name, config.type); close(sd2); goto select_again; } else { num = recv(sd2, srvbufptr, sz, 0); if (srvbufptr[num-1] != '\x4') { srvbufptr += num; sz -= num; goto recv_again; /* fragmented query */ } } num = num+(maxqsize-sz); if (qh->num > MAX_QUERIES) { Log(LOG_DEBUG, "DEBUG ( %s/%s ): request discarded. Too much queries.\n", config.name, config.type); close(sd2); continue; } request = qh->type; if (request & WANT_RESET) request ^= WANT_RESET; if (request & WANT_LOCK_OP) { lock = TRUE; request ^= WANT_LOCK_OP; } /* - if explicitely required, we do not fork: query obtains exclusive control - lock - over the memory table; - operations that may cause inconsistencies (full erasure, counter reset for individual entries, etc.) are entitled of an exclusive lock. - if query is matter of just a single short-lived walk through the table, we avoid fork(): the plugin will serve the request; - in all other cases, we fork; the newly created child will serve queries asyncronously. */ if (request & WANT_ERASE) { request ^= WANT_ERASE; if (request) { if (num > 0) process_query_data(sd2, srvbuf, num, &extras, datasize, FALSE); else Log(LOG_DEBUG, "DEBUG ( %s/%s ): %d incoming bytes. Errno: %d\n", config.name, config.type, num, errno); } Log(LOG_DEBUG, "DEBUG ( %s/%s ): Closing connection with client ...\n", config.name, config.type); go_to_clear = TRUE; } else if (((request == WANT_COUNTER) || (request == WANT_MATCH)) && (qh->num == 1) && (qh->what_to_count == config.what_to_count)) { if (num > 0) process_query_data(sd2, srvbuf, num, &extras, datasize, FALSE); else Log(LOG_DEBUG, "DEBUG ( %s/%s ): %d incoming bytes. ERRNO: %d\n", config.name, config.type, num, errno); Log(LOG_DEBUG, "DEBUG ( %s/%s ): Closing connection with client ...\n", config.name, config.type); } else if (request == WANT_CLASS_TABLE) { if (num > 0) process_query_data(sd2, srvbuf, num, &extras, datasize, FALSE); else Log(LOG_DEBUG, "DEBUG ( %s/%s ): %d incoming bytes. ERRNO: %d\n", config.name, config.type, num, errno); Log(LOG_DEBUG, "DEBUG ( %s/%s ): Closing connection with client ...\n", config.name, config.type); } else if (request == WANT_PKT_LEN_DISTRIB_TABLE) { if (num > 0) process_query_data(sd2, srvbuf, num, &extras, datasize, FALSE); else Log(LOG_DEBUG, "DEBUG ( %s/%s ): %d incoming bytes. ERRNO: %d\n", config.name, config.type, num, errno); Log(LOG_DEBUG, "DEBUG ( %s/%s ): Closing connection with client ...\n", config.name, config.type); } else { if (lock) { if (num > 0) process_query_data(sd2, srvbuf, num, &extras, datasize, FALSE); else Log(LOG_DEBUG, "DEBUG ( %s/%s ): %d incoming bytes. Errno: %d\n", config.name, config.type, num, errno); Log(LOG_DEBUG, "DEBUG ( %s/%s ): Closing connection with client ...\n", config.name, config.type); } else { switch (fork()) { case -1: /* Something went wrong */ Log(LOG_WARNING, "WARN ( %s/%s ): Unable to serve client query: %s\n", config.name, config.type, strerror(errno)); break; case 0: /* Child */ close(sd); pm_setproctitle("%s [%s]", "IMT Plugin -- serving client", config.name); if (num > 0) process_query_data(sd2, srvbuf, num, &extras, datasize, TRUE); else Log(LOG_DEBUG, "DEBUG ( %s/%s ): %d incoming bytes. Errno: %d\n", config.name, config.type, num, errno); Log(LOG_DEBUG, "DEBUG ( %s/%s ): Closing connection with client ...\n", config.name, config.type); close(sd2); exit(0); default: /* Parent */ break; } } } close(sd2); } /* clearing stats if requested */ if (go_to_clear) { /* When using extended BGP features we need to free() up memory allocations before erasing */ /* XXX: given the current use of empty_* vars we have always to free_extra_allocs() in order to prevent memory leaks */ /* if (extras.off_pkt_bgp_primitives || extras.off_pkt_nat_primitives || extras.off_pkt_mpls_primitives || extras.off_custom_primitives) */ free_extra_allocs(); clear_memory_pool_table(); current_pool = request_memory_pool(config.buckets*sizeof(struct acc)); if (current_pool == NULL) { Log(LOG_ERR, "ERROR ( %s/%s ): Cannot allocate my first memory pool, try with larger value.\n", config.name, config.type); exit_plugin(1); } a = current_pool->base_ptr; current_pool = request_memory_pool(config.memory_pool_size); if (current_pool == NULL) { Log(LOG_ERR, "ERROR ( %s/%s ): Cannot allocate more memory pools, try with larger value.\n", config.name, config.type); exit_plugin(1); } go_to_clear = FALSE; no_more_space = FALSE; memcpy(&table_reset_stamp, &cycle_stamp, sizeof(struct timeval)); } if (FD_ISSET(pipe_fd, &read_descs)) { if (!pollagain) { seq++; seq %= MAX_SEQNUM; } pollagain = FALSE; if ((num = read(pipe_fd, &rgptr, sizeof(rgptr))) == 0) exit_plugin(1); /* we exit silently; something happened at the write end */ if (num < 0) { pollagain = TRUE; goto select_again; } memcpy(pipebuf, rgptr, config.buffer_size); if (((struct ch_buf_hdr *)pipebuf)->seq != seq) { rg_err_count++; if (config.debug || (rg_err_count > MAX_RG_COUNT_ERR)) { Log(LOG_ERR, "ERROR ( %s/%s ): We are missing data.\n", config.name, config.type); Log(LOG_ERR, "If you see this message once in a while, discard it. Otherwise some solutions follow:\n"); Log(LOG_ERR, "- increase shared memory size, 'plugin_pipe_size'; now: '%d'.\n", config.pipe_size); Log(LOG_ERR, "- increase buffer size, 'plugin_buffer_size'; now: '%d'.\n", config.buffer_size); Log(LOG_ERR, "- increase system maximum socket size.\n\n"); seq = ((struct ch_buf_hdr *)pipebuf)->seq; } } if (num > 0) { data = (struct pkt_data *) (pipebuf+sizeof(struct ch_buf_hdr)); while (((struct ch_buf_hdr *)pipebuf)->num > 0) { // XXX: to be optimized: remove empty_* vars if (extras.off_pkt_bgp_primitives) pbgp = (struct pkt_bgp_primitives *) ((u_char *)data + extras.off_pkt_bgp_primitives); else pbgp = &empty_pbgp; if (extras.off_pkt_nat_primitives) pnat = (struct pkt_nat_primitives *) ((u_char *)data + extras.off_pkt_nat_primitives); else pnat = &empty_pnat; if (extras.off_pkt_mpls_primitives) pmpls = (struct pkt_mpls_primitives *) ((u_char *)data + extras.off_pkt_mpls_primitives); else pmpls = &empty_pmpls; if (extras.off_custom_primitives) pcust = ((u_char *)data + extras.off_custom_primitives); else pcust = empty_pcust; if (extras.off_pkt_vlen_hdr_primitives) pvlen = (struct pkt_vlen_hdr_primitives *) ((u_char *)data + extras.off_pkt_vlen_hdr_primitives); else pvlen = &empty_pvlen; for (num = 0; net_funcs[num]; num++) (*net_funcs[num])(&nt, &nc, &data->primitives, pbgp, &nfd); if (config.ports_file) { if (!pt.table[data->primitives.src_port]) data->primitives.src_port = 0; if (!pt.table[data->primitives.dst_port]) data->primitives.dst_port = 0; } if (config.pkt_len_distrib_bins_str && config.what_to_count_2 & COUNT_PKT_LEN_DISTRIB) evaluate_pkt_len_distrib(data); prim_ptrs.data = data; prim_ptrs.pbgp = pbgp; prim_ptrs.pnat = pnat; prim_ptrs.pmpls = pmpls; prim_ptrs.pcust = pcust; prim_ptrs.pvlen = pvlen; (*insert_func)(&prim_ptrs); ((struct ch_buf_hdr *)pipebuf)->num--; if (((struct ch_buf_hdr *)pipebuf)->num) { dataptr = (unsigned char *) data; dataptr += datasize; data = (struct pkt_data *) dataptr; } } } } if (reload_map) { load_networks(config.networks_file, &nt, &nc); load_ports(config.ports_file, &pt); reload_map = FALSE; } } }
/* Functions */ void kafka_plugin(int pipe_fd, struct configuration *cfgptr, void *ptr) { struct pkt_data *data; struct ports_table pt; unsigned char *pipebuf; struct pollfd pfd; struct insert_data idata; time_t t, avro_schema_deadline = 0; int timeout, refresh_timeout, avro_schema_timeout = 0; int ret, num; struct ring *rg = &((struct channels_list_entry *)ptr)->rg; struct ch_status *status = ((struct channels_list_entry *)ptr)->status; struct plugins_list_entry *plugin_data = ((struct channels_list_entry *)ptr)->plugin; int datasize = ((struct channels_list_entry *)ptr)->datasize; u_int32_t bufsz = ((struct channels_list_entry *)ptr)->bufsize; pid_t core_pid = ((struct channels_list_entry *)ptr)->core_pid; struct networks_file_data nfd; unsigned char *rgptr; int pollagain = TRUE; u_int32_t seq = 1, rg_err_count = 0; struct extra_primitives extras; struct primitives_ptrs prim_ptrs; char *dataptr; #ifdef WITH_AVRO char *avro_acct_schema_str; #endif #ifdef WITH_ZMQ struct p_zmq_host *zmq_host = &((struct channels_list_entry *)ptr)->zmq_host; #endif memcpy(&config, cfgptr, sizeof(struct configuration)); memcpy(&extras, &((struct channels_list_entry *)ptr)->extras, sizeof(struct extra_primitives)); recollect_pipe_memory(ptr); pm_setproctitle("%s [%s]", "Kafka Plugin", config.name); P_set_signals(); P_init_default_values(); P_config_checks(); pipebuf = (unsigned char *) pm_malloc(config.buffer_size); memset(pipebuf, 0, config.buffer_size); timeout = config.sql_refresh_time*1000; if (!config.message_broker_output) config.message_broker_output = PRINT_OUTPUT_JSON; if (config.message_broker_output & PRINT_OUTPUT_JSON) { compose_json(config.what_to_count, config.what_to_count_2); } else if (config.message_broker_output & PRINT_OUTPUT_AVRO) { #ifdef WITH_AVRO avro_acct_schema = build_avro_schema(config.what_to_count, config.what_to_count_2); avro_schema_add_writer_id(avro_acct_schema); if (config.avro_schema_output_file) write_avro_schema_to_file(config.avro_schema_output_file, avro_acct_schema); if (config.kafka_avro_schema_topic) { if (!config.kafka_avro_schema_refresh_time) config.kafka_avro_schema_refresh_time = DEFAULT_AVRO_SCHEMA_REFRESH_TIME; avro_schema_deadline = time(NULL); P_init_refresh_deadline(&avro_schema_deadline, config.kafka_avro_schema_refresh_time, 0, "m"); avro_acct_schema_str = compose_avro_purge_schema(avro_acct_schema, config.name); } else { config.kafka_avro_schema_refresh_time = 0; avro_schema_deadline = 0; avro_schema_timeout = 0; avro_acct_schema_str = NULL; } #endif } if ((config.sql_table && strchr(config.sql_table, '$')) && config.sql_multi_values) { Log(LOG_ERR, "ERROR ( %s/%s ): dynamic 'kafka_topic' is not compatible with 'kafka_multi_values'. Exiting.\n", config.name, config.type); exit_plugin(1); } if ((config.sql_table && strchr(config.sql_table, '$')) && config.amqp_routing_key_rr) { Log(LOG_ERR, "ERROR ( %s/%s ): dynamic 'kafka_topic' is not compatible with 'kafka_topic_rr'. Exiting.\n", config.name, config.type); exit_plugin(1); } /* setting function pointers */ if (config.what_to_count & (COUNT_SUM_HOST|COUNT_SUM_NET)) insert_func = P_sum_host_insert; else if (config.what_to_count & COUNT_SUM_PORT) insert_func = P_sum_port_insert; else if (config.what_to_count & COUNT_SUM_AS) insert_func = P_sum_as_insert; #if defined (HAVE_L2) else if (config.what_to_count & COUNT_SUM_MAC) insert_func = P_sum_mac_insert; #endif else insert_func = P_cache_insert; purge_func = kafka_cache_purge; memset(&nt, 0, sizeof(nt)); memset(&nc, 0, sizeof(nc)); memset(&pt, 0, sizeof(pt)); load_networks(config.networks_file, &nt, &nc); set_net_funcs(&nt); if (config.ports_file) load_ports(config.ports_file, &pt); if (config.pkt_len_distrib_bins_str) load_pkt_len_distrib_bins(); else { if (config.what_to_count_2 & COUNT_PKT_LEN_DISTRIB) { Log(LOG_ERR, "ERROR ( %s/%s ): 'aggregate' contains pkt_len_distrib but no 'pkt_len_distrib_bins' defined. Exiting.\n", config.name, config.type); exit_plugin(1); } } memset(&idata, 0, sizeof(idata)); memset(&prim_ptrs, 0, sizeof(prim_ptrs)); set_primptrs_funcs(&extras); if (config.pipe_zmq) { plugin_pipe_zmq_compile_check(); #ifdef WITH_ZMQ p_zmq_plugin_pipe_init_plugin(zmq_host); p_zmq_plugin_pipe_consume(zmq_host); p_zmq_set_retry_timeout(zmq_host, config.pipe_zmq_retry); pipe_fd = p_zmq_get_fd(zmq_host); seq = 0; #endif } else setnonblocking(pipe_fd); idata.now = time(NULL); /* print_refresh time init: deadline */ refresh_deadline = idata.now; P_init_refresh_deadline(&refresh_deadline, config.sql_refresh_time, config.sql_startup_delay, config.sql_history_roundoff); if (config.sql_history) { basetime_init = P_init_historical_acct; basetime_eval = P_eval_historical_acct; basetime_cmp = P_cmp_historical_acct; (*basetime_init)(idata.now); } /* setting number of entries in _protocols structure */ while (_protocols[protocols_number].number != -1) protocols_number++; /* plugin main loop */ for(;;) { poll_again: status->wakeup = TRUE; calc_refresh_timeout(refresh_deadline, idata.now, &refresh_timeout); if (config.kafka_avro_schema_topic) calc_refresh_timeout(avro_schema_deadline, idata.now, &avro_schema_timeout); pfd.fd = pipe_fd; pfd.events = POLLIN; timeout = MIN(refresh_timeout, (avro_schema_timeout ? avro_schema_timeout : INT_MAX)); ret = poll(&pfd, (pfd.fd == ERR ? 0 : 1), timeout); if (ret <= 0) { if (getppid() == 1) { Log(LOG_ERR, "ERROR ( %s/%s ): Core process *seems* gone. Exiting.\n", config.name, config.type); exit_plugin(1); } if (ret < 0) goto poll_again; } idata.now = time(NULL); if (config.sql_history) { while (idata.now > (basetime.tv_sec + timeslot)) { new_basetime.tv_sec = basetime.tv_sec; basetime.tv_sec += timeslot; if (config.sql_history == COUNT_MONTHLY) timeslot = calc_monthly_timeslot(basetime.tv_sec, config.sql_history_howmany, ADD); } } #ifdef WITH_AVRO if (idata.now > avro_schema_deadline) { kafka_avro_schema_purge(avro_acct_schema_str); avro_schema_deadline += config.kafka_avro_schema_refresh_time; } #endif switch (ret) { case 0: /* timeout */ if (idata.now > refresh_deadline) P_cache_handle_flush_event(&pt); break; default: /* we received data */ read_data: if (config.pipe_homegrown) { if (!pollagain) { seq++; seq %= MAX_SEQNUM; if (seq == 0) rg_err_count = FALSE; } else { if ((ret = read(pipe_fd, &rgptr, sizeof(rgptr))) == 0) exit_plugin(1); /* we exit silently; something happened at the write end */ } if ((rg->ptr + bufsz) > rg->end) rg->ptr = rg->base; if (((struct ch_buf_hdr *)rg->ptr)->seq != seq) { if (!pollagain) { pollagain = TRUE; goto poll_again; } else { rg_err_count++; if (config.debug || (rg_err_count > MAX_RG_COUNT_ERR)) { Log(LOG_WARNING, "WARN ( %s/%s ): Missing data detected (plugin_buffer_size=%llu plugin_pipe_size=%llu).\n", config.name, config.type, config.buffer_size, config.pipe_size); Log(LOG_WARNING, "WARN ( %s/%s ): Increase values or look for plugin_buffer_size, plugin_pipe_size in CONFIG-KEYS document.\n\n", config.name, config.type); } rg->ptr = (rg->base + status->last_buf_off); seq = ((struct ch_buf_hdr *)rg->ptr)->seq; } } pollagain = FALSE; memcpy(pipebuf, rg->ptr, bufsz); rg->ptr += bufsz; } #ifdef WITH_ZMQ else if (config.pipe_zmq) { ret = p_zmq_plugin_pipe_recv(zmq_host, pipebuf, config.buffer_size); if (ret > 0) { if (seq && (((struct ch_buf_hdr *)pipebuf)->seq != ((seq + 1) % MAX_SEQNUM))) { Log(LOG_WARNING, "WARN ( %s/%s ): Missing data detected. Sequence received=%u expected=%u\n", config.name, config.type, ((struct ch_buf_hdr *)pipebuf)->seq, ((seq + 1) % MAX_SEQNUM)); } seq = ((struct ch_buf_hdr *)pipebuf)->seq; } else goto poll_again; } #endif /* lazy refresh time handling */ if (idata.now > refresh_deadline) P_cache_handle_flush_event(&pt); data = (struct pkt_data *) (pipebuf+sizeof(struct ch_buf_hdr)); if (config.debug_internal_msg) Log(LOG_DEBUG, "DEBUG ( %s/%s ): buffer received cpid=%u len=%llu seq=%u num_entries=%u\n", config.name, config.type, core_pid, ((struct ch_buf_hdr *)pipebuf)->len, seq, ((struct ch_buf_hdr *)pipebuf)->num); if (!config.pipe_check_core_pid || ((struct ch_buf_hdr *)pipebuf)->core_pid == core_pid) { while (((struct ch_buf_hdr *)pipebuf)->num > 0) { for (num = 0; primptrs_funcs[num]; num++) (*primptrs_funcs[num])((u_char *)data, &extras, &prim_ptrs); for (num = 0; net_funcs[num]; num++) (*net_funcs[num])(&nt, &nc, &data->primitives, prim_ptrs.pbgp, &nfd); if (config.ports_file) { if (!pt.table[data->primitives.src_port]) data->primitives.src_port = 0; if (!pt.table[data->primitives.dst_port]) data->primitives.dst_port = 0; } if (config.pkt_len_distrib_bins_str && config.what_to_count_2 & COUNT_PKT_LEN_DISTRIB) evaluate_pkt_len_distrib(data); prim_ptrs.data = data; (*insert_func)(&prim_ptrs, &idata); ((struct ch_buf_hdr *)pipebuf)->num--; if (((struct ch_buf_hdr *)pipebuf)->num) { dataptr = (unsigned char *) data; if (!prim_ptrs.vlen_next_off) dataptr += datasize; else dataptr += prim_ptrs.vlen_next_off; data = (struct pkt_data *) dataptr; } } } goto read_data; } } }
int main(int argc,char **argv, char **envp) { bpf_u_int32 localnet, netmask; /* pcap library stuff */ struct bpf_program filter; struct pcap_device device; char errbuf[PCAP_ERRBUF_SIZE]; int index, logf; struct plugins_list_entry *list; struct plugin_requests req; char config_file[SRVBUFLEN]; int psize = ULOG_BUFLEN; struct id_table bpas_table; struct id_table blp_table; struct id_table bmed_table; struct id_table biss_table; struct id_table bta_table; struct id_table idt; struct pcap_callback_data cb_data; /* getopt() stuff */ extern char *optarg; extern int optind, opterr, optopt; int errflag, cp; /* ULOG stuff */ int ulog_fd, one = 1; struct nlmsghdr *nlh; struct sockaddr_nl nls; ulog_packet_msg_t *ulog_pkt; ssize_t len = 0; socklen_t alen; unsigned char *ulog_buffer; struct pcap_pkthdr hdr; struct timeval tv; char jumbo_container[10000]; u_int8_t mac_len; #if defined ENABLE_IPV6 struct sockaddr_storage client; #else struct sockaddr client; #endif #if defined HAVE_MALLOPT mallopt(M_CHECK_ACTION, 0); #endif umask(077); compute_once(); /* a bunch of default definitions */ have_num_memory_pools = FALSE; reload_map = FALSE; tag_map_allocated = FALSE; bpas_map_allocated = FALSE; blp_map_allocated = FALSE; bmed_map_allocated = FALSE; biss_map_allocated = FALSE; bta_map_caching = FALSE; sampling_map_caching = FALSE; find_id_func = PM_find_id; errflag = 0; memset(cfg_cmdline, 0, sizeof(cfg_cmdline)); memset(&config, 0, sizeof(struct configuration)); memset(&device, 0, sizeof(struct pcap_device)); memset(&config_file, 0, sizeof(config_file)); memset(&failed_plugins, 0, sizeof(failed_plugins)); memset(&req, 0, sizeof(req)); memset(dummy_tlhdr, 0, sizeof(dummy_tlhdr)); memset(sll_mac, 0, sizeof(sll_mac)); memset(&bpas_table, 0, sizeof(bpas_table)); memset(&blp_table, 0, sizeof(blp_table)); memset(&bmed_table, 0, sizeof(bmed_table)); memset(&biss_table, 0, sizeof(biss_table)); memset(&bta_table, 0, sizeof(bta_table)); memset(&client, 0, sizeof(client)); memset(&cb_data, 0, sizeof(cb_data)); memset(&tunnel_registry, 0, sizeof(tunnel_registry)); memset(&reload_map_tstamp, 0, sizeof(reload_map_tstamp)); config.acct_type = ACCT_PM; rows = 0; glob_pcapt = NULL; /* getting commandline values */ while (!errflag && ((cp = getopt(argc, argv, ARGS_UACCTD)) != -1)) { cfg_cmdline[rows] = malloc(SRVBUFLEN); switch (cp) { case 'P': strlcpy(cfg_cmdline[rows], "plugins: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'D': strlcpy(cfg_cmdline[rows], "daemonize: true", SRVBUFLEN); rows++; break; case 'd': debug = TRUE; strlcpy(cfg_cmdline[rows], "debug: true", SRVBUFLEN); rows++; break; case 'n': strlcpy(cfg_cmdline[rows], "networks_file: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'o': strlcpy(cfg_cmdline[rows], "ports_file: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'O': strlcpy(cfg_cmdline[rows], "print_output: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'u': strlcpy(cfg_cmdline[rows], "print_num_protos: true", SRVBUFLEN); rows++; break; case 'f': strlcpy(config_file, optarg, sizeof(config_file)); break; case 'F': strlcpy(cfg_cmdline[rows], "pidfile: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'c': strlcpy(cfg_cmdline[rows], "aggregate: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'b': strlcpy(cfg_cmdline[rows], "imt_buckets: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'm': strlcpy(cfg_cmdline[rows], "imt_mem_pools_number: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); have_num_memory_pools = TRUE; rows++; break; case 'p': strlcpy(cfg_cmdline[rows], "imt_path: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'r': strlcpy(cfg_cmdline[rows], "sql_refresh_time: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'v': strlcpy(cfg_cmdline[rows], "sql_table_version: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 's': strlcpy(cfg_cmdline[rows], "imt_mem_pools_size: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'S': strlcpy(cfg_cmdline[rows], "syslog: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'g': strlcpy(cfg_cmdline[rows], "uacctd_group: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'L': strlcpy(cfg_cmdline[rows], "snaplen: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'R': strlcpy(cfg_cmdline[rows], "sfacctd_renormalize: true", SRVBUFLEN); rows++; break; case 'h': usage_daemon(argv[0]); exit(0); break; case 'V': version_daemon(UACCTD_USAGE_HEADER); exit(0); break; default: usage_daemon(argv[0]); exit(1); break; } } /* post-checks and resolving conflicts */ if (strlen(config_file)) { if (parse_configuration_file(config_file) != SUCCESS) exit(1); } else { if (parse_configuration_file(NULL) != SUCCESS) exit(1); } /* XXX: glue; i'm conscious it's a dirty solution from an engineering viewpoint; someday later i'll fix this */ list = plugins_list; while (list) { list->cfg.acct_type = ACCT_PM; set_default_preferences(&list->cfg); if (!strcmp(list->name, "default") && !strcmp(list->type.string, "core")) { memcpy(&config, &list->cfg, sizeof(struct configuration)); config.name = list->name; config.type = list->type.string; } list = list->next; } if (config.files_umask) umask(config.files_umask); if (!config.snaplen) config.snaplen = psize; if (!config.uacctd_nl_size) config.uacctd_nl_size = psize; /* Let's check whether we need superuser privileges */ if (getuid() != 0) { printf("%s\n\n", UACCTD_USAGE_HEADER); printf("ERROR ( default/core ): You need superuser privileges to run this command.\nExiting ...\n\n"); exit(1); } if (!config.uacctd_group) { config.uacctd_group = DEFAULT_ULOG_GROUP; list = plugins_list; while (list) { list->cfg.uacctd_group = DEFAULT_ULOG_GROUP; list = list->next; } } if (config.daemon) { list = plugins_list; while (list) { if (!strcmp(list->type.string, "print")) printf("WARN ( default/core ): Daemonizing. Hmm, bye bye screen.\n"); list = list->next; } if (debug || config.debug) printf("WARN ( default/core ): debug is enabled; forking in background. Console logging will get lost.\n"); daemonize(); } initsetproctitle(argc, argv, envp); if (config.syslog) { logf = parse_log_facility(config.syslog); if (logf == ERR) { config.syslog = NULL; printf("WARN ( default/core ): specified syslog facility is not supported; logging to console.\n"); } else openlog(NULL, LOG_PID, logf); Log(LOG_INFO, "INFO ( default/core ): Start logging ...\n"); } if (config.logfile) { config.logfile_fd = open_logfile(config.logfile); list = plugins_list; while (list) { list->cfg.logfile_fd = config.logfile_fd ; list = list->next; } } /* Enforcing policies over aggregation methods */ list = plugins_list; while (list) { if (list->type.id != PLUGIN_ID_CORE) { /* applies to all plugins */ if (config.classifiers_path && (list->cfg.sampling_rate || config.ext_sampling_rate)) { Log(LOG_ERR, "ERROR ( default/core ): Packet sampling and classification are mutual exclusive.\n"); exit(1); } if (list->cfg.sampling_rate && config.ext_sampling_rate) { Log(LOG_ERR, "ERROR ( default/core ): Internal packet sampling and external packet sampling are mutual exclusive.\n"); exit(1); } if (list->type.id == PLUGIN_ID_TEE) { Log(LOG_ERR, "ERROR ( default/core ): 'tee' plugin not supported in 'uacctd'.\n"); exit(1); } else if (list->type.id == PLUGIN_ID_NFPROBE) { /* If we already renormalizing an external sampling rate, we cancel the sampling information from the probe plugin */ if (config.sfacctd_renormalize && list->cfg.ext_sampling_rate) list->cfg.ext_sampling_rate = 0; config.handle_fragments = TRUE; list->cfg.nfprobe_what_to_count = list->cfg.what_to_count; list->cfg.nfprobe_what_to_count_2 = list->cfg.what_to_count_2; list->cfg.what_to_count = 0; list->cfg.what_to_count_2 = 0; #if defined (HAVE_L2) if (list->cfg.nfprobe_version == 9 || list->cfg.nfprobe_version == 10) { list->cfg.what_to_count |= COUNT_SRC_MAC; list->cfg.what_to_count |= COUNT_DST_MAC; list->cfg.what_to_count |= COUNT_VLAN; } #endif list->cfg.what_to_count |= COUNT_SRC_HOST; list->cfg.what_to_count |= COUNT_DST_HOST; if (list->cfg.networks_file || list->cfg.networks_mask || list->cfg.nfacctd_net) { list->cfg.what_to_count |= COUNT_SRC_NMASK; list->cfg.what_to_count |= COUNT_DST_NMASK; } list->cfg.what_to_count |= COUNT_SRC_PORT; list->cfg.what_to_count |= COUNT_DST_PORT; list->cfg.what_to_count |= COUNT_IP_TOS; list->cfg.what_to_count |= COUNT_IP_PROTO; if (list->cfg.networks_file || (list->cfg.nfacctd_bgp && list->cfg.nfacctd_as == NF_AS_BGP)) { list->cfg.what_to_count |= COUNT_SRC_AS; list->cfg.what_to_count |= COUNT_DST_AS; list->cfg.what_to_count |= COUNT_PEER_DST_IP; } if ((list->cfg.nfprobe_version == 9 || list->cfg.nfprobe_version == 10) && list->cfg.classifiers_path) { list->cfg.what_to_count |= COUNT_CLASS; config.handle_flows = TRUE; } if (list->cfg.pre_tag_map) { list->cfg.what_to_count |= COUNT_ID; list->cfg.what_to_count |= COUNT_ID2; } list->cfg.what_to_count |= COUNT_IN_IFACE; list->cfg.what_to_count |= COUNT_OUT_IFACE; if (list->cfg.what_to_count & (COUNT_STD_COMM|COUNT_EXT_COMM|COUNT_LOCAL_PREF|COUNT_MED|COUNT_AS_PATH| COUNT_PEER_SRC_AS|COUNT_PEER_DST_AS|COUNT_PEER_SRC_IP|COUNT_SRC_STD_COMM| COUNT_SRC_EXT_COMM|COUNT_SRC_AS_PATH|COUNT_SRC_MED|COUNT_SRC_LOCAL_PREF| COUNT_MPLS_VPN_RD)) { Log(LOG_ERR, "ERROR ( default/core ): 'src_as', 'dst_as' and 'peer_dst_ip' are currently the only BGP-related primitives supported within the 'nfprobe' plugin.\n"); exit(1); } list->cfg.what_to_count |= COUNT_COUNTERS; list->cfg.data_type = PIPE_TYPE_METADATA; list->cfg.data_type |= PIPE_TYPE_EXTRAS; } else if (list->type.id == PLUGIN_ID_SFPROBE) { /* If we already renormalizing an external sampling rate, we cancel the sampling information from the probe plugin */ if (config.sfacctd_renormalize && list->cfg.ext_sampling_rate) list->cfg.ext_sampling_rate = 0; if (psize < 128) psize = config.snaplen = 128; /* SFL_DEFAULT_HEADER_SIZE */ list->cfg.what_to_count = COUNT_PAYLOAD; list->cfg.what_to_count_2 = 0; if (list->cfg.classifiers_path) { list->cfg.what_to_count |= COUNT_CLASS; config.handle_fragments = TRUE; config.handle_flows = TRUE; } if (list->cfg.nfacctd_bgp && list->cfg.nfacctd_as == NF_AS_BGP) { list->cfg.what_to_count |= COUNT_SRC_AS; list->cfg.what_to_count |= COUNT_DST_AS; list->cfg.what_to_count |= COUNT_PEER_DST_IP; } if ((list->cfg.nfacctd_bgp && list->cfg.nfacctd_net == NF_NET_BGP) || (list->cfg.nfacctd_isis && list->cfg.nfacctd_net == NF_NET_IGP)) { list->cfg.what_to_count |= COUNT_SRC_NMASK; list->cfg.what_to_count |= COUNT_DST_NMASK; } if (list->cfg.pre_tag_map) { list->cfg.what_to_count |= COUNT_ID; list->cfg.what_to_count |= COUNT_ID2; } if (list->cfg.what_to_count & (COUNT_STD_COMM|COUNT_EXT_COMM|COUNT_LOCAL_PREF|COUNT_MED|COUNT_AS_PATH| COUNT_PEER_SRC_AS|COUNT_PEER_DST_AS|COUNT_PEER_SRC_IP|COUNT_SRC_STD_COMM| COUNT_SRC_EXT_COMM|COUNT_SRC_AS_PATH|COUNT_SRC_MED|COUNT_SRC_LOCAL_PREF| COUNT_MPLS_VPN_RD)) { Log(LOG_ERR, "ERROR ( default/core ): 'src_as', 'dst_as' and 'peer_dst_ip' are currently the only BGP-related primitives supported within the 'sfprobe' plugin.\n"); exit(1); } #if defined (HAVE_L2) list->cfg.what_to_count |= COUNT_VLAN; list->cfg.what_to_count |= COUNT_COS; #endif list->cfg.data_type = PIPE_TYPE_PAYLOAD; } else { if (list->cfg.what_to_count_2 & (COUNT_POST_NAT_SRC_HOST|COUNT_POST_NAT_DST_HOST| COUNT_POST_NAT_SRC_PORT|COUNT_POST_NAT_DST_PORT|COUNT_NAT_EVENT| COUNT_TIMESTAMP_START|COUNT_TIMESTAMP_END)) list->cfg.data_type |= PIPE_TYPE_NAT; evaluate_sums(&list->cfg.what_to_count, list->name, list->type.string); if (list->cfg.what_to_count & (COUNT_SRC_PORT|COUNT_DST_PORT|COUNT_SUM_PORT|COUNT_TCPFLAGS)) config.handle_fragments = TRUE; if (list->cfg.what_to_count & COUNT_FLOWS) { config.handle_fragments = TRUE; config.handle_flows = TRUE; } if (list->cfg.what_to_count & COUNT_CLASS) { config.handle_fragments = TRUE; config.handle_flows = TRUE; } if (!list->cfg.what_to_count && !list->cfg.what_to_count_2) { Log(LOG_WARNING, "WARN ( %s/%s ): defaulting to SRC HOST aggregation.\n", list->name, list->type.string); list->cfg.what_to_count |= COUNT_SRC_HOST; } if (list->cfg.what_to_count & (COUNT_SRC_AS|COUNT_DST_AS|COUNT_SUM_AS)) { if (!list->cfg.networks_file && list->cfg.nfacctd_as != NF_AS_BGP) { Log(LOG_ERR, "ERROR ( %s/%s ): AS aggregation selected but NO 'networks_file' or 'uacctd_as' are specified. Exiting...\n\n", list->name, list->type.string); exit(1); } if (list->cfg.nfacctd_as & NF_AS_FALLBACK && list->cfg.networks_file) list->cfg.nfacctd_as |= NF_AS_NEW; } if (list->cfg.what_to_count & (COUNT_SRC_NET|COUNT_DST_NET|COUNT_SUM_NET|COUNT_SRC_NMASK|COUNT_DST_NMASK|COUNT_PEER_DST_IP)) { if (!list->cfg.nfacctd_net) { if (list->cfg.networks_file) list->cfg.nfacctd_net |= NF_NET_NEW; if (list->cfg.networks_mask) list->cfg.nfacctd_net |= NF_NET_STATIC; if (!list->cfg.nfacctd_net) { Log(LOG_ERR, "ERROR ( %s/%s ): network aggregation selected but none of 'uacctd_net', 'networks_file', 'networks_mask' is specified. Exiting ...\n\n", list->name, list->type.string); exit(1); } } else { if ((list->cfg.nfacctd_net == NF_NET_NEW && !list->cfg.networks_file) || (list->cfg.nfacctd_net == NF_NET_STATIC && !list->cfg.networks_mask) || (list->cfg.nfacctd_net == NF_NET_BGP && !list->cfg.nfacctd_bgp) || (list->cfg.nfacctd_net == NF_NET_IGP && !list->cfg.nfacctd_isis) || (list->cfg.nfacctd_net == NF_NET_KEEP)) { Log(LOG_ERR, "ERROR ( %s/%s ): network aggregation selected but none of 'bgp_daemon', 'isis_daemon', 'networks_file', 'networks_mask' is specified. Exiting ...\n\n", list->name, list->type.string); exit(1); } if (list->cfg.nfacctd_net & NF_NET_FALLBACK && list->cfg.networks_file) list->cfg.nfacctd_net |= NF_NET_NEW; } } if (list->cfg.what_to_count & COUNT_CLASS && !list->cfg.classifiers_path) { Log(LOG_ERR, "ERROR ( %s/%s ): 'class' aggregation selected but NO 'classifiers' key specified. Exiting...\n\n", list->name, list->type.string); exit(1); } bgp_config_checks(&list->cfg); list->cfg.what_to_count |= COUNT_COUNTERS; list->cfg.data_type |= PIPE_TYPE_METADATA; } } list = list->next; } /* plugins glue: creation (since 094) */ if (config.classifiers_path) { init_classifiers(config.classifiers_path); init_conntrack_table(); } load_plugins(&req); if (config.handle_fragments) init_ip_fragment_handler(); if (config.handle_flows) init_ip_flow_handler(); load_networks(config.networks_file, &nt, &nc); #if defined (HAVE_L2) device.link_type = DLT_EN10MB; #else device.link_type = DLT_RAW; #endif for (index = 0; _devices[index].link_type != -1; index++) { if (device.link_type == _devices[index].link_type) device.data = &_devices[index]; } load_plugin_filters(device.link_type); cb_data.device = &device; /* signal handling we want to inherit to plugins (when not re-defined elsewhere) */ signal(SIGCHLD, startup_handle_falling_child); /* takes note of plugins failed during startup phase */ signal(SIGHUP, reload); /* handles reopening of syslog channel */ signal(SIGUSR1, push_stats); /* logs various statistics via Log() calls */ signal(SIGUSR2, reload_maps); /* sets to true the reload_maps flag */ signal(SIGPIPE, SIG_IGN); /* we want to exit gracefully when a pipe is broken */ ulog_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_NFLOG); if (ulog_fd == -1) { Log(LOG_ERR, "ERROR ( default/core ): Failed to create Netlink ULOG socket\n"); exit_all(1); } Log(LOG_INFO, "INFO ( default/core ): Successfully connected Netlink ULOG socket\n"); /* Turn off netlink errors from overrun. */ if (setsockopt(ulog_fd, SOL_NETLINK, NETLINK_NO_ENOBUFS, &one, sizeof(one))) Log(LOG_ERR, "ERROR ( default/core ): Failed to turn off netlink ENOBUFS\n"); if (config.uacctd_nl_size > ULOG_BUFLEN) { /* If configured buffer size is larger than default 4KB */ if (setsockopt(ulog_fd, SOL_SOCKET, SO_RCVBUF, &config.uacctd_nl_size, sizeof(config.uacctd_nl_size))) Log(LOG_ERR, "ERROR ( default/core ): Failed to set Netlink receive buffer size\n"); else Log(LOG_INFO, "INFO ( default/core ): Netlink receive buffer size set to %u\n", config.uacctd_nl_size); } ulog_buffer = malloc(config.snaplen); if (ulog_buffer == NULL) { Log(LOG_ERR, "ERROR ( default/core ): ULOG buffer malloc() failed\n"); close(ulog_fd); exit_all(1); } memset(&nls, 0, sizeof(nls)); nls.nl_family = AF_NETLINK; nls.nl_pid = getpid(); nls.nl_groups = config.uacctd_group; alen = sizeof(nls); if (bind(ulog_fd, (struct sockaddr *) &nls, sizeof(nls))) { Log(LOG_ERR, "ERROR ( default/core ): bind() to Netlink ULOG socket failed\n"); close(ulog_fd); exit_all(1); } Log(LOG_INFO, "INFO ( default/core ): Netlink ULOG: binding to group %x\n", config.uacctd_group); /* loading pre-tagging map, if any */ if (config.pre_tag_map) { load_id_file(config.acct_type, config.pre_tag_map, &idt, &req, &tag_map_allocated); cb_data.idt = (u_char *) &idt; } else { memset(&idt, 0, sizeof(idt)); cb_data.idt = NULL; } #if defined ENABLE_THREADS /* starting the ISIS threa */ if (config.nfacctd_isis) { req.bpf_filter = TRUE; nfacctd_isis_wrapper(); /* Let's give the ISIS thread some advantage to create its structures */ sleep(5); } /* starting the BGP thread */ if (config.nfacctd_bgp) { req.bpf_filter = TRUE; load_comm_patterns(&config.nfacctd_bgp_stdcomm_pattern, &config.nfacctd_bgp_extcomm_pattern, &config.nfacctd_bgp_stdcomm_pattern_to_asn); if (config.nfacctd_bgp_peer_as_src_type == BGP_SRC_PRIMITIVES_MAP) { if (config.nfacctd_bgp_peer_as_src_map) { load_id_file(MAP_BGP_PEER_AS_SRC, config.nfacctd_bgp_peer_as_src_map, &bpas_table, &req, &bpas_map_allocated); cb_data.bpas_table = (u_char *) &bpas_table; } else { Log(LOG_ERR, "ERROR: bgp_peer_as_src_type set to 'map' but no map defined. Exiting.\n"); exit(1); } } else cb_data.bpas_table = NULL; if (config.nfacctd_bgp_src_local_pref_type == BGP_SRC_PRIMITIVES_MAP) { if (config.nfacctd_bgp_src_local_pref_map) { load_id_file(MAP_BGP_SRC_LOCAL_PREF, config.nfacctd_bgp_src_local_pref_map, &blp_table, &req, &blp_map_allocated); cb_data.blp_table = (u_char *) &blp_table; } else { Log(LOG_ERR, "ERROR: bgp_src_local_pref_type set to 'map' but no map defined. Exiting.\n"); exit(1); } } else cb_data.bpas_table = NULL; if (config.nfacctd_bgp_src_med_type == BGP_SRC_PRIMITIVES_MAP) { if (config.nfacctd_bgp_src_med_map) { load_id_file(MAP_BGP_SRC_MED, config.nfacctd_bgp_src_med_map, &bmed_table, &req, &bmed_map_allocated); cb_data.bmed_table = (u_char *) &bmed_table; } else { Log(LOG_ERR, "ERROR: bgp_src_med_type set to 'map' but no map defined. Exiting.\n"); exit(1); } } else cb_data.bmed_table = NULL; if (config.nfacctd_bgp_to_agent_map) { load_id_file(MAP_BGP_TO_XFLOW_AGENT, config.nfacctd_bgp_to_agent_map, &bta_table, &req, &bta_map_allocated); cb_data.bta_table = (u_char *) &bta_table; } else { Log(LOG_ERR, "ERROR ( default/core ): 'bgp_daemon' configured but no 'bgp_agent_map' has been specified. Exiting.\n"); exit(1); } /* Limiting BGP peers to only two: one would suffice in pmacctd but in case maps are reloadable (ie. bta), it could be handy to keep a backup feed in memory */ config.nfacctd_bgp_max_peers = 2; if (config.nfacctd_bgp_iface_to_rd_map) { Log(LOG_ERR, "ERROR ( default/core ): 'bgp_iface_to_rd_map' is not supported by this daemon. Exiting.\n"); exit(1); } cb_data.f_agent = (char *)&client; nfacctd_bgp_wrapper(); /* Let's give the BGP thread some advantage to create its structures */ sleep(5); } #else if (config.nfacctd_isis) { Log(LOG_ERR, "ERROR ( default/core ): 'isis_daemon' is available only with threads (--enable-threads). Exiting.\n"); exit(1); } if (config.nfacctd_bgp) { Log(LOG_ERR, "ERROR ( default/core ): 'bgp_daemon' is available only with threads (--enable-threads). Exiting.\n"); exit(1); } #endif #if defined WITH_GEOIP if (config.geoip_ipv4_file || config.geoip_ipv6_file) { req.bpf_filter = TRUE; } #endif /* plugins glue: creation (until 093) */ evaluate_packet_handlers(); pm_setproctitle("%s [%s]", "Core Process", "default"); if (config.pidfile) write_pid_file(config.pidfile); /* signals to be handled only by pmacctd; we set proper handlers after plugin creation */ signal(SIGINT, my_sigint_handler); signal(SIGTERM, my_sigint_handler); signal(SIGCHLD, handle_falling_child); kill(getpid(), SIGCHLD); /* Main loop: if pcap_loop() exits maybe an error occurred; we will try closing and reopening again our listening device */ for (;;) { if (len == -1) { if (errno != EAGAIN) { /* We can't deal with permanent errors. * Just sleep a bit. */ Log(LOG_ERR, "ERROR ( default/core ): Syscall returned %d: %s. Sleeping for 1 sec.\n", errno, strerror(errno)); sleep(1); } } len = recvfrom(ulog_fd, ulog_buffer, config.snaplen, 0, (struct sockaddr*) &nls, &alen); /* * Read timeout or failure condition. */ if (len < (int)sizeof(struct nlmsghdr)) continue; if (alen != sizeof(nls)) continue; nlh = (struct nlmsghdr*) ulog_buffer; if ((nlh->nlmsg_flags & MSG_TRUNC) || ((size_t)len > config.snaplen)) continue; gettimeofday(&tv, NULL); while (NLMSG_OK(nlh, (size_t)len)) { ulog_pkt = NLMSG_DATA(nlh); hdr.ts = tv; hdr.caplen = MIN(ulog_pkt->data_len, config.snaplen); hdr.len = ulog_pkt->data_len; if (strlen(ulog_pkt->indev_name) > 1) { cb_data.ifindex_in = cache_ifindex(ulog_pkt->indev_name, tv.tv_sec); } else cb_data.ifindex_in = 0; if (strlen(ulog_pkt->outdev_name) > 1) { cb_data.ifindex_out = cache_ifindex(ulog_pkt->outdev_name, tv.tv_sec); } else cb_data.ifindex_out = 0; #if defined (HAVE_L2) if (ulog_pkt->mac_len) { memcpy(jumbo_container, ulog_pkt->mac, ulog_pkt->mac_len); memcpy(jumbo_container+ulog_pkt->mac_len, ulog_pkt->payload, hdr.caplen); // XXX hdr.caplen += ulog_pkt->mac_len; hdr.len += ulog_pkt->mac_len; } else { memset(jumbo_container, 0, ETHER_HDRLEN); memcpy(jumbo_container+ETHER_HDRLEN, ulog_pkt->payload, hdr.caplen); hdr.caplen += ETHER_HDRLEN; hdr.len += ETHER_HDRLEN; switch (IP_V((struct my_iphdr *) ulog_pkt->payload)) { case 4: ((struct eth_header *)jumbo_container)->ether_type = ntohs(ETHERTYPE_IP); break; case 6: ((struct eth_header *)jumbo_container)->ether_type = ntohs(ETHERTYPE_IPV6); break; } } pcap_cb((u_char *) &cb_data, &hdr, jumbo_container); #else pcap_cb((u_char *) &cb_data, &hdr, ulog_pkt->payload); #endif if (nlh->nlmsg_type == NLMSG_DONE || !(nlh->nlmsg_flags & NLM_F_MULTI)) { /* Last part of the multilink message */ break; } nlh = NLMSG_NEXT(nlh, len); } } }
int main(int argc,char **argv, char **envp) { bpf_u_int32 localnet, netmask; /* pcap library stuff */ struct bpf_program filter; struct pcap_device device; char errbuf[PCAP_ERRBUF_SIZE]; int index, logf; struct plugins_list_entry *list; struct plugin_requests req; char config_file[SRVBUFLEN]; int psize = DEFAULT_SNAPLEN; struct id_table bpas_table; struct id_table blp_table; struct id_table bmed_table; struct id_table biss_table; struct id_table bta_table; struct id_table idt; struct pcap_callback_data cb_data; /* getopt() stuff */ extern char *optarg; extern int optind, opterr, optopt; int errflag, cp; #if defined ENABLE_IPV6 struct sockaddr_storage client; #else struct sockaddr client; #endif umask(077); compute_once(); /* a bunch of default definitions */ have_num_memory_pools = FALSE; reload_map = FALSE; tag_map_allocated = FALSE; bpas_map_allocated = FALSE; blp_map_allocated = FALSE; bmed_map_allocated = FALSE; biss_map_allocated = FALSE; find_id_func = PM_find_id; errflag = 0; memset(cfg_cmdline, 0, sizeof(cfg_cmdline)); memset(&config, 0, sizeof(struct configuration)); memset(&device, 0, sizeof(struct pcap_device)); memset(&config_file, 0, sizeof(config_file)); memset(&failed_plugins, 0, sizeof(failed_plugins)); memset(&req, 0, sizeof(req)); memset(dummy_tlhdr, 0, sizeof(dummy_tlhdr)); memset(sll_mac, 0, sizeof(sll_mac)); memset(&bpas_table, 0, sizeof(bpas_table)); memset(&blp_table, 0, sizeof(blp_table)); memset(&bmed_table, 0, sizeof(bmed_table)); memset(&biss_table, 0, sizeof(biss_table)); memset(&bta_table, 0, sizeof(bta_table)); memset(&client, 0, sizeof(client)); memset(&cb_data, 0, sizeof(cb_data)); memset(&tunnel_registry, 0, sizeof(tunnel_registry)); config.acct_type = ACCT_PM; rows = 0; glob_pcapt = NULL; /* getting commandline values */ while (!errflag && ((cp = getopt(argc, argv, ARGS_PMACCTD)) != -1)) { cfg_cmdline[rows] = malloc(SRVBUFLEN); switch (cp) { case 'P': strlcpy(cfg_cmdline[rows], "plugins: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'D': strlcpy(cfg_cmdline[rows], "daemonize: true", SRVBUFLEN); rows++; break; case 'd': debug = TRUE; strlcpy(cfg_cmdline[rows], "debug: true", SRVBUFLEN); rows++; break; case 'n': strlcpy(cfg_cmdline[rows], "networks_file: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'o': strlcpy(cfg_cmdline[rows], "ports_file: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'O': strlcpy(cfg_cmdline[rows], "print_output: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'u': strlcpy(cfg_cmdline[rows], "print_num_protos: true", SRVBUFLEN); rows++; break; case 'N': strlcpy(cfg_cmdline[rows], "promisc: false", SRVBUFLEN); rows++; break; case 'f': strlcpy(config_file, optarg, sizeof(config_file)); break; case 'F': strlcpy(cfg_cmdline[rows], "pidfile: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'c': strlcpy(cfg_cmdline[rows], "aggregate: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'b': strlcpy(cfg_cmdline[rows], "imt_buckets: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'm': strlcpy(cfg_cmdline[rows], "imt_mem_pools_number: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); have_num_memory_pools = TRUE; rows++; break; case 'p': strlcpy(cfg_cmdline[rows], "imt_path: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'r': strlcpy(cfg_cmdline[rows], "sql_refresh_time: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; cfg_cmdline[rows] = malloc(SRVBUFLEN); strlcpy(cfg_cmdline[rows], "print_refresh_time: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'v': strlcpy(cfg_cmdline[rows], "sql_table_version: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 's': strlcpy(cfg_cmdline[rows], "imt_mem_pools_size: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'S': strlcpy(cfg_cmdline[rows], "syslog: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'i': strlcpy(cfg_cmdline[rows], "interface: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'I': strlcpy(cfg_cmdline[rows], "pcap_savefile: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'w': strlcpy(cfg_cmdline[rows], "interface_wait: true", SRVBUFLEN); rows++; break; case 'W': strlcpy(cfg_cmdline[rows], "savefile_wait: true", SRVBUFLEN); rows++; break; case 'L': strlcpy(cfg_cmdline[rows], "snaplen: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'R': strlcpy(cfg_cmdline[rows], "sfacctd_renormalize: true", SRVBUFLEN); rows++; break; case 'h': usage_daemon(argv[0]); exit(0); break; default: usage_daemon(argv[0]); exit(1); break; } } /* post-checks and resolving conflicts */ if (strlen(config_file)) { if (parse_configuration_file(config_file) != SUCCESS) exit(1); } else { if (parse_configuration_file(NULL) != SUCCESS) exit(1); } /* XXX: glue; i'm conscious it's a dirty solution from an engineering viewpoint; someday later i'll fix this */ list = plugins_list; while(list) { list->cfg.acct_type = ACCT_PM; set_default_preferences(&list->cfg); if (!strcmp(list->name, "default") && !strcmp(list->type.string, "core")) memcpy(&config, &list->cfg, sizeof(struct configuration)); list = list->next; } if (config.files_umask) umask(config.files_umask); /* Let's check whether we need superuser privileges */ if (config.snaplen) psize = config.snaplen; else config.snaplen = psize; if (!config.pcap_savefile) { if (getuid() != 0) { printf("%s\n\n", PMACCTD_USAGE_HEADER); printf("ERROR ( default/core ): You need superuser privileges to run this command.\nExiting ...\n\n"); exit(1); } } if (config.daemon) { list = plugins_list; while (list) { if (!strcmp(list->type.string, "print")) printf("WARN ( default/core ): Daemonizing. Hmm, bye bye screen.\n"); list = list->next; } if (debug || config.debug) printf("WARN ( default/core ): debug is enabled; forking in background. Console logging will get lost.\n"); daemonize(); } initsetproctitle(argc, argv, envp); if (config.syslog) { logf = parse_log_facility(config.syslog); if (logf == ERR) { config.syslog = NULL; printf("WARN ( default/core ): specified syslog facility is not supported; logging to console.\n"); } else openlog(NULL, LOG_PID, logf); Log(LOG_INFO, "INFO ( default/core ): Start logging ...\n"); } if (config.logfile) { config.logfile_fd = open_logfile(config.logfile); list = plugins_list; while (list) { list->cfg.logfile_fd = config.logfile_fd ; list = list->next; } } /* Enforcing policies over aggregation methods */ list = plugins_list; while (list) { if (list->type.id != PLUGIN_ID_CORE) { /* applies to all plugins */ if (config.classifiers_path && (list->cfg.sampling_rate || config.ext_sampling_rate)) { Log(LOG_ERR, "ERROR ( default/core ): Packet sampling and classification are mutual exclusive.\n"); exit(1); } if (list->cfg.sampling_rate && config.ext_sampling_rate) { Log(LOG_ERR, "ERROR ( default/core ): Internal packet sampling and external packet sampling are mutual exclusive.\n"); exit(1); } if (list->type.id == PLUGIN_ID_TEE) { Log(LOG_ERR, "ERROR ( default/core ): 'tee' plugin not supported in 'pmacctd'.\n"); exit(1); } else if (list->type.id == PLUGIN_ID_NFPROBE) { /* If we already renormalizing an external sampling rate, we cancel the sampling information from the probe plugin */ if (config.sfacctd_renormalize && list->cfg.ext_sampling_rate) list->cfg.ext_sampling_rate = 0; config.handle_fragments = TRUE; list->cfg.nfprobe_what_to_count = list->cfg.what_to_count; list->cfg.what_to_count = 0; #if defined (HAVE_L2) if (list->cfg.nfprobe_version == 9 || list->cfg.nfprobe_version == 10) { list->cfg.what_to_count |= COUNT_SRC_MAC; list->cfg.what_to_count |= COUNT_DST_MAC; list->cfg.what_to_count |= COUNT_VLAN; } #endif list->cfg.what_to_count |= COUNT_SRC_HOST; list->cfg.what_to_count |= COUNT_DST_HOST; if (list->cfg.networks_file || list->cfg.networks_mask || list->cfg.nfacctd_net) { list->cfg.what_to_count |= COUNT_SRC_NMASK; list->cfg.what_to_count |= COUNT_DST_NMASK; } list->cfg.what_to_count |= COUNT_SRC_PORT; list->cfg.what_to_count |= COUNT_DST_PORT; list->cfg.what_to_count |= COUNT_IP_TOS; list->cfg.what_to_count |= COUNT_IP_PROTO; if (list->cfg.networks_file || (list->cfg.nfacctd_bgp && list->cfg.nfacctd_as == NF_AS_BGP)) { list->cfg.what_to_count |= COUNT_SRC_AS; list->cfg.what_to_count |= COUNT_DST_AS; list->cfg.what_to_count |= COUNT_PEER_DST_IP; } if ((list->cfg.nfprobe_version == 9 || list->cfg.nfprobe_version == 10) && list->cfg.classifiers_path) { list->cfg.what_to_count |= COUNT_CLASS; config.handle_flows = TRUE; } if (list->cfg.pre_tag_map) { list->cfg.what_to_count |= COUNT_ID; list->cfg.what_to_count |= COUNT_ID2; } list->cfg.what_to_count |= COUNT_IN_IFACE; list->cfg.what_to_count |= COUNT_OUT_IFACE; if (list->cfg.what_to_count & (COUNT_STD_COMM|COUNT_EXT_COMM|COUNT_LOCAL_PREF|COUNT_MED|COUNT_AS_PATH| COUNT_PEER_SRC_AS|COUNT_PEER_DST_AS|COUNT_PEER_SRC_IP|COUNT_SRC_STD_COMM| COUNT_SRC_EXT_COMM|COUNT_SRC_AS_PATH|COUNT_SRC_MED|COUNT_SRC_LOCAL_PREF| COUNT_MPLS_VPN_RD)) { Log(LOG_ERR, "ERROR ( default/core ): 'src_as', 'dst_as' and 'peer_dst_ip' are currently the only BGP-related primitives supported within the 'nfprobe' plugin.\n"); exit(1); } list->cfg.what_to_count |= COUNT_COUNTERS; list->cfg.data_type = PIPE_TYPE_METADATA; list->cfg.data_type |= PIPE_TYPE_EXTRAS; } else if (list->type.id == PLUGIN_ID_SFPROBE) { /* If we already renormalizing an external sampling rate, we cancel the sampling information from the probe plugin */ if (config.sfacctd_renormalize && list->cfg.ext_sampling_rate) list->cfg.ext_sampling_rate = 0; if (psize < 128) psize = config.snaplen = 128; /* SFL_DEFAULT_HEADER_SIZE */ list->cfg.what_to_count = COUNT_PAYLOAD; if (list->cfg.classifiers_path) { list->cfg.what_to_count |= COUNT_CLASS; config.handle_fragments = TRUE; config.handle_flows = TRUE; } if (list->cfg.nfacctd_bgp && list->cfg.nfacctd_as == NF_AS_BGP) { list->cfg.what_to_count |= COUNT_SRC_AS; list->cfg.what_to_count |= COUNT_DST_AS; list->cfg.what_to_count |= COUNT_PEER_DST_IP; } if (list->cfg.nfacctd_bgp && list->cfg.nfacctd_net == NF_NET_BGP) { list->cfg.what_to_count |= COUNT_SRC_NMASK; list->cfg.what_to_count |= COUNT_DST_NMASK; } if (list->cfg.pre_tag_map) { list->cfg.what_to_count |= COUNT_ID; list->cfg.what_to_count |= COUNT_ID2; } if (list->cfg.what_to_count & (COUNT_STD_COMM|COUNT_EXT_COMM|COUNT_LOCAL_PREF|COUNT_MED|COUNT_AS_PATH| COUNT_PEER_SRC_AS|COUNT_PEER_DST_AS|COUNT_PEER_SRC_IP|COUNT_SRC_STD_COMM| COUNT_SRC_EXT_COMM|COUNT_SRC_AS_PATH|COUNT_SRC_MED|COUNT_SRC_LOCAL_PREF| COUNT_MPLS_VPN_RD)) { Log(LOG_ERR, "ERROR ( default/core ): 'src_as', 'dst_as' and 'peer_dst_ip' are currently the only BGP-related primitives supported within the 'sfprobe' plugin.\n"); exit(1); } #if defined (HAVE_L2) list->cfg.what_to_count |= COUNT_VLAN; list->cfg.what_to_count |= COUNT_COS; #endif list->cfg.data_type = PIPE_TYPE_PAYLOAD; } else { evaluate_sums(&list->cfg.what_to_count, list->name, list->type.string); if (list->cfg.what_to_count & (COUNT_SRC_PORT|COUNT_DST_PORT|COUNT_SUM_PORT|COUNT_TCPFLAGS)) config.handle_fragments = TRUE; if (list->cfg.what_to_count & COUNT_FLOWS) { config.handle_fragments = TRUE; config.handle_flows = TRUE; } if (list->cfg.what_to_count & COUNT_CLASS) { config.handle_fragments = TRUE; config.handle_flows = TRUE; } if (!list->cfg.what_to_count) { Log(LOG_WARNING, "WARN ( %s/%s ): defaulting to SRC HOST aggregation.\n", list->name, list->type.string); list->cfg.what_to_count |= COUNT_SRC_HOST; } if ((list->cfg.what_to_count & (COUNT_SRC_AS|COUNT_DST_AS|COUNT_SUM_AS)) && !list->cfg.networks_file && list->cfg.nfacctd_as != NF_AS_BGP) { Log(LOG_ERR, "ERROR ( %s/%s ): AS aggregation selected but NO 'networks_file' or 'pmacctd_as' are specified. Exiting...\n\n", list->name, list->type.string); exit(1); } if (list->cfg.what_to_count & (COUNT_SRC_NET|COUNT_DST_NET|COUNT_SUM_NET|COUNT_SRC_NMASK|COUNT_DST_NMASK)) { if (!list->cfg.nfacctd_net) { if (list->cfg.networks_file) list->cfg.nfacctd_net |= NF_NET_NEW; if (list->cfg.networks_mask) list->cfg.nfacctd_net |= NF_NET_STATIC; if (!list->cfg.nfacctd_net) { Log(LOG_ERR, "ERROR ( %s/%s ): network aggregation selected but none of 'pmacctd_net', 'networks_file', 'networks_mask' is specified. Exiting ...\n\n", list->name, list->type.string); exit(1); } } else { if ((list->cfg.nfacctd_net == NF_NET_NEW && !list->cfg.networks_file) || (list->cfg.nfacctd_net == NF_NET_STATIC && !list->cfg.networks_mask) || (list->cfg.nfacctd_net == NF_NET_BGP && !list->cfg.nfacctd_bgp) || (list->cfg.nfacctd_net == NF_NET_KEEP)) { Log(LOG_ERR, "ERROR ( %s/%s ): network aggregation selected but none of 'bgp_daemon', 'networks_file', 'networks_mask' is specified. Exiting ...\n\n", list->name, list->type.string); exit(1); } } } if (list->cfg.what_to_count & COUNT_CLASS && !list->cfg.classifiers_path) { Log(LOG_ERR, "ERROR ( %s/%s ): 'class' aggregation selected but NO 'classifiers' key specified. Exiting...\n\n", list->name, list->type.string); exit(1); } bgp_config_checks(&list->cfg); list->cfg.what_to_count |= COUNT_COUNTERS; list->cfg.data_type |= PIPE_TYPE_METADATA; } } list = list->next; } /* plugins glue: creation (since 094) */ if (config.classifiers_path) { init_classifiers(config.classifiers_path); init_conntrack_table(); } load_plugins(&req); if (config.handle_fragments) init_ip_fragment_handler(); if (config.handle_flows) init_ip_flow_handler(); load_networks(config.networks_file, &nt, &nc); /* If any device/savefile have been specified, choose a suitable device where to listen for traffic */ if (!config.dev && !config.pcap_savefile) { Log(LOG_WARNING, "WARN ( default/core ): Selecting a suitable device.\n"); config.dev = pcap_lookupdev(errbuf); if (!config.dev) { Log(LOG_WARNING, "WARN ( default/core ): Unable to find a suitable device. Exiting.\n"); exit_all(1); } else Log(LOG_DEBUG, "DEBUG ( default/core ): device is %s\n", config.dev); } /* reading filter; if it exists, we'll take an action later */ if (!strlen(config_file)) config.clbuf = copy_argv(&argv[optind]); if (config.dev && config.pcap_savefile) { Log(LOG_ERR, "ERROR ( default/core ): 'interface' (-i) and 'pcap_savefile' (-I) directives are mutually exclusive. Exiting.\n"); exit_all(1); } throttle_startup: if (config.dev) { if ((device.dev_desc = pcap_open_live(config.dev, psize, config.promisc, 1000, errbuf)) == NULL) { if (!config.if_wait) { Log(LOG_ERR, "ERROR ( default/core ): pcap_open_live(): %s\n", errbuf); exit_all(1); } else { sleep(5); /* XXX: user defined ? */ goto throttle_startup; } } } else if (config.pcap_savefile) { if ((device.dev_desc = pcap_open_offline(config.pcap_savefile, errbuf)) == NULL) { Log(LOG_ERR, "ERROR ( default/core ): pcap_open_offline(): %s\n", errbuf); exit_all(1); } } device.active = TRUE; glob_pcapt = device.dev_desc; /* SIGINT/stats handling */ if (config.pipe_size) { int slen = sizeof(config.pipe_size), x; #if defined (PCAP_TYPE_linux) || (PCAP_TYPE_snoop) Setsocksize(pcap_fileno(device.dev_desc), SOL_SOCKET, SO_RCVBUF, &config.pipe_size, slen); getsockopt(pcap_fileno(device.dev_desc), SOL_SOCKET, SO_RCVBUF, &x, &slen); Log(LOG_DEBUG, "DEBUG ( default/core ): PCAP buffer: obtained %d / %d bytes.\n", x, config.pipe_size); #endif } device.link_type = pcap_datalink(device.dev_desc); for (index = 0; _devices[index].link_type != -1; index++) { if (device.link_type == _devices[index].link_type) device.data = &_devices[index]; } load_plugin_filters(device.link_type); /* we need to solve some link constraints */ if (device.data == NULL) { Log(LOG_ERR, "ERROR ( default/core ): data link not supported: %d\n", device.link_type); exit_all(1); } else Log(LOG_INFO, "OK ( default/core ): link type is: %d\n", device.link_type); if (device.link_type != DLT_EN10MB && device.link_type != DLT_IEEE802 && device.link_type != DLT_LINUX_SLL) { list = plugins_list; while (list) { if ((list->cfg.what_to_count & COUNT_SRC_MAC) || (list->cfg.what_to_count & COUNT_DST_MAC)) { Log(LOG_ERR, "ERROR ( default/core ): MAC aggregation not available for link type: %d\n", device.link_type); exit_all(1); } list = list->next; } } cb_data.device = &device; /* doing pcap stuff */ if (!config.dev || pcap_lookupnet(config.dev, &localnet, &netmask, errbuf) < 0) { localnet = 0; netmask = 0; Log(LOG_WARNING, "WARN ( default/core ): %s\n", errbuf); } if (pcap_compile(device.dev_desc, &filter, config.clbuf, 0, netmask) < 0) Log(LOG_WARNING, "WARN: %s\nWARN ( default/core ): going on without a filter\n", pcap_geterr(device.dev_desc)); else { if (pcap_setfilter(device.dev_desc, &filter) < 0) Log(LOG_WARNING, "WARN: %s\nWARN ( default/core ): going on without a filter\n", pcap_geterr(device.dev_desc)); } /* signal handling we want to inherit to plugins (when not re-defined elsewhere) */ signal(SIGCHLD, startup_handle_falling_child); /* takes note of plugins failed during startup phase */ signal(SIGHUP, reload); /* handles reopening of syslog channel */ signal(SIGUSR1, push_stats); /* logs various statistics via Log() calls */ signal(SIGUSR2, reload_maps); /* sets to true the reload_maps flag */ signal(SIGPIPE, SIG_IGN); /* we want to exit gracefully when a pipe is broken */ /* loading pre-tagging map, if any */ if (config.pre_tag_map) { load_id_file(config.acct_type, config.pre_tag_map, &idt, &req, &tag_map_allocated); cb_data.idt = (u_char *) &idt; } else { memset(&idt, 0, sizeof(idt)); cb_data.idt = NULL; } #if defined ENABLE_THREADS /* starting the BGP thread */ if (config.nfacctd_bgp) { req.bpf_filter = TRUE; load_comm_patterns(&config.nfacctd_bgp_stdcomm_pattern, &config.nfacctd_bgp_extcomm_pattern, &config.nfacctd_bgp_stdcomm_pattern_to_asn); if (config.nfacctd_bgp_peer_as_src_type == BGP_SRC_PRIMITIVES_MAP) { if (config.nfacctd_bgp_peer_as_src_map) { load_id_file(MAP_BGP_PEER_AS_SRC, config.nfacctd_bgp_peer_as_src_map, &bpas_table, &req, &bpas_map_allocated); cb_data.bpas_table = (u_char *) &bpas_table; } else { Log(LOG_ERR, "ERROR: bgp_peer_as_src_type set to 'map' but no map defined. Exiting.\n"); exit(1); } } else cb_data.bpas_table = NULL; if (config.nfacctd_bgp_src_local_pref_type == BGP_SRC_PRIMITIVES_MAP) { if (config.nfacctd_bgp_src_local_pref_map) { load_id_file(MAP_BGP_SRC_LOCAL_PREF, config.nfacctd_bgp_src_local_pref_map, &blp_table, &req, &blp_map_allocated); cb_data.blp_table = (u_char *) &blp_table; } else { Log(LOG_ERR, "ERROR: bgp_src_local_pref_type set to 'map' but no map defined. Exiting.\n"); exit(1); } } else cb_data.blp_table = NULL; if (config.nfacctd_bgp_src_med_type == BGP_SRC_PRIMITIVES_MAP) { if (config.nfacctd_bgp_src_med_map) { load_id_file(MAP_BGP_SRC_MED, config.nfacctd_bgp_src_med_map, &bmed_table, &req, &bmed_map_allocated); cb_data.bmed_table = (u_char *) &bmed_table; } else { Log(LOG_ERR, "ERROR: bgp_src_med_type set to 'map' but no map defined. Exiting.\n"); exit(1); } } else cb_data.bmed_table = NULL; if (config.nfacctd_bgp_to_agent_map) { load_id_file(MAP_BGP_TO_XFLOW_AGENT, config.nfacctd_bgp_to_agent_map, &bta_table, &req, &bta_map_allocated); cb_data.bta_table = (u_char *) &bta_table; } else { Log(LOG_ERR, "ERROR ( default/core ): 'bgp_daemon' configured but no 'bgp_agent_map' has been specified. Exiting.\n"); exit(1); } /* Limiting BGP peers to only two: one would suffice in pmacctd but in case maps are reloadable (ie. bta), it could be handy to keep a backup feed in memory */ config.nfacctd_bgp_max_peers = 2; if (config.nfacctd_bgp_iface_to_rd_map) { Log(LOG_ERR, "ERROR ( default/core ): 'bgp_iface_to_rd_map' is not supported by this daemon. Exiting.\n"); exit(1); } cb_data.f_agent = (char *)&client; nfacctd_bgp_wrapper(); /* Let's give the BGP thread some advantage to create its structures */ sleep(5); } #else if (config.nfacctd_bgp) { Log(LOG_ERR, "ERROR ( default/core ): 'bgp_daemon' is available only with threads (--enable-threads). Exiting.\n"); exit(1); } #endif /* Init tunnel handlers */ tunnel_registry_init(); /* plugins glue: creation (until 093) */ evaluate_packet_handlers(); pm_setproctitle("%s [%s]", "Core Process", "default"); if (config.pidfile) write_pid_file(config.pidfile); /* signals to be handled only by pmacctd; we set proper handlers after plugin creation */ signal(SIGINT, my_sigint_handler); signal(SIGTERM, my_sigint_handler); signal(SIGCHLD, handle_falling_child); kill(getpid(), SIGCHLD); /* When reading packets from a savefile, things are lightning fast; we will sit here just few seconds, thus allowing plugins to complete their startup operations */ if (config.pcap_savefile) { Log(LOG_INFO, "INFO ( default/core ): PCAP capture file, sleeping for 2 seconds\n"); sleep(2); } /* Main loop: if pcap_loop() exits maybe an error occurred; we will try closing and reopening again our listening device */ for(;;) { if (!device.active) { Log(LOG_WARNING, "WARN ( default/core ): %s has become unavailable; throttling ...\n", config.dev); throttle_loop: sleep(5); /* XXX: user defined ? */ if ((device.dev_desc = pcap_open_live(config.dev, psize, config.promisc, 1000, errbuf)) == NULL) goto throttle_loop; pcap_setfilter(device.dev_desc, &filter); device.active = TRUE; } pcap_loop(device.dev_desc, -1, pcap_cb, (u_char *) &cb_data); pcap_close(device.dev_desc); if (config.pcap_savefile) { if (config.sf_wait) { fill_pipe_buffer(); Log(LOG_INFO, "INFO ( default/core ): finished reading PCAP capture file\n"); wait(NULL); } stop_all_childs(); } device.active = FALSE; } }
/* Functions */ void sqlite3_plugin(int pipe_fd, struct configuration *cfgptr, void *ptr) { struct pkt_data *data; struct ports_table pt; struct pollfd pfd; struct insert_data idata; struct timezone tz; time_t refresh_deadline; int timeout; int ret, num; struct ring *rg = &((struct channels_list_entry *)ptr)->rg; struct ch_status *status = ((struct channels_list_entry *)ptr)->status; u_int32_t bufsz = ((struct channels_list_entry *)ptr)->bufsize; struct pkt_bgp_primitives *pbgp; char *dataptr; unsigned char *rgptr; int pollagain = TRUE; u_int32_t seq = 1, rg_err_count = 0; memcpy(&config, cfgptr, sizeof(struct configuration)); recollect_pipe_memory(ptr); pm_setproctitle("%s [%s]", "SQLite3 Plugin", config.name); memset(&idata, 0, sizeof(idata)); if (config.pidfile) write_pid_file_plugin(config.pidfile, config.type, config.name); if (config.logfile) { fclose(config.logfile_fd); config.logfile_fd = open_logfile(config.logfile); } sql_set_signals(); sql_init_default_values(); SQLI_init_default_values(&idata); SQLI_set_callbacks(&sqlfunc_cbr); sql_set_insert_func(); /* some LOCAL initialization AFTER setting some default values */ reload_map = FALSE; idata.now = time(NULL); refresh_deadline = idata.now; sql_init_maps(&nt, &nc, &pt); sql_init_global_buffers(); sql_init_pipe(&pfd, pipe_fd); sql_init_historical_acct(idata.now, &idata); sql_init_triggers(idata.now, &idata); sql_init_refresh_deadline(&refresh_deadline); /* setting number of entries in _protocols structure */ while (_protocols[protocols_number].number != -1) protocols_number++; /* building up static SQL clauses */ idata.num_primitives = SQLI_compose_static_queries(); glob_num_primitives = idata.num_primitives; /* handling purge preprocessor */ set_preprocess_funcs(config.sql_preprocess, &prep); /* setting up environment variables */ SQL_SetENV(); sql_link_backend_descriptors(&bed, &p, &b); /* plugin main loop */ for(;;) { poll_again: status->wakeup = TRUE; sql_calc_refresh_timeout(refresh_deadline, idata.now, &timeout); ret = poll(&pfd, 1, timeout); if (ret < 0) goto poll_again; idata.now = time(NULL); if (config.sql_history) { while (idata.now > (idata.basetime + idata.timeslot)) { time_t saved_basetime = idata.basetime; idata.basetime += idata.timeslot; if (config.sql_history == COUNT_MONTHLY) idata.timeslot = calc_monthly_timeslot(idata.basetime, config.sql_history_howmany, ADD); glob_basetime = idata.basetime; idata.new_basetime = saved_basetime; glob_new_basetime = saved_basetime; } } switch (ret) { case 0: /* timeout */ if (qq_ptr) sql_cache_flush(queries_queue, qq_ptr, &idata, FALSE); switch (fork()) { case 0: /* Child */ /* we have to ignore signals to avoid loops: because we are already forked */ signal(SIGINT, SIG_IGN); signal(SIGHUP, SIG_IGN); pm_setproctitle("%s [%s]", "SQLite3 Plugin -- DB Writer", config.name); if (qq_ptr && sql_writers.flags != CHLD_ALERT) { if (sql_writers.flags == CHLD_WARNING) sql_db_fail(&p); (*sqlfunc_cbr.connect)(&p, NULL); (*sqlfunc_cbr.purge)(queries_queue, qq_ptr, &idata); (*sqlfunc_cbr.close)(&bed); } if (config.sql_trigger_exec) { if (idata.now > idata.triggertime) sql_trigger_exec(config.sql_trigger_exec); } exit(0); default: /* Parent */ if (pqq_ptr) sql_cache_flush_pending(pending_queries_queue, pqq_ptr, &idata); gettimeofday(&idata.flushtime, &tz); while (idata.now > refresh_deadline) refresh_deadline += config.sql_refresh_time; while (idata.now > idata.triggertime && idata.t_timeslot > 0) { idata.triggertime += idata.t_timeslot; if (config.sql_trigger_time == COUNT_MONTHLY) idata.t_timeslot = calc_monthly_timeslot(idata.triggertime, config.sql_trigger_time_howmany, ADD); } idata.new_basetime = FALSE; glob_new_basetime = FALSE; qq_ptr = pqq_ptr; memcpy(queries_queue, pending_queries_queue, qq_ptr*sizeof(struct db_cache *)); if (reload_map) { load_networks(config.networks_file, &nt, &nc); load_ports(config.ports_file, &pt); reload_map = FALSE; } break; } break; default: /* we received data */ read_data: if (!pollagain) { seq++; seq %= MAX_SEQNUM; if (seq == 0) rg_err_count = FALSE; idata.now = time(NULL); } else { if ((ret = read(pipe_fd, &rgptr, sizeof(rgptr))) == 0) exit_plugin(1); /* we exit silently; something happened at the write end */ } if (((struct ch_buf_hdr *)rg->ptr)->seq != seq) { if (!pollagain) { pollagain = TRUE; goto poll_again; } else { rg_err_count++; if (config.debug || (rg_err_count > MAX_RG_COUNT_ERR)) { Log(LOG_ERR, "ERROR ( %s/%s ): We are missing data.\n", config.name, config.type); Log(LOG_ERR, "If you see this message once in a while, discard it. Otherwise some solutions follow:\n"); Log(LOG_ERR, "- increase shared memory size, 'plugin_pipe_size'; now: '%u'.\n", config.pipe_size); Log(LOG_ERR, "- increase buffer size, 'plugin_buffer_size'; now: '%u'.\n", config.buffer_size); Log(LOG_ERR, "- increase system maximum socket size.\n\n"); } seq = ((struct ch_buf_hdr *)rg->ptr)->seq; } } pollagain = FALSE; memcpy(pipebuf, rg->ptr, bufsz); if ((rg->ptr+bufsz) >= rg->end) rg->ptr = rg->base; else rg->ptr += bufsz; /* lazy sql refresh handling */ if (idata.now > refresh_deadline) { if (qq_ptr) sql_cache_flush(queries_queue, qq_ptr, &idata, FALSE); switch (fork()) { case 0: /* Child */ /* we have to ignore signals to avoid loops: because we are already forked */ signal(SIGINT, SIG_IGN); signal(SIGHUP, SIG_IGN); pm_setproctitle("%s [%s]", "SQLite3 Plugin -- DB Writer", config.name); if (qq_ptr && sql_writers.flags != CHLD_ALERT) { if (sql_writers.flags == CHLD_WARNING) sql_db_fail(&p); (*sqlfunc_cbr.connect)(&p, NULL); (*sqlfunc_cbr.purge)(queries_queue, qq_ptr, &idata); (*sqlfunc_cbr.close)(&bed); } if (config.sql_trigger_exec) { if (idata.now > idata.triggertime) sql_trigger_exec(config.sql_trigger_exec); } exit(0); default: /* Parent */ if (pqq_ptr) sql_cache_flush_pending(pending_queries_queue, pqq_ptr, &idata); gettimeofday(&idata.flushtime, &tz); while (idata.now > refresh_deadline) refresh_deadline += config.sql_refresh_time; while (idata.now > idata.triggertime && idata.t_timeslot > 0) { idata.triggertime += idata.t_timeslot; if (config.sql_trigger_time == COUNT_MONTHLY) idata.t_timeslot = calc_monthly_timeslot(idata.triggertime, config.sql_trigger_time_howmany, ADD); } idata.new_basetime = FALSE; glob_new_basetime = FALSE; qq_ptr = pqq_ptr; memcpy(queries_queue, pending_queries_queue, qq_ptr*sizeof(struct db_cache *)); if (reload_map) { load_networks(config.networks_file, &nt, &nc); load_ports(config.ports_file, &pt); reload_map = FALSE; } break; } } else { if (config.sql_trigger_exec) { while (idata.now > idata.triggertime && idata.t_timeslot > 0) { sql_trigger_exec(config.sql_trigger_exec); idata.triggertime += idata.t_timeslot; if (config.sql_trigger_time == COUNT_MONTHLY) idata.t_timeslot = calc_monthly_timeslot(idata.triggertime, config.sql_trigger_time_howmany, ADD); } } } data = (struct pkt_data *) (pipebuf+sizeof(struct ch_buf_hdr)); while (((struct ch_buf_hdr *)pipebuf)->num) { for (num = 0; net_funcs[num]; num++) (*net_funcs[num])(&nt, &nc, &data->primitives); if (config.ports_file) { if (!pt.table[data->primitives.src_port]) data->primitives.src_port = 0; if (!pt.table[data->primitives.dst_port]) data->primitives.dst_port = 0; } if (PbgpSz) pbgp = (struct pkt_bgp_primitives *) ((u_char *)data+PdataSz); else pbgp = NULL; (*insert_func)(data, pbgp, &idata); ((struct ch_buf_hdr *)pipebuf)->num--; if (((struct ch_buf_hdr *)pipebuf)->num) { dataptr = (unsigned char *) data; dataptr += PdataSz + PbgpSz; data = (struct pkt_data *) dataptr; } } goto read_data; } } }
int main(int argc,char **argv, char **envp) { struct pcap_device device; int index, logf, ret; struct plugins_list_entry *list; struct plugin_requests req; char config_file[SRVBUFLEN]; struct id_table bpas_table; struct id_table blp_table; struct id_table bmed_table; struct id_table biss_table; struct id_table bta_table; struct pcap_callback_data cb_data; /* getopt() stuff */ extern char *optarg; extern int optind, opterr, optopt; int errflag, cp; /* NFLOG stuff */ struct nflog_handle *nfh = NULL; struct nflog_g_handle *nfgh = NULL; int one = 1; ssize_t len = 0; unsigned char *nflog_buffer; #if defined ENABLE_IPV6 struct sockaddr_storage client; #else struct sockaddr client; #endif #if defined HAVE_MALLOPT mallopt(M_CHECK_ACTION, 0); #endif umask(077); compute_once(); /* a bunch of default definitions */ reload_map = FALSE; reload_geoipv2_file = FALSE; bpas_map_allocated = FALSE; blp_map_allocated = FALSE; bmed_map_allocated = FALSE; biss_map_allocated = FALSE; bta_map_caching = FALSE; sampling_map_caching = FALSE; custom_primitives_allocated = FALSE; find_id_func = PM_find_id; plugins_list = NULL; errflag = 0; memset(cfg_cmdline, 0, sizeof(cfg_cmdline)); memset(&config, 0, sizeof(struct configuration)); memset(&device, 0, sizeof(struct pcap_device)); memset(&config_file, 0, sizeof(config_file)); memset(&failed_plugins, 0, sizeof(failed_plugins)); memset(&req, 0, sizeof(req)); memset(dummy_tlhdr, 0, sizeof(dummy_tlhdr)); memset(sll_mac, 0, sizeof(sll_mac)); memset(&bpas_table, 0, sizeof(bpas_table)); memset(&blp_table, 0, sizeof(blp_table)); memset(&bmed_table, 0, sizeof(bmed_table)); memset(&biss_table, 0, sizeof(biss_table)); memset(&bta_table, 0, sizeof(bta_table)); memset(&client, 0, sizeof(client)); memset(&cb_data, 0, sizeof(cb_data)); memset(&tunnel_registry, 0, sizeof(tunnel_registry)); memset(&reload_map_tstamp, 0, sizeof(reload_map_tstamp)); log_notifications_init(&log_notifications); config.acct_type = ACCT_PM; rows = 0; glob_pcapt = NULL; /* getting commandline values */ while (!errflag && ((cp = getopt(argc, argv, ARGS_UACCTD)) != -1)) { cfg_cmdline[rows] = malloc(SRVBUFLEN); switch (cp) { case 'P': strlcpy(cfg_cmdline[rows], "plugins: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'D': strlcpy(cfg_cmdline[rows], "daemonize: true", SRVBUFLEN); rows++; break; case 'd': debug = TRUE; strlcpy(cfg_cmdline[rows], "debug: true", SRVBUFLEN); rows++; break; case 'n': strlcpy(cfg_cmdline[rows], "networks_file: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'o': strlcpy(cfg_cmdline[rows], "ports_file: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'O': strlcpy(cfg_cmdline[rows], "print_output: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'u': strlcpy(cfg_cmdline[rows], "print_num_protos: true", SRVBUFLEN); rows++; break; case 'f': strlcpy(config_file, optarg, sizeof(config_file)); break; case 'F': strlcpy(cfg_cmdline[rows], "pidfile: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'c': strlcpy(cfg_cmdline[rows], "aggregate: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'b': strlcpy(cfg_cmdline[rows], "imt_buckets: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'm': strlcpy(cfg_cmdline[rows], "imt_mem_pools_number: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'p': strlcpy(cfg_cmdline[rows], "imt_path: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'r': strlcpy(cfg_cmdline[rows], "sql_refresh_time: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'v': strlcpy(cfg_cmdline[rows], "sql_table_version: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 's': strlcpy(cfg_cmdline[rows], "imt_mem_pools_size: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'S': strlcpy(cfg_cmdline[rows], "syslog: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'g': strlcpy(cfg_cmdline[rows], "uacctd_group: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'L': strlcpy(cfg_cmdline[rows], "snaplen: ", SRVBUFLEN); strncat(cfg_cmdline[rows], optarg, CFG_LINE_LEN(cfg_cmdline[rows])); rows++; break; case 'R': strlcpy(cfg_cmdline[rows], "sfacctd_renormalize: true", SRVBUFLEN); rows++; break; case 'h': usage_daemon(argv[0]); exit(0); break; case 'V': version_daemon(UACCTD_USAGE_HEADER); exit(0); break; case 'a': print_primitives(config.acct_type, UACCTD_USAGE_HEADER); exit(0); break; default: usage_daemon(argv[0]); exit(1); break; } } /* post-checks and resolving conflicts */ if (strlen(config_file)) { if (parse_configuration_file(config_file) != SUCCESS) exit(1); } else { if (parse_configuration_file(NULL) != SUCCESS) exit(1); } /* XXX: glue; i'm conscious it's a dirty solution from an engineering viewpoint; someday later i'll fix this */ list = plugins_list; while (list) { list->cfg.acct_type = ACCT_PM; set_default_preferences(&list->cfg); if (!strcmp(list->type.string, "core")) { memcpy(&config, &list->cfg, sizeof(struct configuration)); config.name = list->name; config.type = list->type.string; } list = list->next; } if (config.files_umask) umask(config.files_umask); if (!config.snaplen) config.snaplen = DEFAULT_SNAPLEN; if (!config.uacctd_nl_size) config.uacctd_nl_size = DEFAULT_NFLOG_BUFLEN; if (!config.uacctd_threshold) config.uacctd_threshold = DEFAULT_NFLOG_THRESHOLD; /* Let's check whether we need superuser privileges */ if (getuid() != 0) { printf("%s (%s)\n\n", UACCTD_USAGE_HEADER, PMACCT_BUILD); printf("ERROR ( %s/core ): You need superuser privileges to run this command.\nExiting ...\n\n", config.name); exit(1); } if (!config.uacctd_group) { config.uacctd_group = DEFAULT_NFLOG_GROUP; list = plugins_list; while (list) { list->cfg.uacctd_group = DEFAULT_NFLOG_GROUP; list = list->next; } } if (config.daemon) { list = plugins_list; while (list) { if (!strcmp(list->type.string, "print") && !list->cfg.print_output_file) printf("INFO ( %s/%s ): Daemonizing. Bye bye screen.\n", list->name, list->type.string); list = list->next; } if (debug || config.debug) printf("WARN ( %s/core ): debug is enabled; forking in background. Logging to standard error (stderr) will get lost.\n", config.name); daemonize(); } initsetproctitle(argc, argv, envp); if (config.syslog) { logf = parse_log_facility(config.syslog); if (logf == ERR) { config.syslog = NULL; printf("WARN ( %s/core ): specified syslog facility is not supported. Logging to standard error (stderr).\n", config.name); } else openlog(NULL, LOG_PID, logf); Log(LOG_INFO, "INFO ( %s/core ): Start logging ...\n", config.name); } if (config.logfile) { config.logfile_fd = open_output_file(config.logfile, "a", FALSE); list = plugins_list; while (list) { list->cfg.logfile_fd = config.logfile_fd ; list = list->next; } } if (config.proc_priority) { int ret; ret = setpriority(PRIO_PROCESS, 0, config.proc_priority); if (ret) Log(LOG_WARNING, "WARN ( %s/core ): proc_priority failed (errno: %d)\n", config.name, errno); else Log(LOG_INFO, "INFO ( %s/core ): proc_priority set to %d\n", config.name, getpriority(PRIO_PROCESS, 0)); } if (strlen(config_file)) { char canonical_path[PATH_MAX], *canonical_path_ptr; canonical_path_ptr = realpath(config_file, canonical_path); if (canonical_path_ptr) Log(LOG_INFO, "INFO ( %s/core ): Reading configuration file '%s'.\n", config.name, canonical_path); } else Log(LOG_INFO, "INFO ( %s/core ): Reading configuration from cmdline.\n", config.name); /* Enforcing policies over aggregation methods */ list = plugins_list; while (list) { if (list->type.id != PLUGIN_ID_CORE) { /* applies to all plugins */ plugin_pipe_check(&list->cfg); if (config.classifiers_path && (list->cfg.sampling_rate || config.ext_sampling_rate)) { Log(LOG_ERR, "ERROR ( %s/core ): Packet sampling and classification are mutual exclusive.\n", config.name); exit(1); } if (list->cfg.sampling_rate && config.ext_sampling_rate) { Log(LOG_ERR, "ERROR ( %s/core ): Internal packet sampling and external packet sampling are mutual exclusive.\n", config.name); exit(1); } /* applies to specific plugins */ if (list->type.id == PLUGIN_ID_TEE) { Log(LOG_ERR, "ERROR ( %s/core ): 'tee' plugin not supported in 'uacctd'.\n", config.name); exit(1); } else if (list->type.id == PLUGIN_ID_NFPROBE) { /* If we already renormalizing an external sampling rate, we cancel the sampling information from the probe plugin */ if (config.sfacctd_renormalize && list->cfg.ext_sampling_rate) list->cfg.ext_sampling_rate = 0; config.handle_fragments = TRUE; list->cfg.nfprobe_what_to_count = list->cfg.what_to_count; list->cfg.nfprobe_what_to_count_2 = list->cfg.what_to_count_2; list->cfg.what_to_count = 0; list->cfg.what_to_count_2 = 0; #if defined (HAVE_L2) if (list->cfg.nfprobe_version == 9 || list->cfg.nfprobe_version == 10) { list->cfg.what_to_count |= COUNT_SRC_MAC; list->cfg.what_to_count |= COUNT_DST_MAC; list->cfg.what_to_count |= COUNT_VLAN; } #endif list->cfg.what_to_count |= COUNT_SRC_HOST; list->cfg.what_to_count |= COUNT_DST_HOST; if (list->cfg.networks_file || list->cfg.networks_mask || list->cfg.nfacctd_net) { list->cfg.what_to_count |= COUNT_SRC_NMASK; list->cfg.what_to_count |= COUNT_DST_NMASK; } list->cfg.what_to_count |= COUNT_SRC_PORT; list->cfg.what_to_count |= COUNT_DST_PORT; list->cfg.what_to_count |= COUNT_IP_TOS; list->cfg.what_to_count |= COUNT_IP_PROTO; if (list->cfg.networks_file || (list->cfg.nfacctd_bgp && list->cfg.nfacctd_as == NF_AS_BGP)) { list->cfg.what_to_count |= COUNT_SRC_AS; list->cfg.what_to_count |= COUNT_DST_AS; list->cfg.what_to_count |= COUNT_PEER_DST_IP; } if ((list->cfg.nfprobe_version == 9 || list->cfg.nfprobe_version == 10) && list->cfg.classifiers_path) { list->cfg.what_to_count |= COUNT_CLASS; config.handle_flows = TRUE; } if (list->cfg.pre_tag_map) { list->cfg.what_to_count |= COUNT_TAG; list->cfg.what_to_count |= COUNT_TAG2; list->cfg.what_to_count_2 |= COUNT_LABEL; } list->cfg.what_to_count |= COUNT_IN_IFACE; list->cfg.what_to_count |= COUNT_OUT_IFACE; if (list->cfg.what_to_count & (COUNT_STD_COMM|COUNT_EXT_COMM|COUNT_LOCAL_PREF|COUNT_MED|COUNT_AS_PATH| COUNT_PEER_SRC_AS|COUNT_PEER_DST_AS|COUNT_PEER_SRC_IP|COUNT_SRC_STD_COMM| COUNT_SRC_EXT_COMM|COUNT_SRC_AS_PATH|COUNT_SRC_MED|COUNT_SRC_LOCAL_PREF| COUNT_MPLS_VPN_RD)) { Log(LOG_ERR, "ERROR ( %s/core ): 'src_as', 'dst_as' and 'peer_dst_ip' are currently the only BGP-related primitives supported within the 'nfprobe' plugin.\n", config.name); exit(1); } list->cfg.what_to_count |= COUNT_COUNTERS; if (list->cfg.nfacctd_as & NF_AS_FALLBACK && list->cfg.networks_file) list->cfg.nfacctd_as |= NF_AS_NEW; if (list->cfg.nfacctd_net & NF_NET_FALLBACK && list->cfg.networks_file) list->cfg.nfacctd_net |= NF_NET_NEW; list->cfg.data_type = PIPE_TYPE_METADATA; list->cfg.data_type |= PIPE_TYPE_EXTRAS; if (list->cfg.what_to_count_2 & (COUNT_LABEL)) list->cfg.data_type |= PIPE_TYPE_VLEN; } else if (list->type.id == PLUGIN_ID_SFPROBE) { /* If we already renormalizing an external sampling rate, we cancel the sampling information from the probe plugin */ if (config.sfacctd_renormalize && list->cfg.ext_sampling_rate) list->cfg.ext_sampling_rate = 0; if (config.snaplen < 128) config.snaplen = 128; /* SFL_DEFAULT_HEADER_SIZE */ list->cfg.what_to_count = COUNT_PAYLOAD; list->cfg.what_to_count_2 = 0; if (list->cfg.classifiers_path) { list->cfg.what_to_count |= COUNT_CLASS; config.handle_fragments = TRUE; config.handle_flows = TRUE; } if (list->cfg.networks_file || (list->cfg.nfacctd_bgp && list->cfg.nfacctd_as == NF_AS_BGP)) { list->cfg.what_to_count |= COUNT_SRC_AS; list->cfg.what_to_count |= COUNT_DST_AS; list->cfg.what_to_count |= COUNT_PEER_DST_IP; } if (list->cfg.networks_file || list->cfg.networks_mask || list->cfg.nfacctd_net) { list->cfg.what_to_count |= COUNT_SRC_NMASK; list->cfg.what_to_count |= COUNT_DST_NMASK; } if (list->cfg.pre_tag_map) { list->cfg.what_to_count |= COUNT_TAG; list->cfg.what_to_count |= COUNT_TAG2; } if (list->cfg.what_to_count & (COUNT_STD_COMM|COUNT_EXT_COMM|COUNT_LOCAL_PREF|COUNT_MED|COUNT_AS_PATH| COUNT_PEER_SRC_AS|COUNT_PEER_DST_AS|COUNT_PEER_SRC_IP|COUNT_SRC_STD_COMM| COUNT_SRC_EXT_COMM|COUNT_SRC_AS_PATH|COUNT_SRC_MED|COUNT_SRC_LOCAL_PREF| COUNT_MPLS_VPN_RD)) { Log(LOG_ERR, "ERROR ( %s/core ): 'src_as', 'dst_as' and 'peer_dst_ip' are currently the only BGP-related primitives supported within the 'sfprobe' plugin.\n", config.name); exit(1); } #if defined (HAVE_L2) list->cfg.what_to_count |= COUNT_VLAN; list->cfg.what_to_count |= COUNT_COS; #endif if (list->cfg.nfacctd_as & NF_AS_FALLBACK && list->cfg.networks_file) list->cfg.nfacctd_as |= NF_AS_NEW; if (list->cfg.nfacctd_net & NF_NET_FALLBACK && list->cfg.networks_file) list->cfg.nfacctd_net |= NF_NET_NEW; list->cfg.data_type = PIPE_TYPE_PAYLOAD; } else { if (list->cfg.what_to_count_2 & (COUNT_POST_NAT_SRC_HOST|COUNT_POST_NAT_DST_HOST| COUNT_POST_NAT_SRC_PORT|COUNT_POST_NAT_DST_PORT|COUNT_NAT_EVENT| COUNT_TIMESTAMP_START|COUNT_TIMESTAMP_END|COUNT_TIMESTAMP_ARRIVAL)) list->cfg.data_type |= PIPE_TYPE_NAT; if (list->cfg.what_to_count_2 & (COUNT_MPLS_LABEL_TOP|COUNT_MPLS_LABEL_BOTTOM| COUNT_MPLS_STACK_DEPTH)) list->cfg.data_type |= PIPE_TYPE_MPLS; if (list->cfg.what_to_count_2 & (COUNT_LABEL)) list->cfg.data_type |= PIPE_TYPE_VLEN; evaluate_sums(&list->cfg.what_to_count, list->name, list->type.string); if (list->cfg.what_to_count & (COUNT_SRC_PORT|COUNT_DST_PORT|COUNT_SUM_PORT|COUNT_TCPFLAGS)) config.handle_fragments = TRUE; if (list->cfg.what_to_count & COUNT_FLOWS) { config.handle_fragments = TRUE; config.handle_flows = TRUE; } if (list->cfg.what_to_count & COUNT_CLASS) { config.handle_fragments = TRUE; config.handle_flows = TRUE; } if (!list->cfg.what_to_count && !list->cfg.what_to_count_2 && !list->cfg.cpptrs.num) { Log(LOG_WARNING, "WARN ( %s/%s ): defaulting to SRC HOST aggregation.\n", list->name, list->type.string); list->cfg.what_to_count |= COUNT_SRC_HOST; } if (((list->cfg.what_to_count & COUNT_SRC_HOST) && (list->cfg.what_to_count & COUNT_SRC_NET)) || ((list->cfg.what_to_count & COUNT_DST_HOST) && (list->cfg.what_to_count & COUNT_DST_NET))) { if (!list->cfg.tmp_net_own_field) { Log(LOG_ERR, "ERROR ( %s/%s ): src_host, src_net and dst_host, dst_net are mutually exclusive: set tmp_net_own_field to true. Exiting...\n\n", list->name, list->type.string); exit(1); } } if (list->cfg.what_to_count & (COUNT_SRC_AS|COUNT_DST_AS|COUNT_SUM_AS)) { if (!list->cfg.networks_file && list->cfg.nfacctd_as != NF_AS_BGP) { Log(LOG_ERR, "ERROR ( %s/%s ): AS aggregation selected but NO 'networks_file' or 'uacctd_as' are specified. Exiting...\n\n", list->name, list->type.string); exit(1); } if (list->cfg.nfacctd_as & NF_AS_FALLBACK && list->cfg.networks_file) list->cfg.nfacctd_as |= NF_AS_NEW; } if (list->cfg.what_to_count & (COUNT_SRC_NET|COUNT_DST_NET|COUNT_SUM_NET|COUNT_SRC_NMASK|COUNT_DST_NMASK|COUNT_PEER_DST_IP)) { if (!list->cfg.nfacctd_net) { if (list->cfg.networks_file) list->cfg.nfacctd_net |= NF_NET_NEW; if (list->cfg.networks_mask) list->cfg.nfacctd_net |= NF_NET_STATIC; if (!list->cfg.nfacctd_net) { Log(LOG_ERR, "ERROR ( %s/%s ): network aggregation selected but none of 'uacctd_net', 'networks_file', 'networks_mask' is specified. Exiting ...\n\n", list->name, list->type.string); exit(1); } } else { if ((list->cfg.nfacctd_net == NF_NET_NEW && !list->cfg.networks_file) || (list->cfg.nfacctd_net == NF_NET_STATIC && !list->cfg.networks_mask) || (list->cfg.nfacctd_net == NF_NET_BGP && !list->cfg.nfacctd_bgp) || (list->cfg.nfacctd_net == NF_NET_IGP && !list->cfg.nfacctd_isis) || (list->cfg.nfacctd_net == NF_NET_KEEP)) { Log(LOG_ERR, "ERROR ( %s/%s ): network aggregation selected but none of 'bgp_daemon', 'isis_daemon', 'networks_file', 'networks_mask' is specified. Exiting ...\n\n", list->name, list->type.string); exit(1); } if (list->cfg.nfacctd_net & NF_NET_FALLBACK && list->cfg.networks_file) list->cfg.nfacctd_net |= NF_NET_NEW; } } if (list->cfg.what_to_count & COUNT_CLASS && !list->cfg.classifiers_path) { Log(LOG_ERR, "ERROR ( %s/%s ): 'class' aggregation selected but NO 'classifiers' key specified. Exiting...\n\n", list->name, list->type.string); exit(1); } bgp_config_checks(&list->cfg); list->cfg.what_to_count |= COUNT_COUNTERS; list->cfg.data_type |= PIPE_TYPE_METADATA; } } list = list->next; } /* plugins glue: creation (since 094) */ if (config.classifiers_path) { init_classifiers(config.classifiers_path); init_conntrack_table(); } if (config.aggregate_primitives) { req.key_value_table = (void *) &custom_primitives_registry; load_id_file(MAP_CUSTOM_PRIMITIVES, config.aggregate_primitives, NULL, &req, &custom_primitives_allocated); } else memset(&custom_primitives_registry, 0, sizeof(custom_primitives_registry)); /* fixing per plugin custom primitives pointers, offsets and lengths */ list = plugins_list; while(list) { custom_primitives_reconcile(&list->cfg.cpptrs, &custom_primitives_registry); if (custom_primitives_vlen(&list->cfg.cpptrs)) list->cfg.data_type |= PIPE_TYPE_VLEN; list = list->next; } load_plugins(&req); if (config.handle_fragments) init_ip_fragment_handler(); if (config.handle_flows) init_ip_flow_handler(); load_networks(config.networks_file, &nt, &nc); #if defined (HAVE_L2) device.link_type = DLT_EN10MB; #else device.link_type = DLT_RAW; #endif for (index = 0; _devices[index].link_type != -1; index++) { if (device.link_type == _devices[index].link_type) device.data = &_devices[index]; } load_plugin_filters(device.link_type); cb_data.device = &device; /* signal handling we want to inherit to plugins (when not re-defined elsewhere) */ signal(SIGCHLD, startup_handle_falling_child); /* takes note of plugins failed during startup phase */ signal(SIGHUP, reload); /* handles reopening of syslog channel */ signal(SIGUSR1, push_stats); /* logs various statistics via Log() calls */ signal(SIGUSR2, reload_maps); /* sets to true the reload_maps flag */ signal(SIGPIPE, SIG_IGN); /* we want to exit gracefully when a pipe is broken */ nfh = nflog_open(); if (nfh == NULL) { Log(LOG_ERR, "ERROR ( %s/core ): Failed to create Netlink NFLOG socket\n", config.name); nflog_close(nfh); exit_all(1); } Log(LOG_INFO, "INFO ( %s/core ): Successfully connected Netlink NFLOG socket\n", config.name); /* Bind to IPv4 (and IPv6) */ if (nflog_unbind_pf(nfh, AF_INET) < 0) { Log(LOG_ERR, "ERROR ( %s/core ): Failed to unbind Netlink NFLOG socket from IPv4\n", config.name); nflog_close(nfh); exit_all(1); } if (nflog_bind_pf(nfh, AF_INET) < 0) { Log(LOG_ERR, "ERROR ( %s/core ): Failed to bind Netlink NFLOG socket from IPv4\n", config.name); nflog_close(nfh); exit_all(1); } #if defined ENABLE_IPV6 if (nflog_unbind_pf(nfh, AF_INET6) < 0) { Log(LOG_ERR, "ERROR ( %s/core ): Failed to unbind Netlink NFLOG socket from IPv6\n", config.name); nflog_close(nfh); exit_all(1); } if (nflog_bind_pf(nfh, AF_INET6) < 0) { Log(LOG_ERR, "ERROR ( %s/core ): Failed to bind Netlink NFLOG socket from IPv6\n", config.name); nflog_close(nfh); exit_all(1); } #endif /* Bind to group */ if ((nfgh = nflog_bind_group(nfh, config.uacctd_group)) == NULL) { Log(LOG_ERR, "ERROR ( %s/core ): Failed to join NFLOG group %d\n", config.name, config.uacctd_group); nflog_close(nfh); exit_all(1); } /* Set snaplen */ if (nflog_set_mode(nfgh, NFULNL_COPY_PACKET, config.snaplen) < 0) { Log(LOG_ERR, "ERROR ( %s/core ): Failed to set snaplen to %d\n", config.name, config.snaplen); nflog_unbind_group(nfgh); nflog_close(nfh); exit_all(1); } /* Set threshold */ if (nflog_set_qthresh(nfgh, config.uacctd_threshold) < 0) { Log(LOG_ERR, "ERROR ( %s/core ): Failed to set threshold to %d\n", config.name, config.uacctd_threshold); nflog_unbind_group(nfgh); nflog_close(nfh); exit_all(1); } /* Set buffer size */ if (nflog_set_nlbufsiz(nfgh, config.uacctd_nl_size) < 0) { Log(LOG_ERR, "ERROR ( %s/core ): Failed to set receive buffer size to %d\n", config.name, config.uacctd_nl_size); nflog_unbind_group(nfgh); nflog_close(nfh); exit_all(1); } /* Turn off netlink errors from overrun. */ if (setsockopt(nflog_fd(nfh), SOL_NETLINK, NETLINK_NO_ENOBUFS, &one, sizeof(one))) Log(LOG_ERR, "ERROR ( %s/core ): Failed to turn off netlink ENOBUFS\n", config.name); nflog_callback_register(nfgh, &nflog_incoming, &cb_data); nflog_buffer = malloc(config.uacctd_nl_size); if (nflog_buffer == NULL) { Log(LOG_ERR, "ERROR ( %s/core ): NFLOG buffer malloc() failed\n", config.name); nflog_unbind_group(nfgh); nflog_close(nfh); exit_all(1); } #if defined ENABLE_THREADS /* starting the ISIS threa */ if (config.nfacctd_isis) { req.bpf_filter = TRUE; nfacctd_isis_wrapper(); /* Let's give the ISIS thread some advantage to create its structures */ sleep(5); } /* starting the BGP thread */ if (config.nfacctd_bgp) { req.bpf_filter = TRUE; load_comm_patterns(&config.nfacctd_bgp_stdcomm_pattern, &config.nfacctd_bgp_extcomm_pattern, &config.nfacctd_bgp_stdcomm_pattern_to_asn); if (config.nfacctd_bgp_peer_as_src_type == BGP_SRC_PRIMITIVES_MAP) { if (config.nfacctd_bgp_peer_as_src_map) { load_id_file(MAP_BGP_PEER_AS_SRC, config.nfacctd_bgp_peer_as_src_map, &bpas_table, &req, &bpas_map_allocated); cb_data.bpas_table = (u_char *) &bpas_table; } else { Log(LOG_ERR, "ERROR ( %s/core ): bgp_peer_as_src_type set to 'map' but no map defined. Exiting.\n", config.name); exit(1); } } else cb_data.bpas_table = NULL; if (config.nfacctd_bgp_src_local_pref_type == BGP_SRC_PRIMITIVES_MAP) { if (config.nfacctd_bgp_src_local_pref_map) { load_id_file(MAP_BGP_SRC_LOCAL_PREF, config.nfacctd_bgp_src_local_pref_map, &blp_table, &req, &blp_map_allocated); cb_data.blp_table = (u_char *) &blp_table; } else { Log(LOG_ERR, "ERROR ( %s/core ): bgp_src_local_pref_type set to 'map' but no map defined. Exiting.\n", config.name); exit(1); } } else cb_data.bpas_table = NULL; if (config.nfacctd_bgp_src_med_type == BGP_SRC_PRIMITIVES_MAP) { if (config.nfacctd_bgp_src_med_map) { load_id_file(MAP_BGP_SRC_MED, config.nfacctd_bgp_src_med_map, &bmed_table, &req, &bmed_map_allocated); cb_data.bmed_table = (u_char *) &bmed_table; } else { Log(LOG_ERR, "ERROR ( %s/core ): bgp_src_med_type set to 'map' but no map defined. Exiting.\n", config.name); exit(1); } } else cb_data.bmed_table = NULL; if (config.nfacctd_bgp_to_agent_map) { load_id_file(MAP_BGP_TO_XFLOW_AGENT, config.nfacctd_bgp_to_agent_map, &bta_table, &req, &bta_map_allocated); cb_data.bta_table = (u_char *) &bta_table; } else { Log(LOG_ERR, "ERROR ( %s/core ): 'bgp_daemon' configured but no 'bgp_agent_map' has been specified. Exiting.\n", config.name); exit(1); } /* Limiting BGP peers to only two: one would suffice in pmacctd but in case maps are reloadable (ie. bta), it could be handy to keep a backup feed in memory */ config.nfacctd_bgp_max_peers = 2; cb_data.f_agent = (char *)&client; nfacctd_bgp_wrapper(); /* Let's give the BGP thread some advantage to create its structures */ sleep(5); } #else if (config.nfacctd_isis) { Log(LOG_ERR, "ERROR ( %s/core ): 'isis_daemon' is available only with threads (--enable-threads). Exiting.\n", config.name); exit(1); } if (config.nfacctd_bgp) { Log(LOG_ERR, "ERROR ( %s/core ): 'bgp_daemon' is available only with threads (--enable-threads). Exiting.\n", config.name); exit(1); } #endif #if defined WITH_GEOIP if (config.geoip_ipv4_file || config.geoip_ipv6_file) { req.bpf_filter = TRUE; } #endif #if defined WITH_GEOIPV2 if (config.geoipv2_file) { req.bpf_filter = TRUE; } #endif if (config.nfacctd_flow_to_rd_map) { Log(LOG_ERR, "ERROR ( %s/core ): 'flow_to_rd_map' is not supported by this daemon. Exiting.\n", config.name); exit(1); } /* plugins glue: creation (until 093) */ evaluate_packet_handlers(); pm_setproctitle("%s [%s]", "Core Process", config.proc_name); if (config.pidfile) write_pid_file(config.pidfile); /* signals to be handled only by pmacctd; we set proper handlers after plugin creation */ signal(SIGINT, my_sigint_handler); signal(SIGTERM, my_sigint_handler); signal(SIGCHLD, handle_falling_child); kill(getpid(), SIGCHLD); /* Main loop: if pcap_loop() exits maybe an error occurred; we will try closing and reopening again our listening device */ for (;;) { if (len == -1) { if (errno != EAGAIN) { /* We can't deal with permanent errors. * Just sleep a bit. */ Log(LOG_ERR, "ERROR ( %s/core ): Syscall returned %d: %s. Sleeping for 1 sec.\n", config.name, errno, strerror(errno)); sleep(1); } } len = recv(nflog_fd(nfh), nflog_buffer, config.uacctd_nl_size, 0); if (len < 0) continue; if (nflog_handle_packet(nfh, nflog_buffer, len) != 0) continue; } }
/* Functions */ void amqp_plugin(int pipe_fd, struct configuration *cfgptr, void *ptr) { struct pkt_data *data; struct ports_table pt; unsigned char *pipebuf; struct pollfd pfd; time_t t, now; int timeout, ret, num; struct ring *rg = &((struct channels_list_entry *)ptr)->rg; struct ch_status *status = ((struct channels_list_entry *)ptr)->status; int datasize = ((struct channels_list_entry *)ptr)->datasize; u_int32_t bufsz = ((struct channels_list_entry *)ptr)->bufsize; struct networks_file_data nfd; unsigned char *rgptr; int pollagain = TRUE; u_int32_t seq = 1, rg_err_count = 0; struct extra_primitives extras; struct primitives_ptrs prim_ptrs; char *dataptr; memcpy(&config, cfgptr, sizeof(struct configuration)); memcpy(&extras, &((struct channels_list_entry *)ptr)->extras, sizeof(struct extra_primitives)); recollect_pipe_memory(ptr); pm_setproctitle("%s [%s]", "RabbitMQ/AMQP Plugin", config.name); P_set_signals(); P_init_default_values(); pipebuf = (unsigned char *) Malloc(config.buffer_size); memset(pipebuf, 0, config.buffer_size); timeout = config.sql_refresh_time*1000; if (!config.sql_user) config.sql_user = rabbitmq_user; if (!config.sql_passwd) config.sql_passwd = rabbitmq_pwd; /* setting function pointers */ if (config.what_to_count & (COUNT_SUM_HOST|COUNT_SUM_NET)) insert_func = P_sum_host_insert; else if (config.what_to_count & COUNT_SUM_PORT) insert_func = P_sum_port_insert; else if (config.what_to_count & COUNT_SUM_AS) insert_func = P_sum_as_insert; #if defined (HAVE_L2) else if (config.what_to_count & COUNT_SUM_MAC) insert_func = P_sum_mac_insert; #endif else insert_func = P_cache_insert; purge_func = amqp_cache_purge; memset(&nt, 0, sizeof(nt)); memset(&nc, 0, sizeof(nc)); memset(&pt, 0, sizeof(pt)); load_networks(config.networks_file, &nt, &nc); set_net_funcs(&nt); if (config.ports_file) load_ports(config.ports_file, &pt); if (config.pkt_len_distrib_bins_str) load_pkt_len_distrib_bins(); else { if (config.what_to_count_2 & COUNT_PKT_LEN_DISTRIB) { Log(LOG_ERR, "ERROR ( %s/%s ): 'aggregate' contains pkt_len_distrib but no 'pkt_len_distrib_bins' defined. Exiting.\n", config.name, config.type); exit_plugin(1); } } memset(&prim_ptrs, 0, sizeof(prim_ptrs)); set_primptrs_funcs(&extras); pfd.fd = pipe_fd; pfd.events = POLLIN; setnonblocking(pipe_fd); now = time(NULL); /* print_refresh time init: deadline */ refresh_deadline = now; t = roundoff_time(refresh_deadline, config.sql_history_roundoff); while ((t+config.sql_refresh_time) < refresh_deadline) t += config.sql_refresh_time; refresh_deadline = t; refresh_deadline += config.sql_refresh_time; /* it's a deadline not a basetime */ if (config.sql_history) { basetime_init = P_init_historical_acct; basetime_eval = P_eval_historical_acct; basetime_cmp = P_cmp_historical_acct; (*basetime_init)(now); } /* setting number of entries in _protocols structure */ while (_protocols[protocols_number].number != -1) protocols_number++; /* plugin main loop */ for(;;) { poll_again: status->wakeup = TRUE; ret = poll(&pfd, 1, timeout); if (ret <= 0) { if (getppid() == 1) { Log(LOG_ERR, "ERROR ( %s/%s ): Core process *seems* gone. Exiting.\n", config.name, config.type); exit_plugin(1); } if (ret < 0) goto poll_again; } now = time(NULL); if (config.sql_history) { while (now > (basetime.tv_sec + timeslot)) { new_basetime.tv_sec = basetime.tv_sec; basetime.tv_sec += timeslot; if (config.sql_history == COUNT_MONTHLY) timeslot = calc_monthly_timeslot(basetime.tv_sec, config.sql_history_howmany, ADD); } } switch (ret) { case 0: /* timeout */ P_cache_handle_flush_event(&pt); break; default: /* we received data */ read_data: if (!pollagain) { seq++; seq %= MAX_SEQNUM; if (seq == 0) rg_err_count = FALSE; } else { if ((ret = read(pipe_fd, &rgptr, sizeof(rgptr))) == 0) exit_plugin(1); /* we exit silently; something happened at the write end */ } if (((struct ch_buf_hdr *)rg->ptr)->seq != seq) { if (!pollagain) { pollagain = TRUE; goto poll_again; } else { rg_err_count++; if (config.debug || (rg_err_count > MAX_RG_COUNT_ERR)) { Log(LOG_ERR, "ERROR ( %s/%s ): We are missing data.\n", config.name, config.type); Log(LOG_ERR, "If you see this message once in a while, discard it. Otherwise some solutions follow:\n"); Log(LOG_ERR, "- increase shared memory size, 'plugin_pipe_size'; now: '%u'.\n", config.pipe_size); Log(LOG_ERR, "- increase buffer size, 'plugin_buffer_size'; now: '%u'.\n", config.buffer_size); Log(LOG_ERR, "- increase system maximum socket size.\n\n"); } seq = ((struct ch_buf_hdr *)rg->ptr)->seq; } } pollagain = FALSE; memcpy(pipebuf, rg->ptr, bufsz); if ((rg->ptr+bufsz) >= rg->end) rg->ptr = rg->base; else rg->ptr += bufsz; /* lazy refresh time handling */ if (now > refresh_deadline) P_cache_handle_flush_event(&pt); data = (struct pkt_data *) (pipebuf+sizeof(struct ch_buf_hdr)); while (((struct ch_buf_hdr *)pipebuf)->num > 0) { for (num = 0; primptrs_funcs[num]; num++) (*primptrs_funcs[num])((u_char *)data, &extras, &prim_ptrs); for (num = 0; net_funcs[num]; num++) (*net_funcs[num])(&nt, &nc, &data->primitives, prim_ptrs.pbgp, &nfd); if (config.ports_file) { if (!pt.table[data->primitives.src_port]) data->primitives.src_port = 0; if (!pt.table[data->primitives.dst_port]) data->primitives.dst_port = 0; } if (config.pkt_len_distrib_bins_str && config.what_to_count_2 & COUNT_PKT_LEN_DISTRIB) evaluate_pkt_len_distrib(data); prim_ptrs.data = data; (*insert_func)(&prim_ptrs); ((struct ch_buf_hdr *)pipebuf)->num--; if (((struct ch_buf_hdr *)pipebuf)->num) { dataptr = (unsigned char *) data; dataptr += datasize; data = (struct pkt_data *) dataptr; } } goto read_data; } } }