diff options
-rw-r--r-- | zebra/ChangeLog | 55 | ||||
-rw-r--r-- | zebra/if_ioctl_solaris.c | 20 | ||||
-rw-r--r-- | zebra/interface.c | 86 | ||||
-rw-r--r-- | zebra/interface.h | 9 | ||||
-rw-r--r-- | zebra/ioctl.c | 7 | ||||
-rw-r--r-- | zebra/ioctl.h | 4 | ||||
-rw-r--r-- | zebra/ioctl_solaris.c | 91 | ||||
-rw-r--r-- | zebra/kernel_socket.c | 76 | ||||
-rw-r--r-- | zebra/rt.h | 1 |
9 files changed, 257 insertions, 92 deletions
diff --git a/zebra/ChangeLog b/zebra/ChangeLog index b40f78fb..fab26480 100644 --- a/zebra/ChangeLog +++ b/zebra/ChangeLog @@ -1,3 +1,58 @@ +2006-01-25 Paul Jakma <paul.jakma@sun.com> + + * (general) More solaris PF_ROUTE hacks. The IFF_UP mangling + for solaris was incomplete on the PF_ROUTE side. fix it. + This changeset generally uglifies things. For some future + work I'd like to see the state changes seperated out from + the details of the code. Differences between systems might + then be slightly easier to implement without convoluted + hacks. + Changes should be specific to Solaris mostly, however + also tested on FreeBSD 6. + * if_ioctl_solaris.c: (interface_list_ioctl) ignore ~IFF_UP + interfaces, we'll hear about them when/if interface goes up + through NEWADDR. + Update flags explicitely at end of it to kick mangling. + * ioctl_solaris.c: (if_mangle_up) removed to interface.c, in + kind. + (lifreq_set_name) more convenient to take the string, than + the ifp. + (if_get_flags_direct) new convenience function, returns + the actual flags. Used during bootstrap in if_ioctl_solaris.c + to peek at flags of logical interfaces to see whether or + not to ignore them. + (if_get_flags) ENXIO means it's gone, poke out IFF_UP and + kick flags update. + (if_{un,}set_flags) flags argument should be 64bit. + * ioctl.{c,h}: flags argument should be 64bit. + * interface.h: Add a 'primary_state' flag to struct zebra_if on + SUNOS_5. + Export if_flags_update. + * interface.c: (if_flags_mangle) moved over in kind from + ioctl_solaris.c. Nasty kludge to try get IFF_UP right, as + much as is possible. Also keep track of the actual IFF_UP + value for the primary interface, so we can know when the ifp + must be deleted. + (if_flags_update) Take a new interface flags value, apply it + to the interface, and take whatever actions are required due + to flag transitions. + (if_refresh) flag state change logic is moved out to + previous. Just call if_get_flags, which will end up using + previous to effect the update of flags. + (if_flag_dump_vty) IFF_IPV{4,6} aren't interesting, VIRTUAL + and NOXMIT are though. + * kernel_socket.c: (ifm_read) Down->Down transitions shouldn't + create ifp, for non-IFANNOUNCE systems. + Use if_flags_update to update flags. + flag transition logic is now handled automatically through + if_flags_update. + (ifam_read) Better to call if_refresh *after* adding + connected addresses, as connected count affects IFF_UP on + IFF_UP-mangled systems. + On Solaris, Up->Down due to DELADDR means we need to delete + the ifp - the IFINFO might already have been and gone. + * rt.h: include other dependent headers. + 2006-01-19 Paul Jakma <paul.jakma@sun.com> * (general) various miscellaneous compiler warning fixes. diff --git a/zebra/if_ioctl_solaris.c b/zebra/if_ioctl_solaris.c index a671518e..095bcab6 100644 --- a/zebra/if_ioctl_solaris.c +++ b/zebra/if_ioctl_solaris.c @@ -33,7 +33,8 @@ #include "zebra/interface.h" -void lifreq_set_name (struct lifreq *, struct interface *); +void lifreq_set_name (struct lifreq *, const char *); +int if_get_flags_direct (const char *, uint64_t *, unsigned int af); static int if_get_addr (struct interface *, struct sockaddr *, const char *); static void interface_info_ioctl (struct interface *); extern struct zebra_privs_t zserv_privs; @@ -147,7 +148,20 @@ calculate_lifc_len: /* must hold privileges to enter here */ * <interface name>:<logical interface id> */ unsigned int normallen = 0; + uint64_t lifflags; + /* We should exclude ~IFF_UP interfaces, as we'll find out about them + * coming up later through RTM_NEWADDR message on the route socket. + */ + if (if_get_flags_direct (lifreq->lifr_name, &lifflags, + lifreq->lifr_addr.ss_family) + || !CHECK_FLAG (lifflags, IFF_UP)) + { + lifreq++; + continue; + } + + /* Find the normalised name */ while ( (normallen < sizeof(lifreq->lifr_name)) && ( *(lifreq->lifr_name + normallen) != '\0') && ( *(lifreq->lifr_name + normallen) != ':') ) @@ -180,6 +194,10 @@ calculate_lifc_len: /* must hold privileges to enter here */ lifreq->lifr_name); else if_get_addr (ifp, (struct sockaddr *) &lifreq->lifr_addr, NULL); + + /* Poke the interface flags. Lets IFF_UP mangling kick in */ + if_flags_update (ifp, ifp->flags); + lifreq++; } diff --git a/zebra/interface.c b/zebra/interface.c index 5f9c7a23..d4266f59 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -187,6 +187,73 @@ if_subnet_delete (struct interface *ifp, struct connected *ifc) return 0; } +/* if_flags_mangle: A place for hacks that require mangling + * or tweaking the interface flags. + * + * ******************** Solaris flags hacks ************************** + * + * Solaris IFF_UP flag reflects only the primary interface as the + * routing socket only sends IFINFO for the primary interface. Hence + * ~IFF_UP does not per se imply all the logical interfaces are also + * down - which we only know of as addresses. Instead we must determine + * whether the interface really is up or not according to how many + * addresses are still attached. (Solaris always sends RTM_DELADDR if + * an interface, logical or not, goes ~IFF_UP). + * + * Ie, we mangle IFF_UP to *additionally* reflect whether or not there + * are addresses left in struct connected, not just the actual underlying + * IFF_UP flag. + * + * We must hence remember the real state of IFF_UP, which we do in + * struct zebra_if.primary_state. + * + * Setting IFF_UP within zebra to administratively shutdown the + * interface will affect only the primary interface/address on Solaris. + ************************End Solaris flags hacks *********************** + */ +static inline void +if_flags_mangle (struct interface *ifp, uint64_t *newflags) +{ +#ifdef SUNOS_5 + struct zebra_if *zif = ifp->info; + + zif->primary_state = *newflags & (IFF_UP & 0xff); + + if (CHECK_FLAG (zif->primary_state, IFF_UP) + || listcount(ifp->connected) > 0) + SET_FLAG (*newflags, IFF_UP); + else + UNSET_FLAG (*newflags, IFF_UP); +#endif /* SUNOS_5 */ +} + +/* Update the flags field of the ifp with the new flag set provided. + * Take whatever actions are required for any changes in flags we care + * about. + * + * newflags should be the raw value, as obtained from the OS. + */ +void +if_flags_update (struct interface *ifp, uint64_t newflags) +{ + if_flags_mangle (ifp, &newflags); + + if (if_is_operative (ifp)) + { + /* operative -> inoperative? */ + ifp->flags = newflags; + if (!if_is_operative (ifp)) + if_down (ifp); + } + else + { + /* inoperative -> operative? */ + ifp->flags = newflags; + if (if_is_operative (ifp)) + if_up (ifp); + } +} + /* Wake up configured address if it is not in current kernel address. */ static void @@ -478,23 +545,12 @@ if_down (struct interface *ifp) void if_refresh (struct interface *ifp) { - if (if_is_operative (ifp)) - { - if_get_flags (ifp); - if (! if_is_operative (ifp)) - if_down (ifp); - } - else - { - if_get_flags (ifp); - if (if_is_operative (ifp)) - if_up (ifp); - } + if_get_flags (ifp); } /* Printout flag information into vty */ static void -if_flag_dump_vty (struct vty *vty, unsigned long flag) +if_flag_dump_vty (struct vty *vty, uint64_t flag) { int separator = 0; @@ -526,8 +582,8 @@ if_flag_dump_vty (struct vty *vty, unsigned long flag) IFF_OUT_VTY (IFF_LINK2, "LINK2"); IFF_OUT_VTY (IFF_MULTICAST, "MULTICAST"); #ifdef SOLARIS_IPV6 - IFF_OUT_VTY (IFF_IPV4, "IFF_IPv4"); - IFF_OUT_VTY (IFF_IPV6, "IFF_IPv6"); + IFF_OUT_VTY (IFF_VIRTUAL, "IFF_VIRTUAL"); + IFF_OUT_VTY (IFF_NOXMIT, "IFF_NOXMIT"); #endif /* SOLARIS_IPV6 */ vty_out (vty, ">"); } diff --git a/zebra/interface.h b/zebra/interface.h index 0083cbab..9a69dfa3 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -201,6 +201,14 @@ struct zebra_if struct irdp_interface irdp; #endif +#ifdef SUNOS_5 + /* the real IFF_UP state of the primary interface. + * need this to differentiate between all interfaces being + * down (but primary still plumbed) and primary having gone + * ~IFF_UP, and all addresses gone. + */ + u_char primary_state; +#endif /* SUNOS_5 */ }; extern void if_delete_update (struct interface *ifp); @@ -208,6 +216,7 @@ extern void if_add_update (struct interface *ifp); extern void if_up (struct interface *); extern void if_down (struct interface *); extern void if_refresh (struct interface *); +extern void if_flags_update (struct interface *, uint64_t); extern int if_subnet_add (struct interface *, struct connected *); extern int if_subnet_delete (struct interface *, struct connected *); diff --git a/zebra/ioctl.c b/zebra/ioctl.c index 4137acfa..06be5bec 100644 --- a/zebra/ioctl.c +++ b/zebra/ioctl.c @@ -31,6 +31,7 @@ #include "zebra/rib.h" #include "zebra/rt.h" +#include "zebra/interface.h" extern struct zebra_privs_t zserv_privs; @@ -350,12 +351,12 @@ if_get_flags (struct interface *ifp) return; } - ifp->flags = ifreq.ifr_flags & 0x0000ffff; + if_flags_update (ifp, (ifreq.ifr_flags & 0x0000ffff)); } /* Set interface flags */ int -if_set_flags (struct interface *ifp, unsigned long flags) +if_set_flags (struct interface *ifp, uint64_t flags) { int ret; struct ifreq ifreq; @@ -378,7 +379,7 @@ if_set_flags (struct interface *ifp, unsigned long flags) /* Unset interface's flag. */ int -if_unset_flags (struct interface *ifp, unsigned long flags) +if_unset_flags (struct interface *ifp, uint64_t flags) { int ret; struct ifreq ifreq; diff --git a/zebra/ioctl.h b/zebra/ioctl.h index 5d9e09f0..fee9b725 100644 --- a/zebra/ioctl.h +++ b/zebra/ioctl.h @@ -27,8 +27,8 @@ extern void ifreq_set_name (struct ifreq *, struct interface *); extern int if_ioctl (u_long, caddr_t); -extern int if_set_flags (struct interface *, unsigned long); -extern int if_unset_flags (struct interface *, unsigned long); +extern int if_set_flags (struct interface *, uint64_t); +extern int if_unset_flags (struct interface *, uint64_t); extern void if_get_flags (struct interface *); extern int if_set_prefix (struct interface *, struct connected *); diff --git a/zebra/ioctl_solaris.c b/zebra/ioctl_solaris.c index ec1d2c44..2f05bf12 100644 --- a/zebra/ioctl_solaris.c +++ b/zebra/ioctl_solaris.c @@ -31,14 +31,15 @@ #include "zebra/rib.h" #include "zebra/rt.h" +#include "zebra/interface.h" extern struct zebra_privs_t zserv_privs; /* clear and set interface name string */ void -lifreq_set_name (struct lifreq *lifreq, struct interface *ifp) +lifreq_set_name (struct lifreq *lifreq, const char *ifname) { - strncpy (lifreq->lifr_name, ifp->name, IFNAMSIZ); + strncpy (lifreq->lifr_name, ifname, IFNAMSIZ); } /* call ioctl system call */ @@ -129,7 +130,7 @@ if_get_metric (struct interface *ifp) struct lifreq lifreq; int ret; - lifreq_set_name (&lifreq, ifp); + lifreq_set_name (&lifreq, ifp->name); if (ifp->flags & IFF_IPV4) ret = AF_IOCTL (AF_INET, SIOCGLIFMETRIC, (caddr_t) & lifreq); @@ -158,7 +159,7 @@ if_get_mtu (struct interface *ifp) if (ifp->flags & IFF_IPV4) { - lifreq_set_name (&lifreq, ifp); + lifreq_set_name (&lifreq, ifp->name); ret = AF_IOCTL (AF_INET, SIOCGLIFMTU, (caddr_t) & lifreq); if (ret < 0) { @@ -177,7 +178,7 @@ if_get_mtu (struct interface *ifp) return; memset(&lifreq, 0, sizeof(lifreq)); - lifreq_set_name (&lifreq, ifp); + lifreq_set_name (&lifreq, ifp->name); ret = AF_IOCTL (AF_INET6, SIOCGLIFMTU, (caddr_t) & lifreq); if (ret < 0) @@ -274,28 +275,28 @@ if_unset_prefix (struct interface *ifp, struct connected *ifc) return 0; } -/* Solaris IFF_UP flag reflects only the primary interface as the - * routing socket only sends IFINFO for the primary interface. Hence - * ~IFF_UP does not per se imply all the logical interfaces are also - * down - which we only know of as addresses. Instead we must determine - * whether the interface really is up or not according to how many - * addresses are still attached. (Solaris always sends RTM_DELADDR if - * an interface, logical or not, goes ~IFF_UP). - * - * Ie, we mangle IFF_UP to reflect whether or not there are addresses - * left in struct connected, not the actual underlying IFF_UP flag - * (which corresponds to just one address of all the logical interfaces) - * - * Setting IFF_UP within zebra to administratively shutdown the - * interface will affect only the primary interface/address on Solaris. +/* Get just the flags for the given name. + * Used by the normal 'if_get_flags' function, as well + * as the bootup interface-list code, which has to peek at per-address + * flags in order to figure out which ones should be ignored.. */ -static inline void -if_mangle_up (struct interface *ifp) +int +if_get_flags_direct (const char *ifname, uint64_t *flags, unsigned int af) { - if (listcount(ifp->connected) > 0) - SET_FLAG (ifp->flags, IFF_UP); - else - UNSET_FLAG (ifp->flags, IFF_UP); + struct lifreq lifreq; + int ret; + + lifreq_set_name (&lifreq, ifname); + + ret = AF_IOCTL (af, SIOCGLIFFLAGS, (caddr_t) &lifreq); + + if (ret) + zlog_debug ("%s: ifname %s, error %s (%d)", + __func__, ifname, safe_strerror (errno), errno); + + *flags = lifreq.lifr_flags; + + return ret; } /* get interface flags */ @@ -303,44 +304,50 @@ void if_get_flags (struct interface *ifp) { int ret4, ret6; - struct lifreq lifreq; - unsigned long flags4 = 0, flags6 = 0; + uint64_t newflags = 0; + uint64_t tmpflags; if (ifp->flags & IFF_IPV4) { - lifreq_set_name (&lifreq, ifp); - - ret4 = AF_IOCTL (AF_INET, SIOCGLIFFLAGS, (caddr_t) & lifreq); + ret4 = if_get_flags_direct (ifp->name, &tmpflags, AF_INET); if (!ret4) - flags4 = (lifreq.lifr_flags & 0xffffffff); + newflags |= tmpflags; + else if (errno == ENXIO) + { + /* it's gone */ + UNSET_FLAG (ifp->flags, IFF_UP); + if_flags_update (ifp, ifp->flags); + } } if (ifp->flags & IFF_IPV6) { - lifreq_set_name (&lifreq, ifp); - - ret6 = AF_IOCTL (AF_INET6, SIOCGLIFFLAGS, (caddr_t) & lifreq); + ret6 = if_get_flags_direct (ifp->name, &tmpflags, AF_INET6); if (!ret6) - flags6 = (lifreq.lifr_flags & 0xffffffff); + newflags |= tmpflags; + else if (errno == ENXIO) + { + /* it's gone */ + UNSET_FLAG (ifp->flags, IFF_UP); + if_flags_update (ifp, ifp->flags); + } } /* only update flags if one of above succeeded */ if ( !(ret4 && ret6) ) - ifp->flags = (flags4 | flags6); - - if_mangle_up (ifp); + if_flags_update (ifp, newflags); } /* Set interface flags */ int -if_set_flags (struct interface *ifp, unsigned long flags) +if_set_flags (struct interface *ifp, uint64_t flags) { int ret; struct lifreq lifreq; - lifreq_set_name (&lifreq, ifp); + lifreq_set_name (&lifreq, ifp->name); lifreq.lifr_flags = ifp->flags; lifreq.lifr_flags |= flags; @@ -363,12 +370,12 @@ if_set_flags (struct interface *ifp, unsigned long flags) /* Unset interface's flag. */ int -if_unset_flags (struct interface *ifp, unsigned long flags) +if_unset_flags (struct interface *ifp, uint64_t flags) { int ret; struct lifreq lifreq; - lifreq_set_name (&lifreq, ifp); + lifreq_set_name (&lifreq, ifp->name); lifreq.lifr_flags = ifp->flags; lifreq.lifr_flags &= ~flags; diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index ae19ef8f..9764cbb3 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -396,6 +396,14 @@ ifm_read (struct if_msghdr *ifm) ifm->ifm_index); return -1; } + +#ifndef RTM_IFANNOUNCE + /* Down->Down interface should be ignored here. + * See further comment below. + */ + if (!CHECK_FLAG (ifm->ifm_flags, IFF_UP)) + return 0; +#endif /* !RTM_IFANNOUNCE */ if (ifp == NULL) { @@ -414,7 +422,7 @@ ifm_read (struct if_msghdr *ifm) * structure with ifindex IFINDEX_INTERNAL. */ ifp->ifindex = ifm->ifm_index; - ifp->flags = ifm->ifm_flags; + if_flags_update (ifp, ifm->ifm_flags); #if defined(__bsdi__) if_kvm_get_mtu (ifp); #else @@ -441,34 +449,26 @@ ifm_read (struct if_msghdr *ifm) return -1; } - if (if_is_up (ifp)) - { - ifp->flags = ifm->ifm_flags; - if (! if_is_up (ifp)) - { - if_down (ifp); + /* update flags and handle operative->inoperative transition, if any */ + if_flags_update (ifp, ifm->ifm_flags); + #ifndef RTM_IFANNOUNCE - /* No RTM_IFANNOUNCE on this platform, so we can never - * distinguish between down and delete. We must presume - * it has been deleted. - * Eg, Solaris will not notify us of unplumb. - * - * XXX: Fixme - this should be runtime detected - * So that a binary compiled on a system with IFANNOUNCE - * will still behave correctly if run on a platform without - */ - if_delete_update (ifp); + if (!if_is_up (ifp)) + { + /* No RTM_IFANNOUNCE on this platform, so we can never + * distinguish between ~IFF_UP and delete. We must presume + * it has been deleted. + * Eg, Solaris will not notify us of unplumb. + * + * XXX: Fixme - this should be runtime detected + * So that a binary compiled on a system with IFANNOUNCE + * will still behave correctly if run on a platform without + */ + if_delete_update (ifp); + } #endif /* RTM_IFANNOUNCE */ - } - } - else - { - ifp->flags = ifm->ifm_flags; - if (if_is_up (ifp)) - if_up (ifp); - } } - + #ifdef HAVE_NET_RT_IFLIST ifp->stats = ifm->ifm_data; #endif /* HAVE_NET_RT_IFLIST */ @@ -546,9 +546,6 @@ ifam_read (struct ifa_msghdr *ifam) ifp->metric = ifam->ifam_metric; - /* Check interface flag for implicit up of the interface. */ - if_refresh (ifp); - /* Add connected address. */ switch (sockunion_family (&addr)) { @@ -587,6 +584,27 @@ ifam_read (struct ifa_msghdr *ifam) /* Unsupported family silently ignore... */ break; } + + /* Check interface flag for implicit up of the interface. */ + if_refresh (ifp); + +#ifdef SUNOS_5 + /* In addition to lacking IFANNOUNCE, on SUNOS IFF_UP is strange. + * See comments for SUNOS_5 in interface.c::if_flags_mangle. + * + * Here we take care of case where the real IFF_UP was previously + * unset (as kept in struct zebra_if.primary_state) and the mangled + * IFF_UP (ie IFF_UP set || listcount(connected) has now transitioned + * to unset due to the lost non-primary address having DELADDR'd. + * + * we must delete the interface, because in between here and next + * event for this interface-name the administrator could unplumb + * and replumb the interface. + */ + if (!if_is_up (ifp)) + if_delete_update (ifp); +#endif /* SUNOS_5 */ + return 0; } @@ -25,6 +25,7 @@ #include "prefix.h" #include "if.h" +#include "zebra/rib.h" extern int kernel_add_ipv4 (struct prefix *, struct rib *); extern int kernel_delete_ipv4 (struct prefix *, struct rib *); |