From 1c8239ef6d97b95d5fa62db2bbc29febce779593 Mon Sep 17 00:00:00 2001 From: Alexander Funke Date: Wed, 18 May 2022 15:00:02 +0200 Subject: [PATCH] Initial multipath support --- gtests/net/packetdrill/config.c | 312 ++++++++++++------- gtests/net/packetdrill/config.h | 27 +- gtests/net/packetdrill/net_utils.c | 12 +- gtests/net/packetdrill/netdev.c | 114 ++++--- gtests/net/packetdrill/packet.c | 5 + gtests/net/packetdrill/packet.h | 2 + gtests/net/packetdrill/packet_socket.h | 3 +- gtests/net/packetdrill/packet_socket_linux.c | 168 ++++++---- gtests/net/packetdrill/packet_socket_pcap.c | 35 ++- gtests/net/packetdrill/packet_to_string.c | 7 + gtests/net/packetdrill/parser.y | 106 ++++++- gtests/net/packetdrill/path.h | 32 ++ gtests/net/packetdrill/run_packet.c | 142 +++++++-- gtests/net/packetdrill/run_system_call.c | 99 ++++-- gtests/net/packetdrill/sctp_packet.c | 58 +++- gtests/net/packetdrill/sctp_packet.h | 7 +- gtests/net/packetdrill/system.c | 45 ++- gtests/net/packetdrill/system.h | 6 + gtests/net/packetdrill/types.h | 6 + gtests/net/packetdrill/wire_client_netdev.c | 91 +++--- gtests/net/packetdrill/wire_server_netdev.c | 27 +- 21 files changed, 903 insertions(+), 401 deletions(-) create mode 100644 gtests/net/packetdrill/path.h diff --git a/gtests/net/packetdrill/config.c b/gtests/net/packetdrill/config.c index 68e23054..c6d37f14 100644 --- a/gtests/net/packetdrill/config.c +++ b/gtests/net/packetdrill/config.c @@ -62,6 +62,7 @@ enum option_codes { OPT_DRY_RUN, OPT_DEBUG, OPT_UDP_ENCAPS, + OPT_PATHS, #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) OPT_TUN_DEV, OPT_PERSISTENT_TUN_DEV, @@ -102,6 +103,7 @@ struct option options[] = { { "verbose", .has_arg = false, NULL, OPT_VERBOSE }, { "debug", .has_arg = false, NULL, OPT_DEBUG }, { "udp_encapsulation", .has_arg = true, NULL, OPT_UDP_ENCAPS }, + { "paths", .has_arg = true, NULL, OPT_PATHS }, #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) { "tun_dev", .has_arg = true, NULL, OPT_TUN_DEV }, { "persistent_tun_dev", .has_arg = false, NULL, OPT_PERSISTENT_TUN_DEV }, @@ -119,12 +121,12 @@ void show_usage(void) "\t[--code_format=code_format]\n" "\t[--code_sockopt=TCP_INFO]\n" "\t[--connect_port=connect_port]\n" - "\t[--remote_ip=remote_ip]\n" - "\t[--local_ip=local_ip]\n" - "\t[--local_linklocal_ip=local_linklocal_ip]\n" - "\t[--gateway_ip=gateway_ip]\n" - "\t[--gateway_linklocal_ip=gateway_linklocal_ip]\n" - "\t[--netmask_ip=netmask_ip]\n" + "\t[--remote_ip=]\n" + "\t[--local_ip=]\n" + "\t[--local_linklocal_ip=]\n" + "\t[--gateway_ip=]\n" + "\t[--gateway_linklocal_ip=]\n" + "\t[--netmask_ip=]\n" "\t[--init_scripts=]\n" "\t[--speed=]\n" "\t[--mtu=]\n" @@ -142,6 +144,7 @@ void show_usage(void) "\t[--verbose|-v]\n" "\t[--debug] * requires compilation with DEBUG *\n" "\t[--udp_encapsulation=[sctp,tcp]]\n" + "\t[--paths=]\n" #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) "\t[--tun_dev=]\n" "\t[--persistent_tun_dev]\n" @@ -150,7 +153,44 @@ void show_usage(void) "\tscript_path ...\n"); } -/* Address Configuration for IPv4 +/* Allocate space for paths_cnt paths in config */ +extern void paths_new(struct config *config, uint paths_cnt) { + if (paths_cnt == 0) + die("can't have zero paths\n"); + + if (paths_cnt == config->live_paths_cnt) + return; + + assert(config->live_paths == NULL); + + config->live_paths_cnt = paths_cnt; + config->live_paths = calloc(paths_cnt, sizeof(struct path)); +} + +struct ip_address *paths_get_address(struct config *config, int addr_skip, + int address_family, enum paths_address_types address_type) +{ + for (uint i = 0; i < config->live_paths_cnt; i++) { + struct ip_address *ip_address; + switch (address_type) { + case PATH_ADDRESS_LOCAL_TYPE: + ip_address = &config->live_paths[i].local_ip; + break; + case PATH_ADDRESS_REMOTE_TYPE: + ip_address = &config->live_paths[i].remote_ip; + break; + } + + if (ip_address->address_family == address_family) { + if (addr_skip == 0) + return ip_address; + addr_skip--; + } + } + die("requested to many paths"); +} + +/* Fill in any as-yet-unspecified IP address attributes using IPv4 defaults. * * For IPv4, we use the 192.168.0.0/16 RFC 1918 private IP space for * our tun interface. To avoid accidents and confusion we want remote @@ -164,12 +204,30 @@ void show_usage(void) * - remote address: 192.0.2.0/24 TEST-NET-1 range (RFC 5737) */ -#define DEFAULT_V4_LIVE_REMOTE_IP_STRING "192.0.2.1/24" -#define DEFAULT_V4_LIVE_LOCAL_IP_STRING "192.168.0.1" -#define DEFAULT_V4_LIVE_GATEWAY_IP_STRING "192.168.0.2" -#define DEFAULT_V4_LIVE_NETMASK_IP_STRING "255.255.0.0" +static void set_ipv4_defaults(struct config *config) +{ + if (config->live_paths_cnt == 0) + paths_new(config, 1); + else if (config->live_paths_cnt >= 64) + die("To many paths %d", config->live_paths_cnt); + + for (uint i = 0; i < config->live_paths_cnt; i++) { + struct path *path = &config->live_paths[i]; + + if (strlen(path->remote_ip_string) == 0) + snprintf(path->remote_ip_string, ADDR_STR_LEN, "192.0.2.%d/%s", + i + 1, config->live_paths_cnt == 1 ? "24" : "32"); + if (strlen(path->local_ip_string) == 0) + snprintf(path->local_ip_string, ADDR_STR_LEN, "192.168.%d.1", i); + if (strlen(path->gateway_ip_string) == 0) + snprintf(path->gateway_ip_string, ADDR_STR_LEN, "192.168.%d.2", i); + if (strlen(path->netmask_ip_string) == 0) + strcpy(path->netmask_ip_string, config->live_paths_cnt == 1 ? + "255.255.0.0" : "255.255.255.0"); + } +} -/* Address Configuration for IPv6 +/* Fill in any as-yet-unspecified IP address attributes using IPv6 defaults. * * For IPv6 we use a ULA (unique local address) for our local (tun) * interface, and the RFC 3849 documentation space for our remote @@ -179,49 +237,28 @@ void show_usage(void) * - local address: fd3d:fa7b:d17d::/48 in unique local address space (RFC 4193) * - remote address: 2001:DB8::/32 documentation prefix (RFC 3849) */ - -#define DEFAULT_V6_LIVE_REMOTE_IP_STRING "2001:DB8::1/32" -#define DEFAULT_V6_LIVE_LOCAL_IP_STRING "fd3d:fa7b:d17d::1" -#define DEFAULT_V6_LIVE_LOCAL_LINKLOCAL_IP_STRING "fe80::1" -#define DEFAULT_V6_LIVE_GATEWAY_IP_STRING "fd3d:fa7b:d17d::2" -#define DEFAULT_V6_LIVE_GATEWAY_LINKLOCAL_IP_STRING "fe80::2" -#define DEFAULT_V6_LIVE_PREFIX_LEN 48 - -/* Fill in any as-yet-unspecified IP address attributes using IPv4 defaults. */ -static void set_ipv4_defaults(struct config *config) -{ - if (strlen(config->live_remote_ip_string) == 0) - strcpy(config->live_remote_ip_string, - DEFAULT_V4_LIVE_REMOTE_IP_STRING); - if (strlen(config->live_local_ip_string) == 0) - strcpy(config->live_local_ip_string, - DEFAULT_V4_LIVE_LOCAL_IP_STRING); - if (strlen(config->live_gateway_ip_string) == 0) - strcpy(config->live_gateway_ip_string, - DEFAULT_V4_LIVE_GATEWAY_IP_STRING); - if (strlen(config->live_netmask_ip_string) == 0) - strcpy(config->live_netmask_ip_string, - DEFAULT_V4_LIVE_NETMASK_IP_STRING); -} - -/* Fill in any as-yet-unspecified IP address attributes using IPv6 defaults. */ static void set_ipv6_defaults(struct config *config) { - if (strlen(config->live_remote_ip_string) == 0) - strcpy(config->live_remote_ip_string, - DEFAULT_V6_LIVE_REMOTE_IP_STRING); - if (strlen(config->live_local_ip_string) == 0) - strcpy(config->live_local_ip_string, - DEFAULT_V6_LIVE_LOCAL_IP_STRING); - if (strlen(config->live_local_linklocal_ip_string) == 0) - strcpy(config->live_local_linklocal_ip_string, - DEFAULT_V6_LIVE_LOCAL_LINKLOCAL_IP_STRING); - if (strlen(config->live_gateway_ip_string) == 0) - strcpy(config->live_gateway_ip_string, - DEFAULT_V6_LIVE_GATEWAY_IP_STRING); - if (strlen(config->live_gateway_linklocal_ip_string) == 0) - strcpy(config->live_gateway_linklocal_ip_string, - DEFAULT_V6_LIVE_GATEWAY_LINKLOCAL_IP_STRING); + if (config->live_paths_cnt == 0) + paths_new(config, 1); + else if (config->live_paths_cnt >= 0xffff) + die("To many paths %d", config->live_paths_cnt); + + for (uint i = 0; i < config->live_paths_cnt; i++) { + struct path *path = &config->live_paths[i]; + + if (strlen(path->remote_ip_string) == 0) + snprintf(path->remote_ip_string, ADDR_STR_LEN, "2001:DB8::%d/%s", + i, config->live_paths_cnt == 1 ? "32" : "128"); + if (strlen(path->local_ip_string) == 0) + snprintf(path->local_ip_string, ADDR_STR_LEN, "fd3d:fa7b:d17d::%d:1", i); + if (strlen(path->local_linklocal_ip_string) == 0) + strcpy(path->local_linklocal_ip_string, "fe80::1"); + if (strlen(path->gateway_ip_string) == 0) + snprintf(path->gateway_ip_string, ADDR_STR_LEN, "fd3d:fa7b:d17d::%d:2", i); + if (strlen(path->gateway_linklocal_ip_string) == 0) + strcpy(path->gateway_linklocal_ip_string, "fe80::2"); + } } /* Set default configuration before we begin parsing. */ @@ -250,12 +287,9 @@ void set_default_config(struct config *config) */ config->tcp_ts_tick_usecs = 0; /* disable checks of TS val */ - config->live_remote_ip_string[0] = '\0'; - config->live_local_ip_string[0] = '\0'; - config->live_local_linklocal_ip_string[0] = '\0'; - config->live_gateway_ip_string[0] = '\0'; - config->live_gateway_linklocal_ip_string[0] = '\0'; - config->live_netmask_ip_string[0] = '\0'; + // We initialize these later either by options or in set_XXX_defaults + config->live_paths = NULL; + config->live_paths_cnt = 0; config->init_scripts = NULL; @@ -286,15 +320,13 @@ void set_default_config(struct config *config) #endif } -static void set_remote_ip_and_prefix(struct config *config) +static void set_remote_ip_and_prefix(struct path *path) { - config->live_remote_ip = config->live_remote_prefix.ip; - ip_to_string(&config->live_remote_ip, - config->live_remote_ip_string); + path->remote_ip = path->remote_prefix.ip; + ip_to_string(&path->remote_ip, path->remote_ip_string); - ip_prefix_normalize(&config->live_remote_prefix); - ip_prefix_to_string(&config->live_remote_prefix, - config->live_remote_prefix_string); + ip_prefix_normalize(&path->remote_prefix); + ip_prefix_to_string(&path->remote_prefix, path->remote_prefix_string); } /* Here's a table summarizing the types of various entities in the @@ -312,17 +344,18 @@ static void finalize_ipv4_config(struct config *config) { set_ipv4_defaults(config); - config->live_local_ip = ipv4_parse(config->live_local_ip_string); + for (uint i = 0; i < config->live_paths_cnt; i++) { + struct path *path = &config->live_paths[i]; - config->live_remote_prefix = - ipv4_prefix_parse(config->live_remote_ip_string); - set_remote_ip_and_prefix(config); + path->local_ip = ipv4_parse(path->local_ip_string); + + path->remote_prefix = ipv4_prefix_parse(path->remote_ip_string); + set_remote_ip_and_prefix(path); + + path->prefix_len = netmask_to_prefix(path->netmask_ip_string); + path->gateway_ip = ipv4_parse(path->gateway_ip_string); + } - config->live_prefix_len = - netmask_to_prefix(config->live_netmask_ip_string); - config->live_gateway_ip = ipv4_parse(config->live_gateway_ip_string); - config->live_bind_ip = config->live_local_ip; - config->live_connect_ip = config->live_remote_ip; config->socket_domain = AF_INET; config->wire_protocol = AF_INET; } @@ -332,17 +365,18 @@ static void finalize_ipv4_mapped_ipv6_config(struct config *config) { set_ipv4_defaults(config); - config->live_local_ip = ipv4_parse(config->live_local_ip_string); + for (uint i = 0; i < config->live_paths_cnt; i++) { + struct path *path = &config->live_paths[i]; + + path->local_ip = ipv4_parse(path->local_ip_string); + + path->remote_prefix = ipv4_prefix_parse(path->remote_ip_string); + set_remote_ip_and_prefix(path); - config->live_remote_prefix = - ipv4_prefix_parse(config->live_remote_ip_string); - set_remote_ip_and_prefix(config); + path->prefix_len = netmask_to_prefix(path->netmask_ip_string); + path->gateway_ip = ipv4_parse(path->gateway_ip_string); + } - config->live_prefix_len = - netmask_to_prefix(config->live_netmask_ip_string); - config->live_gateway_ip = ipv4_parse(config->live_gateway_ip_string); - config->live_bind_ip = ipv6_map_from_ipv4(config->live_local_ip); - config->live_connect_ip = ipv6_map_from_ipv4(config->live_remote_ip); config->socket_domain = AF_INET6; config->wire_protocol = AF_INET; } @@ -352,18 +386,20 @@ static void finalize_ipv6_config(struct config *config) { set_ipv6_defaults(config); - config->live_local_ip = ipv6_parse(config->live_local_ip_string); - config->live_local_linklocal_ip = ipv6_parse(config->live_local_linklocal_ip_string); + for (uint i = 0; i < config->live_paths_cnt; i++) { + struct path *path = &config->live_paths[i]; + + path->local_ip = ipv6_parse(path->local_ip_string); + path->local_linklocal_ip = ipv6_parse(path->local_linklocal_ip_string); - config->live_remote_prefix = - ipv6_prefix_parse(config->live_remote_ip_string); - set_remote_ip_and_prefix(config); + path->remote_prefix = ipv6_prefix_parse(path->remote_ip_string); + set_remote_ip_and_prefix(path); + + path->prefix_len = config->live_paths_cnt == 1 ? 48 : 64; + path->gateway_ip = ipv6_parse(path->gateway_ip_string); + path->gateway_linklocal_ip = ipv6_parse(path->gateway_linklocal_ip_string); + } - config->live_prefix_len = DEFAULT_V6_LIVE_PREFIX_LEN; - config->live_gateway_ip = ipv6_parse(config->live_gateway_ip_string); - config->live_gateway_linklocal_ip = ipv6_parse(config->live_gateway_linklocal_ip_string); - config->live_bind_ip = config->live_local_ip; - config->live_connect_ip = config->live_remote_ip; config->socket_domain = AF_INET6; config->wire_protocol = AF_INET6; } @@ -420,6 +456,7 @@ void cleanup_config(struct config *config) } free(config->argv); free(config->script_path); + free(config->live_paths); #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) free(config->tun_device); #endif @@ -455,12 +492,63 @@ void parse_non_fatal_arg(char *arg, struct config *config) free(argdup); } +/* Expect that ips are comma-delimited, allowing for spaces. */ +int parse_ips_arg(char *arg, int target, struct config *config) +{ + char *argdup, *saveptr, *token; + + if (arg == NULL || strlen(arg) == 0) + return STATUS_ERR; + + int ip_cnt = 1; + for (int i = 0; arg[i]; i++) { + if (arg[i] == ',') + ip_cnt++; + } + + if(config->live_paths_cnt == 0) + paths_new(config, ip_cnt); + else if (config->live_paths_cnt != ip_cnt) + return STATUS_ERR; + + argdup = strdup(arg); + token = strtok_r(argdup, ", ", &saveptr); + for (int i = 0; token != NULL; i++) { + switch (target) { + case OPT_REMOTE_IP: + strncpy(config->live_paths[i].remote_ip_string, token, ADDR_STR_LEN - 1); + break; + case OPT_LOCAL_IP: + strncpy(config->live_paths[i].local_ip_string, token, ADDR_STR_LEN - 1); + break; + case OPT_LOCAL_LINKLOCAL_IP: + strncpy(config->live_paths[i].local_linklocal_ip_string, token, ADDR_STR_LEN - 1); + break; + case OPT_GATEWAY_IP: + strncpy(config->live_paths[i].gateway_ip_string, token, ADDR_STR_LEN - 1); + break; + case OPT_GATEWAY_LINKLOCAL_IP: + strncpy(config->live_paths[i].gateway_linklocal_ip_string, token, ADDR_STR_LEN - 1); + break; + case OPT_NETMASK_IP: + strncpy(config->live_paths[i].netmask_ip_string, token, ADDR_STR_LEN - 1); + break; + default: + return STATUS_ERR; + } + token = strtok_r(NULL, ", ", &saveptr); + } + free(argdup); + + return STATUS_OK; +} + /* Process a command line option */ static void process_option(int opt, char *optarg, struct config *config, char *where) { - int port = 0; + int port, paths = 0; char *end = NULL, *equals = NULL, *symbol = NULL, *value = NULL; unsigned long speed = 0; @@ -507,23 +595,33 @@ static void process_option(int opt, char *optarg, struct config *config, break; case OPT_REMOTE_IP: assert(optarg != NULL); - strncpy(config->live_remote_ip_string, optarg, ADDR_STR_LEN-1); + if (parse_ips_arg(optarg, OPT_REMOTE_IP, config) == STATUS_ERR) + die("%s: bad --remote_ip: %s\n", where, optarg); break; case OPT_LOCAL_IP: assert(optarg != NULL); - strncpy(config->live_local_ip_string, optarg, ADDR_STR_LEN-1); + if (parse_ips_arg(optarg, OPT_LOCAL_IP, config) == STATUS_ERR) + die("%s: bad --local_ip: %s\n", where, optarg); break; case OPT_LOCAL_LINKLOCAL_IP: assert(optarg != NULL); - strncpy(config->live_local_linklocal_ip_string, optarg, ADDR_STR_LEN-1); + if (parse_ips_arg(optarg, OPT_LOCAL_LINKLOCAL_IP, config) == STATUS_ERR) + die("%s: bad --local_linklocal_ip: %s\n", where, optarg); break; case OPT_GATEWAY_IP: assert(optarg != NULL); - strncpy(config->live_gateway_ip_string, optarg, ADDR_STR_LEN-1); + if (parse_ips_arg(optarg, OPT_GATEWAY_IP, config) == STATUS_ERR) + die("%s: bad --gateway_ip: %s\n", where, optarg); break; case OPT_GATEWAY_LINKLOCAL_IP: assert(optarg != NULL); - strncpy(config->live_gateway_linklocal_ip_string, optarg, ADDR_STR_LEN-1); + if (parse_ips_arg(optarg, OPT_GATEWAY_LINKLOCAL_IP, config) == STATUS_ERR) + die("%s: bad --gateway_linklocal_ip: %s\n", where, optarg); + break; + case OPT_NETMASK_IP: + assert(optarg != NULL); + if (parse_ips_arg(optarg, OPT_NETMASK_IP, config) == STATUS_ERR) + die("%s: bad --netmask_ip: %s\n", where, optarg); break; case OPT_MTU: assert(optarg != NULL); @@ -531,10 +629,6 @@ static void process_option(int opt, char *optarg, struct config *config, if (config->mtu < 0) die("%s: bad --mtu: %s\n", where, optarg); break; - case OPT_NETMASK_IP: - assert(optarg != NULL); - strncpy(config->live_netmask_ip_string, optarg, ADDR_STR_LEN-1); - break; case OPT_INIT_SCRIPTS: assert(optarg != NULL); config->init_scripts = optarg; @@ -620,6 +714,16 @@ static void process_option(int opt, char *optarg, struct config *config, else die("%s: bad --udp_encapsulation: %s\n", where, optarg); break; + case OPT_PATHS: + assert(optarg != NULL); + paths = atoi(optarg); + if(paths <= 0) + die("%s: bad --paths: %s must be positive\n", where, optarg); + if(config->live_paths_cnt == 0) + paths_new(config, paths); + else if (config->live_paths_cnt != paths) + die("%s: bad --paths: %s does not match the number of IPs\n", where, optarg); + break; #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) case OPT_TUN_DEV: assert(optarg != NULL); diff --git a/gtests/net/packetdrill/config.h b/gtests/net/packetdrill/config.h index 2f4fc4b4..d100afca 100644 --- a/gtests/net/packetdrill/config.h +++ b/gtests/net/packetdrill/config.h @@ -32,7 +32,7 @@ #include #include #include "ip_address.h" -#include "ip_prefix.h" +#include "path.h" #include "script.h" #define TUN_DRIVER_SPEED_CUR 0 /* don't change current speed */ @@ -97,26 +97,8 @@ struct config { u16 live_bind_port; /* local port for bind() */ u16 live_connect_port; /* remote port for connect() */ - struct ip_address live_bind_ip; /* address for bind() */ - struct ip_address live_connect_ip; /* address for connect() */ - - struct ip_address live_local_ip; /* local interface IP */ - struct ip_address live_local_linklocal_ip; /* IPv6 local link-local address */ - struct ip_address live_remote_ip; /* remote interface IP */ - struct ip_prefix live_remote_prefix; /* remote prefix under test */ - struct ip_address live_gateway_ip; /* gateway interface IP */ - struct ip_address live_gateway_linklocal_ip; /* IPv6 gateway link-local address */ - - char live_local_ip_string[ADDR_STR_LEN]; /* human-readable IP */ - char live_local_linklocal_ip_string[ADDR_STR_LEN]; /* human-readable IP */ - char live_remote_ip_string[ADDR_STR_LEN]; /* human-readable IP */ - char live_remote_prefix_string[ADDR_STR_LEN]; /* / */ - - char live_gateway_ip_string[ADDR_STR_LEN]; /* local gateway IP */ - char live_gateway_linklocal_ip_string[ADDR_STR_LEN]; /* local gateway IP */ - char live_netmask_ip_string[ADDR_STR_LEN]; /* local netmask */ - - int live_prefix_len; /* IPv4/IPv6 interface prefix len */ + struct path *live_paths; /* the local/remote/gateway IPs for each path */ + uint live_paths_cnt; /* numer of elements in live_path */ int tolerance_usecs; /* tolerance for time divergence */ int tcp_ts_tick_usecs; /* microseconds per TS val tick */ @@ -208,4 +190,7 @@ extern char **parse_command_line_options(int argc, char *argv[], /* The parser calls this function to finalize processing of config info. */ extern void parse_and_finalize_config(struct invocation *invocation); +extern struct ip_address *paths_get_address(struct config *config, int addr_skip, + int address_family, enum paths_address_types address_type); + #endif /* __CONFIG_H__ */ diff --git a/gtests/net/packetdrill/net_utils.c b/gtests/net/packetdrill/net_utils.c index 401a9877..6978bea9 100644 --- a/gtests/net/packetdrill/net_utils.c +++ b/gtests/net/packetdrill/net_utils.c @@ -29,17 +29,7 @@ #include #include "logging.h" - -static void verbose_system(const char *command) -{ - int result; - - DEBUGP("running: '%s'\n", command); - result = system(command); - DEBUGP("result: %d\n", result); - if (result != 0) - DEBUGP("error executing command '%s'\n", command); -} +#include "system.h" /* Configure a local IPv4 address and netmask for the device */ static void net_add_ipv4_address(const char *dev_name, diff --git a/gtests/net/packetdrill/netdev.c b/gtests/net/packetdrill/netdev.c index fb0edd71..0e5ff5f7 100644 --- a/gtests/net/packetdrill/netdev.c +++ b/gtests/net/packetdrill/netdev.c @@ -59,6 +59,7 @@ #include "packet.h" #include "packet_parser.h" #include "packet_socket.h" +#include "system.h" #include "tcp.h" #include "tun.h" @@ -92,9 +93,6 @@ static void cleanup_old_device(struct config *config, { #if defined(__NetBSD__) char *cleanup_command = NULL; -#ifdef DEBUG - int result; -#endif if ((config->tun_device == NULL) || config->persistent_tun_device) { return; @@ -102,13 +100,7 @@ static void cleanup_old_device(struct config *config, asprintf(&cleanup_command, "/sbin/ifconfig %s down delete > /dev/null 2>&1", config->tun_device); - DEBUGP("running: '%s'\n", cleanup_command); -#ifdef DEBUG - result = system(cleanup_command); -#else - system(cleanup_command); -#endif - DEBUGP("result: %d\n", result); + verbose_system(cleanup_command); free(cleanup_command); #endif /* defined(__NetBSD__) */ } @@ -119,9 +111,11 @@ static void cleanup_old_device(struct config *config, static void check_remote_address(struct config *config, struct local_netdev *netdev) { - if (is_ip_local(&config->live_remote_ip)) { - die("error: live_remote_ip %s is not remote\n", - config->live_remote_ip_string); + for (uint i = 0; i < config->live_paths_cnt; i++) { + if (is_ip_local(&config->live_paths[i].remote_ip)) { + die("error: remote_ip %s of path %d is not remote\n", + config->live_paths[i].remote_ip_string, i); + } } } @@ -166,8 +160,8 @@ static void create_device(struct config *config, struct local_netdev *netdev) DEBUGP("utun index: '%d'\n", netdev->index); if (config->mtu != TUN_DRIVER_DEFAULT_MTU) { asprintf(&command, "ifconfig %s mtu %d", netdev->name, config->mtu); - if (system(command) < 0) - die("Error executing %s\n", command); + if(verbose_system(command) != STATUS_OK) + die(""); free(command); } } @@ -199,7 +193,7 @@ static void create_device(struct config *config, struct local_netdev *netdev) tun_fd = open(tun_path, O_RDWR); #if defined(__FreeBSD__) if ((tun_fd < 0) && (errno == ENOENT)) { - if (system("kldload -q if_tun") < 0) { + if (verbose_system("kldload -q if_tun") != STATUS_OK) { die_perror("kldload -q if_tun"); } tun_fd = open(tun_path, O_RDWR); @@ -275,8 +269,8 @@ static void create_device(struct config *config, struct local_netdev *netdev) char *command; asprintf(&command, "ethtool -s %s speed %u autoneg off", netdev->name, config->speed); - if (system(command) < 0) - die("Error executing %s\n", command); + if(verbose_system(command) != STATUS_OK) + die(""); free(command); /* Need to bring interface down and up so the interface speed @@ -284,8 +278,8 @@ static void create_device(struct config *config, struct local_netdev *netdev) * used by TCP's cwnd bound. */ asprintf(&command, "ifconfig %s down; sleep 1; ifconfig %s up; " "sleep 1", netdev->name, netdev->name); - if (system(command) < 0) - die("Error executing %s\n", command); + if(verbose_system(command) != STATUS_OK) + die(""); free(command); } @@ -293,8 +287,8 @@ static void create_device(struct config *config, struct local_netdev *netdev) char *command; asprintf(&command, "ifconfig %s mtu %d", netdev->name, config->mtu); - if (system(command) < 0) - die("Error executing %s\n", command); + if(verbose_system(command) != STATUS_OK) + die(""); free(command); } #endif @@ -350,39 +344,40 @@ static void route_traffic_to_device(struct config *config, struct local_netdev *netdev) { char *route_command = NULL; + + for (uint i = 0; i < config->live_paths_cnt; i++) { + struct path *path = &config->live_paths[i]; + #if defined(linux) - asprintf(&route_command, - "ip route del %s > /dev/null 2>&1 ; " - "ip route add %s dev %s via %s > /dev/null 2>&1", - config->live_remote_prefix_string, - config->live_remote_prefix_string, - netdev->name, - config->live_gateway_ip_string); -#else - if (config->wire_protocol == AF_INET) { asprintf(&route_command, - "route delete %s > /dev/null 2>&1 ; " - "route add %s %s > /dev/null", - config->live_remote_prefix_string, - config->live_remote_prefix_string, - config->live_gateway_ip_string); - } else if (config->wire_protocol == AF_INET6) { - asprintf(&route_command, - "route delete -inet6 %s > /dev/null 2>&1 ; " - "route add -inet6 %s %s > /dev/null", - config->live_remote_prefix_string, - config->live_remote_prefix_string, - config->live_gateway_ip_string); - } else { - assert(!"bad wire protocol"); - } + "ip route del %s > /dev/null 2>&1 ; " + "ip route add %s dev %s via %s > /dev/null 2>&1", + path->remote_prefix_string, + path->remote_prefix_string, + netdev->name, + path->gateway_ip_string); +#else + if (config->wire_protocol == AF_INET) { + asprintf(&route_command, + "route delete %s > /dev/null 2>&1 ; " + "route add %s %s > /dev/null", + path->remote_prefix_string, + path->remote_prefix_string, + path->gateway_ip_string); + } else if (config->wire_protocol == AF_INET6) { + asprintf(&route_command, + "route delete -inet6 %s > /dev/null 2>&1 ; " + "route add -inet6 %s %s > /dev/null", + path->remote_prefix_string, + path->remote_prefix_string, + path->gateway_ip_string); + } else { + assert(!"bad wire protocol"); + } #endif /* defined(linux) */ - int result = system(route_command); - if ((result == -1) || (WEXITSTATUS(result) != 0)) { - die("error executing route command '%s'\n", - route_command); + verbose_system(route_command); + free(route_command); } - free(route_command); } struct netdev *local_netdev_new(struct config *config) @@ -400,11 +395,14 @@ struct netdev *local_netdev_new(struct config *config) bring_up_device(netdev); #endif - net_setup_dev_address(netdev->name, - &config->live_local_ip, - config->live_prefix_len, - &config->live_local_linklocal_ip, - &config->live_gateway_ip); + for (uint i = 0; i < config->live_paths_cnt; i++) { + struct path *path = &config->live_paths[i]; + net_setup_dev_address(netdev->name, + &path->local_ip, + path->prefix_len, + &path->local_linklocal_ip, + &path->gateway_ip); + } route_traffic_to_device(config, netdev); netdev->psock = packet_socket_new(netdev->name); @@ -412,7 +410,7 @@ struct netdev *local_netdev_new(struct config *config) /* Make sure we only see packets from the machine under test. */ packet_socket_set_filter(netdev->psock, NULL, - &config->live_local_ip); /* client IP */ + config->live_paths, config->live_paths_cnt); #endif /* !defined(linux) */ return (struct netdev *)netdev; @@ -433,7 +431,7 @@ static void local_netdev_free(struct netdev *a_netdev) asprintf(&cleanup_command, "/sbin/ifconfig %s destroy > /dev/null 2>&1", netdev->name); - system(cleanup_command); + verbose_system(cleanup_command); free(cleanup_command); } #endif diff --git a/gtests/net/packetdrill/packet.c b/gtests/net/packetdrill/packet.c index 8916bffd..81c37ebc 100644 --- a/gtests/net/packetdrill/packet.c +++ b/gtests/net/packetdrill/packet.c @@ -162,6 +162,8 @@ static struct packet *packet_copy_with_headroom(struct packet *old_packet, packet->ip_bytes = old_packet->ip_bytes; packet->direction = old_packet->direction; + packet->ip_src_index = old_packet->ip_src_index; + packet->ip_dst_index = old_packet->ip_dst_index; packet->time_usecs = old_packet->time_usecs; packet->flags = old_packet->flags; packet->tos_chk = old_packet->tos_chk; @@ -282,6 +284,9 @@ struct packet *packet_encapsulate(struct packet *outer, struct packet *inner) packet->ip_bytes = outer->ip_bytes + inner->ip_bytes; + packet->ip_src_index = inner->ip_src_index; + packet->ip_dst_index = inner->ip_dst_index; + return packet; } diff --git a/gtests/net/packetdrill/packet.h b/gtests/net/packetdrill/packet.h index 7c5467d1..90c4eaaa 100644 --- a/gtests/net/packetdrill/packet.h +++ b/gtests/net/packetdrill/packet.h @@ -77,6 +77,8 @@ struct packet { u32 buffer_bytes; /* bytes of space in data buffer */ u32 ip_bytes; /* bytes in outermost IP hdrs/payload */ enum direction_t direction; /* direction packet is traveling */ + int ip_src_index; + int ip_dst_index; /* Metadata about all the headers in the packet, including all * layers of encapsulation, from outer to inner, starting from diff --git a/gtests/net/packetdrill/packet_socket.h b/gtests/net/packetdrill/packet_socket.h index 769378c5..49b9c24d 100644 --- a/gtests/net/packetdrill/packet_socket.h +++ b/gtests/net/packetdrill/packet_socket.h @@ -34,6 +34,7 @@ #include "ethernet.h" #include "ip_address.h" #include "packet.h" +#include "path.h" struct packet_socket; @@ -47,7 +48,7 @@ extern void packet_socket_free(struct packet_socket *packet_socket); extern void packet_socket_set_filter( struct packet_socket *psock, const struct ether_addr *client_ether_addr, - const struct ip_address *client_live_ip); + const struct path *paths, uint paths_cnt); /* Send the given packet using writev. Return STATUS_OK on success, * or STATUS_ERR if writev returns an error. diff --git a/gtests/net/packetdrill/packet_socket_linux.c b/gtests/net/packetdrill/packet_socket_linux.c index 1c0d131d..b09eeb69 100644 --- a/gtests/net/packetdrill/packet_socket_linux.c +++ b/gtests/net/packetdrill/packet_socket_linux.c @@ -103,78 +103,124 @@ static void packet_socket_setup(struct packet_socket *psock) ioctl(psock->packet_fd, SIOCGSTAMP, &tv); } +static int count_local_family(const struct path *paths, int paths_cnt, int family) { + int count = 0; + for (int i = 0; i < paths_cnt; i++) { + if(paths[i].local_ip.address_family == family) + count++; + } + return count; +} + /* Add a filter so we only sniff packets we want. */ void packet_socket_set_filter(struct packet_socket *psock, const struct ether_addr *client_ether_addr, - const struct ip_address *client_live_ip) + const struct path *paths, uint paths_cnt) { const u8 *client_ether = client_ether_addr->ether_addr_octet; - struct sock_fprog bpfcode; - struct sock_filter bpf_ipv4_src[] = { - /* this filter works for ethernet interfaces: */ - /* tcpdump -p -n -s 0 -i lo -dd - * "ether src 11:22:33:44:55:66 and ip src 1.2.3.4" - */ - { 0x20, 0, 0, 0x00000008 }, - { 0x15, 0, 7, 0x33445566 }, /* ether: 33:44:55:66 */ - { 0x28, 0, 0, 0x00000006 }, - { 0x15, 0, 5, 0x00001122 }, /* ether: 11:22 */ - { 0x28, 0, 0, 0x0000000c }, - { 0x15, 0, 3, 0x00000800 }, - { 0x20, 0, 0, 0x0000001a }, - { 0x15, 0, 1, 0x01020304 }, /* IPv4: 1.2.3.4 */ - { 0x6, 0, 0, 0x0000ffff }, - { 0x6, 0, 0, 0x00000000 }, - }; - struct sock_filter bpf_ipv6_src[] = { - /* this filter works for ethernet interfaces: */ - /* tcpdump -p -n -s 0 -i lo -dd - * "ether src 11:22:33:44:55:66 and ip6 src 1:2:3:4:5:6:7:8" */ - { 0x20, 0, 0, 0x00000008 }, - { 0x15, 0, 13, 0x33445566 }, /* ether: 33:44:55:66 */ - { 0x28, 0, 0, 0x00000006 }, - { 0x15, 0, 11, 0x00001122 }, /* ether: 11:22 */ - { 0x28, 0, 0, 0x0000000c }, - { 0x15, 0, 9, 0x000086dd }, - { 0x20, 0, 0, 0x00000016 }, - { 0x15, 0, 7, 0x00010002 }, /* IPv6: 1:2 */ - { 0x20, 0, 0, 0x0000001a }, - { 0x15, 0, 5, 0x00030004 }, /* IPv6: 3:4 */ - { 0x20, 0, 0, 0x0000001e }, - { 0x15, 0, 3, 0x00050006 }, /* IPv6: 5:6 */ - { 0x20, 0, 0, 0x00000022 }, - { 0x15, 0, 1, 0x00070008 }, /* IPv6: 7:8 */ - { 0x6, 0, 0, 0x0000ffff }, - { 0x6, 0, 0, 0x00000000 }, - }; - - if (client_live_ip->address_family == AF_INET) { - /* Fill in the client-side IPv6 address to look for. */ - bpf_ipv4_src[7].k = ntohl(client_live_ip->ip.v4.s_addr); + int v4_cnt = count_local_family(paths, paths_cnt, AF_INET); + int v6_cnt = count_local_family(paths, paths_cnt, AF_INET6); + + /* summ lines of filter instructions */ + int filter_lines = 4 + 2; /* ether + return */ + if (v4_cnt > 0) + filter_lines += 3 + v4_cnt; + if (v6_cnt > 0) + filter_lines += 3 + v6_cnt; + + struct sock_filter *bpf_src, *bpf_src_next; + bpf_src = bpf_src_next = calloc(filter_lines, sizeof(struct sock_filter)); + + /* attach flter for ether adddress */ + *bpf_src_next = (struct sock_filter) { 0x20, 0, 0, 0x00000008 }; + bpf_src_next++; + *bpf_src_next = (struct sock_filter) { 0x15, 0, 7, 0x33445566 }; /* ether: 33:44:55:66 */ + bpf_src_next->k = (((u32)client_ether[2] << 24) | + ((u32)client_ether[3] << 16) | + ((u32)client_ether[4] << 8) | + ((u32)client_ether[5])); + bpf_src_next++; + *bpf_src_next = (struct sock_filter) { 0x28, 0, 0, 0x00000006 }; + bpf_src_next++; + *bpf_src_next = (struct sock_filter) { 0x15, 0, 5, 0x00001122 }; /* ether: 11:22 */ + bpf_src_next->k = (((u32)client_ether[0] << 8) | + ((u32)client_ether[1])); + bpf_src_next++; + + if (v4_cnt > 0) { + /* attach flter for ipv4 adddress */ + *bpf_src_next = (struct sock_filter) { 0x28, 0, 0, 0x0000000c }; + bpf_src_next++; + *bpf_src_next = (struct sock_filter) { 0x15, 0, 3, 0x00000800 }; + bpf_src_next++; + *bpf_src_next = (struct sock_filter) { 0x20, 0, 0, 0x0000001a }; + bpf_src_next++; + + /* Fill in the client-side IPv4 address to look for. */ + for (int i = 0; i < paths_cnt; i++) { + const struct path *path = &paths[i]; + + if (path->local_ip.address_family != AF_INET) + continue; + + *bpf_src_next = (struct sock_filter) { 0x15, 0, 1, 0x01020304 }; /* IPv4: 1.2.3.4 */ + bpf_src_next->k = ntohl(path->local_ip.ip.v4.s_addr); + bpf_src_next++; + } + } + + if (v6_cnt > 0) { + /* attach flter for ipv6 adddress */ + *bpf_src_next = (struct sock_filter) { 0x28, 0, 0, 0x0000000c }; + bpf_src_next++; + *bpf_src_next = (struct sock_filter) { 0x15, 0, 9, 0x000086dd }; + bpf_src_next++; + + struct sock_filter bpf_ipv6_src[] = { + { 0x20, 0, 0, 0x00000016 }, + { 0x20, 0, 0, 0x0000001a }, + { 0x20, 0, 0, 0x0000001e }, + { 0x20, 0, 0, 0x00000022 } + }; - bpfcode.len = ARRAY_SIZE(bpf_ipv4_src); - bpfcode.filter = bpf_ipv4_src; - } else if (client_live_ip->address_family == AF_INET6) { /* Fill in the client-side IPv6 address to look for. */ - bpf_ipv6_src[7].k = ntohl(client_live_ip->ip.v6.s6_addr32[0]); - bpf_ipv6_src[9].k = ntohl(client_live_ip->ip.v6.s6_addr32[1]); - bpf_ipv6_src[11].k = ntohl(client_live_ip->ip.v6.s6_addr32[2]); - bpf_ipv6_src[13].k = ntohl(client_live_ip->ip.v6.s6_addr32[3]); + for (int i = 0; i < 4; i++) { + *bpf_src_next = bpf_ipv6_src[i]; + bpf_src_next++; - bpfcode.len = ARRAY_SIZE(bpf_ipv6_src); - bpfcode.filter = bpf_ipv6_src; - } else { - assert(!"bad address family"); + /* check each two blocks */ + for (int i = 0; i < paths_cnt; i++) { + const struct path *path = &paths[i]; + + if (path->local_ip.address_family != AF_INET6) + continue; + + *bpf_src_next = (struct sock_filter) { 0x15, 0, 7, 0x00010002 }, /* IPv6: 1:2 */ + bpf_src_next->k = ntohl(path->local_ip.ip.v6.s6_addr32[i]); + bpf_src_next++; + } + + } } - /* Fill in the client-side ethernet address to look for. */ - bpfcode.filter[1].k = (((u32)client_ether[2] << 24) | - ((u32)client_ether[3] << 16) | - ((u32)client_ether[4] << 8) | - ((u32)client_ether[5])); - bpfcode.filter[3].k = (((u32)client_ether[0] << 8) | - ((u32)client_ether[1])); + /* attach returns */ + *bpf_src_next = (struct sock_filter) { 0x6, 0, 0, 0x0000ffff }; + bpf_src_next++; + *bpf_src_next = (struct sock_filter) { 0x6, 0, 0, 0x00000000 }; + + /* bpf_src_next sould now be at the end of the buffer */ + assert(bpf_src_next == bpf_src + filter_lines); + + /* update length of jumps to the end (return false) */ + for (int i = -1; i < filter_lines; i++, bpf_src_next--) { + if (bpf_src_next->code == 0x15) + bpf_src_next->jf = i; + } + + struct sock_fprog bpfcode; + bpfcode.len = filter_lines; + bpfcode.filter = bpf_src; if (debug_logging) { int i; diff --git a/gtests/net/packetdrill/packet_socket_pcap.c b/gtests/net/packetdrill/packet_socket_pcap.c index e6aad12c..3009559b 100644 --- a/gtests/net/packetdrill/packet_socket_pcap.c +++ b/gtests/net/packetdrill/packet_socket_pcap.c @@ -124,32 +124,43 @@ static void packet_socket_setup(struct packet_socket *psock) /* Add a filter so we only sniff packets we want. */ void packet_socket_set_filter(struct packet_socket *psock, const struct ether_addr *client_ether_addr, - const struct ip_address *client_live_ip) + const struct path *paths, uint paths_cnt) { const u8 *client_ether; struct bpf_program bpf_code; + size_t filter_str_len; char *filter_str = NULL; char client_live_ip_string[ADDR_STR_LEN]; - - ip_to_string(client_live_ip, client_live_ip_string); + FILE *stream = open_memstream(&filter_str, &filter_str_len); if (client_ether_addr != NULL) { client_ether = client_ether_addr->ether_addr_octet; - asprintf(&filter_str, - "ether src %02x:%02x:%02x:%02x:%02x:%02x and %s src %s", + fprintf(stream, + "ether src %02x:%02x:%02x:%02x:%02x:%02x and ", client_ether[0], client_ether[1], client_ether[2], client_ether[3], client_ether[4], - client_ether[5], - client_live_ip->address_family == AF_INET6 ? "ip6" : "ip", - client_live_ip_string); - } else - asprintf(&filter_str, - "%s src %s", - client_live_ip->address_family == AF_INET6 ? "ip6" : "ip", + client_ether[5]); + } + + fprintf(stream, "( "); + + for (int i = 0; i < paths_cnt; i++) { + if (i != 0) + fprintf(stream, " or "); + const struct path *path = &paths[i]; + ip_to_string(&path->local_ip, client_live_ip_string); + fprintf(stream, "%s src %s", + path->local_ip.address_family == AF_INET6 ? "ip6" : "ip", client_live_ip_string); + } + + fprintf(stream, " )"); + + fclose(stream); + DEBUGP("setting BPF filter: %s\n", filter_str); if (pcap_compile(psock->pcap, &bpf_code, filter_str, 1, 0) != 0) diff --git a/gtests/net/packetdrill/packet_to_string.c b/gtests/net/packetdrill/packet_to_string.c index 11d7ca53..a800ac93 100644 --- a/gtests/net/packetdrill/packet_to_string.c +++ b/gtests/net/packetdrill/packet_to_string.c @@ -343,6 +343,13 @@ int packet_to_string(struct packet *packet, int header_count = packet_header_count(packet); int limit; + if (packet->ip_src_index != 0 || packet->ip_dst_index != 0) { + if (packet->ip_src_index == packet->ip_dst_index) + fprintf(s, "[%d] ", packet->ip_src_index); + else + fprintf(s, "[%d>%d] ", packet->ip_src_index, packet->ip_dst_index); + } + /* Print any encapsulation headers preceding layer 3 and 4 headers. */ if (packet->flags & FLAGS_UDP_ENCAPSULATED) limit = header_count - 3; diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y index 5f3cbbaa..452d907d 100644 --- a/gtests/net/packetdrill/parser.y +++ b/gtests/net/packetdrill/parser.y @@ -160,6 +160,9 @@ struct invocation *invocation; bool ignore_ts_val = false; bool absolute_ts_ecr = false; +/* Temporary variable to remember which address is referenced with '...' */ +int addr_ellipsis_counter = 0; + /* Copy the script contents into our single linear buffer. */ void copy_script(const char *script_buffer, struct script *script) { @@ -494,6 +497,7 @@ static struct tcp_option *new_tcp_exp_fast_open_option(const char *cookie_string enum direction_t direction; u8 ip_ecn; struct tos_spec tos_spec; + struct ip_path ip_path; struct ip_info ip_info; struct mpls_stack *mpls_stack; struct mpls mpls_stack_entry; @@ -665,6 +669,7 @@ static struct tcp_option *new_tcp_exp_fast_open_option(const char *cookie_string %type abs_integer %type ignore_integer %type direction +%type ip_path %type ip_info opt_ip_info %type tos_spec %type ip_ecn @@ -689,6 +694,7 @@ static struct tcp_option *new_tcp_exp_fast_open_option(const char *cookie_string %type opt_tcp_fast_open_cookie tcp_fast_open_cookie %type opt_note note word_list %type option_flag option_value script +%type option_addrs %type opt_window %type opt_urg_ptr %type opt_ack @@ -869,14 +875,14 @@ option_flag : OPTION { $$ = $1; } ; +/* TODO: just read the whole remaining string, process_option does checking */ option_value -: INTEGER { $$ = strdup(yytext); } -| WORD { $$ = $1; } -| STRING { $$ = $1; } -| IPV4_ADDR { $$ = $1; } -| IPV6_ADDR { $$ = $1; } -| IPV4 { $$ = strdup("ipv4"); } -| IPV6 { $$ = strdup("ipv6"); } +: INTEGER { $$ = strdup(yytext); } +| WORD { $$ = $1; } +| STRING { $$ = $1; } +| option_addrs { $$ = $1; } +| IPV4 { $$ = strdup("ipv4"); } +| IPV6 { $$ = strdup("ipv6"); } | WORD '=' INTEGER { /* For consistency, allow syntax like: --define=PROTO=132 */ char *lhs = $1; @@ -911,6 +917,25 @@ option_value } ; +option_addrs +: IPV4_ADDR { $$ = $1; } +| IPV6_ADDR { $$ = $1; } +| option_addrs '/' INTEGER { + asprintf(&($$), "%s/%lld", $1, $3); + free($1); +} +| option_addrs ',' IPV4_ADDR { + asprintf(&($$), "%s,%s", $1, $3); + free($1); + free($3); +} +| option_addrs ',' IPV6_ADDR { + asprintf(&($$), "%s,%s", $1, $3); + free($1); + free($3); +} +; + opt_init_command : { } | init_command { } @@ -1077,12 +1102,14 @@ sctp_packet_spec $3.src_port, $3.dst_port, $4.tag, $4.bad_crc32c, $7, $5.udp_src_port, $5.udp_dst_port, - &error); + in_config, &error); if (inner == NULL) { assert(error != NULL); semantic_error(error); free(error); } + inner->ip_src_index = $2.ip_path.src_index; + inner->ip_dst_index = $2.ip_path.dst_index; $$ = packet_encapsulate_and_free(outer, inner); } @@ -1101,6 +1128,8 @@ sctp_packet_spec semantic_error(error); free(error); } + inner->ip_src_index = $2.ip_path.src_index; + inner->ip_dst_index = $2.ip_path.dst_index; $$ = packet_encapsulate_and_free(outer, inner); } @@ -2020,7 +2049,7 @@ sctp_reconfig_chunk_spec opt_parameter_list_spec : ',' ELLIPSIS { $$ = NULL; } | { $$ = sctp_parameter_list_new(); } -| ',' sctp_parameter_list_spec { $$ = $2; } +| ',' sctp_parameter_list_spec { $$ = $2; addr_ellipsis_counter = 0; } ; sctp_parameter_list_spec @@ -2113,10 +2142,12 @@ sctp_ipv4_address_parameter_spec semantic_error("Invalid address"); } free($5); - $$ = sctp_ipv4_address_parameter_new(&addr); + $$ = sctp_ipv4_address_parameter_new(&addr, 0); } | IPV4_ADDRESS '[' ADDR '=' ELLIPSIS ']' { - $$ = sctp_ipv4_address_parameter_new(NULL); + if (addr_ellipsis_counter > in_config->live_paths_cnt) + semantic_error("To few path specified"); + $$ = sctp_ipv4_address_parameter_new(NULL, addr_ellipsis_counter++); } sctp_ipv6_address_parameter_spec @@ -2127,10 +2158,12 @@ sctp_ipv6_address_parameter_spec semantic_error("Invalid address"); } free($5); - $$ = sctp_ipv6_address_parameter_new(&addr); + $$ = sctp_ipv6_address_parameter_new(&addr, 0); } | IPV6_ADDRESS '[' ADDR '=' ELLIPSIS ']' { - $$ = sctp_ipv6_address_parameter_new(NULL); + if (addr_ellipsis_counter > in_config->live_paths_cnt) + semantic_error("To few path specified"); + $$ = sctp_ipv6_address_parameter_new(NULL, addr_ellipsis_counter++); } sctp_state_cookie_parameter_spec @@ -2468,6 +2501,8 @@ tcp_packet_spec semantic_error(error); free(error); } + inner->ip_src_index = $2.ip_path.src_index; + inner->ip_dst_index = $2.ip_path.dst_index; $$ = packet_encapsulate_and_free(outer, inner); } @@ -2782,6 +2817,7 @@ tos_spec | ECN ECT01 { $$.check = TOS_CHECK_ECN_ECT01; $$.value = 0; + } ; @@ -2792,6 +2828,23 @@ ip_ecn | CE { $$ = IP_ECN_CE; } ; +ip_path +: INTEGER '>' INTEGER { + if ($1 >= in_config->live_paths_cnt) + semantic_error("Path identifier out of range"); + $$.src_index = $1; + if ($3 >= in_config->live_paths_cnt) + semantic_error("Path identifier out of range"); + $$.dst_index = $3; +} +| INTEGER { + if ($1 >= in_config->live_paths_cnt) + semantic_error("Path identifier out of range"); + $$.src_index = $1; + $$.dst_index = $1; +} +; + opt_ack_flag : { $$ = strdup(""); @@ -3001,9 +3054,32 @@ opt_ip_info $$.tos.value = 0; $$.flow_label = 0; $$.ttl = 0; + $$.ip_path.src_index = 0; + $$.ip_path.dst_index = 0; +} +| '(' ip_info ')' { + $$ = $2; + $$.ip_path.src_index = 0; + $$.ip_path.dst_index = 0; +} +| '[' ip_info ']' { + $$ = $2; + $$.ip_path.src_index = 0; + $$.ip_path.dst_index = 0; +} +| '[' ip_path ']' { + $$.tos.check = TOS_CHECK_NONE; + $$.tos.value = 0; + $$.flow_label = 0; + $$.ttl = 0; + $$.ip_path.src_index = $2.src_index; + $$.ip_path.dst_index = $2.dst_index; +} +| '[' ip_path ',' ip_info ']' { + $$ = $4; + $$.ip_path.src_index = $2.src_index; + $$.ip_path.dst_index = $2.dst_index; } -| '(' ip_info ')' { $$ = $2; } -| '[' ip_info ']' { $$ = $2; } ; seq diff --git a/gtests/net/packetdrill/path.h b/gtests/net/packetdrill/path.h new file mode 100644 index 00000000..c2cb001e --- /dev/null +++ b/gtests/net/packetdrill/path.h @@ -0,0 +1,32 @@ +#ifndef __PATH_H__ +#define __PATH_H__ + +#include "ip_address.h" +#include "ip_prefix.h" + +struct path { + struct ip_address local_ip; /* local interface IP */ + struct ip_address local_linklocal_ip; /* IPv6 local link-local address */ + struct ip_address remote_ip; /* remote interface IP */ + struct ip_prefix remote_prefix; /* remote prefix under test */ + struct ip_address gateway_ip; /* gateway interface IP */ + struct ip_address gateway_linklocal_ip; /* IPv6 gateway link-local address */ + + char local_ip_string[ADDR_STR_LEN]; /* human-readable IP */ + char local_linklocal_ip_string[ADDR_STR_LEN]; /* human-readable IP */ + char remote_ip_string[ADDR_STR_LEN]; /* human-readable IP */ + char remote_prefix_string[ADDR_STR_LEN]; /* / */ + + char gateway_ip_string[ADDR_STR_LEN]; /* local gateway IP */ + char gateway_linklocal_ip_string[ADDR_STR_LEN]; /* local gateway IP */ + char netmask_ip_string[ADDR_STR_LEN]; /* local netmask */ + + int prefix_len; /* IPv4/IPv6 interface prefix len */ +}; + +enum paths_address_types { + PATH_ADDRESS_LOCAL_TYPE, + PATH_ADDRESS_REMOTE_TYPE +}; + +#endif /* __PATH_H__ */ diff --git a/gtests/net/packetdrill/run_packet.c b/gtests/net/packetdrill/run_packet.c index 09efba32..143c2989 100644 --- a/gtests/net/packetdrill/run_packet.c +++ b/gtests/net/packetdrill/run_packet.c @@ -33,6 +33,7 @@ #include #include "checksum.h" #include "gre.h" +#include "ip_address.h" #include "logging.h" #include "netdev.h" #include "packet.h" @@ -140,6 +141,24 @@ static void verbose_packet_dump(struct state *state, const char *type, } } +static int find_local_path_for_ip(struct config *config, struct ip_address *ip) { + for (uint i = 0; i < config->live_paths_cnt; i++) { + struct path *path = &config->live_paths[i]; + if (is_equal_ip(ip, &path->local_ip)) + return i; + } + return STATUS_ERR; +} + +static int find_remote_path_for_ip(struct config *config, struct ip_address *ip) { + for (uint i = 0; i < config->live_paths_cnt; i++) { + struct path *path = &config->live_paths[i]; + if (is_equal_ip(ip, &path->remote_ip)) + return i; + } + return STATUS_ERR; +} + /* See if the live packet matches the live 4-tuple of the socket under test. */ static struct socket *find_socket_for_live_packet( struct state *state, const struct packet *packet, @@ -147,28 +166,41 @@ static struct socket *find_socket_for_live_packet( { struct socket *socket = state->socket_under_test; /* shortcut */ - DEBUGP("find_connect_for_live_packet\n"); + DEBUGP("find_socket_for_live_packet\n"); if (socket == NULL) return NULL; - struct tuple packet_tuple, live_outbound, live_inbound; + bool matches_ports; + int local_path, remote_path; + struct tuple packet_tuple; get_packet_tuple(packet, &packet_tuple); + /* Is packet inbound to the socket under test? */ - socket_get_inbound(&socket->live, &live_inbound); - if (is_equal_tuple(&packet_tuple, &live_inbound)) { + matches_ports = packet_tuple.src.port == socket->live.remote.port + && packet_tuple.dst.port == socket->live.local.port; + local_path = find_local_path_for_ip(state->config, &packet_tuple.dst.ip); + remote_path = find_remote_path_for_ip(state->config, &packet_tuple.src.ip); + + if (matches_ports && local_path != STATUS_ERR && remote_path != STATUS_ERR) { *direction = DIRECTION_INBOUND; DEBUGP("inbound live packet, socket in state %d\n", socket->state); return socket; } + /* Is packet outbound from the socket under test? */ - socket_get_outbound(&socket->live, &live_outbound); - if (is_equal_tuple(&packet_tuple, &live_outbound)) { + matches_ports = packet_tuple.src.port == socket->live.local.port + && packet_tuple.dst.port == socket->live.remote.port; + local_path = find_local_path_for_ip(state->config, &packet_tuple.src.ip); + remote_path = find_remote_path_for_ip(state->config, &packet_tuple.dst.ip); + + if(matches_ports && local_path != STATUS_ERR && remote_path != STATUS_ERR) { *direction = DIRECTION_OUTBOUND; DEBUGP("outbound live packet, socket in state %d\n", - socket->state); + socket->state); return socket; } + return NULL; } @@ -199,9 +231,9 @@ static struct socket *setup_new_child_socket(struct state *state, const struct p /* Set up the live info for this socket based * on the script packet and our overall config. */ - socket->live.remote.ip = config->live_remote_ip; + socket->live.remote.ip = config->live_paths[0].remote_ip; socket->live.remote.port = htons(next_ephemeral_port(state)); - socket->live.local.ip = config->live_local_ip; + socket->live.local.ip = config->live_paths[0].local_ip; socket->live.local.port = htons(config->live_bind_port); socket->live.fd = -1; return socket; @@ -423,7 +455,7 @@ static struct socket *handle_connect_for_script_packet( socket->script.fd = -1; - socket->live.remote.ip = config->live_remote_ip; + socket->live.remote.ip = config->live_paths[0].remote_ip; socket->live.remote.port = htons(config->live_connect_port); socket->live.fd = -1; } @@ -484,8 +516,13 @@ static struct socket *find_connect_for_live_packet( !is_udp_match && !is_udplite_match) return NULL; - if (!is_equal_ip(&tuple.dst.ip, &socket->live.remote.ip) || - !is_equal_port(tuple.dst.port, socket->live.remote.port)) + bool matched_path = false; + for (uint i = 0; i < state->config->live_paths_cnt; i++) { + struct path *path = &state->config->live_paths[i]; + if (is_equal_ip(&tuple.dst.ip, &path->remote_ip)) + matched_path = true; + } + if (!matched_path || !is_equal_port(tuple.dst.port, socket->live.remote.port)) return NULL; *direction = DIRECTION_OUTBOUND; @@ -961,6 +998,26 @@ static int map_inbound_sctp_packet( return STATUS_OK; } +static inline struct ip_address path_get_local_ip(struct config *config, int pathNum) +{ + return config->live_paths[pathNum].local_ip; +} + +static inline struct ip_address path_get_remote_ip(struct config *config, int pathNum) +{ + return config->live_paths[pathNum].remote_ip; +} + +static inline struct ip_address path_get_gateway_linklocal_ip(struct config *config, int pathNum) +{ + return config->live_paths[pathNum].gateway_linklocal_ip; +} + +static inline struct ip_address path_get_local_linklocal_ip(struct config *config, int pathNum) +{ + return config->live_paths[pathNum].local_linklocal_ip; +} + /* Rewrite the IP and TCP, UDP, or ICMP fields in 'live_packet', mapping * inbound packet values (address 4-tuple and sequence numbers in seq, * ACK, SACK blocks) from script values to live values, so that we can @@ -977,10 +1034,14 @@ static int map_inbound_packet( struct tuple live_inbound; if ((live_packet->icmpv6 != NULL) && (live_packet->icmpv6->type == ICMPV6_ROUTER_ADVERTISEMENT)) { memset(&live_inbound, 0, sizeof(live_inbound)); - live_inbound.src.ip = config->live_gateway_linklocal_ip; - live_inbound.dst.ip = config->live_local_linklocal_ip; + live_inbound.src.ip = path_get_gateway_linklocal_ip(config, live_packet->ip_src_index); + live_inbound.dst.ip = path_get_local_linklocal_ip(config, live_packet->ip_dst_index); } else { socket_get_inbound(&socket->live, &live_inbound); + if (live_packet->ip_src_index != 0) + live_inbound.src.ip = path_get_remote_ip(config, live_packet->ip_src_index); + if (live_packet->ip_dst_index != 0) + live_inbound.dst.ip = path_get_local_ip(config, live_packet->ip_dst_index); } set_packet_tuple(live_packet, &live_inbound, config->udp_encaps != 0); if ((live_packet->icmpv4 != NULL) || (live_packet->icmpv6 != NULL)) @@ -1228,21 +1289,37 @@ static int map_outbound_live_packet( struct packet *live_packet, struct packet *actual_packet, struct packet *script_packet, - u8 udp_encaps, + struct config *config, char **error) { DEBUGP("map_outbound_live_packet\n"); - struct tuple live_packet_tuple, live_outbound, script_outbound; + struct tuple live_packet_tuple, script_outbound; /* Verify packet addresses are outbound and live for this socket. */ get_packet_tuple(live_packet, &live_packet_tuple); - socket_get_outbound(&socket->live, &live_outbound); - assert(is_equal_tuple(&live_packet_tuple, &live_outbound)); + + int local_path = find_local_path_for_ip(config, &live_packet_tuple.src.ip); + if (local_path == STATUS_ERR) { + char ip_string[ADDR_STR_LEN]; + asprintf(error, "no path configured for src ip %s", ip_to_string(&live_packet_tuple.src.ip, ip_string)); + return STATUS_ERR; + } else + actual_packet->ip_src_index = local_path; + + int remote_path = find_remote_path_for_ip(config, &live_packet_tuple.dst.ip); + if (remote_path == STATUS_ERR) { + char ip_string[ADDR_STR_LEN]; + asprintf(error, "no path configured for dst ip %s", ip_to_string(&live_packet_tuple.dst.ip, ip_string)); + return STATUS_ERR; + } else + actual_packet->ip_dst_index = remote_path; /* Rewrite 4-tuple to be outbound script values. */ socket_get_outbound(&socket->script, &script_outbound); - set_packet_tuple(actual_packet, &script_outbound, udp_encaps != 0); + script_outbound.src.ip = config->live_paths[local_path].local_ip; + script_outbound.dst.ip = config->live_paths[remote_path].remote_ip; + set_packet_tuple(actual_packet, &script_outbound, config->udp_encaps != 0); if (live_packet->sctp) { return map_outbound_live_sctp_packet(socket, live_packet, actual_packet, script_packet, error); @@ -3011,8 +3088,28 @@ static int verify_outbound_live_packet( /* Map live packet values into script space for easy comparison. */ if (map_outbound_live_packet( socket, live_packet, actual_packet, script_packet, - state->config->udp_encaps, error)) + state->config, error)) + goto out; + + /* Verify packet addresses matched expected values. */ + struct tuple live_packet_tuple; + get_packet_tuple(live_packet, &live_packet_tuple); + + if (live_packet_tuple.src.port != socket->live.local.port + || live_packet_tuple.dst.port != socket->live.remote.port) { + asprintf(error, "packet ports (%d>%d) do not match expected (%d>%d)", + live_packet_tuple.src.port, live_packet_tuple.dst.port, + socket->live.local.port, socket->live.remote.port); goto out; + } + + if (actual_packet->ip_src_index != script_packet->ip_src_index + || actual_packet->ip_dst_index != script_packet->ip_dst_index) { + asprintf(error, "packet ip indices [%d>%d] do not match expected [%d>%d]", + actual_packet->ip_src_index, actual_packet->ip_dst_index, + script_packet->ip_src_index, script_packet->ip_dst_index); + goto out; + } /* Verify actual IP, TCP/UDP header values matched expected ones. */ if (verify_outbound_live_headers(actual_packet, script_packet, @@ -3548,8 +3645,7 @@ static int do_inbound_script_packet( /* Start with a bit-for-bit copy of the packet from the script. */ struct packet *live_packet = packet_copy(packet); /* Map packet fields from script values to live values. */ - if (map_inbound_packet(socket, live_packet, state->config, - error)) + if (map_inbound_packet(socket, live_packet, state->config, error)) goto out; verbose_packet_dump(state, "inbound injected", live_packet, @@ -3760,7 +3856,7 @@ int abort_association(struct state *state, struct socket *socket) packet = new_sctp_packet(socket->address_family, DIRECTION_INBOUND, ip_info, 0, 0, -1, false, chunk_list, udp_src_port, udp_dst_port, - &error); + state->config, &error); if (packet == NULL) die("%s", error); /* Rewrite addresses and port to match inbound live traffic. */ diff --git a/gtests/net/packetdrill/run_system_call.c b/gtests/net/packetdrill/run_system_call.c index 22102c63..22d7bdbc 100644 --- a/gtests/net/packetdrill/run_system_call.c +++ b/gtests/net/packetdrill/run_system_call.c @@ -1885,15 +1885,37 @@ static int run_syscall_close(struct state *state, int script_fd, */ static int run_syscall_bind(struct state *state, struct sockaddr *live_addr, - socklen_t *live_addrlen, char **error) + socklen_t *live_addrlen, + int live_addrcnt, + char **error) { + socklen_t usedlen = 0; DEBUGP("run_syscall_bind\n"); - /* Fill in the live address we want to bind to */ - ip_to_sockaddr(&state->config->live_bind_ip, - state->config->live_bind_port, - live_addr, live_addrlen); + if (live_addrcnt > state->config->live_paths_cnt) { + asprintf(error, "requested more addresses (%d) than configured (%d)", + live_addrcnt, state->config->live_paths_cnt); + return STATUS_ERR; + } + + for (int i = 0; i < live_addrcnt; i++) { + if (*live_addrlen < usedlen + sizeof(struct sockaddr)) { + asprintf(error, "not enough space (%u) in live_addr for %d addresses", + *live_addrlen, live_addrcnt); + return STATUS_ERR; + } + + /* append i'th path address to live_addr */ + socklen_t addrlen = 0; + struct ip_address ip = state->config->ip_version == IP_VERSION_4_MAPPED_6 ? + ipv6_map_from_ipv4(state->config->live_paths[i].local_ip) : + state->config->live_paths[i].local_ip; + ip_to_sockaddr(&ip, state->config->live_bind_port, + (struct sockaddr *) (((char*)live_addr) + usedlen), &addrlen); + usedlen += addrlen; + } + *live_addrlen = usedlen; return STATUS_OK; } @@ -2000,7 +2022,7 @@ static int run_syscall_accept(struct state *state, socket->live.remote.ip = ip; socket->live.remote.port = port; - socket->live.local.ip = state->config->live_local_ip; + socket->live.local.ip = state->config->live_paths[0].local_ip; socket->live.local.port = htons(state->config->live_bind_port); socket->live.fd = live_accepted_fd; @@ -2031,6 +2053,7 @@ static int run_syscall_connect(struct state *state, bool must_be_new_socket, struct sockaddr *live_addr, socklen_t *live_addrlen, + int live_addrcnt, char **error) { struct socket *socket = NULL; @@ -2039,13 +2062,36 @@ static int run_syscall_connect(struct state *state, /* Fill in the live address we want to connect to */ if ((state->socket_under_test != NULL) && (state->socket_under_test->state == SOCKET_PASSIVE_PACKET_RECEIVED)) { + if (live_addrcnt != 1) { + asprintf(error, "live_addrcnt(%d) != 1", live_addrcnt); + return STATUS_ERR; + } ip_to_sockaddr(&state->socket_under_test->live.remote.ip, ntohs(state->socket_under_test->live.remote.port), live_addr, live_addrlen); } else { - ip_to_sockaddr(&state->config->live_connect_ip, - state->config->live_connect_port, - live_addr, live_addrlen); + socklen_t usedlen = 0; + if (live_addrcnt > state->config->live_paths_cnt) { + asprintf(error, "live_addrcnt(%d) > live_paths_cnt(%d)", live_addrcnt, state->config->live_paths_cnt); + return STATUS_ERR; + } + for (int i = 0; i < live_addrcnt; i++) { + if (*live_addrlen < usedlen + sizeof(struct sockaddr_storage)) { + asprintf(error, "not enough space (%u) in live_addr for %d addresses", *live_addrlen, live_addrcnt); + return STATUS_ERR; + } + + /* append i'th path address to live_addr */ + socklen_t addrlen = 0; + struct ip_address ip = state->config->ip_version == IP_VERSION_4_MAPPED_6 ? + ipv6_map_from_ipv4(state->config->live_paths[i].remote_ip) : + state->config->live_paths[i].remote_ip; + ip_to_sockaddr(&ip, state->config->live_connect_port, + (struct sockaddr *) (((char*)live_addr) + usedlen), &addrlen); + usedlen += addrlen; + } + + *live_addrlen = usedlen; } socket = find_socket_by_script_fd(state, script_fd); @@ -2064,7 +2110,7 @@ static int run_syscall_connect(struct state *state, ip_reset(&socket->script.local.ip); socket->script.remote.port = 0; socket->script.local.port = 0; - socket->live.remote.ip = state->config->live_remote_ip; + socket->live.remote.ip = state->config->live_paths[0].remote_ip; socket->live.remote.port = htons(state->config->live_connect_port); DEBUGP("success: setting socket to state %d\n", socket->state); return STATUS_OK; @@ -2159,7 +2205,7 @@ static int syscall_bind(struct state *state, struct syscall_spec *syscall, { int live_fd, script_fd, result; struct sockaddr_storage live_addr; - socklen_t live_addrlen; + socklen_t live_addrlen = sizeof(live_addr); if (check_arg_count(args, 3, error)) return STATUS_ERR; @@ -2173,7 +2219,7 @@ static int syscall_bind(struct state *state, struct syscall_spec *syscall, return STATUS_ERR; if (run_syscall_bind( state, - (struct sockaddr *)&live_addr, &live_addrlen, error)) + (struct sockaddr *)&live_addr, &live_addrlen, 1, error)) return STATUS_ERR; begin_syscall(state, syscall); @@ -2275,7 +2321,7 @@ static int syscall_connect(struct state *state, struct syscall_spec *syscall, if (run_syscall_connect( state, script_fd, true, - (struct sockaddr *)&live_addr, &live_addrlen, error)) + (struct sockaddr *)&live_addr, &live_addrlen, 1, error)) return STATUS_ERR; begin_syscall(state, syscall); @@ -2606,7 +2652,7 @@ static int syscall_sendto(struct state *state, struct syscall_spec *syscall, if (run_syscall_connect( state, script_fd, false, - (struct sockaddr *)&live_addr, &live_addrlen, error)) + (struct sockaddr *)&live_addr, &live_addrlen, 1, error)) return STATUS_ERR; buf = calloc(count, 1); @@ -2650,7 +2696,7 @@ static int syscall_sendmsg(struct state *state, struct syscall_spec *syscall, if ((msg->msg_name != NULL) && run_syscall_connect(state, script_fd, false, - msg->msg_name, &msg->msg_namelen, error)) + msg->msg_name, &msg->msg_namelen, 1, error)) goto error_out; if (msg->msg_flags != 0) { asprintf(error, "sendmsg ignores msg_flags field in msghdr"); @@ -6633,9 +6679,7 @@ static int syscall_sctp_bindx(struct state *state, struct syscall_spec *syscall, { #if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) int live_fd, script_fd, addrcnt, flags, result; - struct sockaddr_storage addrs; struct expression *addr_list; - socklen_t addrlen = sizeof(addrs); if (check_arg_count(args, 4, error)) return STATUS_ERR; @@ -6645,6 +6689,10 @@ static int syscall_sctp_bindx(struct state *state, struct syscall_spec *syscall, return STATUS_ERR; if (s32_arg(args, 2, &addrcnt, error)) return STATUS_ERR; + if (addrcnt > state->config->live_paths_cnt) { + asprintf(error, "there are not %d addresses specified", addrcnt); + return STATUS_ERR; + } if (s32_arg(args, 3, &flags, error)) return STATUS_ERR; addr_list = get_arg(args, 1, error); @@ -6652,10 +6700,12 @@ static int syscall_sctp_bindx(struct state *state, struct syscall_spec *syscall, return STATUS_ERR; if (ellipsis_arg(addr_list->value.list, 0, error)) return STATUS_ERR; - //TODO: Modify run_syscall_bind for multihoming + + struct sockaddr_storage addrs[addrcnt]; + socklen_t addrlen = sizeof(addrs); if (run_syscall_bind( - state, - (struct sockaddr *)&addrs, &addrlen, error)) + state, (struct sockaddr *)&addrs, + &addrlen, addrcnt, error)) return STATUS_ERR; begin_syscall(state, syscall); @@ -6678,9 +6728,7 @@ static int syscall_sctp_connectx(struct state *state, struct syscall_spec *sysca { #if defined(__FreeBSD__) || defined(linux) || (defined(__APPLE__) && defined(HAVE_SCTP)) || defined(__SunOS_5_11) int live_fd, script_fd, addrcnt, result; - struct sockaddr_storage live_addr; struct expression *addrs_expr, *assoc_expr; - socklen_t live_addrlen = sizeof(live_addr); sctp_assoc_t live_associd; if (check_arg_count(args, 4, error)) @@ -6696,10 +6744,13 @@ static int syscall_sctp_connectx(struct state *state, struct syscall_spec *sysca return STATUS_ERR; if (s32_arg(args, 2, &addrcnt, error)) return STATUS_ERR; - //TODO: modify for Multihoming + + struct sockaddr_storage live_addr[addrcnt]; + socklen_t live_addrlen = sizeof(live_addr); + if (run_syscall_connect( state, script_fd, true, - (struct sockaddr *)&live_addr, &live_addrlen, error)) + (struct sockaddr *)&live_addr, &live_addrlen, addrcnt, error)) return STATUS_ERR; begin_syscall(state, syscall); diff --git a/gtests/net/packetdrill/sctp_packet.c b/gtests/net/packetdrill/sctp_packet.c index 9ea01571..9e50227f 100644 --- a/gtests/net/packetdrill/sctp_packet.c +++ b/gtests/net/packetdrill/sctp_packet.c @@ -26,6 +26,8 @@ #include "sctp_packet.h" #include "ip_packet.h" #include "sctp.h" +#include "path.h" +#include "config.h" /* * ToDo: @@ -1815,7 +1817,7 @@ sctp_heartbeat_information_parameter_new(s64 len, struct sctp_byte_list *bytes) } struct sctp_parameter_list_item * -sctp_ipv4_address_parameter_new(struct in_addr *addr) +sctp_ipv4_address_parameter_new(struct in_addr *addr, int addr_index) { struct sctp_ipv4_address_parameter *parameter; u32 flags; @@ -1826,8 +1828,8 @@ sctp_ipv4_address_parameter_new(struct in_addr *addr) parameter->type = htons(SCTP_IPV4_ADDRESS_PARAMETER_TYPE); parameter->length = htons(sizeof(struct sctp_ipv4_address_parameter)); if (addr == NULL) { - parameter->addr.s_addr = htonl(INADDR_ANY); - flags |= FLAG_PARAMETER_VALUE_NOCHECK; + parameter->addr.s_addr = (in_addr_t) addr_index; + flags |= FLAG_PARAMETER_ADDRESS_IS_INDEX; } else { parameter->addr = *addr; } @@ -1837,7 +1839,7 @@ sctp_ipv4_address_parameter_new(struct in_addr *addr) } struct sctp_parameter_list_item * -sctp_ipv6_address_parameter_new(struct in6_addr *addr) +sctp_ipv6_address_parameter_new(struct in6_addr *addr, int addr_index) { struct sctp_ipv6_address_parameter *parameter; u32 flags; @@ -1848,8 +1850,8 @@ sctp_ipv6_address_parameter_new(struct in6_addr *addr) parameter->type = htons(SCTP_IPV6_ADDRESS_PARAMETER_TYPE); parameter->length = htons(sizeof(struct sctp_ipv6_address_parameter)); if (addr == NULL) { - parameter->addr = in6addr_any; - flags |= FLAG_PARAMETER_VALUE_NOCHECK; + parameter->addr.s6_addr[3] = (uint32_t) addr_index; + flags |= FLAG_PARAMETER_ADDRESS_IS_INDEX; } else { parameter->addr = *addr; } @@ -3066,6 +3068,7 @@ new_sctp_packet(int address_family, struct sctp_chunk_list *list, u16 udp_src_port, u16 udp_dst_port, + struct config *config, char **error) { struct packet *packet; /* the newly-allocated result packet */ @@ -3468,6 +3471,49 @@ new_sctp_packet(int address_family, } } + /* Map the address parameter in the INIT and INIT_ACK chunk */ + enum paths_address_types address_type = direction == DIRECTION_INBOUND ? + PATH_ADDRESS_REMOTE_TYPE : PATH_ADDRESS_LOCAL_TYPE; + for (chunk_item = list->first; + chunk_item != NULL; + chunk_item = chunk_item->next) { + if (chunk_item->chunk->type != SCTP_INIT_CHUNK_TYPE && + chunk_item->chunk->type != SCTP_INIT_ACK_CHUNK_TYPE) + continue; + for (parameter_item = chunk_item->parameter_list->first; + parameter_item != NULL; + parameter_item = parameter_item->next) { + switch(ntohs(parameter_item->parameter->type)) { + case SCTP_IPV4_ADDRESS_PARAMETER_TYPE: + if (parameter_item->flags & FLAG_PARAMETER_ADDRESS_IS_INDEX) { + struct sctp_ipv4_address_parameter *address_parameter = + (struct sctp_ipv4_address_parameter *) + parameter_item->parameter; + struct ip_address *ip_address = paths_get_address(config, + (int) address_parameter->addr.s_addr, + AF_INET, address_type); + address_parameter->addr = ip_address->ip.v4; + parameter_item->flags &= ~FLAG_PARAMETER_ADDRESS_IS_INDEX; + } + break; + case SCTP_IPV6_ADDRESS_PARAMETER_TYPE: + if (parameter_item->flags & FLAG_PARAMETER_ADDRESS_IS_INDEX) { + struct sctp_ipv6_address_parameter *address_parameter = + (struct sctp_ipv6_address_parameter *) + parameter_item->parameter; + struct ip_address *ip_address = paths_get_address(config, + (int) address_parameter->addr.s6_addr[3], + AF_INET6, address_type); + address_parameter->addr = ip_address->ip.v6; + parameter_item->flags &= ~FLAG_PARAMETER_ADDRESS_IS_INDEX; + } + break; + default: + break; + } + } + } + /* Allocate and zero out a packet object of the desired size */ packet = packet_new(overbook ? MAX_SCTP_DATAGRAM_BYTES : ip_bytes); memset(packet->buffer, 0, overbook ? MAX_SCTP_DATAGRAM_BYTES : ip_bytes); diff --git a/gtests/net/packetdrill/sctp_packet.h b/gtests/net/packetdrill/sctp_packet.h index e8c65f3c..2156b863 100644 --- a/gtests/net/packetdrill/sctp_packet.h +++ b/gtests/net/packetdrill/sctp_packet.h @@ -28,6 +28,7 @@ #include "types.h" #include "packet.h" #include "sctp.h" +#include "config.h" struct sctp_byte_list_item { struct sctp_byte_list_item *next; @@ -405,6 +406,7 @@ sctp_chunk_list_free(struct sctp_chunk_list *list); #define FLAG_PARAMETER_LENGTH_NOCHECK 0x00000002 #define FLAG_PARAMETER_VALUE_NOCHECK 0x00000004 #define FLAG_PARAMETER_PARTIAL 0x00000008 +#define FLAG_PARAMETER_ADDRESS_IS_INDEX 0x00000010 struct sctp_parameter_list_item * sctp_parameter_list_item_new(struct sctp_parameter *parameter, @@ -417,10 +419,10 @@ struct sctp_parameter_list_item * sctp_heartbeat_information_parameter_new(s64 len, struct sctp_byte_list *bytes); struct sctp_parameter_list_item * -sctp_ipv4_address_parameter_new(struct in_addr *addr); +sctp_ipv4_address_parameter_new(struct in_addr *addr, int addr_index); struct sctp_parameter_list_item * -sctp_ipv6_address_parameter_new(struct in6_addr *addr); +sctp_ipv6_address_parameter_new(struct in6_addr *addr, int addr_index); struct sctp_parameter_list_item * sctp_state_cookie_parameter_new(s64 len, u8 *cookie); @@ -570,6 +572,7 @@ extern struct packet *new_sctp_packet(int address_family, struct sctp_chunk_list *chunk_list, u16 udp_src_port, u16 udp_dst_port, + struct config *config, char **error); struct packet * diff --git a/gtests/net/packetdrill/system.c b/gtests/net/packetdrill/system.c index 1b17abe5..40af4a95 100644 --- a/gtests/net/packetdrill/system.c +++ b/gtests/net/packetdrill/system.c @@ -31,22 +31,43 @@ #include #include -int safe_system(const char *command, char **error) +#include "logging.h" + +int checked_system(const char *command, char **error) { - int status = system(command); - if (status == -1) { + int result = system(command); + if (result == -1) { asprintf(error, "%s", strerror(errno)); - return STATUS_ERR; - } - if (WIFSIGNALED(status) && - (WTERMSIG(status) == SIGINT || WTERMSIG(status) == SIGQUIT)) { + } else if (WIFSIGNALED(result) && + (WTERMSIG(result) == SIGINT || WTERMSIG(result) == SIGQUIT)) { asprintf(error, "got signal %d (%s)", - WTERMSIG(status), strsignal(WTERMSIG(status))); - return STATUS_ERR; + WTERMSIG(result), strsignal(WTERMSIG(result))); + } else if (WEXITSTATUS(result) != 0) { + asprintf(error, "non-zero status %d", WEXITSTATUS(result)); } - if (WEXITSTATUS(status) != 0) { - asprintf(error, "non-zero status %d", WEXITSTATUS(status)); + return result; +} + +int safe_system(const char *command, char **error) +{ + assert(*error == NULL); + checked_system(command, error); + if (*error != NULL) return STATUS_ERR; - } return STATUS_OK; } + +int verbose_system(const char *command) +{ + int result; + char *error = NULL; + + DEBUGP("running: '%s'\n", command); + result = checked_system(command, &error); + if (result != 0) { + DEBUGP("error: %s executing command '%s'\n", error, command); + } else { + DEBUGP("result: %d\n", result); + } + return result; +} diff --git a/gtests/net/packetdrill/system.h b/gtests/net/packetdrill/system.h index 1f7564d0..228152f6 100644 --- a/gtests/net/packetdrill/system.h +++ b/gtests/net/packetdrill/system.h @@ -32,4 +32,10 @@ */ extern int safe_system(const char *command, char **error); +/* Execute the given command with system(3). + * If debug logging is active logs the command and result / error. + * On success, returns STATUS_OK. On error returns STATUS_ERR. + */ +extern int verbose_system(const char *command); + #endif /* __SYSTEM_H__ */ diff --git a/gtests/net/packetdrill/types.h b/gtests/net/packetdrill/types.h index 5fb098ac..bfc98139 100644 --- a/gtests/net/packetdrill/types.h +++ b/gtests/net/packetdrill/types.h @@ -145,10 +145,16 @@ struct tos_spec { #define TTL_CHECK_NONE 255 +struct ip_path { + int src_index; + int dst_index; +}; + struct ip_info { struct tos_spec tos; u32 flow_label; u8 ttl; + struct ip_path ip_path; /* Only used to pass informaition inside the parser */ }; /* Type to handle ! for specifying absolute vs adjusted values. diff --git a/gtests/net/packetdrill/wire_client_netdev.c b/gtests/net/packetdrill/wire_client_netdev.c index 6c580990..bfcd1497 100644 --- a/gtests/net/packetdrill/wire_client_netdev.c +++ b/gtests/net/packetdrill/wire_client_netdev.c @@ -31,6 +31,7 @@ #include "logging.h" #include "net_utils.h" +#include "system.h" struct wire_client_netdev { struct netdev netdev; /* "inherit" from netdev */ @@ -53,9 +54,11 @@ static inline struct wire_client_netdev *to_client_netdev( static void check_remote_address(struct config *config, struct wire_client_netdev *netdev) { - if (is_ip_local(&config->live_remote_ip)) { - die("error: live_remote_ip %s is not remote\n", - config->live_remote_ip_string); + for (uint i = 0; i < config->live_paths_cnt; i++) { + if (is_ip_local(&config->live_paths[i].remote_ip)) { + die("error: remote_ip %s of path %d is not remote\n", + config->live_paths[i].remote_ip_string, i); + } } } @@ -65,43 +68,48 @@ static void route_traffic_to_wire_server(struct config *config, struct wire_client_netdev *netdev) { char *route_command = NULL; + + for (uint i = 0; i < config->live_paths_cnt; i++) { + struct path *path = &config->live_paths[i]; + #if defined(linux) - asprintf(&route_command, - "ip %s route del %s > /dev/null 2>&1 ; " - "ip %s route add %s dev %s via %s > /dev/null 2>&1", - (config->wire_protocol == AF_INET6) ? "-6" : "", - config->live_remote_prefix_string, - (config->wire_protocol == AF_INET6) ? "-6" : "", - config->live_remote_prefix_string, - netdev->name, - config->live_gateway_ip_string); -#else - if (config->wire_protocol == AF_INET) { - asprintf(&route_command, - "route delete %s > /dev/null 2>&1 ; " - "route add %s %s > /dev/null 2>&1", - config->live_remote_prefix_string, - config->live_remote_prefix_string, - config->live_gateway_ip_string); - } else if (config->wire_protocol == AF_INET6) { asprintf(&route_command, - "route delete -inet6 %s > /dev/null 2>&1 ; " - "route add -inet6 %s %s > /dev/null 2>&1", - config->live_remote_prefix_string, - config->live_remote_prefix_string, - config->live_gateway_ip_string); - } else { - assert(!"bad wire protocol"); - } + "ip %s route del %s > /dev/null 2>&1 ; " + "ip %s route add %s dev %s via %s > /dev/null 2>&1", + (config->wire_protocol == AF_INET6) ? "-6" : "", + path->remote_prefix_string, + (config->wire_protocol == AF_INET6) ? "-6" : "", + path->remote_prefix_string, + netdev->name, + path->gateway_ip_string); +#else + if (config->wire_protocol == AF_INET) { + asprintf(&route_command, + "route delete %s > /dev/null 2>&1 ; " + "route add %s %s > /dev/null 2>&1", + path->remote_prefix_string, + path->remote_prefix_string, + path->gateway_ip_string); + } else if (config->wire_protocol == AF_INET6) { + asprintf(&route_command, + "route delete -inet6 %s > /dev/null 2>&1 ; " + "route add -inet6 %s %s > /dev/null 2>&1", + path->remote_prefix_string, + path->remote_prefix_string, + path->gateway_ip_string); + } else { + assert(!"bad wire protocol"); + } #endif /* defined(linux) */ - /* We intentionally ignore failures and output to stderr, - * since they can happen if there is no previously existing - * route. - */ - system(route_command); + /* We intentionally ignore failures and output to stderr, + * since they can happen if there is no previously existing + * route. + */ + verbose_system(route_command); - free(route_command); + free(route_command); + } } struct netdev *wire_client_netdev_new(struct config *config) @@ -118,11 +126,14 @@ struct netdev *wire_client_netdev_new(struct config *config) check_remote_address(config, netdev); /* Add the client live local IP to our NIC, so we can send/receive */ - net_setup_dev_address(netdev->name, - &config->live_local_ip, - config->live_prefix_len, - &config->live_local_linklocal_ip, - &config->live_gateway_ip); + for (uint i = 0; i < config->live_paths_cnt; i++) { + struct path *path = &config->live_paths[i]; + net_setup_dev_address(netdev->name, + &path->local_ip, + path->prefix_len, + &path->local_linklocal_ip, + &path->gateway_ip); + } route_traffic_to_wire_server(config, netdev); diff --git a/gtests/net/packetdrill/wire_server_netdev.c b/gtests/net/packetdrill/wire_server_netdev.c index 5c37898d..69380c38 100644 --- a/gtests/net/packetdrill/wire_server_netdev.c +++ b/gtests/net/packetdrill/wire_server_netdev.c @@ -35,6 +35,7 @@ #include "packet.h" #include "packet_socket.h" #include "packet_parser.h" +#include "system.h" struct wire_server_netdev { struct netdev netdev; /* "inherit" from netdev */ @@ -76,7 +77,7 @@ void wire_server_netdev_init(const char *netdev_name) netdev_name); /* For now, intentionally ignoring errors. TODO: clean up. */ - system(command); + verbose_system(command); free(command); /* Block outgoing IPv6 "destination unreachable" messages, to @@ -89,7 +90,7 @@ void wire_server_netdev_init(const char *netdev_name) "ip6tables -F OUTPUT; " "ip6tables -A OUTPUT -p icmpv6 --icmpv6-type 1 -j DROP"); /* For now, intentionally ignoring. TODO: clean up. */ - system(command); + verbose_system(command); free(command); #endif } @@ -118,18 +119,20 @@ struct netdev *wire_server_netdev_new( * be using. TODO(ncardwell): make sure we don't delete our * primary host IP (the one matching our hostname). */ - net_setup_dev_address(netdev->name, - &config->live_gateway_ip, - config->live_prefix_len, - &config->live_gateway_linklocal_ip, - &config->live_gateway_ip); + for (uint i = 0; i < config->live_paths_cnt; i++) { + net_setup_dev_address(netdev->name, + &config->live_paths[i].gateway_ip, + config->live_paths[i].prefix_len, + &config->live_paths[i].gateway_linklocal_ip, + &config->live_paths[i].gateway_ip); + } netdev->psock = packet_socket_new(netdev->name); /* Make sure we only see packets from the machine under test. */ packet_socket_set_filter(netdev->psock, client_ether_addr, - &config->live_local_ip); /* client IP */ + config->live_paths, config->live_paths_cnt); return (struct netdev *)netdev; } @@ -140,9 +143,11 @@ static void wire_server_netdev_free(struct netdev *a_netdev) DEBUGP("wire_server_netdev_free\n"); - net_del_dev_address(netdev->name, - &netdev->config->live_gateway_ip, - netdev->config->live_prefix_len); + for (uint i = 0; i < netdev->config->live_paths_cnt; i++) { + net_del_dev_address(netdev->name, + &netdev->config->live_paths[i].gateway_ip, + netdev->config->live_paths[i].prefix_len); + } free(netdev->name); if (netdev->psock)