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;
|
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, ðer_relay_fprog);
|
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(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) {
|
||||||
|
@ -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);
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user