On Wed, 11 Nov 2009, Xin LI wrote:

Author: delphij
Date: Wed Nov 11 21:30:58 2009
New Revision: 199201
URL: http://svn.freebsd.org/changeset/base/199201

Log:
 Add interface description capability as inspired by OpenBSD.

Patches like this keep being proposed and rejected, so I was a bit surprised to see it hit the tree (but also see that I missed yet another thread on it on net@ in August, apparently). Most of my thoughts on it are here:

  http://www.freebsd.org/cgi/query-pr.cgi?pr=83622

This commit seems to have quite a few problems, and would likely have benefited from further review before being committed.

Modified: head/contrib/libpcap/inet.c
==============================================================================
--- head/contrib/libpcap/inet.c Wed Nov 11 21:18:27 2009        (r199200)
+++ head/contrib/libpcap/inet.c Wed Nov 11 21:30:58 2009        (r199201)
@@ -403,22 +403,30 @@ add_addr_to_iflist(pcap_if_t **alldevs,
        pcap_addr_t *curaddr, *prevaddr, *nextaddr;
#ifdef SIOCGIFDESCR
        struct ifreq ifrdesc;
+#ifdef __FreeBSD__
+#define _IFDESCRSIZE 64

Seems like this should be #ifdef IFDESCRSIZE rather than ifdef FreeBSD? Also, have you checked with upstream to see if autoconf foo wouldn't be preferred?

+       char ifdescr[_IFDESCRSIZE];
+#else
        char ifdescr[IFDESCRSIZE];
-       int s;
#endif
+       int s;

-#ifdef SIOCGIFDESCR
        /*
         * Get the description for the interface.
         */
        memset(&ifrdesc, 0, sizeof ifrdesc);
        strlcpy(ifrdesc.ifr_name, name, sizeof ifrdesc.ifr_name);
+#ifdef __FreeBSD__
+       ifrdesc.ifr_buffer.buffer = ifdescr;
+       ifrdesc.ifr_buffer.length = _IFDESCRSIZE;
+#else
        ifrdesc.ifr_data = (caddr_t)&ifdescr;

sizeof(ifdescr) would be more robust in the presence of future code changes.

@@ -258,6 +258,12 @@ Disable permanently promiscuous mode.
Another name for the
.Fl alias
parameter.
+.It Cm description Ar value
+Specify a description of the interface.
+This can be used to label interfaces in situations where they may
+otherwise be difficult to distinguish.
+.It Cm -description
+Clear the interface description.

Possibly a comment on length limits would be appropriate?

Modified: head/sbin/ifconfig/ifconfig.c
==============================================================================
--- head/sbin/ifconfig/ifconfig.c       Wed Nov 11 21:18:27 2009        
(r199200)
+++ head/sbin/ifconfig/ifconfig.c       Wed Nov 11 21:30:58 2009        
(r199201)
@@ -83,6 +83,8 @@ static const char rcsid[] =
struct  ifreq ifr;

char    name[IFNAMSIZ];
+char   *descr = NULL;
+size_t descrlen = 64;
int     setaddr;
int     setmask;
int     doalias;
@@ -822,6 +824,36 @@ setifname(const char *val, int dummy __u
        free(newname);
}

+/* ARGSUSED */
+static void
+setifdescr(const char *val, int dummy __unused, int s,
+    const struct afswtch *afp)
+{
+       char *newdescr;
+
+       newdescr = strdup(val);
+       if (newdescr == NULL) {
+               warn("no memory to set ifdescr");
+               return;
+       }
+       ifr.ifr_buffer.buffer = newdescr;
+       ifr.ifr_buffer.length = strlen(newdescr);
+       if (ioctl(s, SIOCSIFDESCR, (caddr_t)&ifr) < 0) {
+               warn("ioctl (set descr)");
+               free(newdescr);
+               return;

I'm confused about the semantics here: is ifr_buffer.buffer guaranteed to be nul-terminated or not? In some cases the code seems to imply nul-termination (especially on the string coming back to userspace), but in other places (such as here), the nul termination is not included in the buffer. It would be nice to consistently do one or the other, and given the way the code is written, I suggest always having the nul termination.

@@ -866,6 +898,23 @@ status(const struct afswtch *afp, const
                printf(" mtu %d", ifr.ifr_mtu);
        putchar('\n');

+       descr = reallocf(descr, descrlen);
+       if (descr != NULL) {
+               do {
+                       ifr.ifr_buffer.buffer = descr;
+                       ifr.ifr_buffer.length = descrlen;
+                       if (ioctl(s, SIOCGIFDESCR, &ifr) == 0) {
+                           if (strlen(descr) > 0)
+                               printf("\tdescription: %s\n", descr);

Here, we seem to assume that the buffer is nul-terminated.

+                           break;
+                       }
+                       if (errno == ENAMETOOLONG) {
+                               descrlen *= 2;
+                               descr = reallocf(descr, descrlen);
+                       }
+               } while (errno == ENAMETOOLONG);
+       }

Shouldn't we be erroring out if reallocf() fails? That seems to be normal practice elsewhere in ifconfig.

Modified: head/sys/net/if.c
==============================================================================
--- head/sys/net/if.c   Wed Nov 11 21:18:27 2009        (r199200)
+++ head/sys/net/if.c   Wed Nov 11 21:30:58 2009        (r199201)
@@ -463,6 +463,8 @@ if_free_internal(struct ifnet *ifp)
#ifdef MAC
        mac_ifnet_destroy(ifp);
#endif /* MAC */
+       if (ifp->if_description != NULL)
+               sbuf_delete(ifp->if_description);
        IF_AFDATA_DESTROY(ifp);
        IF_ADDR_LOCK_DESTROY(ifp);
        ifq_delete(&ifp->if_snd);
@@ -2090,6 +2092,45 @@ ifhwioctl(u_long cmd, struct ifnet *ifp,
                ifr->ifr_phys = ifp->if_physical;
                break;

+       case SIOCGIFDESCR:
+               IF_AFDATA_RLOCK(ifp);
+               if (ifp->if_description == NULL)
+                       error = ENOMSG;
+               else
+                       error = copystr(sbuf_data(ifp->if_description),
+                                       ifr->ifr_buffer.buffer,
+                                       ifr->ifr_buffer.length, NULL);
+               IF_AFDATA_RUNLOCK(ifp);

Isn't copystr() for use only on kernel addresses, and isn't ifr_buffer a user address? And if this is a user address, you can't access it while the IF_AFDATA lock is held.

+       case SIOCSIFDESCR:
+               error = priv_check(td, PRIV_NET_SETIFDESCR);
+               if (error)
+                       return (error);
+
+               IF_AFDATA_WLOCK(ifp);

This is not really what this lock is for, perhaps it's time to introduce an actual per-if lock.

+               if (ifp->if_description == NULL) {
+                       ifp->if_description = sbuf_new_auto();

Can't do potentially sleeping memory allocations with the afdata lock held.

+                       if (ifp->if_description == NULL) {
+                               error = ENOMEM;
+                               IF_AFDATA_WUNLOCK(ifp);
+                               break;
+                       }
+               } else
+                       sbuf_clear(ifp->if_description);
+
+               if (sbuf_copyin(ifp->if_description, ifr->ifr_buffer.buffer,
+                               ifr->ifr_buffer.length) == -1)

Access to user addresses while holding the afdata lock isn't allowed.

Here, ifr_buffer.buffer is treated as non-nul-terminated.

Some administrative limit on string length here would be appropriate; perhaps 128 bytes or 1024 bytes -- panicking because someone passes in a 1GB string by mistake in some C code is not what we want to do.

Modified: head/sys/net/if.h
==============================================================================
--- head/sys/net/if.h   Wed Nov 11 21:18:27 2009        (r199200)
+++ head/sys/net/if.h   Wed Nov 11 21:30:58 2009        (r199201)
@@ -294,6 +294,7 @@ struct      ifreq {
                struct  sockaddr ifru_addr;
                struct  sockaddr ifru_dstaddr;
                struct  sockaddr ifru_broadaddr;
+               struct { size_t length; caddr_t buffer; } ifru_buffer;

While ifreq already contains a pointer, this adds two more fields that vary in size across 32/64-bit, making it more difficult to support in 32-bit processes on a 64-bit kernel.

Modified: head/sys/net/if_var.h
==============================================================================
--- head/sys/net/if_var.h       Wed Nov 11 21:18:27 2009        (r199200)
+++ head/sys/net/if_var.h       Wed Nov 11 21:30:58 2009        (r199201)
@@ -198,6 +198,7 @@ struct ifnet {
        void    *if_pf_kif;
        void    *if_lagg;               /* lagg glue */
        u_char   if_alloctype;          /* if_type at time of allocation */
+       struct sbuf *if_description;    /* interface description */

        /*
         * Spare fields are added so that we can modify sensitive data
@@ -205,7 +206,7 @@ struct ifnet {
         * be used with care where binary compatibility is required.
         */
        char     if_cspare[3];
-       void    *if_pspare[8];
+       void    *if_pspare[7];
        int     if_ispare[4];
};

Could you confirm that ifnet hasn't changed size on arm, i386, and amd64? This looks like fairly dubious use of a spare field -- normally I'd expect to see if_cspare, which is 3 bytes long, remain after if_alloctype, and that pspare[0] be replaced in-place to prevent alignment decisions in the compiler from spacing out the structure further (i.e., adding another byte of padding on i386 and another 5 bytes of padding on amd64 after if_cspare and before if_pspare).

Robert N M Watson
Computer Laboratory
University of Cambridge
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to