1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* SIOCGIFCONF:
3
4 The behavior of this ioctl varies across systems.
5
6 The "largest gap" values are the largest number of bytes I've seen
7 left unused at the end of the supplied buffer when there were more
8 entries to return. These values may of coures be dependent on the
9 configurations of the particular systems I was testing with.
10
11 NetBSD 1.5-alpha: The returned ifc_len is the desired amount of
12 space, always. The returned list may be truncated if there isn't
13 enough room; no overrun. Largest gap: 43. (NetBSD now has
14 getifaddrs.)
15
16 BSD/OS 4.0.1 (courtesy djm): The returned ifc_len is equal to or
17 less than the supplied ifc_len. Sometimes the entire buffer is
18 used; sometimes N-1 bytes; occasionally, the buffer must have quite
19 a bit of extra room before the next structure will be added.
20 Largest gap: 39.
21
22 Solaris 7,8: Return EINVAL if the buffer space is too small for all
23 the data to be returned, including ifc_len==0. Solaris is the only
24 system I've found so far that actually returns an error. No gap.
25 However, SIOCGIFNUM may be used to query the number of interfaces.
26
27 Linux 2.2.12 (RH 6.1 dist, x86): The buffer is filled in with as
28 many entries as will fit, and the size used is returned in ifc_len.
29 The list is truncated if needed, with no indication. Largest gap: 31.
30
31 IRIX 6.5: The buffer is filled in with as many entries as will fit
32 in N-1 bytes, and the size used is returned in ifc_len. Providing
33 exactly the desired number of bytes is inadequate; the buffer must
34 be *bigger* than needed. (E.g., 32->0, 33->32.) The returned
35 ifc_len is always less than the supplied one. Largest gap: 32.
36
37 AIX 4.3.3: Sometimes the returned ifc_len is bigger than the
38 supplied one, but it may not be big enough for *all* the
39 interfaces. Sometimes it's smaller than the supplied value, even
40 if the returned list is truncated. The list is filled in with as
41 many entries as will fit; no overrun. Largest gap: 143.
42
43 Older AIX: We're told by W. David Shambroom <DShambroom@gte.com> in
44 PR krb5-kdc/919 that older versions of AIX have a bug in the
45 SIOCGIFCONF ioctl which can cause them to overrun the supplied
46 buffer. However, we don't yet have details as to which version,
47 whether the overrun amount was bounded (e.g., one ifreq's worth) or
48 not, whether it's a real buffer overrun or someone assuming it was
49 because ifc_len was increased, etc. Once we've got details, we can
50 try to work around the problem.
51
52 Digital UNIX 4.0F: If input ifc_len is zero, return an ifc_len
53 that's big enough to include all entries. (Actually, on our
54 system, it appears to be larger than that by 32.) If input ifc_len
55 is nonzero, fill in as many entries as will fit, and set ifc_len
56 accordingly. (Tested only with INIT of zero.)
57
58 So... if the returned ifc_len is bigger than the supplied one,
59 we'll need at least that much space -- but possibly more -- to hold
60 all the results. If the returned value is smaller or the same, we
61 may still need more space.
62
63 Using this ioctl is going to be messy. Let's just hope that
64 getifaddrs() catches on quickly.... */
65
66 #include <errno.h>
67 #include <stdio.h>
68 #include <sys/socket.h>
69 #include <sys/ioctl.h>
70 #include <net/if.h>
71 #include <netinet/in.h>
72
73 #if (defined(sun) || defined(__sun__)) && !defined(SIOCGIFCONF)
74 /* Sun puts socket ioctls in another file. */
75 #include <sys/sockio.h>
76 #endif
77
78 #define INIT 0xc3
79
80 int
main(void)81 main(void)
82 {
83 char buffer[2048];
84 int i, sock, t, olen = -9, omod = -9;
85 struct ifconf ifc;
86 int gap = -1, lastgap = -1;
87
88 sock = socket (AF_INET, SOCK_DGRAM, 0);
89 if (sock < 0) {
90 perror ("socket");
91 exit (1);
92 }
93 printf ("sizeof(struct if_req)=%d\n", sizeof (struct ifreq));
94 for (t = 0; t < sizeof (buffer); t++) {
95 ifc.ifc_len = t;
96 ifc.ifc_buf = buffer;
97 memset (buffer, INIT, sizeof (buffer));
98 i = ioctl (sock, SIOCGIFCONF, (char *) &ifc);
99 if (i < 0) {
100 /* Solaris returns "Invalid argument" if the buffer is too
101 small. AIX and Linux return no error indication. */
102 int e = errno;
103 snprintf (buffer, sizeof(buffer), "SIOCGIFCONF(%d)", t);
104 errno = e;
105 perror (buffer);
106 if (e == EINVAL)
107 continue;
108 fprintf (stderr, "exiting on unexpected error\n");
109 exit (1);
110 }
111 i = sizeof (buffer) - 1;
112 while (buffer[i] == ((char)INIT) && i >= 0)
113 i--;
114 if (omod != i) {
115 /* Okay... the gap computed on the *last* iteration is the
116 largest for that particular size of returned data.
117 Save it, and then start computing gaps for the next
118 bigger size of returned data. If we never get anything
119 bigger back, we discard the newer value and only keep
120 LASTGAP because all we care about is how much slop we
121 need to "prove" that there really weren't any more
122 entries to be returned. */
123 if (gap > lastgap)
124 lastgap = gap;
125 }
126 gap = t - i - 1;
127 if (olen != ifc.ifc_len || omod != i) {
128 printf ("ifc_len in = %4d, ifc_len out = %4d, last mod = %4d\n",
129 t, ifc.ifc_len, i);
130 olen = ifc.ifc_len;
131 omod = i;
132 }
133 }
134 printf ("finished at ifc_len %d\n", t);
135 printf ("largest gap = %d\n", lastgap);
136 exit (0);
137 }
138