Skip to content

Commit 4794f1d

Browse files
cscarpittalguohan
authored andcommitted
[FPM]: Fix dplane_fpm_sonic for multiple SRv6 nexthops
Currently, there is a bug in the dplane_fpm_sonic to handle ECMP routes. When FRR has an ECMP route with multiple SRv6 nexthops, the dplane_fpm_sonic encodes the route with only the first SRv6 nextop in the Netlink message, ignoring the other nexthops. As a result, the Netlink message sent to fpmsyncd leads to the installation of the route using only the first SRv6 nexthop, breaking the EMCP nature of SRv6 routes. This pull request fixes the issue in the dplane_fpm_sonic to handle all SRv6 nexthops in the route. Signed-off-by: Carmine Scarpitta <[email protected]>
1 parent e94a1e1 commit 4794f1d

File tree

1 file changed

+179
-17
lines changed

1 file changed

+179
-17
lines changed

src/sonic-frr/dplane_fpm_sonic/dplane_fpm_sonic.c

Lines changed: 179 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,7 +1051,42 @@ static struct zebra_vrf *vrf_lookup_by_table_id(uint32_t table_id)
10511051
}
10521052

10531053
return NULL;
1054-
}
1054+
}
1055+
1056+
static bool has_srv6_nexthop(struct zebra_dplane_ctx *ctx)
1057+
{
1058+
struct nexthop *nexthop;
1059+
1060+
for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop))
1061+
if (nexthop->nh_srv6)
1062+
return true;
1063+
1064+
return false;
1065+
}
1066+
1067+
static bool has_srv6_sidlist_nexthop(struct zebra_dplane_ctx *ctx)
1068+
{
1069+
struct nexthop *nexthop;
1070+
1071+
for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop))
1072+
if (nexthop->nh_srv6 && nexthop->nh_srv6->seg6_segs &&
1073+
!sid_zero(nexthop->nh_srv6->seg6_segs))
1074+
return true;
1075+
1076+
return false;
1077+
}
1078+
1079+
static bool has_srv6_localsid_nexthop(struct zebra_dplane_ctx *ctx)
1080+
{
1081+
struct nexthop *nexthop;
1082+
1083+
for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop))
1084+
if (nexthop->nh_srv6 &&
1085+
nexthop->nh_srv6->seg6local_action != ZEBRA_SEG6_LOCAL_ACTION_UNSPEC)
1086+
return true;
1087+
1088+
return false;
1089+
}
10551090

10561091
/**
10571092
* Resets the SRv6 routes FPM flags so we send all SRv6 routes again.
@@ -1474,6 +1509,70 @@ static ssize_t netlink_vpn_route_msg_encode(int cmd,
14741509
return NLMSG_ALIGN(req->n.nlmsg_len);
14751510
}
14761511

1512+
static bool netlink_srv6_vpn_route_msg_encode_multipath(int cmd, struct zebra_dplane_ctx *ctx,
1513+
uint8_t *data, size_t datalen,
1514+
const struct nexthop *nexthop,
1515+
struct nlmsghdr *nlmsg, size_t req_size,
1516+
bool fpm, bool force_nhg)
1517+
{
1518+
struct rtattr *nest;
1519+
struct rtnexthop *rtnh;
1520+
struct interface *ifp;
1521+
struct in6_addr encap_src_addr = {};
1522+
struct connected *connected;
1523+
struct vrf *vrf;
1524+
struct prefix *cp;
1525+
1526+
rtnh = nl_attr_rtnh(nlmsg, req_size);
1527+
if (rtnh == NULL)
1528+
return false;
1529+
1530+
if (!nl_attr_put16(nlmsg, req_size, RTA_ENCAP_TYPE, FPM_ROUTE_ENCAP_SRV6))
1531+
return false;
1532+
1533+
nest = nl_attr_nest(nlmsg, req_size, RTA_ENCAP);
1534+
if (!nest)
1535+
return false;
1536+
1537+
/*
1538+
* by default, we use the loopback address as encap source address,
1539+
* if it is valid
1540+
*/
1541+
ifp = if_lookup_by_name("lo", VRF_DEFAULT);
1542+
1543+
if (ifp) {
1544+
vrf = vrf_lookup_by_name(VRF_DEFAULT_NAME);
1545+
if (!vrf)
1546+
return false;
1547+
1548+
FOR_ALL_INTERFACES (vrf, ifp) {
1549+
frr_each (if_connected, ifp->connected, connected) {
1550+
cp = connected->address;
1551+
if (cp->family == AF_INET6 &&
1552+
!IN6_IS_ADDR_LOOPBACK(&cp->u.prefix6) &&
1553+
!IN6_IS_ADDR_LINKLOCAL(&cp->u.prefix6)) {
1554+
encap_src_addr = cp->u.prefix6;
1555+
break;
1556+
}
1557+
}
1558+
}
1559+
}
1560+
1561+
if (!nl_attr_put(nlmsg, req_size, FPM_ROUTE_ENCAP_SRV6_ENCAP_SRC_ADDR, &encap_src_addr,
1562+
IPV6_MAX_BYTELEN))
1563+
return false;
1564+
1565+
if (!nl_attr_put(nlmsg, req_size, FPM_ROUTE_ENCAP_SRV6_VPN_SID,
1566+
&nexthop->nh_srv6->seg6_segs->seg[0], IPV6_MAX_BYTELEN))
1567+
return false;
1568+
1569+
nl_attr_nest_end(nlmsg, nest);
1570+
1571+
nl_attr_rtnh_end(nlmsg, rtnh);
1572+
1573+
return true;
1574+
}
1575+
14771576
/*
14781577
* SRv6 VPN route change via netlink interface, using a dataplane context object
14791578
*
@@ -1497,17 +1596,14 @@ static ssize_t netlink_srv6_vpn_route_msg_encode(int cmd,
14971596
struct connected *connected;
14981597
struct vrf *vrf;
14991598
struct prefix *cp;
1599+
unsigned int nexthop_num;
15001600

15011601
struct {
15021602
struct nlmsghdr n;
15031603
struct rtmsg r;
15041604
char buf[];
15051605
} *req = (void *)data;
15061606

1507-
nexthop = dplane_ctx_get_ng(ctx)->nexthop;
1508-
if (!nexthop || !nexthop->nh_srv6 || sid_zero((const struct seg6_seg_stack *)nexthop->nh_srv6->seg6_segs))
1509-
return -1;
1510-
15111607
p = dplane_ctx_get_dest(ctx);
15121608

15131609
if (datalen < sizeof(*req))
@@ -1572,6 +1668,79 @@ static ssize_t netlink_srv6_vpn_route_msg_encode(int cmd,
15721668
nl_msg_type_to_str(cmd), p, dplane_ctx_get_vrf(ctx),
15731669
table_id);
15741670

1671+
/*
1672+
* Counts the number of nexthops to determine if the route is singlepath
1673+
* (single nexthop) or multipath (multiple nexthops)
1674+
*/
1675+
nexthop_num = 0;
1676+
for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) {
1677+
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
1678+
continue;
1679+
if (!NEXTHOP_IS_ACTIVE(nexthop->flags))
1680+
continue;
1681+
1682+
nexthop_num++;
1683+
}
1684+
1685+
/* Multipath case */
1686+
if (nexthop_num > 1) {
1687+
struct rtattr *nest;
1688+
1689+
nest = nl_attr_nest(&req->n, datalen, RTA_MULTIPATH);
1690+
if (nest == NULL)
1691+
return 0;
1692+
1693+
nexthop_num = 0;
1694+
for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) {
1695+
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
1696+
continue;
1697+
1698+
/* Skip non-SRv6 nexthops */
1699+
if (!nexthop->nh_srv6 || sid_zero(nexthop->nh_srv6->seg6_segs))
1700+
continue;
1701+
1702+
if (NEXTHOP_IS_ACTIVE(nexthop->flags)) {
1703+
nexthop_num++;
1704+
1705+
if (!netlink_srv6_vpn_route_msg_encode_multipath(cmd, ctx, data,
1706+
datalen, nexthop,
1707+
&req->n, datalen,
1708+
fpm, force_nhg))
1709+
return 0;
1710+
}
1711+
}
1712+
1713+
nl_attr_nest_end(&req->n, nest);
1714+
1715+
if (nexthop_num == 0) {
1716+
if (IS_ZEBRA_DEBUG_FPM)
1717+
zlog_debug("%s: No useful nexthop.", __func__);
1718+
}
1719+
1720+
return NLMSG_ALIGN(req->n.nlmsg_len);
1721+
}
1722+
1723+
/* Singlepath case */
1724+
nexthop_num = 0;
1725+
for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) {
1726+
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
1727+
continue;
1728+
if (!NEXTHOP_IS_ACTIVE(nexthop->flags))
1729+
continue;
1730+
if (!nexthop->nh_srv6 || sid_zero(nexthop->nh_srv6->seg6_segs))
1731+
continue;
1732+
1733+
nexthop_num++;
1734+
break;
1735+
}
1736+
1737+
if (nexthop_num == 0) {
1738+
if (IS_ZEBRA_DEBUG_FPM)
1739+
zlog_debug("%s: No useful nexthop.", __func__);
1740+
1741+
return NLMSG_ALIGN(req->n.nlmsg_len);
1742+
}
1743+
15751744
if (!nl_attr_put16(&req->n, datalen, RTA_ENCAP_TYPE,
15761745
FPM_ROUTE_ENCAP_SRV6))
15771746
return false;
@@ -1625,20 +1794,16 @@ static ssize_t netlink_srv6_msg_encode(int cmd,
16251794
uint8_t *data, size_t datalen,
16261795
bool fpm, bool force_nhg)
16271796
{
1628-
struct nexthop *nexthop = NULL;
1629-
16301797
struct {
16311798
struct nlmsghdr n;
16321799
struct rtmsg r;
16331800
char buf[];
16341801
} *req = (void *)data;
16351802

1636-
nexthop = dplane_ctx_get_ng(ctx)->nexthop;
1637-
if (!nexthop || !nexthop->nh_srv6)
1803+
if (!has_srv6_nexthop(ctx))
16381804
return -1;
16391805

1640-
if (nexthop->nh_srv6->seg6local_action !=
1641-
ZEBRA_SEG6_LOCAL_ACTION_UNSPEC) {
1806+
if (has_srv6_localsid_nexthop(ctx)) {
16421807
if (cmd == RTM_NEWROUTE)
16431808
cmd = RTM_NEWSRV6LOCALSID;
16441809
else if (cmd == RTM_DELROUTE)
@@ -1647,7 +1812,7 @@ static ssize_t netlink_srv6_msg_encode(int cmd,
16471812
if (!netlink_srv6_localsid_msg_encode(
16481813
cmd, ctx, data, datalen, fpm, force_nhg))
16491814
return 0;
1650-
} else if (!sid_zero(nexthop->nh_srv6->seg6_segs)) {
1815+
} else if (has_srv6_sidlist_nexthop(ctx)) {
16511816
if(force_nhg){
16521817
if (!netlink_vpn_route_msg_encode(
16531818
cmd, ctx, data, datalen, force_nhg))
@@ -2280,7 +2445,6 @@ static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx)
22802445
ssize_t rv;
22812446
uint64_t obytes, obytes_peak;
22822447
enum dplane_op_e op = dplane_ctx_get_op(ctx);
2283-
struct nexthop *nexthop;
22842448

22852449
/*
22862450
* If we were configured to not use next hop groups, then quit as soon
@@ -2321,8 +2485,7 @@ static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx)
23212485
switch (op) {
23222486
case DPLANE_OP_ROUTE_UPDATE:
23232487
case DPLANE_OP_ROUTE_DELETE:
2324-
nexthop = dplane_ctx_get_ng(ctx)->nexthop;
2325-
if (nexthop && nexthop->nh_srv6) {
2488+
if (has_srv6_nexthop(ctx)) {
23262489
rv = netlink_srv6_msg_encode(RTM_DELROUTE, ctx,
23272490
nl_buf, sizeof(nl_buf),
23282491
true, fnc->use_nhg);
@@ -2352,8 +2515,7 @@ static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx)
23522515

23532516
/* FALL THROUGH */
23542517
case DPLANE_OP_ROUTE_INSTALL:
2355-
nexthop = dplane_ctx_get_ng(ctx)->nexthop;
2356-
if (nexthop && nexthop->nh_srv6) {
2518+
if (has_srv6_nexthop(ctx)) {
23572519
rv = netlink_srv6_msg_encode(
23582520
RTM_NEWROUTE, ctx, &nl_buf[nl_buf_len],
23592521
sizeof(nl_buf) - nl_buf_len, true, fnc->use_nhg);

0 commit comments

Comments
 (0)