Discussion:
racoon broken in -current: grab_myaddrs and SIOCGIFCONF
(too old to reply)
Greg Troxel
2007-08-29 14:20:23 UTC
Permalink
I have found the problem. Is racoon working for anyone on -current
without a listen statement? Am I really the only one who uses it?

in grabmyaddr.c:grab_myaddrs, the code iterates over the returned
addresses. There's something wrong with length calculations and perhaps
padding (not sure if kernel or racoon), and thus racoon isn't processing
any of the actual addresses.

See _IFREQ_LEN, which does not pad to alignment boundaries. But the
kernel doesn't seem to need to pad.

The most suspicious thing is length 17 on the sockaddr_dl.

at the top of the while:

plog(LLV_ERROR, LOCATION, NULL,
"get_myaddrs: ifr %p name %s family %d len %d ssi %d\n",
ifr,
ifr->ifr_name,
ifr->ifr_addr.sa_family,
ifr->ifr_addr.sa_len,
sizeof(struct ifreq));




2007-08-29 10:08:37: ERROR: grabmyaddrs top
2007-08-29 10:08:37: ERROR: grabmyaddrs before SIOCIFCONF
2007-08-29 10:08:37: ERROR: get_myaddrs: ifr 0x80ef800 name wm0 family 18 len 17 ssi 144
2007-08-29 10:08:37: ERROR: Unknown AF 18
2007-08-29 10:08:37: ERROR: get_myaddrs: ifr 0x80ef890
2007-08-29 10:08:37: ERROR: get_myaddrs: ifr 0x80ef890 name MESS family 0 len 0 ssi 144

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Martin Husemann
2007-08-29 14:38:18 UTC
Permalink
Post by Greg Troxel
See _IFREQ_LEN, which does not pad to alignment boundaries. But the
kernel doesn't seem to need to pad.
The most suspicious thing is length 17 on the sockaddr_dl.
You mean HAVE_GETIFADDRS is not defined when compiling this?
That sure sounds like a bug to me.

Martin

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Greg Troxel
2007-08-30 15:48:53 UTC
Permalink
racoon doesn't work in current because SIOCGIFCONF is broken. (I know
racoon should probably not use SIOCGIFCONF, but instead getifaddrs. I
won't argue, but SIOCGIFCONF should work in any case.)

As far as I can tell there are two things wrong:

* commit to sys/net/if.h to include sockaddr_storage in struct ifreq.
This breaks ABI compatability and things don't seem versioned. I
don't understand the rationale for this change.

* sys/net/if.c:ifconf() uses struct sockaddr for size tests when the
dominant member of the union is sockaddr_storage. This used to be
correct (or rather a latent bug) before the change to sockaddr_storage
in if.h.

Right now, sockaddrs that fit in struct sockaddr cause a struct ifreq to
be the full size (144 bytes). But ones that don't (AF_LINK, AF_INET6)
are made to fit exactly, which makes them shorter. I have a kludge in
racoon tomatch this, and it then works.

I propose the following:

1. revert addition of sockaddr_storage. I would claim that using struct
sockaddr to place longer things is wrong; that's what sockaddr_storage
is for. But perhaps other uses of struct ifreq don't have this issue.
This restores ABI compat for SIOCGIFCONF, and makes step 2 protective
rather than critical.

2. fix the code in if.c:ifconf() to use sizeof(struct ifconf.ifr_ifru)
instead of knowing the internal structure, so that each reply will be
bigger than a struct ifreq only if the address doesn't fit (the original
API).

If step 1 is also done, step 2 will guard against unintentional changes
restore ABI compatibility. If 1 is not done, it will at least restore
API compatability.


Christos: can you explain which ioctls were trouble? I don't follow
from your commit message below.

Thanks,
Greg


src/sys/net/if.h:
revision 1.124
date: 2007/05/29 21:32:29; author: christos; state: Exp; lines: +2 -1
Add a sockaddr_storage member to "struct ifreq" maintaining backwards
compatibility with the older ioctls. This avoids stack smashing and
abuse of "struct sockaddr" when ioctls placed "struct sockaddr_foo's" that
were longer than "struct sockaddr".
XXX: Some of the emulations might be broken; I tried to add code for
them but I did not test them.

src/sys/net/if.c:
revision 1.190
date: 2007/06/01 09:35:47; author: enami; state: Exp; lines: +26 -32
Fix some bugs in ifconf():
- maintain space left correctly. the pointer is advanced by the size
of struct ifreq when length of address is small.
- single sizeof operator is enough to take the size of struct.
- the type of `sz' must be singed type since it is/was compared against to
the variable which may become negative.
- no need to traverse rest of interfaces once we got an error. note that
the latter `break' statement was inside inner loop.


--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Quentin Garnier
2007-08-30 15:59:02 UTC
Permalink
Post by Greg Troxel
racoon doesn't work in current because SIOCGIFCONF is broken. (I know
racoon should probably not use SIOCGIFCONF, but instead getifaddrs. I
won't argue, but SIOCGIFCONF should work in any case.)
* commit to sys/net/if.h to include sockaddr_storage in struct ifreq.
This breaks ABI compatability and things don't seem versioned. I
don't understand the rationale for this change.
Yes they are. Granted, the code is hard to understand.

Besides, SIOCGIFCONF is explicitely versioned.

[...]
Post by Greg Troxel
Christos: can you explain which ioctls were trouble? I don't follow
from your commit message below.
The issue is that ifreq was used to store stuff larger that struct
sockaddr. Christos's change makes ifreq suitable for any address
family, which is a good thing IMO.
--
Quentin Garnier - ***@cubidou.net - ***@NetBSD.org
"You could have made it, spitting out benchmarks
Owe it to yourself not to fail"
Amplifico, Spitting Out Benchmarks, Hometakes Vol. 2, 2005.
Christos Zoulas
2007-08-31 05:53:47 UTC
Permalink
On Aug 30, 11:48am, ***@ir.bbn.com (Greg Troxel) wrote:
-- Subject: Re: racoon broken in -current: grab_myaddrs and SIOCGIFCONF

| racoon doesn't work in current because SIOCGIFCONF is broken. (I know
| racoon should probably not use SIOCGIFCONF, but instead getifaddrs. I
| won't argue, but SIOCGIFCONF should work in any case.)
|
| As far as I can tell there are two things wrong:
|
| * commit to sys/net/if.h to include sockaddr_storage in struct ifreq.
| This breaks ABI compatability and things don't seem versioned. I
| don't understand the rationale for this change.

The versioning happens automagically because the ioctl number changes
since sizeof(struct ifreq) changes.

| * sys/net/if.c:ifconf() uses struct sockaddr for size tests when the
| dominant member of the union is sockaddr_storage. This used to be
| correct (or rather a latent bug) before the change to sockaddr_storage
| in if.h.

I am not sure about that. I will have to look, but now I have no source
access and I am on dialup :-(

| Right now, sockaddrs that fit in struct sockaddr cause a struct ifreq to
| be the full size (144 bytes). But ones that don't (AF_LINK, AF_INET6)
| are made to fit exactly, which makes them shorter. I have a kludge in
| racoon tomatch this, and it then works.
|
| I propose the following:
|
| 1. revert addition of sockaddr_storage. I would claim that using struct
| sockaddr to place longer things is wrong; that's what sockaddr_storage
| is for. But perhaps other uses of struct ifreq don't have this issue.
| This restores ABI compat for SIOCGIFCONF, and makes step 2 protective
| rather than critical.
|
| 2. fix the code in if.c:ifconf() to use sizeof(struct ifconf.ifr_ifru)
| instead of knowing the internal structure, so that each reply will be
| bigger than a struct ifreq only if the address doesn't fit (the original
| API).
|
| If step 1 is also done, step 2 will guard against unintentional changes
| restore ABI compatibility. If 1 is not done, it will at least restore
| API compatability.
|
|
| Christos: can you explain which ioctls were trouble? I don't follow
| from your commit message below.

Ones where sizeof(ioctl struct used) == sizeof(struct oifreq). But that
has been fixed since.

christos

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Greg Troxel
2007-08-31 14:39:54 UTC
Permalink
I have fixed the problems in the SIOCGIFCONF implementation, so that
racoon in current works again. The basic issue was that
sys/net/if.c:ifconf() was doing the wrong length calculation. I've
documented the expected behavior of the function, written down
invariants, and also fixed some subtle bugs where the space variable was
decremented even if there was not adequate space. Because of
sockaddr_storage, the hairy case where ifreq is expanded due to
overlength sockaddrs can't occur, so I replaced the complex code with a
KASSERT. I also added KASSERTs to check a bunch of things; clearly this
code is too tricky. Because of that, I split out the handling of the
cases where data is returned vs getting sizes more explicitly, going for
readability and being clearly correct rather than compact.

We indeed need a regression test. Does anyone want to write a program
that calls getifaddrs and also does SIOCGIFCONF and compares the
results?

Here's the new code, and then the diff. I will commit this in a week or
so if there are no objections.

/*
* 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.
*/
/*ARGSUSED*/
int
ifconf(u_long cmd, void *data)
{
struct ifconf *ifc = (struct ifconf *)data;
struct ifnet *ifp;
struct ifaddr *ifa;
struct ifreq ifr, *ifrp;
int space, error = 0;
const int sz = (int)sizeof(struct ifreq);

if ((ifrp = ifc->ifc_req) == NULL)
space = 0;
else
space = ifc->ifc_len;
IFNET_FOREACH(ifp) {
(void)strncpy(ifr.ifr_name, ifp->if_xname,
sizeof(ifr.ifr_name));
if (ifr.ifr_name[sizeof(ifr.ifr_name) - 1] != '\0')
return ENAMETOOLONG;
if (TAILQ_EMPTY(&ifp->if_addrlist)) {
/* Interface with no addresses - send zero sockaddr. */
memset(&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr));
if (ifrp != NULL)
{
if (space >= sz) {
error = copyout(&ifr, ifrp, sz);
if (error != 0)
return (error);
ifrp++; space -= sz;
}
}
else
space += sz;
continue;
}

TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
struct sockaddr *sa = ifa->ifa_addr;
/* all sockaddrs must fit in sockaddr_storage */
KASSERT(sa->sa_len <= sizeof(ifr.ifr_ifru));

if (ifrp != NULL)
{
ifr.ifr_addr = *sa;
if (space >= sz) {
error = copyout(&ifr, ifrp, sz);
if (error != 0)
return (error);
ifrp++; space -= sz;
}
}
else
space += sz;
}
}
if (ifrp != NULL)
{
KASSERT(0 <= space && space <= ifc->ifc_len);
ifc->ifc_len -= space;
}
else
{
KASSERT(space >= 0);
ifc->ifc_len = space;
}
return (0);
}

--- if.c 31 Aug 2007 08:23:43 -0400 1.197
+++ if.c 31 Aug 2007 09:31:27 -0400
@@ -1602,6 +1602,28 @@ ifioctl(struct socket *so, u_long cmd, v
* 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.
*/
/*ARGSUSED*/
int
@@ -1612,8 +1634,7 @@ ifconf(u_long cmd, void *data)
struct ifaddr *ifa;
struct ifreq ifr, *ifrp;
int space, error = 0;
- const int sz = offsetof(struct ifreq, ifr_ifru) +
- sizeof(struct sockaddr);
+ const int sz = (int)sizeof(struct ifreq);

if ((ifrp = ifc->ifc_req) == NULL)
space = 0;
@@ -1625,46 +1646,51 @@ ifconf(u_long cmd, void *data)
if (ifr.ifr_name[sizeof(ifr.ifr_name) - 1] != '\0')
return ENAMETOOLONG;
if (TAILQ_EMPTY(&ifp->if_addrlist)) {
+ /* Interface with no addresses - send zero sockaddr. */
memset(&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr));
- if (space >= sz) {
- error = copyout(&ifr, ifrp, sz);
- if (error != 0)
- return (error);
- ifrp++;
+ if (ifrp != NULL)
+ {
+ if (space >= sz) {
+ error = copyout(&ifr, ifrp, sz);
+ if (error != 0)
+ return (error);
+ ifrp++; space -= sz;
+ }
}
- space -= sizeof(struct ifreq);
+ else
+ space += sz;
continue;
}

TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
struct sockaddr *sa = ifa->ifa_addr;
- if (sa->sa_len <= sizeof(*sa)) {
+ /* all sockaddrs must fit in sockaddr_storage */
+ KASSERT(sa->sa_len <= sizeof(ifr.ifr_ifru));
+
+ if (ifrp != NULL)
+ {
ifr.ifr_addr = *sa;
if (space >= sz) {
error = copyout(&ifr, ifrp, sz);
- ifrp++;
+ if (error != 0)
+ return (error);
+ ifrp++; space -= sz;
}
- space -= sizeof(struct ifreq);
- } else {
- space -= sa->sa_len - sizeof(*sa) + sz;
- if (space < 0)
- continue;
- error = copyout(&ifr, ifrp,
- sizeof(ifr.ifr_name));
- if (error == 0)
- error = copyout(sa,
- &ifrp->ifr_addr, sa->sa_len);
- ifrp = (struct ifreq *)
- (sa->sa_len + (char *)&ifrp->ifr_addr);
}
- if (error != 0)
- return (error);
+ else
+ space += sz;
}
}
if (ifrp != NULL)
+ {
+ KASSERT(0 <= space && space <= ifc->ifc_len);
ifc->ifc_len -= space;
+ }
else
- ifc->ifc_len = -space;
+ {
+ KASSERT(space >= 0);
+ ifc->ifc_len = space;
+ }
return (0);
}


--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
David Young
2007-09-02 02:13:10 UTC
Permalink
Post by Greg Troxel
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
struct sockaddr *sa = ifa->ifa_addr;
/* all sockaddrs must fit in sockaddr_storage */
KASSERT(sa->sa_len <= sizeof(ifr.ifr_ifru));
if (ifrp != NULL)
{
ifr.ifr_addr = *sa;
That will truncate the sockaddr you copy out, if sa->sa_len > sizeof(*sa).
Maybe use sockaddr_copy(&ifr.ifr_addr, sizeof(ifr.ifr_ifru), sa)?

Dave
--
David Young OJC Technologies
***@ojctech.com Urbana, IL * (217) 278-3933 ext 24

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Greg Troxel
2007-09-11 19:51:03 UTC
Permalink
I have fixed the problems in the SIOCGIFCONF implementation, so that
racoon in current works again.

I received no objections and have committed the fix.


--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-***@muc.de
Loading...