From b6495892c44204f4b12f7e8429896c7167a3e3f2 Mon Sep 17 00:00:00 2001
From: jcaiMR <jcai@microsoft.com>
Date: Wed, 7 Jun 2023 13:56:52 +0000
Subject: [PATCH 01/19] 202012 dualtor dhcpv6 relay issue fix

---
 src/main.cpp  |  15 ++-
 src/relay.cpp | 334 +++++++++++++++++++++++++++++++++++++++-----------
 src/relay.h   |  24 ++--
 3 files changed, 286 insertions(+), 87 deletions(-)

diff --git a/src/main.cpp b/src/main.cpp
index 9da3cb8..09613ef 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -4,19 +4,28 @@
 #include "configInterface.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 <loopback interface>]\n");
+    printf("\tloopback interface: is the loopback interface for dual tor setup\n");
 }
 
 int main(int argc, char *argv[]) {
     if (argc > 1) {
         switch (argv[1][1])
         {
-            case 'd':
+            case 'u':
+                if (strlen(argv[i + 1]) < IF_NAMESIZE) {
+                    std::memset(loopback, 0, IF_NAMESIZE);
+                    std::memcpy(loopback, argv[i + 1], strlen(argv[i + 1]));
+                } else {
+                    syslog(LOG_ERR, "loopback interface name over length %d.\n", IF_NAMESIZE);
+                    return 1;
+                }
                 dual_tor_sock = true;
+                i += 2;
                 break;
             default:
                 fprintf(stderr, "%s: Unknown option\n", basename(argv[0]));
diff --git a/src/relay.cpp b/src/relay.cpp
index 64390a9..e98e791 100644
--- a/src/relay.cpp
+++ b/src/relay.cpp
@@ -14,8 +14,6 @@
 
 #define BUFFER_SIZE 9200
 
-struct event *listen_event;
-struct event *server_listen_event;
 struct event_base *base;
 struct event *ev_sigint;
 struct event *ev_sigterm;
@@ -71,6 +69,9 @@ std::map<int, std::string> counterMap = {{DHCPv6_MESSAGE_TYPE_UNKNOWN, "Unknown"
 /* interface to vlan mapping */
 std::unordered_map<std::string, std::string> vlan_map;
 
+/* ipv6 address to vlan name mapping */
+std::unordered_map<sockaddr_in6, std::string> addr_vlan_map;
+
 /**
  * @code                initialize_counter(std::shared_ptr<swss::DBConnector> state_db, std::string counterVlan);
  *
@@ -269,6 +270,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),
@@ -306,22 +310,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) {
@@ -360,38 +364,91 @@ void prepare_relay_config(relay_config &interface_config, int local_sock, int fi
     
     if(!IN6_IS_ADDR_LINKLOCAL(&non_link_local.sin6_addr)) {
         interface_config.link_address = non_link_local;
+        addr_vlan_map[non_link_local] = interface_config.interface;
     }
     else {
         interface_config.link_address = link_local;
+        addr_vlan_map[link_local] = interface_config.interface;
     }
 }
 
+int prepare_lo_socket(const char *lo) {
+    struct ifaddrs *ifa, *ifa_tmp;
+    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;
+    }
+
+    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 (!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_socket(int &local_sock, int &server_sock, relay_config &config);
+ * @code            prepare_vlan_sockets(int &gua_sock, int &lla_sock, relay_config &config);
  * 
- * @brief               prepare L3 socket for sending
+ * @brief           prepare vlan l3 socket for sending
  *
- * @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 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              none
+ * @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) {
     struct ifaddrs *ifa, *ifa_tmp;
-    sockaddr_in6 addr = {0};
-    sockaddr_in6 ll_addr = {0};
+    sockaddr_in6 gua = {0}, lla = {0};
 
-    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());
+    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 ((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 ((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));
@@ -399,19 +456,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;
@@ -419,7 +477,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;
         }
 
@@ -427,13 +485,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;
 }
 
 
@@ -738,11 +805,15 @@ void client_packet_handler(uint8_t *buffer, ssize_t length, struct relay_config
     }
 
     auto option_position = current_position + sizeof(struct dhcpv6_msg);
+    auto relay_socket = config->gua_sock;
+    if (dual_tor_sock) {
+        relay_socket = config->lo_sock;
+    }
 
     switch (msg->msg_type) {
         case DHCPv6_MESSAGE_TYPE_RELAY_FORW:
         {
-            relay_relay_forw(config->local_sock, current_position, ntohs(udp_header->len) - sizeof(udphdr), ip_header, config);
+            relay_relay_forw(relay_socket, current_position, ntohs(udp_header->len) - sizeof(udphdr), ip_header, config);
             break;
         }
         case DHCPv6_MESSAGE_TYPE_SOLICIT:
@@ -766,7 +837,7 @@ void client_packet_handler(uint8_t *buffer, ssize_t length, struct relay_config
             }
             counters[msg->msg_type]++;
             update_counter(config->state_db, counterVlan.append(config->interface), msg->msg_type);
-            relay_client(config->local_sock, current_position, ntohs(udp_header->len) - sizeof(udphdr), ip_header, ether_header, config);
+            relay_client(relay_socket, current_position, ntohs(udp_header->len) - sizeof(udphdr), ip_header, ether_header, config);
             break;
         }
         default:
@@ -777,6 +848,103 @@ void client_packet_handler(uint8_t *buffer, ssize_t length, struct relay_config
     }
 }
 
+/**
+ * @code                bool inline isIPv6Zero(struct in6_addr *addr)
+ * 
+ * @brief               check if ipv6 address is zero
+ *
+ * @param addr          ipv6 address
+ *
+ * @return              bool
+ */
+bool inline isIPv6Zero(struct in6_addr *addr) {
+    return (memcmp(&addr, &in6addr_any, sizeof(in6addr_any)) == 0);
+}
+
+struct relay_config *
+get_relay_int_from_packet(uint8_t *packet, int32_t len, std::unordered_map<std::string, relay_config> &vlans) {
+    auto current_position = packet;
+    auto dhcp_relay_header = parse_dhcpv6_relay(packet);
+
+    // multi-level relay agents
+    if (isIPv6Zero(&dhcp_relay_header->link_address)) {
+        // This is temp solution for multi-level relay
+        // In this case, we need add
+        // 1. Add option82 in Relay Relay-Forward packets
+        // 2. Decode option82 in Relay-Reply to find out source vlan config
+        return NULL;
+    }
+
+    if (addr_vlan_map.find(dhcp_relay_header->link_address) == addr_vlan_map.end()) {
+        char link_addr_str[INET6_ADDRSTRLEN];
+        inet_ntop(AF_INET6, &dhcp_relay_header->link_address, link_addr_str, INET6_ADDRSTRLEN);
+        syslog(LOG_WARNING, "DHCPv6 type %d can't find vlan info from link address %s\n",
+               dhcp_relay_header->msg_type, link_addr_str);
+        return NULL;
+    }
+    auto vlan_name = addr_vlan_map[dhcp_relay_header->link_address];
+
+    if (vlans.find(vlan_name) == vlans.end()) {
+        syslog(LOG_WARNING, "DHCPv6 can't find vlan %s config\n", vlan_name);
+        return NULL;
+    }
+    return &vlans[vlan_name];
+}
+
+/**
+ * @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<std::unordered_map<std::string, struct relay_config> *>(arg);
+    sockaddr_in6 from;
+    socklen_t len = sizeof(from);
+    int32_t pkts_num = 0;
+    static uint8_t message_buffer[BUFFER_SIZE];
+
+    while (pkts_num++ < BATCH_SIZE) {
+        std::string counterVlan = counter_table;
+        auto buffer_sz = recvfrom(fd, message_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 %ld, no space for dhcpv6 msg header\n", buffer_sz);
+            continue;
+        }
+
+        auto config = get_relay_int_from_packet(message_buffer, buffer_sz, vlans)) {
+            syslog(LOG_WARNING, "Invalid DHCPv6 header content, packet will be dropped\n");
+            continue;
+        }
+        auto msg = parse_dhcpv6_hdr(message_buffer);
+        // RFC3315 only
+        if (msg->msg_type < DHCPv6_MESSAGE_TYPE_SOLICIT || msg->msg_type > DHCPv6_MESSAGE_TYPE_RELAY_REPL) {
+            update_counter(config->state_db, counterVlan.append(config->interface), DHCPv6_MESSAGE_TYPE_UNKNOWN);
+            syslog(LOG_WARNING, "Unknown DHCPv6 message type %d\n", msg->msg_type);
+            continue;
+        } else {
+            counters[msg->msg_type]++;
+        }
+
+        update_counter(config->state_db, counterVlan.append(config->interface), msg->msg_type);
+        if (msg->msg_type == DHCPv6_MESSAGE_TYPE_RELAY_REPL) {
+            relay_relay_reply(config->lla_sock, message_buffer, buffer_sz, config);
+        }
+    }
+}
+
 
 /**
  * @code                void server_callback(evutil_socket_t fd, short event, void *arg);
@@ -795,10 +963,10 @@ void server_callback(evutil_socket_t fd, short event, void *arg) {
     socklen_t len = sizeof(from);
     int32_t pkts_num = 0;
     static uint8_t message_buffer[BUFFER_SIZE];
-    std::string counterVlan = counter_table;
 
     while (pkts_num++ < BATCH_SIZE) {
-        auto buffer_sz = recvfrom(config->local_sock, message_buffer, BUFFER_SIZE, 0, (sockaddr *)&from, &len);
+        std::string counterVlan = counter_table;
+        auto buffer_sz = recvfrom(config->gua_sock, message_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));
@@ -823,7 +991,7 @@ void server_callback(evutil_socket_t fd, short event, void *arg) {
 
         update_counter(config->state_db, counterVlan.append(config->interface), msg->msg_type);
         if (msg->msg_type == DHCPv6_MESSAGE_TYPE_RELAY_REPL) {
-            relay_relay_reply(config->server_sock, message_buffer, buffer_sz, config);
+            relay_relay_reply(config->lla_sock, message_buffer, buffer_sz, config);
         }
     }
 }
@@ -922,7 +1090,7 @@ void loop_relay(std::unordered_map<std::string, relay_config> &vlans) {
     std::vector<int> 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");
     }
 
     std::shared_ptr<swss::DBConnector> state_db = std::make_shared<swss::DBConnector> ("STATE_DB", 0);
@@ -934,21 +1102,41 @@ void loop_relay(std::unordered_map<std::string, relay_config> &vlans) {
     auto filter = sock_open(&ether_relay_fprog);
     if (filter != -1) {
         sockets.push_back(filter);
-        listen_event = event_new(base, filter, EV_READ|EV_PERSIST, client_callback,
-                                 reinterpret_cast<void *>(&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<void *>(&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<void *>(&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;
@@ -958,28 +1146,28 @@ void loop_relay(std::unordered_map<std::string, relay_config> &vlans) {
 
         std::string counterVlan = counter_table;
         initialize_counter(vlan.second.state_db, counterVlan.append(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);
+        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);
     
-	    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 (!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 240db54..668f8df 100644
--- a/src/relay.h
+++ b/src/relay.h
@@ -58,8 +58,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<swss::DBConnector> state_db;
@@ -116,30 +117,31 @@ struct interface_id_option  {
  */
 int sock_open(const struct sock_fprog *fprog);
 
+
 /**
- * @code                prepare_socket(int *local_sock, int *server_sock, relay_config *config);
+ * @code            prepare_vlan_sockets(int &gua_sock, int &lla_sock, relay_config &config);
  * 
- * @brief               prepare L3 socket for sending
+ * @brief           prepare vlan l3 socket for sending
  *
- * @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 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              none
+ * @return          none
  */
-void prepare_socket(int *local_sock, int *server_sock, relay_config *config);
+void 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_forward(uint8_t *buffer, const struct dhcpv6_msg *msg, uint16_t msg_length);

From 7b84d91c63a843e8e82529ab8a6b5b7616b2d4ef Mon Sep 17 00:00:00 2001
From: jcaiMR <jcai@microsoft.com>
Date: Sat, 10 Jun 2023 14:06:55 +0000
Subject: [PATCH 02/19] fix multi-relay agent case

---
 src/relay.cpp | 124 +++++++++++++++++++++++++++++++++++---------------
 src/relay.h   |   9 ++--
 2 files changed, 91 insertions(+), 42 deletions(-)

diff --git a/src/relay.cpp b/src/relay.cpp
index e98e791..b0e6a56 100644
--- a/src/relay.cpp
+++ b/src/relay.cpp
@@ -112,6 +112,7 @@ void initialize_counter(std::shared_ptr<swss::DBConnector> state_db, std::string
  * @return              none
  */
 void update_counter(std::shared_ptr<swss::DBConnector> state_db, std::string counterVlan, uint8_t msg_type) {
+    counters[msg_type]++;
     state_db->hset(counterVlan, counterMap.find(msg_type)->second, toString(counters[msg_type]));
 }
 
@@ -226,7 +227,6 @@ const struct dhcpv6_option *parse_dhcpv6_opt(const uint8_t *buffer, const uint8_
 void process_sent_msg(relay_config *config, uint8_t msg_type) {
     std::string counterVlan = counter_table;
     if (counterMap.find(msg_type) != counterMap.end()) {
-        counters[msg_type]++;
         update_counter(config->state_db, counterVlan.append(config->interface), msg_type);
     } else {
         syslog(LOG_WARNING, "unexpected message type %d(0x%x)\n", msg_type, msg_type);
@@ -518,7 +518,7 @@ int prepare_vlan_sockets(int &gua_sock, int &lla_sock, relay_config &config) {
  *
  * @return none
  */
-void relay_client(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, const ether_header *ether_hdr, relay_config *config) {    
+void relay_client(const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, const ether_header *ether_hdr, relay_config *config) {    
     static uint8_t buffer[BUFFER_SIZE];
     auto current_buffer_position = buffer;
     dhcpv6_relay_msg new_message;
@@ -563,6 +563,10 @@ void relay_client(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_h
     relay_forward(current_buffer_position, parse_dhcpv6_hdr(msg), dhcp_message_length);
     current_buffer_position += dhcp_message_length + sizeof(dhcpv6_option);
 
+    auto sock = config->gua_sock;
+    if (dual_tor_sock) {
+        sock = config->lo_sock;
+    }
     for(auto server: config->servers_sock) {
         if(send_udp(sock, buffer, server, current_buffer_position - buffer)) {
             process_sent_msg(config, new_message.msg_type);
@@ -583,7 +587,7 @@ void relay_client(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_h
  *
  * @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) {
     static uint8_t buffer[BUFFER_SIZE];
     dhcpv6_relay_msg new_message;
     auto current_buffer_position = buffer;
@@ -601,10 +605,28 @@ void relay_relay_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *
     memcpy(current_buffer_position, &new_message, sizeof(dhcpv6_relay_msg));
     current_buffer_position += sizeof(dhcpv6_relay_msg);
 
+    // insert option82 for new relay-forward packet, we need this information
+    // to get original relay-forward source interface for accurate counting in dualtor env
+    if (dual_tor_sock) {
+        interface_id_option intf_id;
+        intf_id.option_code = htons(OPTION_INTERFACE_ID);
+        intf_id.option_length = htons(sizeof(in6_addr));
+        intf_id.interface_id = config->link_address.sin6_addr;
+        if ((unsigned)(current_buffer_position + sizeof(linklayer_addr_option) - buffer) > sizeof(buffer)) {
+            return;
+        }
+        memcpy(current_buffer_position, &intf_id, sizeof(interface_id_option));
+        current_buffer_position += sizeof(interface_id_option);
+    }
+
     auto dhcp_message_length = len;
     relay_forward(current_buffer_position, parse_dhcpv6_hdr(msg), dhcp_message_length);
     current_buffer_position += dhcp_message_length + sizeof(dhcpv6_option);
 
+    auto sock = config->gua_sock;
+    if (dual_tor_sock) {
+        sock = config->lo_sock;
+    }
     for(auto server: config->servers_sock) {
         if(send_udp(sock, buffer, server, current_buffer_position - buffer)) {
             process_sent_msg(config, new_message.msg_type);
@@ -658,6 +680,11 @@ void relay_relay_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *
     target_addr.sin6_flowinfo = 0;
     target_addr.sin6_port = htons(CLIENT_PORT);
     target_addr.sin6_scope_id = if_nametoindex(config->interface.c_str());
+    auto sock = config->lla_sock;
+    if (isIPv6Zero(dhcp_relay_header->link_address)) {
+        // In this case, it's multi-level relay
+        sock = config->gua_sock;
+    }
 
     if(send_udp(sock, buffer, target_addr, current_buffer_position - buffer)) {
         process_sent_msg(config, type);
@@ -805,15 +832,10 @@ void client_packet_handler(uint8_t *buffer, ssize_t length, struct relay_config
     }
 
     auto option_position = current_position + sizeof(struct dhcpv6_msg);
-    auto relay_socket = config->gua_sock;
-    if (dual_tor_sock) {
-        relay_socket = config->lo_sock;
-    }
-
     switch (msg->msg_type) {
         case DHCPv6_MESSAGE_TYPE_RELAY_FORW:
         {
-            relay_relay_forw(relay_socket, current_position, ntohs(udp_header->len) - sizeof(udphdr), ip_header, config);
+            relay_relay_forw(current_position, ntohs(udp_header->len) - sizeof(udphdr), ip_header, config);
             break;
         }
         case DHCPv6_MESSAGE_TYPE_SOLICIT:
@@ -829,15 +851,13 @@ void client_packet_handler(uint8_t *buffer, ssize_t length, struct relay_config
                 auto option = parse_dhcpv6_opt(option_position, &tmp);
                 option_position = tmp;
                 if (ntohs(option->option_code) > DHCPv6_OPTION_LIMIT) {
-                    counters[DHCPv6_MESSAGE_TYPE_MALFORMED]++;
                     update_counter(config->state_db, counterVlan.append(config->interface), DHCPv6_MESSAGE_TYPE_MALFORMED);
                     syslog(LOG_WARNING, "DHCPv6 option is invalid or contains malformed payload from %s\n", ifname.c_str());
                     return;
                 }
             }
-            counters[msg->msg_type]++;
             update_counter(config->state_db, counterVlan.append(config->interface), msg->msg_type);
-            relay_client(relay_socket, current_position, ntohs(udp_header->len) - sizeof(udphdr), ip_header, ether_header, config);
+            relay_client(current_position, ntohs(udp_header->len) - sizeof(udphdr), ip_header, ether_header, config);
             break;
         }
         default:
@@ -861,28 +881,62 @@ bool inline isIPv6Zero(struct in6_addr *addr) {
     return (memcmp(&addr, &in6addr_any, sizeof(in6addr_any)) == 0);
 }
 
+/**
+ * @code                struct relay_config *
+ *                      get_relay_int_from_relay_msg(uint8_t *packet, int32_t len,
+ *                                                   std::unordered_map<std::string, relay_config> &vlans)
+ * 
+ * @brief               get relay interface info from relay message
+ *
+ * @param addr          ipv6 address
+ *
+ * @return              bool
+ */
 struct relay_config *
-get_relay_int_from_packet(uint8_t *packet, int32_t len, std::unordered_map<std::string, relay_config> &vlans) {
+get_relay_int_from_relay_msg(uint8_t *packet, int32_t len, std::unordered_map<std::string, relay_config> &vlans) {
     auto current_position = packet;
     auto dhcp_relay_header = parse_dhcpv6_relay(packet);
+    interface_id_option intf_id;
 
+    current_position += sizeof(struct dhcpv6_relay_msg);
+    while ((current_position - packet) < len) {
+        const uint8_t *tmp = NULL;
+        auto option = parse_dhcpv6_opt(current_position, &tmp);
+        current_position = tmp;
+        if (current_position - msg > len) {
+            break;
+        }
+        switch (ntohs(option->option_code)) {
+            case OPTION_INTERFACE_ID: {
+                intf_id.option_code = OPTION_INTERFACE_ID;
+                intf_id.option_length = ntohs(option->option_length);
+                memcpy(&intf_id.interface_id, ((uint8_t *)option) + sizeof(struct dhcpv6_option), intf_id.option_length);
+                break;
+            }
+            default:
+                break;
+        }
+    }
+
+    struct in6_addr *addr = NULL;
+    if (!isIPv6Zero(&intf_id.interface_id)) {
+        addr = &intf_id.interface_id;
+    } else if (isIPv6Zero(&dhcp_relay_header->link_address)) {
+        addr = &dhcp_relay_header->link_address;
+    }
     // multi-level relay agents
-    if (isIPv6Zero(&dhcp_relay_header->link_address)) {
-        // This is temp solution for multi-level relay
-        // In this case, we need add
-        // 1. Add option82 in Relay Relay-Forward packets
-        // 2. Decode option82 in Relay-Reply to find out source vlan config
+    if (!addr) {
         return NULL;
     }
 
-    if (addr_vlan_map.find(dhcp_relay_header->link_address) == addr_vlan_map.end()) {
+    if (addr_vlan_map.find(*addr) == addr_vlan_map.end()) {
         char link_addr_str[INET6_ADDRSTRLEN];
-        inet_ntop(AF_INET6, &dhcp_relay_header->link_address, link_addr_str, INET6_ADDRSTRLEN);
+        inet_ntop(AF_INET6, addr, link_addr_str, INET6_ADDRSTRLEN);
         syslog(LOG_WARNING, "DHCPv6 type %d can't find vlan info from link address %s\n",
                dhcp_relay_header->msg_type, link_addr_str);
         return NULL;
     }
-    auto vlan_name = addr_vlan_map[dhcp_relay_header->link_address];
+    auto vlan_name = addr_vlan_map[*addr];
 
     if (vlans.find(vlan_name) == vlans.end()) {
         syslog(LOG_WARNING, "DHCPv6 can't find vlan %s config\n", vlan_name);
@@ -924,24 +978,22 @@ void server_callback_dualtor(evutil_socket_t fd, short event, void *arg) {
             continue;
         }
 
-        auto config = get_relay_int_from_packet(message_buffer, buffer_sz, vlans)) {
-            syslog(LOG_WARNING, "Invalid DHCPv6 header content, packet will be dropped\n");
+        auto msg = parse_dhcpv6_hdr(message_buffer);
+
+        if (msg->msg_type != DHCPv6_MESSAGE_TYPE_RELAY_REPL) {
+            syslog(LOG_WARNING, "Invalid DHCPv6 message type %d received on loopback interface\n", msg->msg_type);
+            update_counter(config->state_db, counterVlan.append(std::string(loopback)), msg->msg_type);
             continue;
         }
-        auto msg = parse_dhcpv6_hdr(message_buffer);
-        // RFC3315 only
-        if (msg->msg_type < DHCPv6_MESSAGE_TYPE_SOLICIT || msg->msg_type > DHCPv6_MESSAGE_TYPE_RELAY_REPL) {
-            update_counter(config->state_db, counterVlan.append(config->interface), DHCPv6_MESSAGE_TYPE_UNKNOWN);
-            syslog(LOG_WARNING, "Unknown DHCPv6 message type %d\n", msg->msg_type);
+        auto config = get_relay_int_from_relay_msg(message_buffer, buffer_sz, vlans);
+        if (!config) {
+            syslog(LOG_WARNING, "Invalid DHCPv6 header content on loopback socket, packet will be dropped\n");
+            update_counter(config->state_db, counterVlan.append(std::string(loopback)), msg->msg_type);
             continue;
-        } else {
-            counters[msg->msg_type]++;
         }
+        update_counter(config->state_db, counterVlan.append(std::string(loopback)), msg->msg_type);
 
-        update_counter(config->state_db, counterVlan.append(config->interface), msg->msg_type);
-        if (msg->msg_type == DHCPv6_MESSAGE_TYPE_RELAY_REPL) {
-            relay_relay_reply(config->lla_sock, message_buffer, buffer_sz, config);
-        }
+        relay_relay_reply(message_buffer, buffer_sz, config);
     }
 }
 
@@ -985,13 +1037,11 @@ void server_callback(evutil_socket_t fd, short event, void *arg) {
             update_counter(config->state_db, counterVlan.append(config->interface), DHCPv6_MESSAGE_TYPE_UNKNOWN);
             syslog(LOG_WARNING, "Unknown DHCPv6 message type %d\n", msg->msg_type);
             continue;
-        } else {
-            counters[msg->msg_type]++;
         }
 
         update_counter(config->state_db, counterVlan.append(config->interface), msg->msg_type);
         if (msg->msg_type == DHCPv6_MESSAGE_TYPE_RELAY_REPL) {
-            relay_relay_reply(config->lla_sock, message_buffer, buffer_sz, config);
+            relay_relay_reply(message_buffer, buffer_sz, config);
         }
     }
 }
diff --git a/src/relay.h b/src/relay.h
index 668f8df..ba2c49e 100644
--- a/src/relay.h
+++ b/src/relay.h
@@ -170,7 +170,7 @@ void relay_forward(uint8_t *buffer, const struct dhcpv6_msg *msg, uint16_t msg_l
  *
  * @return none
  */
-void relay_client(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, const ether_header *ether_hdr, relay_config *config);
+void relay_client(const uint8_t *msg, int32_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)
@@ -185,21 +185,20 @@ void relay_client(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_h
  *
  * @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                loop_relay(std::unordered_map<std::string, relay_config> &vlans);

From 1c183075c45040c454839a874b8ca838c628e1dd Mon Sep 17 00:00:00 2001
From: jcaiMR <jcai@microsoft.com>
Date: Tue, 13 Jun 2023 12:31:16 +0000
Subject: [PATCH 03/19] fix input parameter issue

---
 src/main.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/main.cpp b/src/main.cpp
index 09613ef..54fc75c 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -13,23 +13,23 @@ static void usage()
 }
 
 int main(int argc, char *argv[]) {
-    if (argc > 1) {
+    if (argc > 2) {
         switch (argv[1][1])
         {
             case 'u':
-                if (strlen(argv[i + 1]) < IF_NAMESIZE) {
+                if (strlen(argv[2]) != 0 && strlen(argv[2]) < IF_NAMESIZE) {
                     std::memset(loopback, 0, IF_NAMESIZE);
-                    std::memcpy(loopback, argv[i + 1], strlen(argv[i + 1]));
+                    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;
-                i += 2;
                 break;
             default:
                 fprintf(stderr, "%s: Unknown option\n", basename(argv[0]));
                 usage();
+                return 0;
         }
     }
     try {

From fdeed08a8e898727ee049d1d0403a1be7cf430d3 Mon Sep 17 00:00:00 2001
From: jcaiMR <jcai@microsoft.com>
Date: Wed, 14 Jun 2023 13:09:14 +0000
Subject: [PATCH 04/19] fix build issue

---
 src/relay.cpp      | 3 +--
 src/relay.h        | 6 ++----
 test/MockRelay.cpp | 6 +++---
 3 files changed, 6 insertions(+), 9 deletions(-)

diff --git a/src/relay.cpp b/src/relay.cpp
index b0e6a56..1136709 100644
--- a/src/relay.cpp
+++ b/src/relay.cpp
@@ -575,11 +575,10 @@ void relay_client(const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, const
 }
 
 /**
- * @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
diff --git a/src/relay.h b/src/relay.h
index ba2c49e..4a9f334 100644
--- a/src/relay.h
+++ b/src/relay.h
@@ -157,11 +157,10 @@ void prepare_relay_config(relay_config &interface_config, int gua_sock, int filt
 void relay_forward(uint8_t *buffer, const struct dhcpv6_msg *msg, uint16_t msg_length);
 
 /**
- * @code                 relay_client(int sock, const uint8_t *msg, int32_t len, ip6_hdr *ip_hdr, const ether_header *ether_hdr, relay_config *config);
+ * @code                 relay_client(const uint8_t *msg, int32_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
@@ -173,11 +172,10 @@ void relay_forward(uint8_t *buffer, const struct dhcpv6_msg *msg, uint16_t msg_l
 void relay_client(const uint8_t *msg, int32_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
diff --git a/test/MockRelay.cpp b/test/MockRelay.cpp
index 0d14de7..af0208d 100644
--- a/test/MockRelay.cpp
+++ b/test/MockRelay.cpp
@@ -357,7 +357,7 @@ TEST(relay, relay_client)
   ip6_hdr ip_hdr;
   std::string s_addr = "2000::3";
 
-  relay_client(mock_sock, msg, msg_len, &ip_hdr, &ether_hdr, &config);
+  relay_client(msg, msg_len, &ip_hdr, &ether_hdr, &config);
 
   EXPECT_EQ(last_used_sock, 124);
 
@@ -433,7 +433,7 @@ TEST(relay, relay_relay_forw) {
   std::string s_addr = "2000::3";
   inet_pton(AF_INET6, s_addr.c_str(), &ip_hdr.ip6_src);
 
-  relay_relay_forw(mock_sock, msg, msg_len, &ip_hdr, &config);
+  relay_relay_forw(msg, msg_len, &ip_hdr, &config);
 
   EXPECT_EQ(last_used_sock, 125);
 
@@ -497,7 +497,7 @@ TEST(relay, relay_relay_reply)
 
   prepare_relay_config(config, local_sock, filter);
 
-  relay_relay_reply(mock_sock, msg, msg_len, &config);
+  relay_relay_reply(msg, msg_len, &config);
 
   EXPECT_EQ(last_used_sock, 123);
 

From ad0fa28228b6043fbbd012197a807bc68c54b3bb Mon Sep 17 00:00:00 2001
From: jcaiMR <jcai@microsoft.com>
Date: Wed, 14 Jun 2023 13:37:06 +0000
Subject: [PATCH 05/19] fix build issue, unordered_map

---
 src/relay.cpp | 6 +++---
 src/relay.h   | 4 ++--
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/relay.cpp b/src/relay.cpp
index 1136709..bddf870 100644
--- a/src/relay.cpp
+++ b/src/relay.cpp
@@ -70,7 +70,7 @@ std::map<int, std::string> counterMap = {{DHCPv6_MESSAGE_TYPE_UNKNOWN, "Unknown"
 std::unordered_map<std::string, std::string> vlan_map;
 
 /* ipv6 address to vlan name mapping */
-std::unordered_map<sockaddr_in6, std::string> addr_vlan_map;
+std::unordered_map<in6_addr, std::string> addr_vlan_map;
 
 /**
  * @code                initialize_counter(std::shared_ptr<swss::DBConnector> state_db, std::string counterVlan);
@@ -364,11 +364,11 @@ void prepare_relay_config(relay_config &interface_config, int gua_sock, int filt
     
     if(!IN6_IS_ADDR_LINKLOCAL(&non_link_local.sin6_addr)) {
         interface_config.link_address = non_link_local;
-        addr_vlan_map[non_link_local] = interface_config.interface;
+        addr_vlan_map[non_link_local.sin6_addr] = interface_config.interface;
     }
     else {
         interface_config.link_address = link_local;
-        addr_vlan_map[link_local] = interface_config.interface;
+        addr_vlan_map[link_local.sin6_addr] = interface_config.interface;
     }
 }
 
diff --git a/src/relay.h b/src/relay.h
index 4a9f334..fd23558 100644
--- a/src/relay.h
+++ b/src/relay.h
@@ -126,9 +126,9 @@ int sock_open(const struct sock_fprog *fprog);
  * @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          none
+ * @return          int
  */
-void prepare_vlan_sockets(int &gua_sock, int &lla_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 gua_sock, int filter);

From 14105af4072491c83147fb0ca731e6da740cd475 Mon Sep 17 00:00:00 2001
From: jcaiMR <jcai@microsoft.com>
Date: Wed, 14 Jun 2023 14:54:43 +0000
Subject: [PATCH 06/19] fix build issue and multi-level relay target port issue

---
 src/relay.cpp      | 61 +++++++++++++++++++++++-----------------------
 src/relay.h        |  1 +
 test/MockRelay.cpp | 15 +++++++-----
 3 files changed, 41 insertions(+), 36 deletions(-)

diff --git a/src/relay.cpp b/src/relay.cpp
index bddf870..0515f09 100644
--- a/src/relay.cpp
+++ b/src/relay.cpp
@@ -70,7 +70,7 @@ std::map<int, std::string> counterMap = {{DHCPv6_MESSAGE_TYPE_UNKNOWN, "Unknown"
 std::unordered_map<std::string, std::string> vlan_map;
 
 /* ipv6 address to vlan name mapping */
-std::unordered_map<in6_addr, std::string> addr_vlan_map;
+std::map<struct in6_addr, std::string> addr_vlan_map;
 
 /**
  * @code                initialize_counter(std::shared_ptr<swss::DBConnector> state_db, std::string counterVlan);
@@ -132,6 +132,19 @@ std::string toString(uint64_t count) {
     return countValue;
 }
 
+/**
+ * @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);
+}
+
 /**
  * @code                const struct ether_header *parse_ether_frame(const uint8_t *buffer, const uint8_t **out_end);
  *
@@ -392,7 +405,7 @@ int prepare_lo_socket(const char *lo) {
     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)) {
+            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;
@@ -457,7 +470,7 @@ int prepare_vlan_sockets(int &gua_sock, int &lla_sock, relay_config &config) {
             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, config.interface.c_str()) == 0)) {
+                    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;
@@ -563,7 +576,7 @@ void relay_client(const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, const
     relay_forward(current_buffer_position, parse_dhcpv6_hdr(msg), dhcp_message_length);
     current_buffer_position += dhcp_message_length + sizeof(dhcpv6_option);
 
-    auto sock = config->gua_sock;
+    int sock = config->gua_sock;
     if (dual_tor_sock) {
         sock = config->lo_sock;
     }
@@ -622,7 +635,7 @@ void relay_relay_forw(const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, re
     relay_forward(current_buffer_position, parse_dhcpv6_hdr(msg), dhcp_message_length);
     current_buffer_position += dhcp_message_length + sizeof(dhcpv6_option);
 
-    auto sock = config->gua_sock;
+    int sock = config->gua_sock;
     if (dual_tor_sock) {
         sock = config->lo_sock;
     }
@@ -679,10 +692,12 @@ void relay_relay_forw(const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, re
     target_addr.sin6_flowinfo = 0;
     target_addr.sin6_port = htons(CLIENT_PORT);
     target_addr.sin6_scope_id = if_nametoindex(config->interface.c_str());
-    auto sock = config->lla_sock;
+    int sock = config->lla_sock;
     if (isIPv6Zero(dhcp_relay_header->link_address)) {
         // In this case, it's multi-level relay
-        sock = config->gua_sock;
+        if (!IN6_IS_ADDR_LINKLOCAL(&dhcp_relay_header->peer_address))
+            sock = config->gua_sock;
+        target_addr.sin6_port = htons(RELAY_PORT);
     }
 
     if(send_udp(sock, buffer, target_addr, current_buffer_position - buffer)) {
@@ -867,23 +882,10 @@ void client_packet_handler(uint8_t *buffer, ssize_t length, struct relay_config
     }
 }
 
-/**
- * @code                bool inline isIPv6Zero(struct in6_addr *addr)
- * 
- * @brief               check if ipv6 address is zero
- *
- * @param addr          ipv6 address
- *
- * @return              bool
- */
-bool inline isIPv6Zero(struct in6_addr *addr) {
-    return (memcmp(&addr, &in6addr_any, sizeof(in6addr_any)) == 0);
-}
-
 /**
  * @code                struct relay_config *
- *                      get_relay_int_from_relay_msg(uint8_t *packet, int32_t len,
- *                                                   std::unordered_map<std::string, relay_config> &vlans)
+ *                      get_relay_int_from_relay_msg(uint8_t *msg, int32_t len,
+ *                                                   std::unordered_map<std::string, relay_config> *vlans)
  * 
  * @brief               get relay interface info from relay message
  *
@@ -892,14 +894,14 @@ bool inline isIPv6Zero(struct in6_addr *addr) {
  * @return              bool
  */
 struct relay_config *
-get_relay_int_from_relay_msg(uint8_t *packet, int32_t len, std::unordered_map<std::string, relay_config> &vlans) {
-    auto current_position = packet;
-    auto dhcp_relay_header = parse_dhcpv6_relay(packet);
+get_relay_int_from_relay_msg(uint8_t *msg, int32_t len, std::unordered_map<std::string, relay_config> *vlans) {
+    auto current_position = msg;
+    auto dhcp_relay_header = parse_dhcpv6_relay(msg);
     interface_id_option intf_id;
 
     current_position += sizeof(struct dhcpv6_relay_msg);
-    while ((current_position - packet) < len) {
-        const uint8_t *tmp = NULL;
+    while ((current_position - msg) < len) {
+        uint8_t *tmp = NULL;
         auto option = parse_dhcpv6_opt(current_position, &tmp);
         current_position = tmp;
         if (current_position - msg > len) {
@@ -937,8 +939,8 @@ get_relay_int_from_relay_msg(uint8_t *packet, int32_t len, std::unordered_map<st
     }
     auto vlan_name = addr_vlan_map[*addr];
 
-    if (vlans.find(vlan_name) == vlans.end()) {
-        syslog(LOG_WARNING, "DHCPv6 can't find vlan %s config\n", vlan_name);
+    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[vlan_name];
@@ -981,7 +983,6 @@ void server_callback_dualtor(evutil_socket_t fd, short event, void *arg) {
 
         if (msg->msg_type != DHCPv6_MESSAGE_TYPE_RELAY_REPL) {
             syslog(LOG_WARNING, "Invalid DHCPv6 message type %d received on loopback interface\n", msg->msg_type);
-            update_counter(config->state_db, counterVlan.append(std::string(loopback)), msg->msg_type);
             continue;
         }
         auto config = get_relay_int_from_relay_msg(message_buffer, buffer_sz, vlans);
diff --git a/src/relay.h b/src/relay.h
index fd23558..d2a45e2 100644
--- a/src/relay.h
+++ b/src/relay.h
@@ -34,6 +34,7 @@
 #define BATCH_SIZE 64
 
 extern bool dual_tor_sock;
+extern char loopback[IF_NAMESIZE];
 
 /* DHCPv6 message types */
 typedef enum
diff --git a/test/MockRelay.cpp b/test/MockRelay.cpp
index af0208d..2e84081 100644
--- a/test/MockRelay.cpp
+++ b/test/MockRelay.cpp
@@ -316,8 +316,6 @@ TEST(counter, update_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,
@@ -345,6 +343,9 @@ TEST(relay, relay_client)
   }
   std::shared_ptr<swss::DBConnector> state_db = std::make_shared<swss::DBConnector> ("STATE_DB", 0);
   config.state_db = state_db;
+  config.gua_sock = 125;
+  config.lla_sock = 125;
+  config.lo_sock = 125;
 
   struct ether_header ether_hdr;
   ether_hdr.ether_shost[0] = 0x5a;
@@ -394,8 +395,6 @@ TEST(relay, relay_client)
 }
 
 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,
@@ -428,6 +427,9 @@ TEST(relay, relay_relay_forw) {
   }
   std::shared_ptr<swss::DBConnector> state_db = std::make_shared<swss::DBConnector> ("STATE_DB", 0);
   config.state_db = state_db;
+  config.gua_sock = 125;
+  config.lla_sock = 125;
+  config.lo_sock = 125;
 
   ip6_hdr ip_hdr;
   std::string s_addr = "2000::3";
@@ -453,8 +455,6 @@ TEST(relay, relay_relay_forw) {
 
 TEST(relay, relay_relay_reply) 
 {
-  int mock_sock = 123;
-
   uint8_t msg[] = { 
       0x0d, 0x00, 0x20, 0x01, 0x0d, 0xb8, 0x01, 0x5a,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -491,6 +491,9 @@ TEST(relay, relay_relay_reply)
   config.interface = "Vlan1000";
   std::shared_ptr<swss::DBConnector> state_db = std::make_shared<swss::DBConnector> ("STATE_DB", 0);
   config.state_db = state_db;
+  config.gua_sock = 125;
+  config.lla_sock = 125;
+  config.lo_sock = 125;
 
   int local_sock = 1;
   int filter = 1;

From 8b18980574e048ba4a7feacbd45dcc24414e291d Mon Sep 17 00:00:00 2001
From: jcaiMR <jcai@microsoft.com>
Date: Wed, 14 Jun 2023 15:16:56 +0000
Subject: [PATCH 07/19] fix  build issue

---
 src/relay.cpp | 25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/src/relay.cpp b/src/relay.cpp
index 0515f09..ae886c0 100644
--- a/src/relay.cpp
+++ b/src/relay.cpp
@@ -410,8 +410,8 @@ int prepare_lo_socket(const char *lo) {
                 if (!IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) {
                     bind_gua = true;
                     gua = *in6;
-                    gua->sin6_family = AF_INET6;
-                    gua->sin6_port = htons(RELAY_PORT);
+                    gua.sin6_family = AF_INET6;
+                    gua.sin6_port = htons(RELAY_PORT);
                     break;
                 }
             }
@@ -475,13 +475,13 @@ int prepare_vlan_sockets(int &gua_sock, int &lla_sock, relay_config &config) {
                         if (!IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) {
                             bind_gua = true;
                             gua = *in6;
-                            gua->sin6_family = AF_INET6;
-                            gua->sin6_port = htons(RELAY_PORT);
+                            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);
+                            lla.sin6_addr = AF_INET6;
+                            lla.sin6_port = htons(RELAY_PORT);
                         }
                     }
                 }
@@ -647,18 +647,17 @@ void relay_relay_forw(const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, re
 }
 
 /**
- * @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 *config) {
+ void relay_relay_reply(const uint8_t *msg, int32_t len, relay_config *config) {
     static uint8_t buffer[BUFFER_SIZE];
     uint8_t type = 0;
     struct sockaddr_in6 target_addr;
@@ -693,7 +692,7 @@ void relay_relay_forw(const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, re
     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(dhcp_relay_header->link_address)) {
+    if (isIPv6Zero(&dhcp_relay_header->link_address)) {
         // In this case, it's multi-level relay
         if (!IN6_IS_ADDR_LINKLOCAL(&dhcp_relay_header->peer_address))
             sock = config->gua_sock;
@@ -901,7 +900,7 @@ get_relay_int_from_relay_msg(uint8_t *msg, int32_t len, std::unordered_map<std::
 
     current_position += sizeof(struct dhcpv6_relay_msg);
     while ((current_position - msg) < len) {
-        uint8_t *tmp = NULL;
+        const uint8_t *tmp = NULL;
         auto option = parse_dhcpv6_opt(current_position, &tmp);
         current_position = tmp;
         if (current_position - msg > len) {
@@ -919,7 +918,7 @@ get_relay_int_from_relay_msg(uint8_t *msg, int32_t len, std::unordered_map<std::
         }
     }
 
-    struct in6_addr *addr = NULL;
+    const in6_addr *addr = NULL;
     if (!isIPv6Zero(&intf_id.interface_id)) {
         addr = &intf_id.interface_id;
     } else if (isIPv6Zero(&dhcp_relay_header->link_address)) {
@@ -943,7 +942,7 @@ get_relay_int_from_relay_msg(uint8_t *msg, int32_t len, std::unordered_map<std::
         syslog(LOG_WARNING, "DHCPv6 can't find vlan %s config\n", vlan_name.c_str());
         return NULL;
     }
-    return &vlans[vlan_name];
+    return &vlans->find(vlan_name)->second;
 }
 
 /**

From 2cbaea84f6ced5b392e45393d8aa28b42e384818 Mon Sep 17 00:00:00 2001
From: jcaiMR <jcai@microsoft.com>
Date: Wed, 14 Jun 2023 15:57:55 +0000
Subject: [PATCH 08/19] fix hash map

---
 src/relay.cpp | 22 ++++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)

diff --git a/src/relay.cpp b/src/relay.cpp
index ae886c0..45eb2c8 100644
--- a/src/relay.cpp
+++ b/src/relay.cpp
@@ -70,7 +70,17 @@ std::map<int, std::string> counterMap = {{DHCPv6_MESSAGE_TYPE_UNKNOWN, "Unknown"
 std::unordered_map<std::string, std::string> vlan_map;
 
 /* ipv6 address to vlan name mapping */
-std::map<struct in6_addr, std::string> addr_vlan_map;
+struct in6_addrHash {
+    std::size_t operator()(const in6_addr& k) const {
+        std::size_t res = 17;
+        res = res * 31 + std::hash<uint32_t>()(k.__in6_u.__u6_addr32[0]);
+        res = res * 31 + std::hash<uint32_t>()(k.__in6_u.__u6_addr32[1]);
+        res = res * 31 + std::hash<uint32_t>()(k.__in6_u.__u6_addr32[2]);
+        res = res * 31 + std::hash<uint32_t>()(k.__in6_u.__u6_addr32[3]);
+        return res;
+    }
+};
+std::unordered_map<in6_addr, std::string, struct in6_addrHash> addr_vlan_map;
 
 /**
  * @code                initialize_counter(std::shared_ptr<swss::DBConnector> state_db, std::string counterVlan);
@@ -377,11 +387,11 @@ void prepare_relay_config(relay_config &interface_config, int gua_sock, int filt
     
     if(!IN6_IS_ADDR_LINKLOCAL(&non_link_local.sin6_addr)) {
         interface_config.link_address = non_link_local;
-        addr_vlan_map[non_link_local.sin6_addr] = interface_config.interface;
+        addr_vlan_map.insert({non_link_local.sin6_addr, interface_config.interface});
     }
     else {
         interface_config.link_address = link_local;
-        addr_vlan_map[link_local.sin6_addr] = interface_config.interface;
+        addr_vlan_map.insert({link_local.sin6_addr, interface_config.interface});
     }
 }
 
@@ -480,7 +490,7 @@ int prepare_vlan_sockets(int &gua_sock, int &lla_sock, relay_config &config) {
                         } else {
                             bind_lla = true;
                             lla = *in6;
-                            lla.sin6_addr = AF_INET6;
+                            lla.sin6_family = AF_INET6;
                             lla.sin6_port = htons(RELAY_PORT);
                         }
                     }
@@ -883,7 +893,7 @@ void client_packet_handler(uint8_t *buffer, ssize_t length, struct relay_config
 
 /**
  * @code                struct relay_config *
- *                      get_relay_int_from_relay_msg(uint8_t *msg, int32_t len,
+ *                      get_relay_int_from_relay_msg(const uint8_t *msg, int32_t len,
  *                                                   std::unordered_map<std::string, relay_config> *vlans)
  * 
  * @brief               get relay interface info from relay message
@@ -893,7 +903,7 @@ void client_packet_handler(uint8_t *buffer, ssize_t length, struct relay_config
  * @return              bool
  */
 struct relay_config *
-get_relay_int_from_relay_msg(uint8_t *msg, int32_t len, std::unordered_map<std::string, relay_config> *vlans) {
+get_relay_int_from_relay_msg(const uint8_t *msg, int32_t len, std::unordered_map<std::string, relay_config> *vlans) {
     auto current_position = msg;
     auto dhcp_relay_header = parse_dhcpv6_relay(msg);
     interface_id_option intf_id;

From 41e6bc948e0fc30532c521179fab99ab434d7118 Mon Sep 17 00:00:00 2001
From: jcaiMR <jcai@microsoft.com>
Date: Wed, 14 Jun 2023 16:19:34 +0000
Subject: [PATCH 09/19] fix map issue

---
 src/relay.cpp | 30 ++++++++++++------------------
 1 file changed, 12 insertions(+), 18 deletions(-)

diff --git a/src/relay.cpp b/src/relay.cpp
index 45eb2c8..ac1d477 100644
--- a/src/relay.cpp
+++ b/src/relay.cpp
@@ -70,17 +70,7 @@ std::map<int, std::string> counterMap = {{DHCPv6_MESSAGE_TYPE_UNKNOWN, "Unknown"
 std::unordered_map<std::string, std::string> vlan_map;
 
 /* ipv6 address to vlan name mapping */
-struct in6_addrHash {
-    std::size_t operator()(const in6_addr& k) const {
-        std::size_t res = 17;
-        res = res * 31 + std::hash<uint32_t>()(k.__in6_u.__u6_addr32[0]);
-        res = res * 31 + std::hash<uint32_t>()(k.__in6_u.__u6_addr32[1]);
-        res = res * 31 + std::hash<uint32_t>()(k.__in6_u.__u6_addr32[2]);
-        res = res * 31 + std::hash<uint32_t>()(k.__in6_u.__u6_addr32[3]);
-        return res;
-    }
-};
-std::unordered_map<in6_addr, std::string, struct in6_addrHash> addr_vlan_map;
+std::unordered_map<std::string, std::string> addr_vlan_map;
 
 /**
  * @code                initialize_counter(std::shared_ptr<swss::DBConnector> state_db, std::string counterVlan);
@@ -385,13 +375,16 @@ void prepare_relay_config(relay_config &interface_config, int gua_sock, int filt
     }
     freeifaddrs(ifa); 
     
+    char ipv6_str[INET6_ADDRSTRLEN] = {};
     if(!IN6_IS_ADDR_LINKLOCAL(&non_link_local.sin6_addr)) {
         interface_config.link_address = non_link_local;
-        addr_vlan_map.insert({non_link_local.sin6_addr, interface_config.interface});
+        inet_ntop(AF_INET6, &non_link_local.sin6_addr, ipv6_str, INET6_ADDRSTRLEN);
+        addr_vlan_map[std::string(ipv6_str)] = interface_config.interface;
     }
     else {
         interface_config.link_address = link_local;
-        addr_vlan_map.insert({link_local.sin6_addr, interface_config.interface});
+        inet_ntop(AF_INET6, &link_local.sin6_addr, ipv6_str, INET6_ADDRSTRLEN);
+        addr_vlan_map[std::string(ipv6_str)] = interface_config.interface;
     }
 }
 
@@ -939,14 +932,15 @@ get_relay_int_from_relay_msg(const uint8_t *msg, int32_t len, std::unordered_map
         return NULL;
     }
 
-    if (addr_vlan_map.find(*addr) == addr_vlan_map.end()) {
-        char link_addr_str[INET6_ADDRSTRLEN];
-        inet_ntop(AF_INET6, addr, link_addr_str, INET6_ADDRSTRLEN);
+    char ipv6_str[INET6_ADDRSTRLEN] = {};
+    inet_ntop(AF_INET6, addr, 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",
-               dhcp_relay_header->msg_type, link_addr_str);
+               dhcp_relay_header->msg_type, ipv6_str);
         return NULL;
     }
-    auto vlan_name = addr_vlan_map[*addr];
+    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());

From 2c38694fd92ad5a3b15418675f5e97465a430f0c Mon Sep 17 00:00:00 2001
From: jcaiMR <jcai@microsoft.com>
Date: Wed, 14 Jun 2023 16:27:28 +0000
Subject: [PATCH 10/19] fix build issue

---
 src/configInterface.h | 3 +++
 src/main.cpp          | 1 -
 src/relay.h           | 1 -
 3 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/configInterface.h b/src/configInterface.h
index 94a0578..21b0669 100644
--- a/src/configInterface.h
+++ b/src/configInterface.h
@@ -11,6 +11,9 @@ struct swssNotification {
     std::unordered_map<std::string, relay_config> vlans;
     swss::SubscriberStateTable *ipHelpersTable;
 };
+
+char loopback[IF_NAMESIZE] = "Loopback0";
+
 /**
  * @code                void initialize_swss()
  * 
diff --git a/src/main.cpp b/src/main.cpp
index 54fc75c..d952222 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -4,7 +4,6 @@
 #include "configInterface.h"
 
 bool dual_tor_sock = false;
-char loopback[IF_NAMESIZE] = "Loopback0";
 
 static void usage()
 {
diff --git a/src/relay.h b/src/relay.h
index d2a45e2..fd23558 100644
--- a/src/relay.h
+++ b/src/relay.h
@@ -34,7 +34,6 @@
 #define BATCH_SIZE 64
 
 extern bool dual_tor_sock;
-extern char loopback[IF_NAMESIZE];
 
 /* DHCPv6 message types */
 typedef enum

From e8b2b9a1947cb0b884ea9edfd49577d6f5672233 Mon Sep 17 00:00:00 2001
From: jcaiMR <jcai@microsoft.com>
Date: Thu, 15 Jun 2023 06:15:09 +0000
Subject: [PATCH 11/19] fix build issue

---
 src/configInterface.h | 2 --
 src/main.cpp          | 1 +
 src/relay.cpp         | 2 +-
 src/relay.h           | 1 +
 test/main.cpp         | 2 ++
 5 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/configInterface.h b/src/configInterface.h
index 21b0669..38f3ee2 100644
--- a/src/configInterface.h
+++ b/src/configInterface.h
@@ -12,8 +12,6 @@ struct swssNotification {
     swss::SubscriberStateTable *ipHelpersTable;
 };
 
-char loopback[IF_NAMESIZE] = "Loopback0";
-
 /**
  * @code                void initialize_swss()
  * 
diff --git a/src/main.cpp b/src/main.cpp
index d952222..54fc75c 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -4,6 +4,7 @@
 #include "configInterface.h"
 
 bool dual_tor_sock = false;
+char loopback[IF_NAMESIZE] = "Loopback0";
 
 static void usage()
 {
diff --git a/src/relay.cpp b/src/relay.cpp
index ac1d477..b9252ad 100644
--- a/src/relay.cpp
+++ b/src/relay.cpp
@@ -142,7 +142,7 @@ std::string toString(uint64_t count) {
  * @return              bool
  */
 bool inline isIPv6Zero(const in6_addr *addr) {
-    return (memcmp(&addr, &in6addr_any, sizeof(in6addr_any)) == 0);
+    return (memcmp(addr, &in6addr_any, sizeof(in6addr_any)) == 0);
 }
 
 /**
diff --git a/src/relay.h b/src/relay.h
index fd23558..d2a45e2 100644
--- a/src/relay.h
+++ b/src/relay.h
@@ -34,6 +34,7 @@
 #define BATCH_SIZE 64
 
 extern bool dual_tor_sock;
+extern char loopback[IF_NAMESIZE];
 
 /* DHCPv6 message types */
 typedef enum
diff --git a/test/main.cpp b/test/main.cpp
index dfca267..8bbf55c 100644
--- a/test/main.cpp
+++ b/test/main.cpp
@@ -3,6 +3,8 @@
 #include <string>
 
 std::string database_config = "./test/database_config.json";
+bool dual_tor_sock = false;
+char loopback[IF_NAMESIZE] = "Loopback0";
 
 class DhcpRelayEnvironment : public ::testing::Environment {
 public:

From 2bb0f3bae37791f42ac737f3bce7f482bf8722b8 Mon Sep 17 00:00:00 2001
From: jcaiMR <jcai@microsoft.com>
Date: Thu, 15 Jun 2023 06:26:37 +0000
Subject: [PATCH 12/19] fix test code build issue

---
 test/MockRelay.cpp | 1 +
 test/main.cpp      | 3 +--
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/test/MockRelay.cpp b/test/MockRelay.cpp
index 2e84081..dece937 100644
--- a/test/MockRelay.cpp
+++ b/test/MockRelay.cpp
@@ -11,6 +11,7 @@
 #include "MockRelay.h"
 
 bool dual_tor_sock = false;
+char loopback[IF_NAMESIZE] = "Loopback0";
 extern struct event_base *base;
 extern struct event *ev_sigint;
 extern struct event *ev_sigterm;
diff --git a/test/main.cpp b/test/main.cpp
index 8bbf55c..6b3b209 100644
--- a/test/main.cpp
+++ b/test/main.cpp
@@ -1,10 +1,9 @@
 #include "gtest/gtest.h"
+#include <net/if.h>
 #include <swss/dbconnector.h>
 #include <string>
 
 std::string database_config = "./test/database_config.json";
-bool dual_tor_sock = false;
-char loopback[IF_NAMESIZE] = "Loopback0";
 
 class DhcpRelayEnvironment : public ::testing::Environment {
 public:

From 6404aac2a74e970b4132e97b13f9e00fe904636b Mon Sep 17 00:00:00 2001
From: jcaiMR <jcai@microsoft.com>
Date: Wed, 21 Jun 2023 11:35:00 +0000
Subject: [PATCH 13/19] fix link-address get issue

---
 src/relay.cpp | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/src/relay.cpp b/src/relay.cpp
index b9252ad..76eb4ef 100644
--- a/src/relay.cpp
+++ b/src/relay.cpp
@@ -921,27 +921,27 @@ get_relay_int_from_relay_msg(const uint8_t *msg, int32_t len, std::unordered_map
         }
     }
 
-    const in6_addr *addr = NULL;
+    in6_addr address = in6addr_any;
     if (!isIPv6Zero(&intf_id.interface_id)) {
-        addr = &intf_id.interface_id;
+        std::memcpy(&address, &intf_id.interface_id, sizeof(in6_addr));
     } else if (isIPv6Zero(&dhcp_relay_header->link_address)) {
-        addr = &dhcp_relay_header->link_address;
+        std::memcpy(&address, &dhcp_relay_header->link_address, sizeof(in6_addr));
     }
     // multi-level relay agents
-    if (!addr) {
+    if (!isIPv6Zero(&address)) {
         return NULL;
     }
 
     char ipv6_str[INET6_ADDRSTRLEN] = {};
-    inet_ntop(AF_INET6, addr, 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",
                dhcp_relay_header->msg_type, ipv6_str);
         return NULL;
     }
-    auto vlan_name = addr_vlan_map[v6_string];
 
+    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;
@@ -991,7 +991,6 @@ void server_callback_dualtor(evutil_socket_t fd, short event, void *arg) {
         auto config = get_relay_int_from_relay_msg(message_buffer, buffer_sz, vlans);
         if (!config) {
             syslog(LOG_WARNING, "Invalid DHCPv6 header content on loopback socket, packet will be dropped\n");
-            update_counter(config->state_db, counterVlan.append(std::string(loopback)), msg->msg_type);
             continue;
         }
         update_counter(config->state_db, counterVlan.append(std::string(loopback)), msg->msg_type);

From 41827a733805d2d8cf92ef5b4b1240f2120328e7 Mon Sep 17 00:00:00 2001
From: jcaiMR <jcai@microsoft.com>
Date: Wed, 21 Jun 2023 12:24:21 +0000
Subject: [PATCH 14/19] fix issue

---
 src/relay.cpp | 6 +++---
 src/relay.h   | 1 -
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/src/relay.cpp b/src/relay.cpp
index 76eb4ef..4ae57bb 100644
--- a/src/relay.cpp
+++ b/src/relay.cpp
@@ -231,7 +231,6 @@ const struct dhcpv6_relay_msg *parse_dhcpv6_relay(const uint8_t *buffer) {
 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;
@@ -901,6 +900,7 @@ get_relay_int_from_relay_msg(const uint8_t *msg, int32_t len, std::unordered_map
     auto dhcp_relay_header = parse_dhcpv6_relay(msg);
     interface_id_option intf_id;
 
+    std::memset(&intf_id, 0, sizeof(interface_id_option));
     current_position += sizeof(struct dhcpv6_relay_msg);
     while ((current_position - msg) < len) {
         const uint8_t *tmp = NULL;
@@ -913,7 +913,7 @@ get_relay_int_from_relay_msg(const uint8_t *msg, int32_t len, std::unordered_map
             case OPTION_INTERFACE_ID: {
                 intf_id.option_code = OPTION_INTERFACE_ID;
                 intf_id.option_length = ntohs(option->option_length);
-                memcpy(&intf_id.interface_id, ((uint8_t *)option) + sizeof(struct dhcpv6_option), intf_id.option_length);
+                std::memcpy(&intf_id.interface_id, ((uint8_t *)option) + sizeof(struct dhcpv6_option), intf_id.option_length);
                 break;
             }
             default:
@@ -933,7 +933,7 @@ get_relay_int_from_relay_msg(const uint8_t *msg, int32_t len, std::unordered_map
     }
 
     char ipv6_str[INET6_ADDRSTRLEN] = {};
-    inet_ntop(AF_INET6, address, 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",
diff --git a/src/relay.h b/src/relay.h
index d2a45e2..c43917e 100644
--- a/src/relay.h
+++ b/src/relay.h
@@ -89,7 +89,6 @@ struct PACKED dhcpv6_relay_msg {
     struct in6_addr peer_address;
 };
 
-
 struct dhcpv6_option {
     uint16_t option_code;
     uint16_t option_length;

From c8b6915187e5d5f8be30be5fdea4d759d763b9dc Mon Sep 17 00:00:00 2001
From: jcaiMR <jcai@microsoft.com>
Date: Wed, 7 Jun 2023 13:56:52 +0000
Subject: [PATCH 15/19] 202012 dualtor dhcpv6 relay issue fix

---
 src/configInterface.h |   1 +
 src/main.cpp          |  17 +-
 src/relay.cpp         | 413 +++++++++++++++++++++++++++++++++---------
 src/relay.h           |  41 ++---
 test/MockRelay.cpp    |  22 ++-
 test/main.cpp         |   1 +
 6 files changed, 374 insertions(+), 121 deletions(-)

diff --git a/src/configInterface.h b/src/configInterface.h
index 94a0578..38f3ee2 100644
--- a/src/configInterface.h
+++ b/src/configInterface.h
@@ -11,6 +11,7 @@ struct swssNotification {
     std::unordered_map<std::string, relay_config> vlans;
     swss::SubscriberStateTable *ipHelpersTable;
 };
+
 /**
  * @code                void initialize_swss()
  * 
diff --git a/src/main.cpp b/src/main.cpp
index 9da3cb8..54fc75c 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -4,23 +4,32 @@
 #include "configInterface.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 <loopback interface>]\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 64390a9..7b511db 100644
--- a/src/relay.cpp
+++ b/src/relay.cpp
@@ -14,8 +14,6 @@
 
 #define BUFFER_SIZE 9200
 
-struct event *listen_event;
-struct event *server_listen_event;
 struct event_base *base;
 struct event *ev_sigint;
 struct event *ev_sigterm;
@@ -71,6 +69,9 @@ std::map<int, std::string> counterMap = {{DHCPv6_MESSAGE_TYPE_UNKNOWN, "Unknown"
 /* interface to vlan mapping */
 std::unordered_map<std::string, std::string> vlan_map;
 
+/* ipv6 address to vlan name mapping */
+std::unordered_map<std::string, std::string> addr_vlan_map;
+
 /**
  * @code                initialize_counter(std::shared_ptr<swss::DBConnector> state_db, std::string counterVlan);
  *
@@ -111,6 +112,7 @@ void initialize_counter(std::shared_ptr<swss::DBConnector> state_db, std::string
  * @return              none
  */
 void update_counter(std::shared_ptr<swss::DBConnector> state_db, std::string counterVlan, uint8_t msg_type) {
+    counters[msg_type]++;
     state_db->hset(counterVlan, counterMap.find(msg_type)->second, toString(counters[msg_type]));
 }
 
@@ -130,6 +132,19 @@ std::string toString(uint64_t count) {
     return countValue;
 }
 
+/**
+ * @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);
+}
+
 /**
  * @code                const struct ether_header *parse_ether_frame(const uint8_t *buffer, const uint8_t **out_end);
  *
@@ -216,7 +231,6 @@ const struct dhcpv6_relay_msg *parse_dhcpv6_relay(const uint8_t *buffer) {
 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;
@@ -225,7 +239,6 @@ const struct dhcpv6_option *parse_dhcpv6_opt(const uint8_t *buffer, const uint8_
 void process_sent_msg(relay_config *config, uint8_t msg_type) {
     std::string counterVlan = counter_table;
     if (counterMap.find(msg_type) != counterMap.end()) {
-        counters[msg_type]++;
         update_counter(config->state_db, counterVlan.append(config->interface), msg_type);
     } else {
         syslog(LOG_WARNING, "unexpected message type %d(0x%x)\n", msg_type, msg_type);
@@ -269,6 +282,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),
@@ -306,22 +322,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) {
@@ -358,40 +374,96 @@ void prepare_relay_config(relay_config &interface_config, int local_sock, int fi
     }
     freeifaddrs(ifa); 
     
+    char ipv6_str[INET6_ADDRSTRLEN] = {};
     if(!IN6_IS_ADDR_LINKLOCAL(&non_link_local.sin6_addr)) {
         interface_config.link_address = non_link_local;
+        inet_ntop(AF_INET6, &non_link_local.sin6_addr, ipv6_str, INET6_ADDRSTRLEN);
+        addr_vlan_map[std::string(ipv6_str)] = interface_config.interface;
     }
     else {
         interface_config.link_address = link_local;
+        inet_ntop(AF_INET6, &link_local.sin6_addr, ipv6_str, INET6_ADDRSTRLEN);
+        addr_vlan_map[std::string(ipv6_str)] = interface_config.interface;
     }
 }
 
+int prepare_lo_socket(const char *lo) {
+    struct ifaddrs *ifa, *ifa_tmp;
+    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;
+    }
+
+    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 (!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_socket(int &local_sock, int &server_sock, relay_config &config);
+ * @code            prepare_vlan_sockets(int &gua_sock, int &lla_sock, relay_config &config);
  * 
- * @brief               prepare L3 socket for sending
+ * @brief           prepare vlan l3 socket for sending
  *
- * @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 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              none
+ * @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) {
     struct ifaddrs *ifa, *ifa_tmp;
-    sockaddr_in6 addr = {0};
-    sockaddr_in6 ll_addr = {0};
+    sockaddr_in6 gua = {0}, lla = {0};
 
-    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());
+    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 ((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 ((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));
@@ -399,19 +471,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;
@@ -419,7 +492,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;
         }
 
@@ -427,13 +500,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;
 }
 
 
@@ -451,7 +533,7 @@ void prepare_socket(int &local_sock, int &server_sock, relay_config &config) {
  *
  * @return none
  */
-void relay_client(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, const ether_header *ether_hdr, relay_config *config) {    
+void relay_client(const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, const ether_header *ether_hdr, relay_config *config) {    
     static uint8_t buffer[BUFFER_SIZE];
     auto current_buffer_position = buffer;
     dhcpv6_relay_msg new_message;
@@ -496,6 +578,10 @@ void relay_client(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_h
     relay_forward(current_buffer_position, parse_dhcpv6_hdr(msg), dhcp_message_length);
     current_buffer_position += dhcp_message_length + sizeof(dhcpv6_option);
 
+    int sock = config->gua_sock;
+    if (dual_tor_sock) {
+        sock = config->lo_sock;
+    }
     for(auto server: config->servers_sock) {
         if(send_udp(sock, buffer, server, current_buffer_position - buffer)) {
             process_sent_msg(config, new_message.msg_type);
@@ -504,11 +590,10 @@ void relay_client(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_h
 }
 
 /**
- * @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
@@ -516,7 +601,7 @@ void relay_client(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_h
  *
  * @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) {
     static uint8_t buffer[BUFFER_SIZE];
     dhcpv6_relay_msg new_message;
     auto current_buffer_position = buffer;
@@ -534,10 +619,28 @@ void relay_relay_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *
     memcpy(current_buffer_position, &new_message, sizeof(dhcpv6_relay_msg));
     current_buffer_position += sizeof(dhcpv6_relay_msg);
 
+    // insert option82 for new relay-forward packet, we need this information
+    // to get original relay-forward source interface for accurate counting in dualtor env
+    if (dual_tor_sock) {
+        interface_id_option intf_id;
+        intf_id.option_code = htons(OPTION_INTERFACE_ID);
+        intf_id.option_length = htons(sizeof(in6_addr));
+        intf_id.interface_id = config->link_address.sin6_addr;
+        if ((unsigned)(current_buffer_position + sizeof(linklayer_addr_option) - buffer) > sizeof(buffer)) {
+            return;
+        }
+        memcpy(current_buffer_position, &intf_id, sizeof(interface_id_option));
+        current_buffer_position += sizeof(interface_id_option);
+    }
+
     auto dhcp_message_length = len;
     relay_forward(current_buffer_position, parse_dhcpv6_hdr(msg), dhcp_message_length);
     current_buffer_position += dhcp_message_length + sizeof(dhcpv6_option);
 
+    int sock = config->gua_sock;
+    if (dual_tor_sock) {
+        sock = config->lo_sock;
+    }
     for(auto server: config->servers_sock) {
         if(send_udp(sock, buffer, server, current_buffer_position - buffer)) {
             process_sent_msg(config, new_message.msg_type);
@@ -546,18 +649,17 @@ void relay_relay_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *
 }
 
 /**
- * @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 *config) {
+ void relay_relay_reply(const uint8_t *msg, int32_t len, relay_config *config) {
     static uint8_t buffer[BUFFER_SIZE];
     uint8_t type = 0;
     struct sockaddr_in6 target_addr;
@@ -591,6 +693,13 @@ void relay_relay_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *
     target_addr.sin6_flowinfo = 0;
     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(&dhcp_relay_header->link_address)) {
+        // In this case, it's multi-level relay
+        if (!IN6_IS_ADDR_LINKLOCAL(&dhcp_relay_header->peer_address))
+            sock = config->gua_sock;
+        target_addr.sin6_port = htons(RELAY_PORT);
+    }
 
     if(send_udp(sock, buffer, target_addr, current_buffer_position - buffer)) {
         process_sent_msg(config, type);
@@ -738,11 +847,10 @@ void client_packet_handler(uint8_t *buffer, ssize_t length, struct relay_config
     }
 
     auto option_position = current_position + sizeof(struct dhcpv6_msg);
-
     switch (msg->msg_type) {
         case DHCPv6_MESSAGE_TYPE_RELAY_FORW:
         {
-            relay_relay_forw(config->local_sock, current_position, ntohs(udp_header->len) - sizeof(udphdr), ip_header, config);
+            relay_relay_forw(current_position, ntohs(udp_header->len) - sizeof(udphdr), ip_header, config);
             break;
         }
         case DHCPv6_MESSAGE_TYPE_SOLICIT:
@@ -758,15 +866,13 @@ void client_packet_handler(uint8_t *buffer, ssize_t length, struct relay_config
                 auto option = parse_dhcpv6_opt(option_position, &tmp);
                 option_position = tmp;
                 if (ntohs(option->option_code) > DHCPv6_OPTION_LIMIT) {
-                    counters[DHCPv6_MESSAGE_TYPE_MALFORMED]++;
                     update_counter(config->state_db, counterVlan.append(config->interface), DHCPv6_MESSAGE_TYPE_MALFORMED);
                     syslog(LOG_WARNING, "DHCPv6 option is invalid or contains malformed payload from %s\n", ifname.c_str());
                     return;
                 }
             }
-            counters[msg->msg_type]++;
             update_counter(config->state_db, counterVlan.append(config->interface), msg->msg_type);
-            relay_client(config->local_sock, current_position, ntohs(udp_header->len) - sizeof(udphdr), ip_header, ether_header, config);
+            relay_client(current_position, ntohs(udp_header->len) - sizeof(udphdr), ip_header, ether_header, config);
             break;
         }
         default:
@@ -777,6 +883,121 @@ 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<std::string, relay_config> *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<std::string, relay_config> *vlans) {
+    auto current_position = msg;
+    auto dhcp_relay_header = parse_dhcpv6_relay(msg);
+    interface_id_option intf_id;
+
+    std::memset(&intf_id, 0, sizeof(interface_id_option));
+    current_position += sizeof(struct dhcpv6_relay_msg);
+    while ((current_position - msg) < len) {
+        const uint8_t *tmp = NULL;
+        auto option = parse_dhcpv6_opt(current_position, &tmp);
+        current_position = tmp;
+        if (current_position - msg > len) {
+            break;
+        }
+        switch (ntohs(option->option_code)) {
+            case OPTION_INTERFACE_ID: {
+                intf_id.option_code = OPTION_INTERFACE_ID;
+                intf_id.option_length = ntohs(option->option_length);
+                std::memcpy(&intf_id.interface_id, ((uint8_t *)option) + sizeof(struct dhcpv6_option), intf_id.option_length);
+                break;
+            }
+            default:
+                break;
+        }
+    }
+
+    in6_addr address = in6addr_any;
+    if (!isIPv6Zero(&intf_id.interface_id)) {
+        std::memcpy(&address, &intf_id.interface_id, sizeof(in6_addr));
+    } else {
+        std::memcpy(&address, &dhcp_relay_header->link_address, sizeof(in6_addr));
+    }
+    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",
+               dhcp_relay_header->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<std::unordered_map<std::string, struct relay_config> *>(arg);
+    sockaddr_in6 from;
+    socklen_t len = sizeof(from);
+    int32_t pkts_num = 0;
+    static uint8_t message_buffer[BUFFER_SIZE];
+
+    while (pkts_num++ < BATCH_SIZE) {
+        std::string counterVlan = counter_table;
+        auto buffer_sz = recvfrom(fd, message_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 %ld, no space for dhcpv6 msg header\n", buffer_sz);
+            continue;
+        }
+
+        auto msg = parse_dhcpv6_hdr(message_buffer);
+
+        if (msg->msg_type != DHCPv6_MESSAGE_TYPE_RELAY_REPL) {
+            syslog(LOG_WARNING, "Invalid DHCPv6 message type %d received on loopback interface\n", msg->msg_type);
+            continue;
+        }
+        auto config = get_relay_int_from_relay_msg(message_buffer, buffer_sz, vlans);
+        if (!config) {
+            syslog(LOG_WARNING, "Invalid DHCPv6 header content on loopback socket, packet will be dropped\n");
+            continue;
+        }
+        update_counter(config->state_db, counterVlan.append(std::string(loopback)), msg->msg_type);
+
+        relay_relay_reply(message_buffer, buffer_sz, config);
+    }
+}
+
 
 /**
  * @code                void server_callback(evutil_socket_t fd, short event, void *arg);
@@ -795,10 +1016,10 @@ void server_callback(evutil_socket_t fd, short event, void *arg) {
     socklen_t len = sizeof(from);
     int32_t pkts_num = 0;
     static uint8_t message_buffer[BUFFER_SIZE];
-    std::string counterVlan = counter_table;
 
     while (pkts_num++ < BATCH_SIZE) {
-        auto buffer_sz = recvfrom(config->local_sock, message_buffer, BUFFER_SIZE, 0, (sockaddr *)&from, &len);
+        std::string counterVlan = counter_table;
+        auto buffer_sz = recvfrom(config->gua_sock, message_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));
@@ -817,13 +1038,11 @@ void server_callback(evutil_socket_t fd, short event, void *arg) {
             update_counter(config->state_db, counterVlan.append(config->interface), DHCPv6_MESSAGE_TYPE_UNKNOWN);
             syslog(LOG_WARNING, "Unknown DHCPv6 message type %d\n", msg->msg_type);
             continue;
-        } else {
-            counters[msg->msg_type]++;
         }
 
         update_counter(config->state_db, counterVlan.append(config->interface), msg->msg_type);
         if (msg->msg_type == DHCPv6_MESSAGE_TYPE_RELAY_REPL) {
-            relay_relay_reply(config->server_sock, message_buffer, buffer_sz, config);
+            relay_relay_reply(message_buffer, buffer_sz, config);
         }
     }
 }
@@ -922,7 +1141,7 @@ void loop_relay(std::unordered_map<std::string, relay_config> &vlans) {
     std::vector<int> 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");
     }
 
     std::shared_ptr<swss::DBConnector> state_db = std::make_shared<swss::DBConnector> ("STATE_DB", 0);
@@ -934,21 +1153,41 @@ void loop_relay(std::unordered_map<std::string, relay_config> &vlans) {
     auto filter = sock_open(&ether_relay_fprog);
     if (filter != -1) {
         sockets.push_back(filter);
-        listen_event = event_new(base, filter, EV_READ|EV_PERSIST, client_callback,
-                                 reinterpret_cast<void *>(&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<void *>(&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<void *>(&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;
@@ -958,28 +1197,28 @@ void loop_relay(std::unordered_map<std::string, relay_config> &vlans) {
 
         std::string counterVlan = counter_table;
         initialize_counter(vlan.second.state_db, counterVlan.append(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);
+        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);
     
-	    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 (!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 240db54..c43917e 100644
--- a/src/relay.h
+++ b/src/relay.h
@@ -34,6 +34,7 @@
 #define BATCH_SIZE 64
 
 extern bool dual_tor_sock;
+extern char loopback[IF_NAMESIZE];
 
 /* DHCPv6 message types */
 typedef enum
@@ -58,8 +59,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<swss::DBConnector> state_db;
@@ -87,7 +89,6 @@ struct PACKED dhcpv6_relay_msg {
     struct in6_addr peer_address;
 };
 
-
 struct dhcpv6_option {
     uint16_t option_code;
     uint16_t option_length;
@@ -116,30 +117,31 @@ struct interface_id_option  {
  */
 int sock_open(const struct sock_fprog *fprog);
 
+
 /**
- * @code                prepare_socket(int *local_sock, int *server_sock, relay_config *config);
+ * @code            prepare_vlan_sockets(int &gua_sock, int &lla_sock, relay_config &config);
  * 
- * @brief               prepare L3 socket for sending
+ * @brief           prepare vlan l3 socket for sending
  *
- * @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 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              none
+ * @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_forward(uint8_t *buffer, const struct dhcpv6_msg *msg, uint16_t msg_length);
@@ -155,11 +157,10 @@ void prepare_relay_config(relay_config &interface_config, int local_sock, int fi
 void relay_forward(uint8_t *buffer, const struct dhcpv6_msg *msg, uint16_t msg_length);
 
 /**
- * @code                 relay_client(int sock, const uint8_t *msg, int32_t len, ip6_hdr *ip_hdr, const ether_header *ether_hdr, relay_config *config);
+ * @code                 relay_client(const uint8_t *msg, int32_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
@@ -168,14 +169,13 @@ void relay_forward(uint8_t *buffer, const struct dhcpv6_msg *msg, uint16_t msg_l
  *
  * @return none
  */
-void relay_client(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, const ether_header *ether_hdr, relay_config *config);
+void relay_client(const uint8_t *msg, int32_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
@@ -183,21 +183,20 @@ void relay_client(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_h
  *
  * @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                loop_relay(std::unordered_map<std::string, relay_config> &vlans);
diff --git a/test/MockRelay.cpp b/test/MockRelay.cpp
index 0d14de7..dece937 100644
--- a/test/MockRelay.cpp
+++ b/test/MockRelay.cpp
@@ -11,6 +11,7 @@
 #include "MockRelay.h"
 
 bool dual_tor_sock = false;
+char loopback[IF_NAMESIZE] = "Loopback0";
 extern struct event_base *base;
 extern struct event *ev_sigint;
 extern struct event *ev_sigterm;
@@ -316,8 +317,6 @@ TEST(counter, update_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,
@@ -345,6 +344,9 @@ TEST(relay, relay_client)
   }
   std::shared_ptr<swss::DBConnector> state_db = std::make_shared<swss::DBConnector> ("STATE_DB", 0);
   config.state_db = state_db;
+  config.gua_sock = 125;
+  config.lla_sock = 125;
+  config.lo_sock = 125;
 
   struct ether_header ether_hdr;
   ether_hdr.ether_shost[0] = 0x5a;
@@ -357,7 +359,7 @@ TEST(relay, relay_client)
   ip6_hdr ip_hdr;
   std::string s_addr = "2000::3";
 
-  relay_client(mock_sock, msg, msg_len, &ip_hdr, &ether_hdr, &config);
+  relay_client(msg, msg_len, &ip_hdr, &ether_hdr, &config);
 
   EXPECT_EQ(last_used_sock, 124);
 
@@ -394,8 +396,6 @@ TEST(relay, relay_client)
 }
 
 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,
@@ -428,12 +428,15 @@ TEST(relay, relay_relay_forw) {
   }
   std::shared_ptr<swss::DBConnector> state_db = std::make_shared<swss::DBConnector> ("STATE_DB", 0);
   config.state_db = state_db;
+  config.gua_sock = 125;
+  config.lla_sock = 125;
+  config.lo_sock = 125;
 
   ip6_hdr ip_hdr;
   std::string s_addr = "2000::3";
   inet_pton(AF_INET6, s_addr.c_str(), &ip_hdr.ip6_src);
 
-  relay_relay_forw(mock_sock, msg, msg_len, &ip_hdr, &config);
+  relay_relay_forw(msg, msg_len, &ip_hdr, &config);
 
   EXPECT_EQ(last_used_sock, 125);
 
@@ -453,8 +456,6 @@ TEST(relay, relay_relay_forw) {
 
 TEST(relay, relay_relay_reply) 
 {
-  int mock_sock = 123;
-
   uint8_t msg[] = { 
       0x0d, 0x00, 0x20, 0x01, 0x0d, 0xb8, 0x01, 0x5a,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -491,13 +492,16 @@ TEST(relay, relay_relay_reply)
   config.interface = "Vlan1000";
   std::shared_ptr<swss::DBConnector> state_db = std::make_shared<swss::DBConnector> ("STATE_DB", 0);
   config.state_db = state_db;
+  config.gua_sock = 125;
+  config.lla_sock = 125;
+  config.lo_sock = 125;
 
   int local_sock = 1;
   int filter = 1;
 
   prepare_relay_config(config, local_sock, filter);
 
-  relay_relay_reply(mock_sock, msg, msg_len, &config);
+  relay_relay_reply(msg, msg_len, &config);
 
   EXPECT_EQ(last_used_sock, 123);
 
diff --git a/test/main.cpp b/test/main.cpp
index dfca267..6b3b209 100644
--- a/test/main.cpp
+++ b/test/main.cpp
@@ -1,4 +1,5 @@
 #include "gtest/gtest.h"
+#include <net/if.h>
 #include <swss/dbconnector.h>
 #include <string>
 

From 9e7eaacc4c4749d2d84e075b05e078c06e415996 Mon Sep 17 00:00:00 2001
From: jcaiMR <jcai@microsoft.com>
Date: Thu, 22 Jun 2023 03:04:06 +0000
Subject: [PATCH 16/19] cherry pick 5b3eea1

---
 .azure-pipelines/build.yml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/.azure-pipelines/build.yml b/.azure-pipelines/build.yml
index 1702ef8..758e8a6 100644
--- a/.azure-pipelines/build.yml
+++ b/.azure-pipelines/build.yml
@@ -30,6 +30,8 @@ jobs:
         sudo apt-get install -y dotnet-sdk-6.0
       displayName: install .Net
   - script: |
+      set -ex
+      sudo apt-get update
       sudo apt-get install -y \
           libboost-system1.71-dev \
           libboost-thread1.71-dev \

From 65615ea93fbed464a2a040c2583dd115de79a8d4 Mon Sep 17 00:00:00 2001
From: jcaiMR <jcai@microsoft.com>
Date: Thu, 22 Jun 2023 13:11:05 +0000
Subject: [PATCH 17/19] fix format issu

---
 src/relay.cpp | 3 +--
 src/relay.h   | 8 ++++----
 2 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/src/relay.cpp b/src/relay.cpp
index 7b511db..f36884b 100644
--- a/src/relay.cpp
+++ b/src/relay.cpp
@@ -230,8 +230,7 @@ const struct dhcpv6_relay_msg *parse_dhcpv6_relay(const uint8_t *buffer) {
  */
 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
-    (*out_end) =  buffer + size + ntohs(option->option_length);
+    (*out_end) =  buffer + sizeof(struct dhcpv6_option) + ntohs(option->option_length);
 
     return option;
 }
diff --git a/src/relay.h b/src/relay.h
index c43917e..6ee7f5e 100644
--- a/src/relay.h
+++ b/src/relay.h
@@ -77,7 +77,7 @@ struct relay_config {
 
 /* DHCPv6 messages and options */
 
-struct dhcpv6_msg {
+struct PACKED dhcpv6_msg {
     uint8_t msg_type;
     uint8_t xid[3];
 };
@@ -89,18 +89,18 @@ struct PACKED dhcpv6_relay_msg {
     struct in6_addr peer_address;
 };
 
-struct dhcpv6_option {
+struct PACKED dhcpv6_option {
     uint16_t option_code;
     uint16_t option_length;
 };
 
-struct linklayer_addr_option  {
+struct PACKED linklayer_addr_option  {
     uint16_t option_code;
     uint16_t option_length;
     uint16_t link_layer_type;
 };
 
-struct interface_id_option  {
+struct PACKED interface_id_option  {
     uint16_t option_code;
     uint16_t option_length;
     in6_addr interface_id;  // to accomodate dual-tor, this opaque value is set to carry relay interface's global ipv6 address

From 899f03eca60f8fc1f18fbbfc38e8c2ba1f4088aa Mon Sep 17 00:00:00 2001
From: jcaiMR <jcai@microsoft.com>
Date: Fri, 7 Jul 2023 13:33:02 +0000
Subject: [PATCH 18/19] fix comments

---
 src/configInterface.cpp | 12 ++++++++--
 src/relay.cpp           | 18 +++++++--------
 src/relay.h             | 14 ++++++++++++
 test/MockRelay.cpp      | 49 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 81 insertions(+), 12 deletions(-)

diff --git a/src/configInterface.cpp b/src/configInterface.cpp
index d0801af..6ea9cc3 100644
--- a/src/configInterface.cpp
+++ b/src/configInterface.cpp
@@ -9,6 +9,8 @@ bool pollSwssNotifcation = true;
 std::shared_ptr<boost::thread> mSwssThreadPtr;
 swss::Select swssSelect;
 
+extern bool dual_tor_sock;
+
 /**
  * @code                void initialize_swss()
  * 
@@ -116,6 +118,12 @@ void handleRelayNotification(swss::SubscriberStateTable &ipHelpersTable, std::un
 void processRelayNotification(std::deque<swss::KeyOpFieldsValuesTuple> &entries, std::unordered_map<std::string, relay_config> &vlans)
 {
     std::vector<std::string> 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);
@@ -123,8 +131,8 @@ void processRelayNotification(std::deque<swss::KeyOpFieldsValuesTuple> &entries,
         std::vector<swss::FieldValueTuple> 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/relay.cpp b/src/relay.cpp
index 4ae57bb..cf2ae0c 100644
--- a/src/relay.cpp
+++ b/src/relay.cpp
@@ -377,14 +377,11 @@ void prepare_relay_config(relay_config &interface_config, int gua_sock, int filt
     char ipv6_str[INET6_ADDRSTRLEN] = {};
     if(!IN6_IS_ADDR_LINKLOCAL(&non_link_local.sin6_addr)) {
         interface_config.link_address = non_link_local;
-        inet_ntop(AF_INET6, &non_link_local.sin6_addr, ipv6_str, INET6_ADDRSTRLEN);
-        addr_vlan_map[std::string(ipv6_str)] = interface_config.interface;
-    }
-    else {
+    } else {
         interface_config.link_address = link_local;
-        inet_ntop(AF_INET6, &link_local.sin6_addr, ipv6_str, INET6_ADDRSTRLEN);
-        addr_vlan_map[std::string(ipv6_str)] = interface_config.interface;
     }
+    inet_ntop(AF_INET6, &interface_config.link_address.sin6_addr, ipv6_str, INET6_ADDRSTRLEN);
+    addr_vlan_map[std::string(ipv6_str)] = interface_config.interface;
 }
 
 int prepare_lo_socket(const char *lo) {
@@ -620,8 +617,9 @@ void relay_relay_forw(const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, re
     current_buffer_position += sizeof(dhcpv6_relay_msg);
 
     // insert option82 for new relay-forward packet, we need this information
-    // to get original relay-forward source interface for accurate counting in dualtor env
-    if (dual_tor_sock) {
+    // 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) {
         interface_id_option intf_id;
         intf_id.option_code = htons(OPTION_INTERFACE_ID);
         intf_id.option_length = htons(sizeof(in6_addr));
@@ -924,11 +922,11 @@ get_relay_int_from_relay_msg(const uint8_t *msg, int32_t len, std::unordered_map
     in6_addr address = in6addr_any;
     if (!isIPv6Zero(&intf_id.interface_id)) {
         std::memcpy(&address, &intf_id.interface_id, sizeof(in6_addr));
-    } else if (isIPv6Zero(&dhcp_relay_header->link_address)) {
+    } else {
         std::memcpy(&address, &dhcp_relay_header->link_address, sizeof(in6_addr));
     }
     // multi-level relay agents
-    if (!isIPv6Zero(&address)) {
+    if (isIPv6Zero(&address)) {
         return NULL;
     }
 
diff --git a/src/relay.h b/src/relay.h
index c43917e..a45dd31 100644
--- a/src/relay.h
+++ b/src/relay.h
@@ -423,3 +423,17 @@ void server_callback(evutil_socket_t fd, short event, void *arg);
  * @return              none
  */
 void server_callback_dual_tor(evutil_socket_t fd, short event, void *arg);
+
+/**
+ * @code                struct relay_config *
+ *                      get_relay_int_from_relay_msg(const uint8_t *msg, int32_t len,
+ *                                                   std::unordered_map<std::string, relay_config> *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<std::string, relay_config> *vlans);
diff --git a/test/MockRelay.cpp b/test/MockRelay.cpp
index dece937..2da9b3c 100644
--- a/test/MockRelay.cpp
+++ b/test/MockRelay.cpp
@@ -15,6 +15,7 @@ char loopback[IF_NAMESIZE] = "Loopback0";
 extern struct event_base *base;
 extern struct event *ev_sigint;
 extern struct event *ev_sigterm;
+extern std::unordered_map<std::string, std::string> addr_vlan_map;
 
 static struct sock_filter ether_relay_filter[] = {
 
@@ -563,3 +564,51 @@ TEST(relay, dhcp6relay_stop) {
   event_base_free(base);
   base = NULL;
 }
+
+TEST(relay, get_relay_int_from_relay_msg) {
+  struct relay_config config{};
+  std::string lla_str = "fc02:1000::1";
+  std::unordered_map<std::string, relay_config> vlans;
+
+  config.is_option_79 = true;
+  config.is_interface_id = true;
+  inet_pton(AF_INET6, lla_str.c_str(), &config.link_address.sin6_addr);
+  config.servers.push_back("fc02:2000::1");
+  config.servers.push_back("fc02:2000::2");
+  config.interface = "Vlan1000";
+  config.state_db = std::make_shared<swss::DBConnector> ("STATE_DB", 0);
+
+  vlans["Vlan1000"] = config;
+  addr_vlan_map[lla_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,0x0c,0x66,0x63,
+      0x30,0x32,0x3a,0x31,0x30,0x30,0x30,0x3a,0x3a,0x31
+  };
+
+  auto cfg = get_relay_int_from_relay_msg(relay_reply_with_opt18, sizeof(relay_reply_with_opt18), &vlans);
+  EXPECT_NE((uintptr_t)cfg, NULL);
+  EXPECT_EQ(cfg->interface, "Vlan1000");
+
+  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
+  };
+
+  cfg = get_relay_int_from_relay_msg(relay_reply_without_opt18, sizeof(relay_reply_without_opt18), &vlans);
+  EXPECT_NE((uintptr_t)cfg, NULL);
+  EXPECT_EQ(cfg->interface, "Vlan1000");
+
+  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
+  };
+
+  cfg = get_relay_int_from_relay_msg(relay_reply_without_opt18_linkaddr_zero,
+                                     sizeof(relay_reply_without_opt18_linkaddr_zero), &vlans);
+  EXPECT_EQ((uintptr_t)cfg, NULL);
+}
\ No newline at end of file

From ffc6932d8812e90b428f209e37223df492432443 Mon Sep 17 00:00:00 2001
From: jcaiMR <jcai@microsoft.com>
Date: Sat, 8 Jul 2023 15:40:18 +0000
Subject: [PATCH 19/19] fix test code issue

---
 test/MockRelay.cpp | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/test/MockRelay.cpp b/test/MockRelay.cpp
index 2da9b3c..cccf829 100644
--- a/test/MockRelay.cpp
+++ b/test/MockRelay.cpp
@@ -313,7 +313,7 @@ TEST(counter, update_counter)
   update_counter(state_db, "DHCPv6_COUNTER_TABLE|Vlan1000", 1);
   std::shared_ptr<std::string> output = state_db->hget("DHCPv6_COUNTER_TABLE|Vlan1000", "Solicit");
   std::string *ptr = output.get();
-  EXPECT_EQ(*ptr, "0");
+  EXPECT_EQ(*ptr, "1");
 }
 
 TEST(relay, relay_client) 
@@ -362,7 +362,7 @@ TEST(relay, relay_client)
 
   relay_client(msg, msg_len, &ip_hdr, &ether_hdr, &config);
 
-  EXPECT_EQ(last_used_sock, 124);
+  EXPECT_EQ(last_used_sock, 125);
 
   auto sent_msg = parse_dhcpv6_relay(sender_buffer);
 
@@ -504,7 +504,7 @@ TEST(relay, relay_relay_reply)
 
   relay_relay_reply(msg, msg_len, &config);
 
-  EXPECT_EQ(last_used_sock, 123);
+  EXPECT_EQ(last_used_sock, 125);
 
   uint8_t expected_bytes[] = {
       0x07, 0x4f, 0x6d, 0x04, 0x00, 0x03, 0x00, 0x28,
@@ -576,7 +576,6 @@ TEST(relay, get_relay_int_from_relay_msg) {
   config.servers.push_back("fc02:2000::1");
   config.servers.push_back("fc02:2000::2");
   config.interface = "Vlan1000";
-  config.state_db = std::make_shared<swss::DBConnector> ("STATE_DB", 0);
 
   vlans["Vlan1000"] = config;
   addr_vlan_map[lla_str] = "Vlan1000";
@@ -584,8 +583,8 @@ TEST(relay, get_relay_int_from_relay_msg) {
   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,0x0c,0x66,0x63,
-      0x30,0x32,0x3a,0x31,0x30,0x30,0x30,0x3a,0x3a,0x31
+      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
   };
 
   auto cfg = get_relay_int_from_relay_msg(relay_reply_with_opt18, sizeof(relay_reply_with_opt18), &vlans);