Subject: Re: samba and SIOCGIFCONF
To: None <M.Drochner@fz-juelich.de>
From: Greg Troxel <gdt@ir.bbn.com>
List: tech-net
Date: 12/21/2007 19:39:24
  Is there some documentation or example how to scan the
  result of an SIOCGIFCONF ioctl correctly now?

Yes, it's in the comment in src/sys/net/if.c:ifconf().  Basically, if
the sa_len is > the size of the union in ifreq, the new ifreq starts at
the end of the included sockaddr.  otherwise it's at the end of the
ifreq.  This is the same rule as always.  In current, the sockaddr is
always <= the union (becaues it's sockaddr_storage), so it's just ifr+1.
But the code has to be ok on pre-current, and other systems.

The dhcp

/*
 * Return interface configuration
 * of system.  List may be used
 * in later ioctl's (above) to get
 * other information.
 *
 * Each record is a struct ifreq.  Before the addition of
 * sockaddr_storage, the API rule was that sockaddr flavors that did
 * not fit would extend beyond the struct ifreq, with the next struct
 * ifreq starting sa_len beyond the struct sockaddr.  Because the
 * union in struct ifreq includes struct sockaddr_storage, every kind
 * of sockaddr must fit.  Thus, there are no longer any overlength
 * records.
 *
 * Records are added to the user buffer if they fit, and ifc_len is
 * adjusted to the length that was written.  Thus, the user is only
 * assured of getting the complete list if ifc_len on return is at
 * least sizeof(struct ifreq) less than it was on entry.
 *
 * If the user buffer pointer is NULL, this routine copies no data and
 * returns the amount of space that would be needed.
 *
 * Invariants:
 * ifrp points to the next part of the user's buffer to be used.  If
 * ifrp != NULL, space holds the number of bytes remaining that we may
 * write at ifrp.  Otherwise, space holds the number of bytes that
 * would have been written had there been adequate space.
 */

I believe the dhcp code in src/dist/dhcp/common/discover.c is correct:

#ifdef HAVE_SA_LEN
		/*
		 * Classically, struct ifreq used with SIOCGIFCONF had
		 * a union where struct sockaddr was the largest
		 * member.  If the address fit in the union, the next
		 * ifreq followed immediately.  If not, the next ifreq
		 * followed the end of the actual sockaddr.  In
		 * NetBSD-current after ~2007-05, ifreq has a
		 * sockaddr_storage member, and the next ifreq follows
		 * the current ifreq always, because no sockaddr can
		 * be bigger than sockaddr_storage.  Thus, compare the
		 * length to the union's size, not struct sockaddr.
		 */
		if (ifp -> ifr_addr.sa_len > sizeof (ifp->ifr_ifru)) {
			if (sizeof(struct ifreq) + ifp->ifr_addr.sa_len >
			    sizeof(ifcpy))
				break;
			/* XXX This copies IFNAMSIZ bytes too many. */ 
			memcpy(&ifcpy, (caddr_t)ic.ifc_req + i, 
			    sizeof(struct ifreq) + ifp->ifr_addr.sa_len);
			i += offsetof(struct ifreq, ifr_ifru) +
			    ifp -> ifr_addr.sa_len;
		} else
#endif
			i += sizeof *ifp;






  but anyway, I've patched samba to use getifaddrs() if possible.
  It works well now for me.

That's a better approach anyway.  As you are probably getting the
impression, SIOCGIFCONF is too hard to use.