svn commit: r257692 - head/sys/netinet
Gleb Smirnoff
glebius at FreeBSD.org
Tue Nov 5 07:44:15 UTC 2013
Author: glebius
Date: Tue Nov 5 07:44:15 2013
New Revision: 257692
URL: http://svnweb.freebsd.org/changeset/base/257692
Log:
Rewrite in_control(), so that it is comprehendable without getting mad.
o Provide separate functions for SIOCAIFADDR and for SIOCDIFADDR, with
clear code flow from beginning to the end. After that the rest of
in_control() gets very small and clear.
o Provide sx(9) lock to protect against parallel ioctl() invocations.
o Reimplement logic from r201282, that tried to keep localhost route in
table when multiple P2P interfaces with same local address are created
and deleted.
Discussed with: pluknet, melifaro
Sponsored by: Netflix
Sponsored by: Nginx, Inc.
Modified:
head/sys/netinet/in.c
Modified: head/sys/netinet/in.c
==============================================================================
--- head/sys/netinet/in.c Tue Nov 5 07:37:35 2013 (r257691)
+++ head/sys/netinet/in.c Tue Nov 5 07:44:15 2013 (r257692)
@@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$");
#include <sys/proc.h>
#include <sys/sysctl.h>
#include <sys/syslog.h>
+#include <sys/sx.h>
#include <net/if.h>
#include <net/if_var.h>
@@ -71,10 +72,10 @@ static int in_mask2len(struct in_addr *)
static void in_len2mask(struct in_addr *, int);
static int in_lifaddr_ioctl(struct socket *, u_long, caddr_t,
struct ifnet *, struct thread *);
+static int in_aifaddr_ioctl(caddr_t, struct ifnet *, struct thread *);
+static int in_difaddr_ioctl(caddr_t, struct ifnet *, struct thread *);
static void in_socktrim(struct sockaddr_in *);
-static int in_ifinit(struct ifnet *, struct in_ifaddr *,
- struct sockaddr_in *, int, int);
static void in_purgemaddrs(struct ifnet *);
static VNET_DEFINE(int, nosameprefix);
@@ -86,6 +87,9 @@ SYSCTL_VNET_INT(_net_inet_ip, OID_AUTO,
VNET_DECLARE(struct inpcbinfo, ripcbinfo);
#define V_ripcbinfo VNET(ripcbinfo)
+static struct sx in_control_sx;
+SX_SYSINIT(in_control_sx, &in_control_sx, "in_control");
+
/*
* Return 1 if an internet address is for a ``local'' host
* (one to which we have a connection).
@@ -128,6 +132,29 @@ in_localip(struct in_addr in)
}
/*
+ * Return a reference to the interface address which is different to
+ * the supplied one but with same IP address value.
+ */
+static struct in_ifaddr *
+in_localip_more(struct in_ifaddr *ia)
+{
+ in_addr_t in = IA_SIN(ia)->sin_addr.s_addr;
+ struct in_ifaddr *it;
+
+ IN_IFADDR_RLOCK();
+ LIST_FOREACH(it, INADDR_HASH(in), ia_hash) {
+ if (it != ia && IA_SIN(it)->sin_addr.s_addr == in) {
+ ifa_ref(&it->ia_ifa);
+ IN_IFADDR_RUNLOCK();
+ return (it);
+ }
+ }
+ IN_IFADDR_RUNLOCK();
+
+ return (NULL);
+}
+
+/*
* Determine whether an IP address is in a reserved set of addresses
* that may not be forwarded, or whether datagrams to that destination
* may be forwarded.
@@ -203,380 +230,396 @@ in_len2mask(struct in_addr *mask, int le
/*
* Generic internet control operations (ioctl's).
- *
- * ifp is NULL if not an interface-specific ioctl.
*/
-/* ARGSUSED */
int
in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp,
struct thread *td)
{
- register struct ifreq *ifr = (struct ifreq *)data;
- register struct in_ifaddr *ia, *iap;
- register struct ifaddr *ifa;
- struct in_addr allhosts_addr;
- struct in_addr dst;
- struct in_ifinfo *ii;
- struct in_aliasreq *ifra = (struct in_aliasreq *)data;
- int error, hostIsNew, iaIsNew, maskIsNew;
- int iaIsFirst;
- u_long ocmd = cmd;
-
- /*
- * Pre-10.x compat: OSIOCAIFADDR passes a shorter
- * struct in_aliasreq, without ifra_vhid.
- */
- if (cmd == OSIOCAIFADDR)
- cmd = SIOCAIFADDR;
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct sockaddr_in *addr = (struct sockaddr_in *)&ifr->ifr_addr;
+ struct in_ifaddr *ia;
+ int error;
- ia = NULL;
- iaIsFirst = 0;
- iaIsNew = 0;
- allhosts_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
+ if (ifp == NULL)
+ return (EADDRNOTAVAIL);
/*
- * Filter out ioctls we implement directly; forward the rest on to
- * in_lifaddr_ioctl() and ifp->if_ioctl().
+ * Filter out 4 ioctls we implement directly. Forward the rest
+ * to specific functions and ifp->if_ioctl().
*/
switch (cmd) {
case SIOCGIFADDR:
case SIOCGIFBRDADDR:
case SIOCGIFDSTADDR:
case SIOCGIFNETMASK:
- case SIOCDIFADDR:
break;
+ case SIOCDIFADDR:
+ sx_xlock(&in_control_sx);
+ error = in_difaddr_ioctl(data, ifp, td);
+ sx_xunlock(&in_control_sx);
+ return (error);
case SIOCAIFADDR:
- /*
- * ifra_addr must be present and be of INET family.
- * ifra_broadaddr and ifra_mask are optional.
- */
- if (ifra->ifra_addr.sin_len != sizeof(struct sockaddr_in) ||
- ifra->ifra_addr.sin_family != AF_INET)
- return (EINVAL);
- if (ifra->ifra_broadaddr.sin_len != 0 &&
- (ifra->ifra_broadaddr.sin_len !=
- sizeof(struct sockaddr_in) ||
- ifra->ifra_broadaddr.sin_family != AF_INET))
- return (EINVAL);
-#if 0
- /*
- * ifconfig(8) in pre-10.x doesn't set sin_family for the
- * mask. The code is disabled for the 10.x timeline, to
- * make SIOCAIFADDR compatible with 9.x ifconfig(8).
- * The code should be enabled in 11.x
- */
- if (ifra->ifra_mask.sin_len != 0 &&
- (ifra->ifra_mask.sin_len != sizeof(struct sockaddr_in) ||
- ifra->ifra_mask.sin_family != AF_INET))
- return (EINVAL);
-#endif
- break;
+ sx_xlock(&in_control_sx);
+ error = in_aifaddr_ioctl(data, ifp, td);
+ sx_xunlock(&in_control_sx);
+ return (error);
+ case SIOCALIFADDR:
+ case SIOCDLIFADDR:
+ case SIOCGLIFADDR:
+ return (in_lifaddr_ioctl(so, cmd, data, ifp, td));
case SIOCSIFADDR:
case SIOCSIFBRDADDR:
case SIOCSIFDSTADDR:
case SIOCSIFNETMASK:
/* We no longer support that old commands. */
return (EINVAL);
-
- case SIOCALIFADDR:
- if (td != NULL) {
- error = priv_check(td, PRIV_NET_ADDIFADDR);
- if (error)
- return (error);
- }
- if (ifp == NULL)
- return (EINVAL);
- return in_lifaddr_ioctl(so, cmd, data, ifp, td);
-
- case SIOCDLIFADDR:
- if (td != NULL) {
- error = priv_check(td, PRIV_NET_DELIFADDR);
- if (error)
- return (error);
- }
- if (ifp == NULL)
- return (EINVAL);
- return in_lifaddr_ioctl(so, cmd, data, ifp, td);
-
- case SIOCGLIFADDR:
- if (ifp == NULL)
- return (EINVAL);
- return in_lifaddr_ioctl(so, cmd, data, ifp, td);
-
default:
- if (ifp == NULL || ifp->if_ioctl == NULL)
+ if (ifp->if_ioctl == NULL)
return (EOPNOTSUPP);
return ((*ifp->if_ioctl)(ifp, cmd, data));
}
- if (ifp == NULL)
- return (EADDRNOTAVAIL);
-
- /*
- * Security checks before we get involved in any work.
- */
- switch (cmd) {
- case SIOCAIFADDR:
- if (td != NULL) {
- error = priv_check(td, PRIV_NET_ADDIFADDR);
- if (error)
- return (error);
- }
- break;
-
- case SIOCDIFADDR:
- if (td != NULL) {
- error = priv_check(td, PRIV_NET_DELIFADDR);
- if (error)
- return (error);
- }
- break;
- }
-
/*
* Find address for this interface, if it exists.
- *
- * If an alias address was specified, find that one instead of the
- * first one on the interface, if possible.
*/
- dst = ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
IN_IFADDR_RLOCK();
- LIST_FOREACH(iap, INADDR_HASH(dst.s_addr), ia_hash) {
- if (iap->ia_ifp == ifp &&
- iap->ia_addr.sin_addr.s_addr == dst.s_addr) {
- if (td == NULL || prison_check_ip4(td->td_ucred,
- &dst) == 0)
- ia = iap;
+ LIST_FOREACH(ia, INADDR_HASH(addr->sin_addr.s_addr), ia_hash) {
+ if (ia->ia_ifp == ifp &&
+ ia->ia_addr.sin_addr.s_addr == addr->sin_addr.s_addr &&
+ prison_check_ip4(td->td_ucred, &addr->sin_addr) == 0)
break;
- }
}
- if (ia != NULL)
- ifa_ref(&ia->ia_ifa);
- IN_IFADDR_RUNLOCK();
+
if (ia == NULL) {
- IF_ADDR_RLOCK(ifp);
- TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
- iap = ifatoia(ifa);
- if (iap->ia_addr.sin_family == AF_INET) {
- if (td != NULL &&
- prison_check_ip4(td->td_ucred,
- &iap->ia_addr.sin_addr) != 0)
- continue;
- ia = iap;
- break;
- }
- }
- if (ia != NULL)
- ifa_ref(&ia->ia_ifa);
- IF_ADDR_RUNLOCK(ifp);
+ IN_IFADDR_RUNLOCK();
+ return (EADDRNOTAVAIL);
}
- if (ia == NULL)
- iaIsFirst = 1;
error = 0;
switch (cmd) {
- case SIOCAIFADDR:
- case SIOCDIFADDR:
- if (ifra->ifra_addr.sin_family == AF_INET) {
- struct in_ifaddr *oia;
+ case SIOCGIFADDR:
+ *addr = ia->ia_addr;
+ break;
- IN_IFADDR_RLOCK();
- for (oia = ia; ia; ia = TAILQ_NEXT(ia, ia_link)) {
- if (ia->ia_ifp == ifp &&
- ia->ia_addr.sin_addr.s_addr ==
- ifra->ifra_addr.sin_addr.s_addr)
- break;
- }
- if (ia != NULL && ia != oia)
- ifa_ref(&ia->ia_ifa);
- if (oia != NULL && ia != oia)
- ifa_free(&oia->ia_ifa);
- IN_IFADDR_RUNLOCK();
- if ((ifp->if_flags & IFF_POINTOPOINT)
- && (cmd == SIOCAIFADDR)
- && (ifra->ifra_dstaddr.sin_addr.s_addr
- == INADDR_ANY)) {
- error = EDESTADDRREQ;
- goto out;
- }
+ case SIOCGIFBRDADDR:
+ if ((ifp->if_flags & IFF_BROADCAST) == 0) {
+ error = EINVAL;
+ break;
}
- if (cmd == SIOCDIFADDR && ia == NULL) {
- error = EADDRNOTAVAIL;
- goto out;
- }
- if (ia == NULL) {
- ifa = ifa_alloc(sizeof(struct in_ifaddr), M_WAITOK);
- ia = (struct in_ifaddr *)ifa;
- ifa->ifa_addr = (struct sockaddr *)&ia->ia_addr;
- ifa->ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr;
- ifa->ifa_netmask = (struct sockaddr *)&ia->ia_sockmask;
-
- ia->ia_sockmask.sin_len = 8;
- ia->ia_sockmask.sin_family = AF_INET;
- if (ifp->if_flags & IFF_BROADCAST) {
- ia->ia_broadaddr.sin_len = sizeof(ia->ia_addr);
- ia->ia_broadaddr.sin_family = AF_INET;
- }
- ia->ia_ifp = ifp;
+ *addr = ia->ia_broadaddr;
+ break;
- ifa_ref(ifa); /* if_addrhead */
- IF_ADDR_WLOCK(ifp);
- TAILQ_INSERT_TAIL(&ifp->if_addrhead, ifa, ifa_link);
- IF_ADDR_WUNLOCK(ifp);
- ifa_ref(ifa); /* in_ifaddrhead */
- IN_IFADDR_WLOCK();
- TAILQ_INSERT_TAIL(&V_in_ifaddrhead, ia, ia_link);
- IN_IFADDR_WUNLOCK();
- iaIsNew = 1;
+ case SIOCGIFDSTADDR:
+ if ((ifp->if_flags & IFF_POINTOPOINT) == 0) {
+ error = EINVAL;
+ break;
}
+ *addr = ia->ia_dstaddr;
break;
- case SIOCGIFADDR:
case SIOCGIFNETMASK:
- case SIOCGIFDSTADDR:
- case SIOCGIFBRDADDR:
- if (ia == NULL) {
- error = EADDRNOTAVAIL;
- goto out;
- }
+ *addr = ia->ia_sockmask;
break;
}
+ IN_IFADDR_RUNLOCK();
+
+ return (error);
+}
+
+static int
+in_aifaddr_ioctl(caddr_t data, struct ifnet *ifp, struct thread *td)
+{
+ const struct in_aliasreq *ifra = (struct in_aliasreq *)data;
+ const struct sockaddr_in *addr = &ifra->ifra_addr;
+ const struct sockaddr_in *broadaddr = &ifra->ifra_broadaddr;
+ const struct sockaddr_in *mask = &ifra->ifra_mask;
+ const struct sockaddr_in *dstaddr = &ifra->ifra_dstaddr;
+ const int vhid = ifra->ifra_vhid;
+ struct ifaddr *ifa;
+ struct in_ifaddr *ia;
+ bool iaIsFirst;
+ int error = 0;
+
+ error = priv_check(td, PRIV_NET_ADDIFADDR);
+ if (error)
+ return (error);
+
/*
- * Most paths in this switch return directly or via out. Only paths
- * that remove the address break in order to hit common removal code.
+ * ifra_addr must be present and be of INET family.
+ * ifra_broadaddr/ifra_dstaddr and ifra_mask are optional.
*/
- switch (cmd) {
- case SIOCGIFADDR:
- *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_addr;
- goto out;
+ if (addr->sin_len != sizeof(struct sockaddr_in) ||
+ addr->sin_family != AF_INET)
+ return (EINVAL);
+ if (broadaddr->sin_len != 0 &&
+ (broadaddr->sin_len != sizeof(struct sockaddr_in) ||
+ broadaddr->sin_family != AF_INET))
+ return (EINVAL);
+ if (mask->sin_len != 0 &&
+ (mask->sin_len != sizeof(struct sockaddr_in) ||
+ mask->sin_family != AF_INET))
+ return (EINVAL);
+ if ((ifp->if_flags & IFF_POINTOPOINT) &&
+ (dstaddr->sin_len != sizeof(struct sockaddr_in) ||
+ dstaddr->sin_addr.s_addr == INADDR_ANY))
+ return (EDESTADDRREQ);
+ if (vhid > 0 && carp_attach_p == NULL)
+ return (EPROTONOSUPPORT);
- case SIOCGIFBRDADDR:
- if ((ifp->if_flags & IFF_BROADCAST) == 0) {
- error = EINVAL;
- goto out;
- }
- *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_broadaddr;
- goto out;
+ /*
+ * See whether address already exist.
+ */
+ iaIsFirst = true;
+ ia = NULL;
+ IF_ADDR_RLOCK(ifp);
+ TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
+ struct in_ifaddr *it = ifatoia(ifa);
- case SIOCGIFDSTADDR:
- if ((ifp->if_flags & IFF_POINTOPOINT) == 0) {
- error = EINVAL;
- goto out;
- }
- *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_dstaddr;
- goto out;
+ if (it->ia_addr.sin_family != AF_INET)
+ continue;
- case SIOCGIFNETMASK:
- *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_sockmask;
- goto out;
+ iaIsFirst = false;
+ if (it->ia_addr.sin_addr.s_addr == addr->sin_addr.s_addr &&
+ prison_check_ip4(td->td_ucred, &addr->sin_addr) == 0)
+ ia = it;
+ }
+ IF_ADDR_RUNLOCK(ifp);
- case SIOCAIFADDR:
- maskIsNew = 0;
- hostIsNew = 1;
- error = 0;
- if (ifra->ifra_addr.sin_addr.s_addr ==
- ia->ia_addr.sin_addr.s_addr)
- hostIsNew = 0;
- if (ifra->ifra_mask.sin_len) {
- /*
- * QL: XXX
- * Need to scrub the prefix here in case
- * the issued command is SIOCAIFADDR with
- * the same address, but with a different
- * prefix length. And if the prefix length
- * is the same as before, then the call is
- * un-necessarily executed here.
- */
- in_scrubprefix(ia, LLE_STATIC);
- ia->ia_sockmask = ifra->ifra_mask;
- ia->ia_sockmask.sin_family = AF_INET;
- ia->ia_subnetmask =
- ntohl(ia->ia_sockmask.sin_addr.s_addr);
- maskIsNew = 1;
- }
- if ((ifp->if_flags & IFF_POINTOPOINT) &&
- (ifra->ifra_dstaddr.sin_family == AF_INET)) {
- in_scrubprefix(ia, LLE_STATIC);
- ia->ia_dstaddr = ifra->ifra_dstaddr;
- maskIsNew = 1; /* We lie; but the effect's the same */
- }
- if (hostIsNew || maskIsNew)
- error = in_ifinit(ifp, ia, &ifra->ifra_addr, maskIsNew,
- (ocmd == cmd ? ifra->ifra_vhid : 0));
- if (error != 0 && iaIsNew)
- break;
+ if (ia != NULL)
+ (void )in_difaddr_ioctl(data, ifp, td);
- if ((ifp->if_flags & IFF_BROADCAST) &&
- ifra->ifra_broadaddr.sin_len)
- ia->ia_broadaddr = ifra->ifra_broadaddr;
- if (error == 0) {
- ii = ((struct in_ifinfo *)ifp->if_afdata[AF_INET]);
- if (iaIsFirst &&
- (ifp->if_flags & IFF_MULTICAST) != 0) {
- error = in_joingroup(ifp, &allhosts_addr,
- NULL, &ii->ii_allhosts);
- }
- EVENTHANDLER_INVOKE(ifaddr_event, ifp);
- }
- goto out;
+ ifa = ifa_alloc(sizeof(struct in_ifaddr), M_WAITOK);
+ ia = (struct in_ifaddr *)ifa;
+ ifa->ifa_addr = (struct sockaddr *)&ia->ia_addr;
+ ifa->ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr;
+ ifa->ifa_netmask = (struct sockaddr *)&ia->ia_sockmask;
- case SIOCDIFADDR:
- /*
- * in_scrubprefix() kills the interface route.
- */
- in_scrubprefix(ia, LLE_STATIC);
+ ia->ia_ifp = ifp;
+ ia->ia_ifa.ifa_metric = ifp->if_metric;
+ ia->ia_addr = *addr;
+ if (mask->sin_len != 0) {
+ ia->ia_sockmask = *mask;
+ ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr);
+ } else {
+ in_addr_t i = ntohl(addr->sin_addr.s_addr);
/*
- * in_ifadown gets rid of all the rest of
- * the routes. This is not quite the right
- * thing to do, but at least if we are running
- * a routing process they will come back.
- */
- in_ifadown(&ia->ia_ifa, 1);
- EVENTHANDLER_INVOKE(ifaddr_event, ifp);
- error = 0;
- break;
+ * Be compatible with network classes, if netmask isn't
+ * supplied, guess it based on classes.
+ */
+ if (IN_CLASSA(i))
+ ia->ia_subnetmask = IN_CLASSA_NET;
+ else if (IN_CLASSB(i))
+ ia->ia_subnetmask = IN_CLASSB_NET;
+ else
+ ia->ia_subnetmask = IN_CLASSC_NET;
+ ia->ia_sockmask.sin_addr.s_addr = htonl(ia->ia_subnetmask);
+ }
+ ia->ia_subnet = ntohl(addr->sin_addr.s_addr) & ia->ia_subnetmask;
+ in_socktrim(&ia->ia_sockmask);
- default:
- panic("in_control: unsupported ioctl");
+ if (ifp->if_flags & IFF_BROADCAST) {
+ if (broadaddr->sin_len != 0) {
+ ia->ia_broadaddr = *broadaddr;
+ } else if (ia->ia_subnetmask == IN_RFC3021_MASK) {
+ ia->ia_broadaddr.sin_addr.s_addr = INADDR_BROADCAST;
+ ia->ia_broadaddr.sin_len = sizeof(struct sockaddr_in);
+ ia->ia_broadaddr.sin_family = AF_INET;
+ } else {
+ ia->ia_broadaddr.sin_addr.s_addr =
+ htonl(ia->ia_subnet | ~ia->ia_subnetmask);
+ ia->ia_broadaddr.sin_len = sizeof(struct sockaddr_in);
+ ia->ia_broadaddr.sin_family = AF_INET;
+ }
}
+ if (ifp->if_flags & IFF_POINTOPOINT)
+ ia->ia_dstaddr = *dstaddr;
+
+ /* XXXGL: rtinit() needs this strange assignment. */
+ if (ifp->if_flags & IFF_LOOPBACK)
+ ia->ia_dstaddr = ia->ia_addr;
+
+ ifa_ref(ifa); /* if_addrhead */
+ IF_ADDR_WLOCK(ifp);
+ TAILQ_INSERT_TAIL(&ifp->if_addrhead, ifa, ifa_link);
+ IF_ADDR_WUNLOCK(ifp);
+
+ ifa_ref(ifa); /* in_ifaddrhead */
+ IN_IFADDR_WLOCK();
+ TAILQ_INSERT_TAIL(&V_in_ifaddrhead, ia, ia_link);
+ LIST_INSERT_HEAD(INADDR_HASH(ia->ia_addr.sin_addr.s_addr), ia, ia_hash);
+ IN_IFADDR_WUNLOCK();
+
+ if (vhid != 0)
+ error = (*carp_attach_p)(&ia->ia_ifa, vhid);
+ if (error)
+ goto fail1;
+
+ /*
+ * Give the interface a chance to initialize
+ * if this is its first address,
+ * and to validate the address if necessary.
+ */
+ if (ifp->if_ioctl != NULL)
+ error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia);
+ if (error)
+ goto fail2;
+
+ /*
+ * Add route for the network.
+ */
+ if (vhid == 0) {
+ int flags = RTF_UP;
+
+ if (ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT))
+ flags |= RTF_HOST;
+
+ error = in_addprefix(ia, flags);
+ if (error)
+ goto fail2;
+ }
+
+ /*
+ * Add a loopback route to self.
+ */
+ if (vhid == 0 && (ifp->if_flags & IFF_LOOPBACK) == 0 &&
+ ia->ia_addr.sin_addr.s_addr != INADDR_ANY) {
+ struct in_ifaddr *eia;
+
+ eia = in_localip_more(ia);
+
+ if (eia == NULL) {
+ error = ifa_add_loopback_route((struct ifaddr *)ia,
+ (struct sockaddr *)&ia->ia_addr);
+ if (error)
+ goto fail3;
+ } else
+ ifa_free(&eia->ia_ifa);
+ }
+
+ if (iaIsFirst && (ifp->if_flags & IFF_MULTICAST)) {
+ struct in_addr allhosts_addr;
+ struct in_ifinfo *ii;
+
+ ii = ((struct in_ifinfo *)ifp->if_afdata[AF_INET]);
+ allhosts_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
+
+ error = in_joingroup(ifp, &allhosts_addr, NULL,
+ &ii->ii_allhosts);
+ }
+
+ EVENTHANDLER_INVOKE(ifaddr_event, ifp);
+
+ return (error);
+
+fail3:
+ if (vhid == 0)
+ (void )in_scrubprefix(ia, LLE_STATIC);
+
+fail2:
if (ia->ia_ifa.ifa_carp)
(*carp_detach_p)(&ia->ia_ifa);
+fail1:
+ IF_ADDR_WLOCK(ifp);
+ TAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifa_link);
+ IF_ADDR_WUNLOCK(ifp);
+ ifa_free(&ia->ia_ifa);
+
+ IN_IFADDR_WLOCK();
+ TAILQ_REMOVE(&V_in_ifaddrhead, ia, ia_link);
+ LIST_REMOVE(ia, ia_hash);
+ IN_IFADDR_WUNLOCK();
+ ifa_free(&ia->ia_ifa);
+
+ return (error);
+}
+
+static int
+in_difaddr_ioctl(caddr_t data, struct ifnet *ifp, struct thread *td)
+{
+ const struct ifreq *ifr = (struct ifreq *)data;
+ const struct sockaddr_in *addr = (struct sockaddr_in *)&ifr->ifr_addr;
+ struct ifaddr *ifa;
+ struct in_ifaddr *ia;
+ bool deleteAny, iaIsLast;
+ int error;
+
+ if (td != NULL) {
+ error = priv_check(td, PRIV_NET_DELIFADDR);
+ if (error)
+ return (error);
+ }
+
+ if (addr->sin_len != sizeof(struct sockaddr_in) ||
+ addr->sin_family != AF_INET)
+ deleteAny = true;
+ else
+ deleteAny = false;
+
+ iaIsLast = true;
+ ia = NULL;
IF_ADDR_WLOCK(ifp);
- /* Re-check that ia is still part of the list. */
TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
- if (ifa == &ia->ia_ifa)
- break;
+ struct in_ifaddr *it = ifatoia(ifa);
+
+ if (it->ia_addr.sin_family != AF_INET)
+ continue;
+
+ if (deleteAny && ia == NULL && (td == NULL ||
+ prison_check_ip4(td->td_ucred, &it->ia_addr.sin_addr) == 0))
+ ia = it;
+
+ if (it->ia_addr.sin_addr.s_addr == addr->sin_addr.s_addr &&
+ (td == NULL || prison_check_ip4(td->td_ucred,
+ &addr->sin_addr) == 0))
+ ia = it;
+
+ if (it != ia)
+ iaIsLast = false;
}
- if (ifa == NULL) {
- /*
- * If we lost the race with another thread, there is no need to
- * try it again for the next loop as there is no other exit
- * path between here and out.
- */
+
+ if (ia == NULL) {
IF_ADDR_WUNLOCK(ifp);
- error = EADDRNOTAVAIL;
- goto out;
+ return (EADDRNOTAVAIL);
}
+
TAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifa_link);
IF_ADDR_WUNLOCK(ifp);
- ifa_free(&ia->ia_ifa); /* if_addrhead */
+ ifa_free(&ia->ia_ifa); /* if_addrhead */
IN_IFADDR_WLOCK();
TAILQ_REMOVE(&V_in_ifaddrhead, ia, ia_link);
-
LIST_REMOVE(ia, ia_hash);
IN_IFADDR_WUNLOCK();
+ ifa_free(&ia->ia_ifa); /* in_ifaddrhead */
+
+ /*
+ * in_scrubprefix() kills the interface route.
+ */
+ in_scrubprefix(ia, LLE_STATIC);
+
+ /*
+ * in_ifadown gets rid of all the rest of
+ * the routes. This is not quite the right
+ * thing to do, but at least if we are running
+ * a routing process they will come back.
+ */
+ in_ifadown(&ia->ia_ifa, 1);
+
+ if (ia->ia_ifa.ifa_carp)
+ (*carp_detach_p)(&ia->ia_ifa);
+
/*
* If this is the last IPv4 address configured on this
* interface, leave the all-hosts group.
* No state-change report need be transmitted.
*/
- IFP_TO_IA(ifp, iap);
- if (iap == NULL) {
+ if (iaIsLast && (ifp->if_flags & IFF_MULTICAST)) {
+ struct in_ifinfo *ii;
+
ii = ((struct in_ifinfo *)ifp->if_afdata[AF_INET]);
IN_MULTI_LOCK();
if (ii->ii_allhosts) {
@@ -584,14 +627,11 @@ in_control(struct socket *so, u_long cmd
ii->ii_allhosts = NULL;
}
IN_MULTI_UNLOCK();
- } else
- ifa_free(&iap->ia_ifa);
+ }
- ifa_free(&ia->ia_ifa); /* in_ifaddrhead */
-out:
- if (ia != NULL)
- ifa_free(&ia->ia_ifa);
- return (error);
+ EVENTHANDLER_INVOKE(ifaddr_event, ifp);
+
+ return (0);
}
/*
@@ -616,11 +656,23 @@ in_lifaddr_ioctl(struct socket *so, u_lo
{
struct if_laddrreq *iflr = (struct if_laddrreq *)data;
struct ifaddr *ifa;
+ int error;
- /* sanity checks */
- if (data == NULL || ifp == NULL) {
- panic("invalid argument to in_lifaddr_ioctl");
- /*NOTRECHED*/
+ switch (cmd) {
+ case SIOCALIFADDR:
+ if (td != NULL) {
+ error = priv_check(td, PRIV_NET_ADDIFADDR);
+ if (error)
+ return (error);
+ }
+ break;
+ case SIOCDLIFADDR:
+ if (td != NULL) {
+ error = priv_check(td, PRIV_NET_DELIFADDR);
+ if (error)
+ return (error);
+ }
+ break;
}
switch (cmd) {
@@ -770,115 +822,6 @@ in_lifaddr_ioctl(struct socket *so, u_lo
return (EOPNOTSUPP); /*just for safety*/
}
-/*
- * Initialize an interface's internet address
- * and routing table entry.
- */
-static int
-in_ifinit(struct ifnet *ifp, struct in_ifaddr *ia, struct sockaddr_in *sin,
- int masksupplied, int vhid)
-{
- register u_long i = ntohl(sin->sin_addr.s_addr);
- int flags, error = 0;
-
- IN_IFADDR_WLOCK();
- if (ia->ia_addr.sin_family == AF_INET)
- LIST_REMOVE(ia, ia_hash);
- ia->ia_addr = *sin;
- LIST_INSERT_HEAD(INADDR_HASH(ia->ia_addr.sin_addr.s_addr),
- ia, ia_hash);
- IN_IFADDR_WUNLOCK();
-
- if (vhid > 0) {
- if (carp_attach_p != NULL)
- error = (*carp_attach_p)(&ia->ia_ifa, vhid);
- else
- error = EPROTONOSUPPORT;
- }
- if (error)
- return (error);
-
- /*
- * Give the interface a chance to initialize
- * if this is its first address,
- * and to validate the address if necessary.
- */
- 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);
-
- /*
- * Be compatible with network classes, if netmask isn't supplied,
- * guess it based on classes.
- */
- if (!masksupplied) {
- if (IN_CLASSA(i))
- ia->ia_subnetmask = IN_CLASSA_NET;
- else if (IN_CLASSB(i))
- ia->ia_subnetmask = IN_CLASSB_NET;
- else
- ia->ia_subnetmask = IN_CLASSC_NET;
- ia->ia_sockmask.sin_addr.s_addr = htonl(ia->ia_subnetmask);
- }
- ia->ia_subnet = i & ia->ia_subnetmask;
- in_socktrim(&ia->ia_sockmask);
-
- /*
- * Add route for the network.
- */
- flags = RTF_UP;
- ia->ia_ifa.ifa_metric = ifp->if_metric;
- if (ifp->if_flags & IFF_BROADCAST) {
- if (ia->ia_subnetmask == IN_RFC3021_MASK)
- ia->ia_broadaddr.sin_addr.s_addr = INADDR_BROADCAST;
- else
- ia->ia_broadaddr.sin_addr.s_addr =
- htonl(ia->ia_subnet | ~ia->ia_subnetmask);
- } else if (ifp->if_flags & IFF_LOOPBACK) {
- ia->ia_dstaddr = ia->ia_addr;
- flags |= RTF_HOST;
- } else if (ifp->if_flags & IFF_POINTOPOINT) {
- if (ia->ia_dstaddr.sin_family != AF_INET)
- return (0);
- flags |= RTF_HOST;
- }
- if (!vhid && (error = in_addprefix(ia, flags)) != 0)
- return (error);
-
- if (ia->ia_addr.sin_addr.s_addr == INADDR_ANY)
- return (0);
-
- if (ifp->if_flags & IFF_POINTOPOINT &&
- ia->ia_dstaddr.sin_addr.s_addr == ia->ia_addr.sin_addr.s_addr)
- return (0);
-
- /*
- * add a loopback route to self
- */
- if (!vhid && !(ifp->if_flags & IFF_LOOPBACK)) {
- struct route ia_ro;
-
- bzero(&ia_ro, sizeof(ia_ro));
- *((struct sockaddr_in *)(&ia_ro.ro_dst)) = ia->ia_addr;
- rtalloc_ign_fib(&ia_ro, 0, RT_DEFAULT_FIB);
- if ((ia_ro.ro_rt != NULL) && (ia_ro.ro_rt->rt_ifp != NULL) &&
- (ia_ro.ro_rt->rt_ifp == V_loif)) {
- RT_LOCK(ia_ro.ro_rt);
- RT_ADDREF(ia_ro.ro_rt);
- RTFREE_LOCKED(ia_ro.ro_rt);
- } else
- error = ifa_add_loopback_route((struct ifaddr *)ia,
- (struct sockaddr *)&ia->ia_addr);
- if (error == 0)
- ia->ia_flags |= IFA_RTSELF;
- if (ia_ro.ro_rt != NULL)
- RTFREE(ia_ro.ro_rt);
- }
-
- return (error);
-}
-
#define rtinitflags(x) \
((((x)->ia_ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) != 0) \
? RTF_HOST : 0)
@@ -1007,44 +950,27 @@ in_scrubprefix(struct in_ifaddr *target,
/*
* Remove the loopback route to the interface address.
- * The "useloopback" setting is not consulted because if the
- * user configures an interface address, turns off this
- * setting, and then tries to delete that interface address,
- * checking the current setting of "useloopback" would leave
- * that interface address loopback route untouched, which
- * would be wrong. Therefore the interface address loopback route
- * deletion is unconditional.
*/
if ((target->ia_addr.sin_addr.s_addr != INADDR_ANY) &&
!(target->ia_ifp->if_flags & IFF_LOOPBACK) &&
- (target->ia_flags & IFA_RTSELF)) {
- struct route ia_ro;
- int freeit = 0;
-
- bzero(&ia_ro, sizeof(ia_ro));
- *((struct sockaddr_in *)(&ia_ro.ro_dst)) = target->ia_addr;
- rtalloc_ign_fib(&ia_ro, 0, 0);
- if ((ia_ro.ro_rt != NULL) && (ia_ro.ro_rt->rt_ifp != NULL) &&
- (ia_ro.ro_rt->rt_ifp == V_loif)) {
- RT_LOCK(ia_ro.ro_rt);
- if (ia_ro.ro_rt->rt_refcnt <= 1)
- freeit = 1;
- else if (flags & LLE_STATIC) {
- RT_REMREF(ia_ro.ro_rt);
- target->ia_flags &= ~IFA_RTSELF;
- }
- RTFREE_LOCKED(ia_ro.ro_rt);
- }
- if (freeit && (flags & LLE_STATIC)) {
+ (flags & LLE_STATIC)) {
+ struct in_ifaddr *eia;
+
+ eia = in_localip_more(target);
+
+ if (eia != NULL) {
+ error = ifa_switch_loopback_route((struct ifaddr *)eia,
+ (struct sockaddr *)&target->ia_addr);
+ ifa_free(&eia->ia_ifa);
+ } else {
error = ifa_del_loopback_route((struct ifaddr *)target,
(struct sockaddr *)&target->ia_addr);
- if (error == 0)
- target->ia_flags &= ~IFA_RTSELF;
}
- if ((flags & LLE_STATIC) &&
- !(target->ia_ifp->if_flags & IFF_NOARP))
+
+ if (!(target->ia_ifp->if_flags & IFF_NOARP))
/* remove arp cache */
- arp_ifscrub(target->ia_ifp, IA_SIN(target)->sin_addr.s_addr);
+ arp_ifscrub(target->ia_ifp,
+ IA_SIN(target)->sin_addr.s_addr);
}
if (rtinitflags(target)) {
More information about the svn-src-head
mailing list