| /* |
| * Address linked list routine. |
| * Copyright (C) 1997, 98 Kunihiro Ishiguro |
| * |
| * This file is part of GNU Zebra. |
| * |
| * GNU Zebra is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by the |
| * Free Software Foundation; either version 2, or (at your option) any |
| * later version. |
| * |
| * GNU Zebra is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with GNU Zebra; see the file COPYING. If not, write to the Free |
| * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA |
| * 02111-1307, USA. |
| */ |
| |
| #include <zebra.h> |
| |
| #include "prefix.h" |
| #include "linklist.h" |
| #include "if.h" |
| #include "table.h" |
| #include "rib.h" |
| #include "table.h" |
| #include "log.h" |
| #include "memory.h" |
| |
| #include "zebra/zserv.h" |
| #include "zebra/redistribute.h" |
| #include "zebra/interface.h" |
| #include "zebra/connected.h" |
| extern struct zebra_t zebrad; |
| |
| /* communicate the withdrawal of a connected address */ |
| static void |
| connected_withdraw (struct connected *ifc) |
| { |
| if (! ifc) |
| return; |
| |
| /* Update interface address information to protocol daemon. */ |
| if (CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL)) |
| { |
| zebra_interface_address_delete_update (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 |
| connected_down_ipv6 (ifc->ifp, ifc); |
| #endif |
| |
| 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); |
| connected_free (ifc); |
| } |
| } |
| |
| static void |
| connected_announce (struct interface *ifp, struct connected *ifc) |
| { |
| if (!ifc) |
| return; |
| |
| listnode_add (ifp->connected, ifc); |
| |
| /* Update interface address information to protocol daemon. */ |
| if (ifc->address->family == AF_INET) |
| if_subnet_add (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); |
| #ifdef HAVE_IPV6 |
| else |
| connected_up_ipv6 (ifp, ifc); |
| #endif |
| } |
| } |
| |
| /* If same interface address is already exist... */ |
| struct connected * |
| connected_check (struct interface *ifp, struct prefix *p) |
| { |
| struct connected *ifc; |
| struct listnode *node; |
| |
| for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, ifc)) |
| if (prefix_same (ifc->address, p)) |
| return ifc; |
| |
| return NULL; |
| } |
| |
| /* Check if two ifc's describe the same address in the same state */ |
| static int |
| connected_same (struct connected *ifc1, struct connected *ifc2) |
| { |
| if (ifc1->ifp != ifc2->ifp) |
| return 0; |
| |
| if (ifc1->destination) |
| if (!ifc2->destination) |
| return 0; |
| if (ifc2->destination) |
| if (!ifc1->destination) |
| return 0; |
| |
| if (ifc1->destination && ifc2->destination) |
| if (!prefix_same (ifc1->destination, ifc2->destination)) |
| return 0; |
| |
| if (ifc1->flags != ifc2->flags) |
| return 0; |
| |
| if (ifc1->conf != ifc2->conf) |
| return 0; |
| |
| return 1; |
| } |
| |
| /* Handle changes to addresses and send the neccesary announcements |
| * to clients. */ |
| static void |
| connected_update(struct interface *ifp, struct connected *ifc) |
| { |
| struct connected *current; |
| |
| /* Check same connected route. */ |
| if ((current = connected_check (ifp, (struct prefix *) ifc->address))) |
| { |
| if (CHECK_FLAG(current->conf, ZEBRA_IFC_CONFIGURED)) |
| SET_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED); |
| |
| /* Avoid spurious withdraws, this might be just the kernel 'reflecting' |
| * back an address we have already added. |
| */ |
| if (connected_same (current, ifc)) |
| { |
| /* nothing to do */ |
| connected_free (ifc); |
| 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 */ |
| } |
| |
| /* 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(). */ |
| void |
| connected_up_ipv4 (struct interface *ifp, struct connected *ifc) |
| { |
| struct prefix_ipv4 p; |
| |
| if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL)) |
| return; |
| |
| PREFIX_COPY_IPV4(&p, CONNECTED_PREFIX(ifc)); |
| |
| /* Apply mask to the network. */ |
| apply_mask_ipv4 (&p); |
| |
| /* In case of connected address is 0.0.0.0/0 we treat it tunnel |
| address. */ |
| if (prefix_ipv4_any (&p)) |
| return; |
| |
| rib_add_ipv4 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, NULL, ifp->ifindex, |
| ifp->vrf_id, RT_TABLE_MAIN, ifp->metric, 0, 0, SAFI_UNICAST); |
| |
| rib_add_ipv4 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, NULL, ifp->ifindex, |
| ifp->vrf_id, RT_TABLE_MAIN, ifp->metric, 0, 0, SAFI_MULTICAST); |
| |
| rib_update (ifp->vrf_id); |
| } |
| |
| /* Add connected IPv4 route to the interface. */ |
| void |
| connected_add_ipv4 (struct interface *ifp, int flags, struct in_addr *addr, |
| u_char prefixlen, struct in_addr *broad, |
| const char *label) |
| { |
| struct prefix_ipv4 *p; |
| struct connected *ifc; |
| |
| /* Make connected structure. */ |
| 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 (); |
| p->family = AF_INET; |
| p->prefix = *addr; |
| p->prefixlen = prefixlen; |
| ifc->address = (struct prefix *) p; |
| |
| /* If there is broadcast or peer address. */ |
| if (broad) |
| { |
| p = prefix_ipv4_new (); |
| p->family = AF_INET; |
| p->prefix = *broad; |
| p->prefixlen = prefixlen; |
| ifc->destination = (struct prefix *) p; |
| |
| /* validate the destination address */ |
| if (CONNECTED_PEER(ifc)) |
| { |
| if (IPV4_ADDR_SAME(addr,broad)) |
| zlog_warn("warning: interface %s has same local and peer " |
| "address %s, routing protocols may malfunction", |
| ifp->name,inet_ntoa(*addr)); |
| } |
| else |
| { |
| if (broad->s_addr != ipv4_broadcast_addr(addr->s_addr,prefixlen)) |
| { |
| char buf[2][INET_ADDRSTRLEN]; |
| struct in_addr bcalc; |
| bcalc.s_addr = ipv4_broadcast_addr(addr->s_addr,prefixlen); |
| zlog_warn("warning: interface %s broadcast addr %s/%d != " |
| "calculated %s, routing protocols may malfunction", |
| ifp->name, |
| inet_ntop (AF_INET, broad, buf[0], sizeof(buf[0])), |
| prefixlen, |
| inet_ntop (AF_INET, &bcalc, buf[1], sizeof(buf[1]))); |
| } |
| } |
| |
| } |
| else |
| { |
| if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_PEER)) |
| { |
| zlog_warn("warning: %s called for interface %s " |
| "with peer flag set, but no peer address supplied", |
| __func__, ifp->name); |
| UNSET_FLAG(ifc->flags, ZEBRA_IFA_PEER); |
| } |
| |
| /* no broadcast or destination address was supplied */ |
| if ((prefixlen == IPV4_MAX_PREFIXLEN) && if_is_pointopoint(ifp)) |
| zlog_warn("warning: PtP interface %s with addr %s/%d needs a " |
| "peer address",ifp->name,inet_ntoa(*addr),prefixlen); |
| } |
| |
| /* Label of this address. */ |
| if (label) |
| ifc->label = XSTRDUP (MTYPE_CONNECTED_LABEL, label); |
| |
| /* 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 |
| connected_down_ipv4 (struct interface *ifp, struct connected *ifc) |
| { |
| struct prefix_ipv4 p; |
| |
| if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL)) |
| return; |
| |
| PREFIX_COPY_IPV4(&p, CONNECTED_PREFIX(ifc)); |
| |
| /* Apply mask to the network. */ |
| apply_mask_ipv4 (&p); |
| |
| /* In case of connected address is 0.0.0.0/0 we treat it tunnel |
| address. */ |
| if (prefix_ipv4_any (&p)) |
| 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, ifp->vrf_id, |
| SAFI_UNICAST); |
| |
| rib_delete_ipv4 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, ifp->ifindex, ifp->vrf_id, |
| SAFI_MULTICAST); |
| |
| rib_update (ifp->vrf_id); |
| } |
| |
| /* Delete connected IPv4 route to the interface. */ |
| void |
| connected_delete_ipv4 (struct interface *ifp, int flags, struct in_addr *addr, |
| u_char prefixlen, struct in_addr *broad) |
| { |
| struct prefix_ipv4 p; |
| struct connected *ifc; |
| |
| memset (&p, 0, sizeof (struct prefix_ipv4)); |
| p.family = AF_INET; |
| p.prefix = *addr; |
| p.prefixlen = prefixlen; |
| |
| ifc = connected_check (ifp, (struct prefix *) &p); |
| if (! ifc) |
| return; |
| |
| connected_withdraw (ifc); |
| |
| rib_update (ifp->vrf_id); |
| } |
| |
| #ifdef HAVE_IPV6 |
| void |
| connected_up_ipv6 (struct interface *ifp, struct connected *ifc) |
| { |
| struct prefix_ipv6 p; |
| |
| if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL)) |
| return; |
| |
| PREFIX_COPY_IPV6(&p, CONNECTED_PREFIX(ifc)); |
| |
| /* Apply mask to the network. */ |
| apply_mask_ipv6 (&p); |
| |
| #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, ifp->vrf_id, |
| RT_TABLE_MAIN, ifp->metric, 0, 0, SAFI_UNICAST); |
| |
| rib_update (ifp->vrf_id); |
| } |
| |
| /* Add connected IPv6 route to the interface. */ |
| void |
| connected_add_ipv6 (struct interface *ifp, int flags, struct in6_addr *addr, |
| u_char prefixlen, struct in6_addr *broad, |
| const char *label) |
| { |
| struct prefix_ipv6 *p; |
| struct connected *ifc; |
| |
| /* Make connected structure. */ |
| 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 (); |
| p->family = AF_INET6; |
| IPV6_ADDR_COPY (&p->prefix, addr); |
| p->prefixlen = prefixlen; |
| ifc->address = (struct prefix *) p; |
| |
| /* If there is broadcast or peer address. */ |
| if (broad) |
| { |
| if (IN6_IS_ADDR_UNSPECIFIED(broad)) |
| zlog_warn("warning: %s called for interface %s with unspecified " |
| "destination address; ignoring!", __func__, ifp->name); |
| else |
| { |
| p = prefix_ipv6_new (); |
| p->family = AF_INET6; |
| IPV6_ADDR_COPY (&p->prefix, broad); |
| p->prefixlen = prefixlen; |
| ifc->destination = (struct prefix *) p; |
| } |
| } |
| if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_PEER) && !ifc->destination) |
| { |
| zlog_warn("warning: %s called for interface %s " |
| "with peer flag set, but no peer address supplied", |
| __func__, ifp->name); |
| UNSET_FLAG(ifc->flags, ZEBRA_IFA_PEER); |
| } |
| |
| /* Label of this address. */ |
| if (label) |
| ifc->label = XSTRDUP (MTYPE_CONNECTED_LABEL, label); |
| |
| /* 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 |
| connected_down_ipv6 (struct interface *ifp, struct connected *ifc) |
| { |
| struct prefix_ipv6 p; |
| |
| if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL)) |
| return; |
| |
| PREFIX_COPY_IPV6(&p, CONNECTED_PREFIX(ifc)); |
| |
| apply_mask_ipv6 (&p); |
| |
| if (IN6_IS_ADDR_UNSPECIFIED (&p.prefix)) |
| return; |
| |
| rib_delete_ipv6 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, ifp->ifindex, ifp->vrf_id, |
| SAFI_UNICAST); |
| |
| rib_update (ifp->vrf_id); |
| } |
| |
| void |
| connected_delete_ipv6 (struct interface *ifp, struct in6_addr *address, |
| u_char prefixlen, struct in6_addr *broad) |
| { |
| struct prefix_ipv6 p; |
| struct connected *ifc; |
| |
| memset (&p, 0, sizeof (struct prefix_ipv6)); |
| p.family = AF_INET6; |
| memcpy (&p.prefix, address, sizeof (struct in6_addr)); |
| p.prefixlen = prefixlen; |
| |
| ifc = connected_check (ifp, (struct prefix *) &p); |
| if (! ifc) |
| return; |
| |
| connected_withdraw (ifc); |
| |
| rib_update (ifp->vrf_id); |
| } |
| #endif /* HAVE_IPV6 */ |