diff options
author | Jianfeng Tan <henry.tjf@antfin.com> | 2019-11-18 06:59:50 +0000 |
---|---|---|
committer | Jianfeng Tan <henry.tjf@antfin.com> | 2020-03-05 01:31:33 +0800 |
commit | 78c896b3b3127515478090c19447e27dc406427e (patch) | |
tree | d6d67d4683e9ca0409f9984a834547a572fb5310 /test/packetdrill/ip_address.c | |
parent | e4380f4866091fd92a7a57667dd938a99144f9cd (diff) |
TLDKv2dev-next-socket
Signed-off-by: Jianfeng Tan <henry.tjf@antfin.com>
Signed-off-by: Jielong Zhou <jielong.zjl@antfin.com>
Signed-off-by: Jian Zhang <wuzai.zj@antfin.com>
Signed-off-by: Chen Zhao <winters.zc@antfin.com>
Change-Id: I55c39de4c6cd30f991f35631eb507f770230f08e
Diffstat (limited to 'test/packetdrill/ip_address.c')
-rw-r--r-- | test/packetdrill/ip_address.c | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/test/packetdrill/ip_address.c b/test/packetdrill/ip_address.c new file mode 100644 index 0000000..9518f17 --- /dev/null +++ b/test/packetdrill/ip_address.c @@ -0,0 +1,379 @@ +/* + * Copyright 2013-2015 Google Inc. + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ +/* + * Author: ncardwell@google.com (Neal Cardwell) + * + * Implementation for operations for IPv4 and IPv6 addresses. + */ + +#include "ip_address.h" + +#include <ifaddrs.h> +#include <net/if.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> + +#include "logging.h" + +/* IPv6 prefix for IPv4-mapped addresses. These are in the + * ::FFFF:0:0/96 space, i.e. 10 bytes of 0x00 and 2 bytes of 0xFF. See + * RFC 4291 ("IPv6 Addressing Architecture") section 2.5.5.2 + * ("IPv4-Mapped IPv6 Address"). + */ +const u8 ipv4_mapped_prefix[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF }; + +int ip_address_length(int address_family) +{ + switch (address_family) { + case AF_INET: + return sizeof(struct in_addr); + case AF_INET6: + return sizeof(struct in6_addr); + default: + die("ip_address_length: bad address family: %d\n", + address_family); + break; + } + return 0; /* not reached */ +} + +int sockaddr_length(int address_family) +{ + switch (address_family) { + case AF_INET: + return sizeof(struct sockaddr_in); + case AF_INET6: + return sizeof(struct sockaddr_in6); + default: + die("sockaddr_length: bad address family: %d\n", + address_family); + break; + } + return 0; /* not reached */ +} + +static void ipv4_init(struct ip_address *ipv4) +{ + memset(ipv4, 0, sizeof(*ipv4)); + ipv4->address_family = AF_INET; +} + +static void ipv6_init(struct ip_address *ipv6) +{ + memset(ipv6, 0, sizeof(*ipv6)); + ipv6->address_family = AF_INET6; +} + +void ip_from_ipv4(const struct in_addr *ipv4, struct ip_address *ip) +{ + ipv4_init(ip); + ip->ip.v4 = *ipv4; +} + +void ip_from_ipv6(const struct in6_addr *ipv6, struct ip_address *ip) +{ + ipv6_init(ip); + ip->ip.v6 = *ipv6; +} + +void ip_to_ipv4(const struct ip_address *ip, struct in_addr *ipv4) +{ + *ipv4 = ip->ip.v4; +} + +void ip_to_ipv6(const struct ip_address *ip, struct in6_addr *ipv6) +{ + *ipv6 = ip->ip.v6; +} + +struct ip_address ipv4_parse(const char *ip_string) +{ + struct ip_address ipv4; + ipv4_init(&ipv4); + + if (inet_pton(AF_INET, ip_string, &ipv4.ip.v4) != 1) + die("bad IPv4 address: %s\n", ip_string); + + return ipv4; +} + +struct ip_address ipv6_parse(const char *ip_string) +{ + struct ip_address ipv6; + ipv6_init(&ipv6); + + if (inet_pton(AF_INET6, ip_string, &ipv6.ip.v6) != 1) + die("bad IPv6 address: %s\n", ip_string); + + return ipv6; +} + +const char *ip_to_string(const struct ip_address *ip, char *buffer) +{ + if (!inet_ntop(ip->address_family, &ip->ip, buffer, ADDR_STR_LEN)) + die_perror("inet_ntop"); + + return buffer; +} + +struct ip_address ipv6_map_from_ipv4(const struct ip_address ipv4) +{ + struct ip_address ipv6; + ipv6_init(&ipv6); + + assert(sizeof(ipv4.ip.v4) + sizeof(ipv4_mapped_prefix) == + sizeof(ipv6.ip.v6)); + memcpy(ipv6.ip.v6.s6_addr, ipv4_mapped_prefix, + sizeof(ipv4_mapped_prefix)); + memcpy(ipv6.ip.v6.s6_addr + sizeof(ipv4_mapped_prefix), + &ipv4.ip.v4, sizeof(ipv4.ip.v4)); + return ipv6; +} + +int ipv6_map_to_ipv4(const struct ip_address ipv6, struct ip_address *ipv4) +{ + if (memcmp(&ipv6.ip.v6.s6_addr, + ipv4_mapped_prefix, sizeof(ipv4_mapped_prefix)) == 0) { + ipv4_init(ipv4); + memcpy(&ipv4->ip.v4, + ipv6.ip.v6.s6_addr + sizeof(ipv4_mapped_prefix), + sizeof(ipv4->ip.v4)); + return STATUS_OK; + } else { + return STATUS_ERR; + } +} + +/* Fill in a sockaddr struct and socklen_t using the given IPv4 + * address and port. + */ +static void ipv4_to_sockaddr(const struct ip_address *ipv4, u16 port, + struct sockaddr *address, socklen_t *length) +{ + struct sockaddr_in sa_v4; + memset(&sa_v4, 0, sizeof(sa_v4)); +#ifndef linux + sa_v4.sin_len = sizeof(sa_v4); +#endif + sa_v4.sin_family = AF_INET; + sa_v4.sin_port = htons(port); + memcpy(&sa_v4.sin_addr, &ipv4->ip.v4, sizeof(sa_v4.sin_addr)); + *length = sizeof(sa_v4); + memcpy(address, &sa_v4, *length); +} + +/* Fill in a sockaddr struct and socklen_t using the given IPv6 + * address and port. + */ +static void ipv6_to_sockaddr(const struct ip_address *ipv6, u16 port, + struct sockaddr *address, socklen_t *length) +{ + struct sockaddr_in6 sa_v6; + memset(&sa_v6, 0, sizeof(sa_v6)); +#ifndef linux + sa_v6.sin6_len = sizeof(sa_v6); +#endif + sa_v6.sin6_family = AF_INET6; + sa_v6.sin6_port = htons(port); + memcpy(&sa_v6.sin6_addr, &ipv6->ip.v6, sizeof(sa_v6.sin6_addr)); + *length = sizeof(sa_v6); + memcpy(address, &sa_v6, *length); +} + +void ip_to_sockaddr(const struct ip_address *ip, u16 port, + struct sockaddr *address, socklen_t *length) +{ + switch (ip->address_family) { + case AF_INET: + ipv4_to_sockaddr(ip, port, address, length); + break; + case AF_INET6: + ipv6_to_sockaddr(ip, port, address, length); + break; + default: + die("ip_to_sockaddr: bad address family: %d\n", + ip->address_family); + break; + } +} + +/* Extract and return the IPv4 address and port from the given sockaddr. */ +static void ipv4_from_sockaddr(const struct sockaddr *address, socklen_t length, + struct ip_address *ipv4, u16 *port) +{ + assert(address->sa_family == AF_INET); + ipv4_init(ipv4); + + struct sockaddr_in sa_v4; + assert(length == sizeof(sa_v4)); + memcpy(&sa_v4, address, length); /* to avoid aliasing issues */ + ipv4->ip.v4 = sa_v4.sin_addr; + *port = ntohs(sa_v4.sin_port); +} + +/* Extract and return the IPv6 address and port from the given sockaddr. */ +static void ipv6_from_sockaddr(const struct sockaddr *address, socklen_t length, + struct ip_address *ipv4, u16 *port) +{ + assert(address->sa_family == AF_INET6); + ipv6_init(ipv4); + + struct sockaddr_in6 sa_v6; + assert(length == sizeof(sa_v6)); + memcpy(&sa_v6, address, length); /* to avoid aliasing issues */ + ipv4->ip.v6 = sa_v6.sin6_addr; + *port = ntohs(sa_v6.sin6_port); +} + +void ip_from_sockaddr(const struct sockaddr *address, socklen_t length, + struct ip_address *ip, u16 *port) +{ + switch (address->sa_family) { + case AF_INET: + ipv4_from_sockaddr(address, length, ip, port); + break; + case AF_INET6: + ipv6_from_sockaddr(address, length, ip, port); + break; + default: + die("ip_from_sockaddr: bad address family: %d\n", + address->sa_family); + break; + } +} + +int get_ip_device(const struct ip_address *ip, char *dev_name) +{ + struct ifaddrs *ifaddr_list, *ifaddr; + bool is_local = false; + + if (getifaddrs(&ifaddr_list)) + die_perror("getifaddrs"); + + for (ifaddr = ifaddr_list; ifaddr != NULL; ifaddr = ifaddr->ifa_next) { + int family; + struct ip_address interface_ip; + u16 port; + + if (ifaddr->ifa_addr == NULL) + continue; + + family = ifaddr->ifa_addr->sa_family; + if (family != ip->address_family) + continue; + + ip_from_sockaddr(ifaddr->ifa_addr, sockaddr_length(family), + &interface_ip, &port); + if (is_equal_ip(ip, &interface_ip)) { + assert(ifaddr->ifa_name); + assert(strlen(ifaddr->ifa_name) < IFNAMSIZ); + strcpy(dev_name, ifaddr->ifa_name); + is_local = true; + break; + } + } + + freeifaddrs(ifaddr_list); + + return is_local; +} + +int is_ip_local(const struct ip_address *ip) +{ + char dev_name[IFNAMSIZ]; + + return get_ip_device(ip, dev_name); +} + +int netmask_to_prefix(const char *netmask) +{ + int pos; + struct ip_address mask = ipv4_parse(netmask); + u32 mask_addr = ntohl(mask.ip.v4.s_addr); + int prefix_len = 0; + + for (pos = 31; pos >= 0; --pos) { + if (!(mask_addr & (1<<pos))) + break; + ++prefix_len; + } + return prefix_len; +} + +static int urandom_read(void *buffer, int sz) +{ + static int fd_urandom = -1; + + if (fd_urandom == -1) + fd_urandom = open("/dev/urandom", O_RDONLY); + return read(fd_urandom, buffer, sz); +} + +void generate_random_ipv4_addr(char *result, const char *base, + const char *netmask) +{ + int prefix_len = netmask_to_prefix(netmask); + struct ip_address addr = ipv4_parse(base); + + if (prefix_len < 31) { + unsigned int rnd; + + if (urandom_read(&rnd, sizeof(rnd)) == sizeof(rnd)) { + if (prefix_len) { + u32 mask = (1U << (32 - prefix_len)) - 1; + + rnd &= mask; + /* .0 is reserved for network address. + * .1 is reserved for the gateway + */ + if (rnd < 2) + rnd = 2; + /* .255.255 is reserved for net broadcast */ + if (rnd == mask) + rnd--; + } + addr.ip.v4.s_addr |= htonl(rnd); + } + } + ip_to_string(&addr, result); +} + +/* In this version, we randomize last 32bits (or less) of the address. + * There is no need to fully use RFC 4193 range. + * ( fd3d:fa7b:d17d::/48 in unique local address space ) + */ +void generate_random_ipv6_addr(char *result, const char *base, int prefixlen) +{ + struct ip_address addr = ipv6_parse(base); + unsigned int mask = ~0U, rnd = 0; + + urandom_read(&rnd, sizeof(rnd)); + if (prefixlen > 128 - 32) { + mask = (1U << (128 - prefixlen)) - 1; + rnd &= mask; + } + if (!rnd) + rnd++; + if (rnd == mask) + rnd--; + addr.ip.v6.s6_addr32[3] |= htonl(rnd); + ip_to_string(&addr, result); +} |