-
Notifications
You must be signed in to change notification settings - Fork 694
[fpmsyncd] Add VNET routes support #772
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
ce49686
45f273e
cdb3d96
149b954
d865382
02bf798
c591617
8d228c5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,12 +9,17 @@ | |
| #include "producerstatetable.h" | ||
| #include "fpmsyncd/fpmlink.h" | ||
| #include "fpmsyncd/routesync.h" | ||
| #include <string.h> | ||
|
|
||
| using namespace std; | ||
| using namespace swss; | ||
|
|
||
| #define VXLAN_IF_NAME_PREFIX "brvxlan" | ||
|
|
||
| RouteSync::RouteSync(RedisPipeline *pipeline) : | ||
| m_routeTable(pipeline, APP_ROUTE_TABLE_NAME, true), | ||
| m_routeTable(pipeline, APP_ROUTE_TABLE_NAME, true), | ||
| m_vnet_routeTable(pipeline, APP_VNET_RT_TABLE_NAME, true), | ||
| m_vnet_tunnelTable(pipeline, APP_VNET_RT_TUNNEL_TABLE_NAME, true), | ||
| m_warmStartHelper(pipeline, &m_routeTable, APP_ROUTE_TABLE_NAME, "bgp", "bgp") | ||
| { | ||
| m_nl_sock = nl_socket_alloc(); | ||
|
|
@@ -25,20 +30,41 @@ RouteSync::RouteSync(RedisPipeline *pipeline) : | |
| void RouteSync::onMsg(int nlmsg_type, struct nl_object *obj) | ||
| { | ||
| struct rtnl_route *route_obj = (struct rtnl_route *)obj; | ||
| struct nl_addr *dip; | ||
| char destipprefix[MAX_ADDR_SIZE + 1] = {0}; | ||
|
|
||
| dip = rtnl_route_get_dst(route_obj); | ||
| nl_addr2str(dip, destipprefix, MAX_ADDR_SIZE); | ||
| SWSS_LOG_DEBUG("Receive new route message dest ip prefix: %s\n", destipprefix); | ||
|
|
||
| /* Supports IPv4 or IPv6 address, otherwise return immediately */ | ||
| auto family = rtnl_route_get_family(route_obj); | ||
| if (family != AF_INET && family != AF_INET6) | ||
| { | ||
| SWSS_LOG_INFO("Unknown route family support: %s (object: %s)\n", destipprefix, nl_object_get_type(obj)); | ||
| SWSS_LOG_INFO("Unknown route family support (object: %s)\n", nl_object_get_type(obj)); | ||
| return; | ||
| } | ||
|
|
||
| /* Get the index of routing table */ | ||
| unsigned int table_index = rtnl_route_get_table(route_obj); | ||
|
|
||
| /* Default routing table. This line may have problems. */ | ||
| if (table_index == RT_TABLE_UNSPEC) | ||
| { | ||
| onRouteMsg(nlmsg_type, obj); | ||
| } | ||
| /* VNET route */ | ||
| else | ||
| { | ||
| onVnetRouteMsg(nlmsg_type, obj); | ||
| } | ||
| } | ||
|
|
||
| /* Handle regular route (without vnet) */ | ||
| void RouteSync::onRouteMsg(int nlmsg_type, struct nl_object *obj) | ||
| { | ||
| struct rtnl_route *route_obj = (struct rtnl_route *)obj; | ||
| struct nl_addr *dip; | ||
| char destipprefix[MAX_ADDR_SIZE + 1] = {0}; | ||
|
|
||
| dip = rtnl_route_get_dst(route_obj); | ||
| nl_addr2str(dip, destipprefix, MAX_ADDR_SIZE); | ||
| SWSS_LOG_DEBUG("Receive new route message dest ip prefix: %s\n", destipprefix); | ||
|
|
||
| /* | ||
| * Upon arrival of a delete msg we could either push the change right away, | ||
| * or we could opt to defer it if we are going through a warm-reboot cycle. | ||
|
|
@@ -74,13 +100,13 @@ void RouteSync::onMsg(int nlmsg_type, struct nl_object *obj) | |
| switch (rtnl_route_get_type(route_obj)) | ||
| { | ||
| case RTN_BLACKHOLE: | ||
| { | ||
| vector<FieldValueTuple> fvVector; | ||
| FieldValueTuple fv("blackhole", "true"); | ||
| fvVector.push_back(fv); | ||
| m_routeTable.set(destipprefix, fvVector); | ||
| return; | ||
| } | ||
| { | ||
| vector<FieldValueTuple> fvVector; | ||
| FieldValueTuple fv("blackhole", "true"); | ||
| fvVector.push_back(fv); | ||
| m_routeTable.set(destipprefix, fvVector); | ||
| return; | ||
| } | ||
| case RTN_UNICAST: | ||
| break; | ||
|
|
||
|
|
@@ -94,48 +120,16 @@ void RouteSync::onMsg(int nlmsg_type, struct nl_object *obj) | |
| return; | ||
| } | ||
|
|
||
| /* Geting nexthop lists */ | ||
| string nexthops; | ||
| string ifnames; | ||
|
|
||
| struct nl_list_head *nhs = rtnl_route_get_nexthops(route_obj); | ||
| if (!nhs) | ||
| { | ||
| SWSS_LOG_INFO("Nexthop list is empty for %s\n", destipprefix); | ||
| return; | ||
| } | ||
|
|
||
| char ifname[IFNAMSIZ] = {0}; | ||
| for (int i = 0; i < rtnl_route_get_nnexthops(route_obj); i++) | ||
| { | ||
| struct rtnl_nexthop *nexthop = rtnl_route_nexthop_n(route_obj, i); | ||
| struct nl_addr *addr = rtnl_route_nh_get_gateway(nexthop); | ||
| unsigned int ifindex = rtnl_route_nh_get_ifindex(nexthop); | ||
|
|
||
| if (addr != NULL) | ||
| { | ||
| char gwipprefix[MAX_ADDR_SIZE + 1] = {0}; | ||
| nl_addr2str(addr, gwipprefix, MAX_ADDR_SIZE); | ||
| nexthops += gwipprefix; | ||
| } | ||
|
|
||
| rtnl_link_i2name(m_link_cache, ifindex, ifname, IFNAMSIZ); | ||
| /* Cannot get ifname. Possibly interfaces get re-created. */ | ||
| if (!strlen(ifname)) | ||
| { | ||
| rtnl_link_alloc_cache(m_nl_sock, AF_UNSPEC, &m_link_cache); | ||
| rtnl_link_i2name(m_link_cache, ifindex, ifname, IFNAMSIZ); | ||
| if (!strlen(ifname)) | ||
| strcpy(ifname, "unknown"); | ||
| } | ||
| ifnames += ifname; | ||
|
|
||
| if (i + 1 < rtnl_route_get_nnexthops(route_obj)) | ||
| { | ||
| nexthops += string(","); | ||
| ifnames += string(","); | ||
| } | ||
| } | ||
| /* Get nexthop lists */ | ||
| string nexthops = getNextHopGw(route_obj); | ||
| string ifnames = getNextHopIf(route_obj); | ||
|
|
||
| vector<FieldValueTuple> fvVector; | ||
| FieldValueTuple nh("nexthop", nexthops); | ||
|
|
@@ -166,3 +160,206 @@ void RouteSync::onMsg(int nlmsg_type, struct nl_object *obj) | |
| m_warmStartHelper.insertRefreshMap(kfv); | ||
| } | ||
| } | ||
|
|
||
| /* Handle vnet route */ | ||
| void RouteSync::onVnetRouteMsg(int nlmsg_type, struct nl_object *obj) | ||
| { | ||
| struct rtnl_route *route_obj = (struct rtnl_route *)obj; | ||
|
|
||
| /* Get the destination IP prefix */ | ||
| struct nl_addr *dip = rtnl_route_get_dst(route_obj); | ||
| char destipprefix[MAX_ADDR_SIZE + 1] = {0}; | ||
| nl_addr2str(dip, destipprefix, MAX_ADDR_SIZE); | ||
|
|
||
| /* Get VRF index and VRF name */ | ||
| unsigned int vrf_index = rtnl_route_get_table(route_obj); | ||
| char vrf_name[IFNAMSIZ] = {0}; | ||
|
|
||
| /* If we cannot get the VRF name */ | ||
| if (!getIfName(vrf_index, vrf_name, IFNAMSIZ)) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if we want to proceed if
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @prsunny Do you mean the function should immediately return if it cannot get the VRF name?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, my point is, we got a route update on a VRF but the VRF device is not found/deleted. In this case, I would suggest to log error and return.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in d865382 |
||
| { | ||
| SWSS_LOG_INFO("Fail to get the VRF name (table ID %u)\n", vrf_index); | ||
| return; | ||
| } | ||
|
|
||
| /* vrf name = vnet name */ | ||
| string vnet_dip = vrf_name + string(":") + destipprefix; | ||
| SWSS_LOG_DEBUG("Receive new vnet route message %s\n", vnet_dip.c_str()); | ||
|
|
||
| if (nlmsg_type == RTM_DELROUTE) | ||
| { | ||
| /* Duplicated delete as we do not know if it is a VXLAN tunnel route*/ | ||
| m_vnet_routeTable.del(vnet_dip); | ||
| m_vnet_tunnelTable.del(vnet_dip); | ||
| return; | ||
| } | ||
| else if (nlmsg_type != RTM_NEWROUTE) | ||
| { | ||
| SWSS_LOG_INFO("Unknown message-type: %d for %s\n", nlmsg_type, vnet_dip.c_str()); | ||
| return; | ||
| } | ||
|
|
||
| switch (rtnl_route_get_type(route_obj)) | ||
| { | ||
| case RTN_UNICAST: | ||
| break; | ||
|
|
||
| /* We may support blackhole in the future */ | ||
| case RTN_BLACKHOLE: | ||
| SWSS_LOG_INFO("Blackhole route is supported yet (%s)\n", vnet_dip.c_str()); | ||
| return; | ||
|
|
||
| case RTN_MULTICAST: | ||
| case RTN_BROADCAST: | ||
| case RTN_LOCAL: | ||
| SWSS_LOG_INFO("BUM routes aren't supported yet (%s)\n", vnet_dip.c_str()); | ||
| return; | ||
|
|
||
| default: | ||
| return; | ||
| } | ||
|
|
||
| struct nl_list_head *nhs = rtnl_route_get_nexthops(route_obj); | ||
| if (!nhs) | ||
| { | ||
| SWSS_LOG_INFO("Nexthop list is empty for %s\n", vnet_dip.c_str()); | ||
| return; | ||
| } | ||
|
|
||
| /* Get nexthop lists */ | ||
| string nexthops = getNextHopGw(route_obj); | ||
| string ifnames = getNextHopIf(route_obj); | ||
|
|
||
| /* If the the first interface name starts with VXLAN_IF_NAME_PREFIX, | ||
| the route is a VXLAN tunnel route. */ | ||
| if (ifnames.find(VXLAN_IF_NAME_PREFIX) == 0) | ||
| { | ||
| vector<FieldValueTuple> fvVector; | ||
| FieldValueTuple ep("endpoint", nexthops); | ||
| fvVector.push_back(ep); | ||
|
|
||
| m_vnet_tunnelTable.set(vnet_dip, fvVector); | ||
| SWSS_LOG_DEBUG("%s set msg: %s %s\n", | ||
| APP_VNET_RT_TUNNEL_TABLE_NAME, vnet_dip.c_str(), nexthops.c_str()); | ||
| return; | ||
| } | ||
| /* Regular VNET route */ | ||
| else | ||
| { | ||
| vector<FieldValueTuple> fvVector; | ||
| FieldValueTuple idx("ifname", ifnames); | ||
| fvVector.push_back(idx); | ||
|
|
||
| /* If the route has at least one next hop gateway, e.g., nexthops does not only have ',' */ | ||
| if (nexthops.length() + 1 > (unsigned int)rtnl_route_get_nnexthops(route_obj)) | ||
| { | ||
| FieldValueTuple nh("nexthop", nexthops); | ||
| fvVector.push_back(nh); | ||
| SWSS_LOG_DEBUG("%s set msg: %s %s %s\n", | ||
| APP_VNET_RT_TABLE_NAME, vnet_dip.c_str(), ifnames.c_str(), nexthops.c_str()); | ||
| } | ||
| else | ||
| { | ||
| SWSS_LOG_DEBUG("%s set msg: %s %s\n", | ||
| APP_VNET_RT_TABLE_NAME, vnet_dip.c_str(), ifnames.c_str()); | ||
| } | ||
|
|
||
| m_vnet_routeTable.set(vnet_dip, fvVector); | ||
| } | ||
| } | ||
|
|
||
| /* | ||
| * Get interface/VRF name based on interface/VRF index | ||
| * @arg if_index Interface/VRF index | ||
| * @arg if_name String to store interface name | ||
| * @arg name_len Length of destination string, including terminating zero byte | ||
| * | ||
| * Return true if we successfully gets the interface/VRF name. | ||
| */ | ||
| bool RouteSync::getIfName(int if_index, char *if_name, size_t name_len) | ||
| { | ||
| if (!if_name || name_len == 0) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| memset(if_name, 0, name_len); | ||
|
|
||
| /* Cannot get interface name. Possibly the interface gets re-created. */ | ||
| if (!rtnl_link_i2name(m_link_cache, if_index, if_name, name_len)) | ||
| { | ||
| rtnl_link_alloc_cache(m_nl_sock, AF_UNSPEC, &m_link_cache); | ||
| if (!rtnl_link_i2name(m_link_cache, if_index, if_name, name_len)) | ||
| { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| /* | ||
| * Get next hop gateway IP addresses | ||
| * @arg route_obj route object | ||
| * | ||
| * Return concatenation of IP addresses: gw0 + "," + gw1 + .... + "," + gwN | ||
| */ | ||
| string RouteSync::getNextHopGw(struct rtnl_route *route_obj) | ||
| { | ||
| string result = ""; | ||
|
|
||
| for (int i = 0; i < rtnl_route_get_nnexthops(route_obj); i++) | ||
| { | ||
| struct rtnl_nexthop *nexthop = rtnl_route_nexthop_n(route_obj, i); | ||
| struct nl_addr *addr = rtnl_route_nh_get_gateway(nexthop); | ||
|
|
||
| /* Next hop gateway is not empty */ | ||
| if (addr) | ||
| { | ||
| char gw_ip[MAX_ADDR_SIZE + 1] = {0}; | ||
| nl_addr2str(addr, gw_ip, MAX_ADDR_SIZE); | ||
| result += gw_ip; | ||
| } | ||
|
|
||
| if (i + 1 < rtnl_route_get_nnexthops(route_obj)) | ||
| { | ||
| result += string(","); | ||
| } | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| /* | ||
| * Get next hop interface names | ||
| * @arg route_obj route object | ||
| * | ||
| * Return concatenation of interface names: if0 + "," + if1 + .... + "," + ifN | ||
| */ | ||
| string RouteSync::getNextHopIf(struct rtnl_route *route_obj) | ||
| { | ||
| string result = ""; | ||
|
|
||
| for (int i = 0; i < rtnl_route_get_nnexthops(route_obj); i++) | ||
| { | ||
| struct rtnl_nexthop *nexthop = rtnl_route_nexthop_n(route_obj, i); | ||
| /* Get the ID of next hop interface */ | ||
| unsigned if_index = rtnl_route_nh_get_ifindex(nexthop); | ||
| char if_name[IFNAMSIZ] = "0"; | ||
|
|
||
| /* If we cannot get the interface name */ | ||
| if (!getIfName(if_index, if_name, IFNAMSIZ)) | ||
| { | ||
| strcpy(if_name, "unknown"); | ||
| } | ||
|
|
||
| result += if_name; | ||
|
|
||
| if (i + 1 < rtnl_route_get_nnexthops(route_obj)) | ||
| { | ||
| result += string(","); | ||
| } | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be unsafe to classify every other routes to be VNET. As we discussed, it would be safe if we can use the id range check for table index
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will address this in the future