diff --git a/modules/ip/control/route.c b/modules/ip/control/route.c index 3b54fd2fd..3476f84f4 100644 --- a/modules/ip/control/route.c +++ b/modules/ip/control/route.c @@ -122,15 +122,16 @@ struct nexthop *rib4_lookup_exact(uint16_t vrf_id, ip4_addr_t ip, uint8_t prefix return nh_id_to_ptr(nh_id); } -int rib4_insert( +static int rib4_insert_or_replace( uint16_t vrf_id, ip4_addr_t ip, uint8_t prefixlen, gr_nh_origin_t origin, - struct nexthop *nh + struct nexthop *nh, + bool replace ) { struct rte_rib *rib = get_or_create_rib(vrf_id); - struct nexthop *existing; + struct nexthop *existing = NULL; struct rte_rib_node *rn; gr_nh_origin_t *o; int ret; @@ -141,15 +142,21 @@ int rib4_insert( ret = -errno; goto fail; } - existing = rib4_lookup_exact(vrf_id, ip, prefixlen); - if (existing != NULL) { - ret = nexthop_equal(nh, existing) ? -EEXIST : -EBUSY; - goto fail; - } - if ((rn = rte_rib_insert(rib, rte_be_to_cpu_32(ip), prefixlen)) == NULL) { - ret = -rte_errno; - goto fail; + if ((rn = rte_rib_lookup_exact(rib, rte_be_to_cpu_32(ip), prefixlen)) == NULL) { + rn = rte_rib_insert(rib, rte_be_to_cpu_32(ip), prefixlen); + if (rn == NULL) { + ret = -rte_errno; + goto fail; + } + } else { + uintptr_t nh_id; + rte_rib_get_nh(rn, &nh_id); + existing = nh_id_to_ptr(nh_id); + if (!replace) { + ret = nexthop_equal(nh, existing) ? -EEXIST : -EBUSY; + goto fail; + } } nh->flags |= GR_NH_F_GATEWAY; @@ -170,12 +177,25 @@ int rib4_insert( ); } + if (existing) + nexthop_decref(existing); + return 0; fail: nexthop_decref(nh); return errno_set(-ret); } +int rib4_insert( + uint16_t vrf_id, + ip4_addr_t ip, + uint8_t prefixlen, + gr_nh_origin_t origin, + struct nexthop *nh +) { + return rib4_insert_or_replace(vrf_id, ip, prefixlen, origin, nh, false); +} + int rib4_delete(uint16_t vrf_id, ip4_addr_t ip, uint8_t prefixlen, gr_nh_type_t nh_type) { struct rte_rib *rib = get_rib(vrf_id); gr_nh_origin_t *o, origin; @@ -252,9 +272,9 @@ static struct api_out route4_add(const void *request, void ** /*response*/) { } // if route insert fails, the created nexthop will be freed - ret = rib4_insert(req->vrf_id, req->dest.ip, req->dest.prefixlen, req->origin, nh); - if (ret == -EEXIST && req->exist_ok) - ret = 0; + ret = rib4_insert_or_replace( + req->vrf_id, req->dest.ip, req->dest.prefixlen, req->origin, nh, req->exist_ok + ); return api_out(-ret, 0); } diff --git a/modules/ip6/control/route.c b/modules/ip6/control/route.c index 67f9c1108..10fdb4f2c 100644 --- a/modules/ip6/control/route.c +++ b/modules/ip6/control/route.c @@ -132,40 +132,49 @@ struct nexthop *rib6_lookup_exact( return nh_id_to_ptr(nh_id); } -int rib6_insert( +static int rib6_insert_or_replace( uint16_t vrf_id, uint16_t iface_id, const struct rte_ipv6_addr *ip, uint8_t prefixlen, gr_nh_origin_t origin, - struct nexthop *nh + struct nexthop *nh, + bool replace ) { struct rte_rib6 *rib = get_or_create_rib6(vrf_id); const struct rte_ipv6_addr *scoped_ip; + struct nexthop *existing = NULL; struct rte_ipv6_addr tmp; struct rte_rib6_node *rn; - struct nexthop *existing; gr_nh_origin_t *o; int ret; nexthop_incref(nh); + scoped_ip = addr6_linklocal_scope(ip, &tmp, iface_id); if (rib == NULL) { ret = -errno; goto fail; } - existing = rib6_lookup_exact(vrf_id, iface_id, ip, prefixlen); - if (existing != NULL) { - ret = nexthop_equal(nh, existing) ? -EEXIST : -EBUSY; - goto fail; - } - scoped_ip = addr6_linklocal_scope(ip, &tmp, iface_id); - if ((rn = rte_rib6_insert(rib, scoped_ip, prefixlen)) == NULL) { - ret = -rte_errno; - goto fail; + if ((rn = rte_rib6_lookup_exact(rib, scoped_ip, prefixlen)) == NULL) { + rn = rte_rib6_insert(rib, scoped_ip, prefixlen); + if (rn == NULL) { + ret = -rte_errno; + goto fail; + } + } else { + uintptr_t nh_id; + rte_rib6_get_nh(rn, &nh_id); + existing = nh_id_to_ptr(nh_id); + if (!replace) { + ret = nexthop_equal(nh, existing) ? -EEXIST : -EBUSY; + goto fail; + } } + nh->flags |= GR_NH_F_GATEWAY; + rte_rib6_set_nh(rn, nh_ptr_to_id(nh)); o = rte_rib6_get_ext(rn); *o = origin; @@ -182,12 +191,26 @@ int rib6_insert( ); } + if (existing) + nexthop_decref(existing); + return 0; fail: nexthop_decref(nh); return errno_set(-ret); } +int rib6_insert( + uint16_t vrf_id, + uint16_t iface_id, + const struct rte_ipv6_addr *ip, + uint8_t prefixlen, + gr_nh_origin_t origin, + struct nexthop *nh +) { + return rib6_insert_or_replace(vrf_id, iface_id, ip, prefixlen, origin, nh, false); +} + int rib6_delete( uint16_t vrf_id, uint16_t iface_id, @@ -272,13 +295,15 @@ static struct api_out route6_add(const void *request, void ** /*response*/) { } // if route insert fails, the created nexthop will be freed - ret = rib6_insert( - req->vrf_id, nh->iface_id, &req->dest.ip, req->dest.prefixlen, req->origin, nh + ret = rib6_insert_or_replace( + req->vrf_id, + nh->iface_id, + &req->dest.ip, + req->dest.prefixlen, + req->origin, + nh, + req->exist_ok ); - if (ret == -EEXIST && req->exist_ok) - ret = 0; - - nh->flags |= GR_NH_F_GATEWAY; return api_out(-ret, 0); } diff --git a/smoke/config_test.sh b/smoke/config_test.sh index 750e352d3..0056c51bb 100755 --- a/smoke/config_test.sh +++ b/smoke/config_test.sh @@ -17,11 +17,13 @@ grcli add nexthop address 4.3.2.1 iface p1 mac ba:d0:ca:ca:00:01 grcli add ip address 10.0.0.1/24 iface p0 grcli add ip address 10.1.0.1/24 iface p1 grcli add ip route 0.0.0.0/0 via 10.0.0.2 +grcli add ip route 0.0.0.0/0 via 10.0.0.1 || fail "route replace should succeed" grcli add ip route 4.5.21.2/27 via id 47 grcli add ip route 172.16.47.0/24 via id 1047 grcli add ip6 address 2345::1/24 iface p0 grcli add ip6 address 2346::1/24 iface p1 grcli add ip6 route ::/0 via 2345::2 +grcli add ip6 route ::/0 via 2345::1 || fail "route6 replace should succeed" grcli add ip6 route 2521:111::4/37 via id 1047 grcli add ip6 route 2521:112::/64 via id 45 grcli add ip6 route 2521:113::/64 via id 47