diff options
Diffstat (limited to 'zebra/connected.c')
| -rw-r--r-- | zebra/connected.c | 69 |
1 files changed, 67 insertions, 2 deletions
diff --git a/zebra/connected.c b/zebra/connected.c index 6f405ca1bb..883334d509 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -38,7 +38,6 @@ #include "zebra/connected.h" #include "zebra/rtadv.h" #include "zebra/zebra_mpls.h" -#include "zebra/debug.h" #include "zebra/zebra_errors.h" #include "zebra/zebra_router.h" @@ -208,6 +207,9 @@ void connected_up(struct interface *ifp, struct connected *ifc) struct zebra_vrf *zvrf; uint32_t metric; uint32_t flags = 0; + uint32_t count = 0; + struct listnode *cnode; + struct connected *c; zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); if (!zvrf) { @@ -220,6 +222,9 @@ void connected_up(struct interface *ifp, struct connected *ifc) if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL)) return; + /* Ensure 'down' flag is cleared */ + UNSET_FLAG(ifc->conf, ZEBRA_IFC_DOWN); + PREFIX_COPY(&p, CONNECTED_PREFIX(ifc)); /* Apply mask to the network. */ @@ -263,6 +268,28 @@ void connected_up(struct interface *ifp, struct connected *ifc) if (zrouter.asic_offloaded) flags |= ZEBRA_FLAG_OFFLOADED; + /* + * It's possible to add the same network and mask + * to an interface over and over. This would + * result in an equivalent number of connected + * routes. Just add one connected route in + * for all the addresses on an interface that + * resolve to the same network and mask + */ + for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) { + struct prefix cp; + + PREFIX_COPY(&cp, CONNECTED_PREFIX(c)); + apply_mask(&cp); + + if (prefix_same(&cp, &p) && + !CHECK_FLAG(c->conf, ZEBRA_IFC_DOWN)) + count++; + + if (count >= 2) + return; + } + rib_add(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, 0, flags, &p, NULL, &nh, 0, zvrf->table_id, metric, 0, 0, 0); @@ -358,12 +385,15 @@ void connected_down(struct interface *ifp, struct connected *ifc) .vrf_id = ifp->vrf_id, }; struct zebra_vrf *zvrf; + uint32_t count = 0; + struct listnode *cnode; + struct connected *c; zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); if (!zvrf) { flog_err( EC_ZEBRA_VRF_NOT_FOUND, - "%s: Received Up for interface but no associated zvrf: %d", + "%s: Received Down for interface but no associated zvrf: %d", __func__, ifp->vrf_id); return; } @@ -371,6 +401,17 @@ void connected_down(struct interface *ifp, struct connected *ifc) if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL)) return; + /* Skip if we've already done this; this can happen if we have a + * config change that takes an interface down, then we receive kernel + * notifications about the downed interface and its addresses. + */ + if (CHECK_FLAG(ifc->conf, ZEBRA_IFC_DOWN)) { + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s: ifc %p, %pFX already DOWN", + __func__, ifc, ifc->address); + return; + } + PREFIX_COPY(&p, CONNECTED_PREFIX(ifc)); /* Apply mask to the network. */ @@ -396,6 +437,30 @@ void connected_down(struct interface *ifp, struct connected *ifc) break; } + /* Mark the address as 'down' */ + SET_FLAG(ifc->conf, ZEBRA_IFC_DOWN); + + /* + * It's possible to have X number of addresses + * on a interface that all resolve to the same + * network and mask. Find them and just + * allow the deletion when are removing the last + * one. + */ + for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) { + struct prefix cp; + + PREFIX_COPY(&cp, CONNECTED_PREFIX(c)); + apply_mask(&cp); + + if (prefix_same(&p, &cp) && + !CHECK_FLAG(c->conf, ZEBRA_IFC_DOWN)) + count++; + + if (count >= 1) + return; + } + /* * Same logic as for connected_up(): push the changes into the * head. |
