summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--zebra/ChangeLog55
-rw-r--r--zebra/if_ioctl_solaris.c20
-rw-r--r--zebra/interface.c86
-rw-r--r--zebra/interface.h9
-rw-r--r--zebra/ioctl.c7
-rw-r--r--zebra/ioctl.h4
-rw-r--r--zebra/ioctl_solaris.c91
-rw-r--r--zebra/kernel_socket.c76
-rw-r--r--zebra/rt.h1
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;
}
diff --git a/zebra/rt.h b/zebra/rt.h
index 82747d3d..8bfe5a42 100644
--- a/zebra/rt.h
+++ b/zebra/rt.h
@@ -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 *);