diff --git a/lib/nexthop.h b/lib/nexthop.h index 43dd71e11231..2b502cb62b17 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -43,6 +43,62 @@ enum blackhole_type { enum nh_encap_type { NET_VXLAN = 100, /* value copied from FPM_NH_ENCAP_VXLAN. */ + FPM_NH_ENCAP_SRV6_ROUTE = 101, + FPM_NH_ENCAP_SRV6_LOCAL_SID = 102, +}; + +enum { + SRV6_ROUTE_UNSPEC = 0, + SRV6_ROUTE_VPN_SID = 1, + SRV6_ROUTE_ENCAP_SRC_ADDR = 2, +}; + +enum srv6_localsid_action { + FPM_SRV6_LOCALSID_ACTION_UNSPEC = 0, + FPM_SRV6_LOCALSID_ACTION_END = 1, + FPM_SRV6_LOCALSID_ACTION_END_X = 2, + FPM_SRV6_LOCALSID_ACTION_END_T = 3, + FPM_SRV6_LOCALSID_ACTION_END_DX2 = 4, + FPM_SRV6_LOCALSID_ACTION_END_DX6 = 5, + FPM_SRV6_LOCALSID_ACTION_END_DX4 = 6, + FPM_SRV6_LOCALSID_ACTION_END_DT6 = 7, + FPM_SRV6_LOCALSID_ACTION_END_DT4 = 8, + FPM_SRV6_LOCALSID_ACTION_END_B6 = 9, + FPM_SRV6_LOCALSID_ACTION_END_B6_ENCAP = 10, + FPM_SRV6_LOCALSID_ACTION_END_BM = 11, + FPM_SRV6_LOCALSID_ACTION_END_S = 12, + FPM_SRV6_LOCALSID_ACTION_END_AS = 13, + FPM_SRV6_LOCALSID_ACTION_END_AM = 14, + FPM_SRV6_LOCALSID_ACTION_END_BPF = 15, + FPM_SRV6_LOCALSID_ACTION_END_DT46 = 16, + FPM_SRV6_LOCALSID_ACTION_UDT4 = 100, + FPM_SRV6_LOCALSID_ACTION_UDT6 = 101, + FPM_SRV6_LOCALSID_ACTION_UDT46 = 102, + FPM_SRV6_LOCALSID_ACTION_MAX, +}; + +enum { + FPM_SRV6_LOCALSID_UNSPEC = 0, + FPM_SRV6_LOCALSID_ACTION = 1, + FPM_SRV6_LOCALSID_SRH = 2, + FPM_SRV6_LOCALSID_TABLE = 3, + FPM_SRV6_LOCALSID_NH4 = 4, + FPM_SRV6_LOCALSID_NH6 = 5, + FPM_SRV6_LOCALSID_IIF = 6, + FPM_SRV6_LOCALSID_OIF = 7, + FPM_SRV6_LOCALSID_BPF = 8, + FPM_SRV6_LOCALSID_VRFTABLE = 9, + FPM_SRV6_LOCALSID_COUNTERS = 10, + FPM_SRV6_LOCALSID_VRFNAME = 100, + FPM_SRV6_LOCALSID_FORMAT = 101, +}; + +enum { + FPM_SRV6_LOCALSID_FORMAT_UNSPEC = 0, + FPM_SRV6_LOCALSID_FORMAT_BLOCK_LEN = 1, + FPM_SRV6_LOCALSID_FORMAT_NODE_LEN = 2, + FPM_SRV6_LOCALSID_FORMAT_FUNC_LEN = 3, + FPM_SRV6_LOCALSID_FORMAT_ARG_LEN = 4, }; /* Fixed limit on the number of backup nexthops per primary nexthop */ diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index de01ced411ef..7d9bd51e6235 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -407,6 +407,29 @@ vrf_id_t vrf_lookup_by_table(uint32_t table_id, ns_id_t ns_id) return VRF_DEFAULT; } +static struct zebra_vrf *vrf_lookup_by_table_id(uint32_t table_id) +{ + struct vrf *vrf; + struct zebra_vrf *zvrf; + + RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { + zvrf = vrf->info; + if (zvrf == NULL) + continue; + /* case vrf with netns : match the netnsid */ + if (vrf_is_backend_netns()) { + return NULL; + } else { + /* VRF is VRF_BACKEND_VRF_LITE */ + if (zvrf->table_id != table_id) + continue; + return zvrf; + } + } + + return NULL; +} + /** * @parse_encap_mpls() - Parses encapsulated mpls attributes * @tb: Pointer to rtattr to look for nested items in. @@ -1537,7 +1560,7 @@ static bool _netlink_route_build_singlepath(const struct prefix *p, const struct nexthop *nexthop, struct nlmsghdr *nlmsg, struct rtmsg *rtmsg, - size_t req_size, int cmd) + size_t req_size, int cmd, bool fpm) { char label_buf[256]; @@ -1552,7 +1575,7 @@ static bool _netlink_route_build_singlepath(const struct prefix *p, label_buf, sizeof(label_buf))) return false; - if (nexthop->nh_srv6) { + if (!fpm && nexthop->nh_srv6) { if (nexthop->nh_srv6->seg6local_action != ZEBRA_SEG6_LOCAL_ACTION_UNSPEC) { struct rtattr *nest; @@ -1674,6 +1697,239 @@ static bool _netlink_route_build_singlepath(const struct prefix *p, } } + if (fpm && nexthop->nh_srv6) { + if (nexthop->nh_srv6->seg6local_action != + ZEBRA_SEG6_LOCAL_ACTION_UNSPEC) { + struct zebra_srv6 *srv6 = zebra_srv6_get_default(); + struct zebra_vrf *zvrf; + bool locator_found = false; + struct srv6_locator *locator; + struct listnode *node; + struct rtattr *nest, *inner_nest; + const struct seg6local_context *ctx; + + ctx = &nexthop->nh_srv6->seg6local_ctx; + + if (!nl_attr_put16(nlmsg, req_size, RTA_ENCAP_TYPE, + FPM_NH_ENCAP_SRV6_LOCAL_SID)) + return false; + + for (ALL_LIST_ELEMENTS_RO(srv6->locators, node, + locator)) { + if (prefix_match(&locator->prefix, p)) { + locator_found = true; + break; + } + } + + nest = nl_attr_nest(nlmsg, req_size, RTA_ENCAP); + + /* Process Local SID format */ + if (locator_found) { + inner_nest = + nl_attr_nest(nlmsg, req_size, + FPM_SRV6_LOCALSID_FORMAT); + + if (locator->block_bits_length) + if (!nl_attr_put8( + nlmsg, req_size, + FPM_SRV6_LOCALSID_FORMAT_BLOCK_LEN, + locator->block_bits_length)) + return false; + + if (locator->node_bits_length) + if (!nl_attr_put8( + nlmsg, req_size, + FPM_SRV6_LOCALSID_FORMAT_NODE_LEN, + locator->node_bits_length)) + return false; + + if (locator->function_bits_length) + if (!nl_attr_put8( + nlmsg, req_size, + FPM_SRV6_LOCALSID_FORMAT_FUNC_LEN, + locator->function_bits_length)) + return false; + + if (locator->argument_bits_length) + if (!nl_attr_put8( + nlmsg, req_size, + FPM_SRV6_LOCALSID_FORMAT_ARG_LEN, + locator->argument_bits_length)) + return false; + + nl_attr_nest_end(nlmsg, inner_nest); + } + + switch (nexthop->nh_srv6->seg6local_action) { + case ZEBRA_SEG6_LOCAL_ACTION_END: + if (!nl_attr_put32( + nlmsg, req_size, + FPM_SRV6_LOCALSID_ACTION, + FPM_SRV6_LOCALSID_ACTION_END)) + return false; + + break; + case ZEBRA_SEG6_LOCAL_ACTION_END_X: + if (!nl_attr_put32( + nlmsg, req_size, + FPM_SRV6_LOCALSID_ACTION, + FPM_SRV6_LOCALSID_ACTION_END_X)) + return false; + + if (!nl_attr_put(nlmsg, req_size, + FPM_SRV6_LOCALSID_NH6, + &ctx->nh6, + sizeof(struct in6_addr))) + return false; + break; + case ZEBRA_SEG6_LOCAL_ACTION_END_T: + zvrf = vrf_lookup_by_table_id(ctx->table); + if (!zvrf) + return false; + + if (!nl_attr_put32( + nlmsg, req_size, + FPM_SRV6_LOCALSID_ACTION, + FPM_SRV6_LOCALSID_ACTION_END_T)) + return false; + + if (!nl_attr_put(nlmsg, req_size, + FPM_SRV6_LOCALSID_VRFNAME, + zvrf->vrf->name, + strlen(zvrf->vrf->name) + 1)) + return false; + + break; + case ZEBRA_SEG6_LOCAL_ACTION_END_DX4: + if (!nl_attr_put32( + nlmsg, req_size, + FPM_SRV6_LOCALSID_ACTION, + FPM_SRV6_LOCALSID_ACTION_END_DX4)) + return false; + + if (!nl_attr_put(nlmsg, req_size, + FPM_SRV6_LOCALSID_NH4, + &ctx->nh4, + sizeof(struct in_addr))) + return false; + + break; + case ZEBRA_SEG6_LOCAL_ACTION_END_DT6: + zvrf = vrf_lookup_by_table_id(ctx->table); + if (!zvrf) + return false; + + if (locator_found && + CHECK_FLAG(locator->flags, + SRV6_LOCATOR_USID)) { + if (!nl_attr_put32( + nlmsg, req_size, + FPM_SRV6_LOCALSID_ACTION, + FPM_SRV6_LOCALSID_ACTION_UDT6)) + return false; + } else { + if (!nl_attr_put32( + nlmsg, req_size, + FPM_SRV6_LOCALSID_ACTION, + FPM_SRV6_LOCALSID_ACTION_END_DT6)) + return false; + } + + if (!nl_attr_put(nlmsg, req_size, + FPM_SRV6_LOCALSID_VRFNAME, + zvrf->vrf->name, + strlen(zvrf->vrf->name) + 1)) + return false; + + break; + case ZEBRA_SEG6_LOCAL_ACTION_END_DT4: + zvrf = vrf_lookup_by_table_id(ctx->table); + if (!zvrf) + return false; + + if (locator_found && + CHECK_FLAG(locator->flags, + SRV6_LOCATOR_USID)) { + if (!nl_attr_put32( + nlmsg, req_size, + FPM_SRV6_LOCALSID_ACTION, + FPM_SRV6_LOCALSID_ACTION_UDT4)) + return false; + } else { + if (!nl_attr_put32( + nlmsg, req_size, + FPM_SRV6_LOCALSID_ACTION, + FPM_SRV6_LOCALSID_ACTION_END_DT4)) + return false; + } + + if (!nl_attr_put(nlmsg, req_size, + FPM_SRV6_LOCALSID_VRFNAME, + zvrf->vrf->name, + strlen(zvrf->vrf->name) + 1)) + return false; + + break; + case ZEBRA_SEG6_LOCAL_ACTION_END_DT46: + zvrf = vrf_lookup_by_table_id(ctx->table); + if (!zvrf) + return false; + + if (locator_found && + CHECK_FLAG(locator->flags, + SRV6_LOCATOR_USID)) { + if (!nl_attr_put32( + nlmsg, req_size, + FPM_SRV6_LOCALSID_ACTION, + FPM_SRV6_LOCALSID_ACTION_UDT46)) + return false; + } else { + if (!nl_attr_put32( + nlmsg, req_size, + FPM_SRV6_LOCALSID_ACTION, + FPM_SRV6_LOCALSID_ACTION_END_DT46)) + return false; + } + + if (!nl_attr_put(nlmsg, req_size, + FPM_SRV6_LOCALSID_VRFNAME, + zvrf->vrf->name, + strlen(zvrf->vrf->name) + 1)) + return false; + + break; + default: + zlog_err( + "Unsupported seg6local behaviour action=%u", + nexthop->nh_srv6->seg6local_action); + return false; + } + nl_attr_nest_end(nlmsg, nest); + } + + if (!sid_zero(&nexthop->nh_srv6->seg6_segs)) { + struct zebra_srv6 *srv6 = zebra_srv6_get_default(); + struct rtattr *nest; + + if (!nl_attr_put16(nlmsg, req_size, RTA_ENCAP_TYPE, + FPM_NH_ENCAP_SRV6_ROUTE)) + return false; + nest = nl_attr_nest(nlmsg, req_size, RTA_ENCAP); + if (!nest) + return false; + if (!nl_attr_put( + nlmsg, req_size, SRV6_ROUTE_ENCAP_SRC_ADDR, + &srv6->encap_src_addr, IPV6_MAX_BYTELEN)) + return false; + if (!nl_attr_put(nlmsg, req_size, SRV6_ROUTE_VPN_SID, + &nexthop->nh_srv6->seg6_segs, + IPV6_MAX_BYTELEN)) + return false; + nl_attr_nest_end(nlmsg, nest); + } + } + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) rtmsg->rtm_flags |= RTNH_F_ONLINK; @@ -1941,7 +2197,7 @@ _netlink_mpls_build_singlepath(const struct prefix *p, const char *routedesc, bytelen = (family == AF_INET ? 4 : 16); return _netlink_route_build_singlepath(p, routedesc, bytelen, nhlfe->nexthop, nlmsg, rtmsg, - req_size, cmd); + req_size, cmd, false); } @@ -2076,6 +2332,8 @@ static int netlink_route_nexthop_encap(struct nlmsghdr *n, size_t nlen, return false; nl_attr_nest_end(n, nest); break; + default: + zlog_err("Unsupported encap type %d", nh->nh_encap_type); } return true; @@ -2326,7 +2584,8 @@ ssize_t netlink_route_multipath_msg_encode(int cmd, if (!_netlink_route_build_singlepath( p, routedesc, bytelen, nexthop, - &req->n, &req->r, datalen, cmd)) + &req->n, &req->r, datalen, cmd, + fpm)) return 0; nexthop_num++; break; diff --git a/zebra/zebra_srv6.h b/zebra/zebra_srv6.h index 51db83d6fb64..972c9d933097 100644 --- a/zebra/zebra_srv6.h +++ b/zebra/zebra_srv6.h @@ -19,6 +19,9 @@ /* SRv6 instance structure. */ struct zebra_srv6 { struct list *locators; + + /* Source address for SRv6 encapsulation */ + struct in6_addr encap_src_addr; }; /* declare hooks for the basic API, so that it can be specialized or served