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:
parent
5826a5c4e7
commit
a86320a773
@ -116,34 +116,6 @@ std::string toString(uint16_t count) {
|
||||
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);
|
||||
*
|
||||
@ -356,11 +328,11 @@ void prepare_relay_config(relay_config *interface_config, int *local_sock, int f
|
||||
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, 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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -368,7 +340,7 @@ void prepare_relay_config(relay_config *interface_config, int *local_sock, int f
|
||||
}
|
||||
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;
|
||||
}
|
||||
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
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
void prepare_socket(int *local_sock) {
|
||||
int flag = 1;
|
||||
void prepare_socket(int *local_sock, int *server_sock, relay_config *config, int index) {
|
||||
struct ifaddrs *ifa, *ifa_tmp;
|
||||
sockaddr_in6 addr;
|
||||
sockaddr_in6 ll_addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin6_family = AF_INET6;
|
||||
addr.sin6_addr = in6addr_any;
|
||||
addr.sin6_port = htons(RELAY_PORT);
|
||||
memset(&ll_addr, 0, sizeof(ll_addr));
|
||||
|
||||
if ((*local_sock = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
|
||||
syslog(LOG_ERR, "socket: Failed to create socket\n");
|
||||
}
|
||||
|
||||
if((setsockopt(*local_sock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag))) == -1) {
|
||||
syslog(LOG_ERR, "setsockopt: Unable to set socket option\n");
|
||||
}
|
||||
if ((*server_sock= socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
|
||||
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) {
|
||||
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;
|
||||
update_counter(config->db, counterVlan.append(config->interface), msg->msg_type);
|
||||
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) {
|
||||
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) {
|
||||
relay_config *config = &vlan;
|
||||
int filter = 0;
|
||||
int local_sock = 0;
|
||||
int server_sock = 0;
|
||||
const char *ifname = config->interface.c_str();
|
||||
int index = if_nametoindex(ifname);
|
||||
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));
|
||||
|
||||
filter = sock_open(index, ðer_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(local_sock);
|
||||
sockets.push_back(server_sock);
|
||||
|
||||
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_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);
|
||||
server_listen_event = event_new(base, local_sock, EV_READ|EV_PERSIST, server_callback, config);
|
||||
if (listen_event == NULL || server_listen_event == NULL) {
|
||||
|
@ -42,6 +42,7 @@ typedef enum
|
||||
|
||||
struct relay_config {
|
||||
int local_sock;
|
||||
int server_sock;
|
||||
int filter;
|
||||
sockaddr_in6 link_address;
|
||||
swss::DBConnector *db;
|
||||
@ -90,15 +91,17 @@ struct linklayer_addr_option {
|
||||
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
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
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);
|
||||
@ -245,28 +248,6 @@ void update_counter(swss::DBConnector *db, std::string counterVlan, uint8_t msg_
|
||||
*/
|
||||
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);
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user