From 6a6ce245125d600ad07fc02638e96ba6c18d1b4b Mon Sep 17 00:00:00 2001 From: jcaiMR <111116206+jcaiMR@users.noreply.github.com> Date: Mon, 17 Jul 2023 13:01:09 +0800 Subject: [PATCH] fix dhcpv6 relay dual tor source interface selection issue (#42) --- src/config_interface.cpp | 10 +- src/config_interface.h | 3 + src/main.cpp | 17 +- src/relay.cpp | 405 +++++++++++++++++++++++++++++---------- src/relay.h | 89 +++++---- test/mock_relay.cpp | 233 +++++++++++++++------- test/mock_relay.h | 3 +- 7 files changed, 556 insertions(+), 204 deletions(-) diff --git a/src/config_interface.cpp b/src/config_interface.cpp index 7d7d04b..08191e4 100644 --- a/src/config_interface.cpp +++ b/src/config_interface.cpp @@ -108,6 +108,12 @@ void handleRelayNotification(swss::SubscriberStateTable &ipHelpersTable, std::un void processRelayNotification(std::deque &entries, std::unordered_map &vlans) { std::vector servers; + bool option_79_default = true; + bool interface_id_default = false; + + if (dual_tor_sock) { + interface_id_default = true; + } for (auto &entry: entries) { std::string vlan = kfvKey(entry); @@ -115,8 +121,8 @@ void processRelayNotification(std::deque &entries, std::vector fieldValues = kfvFieldsValues(entry); relay_config intf; - intf.is_option_79 = true; - intf.is_interface_id = false; + intf.is_option_79 = option_79_default; + intf.is_interface_id = interface_id_default; intf.interface = vlan; intf.mux_key = ""; intf.state_db = nullptr; diff --git a/src/config_interface.h b/src/config_interface.h index 96f221b..019838a 100644 --- a/src/config_interface.h +++ b/src/config_interface.h @@ -7,10 +7,13 @@ #include "select.h" #include "relay.h" +extern bool dual_tor_sock; + struct swssNotification { std::unordered_map vlans; swss::SubscriberStateTable *ipHelpersTable; }; + /** * @code void initialize_swss() * diff --git a/src/main.cpp b/src/main.cpp index 17101b6..7dd859f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,23 +4,32 @@ #include "config_interface.h" bool dual_tor_sock = false; +char loopback[IF_NAMESIZE] = "Loopback0"; static void usage() { - printf("Usage: ./dhcp6relay {-d}\n"); - printf("\t-d: enable dual tor option\n"); + printf("Usage: ./dhcp6relay [-u ]\n"); + printf("\tloopback interface: is the loopback interface for dual tor setup\n"); } int main(int argc, char *argv[]) { - if (argc > 1) { + if (argc > 2) { switch (argv[1][1]) { - case 'd': + case 'u': + if (strlen(argv[2]) != 0 && strlen(argv[2]) < IF_NAMESIZE) { + std::memset(loopback, 0, IF_NAMESIZE); + std::memcpy(loopback, argv[2], strlen(argv[2])); + } else { + syslog(LOG_ERR, "loopback interface name over length %d.\n", IF_NAMESIZE); + return 1; + } dual_tor_sock = true; break; default: fprintf(stderr, "%s: Unknown option\n", basename(argv[0])); usage(); + return 0; } } try { diff --git a/src/relay.cpp b/src/relay.cpp index 0746a9b..60de068 100644 --- a/src/relay.cpp +++ b/src/relay.cpp @@ -11,8 +11,6 @@ #include "dbconnector.h" #include "config_interface.h" -struct event *listen_event; -struct event *server_listen_event; struct event_base *base; struct event *ev_sigint; struct event *ev_sigterm; @@ -72,6 +70,22 @@ std::map counterMap = { /* interface to vlan mapping */ std::unordered_map vlan_map; +/* ipv6 address to vlan name mapping */ +std::unordered_map addr_vlan_map; + +/** + * @code bool inline isIPv6Zero(const in6_addr &addr) + * + * @brief check if ipv6 address is zero + * + * @param addr ipv6 address + * + * @return bool + */ +bool inline isIPv6Zero(const in6_addr &addr) { + return (memcmp(&addr, &in6addr_any, sizeof(in6addr_any)) == 0); +} + /* Options Class Definition */ // add single option @@ -382,25 +396,6 @@ const struct dhcpv6_relay_msg *parse_dhcpv6_relay(const uint8_t *buffer) { return (const struct dhcpv6_relay_msg *)buffer; } -/** - * @code const struct dhcpv6_option *parse_dhcpv6_opt(const uint8_t *buffer, const uint8_t **out_end); - * - * @brief parse through dhcpv6 option - * - * @param *buffer message buffer - * @param **out_end pointer - * - * @return dhcpv6_option end of dhcpv6 message option - */ -const struct dhcpv6_option *parse_dhcpv6_opt(const uint8_t *buffer, const uint8_t **out_end) { - auto option = (const struct dhcpv6_option *)buffer; - uint8_t size = 4; // option-code + option-len - size += *(uint16_t *)(buffer); - (*out_end) = buffer + size + ntohs(option->option_length); - - return option; -} - /** * @code sock_open(const struct sock_fprog *fprog); * @@ -419,6 +414,9 @@ int sock_open(const struct sock_fprog *fprog) return -1; } + evutil_make_listen_socket_reuseable(s); + evutil_make_socket_nonblocking(s); + struct sockaddr_ll sll = { .sll_family = AF_PACKET, .sll_protocol = htons(ETH_P_ALL), @@ -456,22 +454,22 @@ int sock_open(const struct sock_fprog *fprog) } /** - * @code prepare_relay_config(relay_config &interface_config, int local_sock, int filter); + * @code prepare_relay_config(relay_config &interface_config, int gua_sock, int filter); * * @brief prepare for specified relay interface config: server and link address * * @param interface_config pointer to relay config to be prepared - * @param local_sock L3 socket used for relaying messages + * @param gua_sock L3 socket used for relaying messages * @param filter socket attached with filter * * @return none */ -void prepare_relay_config(relay_config &interface_config, int local_sock, int filter) { +void prepare_relay_config(relay_config &interface_config, int gua_sock, int filter) { struct ifaddrs *ifa, *ifa_tmp; sockaddr_in6 non_link_local; sockaddr_in6 link_local; - interface_config.local_sock = local_sock; + interface_config.gua_sock = gua_sock; interface_config.filter = filter; for(auto server: interface_config.servers) { @@ -514,34 +512,97 @@ void prepare_relay_config(relay_config &interface_config, int local_sock, int fi else { interface_config.link_address = link_local; } + char ipv6_str[INET6_ADDRSTRLEN] = {}; + inet_ntop(AF_INET6, &interface_config.link_address.sin6_addr, ipv6_str, INET6_ADDRSTRLEN); + addr_vlan_map[std::string(ipv6_str)] = interface_config.interface; } /** - * @code prepare_socket(int &local_sock, int &server_sock, relay_config &config); + * @code prepare_lo_socket(const char *lo); * - * @brief prepare L3 socket for sending + * @brief prepare loopback interface socket for dual tor senario * - * @param local_sock pointer to socket binded to global address for relaying client message to server and listening for server message - * @param server_sock pointer to socket binded to link_local address for relaying server message to client + * @param lo loopback interface name * - * @return none + * @return int */ -void prepare_socket(int &local_sock, int &server_sock, relay_config &config) { +int prepare_lo_socket(const char *lo) { struct ifaddrs *ifa, *ifa_tmp; - sockaddr_in6 addr = {0}; - sockaddr_in6 ll_addr = {0}; + sockaddr_in6 gua = {0}; + int lo_sock = -1; + + if ((lo_sock = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) { + syslog(LOG_ERR, "socket: Failed to create gua socket on interface %s\n", lo); + return -1; + } - if ((local_sock = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) { - syslog(LOG_ERR, "socket: Failed to create socket on interface %s\n", config.interface.c_str()); + evutil_make_listen_socket_reuseable(lo_sock); + evutil_make_socket_nonblocking(lo_sock); + + if (getifaddrs(&ifa) == -1) { + syslog(LOG_WARNING, "getifaddrs: Unable to get network interfaces with %s\n", strerror(errno)); } + bool bind_gua = false; + ifa_tmp = ifa; + while (ifa_tmp) { + if (ifa_tmp->ifa_addr && (ifa_tmp->ifa_addr->sa_family == AF_INET6)) { + if (strcmp(ifa_tmp->ifa_name, lo) == 0) { + struct sockaddr_in6 *in6 = (struct sockaddr_in6*) ifa_tmp->ifa_addr; + if (!IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) { + bind_gua = true; + gua = *in6; + gua.sin6_family = AF_INET6; + gua.sin6_port = htons(RELAY_PORT); + break; + } + } + } + ifa_tmp = ifa_tmp->ifa_next; + } + freeifaddrs(ifa); - if ((server_sock = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) { - syslog(LOG_ERR, "socket: Failed to create socket on interface %s\n", config.interface.c_str()); + if (!bind_gua || bind(lo_sock, (sockaddr *)&gua, sizeof(gua)) == -1) { + syslog(LOG_ERR, "bind: Failed to bind socket on interface %s with %s\n", lo, strerror(errno)); + (void) close(lo_sock); + return -1; } + return lo_sock; +} + +/** + * @code prepare_vlan_sockets(int &gua_sock, int &lla_sock, relay_config &config); + * + * @brief prepare vlan L3 socket for sending + * + * @param gua_sock socket binded to global address for relaying client message to server and listening for server message + * @param lla_sock socket binded to link_local address for relaying server message to client + * + * @return int + */ +int prepare_vlan_sockets(int &gua_sock, int &lla_sock, relay_config &config) { + struct ifaddrs *ifa, *ifa_tmp; + sockaddr_in6 gua = {0}, lla = {0}; + + if ((gua_sock = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) { + syslog(LOG_ERR, "socket: Failed to create gua socket on interface %s\n", config.interface.c_str()); + return -1; + } + + if ((lla_sock = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) { + syslog(LOG_ERR, "socket: Failed to create lla socket on interface %s\n", config.interface.c_str()); + close(gua_sock); + return -1; + } + + evutil_make_listen_socket_reuseable(gua_sock); + evutil_make_socket_nonblocking(gua_sock); + evutil_make_listen_socket_reuseable(lla_sock); + evutil_make_socket_nonblocking(lla_sock); + int retry = 0; - bool bind_addr = false; - bool bind_ll_addr = false; + bool bind_gua = false; + bool bind_lla = false; do { if (getifaddrs(&ifa) == -1) { syslog(LOG_WARNING, "getifaddrs: Unable to get network interfaces with %s\n", strerror(errno)); @@ -549,19 +610,20 @@ void prepare_socket(int &local_sock, int &server_sock, relay_config &config) { else { ifa_tmp = ifa; while (ifa_tmp) { - if (ifa_tmp->ifa_addr && (ifa_tmp->ifa_addr->sa_family == AF_INET6) && (strcmp(ifa_tmp->ifa_name, config.interface.c_str()) == 0)) { - struct sockaddr_in6 *in6 = (struct sockaddr_in6*) ifa_tmp->ifa_addr; - if(!IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) { - bind_addr = true; - in6->sin6_family = AF_INET6; - in6->sin6_port = htons(RELAY_PORT); - addr = *in6; - } - if(IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) { - bind_ll_addr = true; - in6->sin6_family = AF_INET6; - in6->sin6_port = htons(RELAY_PORT); - ll_addr = *in6; + if (ifa_tmp->ifa_addr && (ifa_tmp->ifa_addr->sa_family == AF_INET6)) { + if (strcmp(ifa_tmp->ifa_name, config.interface.c_str()) == 0) { + struct sockaddr_in6 *in6 = (struct sockaddr_in6*) ifa_tmp->ifa_addr; + if (!IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) { + bind_gua = true; + gua = *in6; + gua.sin6_family = AF_INET6; + gua.sin6_port = htons(RELAY_PORT); + } else { + bind_lla = true; + lla = *in6; + lla.sin6_family = AF_INET6; + lla.sin6_port = htons(RELAY_PORT); + } } } ifa_tmp = ifa_tmp->ifa_next; @@ -569,7 +631,7 @@ void prepare_socket(int &local_sock, int &server_sock, relay_config &config) { freeifaddrs(ifa); } - if (bind_addr && bind_ll_addr) { + if (bind_gua && bind_lla) { break; } @@ -577,13 +639,22 @@ void prepare_socket(int &local_sock, int &server_sock, relay_config &config) { sleep(5); } while (retry < 6); - if ((!bind_addr) || (bind(local_sock, (sockaddr *)&addr, sizeof(addr)) == -1)) { - syslog(LOG_ERR, "bind: Failed to bind socket to global ipv6 address on interface %s after %d retries with %s\n", config.interface.c_str(), retry, strerror(errno)); + if ((!bind_gua) || (bind(gua_sock, (sockaddr *)&gua, sizeof(gua)) == -1)) { + syslog(LOG_ERR, "bind: Failed to bind socket to global ipv6 address on interface %s after %d retries with %s\n", + config.interface.c_str(), retry, strerror(errno)); + close(gua_sock); + close(lla_sock); + return -1; } - if ((!bind_ll_addr) || (bind(server_sock, (sockaddr *)&ll_addr, sizeof(addr)) == -1)) { - syslog(LOG_ERR, "bind: Failed to bind socket to link local ipv6 address on interface %s after %d retries with %s\n", config.interface.c_str(), retry, strerror(errno)); + if ((!bind_lla) || (bind(lla_sock, (sockaddr *)&lla, sizeof(lla)) == -1)) { + syslog(LOG_ERR, "bind: Failed to bind socket to link local ipv6 address on interface %s after %d retries with %s\n", + config.interface.c_str(), retry, strerror(errno)); + close(gua_sock); + close(lla_sock); + return -1; } + return 0; } @@ -601,7 +672,7 @@ void prepare_socket(int &local_sock, int &server_sock, relay_config &config) { * * @return none */ -void relay_client(int sock, const uint8_t *msg, uint16_t len, const ip6_hdr *ip_hdr, const ether_header *ether_hdr, relay_config *config) { +void relay_client(const uint8_t *msg, uint16_t len, const ip6_hdr *ip_hdr, const ether_header *ether_hdr, relay_config *config) { /* unmarshal dhcpv6 message to detect malformed message */ DHCPv6Msg dhcpv6; auto result = dhcpv6.UnmarshalBinary(msg, len); @@ -646,6 +717,10 @@ void relay_client(int sock, const uint8_t *msg, uint16_t len, const ip6_hdr *ip_ return; } + int sock = config->gua_sock; + if (dual_tor_sock) { + sock = config->lo_sock; + } for(auto server: config->servers_sock) { if(send_udp(sock, relay_pkt, server, relay_pkt_len)) { increase_counter(config->state_db, config->interface, DHCPv6_MESSAGE_TYPE_RELAY_FORW); @@ -658,7 +733,6 @@ void relay_client(int sock, const uint8_t *msg, uint16_t len, const ip6_hdr *ip_ * * @brief construct a relay-forward message encapsulated relay-forward message * - * @param sock L3 socket for sending data to servers * @param msg pointer to dhcpv6 message header position * @param len size of data received * @param ip_hdr pointer to IPv6 header @@ -666,7 +740,7 @@ void relay_client(int sock, const uint8_t *msg, uint16_t len, const ip6_hdr *ip_ * * @return none */ -void relay_relay_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, relay_config *config) { +void relay_relay_forw(const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, relay_config *config) { auto dhcp_relay_header = parse_dhcpv6_relay(msg); if (dhcp_relay_header->hop_count >= HOP_LIMIT) { @@ -686,6 +760,15 @@ void relay_relay_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr * /* add relay-msg option */ relay.m_option_list.Add(OPTION_RELAY_MSG, msg, len); + // insert option82 for new relay-forward packet, we need this information + // to get original relay-forward source interface for accurate counting in dualtor scenario + // is_interface_id is by-default enabled in dualtor scenario + if(config->is_interface_id) { + option_interface_id intf_id; + intf_id.interface_id = config->link_address.sin6_addr; + relay.m_option_list.Add(OPTION_INTERFACE_ID, (const uint8_t *)&intf_id, sizeof(option_interface_id)); + } + uint16_t send_buffer_len = 0; auto send_buffer = relay.MarshalBinary(send_buffer_len); if (!send_buffer_len || !send_buffer) { @@ -695,6 +778,10 @@ void relay_relay_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr * return; } + int sock = config->gua_sock; + if (dual_tor_sock) { + sock = config->lo_sock; + } for(auto server: config->servers_sock) { if(send_udp(sock, send_buffer, server, send_buffer_len)) { increase_counter(config->state_db, config->interface, DHCPv6_MESSAGE_TYPE_RELAY_FORW); @@ -714,7 +801,7 @@ void relay_relay_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr * * * @return none */ - void relay_relay_reply(int sock, const uint8_t *msg, int32_t len, relay_config *config) { + void relay_relay_reply(const uint8_t *msg, int32_t len, relay_config *config) { class RelayMsg relay; auto result = relay.UnmarshalBinary(msg, len); if (!result) { @@ -740,6 +827,15 @@ void relay_relay_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr * target_addr.sin6_port = htons(CLIENT_PORT); target_addr.sin6_scope_id = if_nametoindex(config->interface.c_str()); + int sock = config->lla_sock; + if (isIPv6Zero(relay.m_msg_hdr.link_address)) { + // relay.m_msg_hdr is packed member, use a temp variable for unaligned case + struct in6_addr peer_addr = relay.m_msg_hdr.peer_address; + if (!IN6_IS_ADDR_LINKLOCAL(&peer_addr)) + sock = config->gua_sock; + target_addr.sin6_port = htons(RELAY_PORT); + } + if(send_udp(sock, dhcpv6, target_addr, length)) { increase_counter(config->state_db, config->interface, msg_type); } @@ -876,7 +972,7 @@ void client_packet_handler(uint8_t *buffer, ssize_t length, struct relay_config switch (msg->msg_type) { case DHCPv6_MESSAGE_TYPE_RELAY_FORW: { - relay_relay_forw(config->local_sock, current_position, ntohs(udp_header->len) - sizeof(udphdr), ip6_header, config); + relay_relay_forw(current_position, ntohs(udp_header->len) - sizeof(udphdr), ip6_header, config); break; } case DHCPv6_MESSAGE_TYPE_SOLICIT: @@ -888,7 +984,7 @@ void client_packet_handler(uint8_t *buffer, ssize_t length, struct relay_config case DHCPv6_MESSAGE_TYPE_DECLINE: case DHCPv6_MESSAGE_TYPE_INFORMATION_REQUEST: { - relay_client(config->local_sock, current_position, ntohs(udp_header->len) - sizeof(udphdr), ip6_header, ether_header, config); + relay_client(current_position, ntohs(udp_header->len) - sizeof(udphdr), ip6_header, ether_header, config); break; } default: @@ -899,6 +995,103 @@ void client_packet_handler(uint8_t *buffer, ssize_t length, struct relay_config } } +/** + * @code struct relay_config * + * get_relay_int_from_relay_msg(const uint8_t *msg, int32_t len, + * std::unordered_map *vlans) + * + * @brief get relay interface info from relay message + * + * @param addr ipv6 address + * + * @return bool + */ +struct relay_config * +get_relay_int_from_relay_msg(const uint8_t *msg, int32_t len, std::unordered_map *vlans) { + class RelayMsg relay; + auto result = relay.UnmarshalBinary(msg, len); + if (!result) { + syslog(LOG_WARNING, "Relay-reply from loopback socket, option is invalid or contains malformed payload\n"); + return NULL; + } + + auto opt_value = relay.m_option_list.Get(OPTION_INTERFACE_ID); + in6_addr address = in6addr_any; + if (opt_value.empty()) { + std::memcpy(&address, &relay.m_msg_hdr.link_address, sizeof(in6_addr)); + } else { + auto interface_id = opt_value.data(); + std::memcpy(&address, interface_id, sizeof(in6_addr)); + } + + // multi-level relay agents + if (isIPv6Zero(address)) { + return NULL; + } + + char ipv6_str[INET6_ADDRSTRLEN] = {}; + inet_ntop(AF_INET6, &address, ipv6_str, INET6_ADDRSTRLEN); + auto v6_string = std::string(ipv6_str); + if (addr_vlan_map.find(v6_string) == addr_vlan_map.end()) { + syslog(LOG_WARNING, "DHCPv6 type %d can't find vlan info from link address %s\n", + relay.m_msg_hdr.msg_type, ipv6_str); + return NULL; + } + + auto vlan_name = addr_vlan_map[v6_string]; + if (vlans->find(vlan_name) == vlans->end()) { + syslog(LOG_WARNING, "DHCPv6 can't find vlan %s config\n", vlan_name.c_str()); + return NULL; + } + return &vlans->find(vlan_name)->second; +} + +/** + * @code void server_callback_dualtor(evutil_socket_t fd, short event, void *arg); + * + * @brief callback for libevent that is called everytime data is received at the loopback socket + * + * @param fd loopback socket + * @param event libevent triggered event + * @param arg callback argument provided by user + * + * @return none + */ +void server_callback_dualtor(evutil_socket_t fd, short event, void *arg) { + auto vlans = reinterpret_cast *>(arg); + sockaddr_in6 from; + socklen_t len = sizeof(from); + int32_t pkts_num = 0; + + while (pkts_num++ < BATCH_SIZE) { + auto buffer_sz = recvfrom(fd, server_recv_buffer, BUFFER_SIZE, 0, (sockaddr *)&from, &len); + if (buffer_sz <= 0) { + if (errno != EAGAIN) { + syslog(LOG_ERR, "recv: Failed to receive data from server: %s\n", strerror(errno)); + } + return; + } + + if (buffer_sz < (int32_t)sizeof(struct dhcpv6_msg)) { + syslog(LOG_WARNING, "Invalid DHCPv6 packet length %zd, no space for dhcpv6 msg header\n", buffer_sz); + continue; + } + + auto msg_type = parse_dhcpv6_hdr(server_recv_buffer)->msg_type; + if (msg_type != DHCPv6_MESSAGE_TYPE_RELAY_REPL) { + syslog(LOG_WARNING, "Invalid DHCPv6 message type %d received on loopback interface\n", msg_type); + continue; + } + auto config = get_relay_int_from_relay_msg(server_recv_buffer, buffer_sz, vlans); + if (!config) { + syslog(LOG_WARNING, "Invalid DHCPv6 header content on loopback socket, packet will be dropped\n"); + continue; + } + auto loopback_str = std::string(loopback); + increase_counter(config->state_db, loopback_str, msg_type); + relay_relay_reply(server_recv_buffer, buffer_sz, config); + } +} /** * @code void server_callback(evutil_socket_t fd, short event, void *arg); @@ -918,7 +1111,7 @@ void server_callback(evutil_socket_t fd, short event, void *arg) { int32_t pkts_num = 0; while (pkts_num++ < BATCH_SIZE) { - auto buffer_sz = recvfrom(config->local_sock, server_recv_buffer, BUFFER_SIZE, 0, (sockaddr *)&from, &len); + auto buffer_sz = recvfrom(config->gua_sock, server_recv_buffer, BUFFER_SIZE, 0, (sockaddr *)&from, &len); if (buffer_sz <= 0) { if (errno != EAGAIN) { syslog(LOG_ERR, "recv: Failed to receive data from server: %s\n", strerror(errno)); @@ -941,7 +1134,7 @@ void server_callback(evutil_socket_t fd, short event, void *arg) { increase_counter(config->state_db, config->interface, msg_type); if (msg_type == DHCPv6_MESSAGE_TYPE_RELAY_REPL) { - relay_relay_reply(config->server_sock, server_recv_buffer, buffer_sz, config); + relay_relay_reply(server_recv_buffer, buffer_sz, config); } } } @@ -1040,7 +1233,7 @@ void loop_relay(std::unordered_map &vlans) { std::vector sockets; base = event_base_new(); if(base == NULL) { - syslog(LOG_ERR, "libevent: Failed to create base\n"); + syslog(LOG_ERR, "libevent: Failed to create event base\n"); exit(EXIT_FAILURE); } @@ -1053,21 +1246,41 @@ void loop_relay(std::unordered_map &vlans) { auto filter = sock_open(ðer_relay_fprog); if (filter != -1) { sockets.push_back(filter); - listen_event = event_new(base, filter, EV_READ|EV_PERSIST, client_callback, - reinterpret_cast(&vlans)); - if (listen_event == NULL) { - syslog(LOG_ERR, "libevent: Failed to create client listen libevent\n"); + auto event = event_new(base, filter, EV_READ|EV_PERSIST, client_callback, + reinterpret_cast(&vlans)); + if (event == NULL) { + syslog(LOG_ERR, "libevent: Failed to create client listen event\n"); + exit(EXIT_FAILURE); } - event_add(listen_event, NULL); - syslog(LOG_INFO, "libevent: Add filter socket event\n"); + event_add(event, NULL); + syslog(LOG_INFO, "libevent: Add client listen socket event\n"); } else { - syslog(LOG_ALERT, "Failed to create relay filter socket"); + syslog(LOG_ERR, "Failed to create client listen socket"); exit(EXIT_FAILURE); } + int lo_sock = -1; + if (dual_tor_sock) { + lo_sock = prepare_lo_socket(loopback); + if (lo_sock != -1) { + sockets.push_back(lo_sock); + auto event = event_new(base, lo_sock, EV_READ|EV_PERSIST, server_callback_dualtor, + reinterpret_cast(&vlans)); + if (event == NULL) { + syslog(LOG_ERR, "libevent: Failed to create dualtor loopback listen event\n"); + exit(EXIT_FAILURE); + } + event_add(event, NULL); + syslog(LOG_INFO, "libevent: Add dualtor loopback socket event\n"); + } else{ + syslog(LOG_ERR, "Failed to create dualtor loopback listen socket"); + exit(EXIT_FAILURE); + } + } + for(auto &vlan : vlans) { - int local_sock = 0; - int server_sock = 0; + int gua_sock = 0; + int lla_sock = 0; vlan.second.config_db = config_db; vlan.second.mux_table = mStateDbMuxTablePtr; vlan.second.state_db = state_db; @@ -1076,28 +1289,28 @@ void loop_relay(std::unordered_map &vlans) { update_vlan_mapping(vlan.first, config_db); initialize_counter(vlan.second.state_db, vlan.second.interface); - prepare_socket(local_sock, server_sock, vlan.second); - - vlan.second.local_sock = local_sock; - vlan.second.server_sock = server_sock; - - sockets.push_back(local_sock); - sockets.push_back(server_sock); - prepare_relay_config(vlan.second, local_sock, filter); - - evutil_make_listen_socket_reuseable(filter); - evutil_make_socket_nonblocking(filter); - - evutil_make_listen_socket_reuseable(local_sock); - evutil_make_socket_nonblocking(local_sock); - - server_listen_event = event_new(base, local_sock, EV_READ|EV_PERSIST, server_callback, &(vlan.second)); - - if (server_listen_event == NULL) { - syslog(LOG_ERR, "libevent: Failed to create server listen libevent\n"); + + if (prepare_vlan_sockets(gua_sock, lla_sock, vlan.second) != -1) { + vlan.second.gua_sock = gua_sock; + vlan.second.lla_sock = lla_sock; + vlan.second.lo_sock = lo_sock; + + sockets.push_back(gua_sock); + sockets.push_back(lla_sock); + prepare_relay_config(vlan.second, gua_sock, filter); + if (!dual_tor_sock) { + auto event = event_new(base, gua_sock, EV_READ|EV_PERSIST, + server_callback, &(vlan.second)); + if (event == NULL) { + syslog(LOG_ERR, "libevent: Failed to create server listen libevent\n"); + } + event_add(event, NULL); + syslog(LOG_INFO, "libevent: add server listen socket for %s\n", vlan.first.c_str()); + } + } else { + syslog(LOG_ERR, "Failed to create dualtor loopback listen socket"); + exit(EXIT_FAILURE); } - event_add(server_listen_event, NULL); - syslog(LOG_INFO, "libevent: Add server listen socket for %s\n", vlan.first.c_str()); } if(signal_init() == 0 && signal_start() == 0) { diff --git a/src/relay.h b/src/relay.h index cb1d2ba..5fb009d 100644 --- a/src/relay.h +++ b/src/relay.h @@ -37,6 +37,7 @@ #define BATCH_SIZE 64 extern bool dual_tor_sock; +extern char loopback[IF_NAMESIZE]; /* DHCPv6 message types */ typedef enum @@ -61,8 +62,9 @@ typedef enum } dhcp_message_type_t; struct relay_config { - int local_sock; - int server_sock; + int gua_sock; + int lla_sock; + int lo_sock; int filter; sockaddr_in6 link_address; std::shared_ptr state_db; @@ -167,36 +169,46 @@ class DHCPv6Msg: public Options { int sock_open(const struct sock_fprog *fprog); /** - * @code prepare_socket(int &local_sock, int &server_sock, relay_config &config); + * @code prepare_lo_socket(const char *lo); * - * @brief prepare L3 socket for sending + * @brief prepare loopback interface socket for dual tor senario * - * @param local_sock pointer to socket binded to global address for relaying client message to server and listening for server message - * @param server_sock pointer to socket binded to link_local address for relaying server message to client + * @param lo loopback interface name * - * @return none + * @return int + */ +int prepare_lo_socket(const char *lo); + +/** + * @code prepare_vlan_sockets(int &gua_sock, int &lla_sock, relay_config &config); + * + * @brief prepare vlan L3 socket for sending + * + * @param gua_sock socket binded to global address for relaying client message to server and listening for server message + * @param lla_sock socket binded to link_local address for relaying server message to client + * + * @return int */ -void prepare_socket(int &local_sock, int &server_sock, relay_config &config); +int prepare_vlan_sockets(int &gua_sock, int &lla_sock, relay_config &config); /** - * @code prepare_relay_config(relay_config &interface_config, int local_sock, int filter); + * @code prepare_relay_config(relay_config &interface_config, int gua_sock, int filter); * * @brief prepare for specified relay interface config: server and link address * * @param interface_config pointer to relay config to be prepared - * @param local_sock L3 socket used for relaying messages + * @param gua_sock L3 socket used for relaying messages * @param filter socket attached with filter * * @return none */ -void prepare_relay_config(relay_config &interface_config, int local_sock, int filter); +void prepare_relay_config(relay_config &interface_config, int gua_sock, int filter); /** - * @code relay_client(int sock, const uint8_t *msg, uint16_t len, ip6_hdr *ip_hdr, const ether_header *ether_hdr, relay_config *config); + * @code relay_client(const uint8_t *msg, uint16_t len, ip6_hdr *ip_hdr, const ether_header *ether_hdr, relay_config *config); * * @brief construct relay-forward message * - * @param sock L3 socket for sending data to servers * @param msg pointer to dhcpv6 message header position * @param len size of data received * @param ip_hdr pointer to IPv6 header @@ -205,14 +217,13 @@ void prepare_relay_config(relay_config &interface_config, int local_sock, int fi * * @return none */ -void relay_client(int sock, const uint8_t *msg, uint16_t len, const ip6_hdr *ip_hdr, const ether_header *ether_hdr, relay_config *config); +void relay_client(const uint8_t *msg, uint16_t len, const ip6_hdr *ip_hdr, const ether_header *ether_hdr, relay_config *config); /** - * @code relay_relay_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, relay_config *config) + * @code relay_relay_forw(const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, relay_config *config) * * @brief construct a relay-forward message encapsulated relay-forward message * - * @param sock L3 socket for sending data to servers * @param msg pointer to dhcpv6 message header position * @param len size of data received * @param ip_hdr pointer to IPv6 header @@ -220,21 +231,47 @@ void relay_client(int sock, const uint8_t *msg, uint16_t len, const ip6_hdr *ip_ * * @return none */ -void relay_relay_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, relay_config *config); +void relay_relay_forw(const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, relay_config *config); /** - * @code relay_relay_reply(int sock, const uint8_t *msg, int32_t len, relay_config *configs); + * @code relay_relay_reply(const uint8_t *msg, int32_t len, relay_config *configs); * * @brief relay and unwrap a relay-reply message * - * @param sock L3 socket for sending data to servers * @param msg pointer to dhcpv6 message header position * @param len size of data received * @param config relay interface config * * @return none */ -void relay_relay_reply(int sock, const uint8_t *msg, int32_t len, relay_config *configs); +void relay_relay_reply(const uint8_t *msg, int32_t len, relay_config *configs); + +/** + * @code struct relay_config * + * get_relay_int_from_relay_msg(const uint8_t *msg, int32_t len, + * std::unordered_map *vlans) + * + * @brief get relay interface info from relay message + * + * @param addr ipv6 address + * + * @return bool + */ +struct relay_config * +get_relay_int_from_relay_msg(const uint8_t *msg, int32_t len, std::unordered_map *vlans); + +/** + * @code void server_callback_dualtor(evutil_socket_t fd, short event, void *arg); + * + * @brief callback for libevent that is called everytime data is received at the loopback socket + * + * @param fd loopback socket + * @param event libevent triggered event + * @param arg callback argument provided by user + * + * @return none + */ +void server_callback_dualtor(evutil_socket_t fd, short event, void *arg); /** * @code loop_relay(std::unordered_map &vlans); @@ -385,18 +422,6 @@ const struct dhcpv6_msg *parse_dhcpv6_hdr(const uint8_t *buffer); */ const struct dhcpv6_relay_msg *parse_dhcpv6_relay(const uint8_t *buffer); -/** - * @code const struct dhcpv6_option *parse_dhcpv6_opt(const uint8_t *buffer, const uint8_t **out_end); - * - * @brief parse through dhcpv6 option - * - * @param *buffer message buffer - * @param **out_end pointer - * - * @return dhcpv6_option end of dhcpv6 message option - */ -const struct dhcpv6_option *parse_dhcpv6_opt(const uint8_t *buffer, const uint8_t **out_end); - /** * @code update_vlan_mapping(std::string vlan, std::shared_ptr cfgdb); * diff --git a/test/mock_relay.cpp b/test/mock_relay.cpp index 3faa76a..c4be1ee 100644 --- a/test/mock_relay.cpp +++ b/test/mock_relay.cpp @@ -14,6 +14,8 @@ using namespace ::testing; bool dual_tor_sock = false; +char loopback[IF_NAMESIZE] = "Loopback0"; +int mock_sock = 124; static struct sock_filter ether_relay_filter[] = { @@ -188,21 +190,6 @@ TEST(parsePacket, parse_dhcpv6_relay) EXPECT_GE("fe80::58df:a801:acb7:886", peer_addr.append(peer)); } -TEST(parsePacket, parse_dhcpv6_opt) -{ - unsigned char relay[] = { /* Relay-Forward Message DHCPv6 Option */ - 0x00, 0x09, 0x00, 0x63, 0x01, 0x34, 0x56, 0x78, 0x00, 0x01, 0x00, 0x0a - }; - - char *ptr = (char *)relay; - const uint8_t *current_position = (uint8_t *)ptr; - const uint8_t *tmp = NULL; - - auto dhcp_relay_header = parse_dhcpv6_opt(current_position, &tmp); - EXPECT_EQ(OPTION_RELAY_MSG, ntohs(dhcp_relay_header->option_code)); - EXPECT_EQ(99, ntohs(dhcp_relay_header->option_length)); -} - TEST(sock, sock_open) { struct sock_filter ether_relay_filter[] = { @@ -239,7 +226,7 @@ TEST(helper, send_udp) TEST(prepareConfig, prepare_relay_config) { - int local_sock = 1; + int gua_sock = 1; int filter = 1; struct relay_config config{}; config.is_option_79 = true; @@ -256,7 +243,7 @@ TEST(prepareConfig, prepare_relay_config) std::shared_ptr state_db = std::make_shared ("STATE_DB", 0); config.state_db = state_db; - prepare_relay_config(config, local_sock, filter); + prepare_relay_config(config, gua_sock, filter); char addr1[INET6_ADDRSTRLEN]; char addr2[INET6_ADDRSTRLEN]; @@ -269,7 +256,42 @@ TEST(prepareConfig, prepare_relay_config) EXPECT_EQ("fc02:2000::2", s2); } -TEST(prepareConfig, prepare_socket) +TEST(prepareConfig, prepare_lo_socket) +{ + // test case use "lo" as an example + std::string ifname1 = "lo"; + std::string ifname2 = "lo222"; + + auto sock = prepare_lo_socket(ifname1.c_str()); + + struct ifaddrs *ifa, *ifa_tmp; + if (getifaddrs(&ifa) == -1) { + EXPECT_EQ(sock, -1); + } + bool find_gua = false; + ifa_tmp = ifa; + while (ifa_tmp) { + if (ifa_tmp->ifa_addr && (ifa_tmp->ifa_addr->sa_family == AF_INET6)) { + if (strcmp(ifa_tmp->ifa_name, ifname1.c_str()) == 0) { + struct sockaddr_in6 *in6 = (struct sockaddr_in6*) ifa_tmp->ifa_addr; + if (!IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) { + find_gua = true; + } + } + } + ifa_tmp = ifa_tmp->ifa_next; + } + freeifaddrs(ifa); + if (find_gua) + EXPECT_GE(sock, 0); + else + EXPECT_EQ(sock, -1); + + sock = prepare_lo_socket(ifname2.c_str()); + EXPECT_EQ(sock, -1); +} + +TEST(prepareConfig, prepare_vlan_sockets) { struct relay_config config{}; config.is_option_79 = true; @@ -286,11 +308,11 @@ TEST(prepareConfig, prepare_socket) std::shared_ptr state_db = std::make_shared ("STATE_DB", 0); config.state_db = state_db; - int local_sock = -1, server_sock = -1; - prepare_socket(local_sock, server_sock, config); + int gua_sock = -1, lla_sock = -1; + prepare_vlan_sockets(gua_sock, lla_sock, config); - EXPECT_GE(local_sock, 0); - EXPECT_GE(server_sock, 0); + EXPECT_GE(gua_sock, 0); + EXPECT_GE(lla_sock, 0); } TEST(counter, initialize_counter) @@ -325,8 +347,6 @@ TEST(counter, increase_counter) TEST(relay, relay_client) { - int mock_sock = 124; - uint8_t msg[] = { 0x01, 0x2f, 0xf4, 0xc8, 0x00, 0x01, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x25, 0x3a, 0x37, 0xb9, @@ -341,6 +361,9 @@ TEST(relay, relay_client) struct relay_config config{}; config.is_option_79 = true; config.is_interface_id = true; + config.gua_sock = mock_sock; + config.lla_sock = mock_sock; + config.lo_sock = mock_sock; std::vector servers; servers.push_back("fc02:2000::1"); servers.push_back("fc02:2000::2"); @@ -368,15 +391,16 @@ TEST(relay, relay_client) std::string s_addr = "2000::3"; // invalid msg_len testing - ASSERT_NO_THROW(relay_client(mock_sock, msg, 2, &ip_hdr, ðer_hdr, &config)); + ASSERT_NO_THROW(relay_client(msg, 2, &ip_hdr, ðer_hdr, &config)); // packet with a super length > sizeof(msg) - EXPECT_DEATH(relay_client(mock_sock, msg, 65535, &ip_hdr, ðer_hdr, &config), ""); + EXPECT_DEATH(relay_client(msg, 65535, &ip_hdr, ðer_hdr, &config), ""); - // normal packet testing - ASSERT_NO_THROW(relay_client(mock_sock, msg, msg_len, &ip_hdr, ðer_hdr, &config)); + // normal packet testing + dual_tor_sock = true; + ASSERT_NO_THROW(relay_client(msg, msg_len, &ip_hdr, ðer_hdr, &config)); - EXPECT_EQ(last_used_sock, 124); + EXPECT_EQ(last_used_sock, mock_sock); auto sent_msg = parse_dhcpv6_relay(sender_buffer); @@ -387,32 +411,9 @@ TEST(relay, relay_client) EXPECT_EQ(sent_msg->link_address.__in6_u.__u6_addr8[i], config.link_address.sin6_addr.__in6_u.__u6_addr8[i]); EXPECT_EQ(sent_msg->peer_address.__in6_u.__u6_addr8[i], ip_hdr.ip6_src.__in6_u.__u6_addr8[i]); } - - const uint8_t *current_position = sender_buffer + sizeof(dhcpv6_relay_msg); - - bool link_layer = false; - bool interface_id = false; - while ((current_position - sender_buffer) < valid_byte_count) { - - auto option = parse_dhcpv6_opt(current_position, ¤t_position); - switch (ntohs(option->option_code)) { - case OPTION_RELAY_MSG: - EXPECT_EQ(memcmp(((uint8_t *)option) + sizeof(dhcpv6_option), msg, msg_len), 0); - case OPTION_CLIENT_LINKLAYER_ADDR: - link_layer = true; - case OPTION_INTERFACE_ID: - interface_id = true; - } - } - EXPECT_TRUE(link_layer); - EXPECT_TRUE(interface_id); - EXPECT_GE(sendUdpCount, 1); - sendUdpCount = 0; } TEST(relay, relay_relay_forw) { - int mock_sock = 125; - uint8_t msg[] = { 0x0c, 0x00, 0x20, 0x01, 0x0d, 0xb8, 0x01, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -445,6 +446,10 @@ TEST(relay, relay_relay_forw) { } std::shared_ptr state_db = std::make_shared ("STATE_DB", 0); config.state_db = state_db; + config.is_interface_id = true; + config.gua_sock = mock_sock; + config.lla_sock = mock_sock; + config.lo_sock = mock_sock; ip6_hdr ip_hdr; std::string s_addr = "2000::3"; @@ -453,18 +458,19 @@ TEST(relay, relay_relay_forw) { // msg with hop count > HOP_LIMIT auto hop = msg[1]; msg[1] = 65; - ASSERT_NO_THROW(relay_relay_forw(mock_sock, msg, msg_len, &ip_hdr, &config)); + ASSERT_NO_THROW(relay_relay_forw(msg, msg_len, &ip_hdr, &config)); msg[1] = hop; // super frame over size limit for secondary relay uint8_t super_frame[BUFFER_SIZE] = {}; ::memcpy(super_frame, msg, msg_len); - ASSERT_NO_THROW(relay_relay_forw(mock_sock, super_frame, BUFFER_SIZE, &ip_hdr, &config)); + ASSERT_NO_THROW(relay_relay_forw(super_frame, BUFFER_SIZE, &ip_hdr, &config)); // normal packet - ASSERT_NO_THROW(relay_relay_forw(mock_sock, msg, msg_len, &ip_hdr, &config)); + dual_tor_sock = true; + ASSERT_NO_THROW(relay_relay_forw(msg, msg_len, &ip_hdr, &config)); - EXPECT_EQ(last_used_sock, 125); + EXPECT_EQ(last_used_sock, mock_sock); auto sent_msg = parse_dhcpv6_relay(sender_buffer); @@ -520,14 +526,17 @@ TEST(relay, relay_relay_reply) config.interface = "Vlan1000"; std::shared_ptr state_db = std::make_shared ("STATE_DB", 0); config.state_db = state_db; + config.gua_sock = mock_sock; + config.lla_sock = mock_sock; + config.lo_sock = mock_sock; - int local_sock = 1; + int gua_sock = 1; int filter = 1; - prepare_relay_config(config, local_sock, filter); + prepare_relay_config(config, gua_sock, filter); // invalid message length - ASSERT_NO_THROW(relay_relay_reply(mock_sock, msg, 2, &config)); + ASSERT_NO_THROW(relay_relay_reply(msg, 2, &config)); // invalid relay msg, without OPTION_RELAY_MSG uint8_t invalid_msg[] = { @@ -549,13 +558,13 @@ TEST(relay, relay_relay_reply) 0x3a, 0x32, 0x33, 0x50, 0xe5, 0x49, 0x50, 0x9e, 0x40 }; - ASSERT_NO_THROW(relay_relay_reply(mock_sock, invalid_msg, msg_len, &config)); + ASSERT_NO_THROW(relay_relay_reply(invalid_msg, msg_len, &config)); // normal message - ASSERT_NO_THROW(relay_relay_reply(mock_sock, msg, msg_len, &config)); + ASSERT_NO_THROW(relay_relay_reply(msg, msg_len, &config)); - EXPECT_EQ(last_used_sock, 123); + EXPECT_EQ(last_used_sock, mock_sock); uint8_t expected_bytes[] = { 0x07, 0x4f, 0x6d, 0x04, 0x00, 0x03, 0x00, 0x28, @@ -720,7 +729,9 @@ TEST(relay, server_callback) { config.servers.push_back("fc02:2000::2"); config.interface = "Vlan1000"; config.state_db = state_db; - config.local_sock = -1; + config.gua_sock = -1; + config.lla_sock = -1; + config.lo_sock = -1; // simulator normal dhcpv6 packet length ssize_t msg_len = 129; @@ -753,7 +764,9 @@ TEST(relay, client_callback) { config.interface = "Vlan1000"; config.state_db = state_db; config.mux_table = mux_table; - config.local_sock = -1; + config.gua_sock = -1; + config.lla_sock = -1; + config.lo_sock = -1; std::unordered_map vlans; // mock normal dhcpv6 packet length @@ -1018,8 +1031,90 @@ TEST(relay, loop_relay) { vlans_in_loop["Vlan1000"] = config; EXPECT_EQ(vlans_in_loop.size(), 1); - EXPECT_GLOBAL_CALL(event_base_dispatch, event_base_dispatch(_)).Times(1).WillOnce(Return(-1)); - EXPECT_GLOBAL_CALL(event_add, event_add(_, NULL)).Times(AtLeast(2)).WillOnce(Return(0)) - .WillOnce(Return(0)); - ASSERT_NO_THROW(loop_relay(vlans_in_loop)); -} \ No newline at end of file + EXPECT_ANY_THROW(loop_relay(vlans_in_loop)); +} + +TEST(relay, get_relay_int_from_relay_msg) { + std::string lla_str = "fc02:1000::1"; + std::string vlan_str = "Vlan1000"; + uint8_t relay_reply_with_opt18[] = { + 0x0d,0x00,0xfc,0x02,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x01,0xfe,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x70,0xfd,0xff,0xfe,0xcb, + 0x0c,0x06,0x00,0x09,0x00,0x04,0x07,0x00,0x30,0x39,0x00,0x12,0x00,0x10,0xfc,0x02, + 0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01 + }; + uint8_t relay_reply_without_opt18[] = { + 0x0d,0x00,0xfc,0x02,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x01,0xfe,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x70,0xfd,0xff,0xfe,0xcb, + 0x0c,0x06,0x00,0x09,0x00,0x04,0x07,0x00,0x30,0x39 + }; + uint8_t relay_reply_without_opt18_linkaddr_zero[] = { + 0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xfe,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x70,0xfd,0xff,0xfe,0xcb, + 0x0c,0x06,0x00,0x09,0x00,0x04,0x07,0x00,0x30,0x39 + }; + std::unordered_map vlans; + struct relay_config config{ + .interface = vlan_str, + .is_option_79 = true, + .is_interface_id = true + }; + + // valid option18 + invalid name mapping + auto value = get_relay_int_from_relay_msg(relay_reply_with_opt18, sizeof(relay_reply_with_opt18), &vlans); + EXPECT_EQ((uintptr_t)value, NULL); + + // valid option18 + valid name mapping + invalid vlan config mapping + addr_vlan_map[lla_str] = vlan_str; + value = get_relay_int_from_relay_msg(relay_reply_with_opt18, sizeof(relay_reply_with_opt18), &vlans); + EXPECT_EQ((uintptr_t)value, NULL); + + // valid option18 + valid name mapping + valid vlan config mapping + vlans[vlan_str] = config; + value = get_relay_int_from_relay_msg(relay_reply_with_opt18, sizeof(relay_reply_with_opt18), &vlans); + EXPECT_NE((uintptr_t)value, NULL); + EXPECT_EQ(value->interface, vlan_str); + + // no option18 + non-zero link-address + valid name mapping + valid vlan config mapping + value = get_relay_int_from_relay_msg(relay_reply_without_opt18, sizeof(relay_reply_without_opt18), &vlans); + EXPECT_NE((uintptr_t)value, NULL); + EXPECT_EQ(value->interface, vlan_str); + + // no option18 + zero link-address + valid name mapping + valid vlan config mapping + value = get_relay_int_from_relay_msg(relay_reply_without_opt18_linkaddr_zero, sizeof(relay_reply_without_opt18_linkaddr_zero), &vlans); + EXPECT_EQ((uintptr_t)value, NULL); +} + +TEST(relay, server_callback_dualtor) { + std::unordered_map vlans_in_loop; + std::string ifname = "Vlan1000"; + struct relay_config config{}; + config.is_option_79 = true; + config.is_interface_id = true; + config.link_address.sin6_addr.__in6_u.__u6_addr8[15] = 0x01; + config.servers.push_back("fc02:2000::1"); + config.servers.push_back("fc02:2000::2"); + config.interface = "Vlan1000"; + config.gua_sock = -1; + config.lla_sock = -1; + config.lo_sock = -1; + // simulator normal dhcpv6 packet length + ssize_t msg_len = 129; + + // cover buffer_sz <= 0 + EXPECT_GLOBAL_CALL(recvfrom, recvfrom(_, _, _, _, _, _)).Times(5) + .WillOnce(Return(0)) + .WillOnce(Return(2)).WillOnce(Return(0)) + .WillOnce(Return(msg_len)).WillOnce(Return(0)); + + ASSERT_NO_THROW(server_callback_dualtor(0, 0, &vlans_in_loop)); + // cover 0 < buffer_sz < sizeof(struct dhcpv6_msg) + ASSERT_NO_THROW(server_callback_dualtor(0, 0, &vlans_in_loop)); + // normal size and right configuration from get_relay_int_from_relay_msg + //ASSERT_NO_THROW(server_callback_dualtor(0, 0, &vlans_in_loop)); + // normal size and NULL from get_relay_int_from_relay_msg + ASSERT_NO_THROW(server_callback_dualtor(0, 0, &vlans_in_loop)); +} + + + diff --git a/test/mock_relay.h b/test/mock_relay.h index 67bf28c..3b575df 100644 --- a/test/mock_relay.h +++ b/test/mock_relay.h @@ -11,4 +11,5 @@ extern struct event_base *base; extern struct event *ev_sigint; extern struct event *ev_sigterm; -extern std::unordered_map vlan_map; \ No newline at end of file +extern std::unordered_map vlan_map; +extern std::unordered_map addr_vlan_map; \ No newline at end of file