[zebra/solaris] Interface state fixups for Solaris.
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.
diff --git a/zebra/ioctl_solaris.c b/zebra/ioctl_solaris.c
index ec1d2c4..2f05bf1 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 @@
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 (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 @@
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 @@
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 @@
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 @@
/* 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;