DHCPv6 Relay multivlan functionality support (#9178)

Fix support for DHCPV6 Relay multi vlan functionality. Make sure the relayed packet is received at correct interface.

How I did it
Bind a socket to each vlan interface's global and link-local address.
Socket binded to global address is used for relaying data from client to server and receiving data from servers.
Socket binded to link-local address is used for relaying data received from server back to the client.
This commit is contained in:
kellyyeh 2021-11-11 17:49:04 -08:00 committed by GitHub
parent 5826a5c4e7
commit a86320a773
No account linked to committer's email address
2 changed files with 62 additions and 75 deletions

View File

@ -116,34 +116,6 @@ std::string toString(uint16_t count) {
return countValue; return countValue;
} }
/**
* @code bool is_addr_gua(in6_addr addr);
*
* @brief check if address is global
*
* @param addr ipv6 address
*
* @return bool
*/
bool is_addr_gua(in6_addr addr) {
auto masked = addr.__in6_u.__u6_addr8[0] & 0xe0;
return (masked ^ 0x20) == 0x00;
}
/**
* @code is_addr_link_local(in6_addr addr);
*
* @brief check if address is link_local
*
* @param addr ipv6 address
*
* @return bool
*/
bool is_addr_link_local(in6_addr addr) {
auto masked = ntohs(addr.__in6_u.__u6_addr16[0]) & 0xffc0;
return (masked ^ 0xfe80) == 0x0000;
}
/** /**
* @code const struct ether_header *parse_ether_frame(const uint8_t *buffer, const uint8_t **out_end); * @code const struct ether_header *parse_ether_frame(const uint8_t *buffer, const uint8_t **out_end);
* *
@ -356,11 +328,11 @@ void prepare_relay_config(relay_config *interface_config, int *local_sock, int f
while (ifa_tmp) { while (ifa_tmp) {
if (ifa_tmp->ifa_addr->sa_family == AF_INET6) { if (ifa_tmp->ifa_addr->sa_family == AF_INET6) {
struct sockaddr_in6 *in6 = (struct sockaddr_in6*) ifa_tmp->ifa_addr; struct sockaddr_in6 *in6 = (struct sockaddr_in6*) ifa_tmp->ifa_addr;
if((strcmp(ifa_tmp->ifa_name, interface_config->interface.c_str()) == 0) && is_addr_gua(in6->sin6_addr)) { if((strcmp(ifa_tmp->ifa_name, interface_config->interface.c_str()) == 0) && !IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) {
non_link_local = *in6; non_link_local = *in6;
break; break;
} }
if((strcmp(ifa_tmp->ifa_name, interface_config->interface.c_str()) == 0) && is_addr_link_local(in6->sin6_addr)) { if((strcmp(ifa_tmp->ifa_name, interface_config->interface.c_str()) == 0) && IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) {
link_local = *in6; link_local = *in6;
} }
} }
@ -368,7 +340,7 @@ void prepare_relay_config(relay_config *interface_config, int *local_sock, int f
} }
freeifaddrs(ifa); freeifaddrs(ifa);
if(is_addr_gua(non_link_local.sin6_addr)) { if(!IN6_IS_ADDR_LINKLOCAL(&non_link_local.sin6_addr)) {
interface_config->link_address = non_link_local; interface_config->link_address = non_link_local;
} }
else { else {
@ -377,33 +349,63 @@ void prepare_relay_config(relay_config *interface_config, int *local_sock, int f
} }
/** /**
* @code prepare_socket(int *local_sock); * @code prepare_socket(int *local_sock, int *server_sock, relay_config *config, int index);
* *
* @brief prepare L3 socket for sending * @brief prepare L3 socket for sending
* *
* @param local_sock pointer to socket to be prepared * @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 index scope id of interface
* *
* @return none * @return none
*/ */
void prepare_socket(int *local_sock) { void prepare_socket(int *local_sock, int *server_sock, relay_config *config, int index) {
int flag = 1; struct ifaddrs *ifa, *ifa_tmp;
sockaddr_in6 addr; sockaddr_in6 addr;
sockaddr_in6 ll_addr;
memset(&addr, 0, sizeof(addr)); memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6; memset(&ll_addr, 0, sizeof(ll_addr));
addr.sin6_addr = in6addr_any;
addr.sin6_port = htons(RELAY_PORT);
if ((*local_sock = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) { if ((*local_sock = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
syslog(LOG_ERR, "socket: Failed to create socket\n"); syslog(LOG_ERR, "socket: Failed to create socket\n");
} }
if((setsockopt(*local_sock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag))) == -1) { if ((*server_sock= socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
syslog(LOG_ERR, "setsockopt: Unable to set socket option\n"); syslog(LOG_ERR, "socket: Failed to create socket\n");
} }
if (getifaddrs(&ifa) == -1) {
syslog(LOG_WARNING, "getifaddrs: Unable to get network interfaces\n");
exit(1);
}
ifa_tmp = ifa;
while (ifa_tmp) {
if (ifa_tmp->ifa_addr->sa_family == AF_INET6) {
struct sockaddr_in6 *in6 = (struct sockaddr_in6*) ifa_tmp->ifa_addr;
if((strcmp(ifa_tmp->ifa_name, config->interface.c_str()) == 0) && !IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) {
in6->sin6_family = AF_INET6;
in6->sin6_port = htons(RELAY_PORT);
addr = *in6;
}
if((strcmp(ifa_tmp->ifa_name, config->interface.c_str()) == 0) && IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) {
in6->sin6_family = AF_INET6;
in6->sin6_port = htons(RELAY_PORT);
ll_addr = *in6;
}
}
ifa_tmp = ifa_tmp->ifa_next;
}
freeifaddrs(ifa);
if (bind(*local_sock, (sockaddr *)&addr, sizeof(addr)) == -1) { if (bind(*local_sock, (sockaddr *)&addr, sizeof(addr)) == -1) {
syslog(LOG_ERR, "bind: Failed to bind to socket\n"); syslog(LOG_ERR, "bind: Failed to bind to socket\n");
} }
if (bind(*server_sock, (sockaddr *)&ll_addr, sizeof(addr)) == -1) {
syslog(LOG_ERR, "bind: Failed to bind to socket\n");
}
} }
@ -584,7 +586,7 @@ void server_callback(evutil_socket_t fd, short event, void *arg) {
std::string counterVlan = counter_table; std::string counterVlan = counter_table;
update_counter(config->db, counterVlan.append(config->interface), msg->msg_type); update_counter(config->db, counterVlan.append(config->interface), msg->msg_type);
if (msg->msg_type == DHCPv6_MESSAGE_TYPE_RELAY_REPL) { if (msg->msg_type == DHCPv6_MESSAGE_TYPE_RELAY_REPL) {
relay_relay_reply(config->local_sock, message_buffer, data, config); relay_relay_reply(config->server_sock, message_buffer, data, config);
} }
} }
@ -681,11 +683,16 @@ void dhcp6relay_stop()
*/ */
void loop_relay(std::vector<relay_config> *vlans, swss::DBConnector *db) { void loop_relay(std::vector<relay_config> *vlans, swss::DBConnector *db) {
std::vector<int> sockets; std::vector<int> sockets;
base = event_base_new();
if(base == NULL) {
syslog(LOG_ERR, "libevent: Failed to create base\n");
}
for(relay_config &vlan : *vlans) { for(relay_config &vlan : *vlans) {
relay_config *config = &vlan; relay_config *config = &vlan;
int filter = 0; int filter = 0;
int local_sock = 0; int local_sock = 0;
int server_sock = 0;
const char *ifname = config->interface.c_str(); const char *ifname = config->interface.c_str();
int index = if_nametoindex(ifname); int index = if_nametoindex(ifname);
config->db = db; config->db = db;
@ -694,10 +701,14 @@ void loop_relay(std::vector<relay_config> *vlans, swss::DBConnector *db) {
initialize_counter(config->db, counterVlan.append(config->interface)); initialize_counter(config->db, counterVlan.append(config->interface));
filter = sock_open(index, &ether_relay_fprog); filter = sock_open(index, &ether_relay_fprog);
prepare_socket(&local_sock, &server_sock, config, index);
config->local_sock = local_sock;
config->server_sock = server_sock;
prepare_socket(&local_sock);
sockets.push_back(filter); sockets.push_back(filter);
sockets.push_back(local_sock); sockets.push_back(local_sock);
sockets.push_back(server_sock);
prepare_relay_config(config, &local_sock, filter); prepare_relay_config(config, &local_sock, filter);
@ -707,11 +718,6 @@ void loop_relay(std::vector<relay_config> *vlans, swss::DBConnector *db) {
evutil_make_listen_socket_reuseable(local_sock); evutil_make_listen_socket_reuseable(local_sock);
evutil_make_socket_nonblocking(local_sock); evutil_make_socket_nonblocking(local_sock);
base = event_base_new();
if(base == NULL) {
syslog(LOG_ERR, "libevent: Failed to create base\n");
}
listen_event = event_new(base, filter, EV_READ|EV_PERSIST, callback, config); listen_event = event_new(base, filter, EV_READ|EV_PERSIST, callback, config);
server_listen_event = event_new(base, local_sock, EV_READ|EV_PERSIST, server_callback, config); server_listen_event = event_new(base, local_sock, EV_READ|EV_PERSIST, server_callback, config);
if (listen_event == NULL || server_listen_event == NULL) { if (listen_event == NULL || server_listen_event == NULL) {

View File

@ -42,6 +42,7 @@ typedef enum
struct relay_config { struct relay_config {
int local_sock; int local_sock;
int server_sock;
int filter; int filter;
sockaddr_in6 link_address; sockaddr_in6 link_address;
swss::DBConnector *db; swss::DBConnector *db;
@ -90,15 +91,17 @@ struct linklayer_addr_option {
int sock_open(int ifindex, const struct sock_fprog *fprog); int sock_open(int ifindex, const struct sock_fprog *fprog);
/** /**
* @code prepare_socket(int *local_sock); * @code prepare_socket(int *local_sock, int *server_sock, relay_config *config, int index);
* *
* @brief prepare L3 socket for sending * @brief prepare L3 socket for sending
* *
* @param local_sock pointer to socket to be prepared * @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 index scope id of interface
* *
* @return none * @return none
*/ */
void prepare_socket(int *local_sock); void prepare_socket(int *local_sock, int *server_sock, relay_config *config, int index);
/** /**
* @code prepare_relay_config(relay_config *interface_config, int local_sock, int filter); * @code prepare_relay_config(relay_config *interface_config, int local_sock, int filter);
@ -245,28 +248,6 @@ void update_counter(swss::DBConnector *db, std::string counterVlan, uint8_t msg_
*/ */
std::string toString(uint16_t count); std::string toString(uint16_t count);
/**
* @code bool is_addr_gua(in6_addr addr);
*
* @brief check if address is global
*
* @param addr ipv6 address
*
* @return bool
*/
bool is_addr_gua(in6_addr addr);
/**
* @code is_addr_link_local(in6_addr addr);
*
* @brief check if address is link_local
*
* @param addr ipv6 address
*
* @return bool
*/
bool is_addr_link_local(in6_addr addr);
/** /**
* @code const struct ether_header *parse_ether_frame(const uint8_t *buffer, const uint8_t **out_end); * @code const struct ether_header *parse_ether_frame(const uint8_t *buffer, const uint8_t **out_end);
* *