📄 softflowd.c
字号:
fprintf(stderr, " -T full|proto|ip Set flow tracking level (default: full)\n"); fprintf(stderr, " -6 Track IPv6 flows, regardless of whether selected \n" " NetFlow export protocol supports it\n"); fprintf(stderr, " -d Don't daemonise\n"); fprintf(stderr, " -D Debug mode: don't daemonise + verbosity + track v6 flows\n"); fprintf(stderr, " -h Display this help\n"); fprintf(stderr, "\n"); fprintf(stderr, "Valid timeout names and default values:\n"); fprintf(stderr, " tcp (default %6d)", DEFAULT_TCP_TIMEOUT); fprintf(stderr, " tcp.rst (default %6d)", DEFAULT_TCP_RST_TIMEOUT); fprintf(stderr, " tcp.fin (default %6d)\n", DEFAULT_TCP_FIN_TIMEOUT); fprintf(stderr, " udp (default %6d)", DEFAULT_UDP_TIMEOUT); fprintf(stderr, " icmp (default %6d)", DEFAULT_ICMP_TIMEOUT); fprintf(stderr, " general (default %6d)\n", DEFAULT_GENERAL_TIMEOUT); fprintf(stderr, " maxlife (default %6d)", DEFAULT_MAXIMUM_LIFETIME); fprintf(stderr, " expint (default %6d)\n", DEFAULT_EXPIRY_INTERVAL); fprintf(stderr, "\n");}static voidset_timeout(struct FLOWTRACK *ft, const char *to_spec){ char *name, *value; int timeout; if ((name = strdup(to_spec)) == NULL) { fprintf(stderr, "Out of memory\n"); exit(1); } if ((value = strchr(name, '=')) == NULL || *(++value) == '\0') { fprintf(stderr, "Invalid -t option \"%s\".\n", name); usage(); exit(1); } *(value - 1) = '\0'; timeout = convtime(value); if (timeout < 0) { fprintf(stderr, "Invalid -t timeout.\n"); usage(); exit(1); } if (strcmp(name, "tcp") == 0) ft->tcp_timeout = timeout; else if (strcmp(name, "tcp.rst") == 0) ft->tcp_rst_timeout = timeout; else if (strcmp(name, "tcp.fin") == 0) ft->tcp_fin_timeout = timeout; else if (strcmp(name, "udp") == 0) ft->udp_timeout = timeout; else if (strcmp(name, "icmp") == 0) ft->icmp_timeout = timeout; else if (strcmp(name, "general") == 0) ft->general_timeout = timeout; else if (strcmp(name, "maxlife") == 0) ft->maximum_lifetime = timeout; else if (strcmp(name, "expint") == 0) ft->expiry_interval = timeout; else { fprintf(stderr, "Invalid -t name.\n"); usage(); exit(1); } if (ft->general_timeout == 0) { fprintf(stderr, "\"general\" flow timeout must be " "greater than zero\n"); exit(1); } free(name);}static voidparse_hostport(const char *s, struct sockaddr *addr, socklen_t *len){ char *orig, *host, *port; struct addrinfo hints, *res; int herr; if ((host = orig = strdup(s)) == NULL) { fprintf(stderr, "Out of memory\n"); exit(1); } if ((port = strrchr(host, ':')) == NULL || *(++port) == '\0' || *host == '\0') { fprintf(stderr, "Invalid -n argument.\n"); usage(); exit(1); } *(port - 1) = '\0'; /* Accept [host]:port for numeric IPv6 addresses */ if (*host == '[' && *(port - 2) == ']') { host++; *(port - 2) = '\0'; } memset(&hints, '\0', sizeof(hints)); hints.ai_socktype = SOCK_DGRAM; if ((herr = getaddrinfo(host, port, &hints, &res)) == -1) { fprintf(stderr, "Address lookup failed: %s\n", gai_strerror(herr)); exit(1); } if (res == NULL || res->ai_addr == NULL) { fprintf(stderr, "No addresses found for [%s]:%s\n", host, port); exit(1); } if (res->ai_addrlen > *len) { fprintf(stderr, "Address too long\n"); exit(1); } memcpy(addr, res->ai_addr, res->ai_addrlen); free(orig); *len = res->ai_addrlen;}/* * Drop privileges and chroot, will exit on failure */static void drop_privs(void){ struct passwd *pw; if ((pw = getpwnam(PRIVDROP_USER)) == NULL) { logit(LOG_ERR, "Unable to find unprivileged user \"%s\"", PRIVDROP_USER); exit(1); } if (chdir(PRIVDROP_CHROOT_DIR) != 0) { logit(LOG_ERR, "Unable to chdir to chroot directory \"%s\": %s", PRIVDROP_CHROOT_DIR, strerror(errno)); exit(1); } if (chroot(PRIVDROP_CHROOT_DIR) != 0) { logit(LOG_ERR, "Unable to chroot to directory \"%s\": %s", PRIVDROP_CHROOT_DIR, strerror(errno)); exit(1); } if (chdir("/") != 0) { logit(LOG_ERR, "Unable to chdir to chroot root: %s", strerror(errno)); exit(1); } if (setgroups(1, &pw->pw_gid) != 0) { logit(LOG_ERR, "Couldn't setgroups (%u): %s", (unsigned int)pw->pw_gid, strerror(errno)); exit(1); }#if defined(HAVE_SETRESGID) if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1) {#elif defined(HAVE_SETREGID) if (setregid(pw->pw_gid, pw->pw_gid) == -1) {#else if (setegid(pw->pw_gid) == -1 || setgid(pw->pw_gid) == -1) {#endif logit(LOG_ERR, "Couldn't set gid (%u): %s", (unsigned int)pw->pw_gid, strerror(errno)); exit(1); }#if defined(HAVE_SETRESUID) if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) {#elif defined(HAVE_SETREUID) if (setreuid(pw->pw_uid, pw->pw_uid) == -1) {#else if (seteuid(pw->pw_uid) == -1 || setuid(pw->pw_uid) == -1) {#endif logit(LOG_ERR, "Couldn't set uid (%u): %s", (unsigned int)pw->pw_uid, strerror(errno)); exit(1); }}intmain(int argc, char **argv){ char *dev, *capfile, *bpf_prog, dest_addr[256], dest_serv[256]; const char *pidfile_path, *ctlsock_path; extern char *optarg; extern int optind; int ch, dontfork_flag, linktype, ctlsock, i, r, err, always_v6; int max_flows, stop_collection_flag, exit_request, hoplimit; pcap_t *pcap = NULL; struct sockaddr_storage dest; struct FLOWTRACK flowtrack; socklen_t dest_len; struct NETFLOW_TARGET target; struct CB_CTXT cb_ctxt; struct pollfd pl[2]; closefrom(STDERR_FILENO + 1); init_flowtrack(&flowtrack); memset(&dest, '\0', sizeof(dest)); memset(&target, '\0', sizeof(target)); target.fd = -1; target.dialect = &nf[0]; hoplimit = -1; bpf_prog = NULL; ctlsock = -1; dev = capfile = NULL; max_flows = DEFAULT_MAX_FLOWS; pidfile_path = DEFAULT_PIDFILE; ctlsock_path = DEFAULT_CTLSOCK; dontfork_flag = 0; always_v6 = 0; while ((ch = getopt(argc, argv, "6hdDL:T:i:r:f:t:n:m:p:c:v:")) != -1) { switch (ch) { case '6': always_v6 = 1; break; case 'h': usage(); return (0); case 'D': verbose_flag = 1; always_v6 = 1; /* FALLTHROUGH */ case 'd': dontfork_flag = 1; break; case 'i': if (capfile != NULL || dev != NULL) { fprintf(stderr, "Packet source already specified.\n\n"); usage(); exit(1); } dev = optarg; break; case 'r': if (capfile != NULL || dev != NULL) { fprintf(stderr, "Packet source already specified.\n\n"); usage(); exit(1); } capfile = optarg; dontfork_flag = 1; ctlsock_path = NULL; break; case 't': /* Will exit on failure */ set_timeout(&flowtrack, optarg); break; case 'T': if (strcasecmp(optarg, "full") == 0) flowtrack.track_level = TRACK_FULL; else if (strcasecmp(optarg, "proto") == 0) flowtrack.track_level = TRACK_IP_PROTO; else if (strcasecmp(optarg, "ip") == 0) flowtrack.track_level = TRACK_IP_ONLY; else { fprintf(stderr, "Unknown flow tracking level\n"); usage(); exit(1); } break; case 'L': hoplimit = atoi(optarg); if (hoplimit < 0 || hoplimit > 255) { fprintf(stderr, "Invalid hop limit\n\n"); usage(); exit(1); } break; case 'm': if ((max_flows = atoi(optarg)) < 0) { fprintf(stderr, "Invalid maximum flows\n\n"); usage(); exit(1); } break; case 'n': /* Will exit on failure */ dest_len = sizeof(dest); parse_hostport(optarg, (struct sockaddr *)&dest, &dest_len); break; case 'p': pidfile_path = optarg; break; case 'c': if (strcmp(optarg, "none") == 0) ctlsock_path = NULL; else ctlsock_path = optarg; break; case 'v': for(i = 0, r = atoi(optarg); nf[i].version != -1; i++) { if (nf[i].version == r) break; } if (nf[i].version == -1) { fprintf(stderr, "Invalid NetFlow version\n"); exit(1); } target.dialect = &nf[i]; break; default: fprintf(stderr, "Invalid commandline option.\n"); usage(); exit(1); } } if (capfile == NULL && dev == NULL) { fprintf(stderr, "-i or -r option not specified.\n"); usage(); exit(1); } /* join remaining arguments (if any) into bpf program */ bpf_prog = argv_join(argc - optind, argv + optind); /* Will exit on failure */ setup_packet_capture(&pcap, &linktype, dev, capfile, bpf_prog, target.dialect->v6_capable || always_v6); /* Netflow send socket */ if (dest.ss_family != 0) { if ((err = getnameinfo((struct sockaddr *)&dest, dest_len, dest_addr, sizeof(dest_addr), dest_serv, sizeof(dest_serv), NI_NUMERICHOST)) == -1) { fprintf(stderr, "getnameinfo: %d\n", err); exit(1); } target.fd = connsock(&dest, dest_len, hoplimit); } /* Control socket */ if (ctlsock_path != NULL) ctlsock = unix_listener(ctlsock_path); /* Will exit on fail */ if (dontfork_flag) { loginit(PROGNAME, 1); } else { FILE *pidfile; daemon(0, 0); loginit(PROGNAME, 0); if ((pidfile = fopen(pidfile_path, "w")) == NULL) { fprintf(stderr, "Couldn't open pidfile %s: %s\n", pidfile_path, strerror(errno)); exit(1); } fprintf(pidfile, "%u\n", getpid()); fclose(pidfile); signal(SIGINT, sighand_graceful_shutdown); signal(SIGTERM, sighand_graceful_shutdown); signal(SIGSEGV, sighand_other); setprotoent(1); drop_privs(); } logit(LOG_NOTICE, "%s v%s starting data collection", PROGNAME, PROGVER); if (dest.ss_family != 0) { logit(LOG_NOTICE, "Exporting flows to [%s]:%s", dest_addr, dest_serv); } /* Main processing loop */ gettimeofday(&flowtrack.system_boot_time, NULL); stop_collection_flag = 0; memset(&cb_ctxt, '\0', sizeof(cb_ctxt)); cb_ctxt.ft = &flowtrack; cb_ctxt.linktype = linktype; cb_ctxt.want_v6 = target.dialect->v6_capable || always_v6; for(;;) { /* * Silly libpcap's timeout function doesn't work, so we * do it here (only if we are reading live) */ r = 0; if (capfile == NULL) { memset(pl, '\0', sizeof(pl)); /* This can only be set via the control socket */ if (!stop_collection_flag) { pl[0].events = POLLIN|POLLERR|POLLHUP; pl[0].fd = pcap_fileno(pcap); } if (ctlsock != -1) { pl[1].fd = ctlsock; pl[1].events = POLLIN|POLLERR|POLLHUP; } r = poll(pl, (ctlsock == -1) ? 1 : 2, next_expire(&flowtrack)); if (r == -1 && errno != EINTR) { logit(LOG_ERR, "Exiting on poll: %s", strerror(errno)); break; } } /* Accept connection on control socket if present */ if (ctlsock != -1 && pl[1].revents != 0) { if (accept_control(ctlsock, &target, &flowtrack, pcap, &exit_request, &stop_collection_flag) != 0) break; } /* If we have data, run it through libpcap */ if (!stop_collection_flag && (capfile != NULL || pl[0].revents != 0)) { r = pcap_dispatch(pcap, max_flows, flow_cb, (void*)&cb_ctxt); if (r == -1) { logit(LOG_ERR, "Exiting on pcap_dispatch: %s", pcap_geterr(pcap)); break; } else if (r == 0) { logit(LOG_NOTICE, "Shutting down after pcap EOF"); graceful_shutdown_request = 1; break; } } r = 0; /* Fatal error from per-packet functions */ if (cb_ctxt.fatal) { logit(LOG_WARNING, "Fatal error - exiting immediately"); break; } /* * Expiry processing happens every recheck_rate seconds * or whenever we have exceeded the maximum number of active * flows */ if (flowtrack.num_flows > max_flows || next_expire(&flowtrack) == 0) {expiry_check: /* * If we are reading from a capture file, we never * expire flows based on time - instead we only * expire flows when the flow table is full. */ if (check_expired(&flowtrack, &target, capfile == NULL ? CE_EXPIRE_NORMAL : CE_EXPIRE_FORCED) < 0) logit(LOG_WARNING, "Unable to export flows"); /* * If we are over max_flows, force-expire the oldest * out first and immediately reprocess to evict them */ if (flowtrack.num_flows > max_flows) { force_expire(&flowtrack, flowtrack.num_flows - max_flows); goto expiry_check; } } } /* Flags set by signal handlers or control socket */ if (graceful_shutdown_request) { logit(LOG_WARNING, "Shutting down on user request"); check_expired(&flowtrack, &target, CE_EXPIRE_ALL); } else if (exit_request) logit(LOG_WARNING, "Exiting immediately on user request"); else logit(LOG_ERR, "Exiting immediately on internal error"); if (capfile != NULL && dontfork_flag) statistics(&flowtrack, stdout, pcap); pcap_close(pcap); if (target.fd != -1) close(target.fd); unlink(pidfile_path); if (ctlsock_path != NULL) unlink(ctlsock_path); return(r == 0 ? 0 : 1);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -