/* $Id$ */ /* * Copyright (c) 2001-2010 Aaron Turner * Copyright (c) 2013-2018 Fred Klassen - AppNeta * * The Tcpreplay Suite of tools is free software: you can redistribute it * and/or modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or with the authors permission any later version. * * The Tcpreplay Suite is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with the Tcpreplay Suite. If not, see . */ #include "config.h" #include "defines.h" #include "common.h" #ifndef HAVE_STRLCPY #include "lib/strlcpy.h" #endif #include #include #include /* required for inet_aton() */ #include #include #include #ifdef DEBUG extern int debug; #endif static tcpr_cidr_t *cidr2cidr(char *); /** * prints to the given fd all the entries in mycidr */ void print_cidr(tcpr_cidr_t * mycidr) { tcpr_cidr_t *cidr_ptr; fprintf(stderr, "Cidr List: "); cidr_ptr = mycidr; while (cidr_ptr != NULL) { /* print it */ fprintf(stderr, "%s/%d, ", get_cidr2name(cidr_ptr, RESOLVE), cidr_ptr->masklen); /* go to the next */ if (cidr_ptr->next != NULL) { cidr_ptr = cidr_ptr->next; } else { break; } } fprintf(stderr, "\n"); } /** * deletes all entries in a cidr and destroys the datastructure */ void destroy_cidr(tcpr_cidr_t * cidr) { if (cidr != NULL) { if (cidr->next != NULL) destroy_cidr(cidr->next); safe_free(cidr); } return; } /** * adds a new tcpr_cidr_t entry to cidrdata */ void add_cidr(tcpr_cidr_t ** cidrdata, tcpr_cidr_t ** newcidr) { tcpr_cidr_t *cidr_ptr; dbg(1, "Running new_cidr()"); if (*cidrdata == NULL) { *cidrdata = *newcidr; } else { cidr_ptr = *cidrdata; while (cidr_ptr->next != NULL) cidr_ptr = cidr_ptr->next; cidr_ptr->next = *newcidr; } } /** * takes in an IP and masklen, and returns a string in * cidr format: x.x.x.x/y. This malloc's memory. */ u_char * ip2cidr(const unsigned long ip, const int masklen) { u_char *network; char mask[3]; network = (u_char *)safe_malloc(20); strlcpy((char *)network, (char *)get_addr2name4(ip, RESOLVE), 20); strcat((char *)network, "/"); if (masklen < 10) { snprintf(mask, 1, "%d", masklen); strncat((char *)network, mask, 1); } else { snprintf(mask, 2, "%d", masklen); strncat((char *)network, mask, 2); } return (network); } /** * Mallocs and sets to sane defaults a tcpr_cidr_t structure */ tcpr_cidr_t * new_cidr(void) { tcpr_cidr_t *newcidr; newcidr = (tcpr_cidr_t *)safe_malloc(sizeof(tcpr_cidr_t)); memset(newcidr, '\0', sizeof(tcpr_cidr_t)); newcidr->masklen = 99; newcidr->next = NULL; return (newcidr); } /** * Creates a new tcpr_cidrmap_t structure. Malloc's memory */ tcpr_cidrmap_t * new_cidr_map(void) { tcpr_cidrmap_t *new; new = (tcpr_cidrmap_t *)safe_malloc(sizeof(tcpr_cidrmap_t)); memset(new, '\0', sizeof(tcpr_cidrmap_t)); new->next = NULL; return (new); } /** * Converts a single cidr (string) in the form of x.x.x.x/y into a * tcpr_cidr_t structure. Will malloc the tcpr_cidr_t structure. */ static tcpr_cidr_t * cidr2cidr(char *cidr) { int count = 0; unsigned int octets[4]; /* used in sscanf */ tcpr_cidr_t *newcidr; char networkip[16], tempoctet[4], ebuf[EBUF_SIZE]; int family; char* p; assert(cidr); assert(strlen(cidr) <= EBUF_SIZE); newcidr = new_cidr(); for (p = cidr; *p; ++p) { if (*p == '#') { *p = ':'; } else if (*p == ']') { *p = 0; break; } } /* * scan it, and make sure it scanned correctly, also copy over the * masklen */ count = sscanf(cidr, "%u.%u.%u.%u/%d", &octets[0], &octets[1], &octets[2], &octets[3], &newcidr->masklen); if (count == 4) { newcidr->masklen = 32; family = AF_INET; } else if (count == 5) { family = AF_INET; } else { p = strstr(cidr, "/"); if (p) { *p = 0; ++p; sscanf(p, "%d", &newcidr->masklen); } else { newcidr->masklen = 128; } if (newcidr->masklen < 0 || newcidr->masklen > 128) goto error; /* skip past the opening [ */ if (*cidr == '[') cidr ++; if (get_name2addr6(cidr, RESOLVE, &newcidr->u.network6) > 0) { family = AF_INET6; } else { goto error; } } if (family == AF_INET) { /* masklen better be 0 =< masklen <= 32 */ if (newcidr->masklen > 32) goto error; /* copy in the ip address */ memset(networkip, '\0', 16); for (count = 0; count < 4; count++) { if (octets[count] > 255) goto error; snprintf(tempoctet, sizeof(octets[count]), "%u", octets[count]); strcat(networkip, tempoctet); /* we don't want a '.' at the end of the last octet */ if (count < 3) strcat(networkip, "."); } /* copy over the network address and return */ #ifdef HAVE_INET_ATON inet_aton(networkip, (struct in_addr *)&newcidr->u.network); #elif HAVE_INET_ADDR newcidr->network = inet_addr(networkip); #endif } else if (family == AF_INET6) { /* Everything's done */ } else { goto error; } newcidr->family = family; return (newcidr); /* we only get here on error parsing input */ error: memset(ebuf, '\0', EBUF_SIZE); strcpy(ebuf, "Unable to parse as a valid CIDR: "); strlcat(ebuf, cidr, EBUF_SIZE); errx(-1, "%s", ebuf); return NULL; } static void mask_cidr6(char **cidrin, char* delim) { if (**cidrin == '[' && *delim == ':') { char *p; ++*cidrin; /* make strtok happy */ for (p = *cidrin; *p && *p != ']'; ++p) { if (*p == ':') { *p = '#'; } } } } /** * parses a list of tcpr_cidr_t's input from the user which should be in the form * of x.x.x.x/y,x.x.x.x/y... * returns 1 for success, or fails to return on failure (exit 1) * since we use strtok to process cidr, it gets zeroed out. */ int parse_cidr(tcpr_cidr_t ** cidrdata, char *cidrin, char *delim) { tcpr_cidr_t *cidr_ptr; /* ptr to current cidr record */ char *network; char *token = NULL; mask_cidr6(&cidrin, delim); /* first iteration of input using strtok */ network = strtok_r(cidrin, delim, &token); *cidrdata = cidr2cidr(network); cidr_ptr = *cidrdata; /* do the same with the rest of the input */ while (1) { if (token) mask_cidr6(&token, delim); network = strtok_r(NULL, delim, &token); /* if that was the last CIDR, then kickout */ if (network == NULL) break; /* next record */ cidr_ptr->next = cidr2cidr(network); cidr_ptr = cidr_ptr->next; } return 1; } /** * parses a pair of IP addresses: : and processes it like: * -N 0.0.0.0/0: -N 0.0.0.0/0: * returns 1 for success or returns 0 on failure * since we use strtok to process optarg, it gets zeroed out */ int parse_endpoints(tcpr_cidrmap_t ** cidrmap1, tcpr_cidrmap_t ** cidrmap2, const char *optarg) { #define NEWMAP_LEN (INET6_ADDRSTRLEN * 2) char *map = NULL, newmap[NEWMAP_LEN]; char *token = NULL; char *string; char *p; int res = 0; string = safe_strdup(optarg); if (*string == '[') { /* ipv6 mode */ memset(newmap, '\0', NEWMAP_LEN); p = strstr(string, "]:["); if (!p) goto done; *p = 0; strlcpy(newmap, "[::/0]:", NEWMAP_LEN); strlcat(newmap, string, NEWMAP_LEN); strlcat(newmap, "]", NEWMAP_LEN); if (! parse_cidr_map(cidrmap1, newmap)) goto done; /* do again with the second IP */ memset(newmap, '\0', NEWMAP_LEN); strlcpy(newmap, "[::/0]:", NEWMAP_LEN); strlcat(newmap, p + 2, NEWMAP_LEN); if (! parse_cidr_map(cidrmap2, newmap)) goto done; } else { /* ipv4 mode */ memset(newmap, '\0', NEWMAP_LEN); map = strtok_r(string, ":", &token); strlcpy(newmap, "0.0.0.0/0:", NEWMAP_LEN); strlcat(newmap, map, NEWMAP_LEN); if (! parse_cidr_map(cidrmap1, newmap)) goto done; /* do again with the second IP */ memset(newmap, '\0', NEWMAP_LEN); map = strtok_r(NULL, ":", &token); strlcpy(newmap, "0.0.0.0/0:", NEWMAP_LEN); strlcat(newmap, map, NEWMAP_LEN); if (! parse_cidr_map(cidrmap2, newmap)) goto done; } /* success */ res = 1; done: safe_free(string); return res; } /** * parses a list of tcpr_cidrmap_t's input from the user which should be in the form * of x.x.x.x/y:x.x.x.x/y,... * IPv6 syntax: [addr/y]:[addr/y],... * returns 1 for success, or returns 0 on failure * since we use strtok to process optarg, it gets zeroed out. */ int parse_cidr_map(tcpr_cidrmap_t **cidrmap, const char *optarg) { tcpr_cidr_t *cidr = NULL; char *map; char *token = NULL, *string; tcpr_cidrmap_t *ptr; int res = 0; string = safe_strdup(optarg); /* first iteration */ map = strtok_r(string, ",", &token); if (! parse_cidr(&cidr, map, ":")) goto done; /* must return a linked list of two */ if (cidr->next == NULL) goto done; /* copy over */ *cidrmap = new_cidr_map(); ptr = *cidrmap; ptr->from = cidr; ptr->to = cidr->next; ptr->from->next = NULL; /* do the same with the reset of the input */ while(1) { map = strtok_r(NULL, ",", &token); if (map == NULL) break; if (! parse_cidr(&cidr, map, ":")) goto done; /* must return a linked list of two */ if (cidr->next == NULL) goto done; /* copy over */ ptr->next = new_cidr_map(); ptr = ptr->next; ptr->from = cidr; ptr->to = cidr->next; ptr->from->next = NULL; } /* success */ res = 1; done: safe_free(string); return res; } /** * checks to see if the ip address is in the cidr * returns 1 for true, 0 for false */ int ip_in_cidr(const tcpr_cidr_t * mycidr, const unsigned long ip) { unsigned long ipaddr = 0, network = 0, mask = 0; int ret = 0; #ifdef DEBUG char netstr[20]; #endif if (mycidr->family != AF_INET) return 0; /* always return 1 if 0.0.0.0/0 */ if (mycidr->masklen == 0 && mycidr->u.network == 0) return 1; mask = ~0; /* turn on all the bits */ /* shift over by the correct number of bits */ mask = mask << (32 - mycidr->masklen); /* apply the mask to the network and ip */ ipaddr = ntohl(ip) & mask; network = htonl(mycidr->u.network) & mask; #ifdef DEBUG /* copy this for debug purposes, since it's not re-entrant */ strlcpy(netstr, get_addr2name4(mycidr->u.network, RESOLVE), 20); #endif /* if they're the same, then ip is in network */ if (network == ipaddr) { #ifdef DEBUG dbgx(1, "The ip %s is inside of %s/%d", get_addr2name4(ip, RESOLVE), netstr, mycidr->masklen); #endif ret = 1; } else { #ifdef DEBUG dbgx(1, "The ip %s is not inside of %s/%d", get_addr2name4(ip, RESOLVE), netstr, mycidr->masklen); #endif ret = 0; } return ret; } static int ip6_addr_is_unspec(const struct tcpr_in6_addr *addr) { return addr->tcpr_s6_addr32[0] == 0 && addr->tcpr_s6_addr32[1] == 0 && addr->tcpr_s6_addr32[2] == 0 && addr->tcpr_s6_addr32[3] == 0; } int ip6_in_cidr(const tcpr_cidr_t * mycidr, const struct tcpr_in6_addr *addr) { int ret = 0; #ifdef DEBUG char netstr[INET6_ADDRSTRLEN]; #endif uint32_t i, j, k; if (mycidr->family != AF_INET6) return 0; /* always return 1 if ::/0 */ if (mycidr->masklen == 0 && ip6_addr_is_unspec(addr)) return 1; j = mycidr->masklen / 8; for (i = 0; i < j; i++) { if (addr->tcpr_s6_addr[i] != mycidr->u.network6.tcpr_s6_addr[i]) { ret = 0; goto out; } } if ((k = mycidr->masklen % 8) == 0) { ret = 1; goto out; } k = (uint32_t)~0 << (8 - k); i = addr->tcpr_s6_addr[j] & k; j = mycidr->u.network6.tcpr_s6_addr[j] & k; ret = i == j; out: #ifdef DEBUG /* copy this for debug purposes, since it's not re-entrant */ strlcpy(netstr, get_addr2name6(&mycidr->u.network6, RESOLVE), INET6_ADDRSTRLEN); #endif /* if they're the same, then ip is in network */ if (ret) { #ifdef DEBUG dbgx(1, "The ip %s is inside of %s/%d", get_addr2name6(addr, RESOLVE), netstr, mycidr->masklen); #endif } else { #ifdef DEBUG dbgx(1, "The ip %s is not inside of %s/%d", get_addr2name6(addr, RESOLVE), netstr, mycidr->masklen); #endif } return ret; } /** * iterates over cidrdata to find if a given ip matches * returns 1 for true, 0 for false */ int check_ip_cidr(tcpr_cidr_t * cidrdata, const unsigned long ip) { tcpr_cidr_t *mycidr; /* if we have no cidrdata, of course it isn't in there * this actually should happen occasionally, so don't put an assert here */ if (cidrdata == NULL) return 1; mycidr = cidrdata; /* loop through cidr */ while (1) { /* if match, return 1 */ if (ip_in_cidr(mycidr, ip)) { dbgx(3, "Found %s in cidr", get_addr2name4(ip, RESOLVE)); return 1; } /* check for next record */ if (mycidr->next != NULL) { mycidr = mycidr->next; } else { break; } } /* if we get here, no match */ dbgx(3, "Didn't find %s in cidr", get_addr2name4(ip, RESOLVE)); return 0; } int check_ip6_cidr(tcpr_cidr_t * cidrdata, const struct tcpr_in6_addr *addr) { tcpr_cidr_t *mycidr; /* if we have no cidrdata, of course it isn't in there * this actually should happen occasionally, so don't put an assert here */ if (cidrdata == NULL) { return 1; } mycidr = cidrdata; /* loop through cidr */ while (1) { /* if match, return 1 */ if (ip6_in_cidr(mycidr, addr)) { dbgx(3, "Found %s in cidr", get_addr2name6(addr, RESOLVE)); return 1; } /* check for next record */ if (mycidr->next != NULL) { mycidr = mycidr->next; } else { break; } } /* if we get here, no match */ dbgx(3, "Didn't find %s in cidr", get_addr2name6(addr, RESOLVE)); return 0; } /** * cidr2ip takes a tcpr_cidr_t and a delimiter * and returns a string which lists all the IP addresses in the cidr * deliminated by the given char */ char * cidr2iplist(tcpr_cidr_t * cidr, char delim) { char *list = NULL; char ipaddr[18], tempbuff[20]; u_int32_t size, addr, first, last, numips; struct in_addr in; int i; /* * 16 bytes per IP + delim * # of IP's = 2^(32-masklen) */ numips = 2; for (i = 2; i <= (32 - cidr->masklen); i++) numips *= 2; size = 17 * numips - 1; list = (char *)safe_malloc(size); memset(list, 0, size); /* first and last should not include network or broadcast */ first = ntohl(cidr->u.network) + 1; last = first + numips - 3; dbgx(1, "First: %u\t\tLast: %u", first, last); /* loop through all but the last one */ for (addr = first; addr < last; addr++) { in.s_addr = htonl(addr); snprintf(tempbuff, 17, "%s%c", inet_ntoa(in), delim); memcpy(ipaddr, tempbuff, 16); dbgx(2, "%s", ipaddr); strlcat(list, ipaddr, size); } /* last is a special case, end in \0 */ in.s_addr = htonl(addr); snprintf(ipaddr, 17, "%s", inet_ntoa(in)); strlcat(list, ipaddr, size); return list; }