diff --git a/Makefile.am b/Makefile.am index a654402..62a4b27 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3,9 +3,9 @@ PACKAGE = samplicator bin_PROGRAMS = samplicate -samplicate_SOURCES = samplicate.c samplicator.h rawsend.c rawsend.h read_config.c read_config.h +samplicate_SOURCES = samplicate.c samplicator.h rawsend.c rawsend.h read_config.c read_config.h inet.c inet.h samplicate_LDADD = @LIBOBJS@ EXTRA_PROGRAMS = rawtest parsetest rawtest_SOURCES = rawtest.c rawsend.c rawsend.h -parsetest_SOURCES = parsetest.c read_config.c rawsend.c read_config.h rawsend.h samplicator.h +parsetest_SOURCES = parsetest.c read_config.c rawsend.c read_config.h rawsend.h samplicator.h inet.c inet.h diff --git a/configure.in b/configure.in index 3836fb5..26dbd58 100644 --- a/configure.in +++ b/configure.in @@ -8,7 +8,6 @@ AC_CHECK_LIB(socket,bind) AC_STDC_HEADERS AC_CHECK_HEADERS(stdlib.h unistd.h ctype.h arpa/inet.h netinet/in_systm.h sys/uio.h) AC_CHECK_FUNCS(memcpy strchr) -AC_REPLACE_FUNCS(inet_aton) AC_DEFINE([HAVE_STRUCT_IP], 1, [Define if the system has `struct ip'.]) AC_DEFINE([HAVE_STRUCT_IPHDR], 1, diff --git a/parsetest.c b/parsetest.c index 4064434..8daa3b8 100644 --- a/parsetest.c +++ b/parsetest.c @@ -58,9 +58,6 @@ #ifdef HAVE_CTYPE_H # include #endif -#ifndef HAVE_INET_ATON -extern int inet_aton (const char *, struct in_addr *); -#endif #include "samplicator.h" #include "read_config.h" @@ -90,7 +87,7 @@ check_receiver (receiver, addr, port, af, freq, ttl) else if (af == AF_INET6) check_int_equal (receiver->addrlen, sizeof (struct sockaddr_in6)); else - return test_fail (); + test_fail (); check_address_equal ((struct sockaddr *) &receiver->addr, addr, port, af); check_int_equal (receiver->freq, freq); check_int_equal (receiver->ttl, ttl); @@ -121,17 +118,15 @@ main (int argc, char **argv) check_int_equal (ctx.fork, 0); check_null (ctx.sources); -#ifdef NOTYET check_int_equal (parse_cf_string ("1.2.3.4/30+ 6.7.8.9/1234\n", &ctx), -1); -#endif - check_int_equal (parse_cf_string ("1.2.3.4/255.255.255.252: 6.7.8.9/1234\n2.3.4.5: 7.8.9.0/4321", &ctx), 0); + check_int_equal (parse_cf_string ("1.2.3.4/255.255.255.252: 6.7.8.9/1234/10,237\n2.3.4.5: 7.8.9.0/4321", &ctx), 0); check_int_equal (ctx.fork, 0); if (check_non_null (sctx = ctx.sources)) { check_source_address_mask (sctx, "1.2.3.4", "255.255.255.252", AF_INET); check_int_equal (sctx->nreceivers, 1); - check_receiver (&sctx->receivers[0], "6.7.8.9", 1234, AF_INET, 1, DEFAULT_TTL); + check_receiver (&sctx->receivers[0], "6.7.8.9", 1234, AF_INET, 10, 237); if (check_non_null (sctx = sctx->next)) { check_source_address_mask (sctx, "2.3.4.5", "255.255.255.255", AF_INET); @@ -141,7 +136,6 @@ main (int argc, char **argv) } } -#ifdef NOTYET check_int_equal (parse_cf_string ("1.2.3.4/30: 6.7.8.9/1234\n2.3.4.5: 7.8.9.0/4321", &ctx), 0); check_int_equal (ctx.fork, 0); if (check_non_null (sctx = ctx.sources)) @@ -158,6 +152,7 @@ main (int argc, char **argv) } } +#ifdef NOTYET check_int_equal (parse_cf_string ("1.2.3.4/30: localhost/1234", &ctx), 0); check_int_equal (ctx.fork, 0); if (check_non_null (sctx = ctx.sources)) @@ -166,6 +161,7 @@ main (int argc, char **argv) check_int_equal (sctx->nreceivers, 1); check_receiver (&sctx->receivers[0], "127.0.0.1", 1234, AF_INET, 1, DEFAULT_TTL); } +#endif check_int_equal (parse_cf_string ("1.2.3.4/30: ip6-localhost/1234", &ctx), 0); check_int_equal (ctx.fork, 0); @@ -209,6 +205,7 @@ main (int argc, char **argv) check_receiver (&sctx->receivers[0], "2001:db8:0::1", 2000, AF_INET6, 1, DEFAULT_TTL); check_null (sctx->next); } +#ifdef NOTYET #endif return 0; } diff --git a/rawsend.c b/rawsend.c index b15d380..922c27c 100644 --- a/rawsend.c +++ b/rawsend.c @@ -184,10 +184,16 @@ raw_send_from_to (s, msg, msglen, saddr_generic, daddr_generic, ttl, flags) #undef daddr extern int -make_raw_udp_socket (sockbuflen) - long sockbuflen; +make_raw_udp_socket (sockbuflen, af) + size_t sockbuflen; + int af; { int s; + if (af == AF_INET6) + { + fprintf (stderr, "Spoofing not supported for IPv6\n"); + return -1; + } if ((s = socket (PF_INET, SOCK_RAW, IPPROTO_RAW)) == -1) return s; if (sockbuflen != -1) diff --git a/rawsend.h b/rawsend.h index fc72142..e76f083 100644 --- a/rawsend.h +++ b/rawsend.h @@ -9,7 +9,7 @@ #define RAWSEND_COMPUTE_UDP_CHECKSUM 0x0001 -extern int make_raw_udp_socket (long); +extern int make_raw_udp_socket (size_t, int); extern int raw_send_from_to (int, const void *, size_t, struct sockaddr *, diff --git a/rawtest.c b/rawtest.c index 6ddd6c6..bd46b27 100644 --- a/rawtest.c +++ b/rawtest.c @@ -24,7 +24,7 @@ main (int argc, char **argv) { int s; - if ((s = make_raw_udp_socket (0)) == -1) + if ((s = make_raw_udp_socket (0, AF_INET)) == -1) { fprintf (stderr, "socket: %s\n", strerror (errno)); diff --git a/read_config.c b/read_config.c index 0a8669f..88bc7d6 100644 --- a/read_config.c +++ b/read_config.c @@ -35,28 +35,87 @@ #ifdef HAVE_CTYPE_H # include #endif -#ifndef HAVE_INET_ATON -extern int inet_aton (const char *, struct in_addr *); -#endif #include "samplicator.h" #include "read_config.h" +#include "inet.h" #include "rawsend.h" -static int make_cooked_udp_socket (long); -static void usage (const char *); - #define PORT_SEPARATOR '/' #define FREQ_SEPARATOR '/' #define TTL_SEPARATOR ',' -#define FLOWPORT 2000 +#define FLOWPORT "2000" #define DEFAULT_SOCKBUFLEN 65536 #define MAX_PEERS 100 #define MAX_LINELEN 8000 +static int parse_line (struct samplicator_context *, char *, const char *); +static int parse_addr_mask (const char *, const char *, + const struct samplicator_context *, + struct sockaddr_storage *, + struct sockaddr_storage *, + socklen_t *); +static void short_usage (const char *); +static void usage (const char *); + +static int +parse_error (const struct samplicator_context *ctx, const char *fmt, ...) +{ + va_list fmt_args; + va_start (fmt_args, fmt); + fprintf (stderr, "%s, line %d: ", ctx->config_file_name, ctx->config_file_lineno); + vfprintf (stderr, fmt, fmt_args); + fprintf (stderr, "\n"); + va_end (fmt_args); + return -1; +} + + +/* copy_string_start_end (start, end) + + Copy a string defined by a start and end pointer to a fresh + null-terminated string. This is useful when using library + functions that expect such strings. + + Will return a null pointer if allocating space fails. + + The copy should be free()'d when the caller is done with it. + */ +static char * +copy_string_start_end (start, end) + const char *start; + const char *end; +{ + size_t len = end-start; + char *copy = malloc (len+1); + if (copy == 0) + return 0; + strncpy (copy, start, len); + copy[len] = 0; + return copy; +} + +/* read_cf_file (file, ctx) + + Read configuration file FILE into samplicator context CTX. + + The file is opened and parsed line by line. The parser fills in + values of CTX. + + The file name and a line counter are stored in CTX for use in + parser error messages. + + Return value: + + 0, if the file could be parsed. + -1, if an error occurred during parsing. + + If an error occurs, the parser will give up immediately. Contents + preceding the errors may have been processed and filled into CTX. + */ int read_cf_file (file, ctx) const char *file; @@ -64,96 +123,525 @@ read_cf_file (file, ctx) { FILE *cf; char tmp_s[MAX_LINELEN]; - int argc; - const char *argv[MAX_PEERS]; - char *c, *slash, *e; - struct source_context *sctx; + ctx->config_file_name = file; + ctx->config_file_lineno = 0; - if ((cf = fopen(file,"r")) == NULL) + if ((cf = fopen (file,"r")) == NULL) { - fprintf(stderr, "read_cf_file: cannot open %s. Aborting.\n",file); - exit(1); + fprintf (stderr, "read_cf_file: cannot open %s. Aborting.\n", file); + return -1; } - while (!feof(cf)) + while (!feof (cf)) { - if (fgets (tmp_s, MAX_LINELEN - 1, cf) == (char *) 0) + if (fgets (tmp_s, MAX_LINELEN, cf) == (char *) 0) { break; } - if ((c = strchr(tmp_s, '#')) != 0) - continue; - - /* lines look like this: + ++ctx->config_file_lineno; + if (parse_line (ctx, tmp_s, tmp_s + strlen (tmp_s)) == -1) + { + return -1; + } + } + fclose (cf); + return 0; +} - ipadd[/mask]: dest[:port[/freq][,ttl]] dest2[:port2[/freq2][,ttl2]]... +/* resolve_addr (string, ctx, addrp, addrlenp) - */ - - if ((c = strchr(tmp_s, ':')) != 0) + Parses, and possibly resolves, an IP address given as a string. + + The preferences in CTX are used to determine whether to return an + IPv4 or IPv6 address. + + The address is stored in the sockaddr_storage structure pointed to + by ADDRP. + + If ADDRLENP is non-null, it the length of the address structure + will be stored to it. + + If an error occurs during parsing or resolution, the function will + return -1, and nothing will be stored in ADDRP or ADDRLENP. + */ +static int +resolve_addr (const char *addrstring, + const struct samplicator_context *ctx, + struct sockaddr_storage *addrp, + socklen_t *addrlenp) +{ + struct addrinfo hints, *res; + + init_hints_from_preferences (&hints, ctx); + + if (getaddrinfo (addrstring, 0, &hints, &res) != 0 || res == 0) + { + return parse_error (ctx, "Could not parse address %s", addrstring); + } + memcpy (addrp, res->ai_addr, res->ai_addrlen); + if (addrlenp != 0) + *addrlenp = res->ai_addrlen; + return 0; +} + +static int +parse_addr_1 (const char *start, + const char *end, + const struct samplicator_context *ctx, + struct sockaddr_storage *addrp, + socklen_t *addrlenp) +{ + char *copy = copy_string_start_end (start, end); + int result; + + if (copy == 0) + { + return parse_error (ctx, "Out of memory"); + } + result = resolve_addr (copy, ctx, addrp, addrlenp); + free (copy); + return result; +} + +static int +parse_addr (const char *start, + const char *end, + const struct samplicator_context *ctx, + struct sockaddr_storage *addrp, + socklen_t *addrlenp) +{ + while (start < end && isspace (*start)) + ++start; + while (start < end && isspace (*(end-1))) + --end; + if (start < end && *start == '[') + { + ++start; + if (start < end && *(end-1) == ']') + --end; + else { - *c++ = 0; + return parse_error (ctx, "Mismatched brackets"); + } + } + return parse_addr_1 (start, end, ctx, addrp, addrlenp); +} - sctx = calloc(1, sizeof(struct source_context)); - if ((slash = strchr (tmp_s, '/')) != 0) - { - *slash++ = 0; +static void +set_ipv4_netmask(struct sockaddr_in *maskp, int preflen) +{ + in_addr_t *addrp = &maskp->sin_addr.s_addr; + int k; + unsigned long bit; + uint32_t haddr; + + haddr = 0xffffffff; + preflen = 32-preflen; + for (k = 0, bit = 1; k < preflen; ++k, bit <<= 1) + { + haddr &= ~bit; + } + *addrp = htonl (haddr); +} - ((struct sockaddr_in *) &sctx->mask)->sin_family = AF_INET; - /* fprintf (stderr, "parsing IP address mask (%s)\n",slash); */ - if (inet_aton (slash, &(((struct sockaddr_in *) &sctx->mask)->sin_addr)) == 0) +static void +set_ipv6_netmask(struct sockaddr_in6 *maskp, int preflen) +{ + uint8_t *addrp = maskp->sin6_addr.s6_addr; + int k; + unsigned bit; + unsigned octet_index; + + memset (addrp, 0, 16); + for (k = 0, bit = 128, octet_index = 0; + k < preflen; + ++k, bit >>= 1) + { + if (bit < 1) + { + bit = 128, octet_index++; + } + addrp[octet_index] |= bit; + } +} + +static int +parse_mask (const char *start, + const char *end, + const struct samplicator_context *ctx, + struct sockaddr_storage *maskp, + struct sockaddr_storage *addrp) +{ + if (addrp->ss_family == AF_INET) + { + const char *cp = start; + while (cp < end && *cp != '.') + ++cp; + if (cp < end) /* contains a dot, assume full netmask */ + { + socklen_t addrlen1; + if (parse_addr_1 (start, end, ctx, maskp, &addrlen1) != 0) + { + return parse_error (ctx, "Cannot parse mask"); + } + else + { + if (addrlen1 != sizeof (struct sockaddr_in)) { - fprintf (stderr, "parsing IP address mask (%s) failed\n",slash); - exit (1); + return parse_error (ctx, "Inconsistent addr/mask structures"); } + return 0; } - else + } + else + { /* no dot, assume this is a prefix length */ + int preflen; + char *int_end; + preflen = strtol (start, &int_end, 10); + if (int_end == start || int_end < end || preflen < 0 || preflen > 32) { - ((struct sockaddr_in *) &sctx->mask)->sin_family = AF_INET; - inet_aton ("255.255.255.255", &(((struct sockaddr_in *) &sctx->mask)->sin_addr)); + return parse_error (ctx, "Bogus prefix length"); } - /* fprintf (stderr, "parsing IP address (%s)\n",tmp_s); */ - ((struct sockaddr_in *) &sctx->source)->sin_family = AF_INET; - if (inet_aton (tmp_s, &((struct sockaddr_in *) &sctx->source)->sin_addr) == 0) - { - fprintf (stderr, "parsing IP address (%s) failed\n",tmp_s); - exit (1); + bzero (maskp, sizeof (struct sockaddr_in)); + ((struct sockaddr_in *)maskp)->sin_family = AF_INET; + set_ipv4_netmask((struct sockaddr_in *)maskp, preflen); + return 0; + } + } + else if (addrp->ss_family == AF_INET6) + { + int preflen; + char *int_end; + preflen = strtol (start, &int_end, 10); + if (int_end == start || int_end < end || preflen < 0 || preflen > 128) + { + return parse_error (ctx, "Bogus prefix length"); + } + bzero (maskp, sizeof (struct sockaddr_in6)); + ((struct sockaddr_in6 *)maskp)->sin6_family = AF_INET6; + set_ipv6_netmask((struct sockaddr_in6 *)maskp, preflen); + return 0; + } + else + { + return parse_error (ctx, "Unsupported address family"); + } +} + +static int +set_default_mask (const struct samplicator_context *ctx, + struct sockaddr_storage *maskp, + struct sockaddr_storage *addrp) +{ + if (addrp->ss_family == AF_INET) + { + bzero (maskp, sizeof (struct sockaddr_in)); + maskp->ss_family = AF_INET; + memset (&((struct sockaddr_in *) maskp)->sin_addr, 0xff, 4); + return 0; + } + else if (addrp->ss_family == AF_INET6) + { + bzero (maskp, sizeof (struct sockaddr_in6)); + maskp->ss_family = AF_INET6; + memset (&((struct sockaddr_in6 *) maskp)->sin6_addr, 0xff, 16); + return 0; + } + return parse_error (ctx, "Unknown address family %d", addrp->ss_family); +} + +static int +parse_addr_mask (start, end, ctx, addrp, maskp, addrlenp) + const char *start, *end; + const struct samplicator_context *ctx; + struct sockaddr_storage *addrp; + struct sockaddr_storage *maskp; + socklen_t *addrlenp; +{ + const char *c, *slash = 0; + + c = start; + while (c < end && *c != '/') + ++c; + if (c < end) + slash = c; + + if (parse_addr (start, slash == 0 ? end : slash, ctx, addrp, addrlenp) == -1) + return -1; + + if (slash != 0) + { + if (parse_mask (slash+1, end, ctx, maskp, addrp) == -1) + return -1; + } + else + set_default_mask (ctx, maskp, addrp); + return 0; +} + +/* + parse_line (ctx, start, end) + + Parse a single line of configuration file. + +*/ +static int +parse_line (ctx, start, end) + struct samplicator_context *ctx; + char *start; + const char *end; +{ + struct source_context *sctx; + int argc; + const char *argv[MAX_PEERS]; + const char *c, *e; + const char *lhs_start, *lhs_end; + const char *rhs_start; + + /* move end before the start of any comment at the end of the line, + and before any whitepace preceding such a comment or EOL. */ + for (c = start; c < end && *c != '#'; ++c) ; + if (c < end) + end = c; + while (end > start && isspace (*(end-1))) + --end; + if (start == end) + return 0; /* empty line; skip. */ + + /* non-empty lines should look like this: + + ipadd[/mask]: dest[:port[/freq][,ttl]] dest2[:port2[/freq2][,ttl2]]... + + The problematic case is where the ipadd is an IPv6 address, + because IPv6 addresses usually contain colons. We insist on the + convention that IPv6 addresses be enclosed in brackets. In + addition, for IPv6 we only accept prefix lengths, not arbitrary + netmasks. + + [2001:db0:0:1::]/64: [2001:db0:0:2::3]:8000 [2001:db0:0:2::4]:8000 + */ + + c = start; + while (c < end && isspace (*c)) + ++c; + lhs_start = c; + if (*c == '[') { + while (c < end && *c != ']') + ++c; + while (c < end && *c != ':') + ++c; + } else { + c = start; + while (c < end && *c != ':') + ++c; + } + /* Now c either points at the colon that separates the left-hand + address/mask from the right hand destinations, or c is equal to + end because such a colon was not found. */ + + if (c < end) + { + lhs_end = c; + ++c; /* skip colon */ + while (c < end && isspace (*c)) + ++c; + rhs_start = c; + sctx = calloc (1, sizeof (struct source_context)); + + if (parse_addr_mask (lhs_start, lhs_end, ctx, + &sctx->source, &sctx->mask, &sctx->addrlen) != 0) + return -1; + + argc = 0; + while (c < end) + { + while (c < end && isspace (*c)) + c++; + if (c >= end) break; + e = c; + while((*e != 0) && !isspace ((int) *e)) + e++; + argv[argc++] = copy_string_start_end (c, e); + c = e; + if (c < end) + c++; + } + if (argc > 0) + { + if (parse_receivers (argc, argv, ctx, sctx) == -1) + { + return -1; } + } + } + else + { + return parse_error (ctx, "Missing colon"); + } + return 0; +} - /* - fprintf (stderr, "parsed into %s/", - inet_ntoa(sctx->source)); - fprintf (stderr, "%s\n", - inet_ntoa(sctx->mask)); - */ +static int +parse_receiver (struct receiver *receiverp, + const char *arg, + struct samplicator_context *ctx) +{ + const char *start, *end; + const char *host_start, *host_end; + char portspec[NI_MAXSERV]; + struct addrinfo hints, *res; + int result; + + receiverp->flags = ctx->default_receiver_flags; + receiverp->freqcount = 0; + receiverp->freq = 1; + receiverp->ttl = DEFAULT_TTL; + + start = arg; end = start + strlen (arg); + while (start < end && isspace (*start)) + ++start; + while (start < end && isspace (*(end-1))) + --end; + + if (start < end && *start == '[') + { + host_end = host_start = start+1; + while (host_end < end && *host_end != ']') + ++host_end; + if (host_end == end) + { + return parse_error (ctx, "Missing closing bracket"); + } + start = host_end+1; + } + else + { + host_end = host_start = start; + while (host_end < end && *host_end != PORT_SEPARATOR) + ++host_end; + start = host_end; + } + + /* extract the port part */ + if (*start == PORT_SEPARATOR) + { + const char *port_start, *port_end; + + ++start; + port_end = port_start = start; + while (port_end < end && *port_end != FREQ_SEPARATOR) + ++port_end; + if (port_end < end) + { + const char *freq_start, *freq_end; + freq_end = freq_start = port_end + 1; - argc = 0; - while (*c != 0) + /* extract the frequency part */ + while (freq_end < end && *freq_end != TTL_SEPARATOR) + ++freq_end; + + if (freq_start < freq_end) { - while ((*c != 0) && isspace ((int) *c)) - c++; - if (*c == 0 ) break; - - e = c; - while((*e != 0) && !isspace ((int) *e)) - e++; - argv[argc++] = c; - c = e; - if (*c != 0) - c++; - *e = 0; + int freq; + char *freq_parse_end; + + freq = strtol (freq_start, &freq_parse_end, 10); + if (freq_parse_end == freq_start + || freq_parse_end != freq_end + || freq < 1) + { + return parse_error (ctx, "Illegal frequency .*s", + freq_end-freq_start, freq_start); + } + else + { + receiverp->freq = freq; + } } - if (argc > 0) + if (freq_end < end) { - if (parse_receivers (argc, argv, ctx, sctx) == -1) + int ttl; + const char *ttl_start = freq_end + 1; + const char *ttl_end = end; + char *ttl_parse_end; + + ttl = strtol (ttl_start, &ttl_parse_end, 10); + if (ttl_parse_end == ttl_start + || ttl_parse_end != ttl_end + || ttl < 1 || ttl > 255) { - usage (argv[0]); - exit (1); + return parse_error (ctx, "Illegal TTL"); + } + else + { + receiverp->ttl = ttl; } } } + if (port_end - port_start >= NI_MAXSERV) + { + return parse_error (ctx, "Service name/port number (%.*s) too long", + port_end-port_start, port_start); + } + strncpy (portspec, port_start, port_end-port_start); + portspec[port_end-port_start] = 0; } - fclose (cf); + else + strcpy (portspec, FLOWPORT); + + init_hints_from_preferences (&hints, ctx); + { + char *tmp_buf = copy_string_start_end (host_start, host_end); + if (tmp_buf == 0) + { + return parse_error (ctx, "Out of memory"); + } + result = getaddrinfo (tmp_buf, portspec, &hints, &res); + if (result != 0) + { + return parse_error (ctx, "Parsing IP address (%s with port spec %s) failed: %s", + tmp_buf, portspec, gai_strerror (result)); + } + memcpy (&receiverp->addr, res->ai_addr, res->ai_addrlen); + receiverp->addrlen = res->ai_addrlen; + return 0; + } +} + +int +parse_receivers (argc, argv, ctx, sctx) + int argc; + const char **argv; + struct samplicator_context *ctx; + struct source_context *sctx; +{ + int i; + + /* allocate for argc receiver entries */ + sctx->nreceivers = argc; + + if (!(sctx->receivers = (struct receiver*) calloc (sctx->nreceivers, sizeof (struct receiver)))) { + return parse_error (ctx, "Out of memory"); + } + + /* fill in receiver entries */ + for (i = 0; i < argc; ++i) + { + if (parse_receiver (&sctx->receivers[i], argv[i], ctx) != 0) + { + return -1; + } + } + if (ctx->sources == NULL) + { + ctx->sources = sctx; + } + else + { + struct source_context *ptr; + for (ptr = ctx->sources; ptr->next != NULL; ptr = ptr->next); + ptr->next = sctx; + } return 0; } @@ -174,29 +662,33 @@ parse_args (argc, argv, ctx) return -1; } + ctx->config_file_name = ""; + ctx->config_file_lineno = 1; sctx->nreceivers = 0; ctx->sources = sctx; sctx->next = (struct source_context *) NULL; ctx->sockbuflen = DEFAULT_SOCKBUFLEN; - ctx->faddr.s_addr = htonl (INADDR_ANY); - ctx->fport = FLOWPORT; + ctx->faddr_spec = 0; + bzero (&ctx->faddr, sizeof ctx->faddr); + ctx->fport_spec = FLOWPORT; ctx->debug = 0; + ctx->ipv4_only = 0; + ctx->ipv6_only = 0; ctx->fork = 0; ctx->pid_file = (const char *) 0; ctx->sources = 0; - ctx->defaultflags = pf_CHECKSUM; + ctx->default_receiver_flags = pf_CHECKSUM; /* assume that command-line supplied receivers want to get all data */ - ((struct sockaddr_in *) &sctx->source)->sin_family = AF_INET; + sctx->source.ss_family = AF_INET; ((struct sockaddr_in *) &sctx->source)->sin_addr.s_addr = 0; - ((struct sockaddr_in *) &sctx->mask)->sin_family = AF_INET; ((struct sockaddr_in *) &sctx->mask)->sin_addr.s_addr = 0; sctx->tx_delay = 0; optind = 1; - while ((i = getopt (argc, (char **) argv, "hb:d:m:p:s:x:c:fSn")) != -1) + while ((i = getopt (argc, (char **) argv, "hb:d:m:p:s:x:c:fSn46")) != -1) { switch (i) { @@ -207,40 +699,25 @@ parse_args (argc, argv, ctx) ctx->debug = atoi (optarg); break; case 'n': /* no UDP checksums */ - ctx->defaultflags &= ~pf_CHECKSUM; + ctx->default_receiver_flags &= ~pf_CHECKSUM; break; case 'p': /* flow port */ - ctx->fport = atoi (optarg); - if (ctx->fport < 0 - || ctx->fport > 65535) - { - fprintf (stderr, - "Illegal receive port %d - \ -should be between 0 and 65535\n", - ctx->fport); - usage (argv[0]); - exit (1); - } + ctx->fport_spec = optarg; break; case 'm': /* make PID file */ ctx->pid_file = optarg; break; case 's': /* flow address */ - if (inet_aton (optarg, &ctx->faddr) == 0) - { - fprintf (stderr, "parsing IP address (%s) failed\n", optarg); - usage (argv[0]); - exit (1); - } + ctx->faddr_spec = optarg; break; case 'x': /* transmit delay */ sctx->tx_delay = atoi (optarg); break; case 'S': /* spoof */ - ctx->defaultflags |= pf_SPOOF; + ctx->default_receiver_flags |= pf_SPOOF; break; case 'c': /* config file */ - if (read_cf_file(optarg, ctx) != 0) + if (read_cf_file (optarg, ctx) != 0) { return -1; } @@ -252,10 +729,17 @@ should be between 0 and 65535\n", usage (argv[0]); exit (0); break; - default: - usage (argv[0]); - exit (1); + case '4': + ctx->ipv6_only = 0; + ctx->ipv4_only = 1; + break; + case '6': + ctx->ipv4_only = 0; + ctx->ipv6_only = 1; break; + default: + short_usage (argv[0]); + return -1; } } @@ -263,177 +747,13 @@ should be between 0 and 65535\n", { if (parse_receivers (argc - optind, argv + optind, ctx, sctx) == -1) { - usage (argv[0]); - exit (1); - } - } - return 0; -} - -int -parse_receivers (argc, argv, ctx, sctx) - int argc; - const char **argv; - struct samplicator_context *ctx; - struct source_context *sctx; -{ - int i; - char tmp_buf[256]; - static int cooked_sock = -1; - static int raw_sock = -1; - char *c; - struct source_context *ptr; - - /* allocate for argc receiver entries */ - sctx->nreceivers = argc; - - if (!(sctx->receivers = (struct receiver*) calloc (sctx->nreceivers, sizeof (struct receiver)))) { - fprintf(stderr, "calloc(): failed.\n"); - return -1; - } - - /* fill in receiver entries */ - for (i = 0; i < argc; ++i) - { - sctx->receivers[i].flags = ctx->defaultflags; - sctx->receivers[i].addrlen = sizeof (struct sockaddr_in); - - if (strlen (argv[i]) > 255) - { - fprintf (stderr, "ouch!\n"); - return -1; - } - strcpy (tmp_buf, argv[i]); - - /* skip to end or port seperator */ - for (c = tmp_buf; (*c != PORT_SEPARATOR) && (*c); ++c); - - /* extract the port part */ - if (*c == PORT_SEPARATOR) - { - int port; - *c = 0; - ++c; - port = atoi(c); - if (port < 0 || port > 65535) - { - fprintf (stderr, "Illegal destination port %d - \ -should be between 0 and 65535\n", port); - return -1; - } - ((struct sockaddr_in *) &sctx->receivers[i].addr)->sin_port = htons (port); - } - else - ((struct sockaddr_in *) &sctx->receivers[i].addr)->sin_port = htons (FLOWPORT); - - /* extract the frequency part */ - sctx->receivers[i].freqcount = 0; - sctx->receivers[i].freq = 1; - for (; (*c != FREQ_SEPARATOR) && (*c); ++c) - if (*c == TTL_SEPARATOR) goto TTL; - if (*c == FREQ_SEPARATOR) - { - *c = 0; - ++c; - sctx->receivers[i].freq = atoi(c); - } - - /* printf("Frequency: %d\n", sctx->receivers[i].freq); */ - - /* extract the TTL part */ - for (; (*c != TTL_SEPARATOR) && (*c); ++c); - TTL: - if ((*c == TTL_SEPARATOR) && (*(c+1) > 0)) - { - *c = 0; - ++c; - sctx->receivers[i].ttl = atoi (c); - if (sctx->receivers[i].ttl < 1 - || sctx->receivers[i].ttl > 255) - { - fprintf (stderr, - "Illegal value %d for TTL - should be between 1 and 255.\n", - sctx->receivers[i].ttl); - return -1; - } - } - else - sctx->receivers[i].ttl = DEFAULT_TTL; - - /* extract the ip address part */ - if (inet_aton (tmp_buf, & ((struct sockaddr_in *) &sctx->receivers[i].addr)->sin_addr) == 0) - { - fprintf (stderr, "parsing IP address (%s) failed\n", tmp_buf); + short_usage (argv[0]); return -1; } - - sctx->receivers[i].addrlen = sizeof (struct sockaddr_in); - sctx->receivers[i].addr.ss_family = AF_INET; - - if (sctx->receivers[i].flags & pf_SPOOF) - { - if (raw_sock == -1) - { - if ((raw_sock = make_raw_udp_socket (ctx->sockbuflen)) < 0) - { - if (errno == EPERM) - { - fprintf (stderr, "Not enough privilege for -S option---try again as root.\n"); - } - else - { - fprintf (stderr, "creating raw socket: %s\n", strerror(errno)); - } - return -1; - } - } - sctx->receivers[i].fd = raw_sock; - } - else - { - if (cooked_sock == -1) - { - if ((cooked_sock = make_cooked_udp_socket (ctx->sockbuflen)) < 0) - { - fprintf (stderr, "creating cooked socket: %s\n", - strerror(errno)); - return -1; - } - } - sctx->receivers[i].fd = cooked_sock; - } - } - if (ctx->sources == NULL) - { - ctx->sources = sctx; } - else - { - for (ptr = ctx->sources; ptr->next != NULL; ptr = ptr->next); - ptr->next = sctx; - } return 0; } -static int -make_cooked_udp_socket (sockbuflen) - long sockbuflen; -{ - int s; - if ((s = socket (PF_INET, SOCK_DGRAM, 0)) == -1) - return s; - if (sockbuflen != -1) - { - if (setsockopt (s, SOL_SOCKET, SO_SNDBUF, - (char *) &sockbuflen, sizeof sockbuflen) == -1) - { - fprintf (stderr, "setsockopt(SO_SNDBUF,%ld): %s\n", - sockbuflen, strerror (errno)); - } - } - return s; -} - void short_usage (progname) const char *progname; { @@ -448,7 +768,7 @@ usage (progname) \n\ Supported options:\n\ \n\ - -p UDP port to accept flows on (default %d)\n\ + -p UDP port to accept flows on (default %s)\n\ -s
Interface address to accept flows on (default any)\n\ -d debug level\n\ -b set socket buffer size (default %lu)\n\ @@ -465,7 +785,7 @@ Specifying receivers:\n\ A.B.C.D[%cport[%cfreq][%cttl]]...\n\ where:\n\ A.B.C.D is the receiver's IP address\n\ - port is the UDP port to send to (default %d)\n\ + port is the UDP port to send to (default %s)\n\ freq is the sampling rate (default 1)\n\ ttl is the outgoing packets' TTL value (default %d)\n\ \n\ diff --git a/samplicate.c b/samplicate.c index 2c4cced..44acbed 100644 --- a/samplicate.c +++ b/samplicate.c @@ -9,6 +9,7 @@ #endif #include #include +#include #ifdef HAVE_ARPA_INET_H # include #endif @@ -30,38 +31,21 @@ #ifdef HAVE_CTYPE_H # include #endif -#ifndef HAVE_INET_ATON -extern int inet_aton (const char *, struct in_addr *); -#endif #include "samplicator.h" #include "read_config.h" #include "rawsend.h" +#include "inet.h" #define PDU_SIZE 1500 static int send_pdu_to_receiver (struct receiver *, const void *, size_t, - struct sockaddr_in *); + struct sockaddr *); static int init_samplicator (struct samplicator_context *); static int samplicate (struct samplicator_context *); - -/* Work around a GCC compatibility problem with respect to the - inet_ntoa() system function */ -#undef inet_ntoa -#define inet_ntoa(x) my_inet_ntoa(&(x)) - -static const char * -my_inet_ntoa (const struct in_addr *in) -{ - unsigned a = ntohl (in->s_addr); - static char buffer[16]; - sprintf (buffer, "%d.%d.%d.%d", - (a >> 24) & 0xff, - (a >> 16) & 0xff, - (a >> 8) & 0xff, - a & 0xff); - return buffer; -} +static int make_udp_socket (long, int, int); +static int make_recv_socket (struct samplicator_context *); +static int make_send_sockets (struct samplicator_context *); int main (argc, argv) @@ -70,9 +54,9 @@ main (argc, argv) { struct samplicator_context ctx; - if (parse_args (argc, argv, &ctx) == -1) + if (parse_args (argc, (const char **) argv, &ctx) == -1) { - exit (-1); + exit (1); } if (init_samplicator (&ctx) == -1) exit (1); @@ -81,6 +65,30 @@ main (argc, argv) exit (0); } +static int +daemonize (void) +{ + pid_t pid; + + pid = fork(); + if (pid == -1) + { + fprintf (stderr, "failed to fork process\n"); + exit (1); + } + else if (pid > 0) + { /* kill the parent */ + exit (0); + } + else + { /* end interaction with shell */ + fclose (stdin); + fclose (stdout); + fclose (stderr); + } + return 0; +} + static int write_pid_file (const char *filename) { @@ -108,78 +116,124 @@ write_pid_file (const char *filename) return 0; } -/* init_samplicator: prepares receiving socket */ +/* + make_recv_socket(ctx) + + Create the socket on which samplicator receives its packets. + + There can only be one. This will be either a wildcard socket + listening on a specific port on all interfaces, or a socket bound to + a specific address (and, thus, interface). + + The creation of this socket is affected by the preferences in CTX: + + CTX->faddr_spec + This is either a null pointer, meaning that a wildcard socket + should be created, or a hostname or address literal specifying + which address to listen on. If this maps to multiple addresses, + the socket will be bound to the first of those addresses that it + can be bound to, in the order returned by getaddrinfo(). + + CTX->fport_spec + This must be a string, and specifies the port number or service + name on which the socket will listen. + + CTX->ipv4_only + If this is non-zero, the socket will be an IPv4 socket. An error + will be signaled if faddr_spec doesn't map to an IPv4 address. + + CTX->ipv6_only + If non zero, only IPv6 addresses will be considered. + + If ipv4_only and ipv6_only are both zero, and faddr_spec is also + null, then the receive socket will be an IPv6 socket bound to a + specific port on all interfaces. This socket will be able to receive + packets over both IPv6 and IPv4. + + CTX->sockbuflen + If this is non-zero, the function will try to set the socket's + receiver buffer size to this many bytes. If setting the socket + buffer fails, a warning will be printed, but the socket will still + be created. The idea here is that a socket with an incorrect + buffer size is more useful than no socket at all, although some + people may differ. + + RETURN VALUE + + If a socket could be created and bound, this function will return + zero. If this was not possible, the function will produce an error + message and return -1. + */ static int -init_samplicator (ctx) +make_recv_socket (ctx) struct samplicator_context *ctx; { - struct sockaddr_in local_address; - - /* setup to receive flows */ - bzero (&local_address, sizeof local_address); - local_address.sin_family = AF_INET; - local_address.sin_addr.s_addr = ctx->faddr.s_addr; - local_address.sin_port = htons (ctx->fport); + struct addrinfo hints, *res; + int result; - if ((ctx->fsockfd = socket (AF_INET, SOCK_DGRAM, 0)) < 0) + init_hints_from_preferences (&hints, ctx); + if ((result = getaddrinfo (ctx->faddr_spec, ctx->fport_spec, &hints, &res)) != 0) { - fprintf (stderr, "socket(): %s\n", strerror (errno)); + fprintf (stderr, "Failed to resolve IP address/port (%s:%s): %s\n", + ctx->faddr_spec, ctx->fport_spec, gai_strerror (result)); return -1; } - if (setsockopt (ctx->fsockfd, SOL_SOCKET, SO_RCVBUF, - (char *) &ctx->sockbuflen, sizeof ctx->sockbuflen) == -1) + for (; res; res = res->ai_next) { - fprintf (stderr, "setsockopt(SO_RCVBUF,%ld): %s\n", - ctx->sockbuflen, strerror (errno)); - } - if (bind (ctx->fsockfd, - (struct sockaddr*)&local_address, sizeof local_address) < 0) - { - fprintf (stderr, "bind(): %s\n", strerror (errno)); - return -1; + if ((ctx->fsockfd = socket (res->ai_family, SOCK_DGRAM, 0)) < 0) + { + fprintf (stderr, "socket(): %s\n", strerror (errno)); + break; + } + if (setsockopt (ctx->fsockfd, SOL_SOCKET, SO_RCVBUF, + (char *) &ctx->sockbuflen, sizeof ctx->sockbuflen) == -1) + { + fprintf (stderr, "Warning: setsockopt(SO_RCVBUF,%ld) failed: %s\n", + ctx->sockbuflen, strerror (errno)); + } + if (bind (ctx->fsockfd, + (struct sockaddr*)res->ai_addr, res->ai_addrlen) < 0) + { + fprintf (stderr, "bind(): %s\n", strerror (errno)); + break; + } + ctx->fsockaddrlen = res->ai_addrlen; + return 0; } - return 0; + return -1; } +/* init_samplicator: prepares receiving socket */ static int -samplicate (ctx) +init_samplicator (ctx) struct samplicator_context *ctx; { - unsigned char fpdu[PDU_SIZE]; - struct sockaddr_in remote_address; struct source_context *sctx; - pid_t pid; - int i, n; - socklen_t len; + int i; + + if (make_recv_socket (ctx) != 0) + { + return -1; + } /* check is there actually at least one configured data receiver */ for (i = 0, sctx = ctx->sources; sctx != NULL; sctx = sctx->next) - if(sctx->nreceivers > 0) i += sctx->nreceivers; + { + i += sctx->nreceivers; + } if (i == 0) { fprintf(stderr, "You have to specify at least one receiver, exiting\n"); - exit(1); + return -1; } - if (ctx->fork == 1) + if (make_send_sockets (ctx) != 0) { - pid = fork(); - if (pid == -1) - { - fprintf (stderr, "failed to fork process\n"); - exit (1); - } - else if (pid > 0) - { /* kill the parent */ - exit (0); - } - else - { /* end interaction with shell */ - fclose(stdin); - fclose(stdout); - fclose(stderr); - } + return -1; } + + if (ctx->fork == 1) + daemonize (); if (ctx->pid_file != 0) { if (write_pid_file (ctx->pid_file) != 0) @@ -187,15 +241,100 @@ samplicate (ctx) return -1; } } + return 0; +} + +static int +match_addr_p (struct sockaddr *input_generic, + struct sockaddr *addr_generic, + struct sockaddr *mask_generic) +{ +#define SPECIALIZE(VAR, STRUCT) \ + struct STRUCT *VAR = (struct STRUCT *) VAR ## _generic + + if (addr_generic->sa_family == AF_INET) + { + SPECIALIZE (addr, sockaddr_in); + SPECIALIZE (mask, sockaddr_in); + if (addr->sin_addr.s_addr == 0) + return 1; + if (input_generic->sa_family == AF_INET) + { + SPECIALIZE (input, sockaddr_in); + if ((input->sin_addr.s_addr & mask->sin_addr.s_addr) == addr->sin_addr.s_addr) + return 1; + return 0; + } + else if (input_generic->sa_family == AF_INET6) + { + SPECIALIZE (input, sockaddr_in6); + if (IN6_IS_ADDR_V4MAPPED (&input->sin6_addr)) + { + abort (); /* TODO */ + } + else + { + return 0; + } + } + else + abort (); /* Unexpected address family */ + } + else + { + SPECIALIZE (addr, sockaddr_in6); + SPECIALIZE (mask, sockaddr_in6); + + if (IN6_IS_ADDR_UNSPECIFIED (&mask->sin6_addr)) + { + return 1; + } + else if (input_generic->sa_family == AF_INET) + { + abort (); + } + else if (input_generic->sa_family == AF_INET6) + { + SPECIALIZE (input, sockaddr_in6); + unsigned k; + for (k = 0; k < 16; ++k) + { + if ((input->sin6_addr.s6_addr[k] & mask->sin6_addr.s6_addr[k]) + != addr->sin6_addr.s6_addr[k]) + { + return 0; + } + return 1; + } + abort (); + } + else + abort (); /* Unexpected address family */ + } +#undef SPECIALIZE +} + +static int +samplicate (ctx) + struct samplicator_context *ctx; +{ + unsigned char fpdu[PDU_SIZE]; + struct sockaddr_storage remote_address; + struct source_context *sctx; + unsigned i; + int n; + socklen_t addrlen; + char host[INET6_ADDRSTRLEN]; + char serv[6]; while (1) { - len = sizeof remote_address; + addrlen = sizeof remote_address; if ((n = recvfrom (ctx->fsockfd, (char*)fpdu, sizeof (fpdu), 0, - (struct sockaddr*) &remote_address, &len)) == -1) + (struct sockaddr *) &remote_address, &addrlen)) == -1) { - fprintf(stderr, "recvfrom(): %s\n", strerror(errno)); + fprintf (stderr, "recvfrom(): %s\n", strerror(errno)); exit (1); } if (n > PDU_SIZE) @@ -204,59 +343,111 @@ samplicate (ctx) n-PDU_SIZE); n = PDU_SIZE; } - if (len != sizeof remote_address) + if (addrlen != ctx->fsockaddrlen) { fprintf (stderr, "recvfrom() return address length %lu - expected %lu\n", - (unsigned long) len, (unsigned long) sizeof remote_address); + (unsigned long) addrlen, (unsigned long) ctx->fsockaddrlen); exit (1); } if (ctx->debug) { - fprintf (stderr, "received %d bytes from %s:%d\n", - n, - inet_ntoa (remote_address.sin_addr), - (int) ntohs (remote_address.sin_port)); + if (getnameinfo ((struct sockaddr *) &remote_address, addrlen, + host, INET6_ADDRSTRLEN, + serv, 6, + NI_NUMERICHOST|NI_NUMERICSERV) == -1) + { + strcpy (host, "???"); + strcpy (serv, "?????"); + } + fprintf (stderr, "received %d bytes from %s:%s\n", n, host, serv); } - - for(sctx = ctx->sources; sctx != NULL; sctx = sctx->next) + for (sctx = ctx->sources; sctx != NULL; sctx = sctx->next) { - if ((((struct sockaddr_in *) &sctx->source)->sin_addr.s_addr == 0) - || ((remote_address.sin_addr.s_addr & ((struct sockaddr_in *) &sctx->mask)->sin_addr.s_addr) - == ((struct sockaddr_in *) &sctx->source)->sin_addr.s_addr)) - for (i = 0; i < sctx->nreceivers; ++i) - { - if (sctx->receivers[i].freqcount == 0) - { - if (send_pdu_to_receiver (& (sctx->receivers[i]), fpdu, n, &remote_address) - == -1) - { - fprintf (stderr, "sending datagram to %s:%d failed: %s\n", - inet_ntoa (((struct sockaddr_in *) &sctx->receivers[i].addr)->sin_addr), - (int) ntohs (((struct sockaddr_in *) &sctx->receivers[i].addr)->sin_port), - strerror (errno)); - } - else if (ctx->debug) - { - fprintf (stderr, " sent to %s:%d\n", - inet_ntoa (((struct sockaddr_in *) &sctx->receivers[i].addr)->sin_addr), - (int) ntohs (((struct sockaddr_in *) &sctx->receivers[i].addr)->sin_port)); - } - sctx->receivers[i].freqcount = sctx->receivers[i].freq-1; - } - else - { - --sctx->receivers[i].freqcount; - } - if (sctx->tx_delay) - usleep (sctx->tx_delay); - } + if (match_addr_p ((struct sockaddr *) &remote_address, + (struct sockaddr *) &sctx->source, + (struct sockaddr *) &sctx->mask)) + { + sctx->matched_packets += 1; + sctx->matched_octets += n; + + for (i = 0; i < sctx->nreceivers; ++i) + { + struct receiver *receiver = &(sctx->receivers[i]); + + if (receiver->freqcount == 0) + { + if (send_pdu_to_receiver (receiver, fpdu, n, (struct sockaddr *) &remote_address) + == -1) + { + receiver->out_errors += 1; + if (getnameinfo ((struct sockaddr *) &receiver->addr, + receiver->addrlen, + host, INET6_ADDRSTRLEN, + serv, 6, + NI_NUMERICHOST|NI_NUMERICSERV) + == -1) + { + strcpy (host, "???"); + strcpy (serv, "?????"); + } + fprintf (stderr, "sending datagram to %s:%s failed: %s\n", + host, serv, strerror (errno)); + } + else + { + receiver->out_packets += 1; + receiver->out_octets += n; + + if (ctx->debug) + { + if (getnameinfo ((struct sockaddr *) &receiver->addr, + receiver->addrlen, + host, INET6_ADDRSTRLEN, + serv, 6, + NI_NUMERICHOST|NI_NUMERICSERV) + == -1) + { + strcpy (host, "???"); + strcpy (serv, "?????"); + } + fprintf (stderr, " sent to %s:%s\n", host, serv); + } + } + receiver->freqcount = receiver->freq-1; + } + else + { + receiver->freqcount -= 1; + } + if (sctx->tx_delay) + usleep (sctx->tx_delay); + } + } else { if (ctx->debug) { - fprintf (stderr, "Not matching %s/", inet_ntoa(((struct sockaddr_in *) &sctx->source)->sin_addr)); - fprintf (stderr, "%s\n", inet_ntoa(((struct sockaddr_in *) &sctx->mask)->sin_addr)); + if (getnameinfo ((struct sockaddr *) &sctx->source, + sctx->addrlen, + host, INET6_ADDRSTRLEN, + 0, 0, + NI_NUMERICHOST|NI_NUMERICSERV) + == -1) + { + strcpy (host, "???"); + } + fprintf (stderr, "Not matching %s/", host); + if (getnameinfo ((struct sockaddr *) &sctx->mask, + sctx->addrlen, + host, INET6_ADDRSTRLEN, + 0, 0, + NI_NUMERICHOST|NI_NUMERICSERV) + == -1) + { + strcpy (host, "???"); + } + fprintf (stderr, "%s\n", host); } } } @@ -268,7 +459,7 @@ send_pdu_to_receiver (receiver, fpdu, length, source_addr) struct receiver * receiver; const void * fpdu; size_t length; - struct sockaddr_in * source_addr; + struct sockaddr * source_addr; { if (receiver->flags & pf_SPOOF) { @@ -282,7 +473,80 @@ send_pdu_to_receiver (receiver, fpdu, length, source_addr) else { return sendto (receiver->fd, (char*) fpdu, length, 0, - (struct sockaddr*) &receiver->addr, - sizeof (struct sockaddr_in)); + (struct sockaddr*) &receiver->addr, receiver->addrlen); + } +} + +static int +make_cooked_udp_socket (long sockbuflen, int af) +{ + int s; + if ((s = socket (af == AF_INET ? PF_INET : PF_INET6, SOCK_DGRAM, 0)) == -1) + return s; + if (sockbuflen != -1) + { + if (setsockopt (s, SOL_SOCKET, SO_SNDBUF, + (char *) &sockbuflen, sizeof sockbuflen) == -1) + { + fprintf (stderr, "setsockopt(SO_SNDBUF,%ld): %s\n", + sockbuflen, strerror (errno)); + } } + return s; +} + +static int +make_udp_socket (long sockbuflen, int raw, int af) +{ + return raw + ? make_raw_udp_socket (sockbuflen, af) + : make_cooked_udp_socket (sockbuflen, af); +} + +static int +make_send_sockets (struct samplicator_context *ctx) +{ + /* Array of four sockets: + + First index: cooked(0)/raw(1) + Second index: IPv4(0)/IPv6(1) + + At a maximum, we need one socket of each kind. These sockets can + be used by multiple receivers of the same type. + */ + int socks[2][2] = { { -1, -1 }, { -1, -1 } }; + + struct source_context *sctx; + unsigned i; + + for (sctx = ctx->sources; sctx != 0; sctx = sctx->next) + { + for (i = 0; i < sctx->nreceivers; ++i) + { + struct receiver *receiver = &sctx->receivers[i]; + int af = receiver->addr.ss_family; + int af_index = af == AF_INET ? 0 : 1; + int spoof_p = receiver->flags & pf_SPOOF; + + if (socks[spoof_p][af_index] == -1) + { + if ((socks[spoof_p][af_index] = make_udp_socket (ctx->sockbuflen, spoof_p, af)) < 0) + { + if (spoof_p && errno == EPERM) + { + fprintf (stderr, "Not enough privilege for -S option---try again as root.\n"); + return -1; + } + else + { + fprintf (stderr, "Error creating%s socket: %s\n", + spoof_p ? " raw" : "", strerror (errno)); + } + return -1; + } + } + receiver->fd = socks[spoof_p][af_index]; + } + } + return 0; } diff --git a/samplicator.h b/samplicator.h index 3a2ebac..f83ce08 100644 --- a/samplicator.h +++ b/samplicator.h @@ -15,16 +15,26 @@ enum receiver_flags }; struct samplicator_context { - struct source_context *sources; - struct in_addr faddr; - int fport; + struct source_context *sources; + const char *faddr_spec; + struct sockaddr_storage faddr; + const char *fport_spec; long sockbuflen; int debug; int fork; + int ipv4_only; + int ipv6_only; const char *pid_file; - enum receiver_flags defaultflags; + enum receiver_flags default_receiver_flags; int fsockfd; + socklen_t fsockaddrlen; + + const char *config_file_name; + int config_file_lineno; + + /* statistics */ + uint32_t unmatched_packets; }; struct receiver { @@ -36,17 +46,26 @@ struct receiver { int freqcount; int ttl; enum receiver_flags flags; + + /* statistics */ + uint32_t out_packets; + uint32_t out_errors; + uint64_t out_octets; }; struct source_context { struct source_context *next; struct sockaddr_storage source; struct sockaddr_storage mask; + socklen_t addrlen; struct receiver *receivers; unsigned nreceivers; unsigned tx_delay; - int - debug; + int debug; + + /* statistics */ + uint32_t matched_packets; + uint64_t matched_octets; }; #endif /* not _SAMPLICATOR_H_ */