summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorglebius <glebius@FreeBSD.org>2012-12-25 13:01:58 +0000
committerglebius <glebius@FreeBSD.org>2012-12-25 13:01:58 +0000
commit9f622a1b388500854580966c32199975628916ba (patch)
tree37cd38db2ea47a2a10776b73e14235ded4b8e0b5 /sys
parentaa703b25c09ae59c8f17176570221deed1999284 (diff)
downloadFreeBSD-src-9f622a1b388500854580966c32199975628916ba.zip
FreeBSD-src-9f622a1b388500854580966c32199975628916ba.tar.gz
The SIOCSIFFLAGS ioctl handler runs if_up()/if_down() that notify
all interested parties in case if interface flag IFF_UP has changed. However, not only SIOCSIFFLAGS can raise the flag, but SIOCAIFADDR and SIOCAIFADDR_IN6 can, too. The actual |= is done not in the protocol code, but in code of interface drivers. To fix this historical layering violation, we will check whether ifp->if_ioctl(SIOCSIFADDR) raised the IFF_UP flag, and if it did, run the if_up() handler. This fixes configuring an address under CARP control on an interface that was initially !IFF_UP. P.S. I intentionally omitted handling the IFF_SMART flag. This flag was never ever used in any driver since it was introduced, and since it means another layering violation, it should be garbage collected instead of pretended to be supported.
Diffstat (limited to 'sys')
-rw-r--r--sys/netinet/in.c11
-rw-r--r--sys/netinet6/in6.c9
2 files changed, 17 insertions, 3 deletions
diff --git a/sys/netinet/in.c b/sys/netinet/in.c
index 2b805c6..6cb37e5 100644
--- a/sys/netinet/in.c
+++ b/sys/netinet/in.c
@@ -819,14 +819,19 @@ in_ifinit(struct ifnet *ifp, struct in_ifaddr *ia, struct sockaddr_in *sin,
return (error);
/*
- * Give the interface a chance to initialize
- * if this is its first address,
- * and to validate the address if necessary.
+ * Give the interface a chance to initialize if this is its first
+ * address, and to validate the address if necessary.
+ *
+ * Historically, drivers managed IFF_UP flag theirselves, so we
+ * need to check whether driver did that.
*/
+ flags = ifp->if_flags;
if (ifp->if_ioctl != NULL &&
(error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia)) != 0)
/* LIST_REMOVE(ia, ia_hash) is done in in_control */
return (error);
+ if ((ifp->if_flags & IFF_UP) && (flags & IFF_UP) == 0)
+ if_up(ifp);
/*
* Be compatible with network classes, if netmask isn't supplied,
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c
index e260e5d..08dd259 100644
--- a/sys/netinet6/in6.c
+++ b/sys/netinet6/in6.c
@@ -1874,9 +1874,18 @@ in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia,
ia->ia_addr = *sin6;
if (ifacount <= 1 && ifp->if_ioctl) {
+ int flags;
+
+ /*
+ * Historically, drivers managed IFF_UP flag theirselves, so we
+ * need to check whether driver did that.
+ */
+ flags = ifp->if_flags;
error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia);
if (error)
return (error);
+ if ((ifp->if_flags & IFF_UP) && (flags & IFF_UP) == 0)
+ if_up(ifp);
}
ia->ia_ifa.ifa_metric = ifp->if_metric;
OpenPOWER on IntegriCloud