From 196fc0351c1e5b39697091c845c73d04510e070e Mon Sep 17 00:00:00 2001 From: Tommy Beadle Date: Mon, 29 Feb 2016 09:29:48 -0500 Subject: mausezahn: Allow IPv6 ranges to be specified for source and dest addresses This allows a user to pass a range of IPv6 addresses, either like: fec0:5000::1-fec0:5000::100 or in CIDR notation: fec0:5000::0/112 These can be used for the -A and/or -B command-line options. The largest range that can be used is a /64. In other words, if using CIDR notation, the masklen must be <= 128 and >= 64. Signed-off-by: Tommy Beadle Signed-off-by: Tobias Klauser --- staging/mausezahn.c | 95 ++++++++++++------ staging/modifications.c | 82 +++++++++++++++- staging/mz.h | 11 +++ staging/send.c | 36 ++++++- staging/tools.c | 254 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 444 insertions(+), 34 deletions(-) diff --git a/staging/mausezahn.c b/staging/mausezahn.c index 5051bcb..70f1e72 100644 --- a/staging/mausezahn.c +++ b/staging/mausezahn.c @@ -276,9 +276,13 @@ int reset(void) tx.ip_src_isrange = 0; tx.ip_src_start = 0; tx.ip_src_stop = 0; + memset(&tx.ip6_src_start, 0, sizeof(tx.ip6_src_start)); + memset(&tx.ip6_src_stop, 0, sizeof(tx.ip6_src_stop)); tx.ip_dst_start = 0; tx.ip_dst_stop = 0; + memset(&tx.ip6_dst_start, 0, sizeof(tx.ip6_dst_start)); + memset(&tx.ip6_dst_stop, 0, sizeof(tx.ip6_dst_stop)); tx.ip_dst_isrange = 0; tx.ip_ttl = 0; @@ -658,12 +662,24 @@ int getopts (int argc, char *argv[]) // Set source IP address: if (strlen(tx.ip_src_txt)) { // option -A has been specified if (mz_strcmp(tx.ip_src_txt, "bcast", 2)==0) { + if (ipv6_mode) { + fprintf(stderr, "Option -A does not support 'bcast' when in IPv6 mode.\n"); + return 1; + } tx.ip_src = libnet_name2addr4 (l, "255.255.255.255", LIBNET_DONT_RESOLVE); } else if (strcmp(tx.ip_src_txt, "rand") == 0) { + if (ipv6_mode) { + fprintf(stderr, "Option -A does not support 'rand' when in IPv6 mode.\n"); + return 1; + } tx.ip_src_rand = 1; tx.ip_src_h = (u_int32_t) ( ((float) rand()/RAND_MAX)*0xE0000000); //this is 224.0.0.0 } - else if (get_ip_range_src(tx.ip_src_txt)) { // returns 1 when no range has been specified + else if ( + (ipv6_mode && get_ip6_range_src(tx.ip_src_txt, l)) || // returns 1 when no range has been specified + (!ipv6_mode && get_ip_range_src(tx.ip_src_txt)) + ) + { // name2addr4 accepts a DOTTED DECIMAL ADDRESS or a FQDN: if (ipv6_mode) tx.ip6_src = libnet_name2addr6 (l, tx.ip_src_txt, LIBNET_RESOLVE); @@ -689,8 +705,15 @@ int getopts (int argc, char *argv[]) } if (mz_strcmp(tx.ip_dst_txt, "bcast", 2)==0) { - tx.ip_dst = libnet_name2addr4 (l, "255.255.255.255", LIBNET_DONT_RESOLVE); - } else if (get_ip_range_dst(tx.ip_dst_txt)) { // returns 1 when no range has been specified + if (ipv6_mode) { + fprintf(stderr, "Option -B does not support 'bcast' when in IPv6 mode.\n"); + return 1; + } + tx.ip_dst = libnet_name2addr4 (l, "255.255.255.255", LIBNET_DONT_RESOLVE); + } else if ( + (ipv6_mode && get_ip6_range_dst(tx.ip_dst_txt, l)) || // returns 1 when no range has been specified + (!ipv6_mode && get_ip_range_dst(tx.ip_dst_txt))) + { // name2addr4 accepts a DOTTED DECIMAL ADDRESS or a FQDN: if (ipv6_mode) tx.ip6_dst = libnet_name2addr6 (l, tx.ip_dst_txt, LIBNET_RESOLVE); @@ -699,7 +722,13 @@ int getopts (int argc, char *argv[]) } } else { // no destination IP specified: by default use broadcast - tx.ip_dst = libnet_name2addr4 (l, "255.255.255.255", LIBNET_DONT_RESOLVE); + if (ipv6_mode) { + // XXX I think we want to use a link-local + // broadcast address instead. + tx.ip6_dst = libnet_name2addr6 (l, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", LIBNET_DONT_RESOLVE); + } else { + tx.ip_dst = libnet_name2addr4 (l, "255.255.255.255", LIBNET_DONT_RESOLVE); + } } // Initialize tx.ip_src_h and tx.ip_dst_h which are used by 'print_frame_details()' @@ -710,28 +739,40 @@ int getopts (int argc, char *argv[]) dum2 = (unsigned char*) &tx.ip_src; } else { // ip_src already given, convert to ip_src_h - dum1 = (unsigned char*) &tx.ip_src; - dum2 = (unsigned char*) &tx.ip_src_h; + if (ipv6_mode) { + if (tx.ip_src_isrange) { + tx.ip6_src = tx.ip6_src_start; + } + } else { + dum1 = (unsigned char*) &tx.ip_src; + dum2 = (unsigned char*) &tx.ip_src_h; + } } - *dum2 = *(dum1+3); - dum2++; - *dum2 = *(dum1+2); - dum2++; - *dum2 = *(dum1+1); - dum2++; - *dum2 = *dum1; - - dum1 = (unsigned char*) &tx.ip_dst; - dum2 = (unsigned char*) &tx.ip_dst_h; - - *dum2 = *(dum1+3); - dum2++; - *dum2 = *(dum1+2); - dum2++; - *dum2 = *(dum1+1); - dum2++; - *dum2 = *dum1; + if (ipv6_mode) { + if (tx.ip_dst_isrange) { + tx.ip6_dst = tx.ip6_dst_start; + } + } else { + *dum2 = *(dum1+3); + dum2++; + *dum2 = *(dum1+2); + dum2++; + *dum2 = *(dum1+1); + dum2++; + *dum2 = *dum1; + + dum1 = (unsigned char*) &tx.ip_dst; + dum2 = (unsigned char*) &tx.ip_dst_h; + + *dum2 = *(dum1+3); + dum2++; + *dum2 = *(dum1+2); + dum2++; + *dum2 = *(dum1+1); + dum2++; + *dum2 = *dum1; + } libnet_destroy(l); } @@ -911,8 +952,6 @@ int main(int argc, char **argv) l = get_link_context(); t4 = create_icmp6_packet(l); // t4 can be used for later header changes t3 = create_ip_packet(l); // t3 can be used for later header changes - if (ipv6_mode) - update_ISUM(l, t4); if (!quiet) complexity(); if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes @@ -925,8 +964,6 @@ int main(int argc, char **argv) l = get_link_context(); t4 = create_udp_packet(l); // t4 can be used for later header changes t3 = create_ip_packet(l); // t3 can be used for later header changes - if (ipv6_mode) - update_USUM(l, t4); if (!quiet) complexity(); if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes @@ -939,8 +976,6 @@ int main(int argc, char **argv) l = get_link_context(); t4 = create_tcp_packet(l); // t4 can be used for later header changes t3 = create_ip_packet(l); // t3 can be used for later header changes - if (ipv6_mode) - update_TSUM(l, t4); if (!quiet) complexity(); if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes diff --git a/staging/modifications.c b/staging/modifications.c index e11ba0e..1affbbb 100644 --- a/staging/modifications.c +++ b/staging/modifications.c @@ -27,6 +27,8 @@ // int update_Eth_SA (libnet_t *l, libnet_ptag_t t) // int update_IP_SA (libnet_t *l, libnet_ptag_t t) // int update_IP_DA (libnet_t *l, libnet_ptag_t t) +// int update_IP6_SA (libnet_t *l, libnet_ptag_t t) +// int update_IP6_DA (libnet_t *l, libnet_ptag_t t) // int update_DPORT (libnet_t *l, libnet_ptag_t t) // int update_SPORT (libnet_t *l, libnet_ptag_t t) // int update_TCP_SQNR (libnet_t *l, libnet_ptag_t t) @@ -135,6 +137,10 @@ int update_IP_SA (libnet_t *l, libnet_ptag_t t) u_int8_t *x, *y; int i=0; + if (ipv6_mode) { + return update_IP6_SA(l, t); + } + if (tx.ip_src_rand) { tx.ip_src_h = (u_int32_t) ( ((float) rand()/RAND_MAX)*0xE0000000); //this is 224.0.0.0 @@ -190,6 +196,41 @@ int update_IP_SA (libnet_t *l, libnet_ptag_t t) } +int +update_IP6_SA(libnet_t *l, libnet_ptag_t t) +{ + int i = 0; + if (tx.ip_src_rand) { + fprintf(stderr, "Random source addresses are not supported in IPv6 mode.\n"); + exit(1); + } else if (tx.ip_src_isrange) { + if (incr_in6_addr(tx.ip6_src, &tx.ip6_src) + || (in6_addr_cmp(tx.ip6_src, tx.ip6_src_stop) > 0)) + { + tx.ip6_src = tx.ip6_src_start; + i = 1; + } + } + + t = libnet_build_ipv6(tx.ip_tos, + tx.ip_flow, + tx.ip_len, + tx.ip_proto, + tx.ip_ttl, + tx.ip6_src, + tx.ip6_dst, + (mode==IP) ? (tx.ip_payload_s) ? tx.ip_payload : NULL : NULL, + (mode==IP) ? tx.ip_payload_s : 0, + l, + t); + + if (t == -1) { + fprintf(stderr," mz/update_IP6_SA: IP address manipulation failed!\n"); + exit (1); + } + + return i; +} ///////////////////////////////////////////////////////////////////////////////////////// @@ -208,6 +249,9 @@ int update_IP_DA(libnet_t *l, libnet_ptag_t t) u_int8_t *x, *y; int i=0; + if (ipv6_mode) { + return update_IP6_DA(l, t); + } if (tx.ip_dst_isrange) { @@ -262,6 +306,39 @@ int update_IP_DA(libnet_t *l, libnet_ptag_t t) } +int +update_IP6_DA(libnet_t *l, libnet_ptag_t t) +{ + int i = 0; + if (tx.ip_dst_isrange) { + if (incr_in6_addr(tx.ip6_dst, &tx.ip6_dst) + || (in6_addr_cmp(tx.ip6_dst, tx.ip6_dst_stop) > 0)) + { + tx.ip6_dst = tx.ip6_dst_start; + i = 1; + } + } + + t = libnet_build_ipv6(tx.ip_tos, + tx.ip_flow, + tx.ip_len, + tx.ip_proto, + tx.ip_ttl, + tx.ip6_src, + tx.ip6_dst, + (mode==IP) ? (tx.ip_payload_s) ? tx.ip_payload : NULL : NULL, + (mode==IP) ? tx.ip_payload_s : 0, + l, + t); + + if (t == -1) { + fprintf(stderr," mz/update_IP6_DA: IP address manipulation failed!\n"); + exit (1); + } + + return i; +} + /////////////////////////////////////////////////////////////////////////////////////// @@ -399,7 +476,7 @@ int update_SPORT(libnet_t *l, libnet_ptag_t t) if (t == -1) { - fprintf(stderr, " mz/update_DPORT: Can't build TCP header: %s\n", libnet_geterror(l)); + fprintf(stderr, " mz/update_SPORT: Can't build TCP header: %s\n", libnet_geterror(l)); exit (0); } } @@ -448,6 +525,7 @@ int update_USUM(libnet_t *l, libnet_ptag_t t) tx.udp_payload_s, l, t); + tx.udp_sum = 0; return t; } @@ -504,6 +582,7 @@ int update_TSUM(libnet_t *l, libnet_ptag_t t) tx.tcp_payload_s, l, t); + tx.tcp_sum = 0; return t; } @@ -544,6 +623,7 @@ int update_ISUM(libnet_t *l, libnet_ptag_t t) tx.icmp_payload_s, l, t); + tx.icmp_chksum = 0; return t; } diff --git a/staging/mz.h b/staging/mz.h index 339be31..5ed05a7 100644 --- a/staging/mz.h +++ b/staging/mz.h @@ -402,6 +402,8 @@ struct tx_struct u_int32_t ip_src_h; // mirror of ip_src (NOT network byte order => easy to count) u_int32_t ip_src_start; // start of range (NOT network byte order => easy to count) u_int32_t ip_src_stop; // stop of range (NOT network byte order => easy to count) + struct libnet_in6_addr ip6_src_start; // start of IPv6 range + struct libnet_in6_addr ip6_src_stop; // stop of IPv6 range int ip_src_isrange; // if set to 1 then the start/stop values above are valid. u_int32_t ip_dst; // has always network byte order(!) struct libnet_in6_addr ip6_dst; @@ -409,6 +411,8 @@ struct tx_struct u_int32_t ip_dst_h; // mirror of ip_dst (NOT network byte order => easy to count) u_int32_t ip_dst_start; // start of range (NOT network byte order => easy to count) u_int32_t ip_dst_stop; // stop of range (NOT network byte order => easy to count) + struct libnet_in6_addr ip6_dst_start; // start of IPv6 range + struct libnet_in6_addr ip6_dst_stop; // stop of IPv6 range int ip_dst_isrange; // if set to 1 then the start/stop values above are valid. u_int16_t ip_len, @@ -653,6 +657,8 @@ int type2str(u_int16_t type, char *str); // int get_ip_range_dst (char *arg); int get_ip_range_src (char *arg); +int get_ip6_range_src (char *arg, libnet_t *l); +int get_ip6_range_dst (char *arg, libnet_t *l); // Sets a random SA for a given IP packet. // Return value: 0 upon success, 1 upon failure @@ -740,6 +746,7 @@ int update_RTP(libnet_t *l, libnet_ptag_t t); // // RETURNS '1' if tx.ip_src restarts int update_IP_SA (libnet_t *l, libnet_ptag_t t); +int update_IP6_SA (libnet_t *l, libnet_ptag_t t); // Applies another DESTINATION IP address from a specified range (tx.ip_dst_isrange==1) @@ -750,6 +757,7 @@ int update_IP_SA (libnet_t *l, libnet_ptag_t t); // // RETURN VALUE: '1' if tx.ip_dst restarts int update_IP_DA(libnet_t *l, libnet_ptag_t t); +int update_IP6_DA (libnet_t *l, libnet_ptag_t t); // Applies another DESTINATION PORT from a specified range to a given UDP- or TCP-PTAG. @@ -784,6 +792,9 @@ int update_TSUM(libnet_t *l, libnet_ptag_t t); // int print_frame_details(void); +int in6_addr_cmp(struct libnet_in6_addr addr1, struct libnet_in6_addr addr2); +int incr_in6_addr(struct libnet_in6_addr src, struct libnet_in6_addr *dst); +uint64_t get_ip6_range_count(struct libnet_in6_addr start, struct libnet_in6_addr stop); // Calculates the number of frames to be sent. // Should be used as standard output except the diff --git a/staging/send.c b/staging/send.c index ef76512..5d5adea 100644 --- a/staging/send.c +++ b/staging/send.c @@ -90,13 +90,27 @@ int complexity(void) if (tx.ip_dst_isrange) { - nr_da = tx.ip_dst_stop - tx.ip_dst_start + 1; - //fprintf(stderr,"DA Range = %lu\n",nr_da); + if (ipv6_mode) + { + nr_da = get_ip6_range_count(tx.ip6_dst_start, tx.ip6_dst_stop); + } + else + { + nr_da = tx.ip_dst_stop - tx.ip_dst_start + 1; + } + //fprintf(stderr,"DA Range = %lu\n",nr_da); } if (tx.ip_src_isrange) { - nr_sa = tx.ip_src_stop - tx.ip_src_start + 1; + if (ipv6_mode) + { + nr_sa = get_ip6_range_count(tx.ip6_src_start, tx.ip6_src_stop); + } + else + { + nr_sa = tx.ip_src_stop - tx.ip_src_start + 1; + } //fprintf(stderr,"SA Range = %lu\n",nr_sa); } @@ -195,6 +209,22 @@ int send_frame (libnet_t *l, libnet_ptag_t t3, libnet_ptag_t t4) { AGAIN: + if (ipv6_mode) { + switch (mode) { + case ICMP6: + update_ISUM(l, t4); + break; + case UDP: + case DNS: + case RTP: + case SYSLOG: + update_USUM(l, t4); + break; + case TCP: + update_TSUM(l, t4); + break; + } + } if (verbose) (void) print_frame_details(); libnet_write(l); diff --git a/staging/tools.c b/staging/tools.c index be8eace..9d2d1be 100644 --- a/staging/tools.c +++ b/staging/tools.c @@ -29,6 +29,8 @@ // xstr2lint ........... Same as above but returns an unsigned long long int // get_ip_range_dst .... Parses string for an IP range and sets start/stop addresses // get_ip_range_src .... Same for source addresses +// get_ip6_range_dst ... Parses string for an IPv6 range and sets start/stop addresses +// get_ip6_range_src ... Same for source addresses // check_eth_mac_txt ... Scans tx.eth_dst|src_txt and sets tx.eth_dst|src appropriately // get_port_range ...... Parses string for a dst|src-port range and sets start/stop values // get_tcp_flags ....... Parses string for TCP arguments and sets tx.tcp_control @@ -54,7 +56,11 @@ #include "mz.h" +#define CMP_INT(a, b) ((a) < (b) ? -1 : (a) > (b)) +#define IPV6_MAX_RANGE_LEN strlen("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128") +#define IPV6_MIN_RANGE_LEN strlen("::/0") +static int in6_range_too_big(struct libnet_in6_addr start, struct libnet_in6_addr stop); // Scan 'str' for an argument 'arg_name' and returns its value in arg_value // Return value: number of occurences of arg_name @@ -188,6 +194,91 @@ unsigned long long int xstr2lint(char *str) } +/* + * Return the IPv6 broadcast address for the given network/mask. + */ +struct libnet_in6_addr +in6_addr_bcast(struct libnet_in6_addr addr, unsigned int masklen) +{ + struct libnet_in6_addr bcast; + uint32_t mask = 0; + int i = 3; + if (masklen > 128) { + fprintf(stderr, "Invalid IPv6 masklen: %u\n", masklen); + exit(1); + } + masklen = 128 - masklen; + + bcast = addr; + + for (i = 3; i >= 0; i--, masklen -= 32) { + if (masklen <= 32) { + bcast.__u6_addr.__u6_addr32[i] = htonl(ntohl(bcast.__u6_addr.__u6_addr32[i]) | ((uint32_t) (~0) >> (32 - masklen))); + break; + } + bcast.__u6_addr.__u6_addr32[i] = (uint32_t) (~0); + } + return bcast; +} + +/* + * Returns 0 if the given IPv6 addresses are equal, + * -1 if addr1 is lower than addr2, + * 1 if addr2 is lower than addr1. + */ +int in6_addr_cmp(struct libnet_in6_addr addr1, + struct libnet_in6_addr addr2) +{ + uint32_t *p1 = addr1.__u6_addr.__u6_addr32, + *p2 = addr2.__u6_addr.__u6_addr32; + int i, val = 0; + + for (i = 0; i < 4; i++) { + val = CMP_INT(ntohl(*p1++), ntohl(*p2++)); + if (val) { + break; + } + } + return val; +} + +/* + * Calculate the address that comes immediately after the one given. + * Store the result in *dst. + * Returns 1 if an overflow occurred. Otherwise, returns 0. + */ +int +incr_in6_addr(struct libnet_in6_addr src, struct libnet_in6_addr *dst) +{ + uint32_t i = 16, carry = 1; + uint8_t *addr; + *dst = src; + addr = dst->__u6_addr.__u6_addr8; + while (carry && i) { + addr[i - 1] += carry; + if (addr[i - 1] > 0xff || !addr[i - 1]) { + addr[i - 1] &= 0xff; + } else { + carry = 0; + } + i--; + } + return (int)carry; +} + + +/* + * Return 1 if the number of addresses that are in the range from start to stop + * is greater than what can be stored in a uint64_t. Otherwise, return 0. + */ +static int +in6_range_too_big(struct libnet_in6_addr start, struct libnet_in6_addr stop) +{ + return ( + (start.__u6_addr.__u6_addr32[0] != stop.__u6_addr.__u6_addr32[0]) + || (start.__u6_addr.__u6_addr32[1] != stop.__u6_addr.__u6_addr32[1]) + ); +} // Parses string 'arg' for an IP range and finds start and stop IP addresses. @@ -383,6 +474,169 @@ int get_ip_range_src (char *arg) } +/* + * Return the number of IPv6 addresses in the range from start to stop or + * UINT64_MAX, whichever is smaller. + */ +uint64_t get_ip6_range_count(struct libnet_in6_addr start, struct libnet_in6_addr stop) +{ + uint64_t retval = 0; + if (in6_range_too_big(start, stop)) { + return UINT64_MAX; + } + retval = ((uint64_t)(ntohl(stop.__u6_addr.__u6_addr32[2]) - ntohl(start.__u6_addr.__u6_addr32[2])) << 32) + + (ntohl(stop.__u6_addr.__u6_addr32[3]) - ntohl(start.__u6_addr.__u6_addr32[3])); + if (retval < UINT64_MAX) { + retval++; + } + return retval; +} + +int get_ip6_range_src (char *arg, libnet_t *l) +{ + + int + i, len, + found_slash=0, found_dash=0; + + unsigned int q; + struct libnet_in6_addr tmp_in6_addr; + uint32_t mask, invmask; + char *start_str, *stop_str; + + len = strnlen(arg, IPV6_MAX_RANGE_LEN); + + if ( (len > IPV6_MAX_RANGE_LEN) || (len < IPV6_MIN_RANGE_LEN) ) + return 1; // ERROR: no range + + // Find "-" or "/" + for (i=0; i IPV6_MAX_RANGE_LEN) || (len < IPV6_MIN_RANGE_LEN) ) + return 1; // ERROR: no range + + // Find "-" or "/" + for (i=0; i