ifnet: Make blocking operation in if_addrheads iteration MPSAFE
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Wed, 18 Feb 2015 13:01:15 +0000 (21:01 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Mon, 2 Mar 2015 05:29:15 +0000 (13:29 +0800)
Two methods are used:
- Dispatch blocking operation in if_addrheads iteration to netisr0
  to run for functions which do not copy data into user space.
- Use ifaddr marker to make sure that the ifaddr list is not broken
  if the ifaddr list is changed during the blocking operation.  And
  reference the ifaddr being used so it will not be destroyed during
  the blocking operation.

sys/emulation/linux/linux_ioctl.c
sys/net/if.c
sys/net/if_var.h
sys/net/pf/pf_if.c
sys/net/rtsock.c
sys/netgraph/eiface/ng_eiface.c
sys/netgraph7/eiface/ng_eiface.c
test/ifconf/Makefile [new file with mode: 0644]
test/ifconf/ifconf.c [new file with mode: 0644]

index bd4878a..e006c83 100644 (file)
@@ -1017,7 +1017,9 @@ linux_ioctl_SIOCGIFCONF(struct file *fp, u_long cmd, u_long ocmd, caddr_t data,
        /* Return all AF_INET addresses of all interfaces */
        ifnet_lock();
        TAILQ_FOREACH(ifp, &ifnetlist, if_link) {
-               struct ifaddr_container *ifac;
+               struct ifaddr_container *ifac, *ifac_mark;
+               struct ifaddr_marker mark;
+               struct ifaddrhead *head;
 
                if (uio.uio_resid <= 0)
                        break;
@@ -1029,11 +1031,27 @@ linux_ioctl_SIOCGIFCONF(struct file *fp, u_long cmd, u_long ocmd, caddr_t data,
                else
                        strlcpy(ifr.ifr_name, ifp->if_xname, LINUX_IFNAMSIZ);
 
-               /* Walk the address list */
-               TAILQ_FOREACH(ifac, &ifp->if_addrheads[mycpuid], ifa_link) {
+               /*
+                * Walk the address list
+                *
+                * Add a marker, since uiomove() could block and during that
+                * period the list could be changed.  Inserting the marker to
+                * the header of the list will not cause trouble for the code
+                * assuming that the first element of the list is AF_LINK; the
+                * marker will be moved to the next position w/o blocking.
+                */
+               ifa_marker_init(&mark, ifp);
+               ifac_mark = &mark.ifac;
+               head = &ifp->if_addrheads[mycpuid];
+
+               TAILQ_INSERT_HEAD(head, ifac_mark, ifa_link);
+               while ((ifac = TAILQ_NEXT(ifac_mark, ifa_link)) != NULL) {
                        struct ifaddr *ifa = ifac->ifa;
                        struct sockaddr *sa = ifa->ifa_addr;
 
+                       TAILQ_REMOVE(head, ifac_mark, ifa_link);
+                       TAILQ_INSERT_AFTER(head, ifac, ifac_mark, ifa_link);
+
                        if (uio.uio_resid <= 0)
                                break;
 
@@ -1045,11 +1063,13 @@ linux_ioctl_SIOCGIFCONF(struct file *fp, u_long cmd, u_long ocmd, caddr_t data,
                                error = uiomove((caddr_t)&ifr, sizeof ifr,
                                    &uio);
                                if (error != 0) {
+                                       TAILQ_REMOVE(head, ifac_mark, ifa_link);
                                        ifnet_unlock();
                                        return (error);
                                }
                        }
                }
+               TAILQ_REMOVE(head, ifac_mark, ifa_link);
        }
        ifnet_unlock();
 
index 3c07853..e34d96d 100644 (file)
@@ -728,15 +728,30 @@ if_attachdomain1(struct ifnet *ifp)
 /*
  * Purge all addresses whose type is _not_ AF_LINK
  */
-void
-if_purgeaddrs_nolink(struct ifnet *ifp)
+static void
+if_purgeaddrs_nolink_dispatch(netmsg_t nmsg)
 {
+       struct lwkt_msg *lmsg = &nmsg->lmsg;
+       struct ifnet *ifp = lmsg->u.ms_resultp;
        struct ifaddr_container *ifac, *next;
 
+       KASSERT(&curthread->td_msgport == netisr_cpuport(0),
+           ("not in netisr0"));
+
+       /*
+        * The ifaddr processing in the following loop will block,
+        * however, this function is called in netisr0, in which
+        * ifaddr list changes happen, so we don't care about the
+        * blockness of the ifaddr processing here.
+        */
        TAILQ_FOREACH_MUTABLE(ifac, &ifp->if_addrheads[mycpuid],
                              ifa_link, next) {
                struct ifaddr *ifa = ifac->ifa;
 
+               /* Ignore marker */
+               if (ifa->ifa_addr->sa_family == AF_UNSPEC)
+                       continue;
+
                /* Leave link ifaddr as it is */
                if (ifa->ifa_addr->sa_family == AF_LINK)
                        continue;
@@ -781,6 +796,22 @@ if_purgeaddrs_nolink(struct ifnet *ifp)
                ifa_ifunlink(ifa, ifp);
                ifa_destroy(ifa);
        }
+
+       lwkt_replymsg(lmsg, 0);
+}
+
+void
+if_purgeaddrs_nolink(struct ifnet *ifp)
+{
+       struct netmsg_base nmsg;
+       struct lwkt_msg *lmsg = &nmsg.lmsg;
+
+       ASSERT_CANDOMSG_NETISR0(curthread);
+
+       netmsg_init(&nmsg, NULL, &curthread->td_msgport, 0,
+           if_purgeaddrs_nolink_dispatch);
+       lmsg->u.ms_resultp = ifp;
+       lwkt_domsg(netisr_cpuport(0), lmsg, 0);
 }
 
 static void
@@ -1447,44 +1478,90 @@ link_rtrequest(int cmd, struct rtentry *rt)
        }
 }
 
+struct netmsg_ifroute {
+       struct netmsg_base      base;
+       struct ifnet            *ifp;
+       int                     flag;
+       int                     fam;
+};
+
 /*
- * Mark an interface down and notify protocols of
- * the transition.
- * NOTE: must be called at splnet or eqivalent.
+ * Mark an interface down and notify protocols of the transition.
  */
-void
-if_unroute(struct ifnet *ifp, int flag, int fam)
+static void
+if_unroute_dispatch(netmsg_t nmsg)
 {
+       struct netmsg_ifroute *msg = (struct netmsg_ifroute *)nmsg;
+       struct ifnet *ifp = msg->ifp;
+       int flag = msg->flag, fam = msg->fam;
        struct ifaddr_container *ifac;
 
        ifp->if_flags &= ~flag;
        getmicrotime(&ifp->if_lastchange);
+       /*
+        * The ifaddr processing in the following loop will block,
+        * however, this function is called in netisr0, in which
+        * ifaddr list changes happen, so we don't care about the
+        * blockness of the ifaddr processing here.
+        */
        TAILQ_FOREACH(ifac, &ifp->if_addrheads[mycpuid], ifa_link) {
                struct ifaddr *ifa = ifac->ifa;
 
+               /* Ignore marker */
+               if (ifa->ifa_addr->sa_family == AF_UNSPEC)
+                       continue;
+
                if (fam == PF_UNSPEC || (fam == ifa->ifa_addr->sa_family))
                        kpfctlinput(PRC_IFDOWN, ifa->ifa_addr);
        }
        ifq_purge_all(&ifp->if_snd);
        rt_ifmsg(ifp);
+
+       lwkt_replymsg(&nmsg->lmsg, 0);
+}
+
+void
+if_unroute(struct ifnet *ifp, int flag, int fam)
+{
+       struct netmsg_ifroute msg;
+
+       ASSERT_CANDOMSG_NETISR0(curthread);
+
+       netmsg_init(&msg.base, NULL, &curthread->td_msgport, 0,
+           if_unroute_dispatch);
+       msg.ifp = ifp;
+       msg.flag = flag;
+       msg.fam = fam;
+       lwkt_domsg(netisr_cpuport(0), &msg.base.lmsg, 0);
 }
 
 /*
- * Mark an interface up and notify protocols of
- * the transition.
- * NOTE: must be called at splnet or eqivalent.
+ * Mark an interface up and notify protocols of the transition.
  */
-void
-if_route(struct ifnet *ifp, int flag, int fam)
+static void
+if_route_dispatch(netmsg_t nmsg)
 {
+       struct netmsg_ifroute *msg = (struct netmsg_ifroute *)nmsg;
+       struct ifnet *ifp = msg->ifp;
+       int flag = msg->flag, fam = msg->fam;
        struct ifaddr_container *ifac;
 
        ifq_purge_all(&ifp->if_snd);
        ifp->if_flags |= flag;
        getmicrotime(&ifp->if_lastchange);
+       /*
+        * The ifaddr processing in the following loop will block,
+        * however, this function is called in netisr0, in which
+        * ifaddr list changes happen, so we don't care about the
+        * blockness of the ifaddr processing here.
+        */
        TAILQ_FOREACH(ifac, &ifp->if_addrheads[mycpuid], ifa_link) {
                struct ifaddr *ifa = ifac->ifa;
 
+               /* Ignore marker */
+               if (ifa->ifa_addr->sa_family == AF_UNSPEC)
+                       continue;
+
                if (fam == PF_UNSPEC || (fam == ifa->ifa_addr->sa_family))
                        kpfctlinput(PRC_IFUP, ifa->ifa_addr);
        }
@@ -1492,6 +1569,23 @@ if_route(struct ifnet *ifp, int flag, int fam)
 #ifdef INET6
        in6_if_up(ifp);
 #endif
+
+       lwkt_replymsg(&nmsg->lmsg, 0);
+}
+
+void
+if_route(struct ifnet *ifp, int flag, int fam)
+{
+       struct netmsg_ifroute msg;
+
+       ASSERT_CANDOMSG_NETISR0(curthread);
+
+       netmsg_init(&msg.base, NULL, &curthread->td_msgport, 0,
+           if_route_dispatch);
+       msg.ifp = ifp;
+       msg.flag = flag;
+       msg.fam = fam;
+       lwkt_domsg(netisr_cpuport(0), &msg.base.lmsg, 0);
 }
 
 /*
@@ -1831,7 +1925,6 @@ ifioctl(struct socket *so, u_long cmd, caddr_t data, struct ucred *cred)
 
                strlcpy(ifp->if_xname, new_name, sizeof(ifp->if_xname));
                ifa = TAILQ_FIRST(&ifp->if_addrheads[mycpuid])->ifa;
-               /* XXX IFA_LOCK(ifa); */
                sdl = (struct sockaddr_dl *)ifa->ifa_addr;
                namelen = strlen(new_name);
                onamelen = sdl->sdl_nlen;
@@ -1850,7 +1943,6 @@ ifioctl(struct socket *so, u_long cmd, caddr_t data, struct ucred *cred)
                bzero(sdl->sdl_data, onamelen);
                while (namelen != 0)
                        sdl->sdl_data[--namelen] = 0xff;
-               /* XXX IFA_UNLOCK(ifa) */
 
                EVENTHANDLER_INVOKE(ifnet_attach_event, ifp);
 
@@ -2145,7 +2237,9 @@ ifconf(u_long cmd, caddr_t data, struct ucred *cred)
 
        ifnet_lock();
        TAILQ_FOREACH(ifp, &ifnetlist, if_link) {
-               struct ifaddr_container *ifac;
+               struct ifaddr_container *ifac, *ifac_mark;
+               struct ifaddr_marker mark;
+               struct ifaddrhead *head;
                int addrs;
 
                if (space <= sizeof ifr)
@@ -2162,10 +2256,29 @@ ifconf(u_long cmd, caddr_t data, struct ucred *cred)
                        break;
                }
 
+               /*
+                * Add a marker, since copyout() could block and during that
+                * period the list could be changed.  Inserting the marker to
+                * the header of the list will not cause trouble for the code
+                * assuming that the first element of the list is AF_LINK; the
+                * marker will be moved to the next position w/o blocking.
+                */
+               ifa_marker_init(&mark, ifp);
+               ifac_mark = &mark.ifac;
+               head = &ifp->if_addrheads[mycpuid];
+
                addrs = 0;
-               TAILQ_FOREACH(ifac, &ifp->if_addrheads[mycpuid], ifa_link) {
+               TAILQ_INSERT_HEAD(head, ifac_mark, ifa_link);
+               while ((ifac = TAILQ_NEXT(ifac_mark, ifa_link)) != NULL) {
                        struct ifaddr *ifa = ifac->ifa;
 
+                       TAILQ_REMOVE(head, ifac_mark, ifa_link);
+                       TAILQ_INSERT_AFTER(head, ifac, ifac_mark, ifa_link);
+
+                       /* Ignore marker */
+                       if (ifa->ifa_addr->sa_family == AF_UNSPEC)
+                               continue;
+
                        if (space <= sizeof ifr)
                                break;
                        sa = ifa->ifa_addr;
@@ -2173,6 +2286,12 @@ ifconf(u_long cmd, caddr_t data, struct ucred *cred)
                            prison_if(cred, sa))
                                continue;
                        addrs++;
+                       /*
+                        * Keep a reference on this ifaddr, so that it will
+                        * not be destroyed when its address is copied to
+                        * the userland, which could block.
+                        */
+                       IFAREF(ifa);
 #ifdef COMPAT_43
                        if (cmd == OSIOCGIFCONF) {
                                struct osockaddr *osa =
@@ -2189,8 +2308,10 @@ ifconf(u_long cmd, caddr_t data, struct ucred *cred)
                                ifrp++;
                        } else {
                                if (space < (sizeof ifr) + sa->sa_len -
-                                           sizeof(*sa))
+                                           sizeof(*sa)) {
+                                       IFAFREE(ifa);
                                        break;
+                               }
                                space -= sa->sa_len - sizeof(*sa);
                                error = copyout(&ifr, ifrp,
                                                sizeof ifr.ifr_name);
@@ -2200,10 +2321,12 @@ ifconf(u_long cmd, caddr_t data, struct ucred *cred)
                                ifrp = (struct ifreq *)
                                        (sa->sa_len + (caddr_t)&ifrp->ifr_addr);
                        }
+                       IFAFREE(ifa);
                        if (error)
                                break;
                        space -= sizeof ifr;
                }
+               TAILQ_REMOVE(head, ifac_mark, ifa_link);
                if (error)
                        break;
                if (!addrs) {
@@ -3486,3 +3609,19 @@ ifnet_array_isempty(void)
        else
                return 0;
 }
+
+void
+ifa_marker_init(struct ifaddr_marker *mark, struct ifnet *ifp)
+{
+       struct ifaddr *ifa;
+
+       memset(mark, 0, sizeof(*mark));
+       ifa = &mark->ifa;
+
+       mark->ifac.ifa = ifa;
+
+       ifa->ifa_addr = &mark->addr;
+       ifa->ifa_dstaddr = &mark->dstaddr;
+       ifa->ifa_netmask = &mark->netmask;
+       ifa->ifa_ifp = ifp;
+}
index aeebc5d..4948623 100644 (file)
@@ -654,6 +654,14 @@ struct ifmultiaddr {
 
 #ifdef _KERNEL
 
+struct ifaddr_marker {
+       struct ifaddr           ifa;
+       struct ifaddr_container ifac;
+       struct sockaddr         addr;
+       struct sockaddr         netmask;
+       struct sockaddr         dstaddr;
+};
+
 /*
  * ifaddr statistics update macro
  */
@@ -974,6 +982,7 @@ void        *ifa_create(int, int);
 void   ifa_destroy(struct ifaddr *);
 void   ifa_iflink(struct ifaddr *, struct ifnet *, int);
 void   ifa_ifunlink(struct ifaddr *, struct ifnet *);
+void   ifa_marker_init(struct ifaddr_marker *, struct ifnet *);
 
 struct ifmultiaddr *ifmaof_ifpforaddr(struct sockaddr *, struct ifnet *);
 int    if_simloop(struct ifnet *ifp, struct mbuf *m, int af, int hlen);
index a7c0a43..d38f380 100644 (file)
@@ -41,6 +41,7 @@
 #include <sys/mbuf.h>
 #include <sys/eventhandler.h>
 #include <sys/filio.h>
+#include <sys/msgport2.h>
 #include <sys/socket.h>
 #include <sys/socketvar.h>
 #include <sys/kernel.h>
@@ -49,6 +50,8 @@
 
 #include <net/if.h>
 #include <net/if_types.h>
+#include <net/netisr2.h>
+#include <net/netmsg2.h>
 #include <net/route.h>
 
 #include <netinet/in.h>
@@ -542,15 +545,31 @@ pfi_table_update(struct pfr_ktable *kt, struct pfi_kif *kif, int net, int flags)
                    "into table %s: %d\n", pfi_buffer_cnt, kt->pfrkt_name, e);
 }
 
-void
-pfi_instance_add(struct ifnet *ifp, int net, int flags)
+struct netmsg_pfiadd {
+       struct netmsg_base      base;
+       struct ifnet            *ifp;
+       int                     net;
+       int                     flags;
+};
+
+static void
+pfi_instance_add_dispatch(netmsg_t nmsg)
 {
+       struct netmsg_pfiadd *msg = (struct netmsg_pfiadd *)nmsg;
        struct ifaddr_container *ifac;
        int              got4 = 0, got6 = 0;
        int              net2, af;
+       struct ifnet *ifp = msg->ifp;
+       int net = msg->net, flags = msg->flags;
 
        if (ifp == NULL)
-               return;
+               goto done;
+       /*
+        * The ifaddr processing in the following loop will block,
+        * however, this function is called in netisr0, in which
+        * ifaddr list changes happen, so we don't care about the
+        * blockness of the ifaddr processing here.
+        */
        TAILQ_FOREACH(ifac, &ifp->if_addrheads[mycpuid], ifa_link) {
                struct ifaddr *ia = ifac->ifa;
 
@@ -609,6 +628,23 @@ pfi_instance_add(struct ifnet *ifp, int net, int flags)
                else
                        pfi_address_add(ia->ifa_addr, af, net2);
        }
+done:
+       lwkt_replymsg(&nmsg->lmsg, 0);
+}
+
+void
+pfi_instance_add(struct ifnet *ifp, int net, int flags)
+{
+       struct netmsg_pfiadd msg;
+
+       ASSERT_CANDOMSG_NETISR0(curthread);
+
+       netmsg_init(&msg.base, NULL, &curthread->td_msgport, 0,
+           pfi_instance_add_dispatch);
+       msg.ifp = ifp;
+       msg.net = net;
+       msg.flags = flags;
+       lwkt_domsg(netisr_cpuport(0), &msg.base.lmsg, 0);
 }
 
 void
index 884e4e2..7b4b936 100644 (file)
@@ -1305,12 +1305,21 @@ sysctl_iflist(int af, struct walkarg *w)
 
        ifnet_lock();
        TAILQ_FOREACH(ifp, &ifnetlist, if_link) {
-               struct ifaddr_container *ifac;
+               struct ifaddr_container *ifac, *ifac_mark;
+               struct ifaddr_marker mark;
+               struct ifaddrhead *head;
                struct ifaddr *ifa;
 
                if (w->w_arg && w->w_arg != ifp->if_index)
                        continue;
-               ifac = TAILQ_FIRST(&ifp->if_addrheads[mycpuid]);
+               head = &ifp->if_addrheads[mycpuid];
+               /*
+                * There is no need to reference the first ifaddr
+                * even if the following resizewalkarg() blocks,
+                * since the first ifaddr will not be destroyed
+                * when the ifnet lock is held.
+                */
+               ifac = TAILQ_FIRST(head);
                ifa = ifac->ifa;
                rtinfo.rti_ifpaddr = ifa->ifa_addr;
                msglen = rt_msgsize(RTM_IFINFO, &rtinfo);
@@ -1334,9 +1343,23 @@ sysctl_iflist(int af, struct walkarg *w)
                                return (error);
                        }
                }
-               while ((ifac = TAILQ_NEXT(ifac, ifa_link)) != NULL) {
+               /*
+                * Add a marker, since SYSCTL_OUT() could block and during
+                * that period the list could be changed.
+                */
+               ifa_marker_init(&mark, ifp);
+               ifac_mark = &mark.ifac;
+               TAILQ_INSERT_AFTER(head, ifac, ifac_mark, ifa_link);
+               while ((ifac = TAILQ_NEXT(ifac_mark, ifa_link)) != NULL) {
+                       TAILQ_REMOVE(head, ifac_mark, ifa_link);
+                       TAILQ_INSERT_AFTER(head, ifac, ifac_mark, ifa_link);
+
                        ifa = ifac->ifa;
 
+                       /* Ignore marker */
+                       if (ifa->ifa_addr->sa_family == AF_UNSPEC)
+                               continue;
+
                        if (af && af != ifa->ifa_addr->sa_family)
                                continue;
                        if (curproc->p_ucred->cr_prison &&
@@ -1346,8 +1369,16 @@ sysctl_iflist(int af, struct walkarg *w)
                        rtinfo.rti_netmask = ifa->ifa_netmask;
                        rtinfo.rti_bcastaddr = ifa->ifa_dstaddr;
                        msglen = rt_msgsize(RTM_NEWADDR, &rtinfo);
+                       /*
+                        * Keep a reference on this ifaddr, so that it will
+                        * not be destroyed if the following resizewalkarg()
+                        * blocks.
+                        */
+                       IFAREF(ifa);
                        if (w->w_tmemsize < msglen &&
                            resizewalkarg(w, msglen) != 0) {
+                               IFAFREE(ifa);
+                               TAILQ_REMOVE(head, ifac_mark, ifa_link);
                                ifnet_unlock();
                                return (ENOMEM);
                        }
@@ -1361,11 +1392,15 @@ sysctl_iflist(int af, struct walkarg *w)
                                ifam->ifam_addrs = rtinfo.rti_addrs;
                                error = SYSCTL_OUT(w->w_req, w->w_tmem, msglen);
                                if (error) {
+                                       IFAFREE(ifa);
+                                       TAILQ_REMOVE(head, ifac_mark, ifa_link);
                                        ifnet_unlock();
                                        return (error);
                                }
                        }
+                       IFAFREE(ifa);
                }
+               TAILQ_REMOVE(head, ifac_mark, ifa_link);
                rtinfo.rti_netmask = NULL;
                rtinfo.rti_ifaaddr = NULL;
                rtinfo.rti_bcastaddr = NULL;
index 1f4554e..0963f78 100644 (file)
@@ -440,8 +440,12 @@ ng_eiface_rcvmsg(node_p node, struct ng_mesg *msg,
                        /* Determine size of response and allocate it */
                        buflen = 0;
                        TAILQ_FOREACH(ifac, &ifp->if_addrheads[mycpuid],
-                                     ifa_link)
+                                     ifa_link) {
+                               /* Ignore marker */
+                               if (ifac->ifa->ifa_addr->sa_family == AF_UNSPEC)
+                                        continue;
                                buflen += SA_SIZE(ifac->ifa->ifa_addr);
+                       }
                        NG_MKRESPONSE(resp, msg, buflen, M_NOWAIT);
                        if (resp == NULL) {
                                error = ENOMEM;
@@ -455,6 +459,10 @@ ng_eiface_rcvmsg(node_p node, struct ng_mesg *msg,
                                struct ifaddr *ifa = ifac->ifa;
                                const int len = SA_SIZE(ifa->ifa_addr);
 
+                               /* Ignore marker */
+                               if (ifa->ifa_addr->sa_family == AF_UNSPEC)
+                                        continue;
+
                                if (buflen < len) {
                                        log(LOG_ERR, "%s: len changed?\n",
                                            ifp->if_xname);
index 45b36c1..e864e31 100644 (file)
@@ -456,8 +456,12 @@ ng_eiface_rcvmsg(node_p node, item_p item, hook_p lasthook)
                        /* Determine size of response and allocate it */
                        buflen = 0;
                        TAILQ_FOREACH(ifac, &ifp->if_addrheads[mycpuid],
-                                     ifa_link)
+                                     ifa_link) {
+                               /* Ignore marker */
+                               if (ifac->ifa->ifa_addr->sa_family == AF_UNSPEC)
+                                       continue;
                                buflen += SA_SIZE(ifac->ifa->ifa_addr);
+                       }
                        NG_MKRESPONSE(resp, msg, buflen, M_WAITOK | M_NULLOK);
                        if (resp == NULL) {
                                error = ENOMEM;
@@ -471,6 +475,10 @@ ng_eiface_rcvmsg(node_p node, item_p item, hook_p lasthook)
                                struct ifaddr *ifa = ifac->ifa;
                                const int len = SA_SIZE(ifa->ifa_addr);
 
+                               /* Ignore marker */
+                               if (ifa->ifa_addr->sa_family == AF_UNSPEC)
+                                       continue;
+
                                if (buflen < len) {
                                        log(LOG_ERR, "%s: len changed?\n",
                                            ifp->if_xname);
diff --git a/test/ifconf/Makefile b/test/ifconf/Makefile
new file mode 100644 (file)
index 0000000..aea49a3
--- /dev/null
@@ -0,0 +1,6 @@
+WARNS= 6
+PROG=  ifconf
+BINDIR=        /usr/local/bin
+NOMAN=
+
+.include <bsd.prog.mk>
diff --git a/test/ifconf/ifconf.c b/test/ifconf/ifconf.c
new file mode 100644 (file)
index 0000000..66fd112
--- /dev/null
@@ -0,0 +1,117 @@
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define NADDRS         128
+
+/* XXX _SIZEOF_ADDR_IFREQ should accept ptr */
+#define _SIZEOF_ADDR_IFREQ1(ifr) \
+       ((ifr)->ifr_addr.sa_len > sizeof(struct sockaddr) ? \
+        (sizeof(struct ifreq) - sizeof(struct sockaddr) + \
+         (ifr)->ifr_addr.sa_len) : sizeof(struct ifreq))
+
+static void
+usage(const char *cmd)
+{
+       fprintf(stderr, "%s [-n naddrs]\n", cmd);
+       exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+       struct ifconf ifc;
+       struct ifreq *ifr;
+       caddr_t pos;
+       int s, naddrs, opt;
+
+       naddrs = NADDRS;
+
+       while ((opt = getopt(argc, argv, "n:")) != -1) {
+               switch (opt) {
+               case 'n':
+                       naddrs = strtol(optarg, NULL, 10);
+                       break;
+               default:
+                       usage(argv[0]);
+               }
+       }
+       if (naddrs <= 0)
+               usage(argv[0]);
+
+       s = socket(AF_INET, SOCK_DGRAM, 0);
+       if (s < 0)
+               err(2, "socket failed");
+
+       memset(&ifc, 0, sizeof(ifc));
+       ifc.ifc_len = sizeof(struct sockaddr_storage) * naddrs;
+       ifc.ifc_buf = malloc(ifc.ifc_len);
+       if (ifc.ifc_buf == NULL)
+               err(2, "malloc failed");
+
+       if (ioctl(s, SIOCGIFCONF, &ifc, sizeof(ifc)) < 0)
+               err(2, "ioctl failed");
+
+       pos = ifc.ifc_buf;
+       while (ifc.ifc_len >= (int)sizeof(*ifr)) {
+               int len;
+
+               ifr = (struct ifreq *)pos;
+               len = _SIZEOF_ADDR_IFREQ1(ifr);
+               if (ifc.ifc_len < len)
+                       err(2, "invalid ifc.ifc_len");
+
+               if (ifr->ifr_addr.sa_family == AF_UNSPEC) {
+                       printf("%s: no address\n", ifr->ifr_name);
+               } else if (ifr->ifr_addr.sa_family == AF_INET ||
+                   ifr->ifr_addr.sa_family == AF_INET6) {
+                       char addr_str[INET6_ADDRSTRLEN];
+                       const void *src;
+                       const char *ret;
+
+                       if (ifr->ifr_addr.sa_family == AF_INET) {
+                               const struct sockaddr_in *in =
+                                   (const struct sockaddr_in *)&ifr->ifr_addr;
+                               src = &in->sin_addr;
+                       } else {
+                               const struct sockaddr_in6 *in6 =
+                                   (const struct sockaddr_in6 *)&ifr->ifr_addr;
+                               src = &in6->sin6_addr;
+                       }
+
+                       ret = inet_ntop(ifr->ifr_addr.sa_family, src,
+                           addr_str, sizeof(addr_str));
+                       if (ret == NULL)
+                               err(2, "inet_ntop failed");
+                       printf("%s: inet%c %s\n", ifr->ifr_name,
+                           ifr->ifr_addr.sa_family == AF_INET ? '4' : '6',
+                           ret);
+               } else if (ifr->ifr_addr.sa_family == AF_LINK) {
+                       const struct sockaddr_dl *dl =
+                           (const struct sockaddr_dl *)&ifr->ifr_addr;
+
+                       printf("%s: link%d\n", ifr->ifr_name, dl->sdl_index);
+               } else {
+                       printf("%s: unknown family %d\n", ifr->ifr_name,
+                           ifr->ifr_addr.sa_family);
+               }
+
+               ifc.ifc_len -= len;
+               pos += len;
+       }
+       if (ifc.ifc_len != 0)
+               printf("ifc_len left %d\n", ifc.ifc_len);
+
+       exit(0);
+}