--- embedaddon/quagga/zebra/connected.c 2013/07/21 23:54:41 1.1.1.3 +++ embedaddon/quagga/zebra/connected.c 2016/11/02 10:09:10 1.1.1.4 @@ -36,8 +36,8 @@ #include "zebra/interface.h" #include "zebra/connected.h" extern struct zebra_t zebrad; - -/* withdraw a connected address */ + +/* communicate the withdrawal of a connected address */ static void connected_withdraw (struct connected *ifc) { @@ -49,9 +49,10 @@ connected_withdraw (struct connected *ifc) { zebra_interface_address_delete_update (ifc->ifp, ifc); - if_subnet_delete (ifc->ifp, ifc); - if (ifc->address->family == AF_INET) + if_subnet_delete (ifc->ifp, ifc); + + if (ifc->address->family == AF_INET) connected_down_ipv4 (ifc->ifp, ifc); #ifdef HAVE_IPV6 else @@ -61,6 +62,9 @@ connected_withdraw (struct connected *ifc) UNSET_FLAG (ifc->conf, ZEBRA_IFC_REAL); } + /* The address is not in the kernel anymore, so clear the flag */ + UNSET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); + if (!CHECK_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED)) { listnode_delete (ifc->ifp->connected, ifc); @@ -77,27 +81,22 @@ connected_announce (struct interface *ifp, struct conn listnode_add (ifp->connected, ifc); /* Update interface address information to protocol daemon. */ - if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL)) - { - if (ifc->address->family == AF_INET) - if_subnet_add (ifp, ifc); + if (ifc->address->family == AF_INET) + if_subnet_add (ifp, ifc); - SET_FLAG (ifc->conf, ZEBRA_IFC_REAL); + zebra_interface_address_add_update (ifp, ifc); - zebra_interface_address_add_update (ifp, ifc); - - if (if_is_operative(ifp)) - { - if (ifc->address->family == AF_INET) - connected_up_ipv4 (ifp, ifc); + if (if_is_operative(ifp)) + { + if (ifc->address->family == AF_INET) + connected_up_ipv4 (ifp, ifc); #ifdef HAVE_IPV6 - else - connected_up_ipv6 (ifp, ifc); + else + connected_up_ipv6 (ifp, ifc); #endif - } } } - + /* If same interface address is already exist... */ struct connected * connected_check (struct interface *ifp, struct prefix *p) @@ -112,7 +111,7 @@ connected_check (struct interface *ifp, struct prefix return NULL; } -/* Check if two ifc's describe the same address */ +/* Check if two ifc's describe the same address in the same state */ static int connected_same (struct connected *ifc1, struct connected *ifc2) { @@ -132,18 +131,17 @@ connected_same (struct connected *ifc1, struct connect if (ifc1->flags != ifc2->flags) return 0; + + if (ifc1->conf != ifc2->conf) + return 0; return 1; } -/* Handle implicit withdrawals of addresses, where a system ADDs an address - * to an interface which already has the same address configured. - * - * Returns the struct connected which must be announced to clients, - * or NULL if nothing to do. - */ -static struct connected * -connected_implicit_withdraw (struct interface *ifp, struct connected *ifc) +/* Handle changes to addresses and send the neccesary announcements + * to clients. */ +static void +connected_update(struct interface *ifp, struct connected *ifc) { struct connected *current; @@ -156,17 +154,22 @@ connected_implicit_withdraw (struct interface *ifp, st /* Avoid spurious withdraws, this might be just the kernel 'reflecting' * back an address we have already added. */ - if (connected_same (current, ifc) && CHECK_FLAG(current->conf, ZEBRA_IFC_REAL)) + if (connected_same (current, ifc)) { /* nothing to do */ connected_free (ifc); - return NULL; + return; } - + + /* Clear the configured flag on the old ifc, so it will be freed by + * connected withdraw. */ UNSET_FLAG(current->conf, ZEBRA_IFC_CONFIGURED); connected_withdraw (current); /* implicit withdraw - freebsd does this */ } - return ifc; + + /* If the connected is new or has changed, announce it, if it is usable */ + if (CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL)) + connected_announce(ifp, ifc); } /* Called from if_up(). */ @@ -189,12 +192,12 @@ connected_up_ipv4 (struct interface *ifp, struct conne return; rib_add_ipv4 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, NULL, ifp->ifindex, - RT_TABLE_MAIN, ifp->metric, 0, SAFI_UNICAST); + ifp->vrf_id, RT_TABLE_MAIN, ifp->metric, 0, 0, SAFI_UNICAST); rib_add_ipv4 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, NULL, ifp->ifindex, - RT_TABLE_MAIN, ifp->metric, 0, SAFI_MULTICAST); + ifp->vrf_id, RT_TABLE_MAIN, ifp->metric, 0, 0, SAFI_MULTICAST); - rib_update (); + rib_update (ifp->vrf_id); } /* Add connected IPv4 route to the interface. */ @@ -210,6 +213,9 @@ connected_add_ipv4 (struct interface *ifp, int flags, ifc = connected_new (); ifc->ifp = ifp; ifc->flags = flags; + /* If we get a notification from the kernel, + * we can safely assume the address is known to the kernel */ + SET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); /* Allocate new connected address. */ p = prefix_ipv4_new (); @@ -272,11 +278,11 @@ connected_add_ipv4 (struct interface *ifp, int flags, if (label) ifc->label = XSTRDUP (MTYPE_CONNECTED_LABEL, label); - /* nothing to do? */ - if ((ifc = connected_implicit_withdraw (ifp, ifc)) == NULL) - return; - - connected_announce (ifp, ifc); + /* For all that I know an IPv4 address is always ready when we receive + * the notification. So it should be safe to set the REAL flag here. */ + SET_FLAG(ifc->conf, ZEBRA_IFC_REAL); + + connected_update(ifp, ifc); } void @@ -298,11 +304,13 @@ connected_down_ipv4 (struct interface *ifp, struct con return; /* Same logic as for connected_up_ipv4(): push the changes into the head. */ - rib_delete_ipv4 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, ifp->ifindex, 0, SAFI_UNICAST); + rib_delete_ipv4 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, ifp->ifindex, ifp->vrf_id, + SAFI_UNICAST); - rib_delete_ipv4 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, ifp->ifindex, 0, SAFI_MULTICAST); + rib_delete_ipv4 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, ifp->ifindex, ifp->vrf_id, + SAFI_MULTICAST); - rib_update (); + rib_update (ifp->vrf_id); } /* Delete connected IPv4 route to the interface. */ @@ -324,7 +332,7 @@ connected_delete_ipv4 (struct interface *ifp, int flag connected_withdraw (ifc); - rib_update(); + rib_update (ifp->vrf_id); } #ifdef HAVE_IPV6 @@ -341,16 +349,16 @@ connected_up_ipv6 (struct interface *ifp, struct conne /* Apply mask to the network. */ apply_mask_ipv6 (&p); -#if ! defined (MUSICA) && ! defined (LINUX) +#ifndef LINUX /* XXX: It is already done by rib_bogus_ipv6 within rib_add_ipv6 */ if (IN6_IS_ADDR_UNSPECIFIED (&p.prefix)) return; #endif - rib_add_ipv6 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, ifp->ifindex, RT_TABLE_MAIN, - ifp->metric, 0, SAFI_UNICAST); + rib_add_ipv6 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, ifp->ifindex, ifp->vrf_id, + RT_TABLE_MAIN, ifp->metric, 0, 0, SAFI_UNICAST); - rib_update (); + rib_update (ifp->vrf_id); } /* Add connected IPv6 route to the interface. */ @@ -366,6 +374,9 @@ connected_add_ipv6 (struct interface *ifp, int flags, ifc = connected_new (); ifc->ifp = ifp; ifc->flags = flags; + /* If we get a notification from the kernel, + * we can safely assume the address is known to the kernel */ + SET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); /* Allocate new connected address. */ p = prefix_ipv6_new (); @@ -400,11 +411,16 @@ connected_add_ipv6 (struct interface *ifp, int flags, /* Label of this address. */ if (label) ifc->label = XSTRDUP (MTYPE_CONNECTED_LABEL, label); - - if ((ifc = connected_implicit_withdraw (ifp, ifc)) == NULL) - return; - - connected_announce (ifp, ifc); + + /* On Linux, we only get here when DAD is complete, therefore we can set + * ZEBRA_IFC_REAL. + * + * On BSD, there currently doesn't seem to be a way to check for completion of + * DAD, so we replicate the old behaviour and set ZEBRA_IFC_REAL, although DAD + * might still be running. + */ + SET_FLAG(ifc->conf, ZEBRA_IFC_REAL); + connected_update(ifp, ifc); } void @@ -422,9 +438,10 @@ connected_down_ipv6 (struct interface *ifp, struct con if (IN6_IS_ADDR_UNSPECIFIED (&p.prefix)) return; - rib_delete_ipv6 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, ifp->ifindex, 0, SAFI_UNICAST); + rib_delete_ipv6 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, ifp->ifindex, ifp->vrf_id, + SAFI_UNICAST); - rib_update (); + rib_update (ifp->vrf_id); } void @@ -445,6 +462,6 @@ connected_delete_ipv6 (struct interface *ifp, struct i connected_withdraw (ifc); - rib_update(); + rib_update (ifp->vrf_id); } #endif /* HAVE_IPV6 */