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.
|