OpenBSD: fix lookup of network interfaces
[openafs.git] / src / rx / rx_getaddr.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13
14 #ifndef KERNEL
15 #ifndef AFS_NT40_ENV
16 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <sys/time.h>
19 #include <net/if.h>
20 #include <netinet/in.h>
21 #include <sys/ioctl.h>
22 #include <string.h>
23 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
24 #include <sys/sysctl.h>
25 #include <net/route.h>
26 #include <net/if_dl.h>
27 #endif
28
29 /*
30  * By including this, we get any system dependencies. In particular,
31  * the pthreads for solaris requires the socket call to be mapped.
32  */
33 #include "rx.h"
34 #include "rx_globals.h"
35 #endif /* AFS_NT40_ENV */
36 #else /* KERNEL */
37 #ifdef UKERNEL
38 #include "rx/rx_kcommon.h"
39 #else /* UKERNEL */
40 #include "rx/rx.h"
41 #endif /* UKERNEL */
42 #endif /* KERNEL */
43
44 #define NIFS            512
45
46 #if defined(AFS_USR_DFBSD_ENV)
47 #include <net/if.h>
48 #include <sys/sockio.h>
49 #endif
50
51 #ifdef KERNEL
52 /* only used for generating random noise */
53
54 afs_uint32 rxi_tempAddr = 0;    /* default attempt */
55
56 /* set the advisory noise */
57 void
58 rxi_setaddr(afs_uint32 x)
59 {
60     rxi_tempAddr = x;
61 }
62
63 /* get approx to net addr */
64 afs_uint32
65 rxi_getaddr(void)
66 {
67     return rxi_tempAddr;
68 }
69
70 #endif /* KERNEL */
71
72 #ifndef KERNEL
73
74 /* to satisfy those who call setaddr */
75 void
76 rxi_setaddr(afs_uint32 x)
77 {
78 }
79
80 #endif /* !KERNEL */
81
82
83 #if !defined(AFS_NT40_ENV)
84 /* For NT, rxi_getaddr has moved to rx_user.c. rxi_GetIfInfo is called by
85  * rx_Init which sets up the list of addresses for us.
86  */
87
88 #ifndef KERNEL
89
90 /* Return our internet address as a long in network byte order.  Returns zero
91  * if it can't find one.
92  */
93 afs_uint32
94 rxi_getaddr(void)
95 {
96     afs_uint32 buffer[1024];
97     int count;
98
99     count = rx_getAllAddr(buffer, 1024);
100     if (count > 0)
101         return buffer[0];       /* returns the first address */
102     else
103         return count;
104 }
105
106 #endif /* !KERNEL */
107
108 #if !defined(KERNEL) || defined(UKERNEL)
109
110 #ifndef MAX
111 #define MAX(A,B) (((A)<(B)) ? (B) : (A))
112 #endif
113
114 #ifdef UKERNEL
115 #undef ioctl
116 #undef socket
117 #endif /* UKERNEL */
118
119 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
120 #define ROUNDUP(a) \
121         ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
122 #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
123
124 static void
125 rt_xaddrs(caddr_t cp, caddr_t cplim, struct rt_addrinfo *rtinfo)
126 {
127     struct sockaddr *sa;
128     int i;
129
130     memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info));
131     for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) {
132         if ((rtinfo->rti_addrs & (1 << i)) == 0)
133             continue;
134         rtinfo->rti_info[i] = sa = (struct sockaddr *)cp;
135         ADVANCE(cp, sa);
136     }
137 }
138 #endif
139
140
141 /* this function returns the total number of interface addresses 
142 ** the buffer has to be passed in by the caller
143 */
144 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
145 #if defined(AFS_OBSD42_ENV)
146 void
147 ifm_fixversion(char *buffer, size_t *size) {
148     struct if_msghdr *ifm;
149     char *b = buffer;
150     char *s, *t;
151
152     if ((t = malloc(*size)) != NULL) {
153         memcpy(t, buffer, *size);
154
155         for (s = t; s < t + *size; s += ifm->ifm_msglen) {
156             ifm = (struct if_msghdr *)s;
157
158             if (ifm->ifm_version == RTM_VERSION) {
159                 memcpy(b, ifm, ifm->ifm_msglen);
160                 b += ifm->ifm_msglen;
161             }
162         }
163
164         free(t);
165
166         *size = b - buffer;
167     }
168 }
169 #endif
170
171 int
172 rx_getAllAddr_internal(afs_uint32 buffer[], int maxSize, int loopbacks)
173 {
174     size_t needed;
175     int mib[6];
176     struct if_msghdr *ifm, *nextifm;
177     struct ifa_msghdr *ifam;
178     struct sockaddr_dl *sdl;
179     struct rt_addrinfo info;
180     char *buf, *lim, *next;
181     int count = 0, addrcount = 0;
182
183     mib[0] = CTL_NET;
184     mib[1] = PF_ROUTE;
185     mib[2] = 0;
186     mib[3] = AF_INET;           /* address family */
187     mib[4] = NET_RT_IFLIST;
188     mib[5] = 0;
189     if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
190         return 0;
191     if ((buf = malloc(needed)) == NULL)
192         return 0;
193     if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
194         free(buf);
195         return 0;
196     }
197 #if defined(AFS_OBSD42_ENV)
198     ifm_fixversion(buf, &needed);
199 #endif
200     lim = buf + needed;
201     next = buf;
202     while (next < lim) {
203         ifm = (struct if_msghdr *)next;
204         if (ifm->ifm_type != RTM_IFINFO) {
205             dpf(("out of sync parsing NET_RT_IFLIST\n"));
206             free(buf);
207             return 0;
208         }
209         sdl = (struct sockaddr_dl *)(ifm + 1);
210         next += ifm->ifm_msglen;
211         ifam = NULL;
212         addrcount = 0;
213         while (next < lim) {
214             nextifm = (struct if_msghdr *)next;
215             if (nextifm->ifm_type != RTM_NEWADDR)
216                 break;
217             if (ifam == NULL)
218                 ifam = (struct ifa_msghdr *)nextifm;
219             addrcount++;
220             next += nextifm->ifm_msglen;
221         }
222         if ((ifm->ifm_flags & IFF_UP) == 0)
223             continue;           /* not up */
224         while (addrcount > 0) {
225             struct sockaddr_in *a;
226
227             info.rti_addrs = ifam->ifam_addrs;
228
229             /* Expand the compacted addresses */
230             rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam,
231                       &info);
232             if (info.rti_info[RTAX_IFA]->sa_family != AF_INET) {
233                 addrcount--;
234                 continue;
235             }
236             a = (struct sockaddr_in *) info.rti_info[RTAX_IFA];
237
238             if (count >= maxSize)       /* no more space */
239                 dpf(("Too many interfaces..ignoring 0x%x\n",
240                        a->sin_addr.s_addr));
241             else if (!loopbacks && a->sin_addr.s_addr == htonl(0x7f000001)) {
242                 addrcount--;
243                 continue;       /* skip loopback address as well. */
244             } else if (loopbacks && ifm->ifm_flags & IFF_LOOPBACK) {
245                 addrcount--;
246                 continue;       /* skip aliased loopbacks as well. */
247             } else
248                 buffer[count++] = a->sin_addr.s_addr;
249             addrcount--;
250             ifam = (struct ifa_msghdr *)((char *)ifam + ifam->ifam_msglen);
251         }
252     }
253     free(buf);
254     return count;
255 }
256
257 int
258 rx_getAllAddrMaskMtu(afs_uint32 addrBuffer[], afs_uint32 maskBuffer[],
259                      afs_uint32 mtuBuffer[], int maxSize)
260 {
261     int s;
262
263     size_t needed;
264     int mib[6];
265     struct if_msghdr *ifm, *nextifm;
266     struct ifa_msghdr *ifam;
267     struct sockaddr_dl *sdl;
268     struct rt_addrinfo info;
269     char *buf, *lim, *next;
270     int count = 0, addrcount = 0;
271
272     mib[0] = CTL_NET;
273     mib[1] = PF_ROUTE;
274     mib[2] = 0;
275     mib[3] = AF_INET;           /* address family */
276     mib[4] = NET_RT_IFLIST;
277     mib[5] = 0;
278     if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
279         return 0;
280     if ((buf = malloc(needed)) == NULL)
281         return 0;
282     if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
283         free(buf);
284         return 0;
285     }
286 #if defined(AFS_OBSD42_ENV)
287     ifm_fixversion(buf, &needed);
288 #endif
289     s = socket(PF_INET, SOCK_DGRAM, 0);
290     if (s < 0)
291         return 0;
292     lim = buf + needed;
293     next = buf;
294     while (next < lim) {
295         ifm = (struct if_msghdr *)next;
296         if (ifm->ifm_type != RTM_IFINFO) {
297             dpf(("out of sync parsing NET_RT_IFLIST\n"));
298             free(buf);
299             return 0;
300         }
301         sdl = (struct sockaddr_dl *)(ifm + 1);
302         next += ifm->ifm_msglen;
303         ifam = NULL;
304         addrcount = 0;
305         while (next < lim) {
306             nextifm = (struct if_msghdr *)next;
307             if (nextifm->ifm_type != RTM_NEWADDR)
308                 break;
309             if (ifam == NULL)
310                 ifam = (struct ifa_msghdr *)nextifm;
311             addrcount++;
312             next += nextifm->ifm_msglen;
313         }
314         if ((ifm->ifm_flags & IFF_UP) == 0)
315             continue;           /* not up */
316         while (addrcount > 0) {
317             struct sockaddr_in *a;
318
319             info.rti_addrs = ifam->ifam_addrs;
320
321             /* Expand the compacted addresses */
322             rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam,
323                       &info);
324             if (info.rti_info[RTAX_IFA]->sa_family != AF_INET) {
325                 addrcount--;
326                 continue;
327             }
328             a = (struct sockaddr_in *) info.rti_info[RTAX_IFA];
329
330             if (a->sin_addr.s_addr != htonl(0x7f000001) ) {
331                 if (count >= maxSize) { /* no more space */
332                     dpf(("Too many interfaces..ignoring 0x%x\n",
333                            a->sin_addr.s_addr));
334                 } else {
335                     struct ifreq ifr;
336                     
337                     addrBuffer[count] = a->sin_addr.s_addr;
338                     a = (struct sockaddr_in *) info.rti_info[RTAX_NETMASK];
339                     if (a)
340                         maskBuffer[count] = a->sin_addr.s_addr;
341                     else
342                         maskBuffer[count] = htonl(0xffffffff);
343                     memset(&ifr, 0, sizeof(ifr));
344                     ifr.ifr_addr.sa_family = AF_INET;
345                     strncpy(ifr.ifr_name, sdl->sdl_data, sdl->sdl_nlen);
346                     if (ioctl(s, SIOCGIFMTU, (caddr_t) & ifr) < 0)
347                         mtuBuffer[count] = htonl(1500);
348                     else
349                         mtuBuffer[count] = htonl(ifr.ifr_mtu);
350                     count++;
351                 }
352             }
353             addrcount--;
354             ifam = (struct ifa_msghdr *)((char *)ifam + ifam->ifam_msglen);
355         }
356     }
357     free(buf);
358     return count;
359 }
360
361
362 int
363 rx_getAllAddr(afs_uint32 buffer[], int maxSize)
364 {
365     return rx_getAllAddr_internal(buffer, maxSize, 0);
366 }
367 /* this function returns the total number of interface addresses
368 ** the buffer has to be passed in by the caller
369 */
370 #else /* UKERNEL indirectly, on DARWIN or XBSD */
371 static int
372 rx_getAllAddr_internal(afs_uint32 buffer[], int maxSize, int loopbacks)
373 {
374     int s;
375     int i, len, count = 0;
376     struct ifconf ifc;
377     struct ifreq ifs[NIFS], *ifr;
378     struct sockaddr_in *a;
379     /* can't ever be AFS_DARWIN_ENV or AFS_XBSD_ENV, no? */
380 #if    defined(AFS_AIX41_ENV) || defined (AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
381     char *cp, *cplim, *cpnext;  /* used only for AIX 41 */
382 #endif
383
384     s = socket(AF_INET, SOCK_DGRAM, 0);
385     if (s < 0)
386         return 0;
387     ifc.ifc_len = sizeof(ifs);
388     ifc.ifc_buf = (caddr_t) ifs;
389     i = ioctl(s, SIOCGIFCONF, &ifc);
390     if (i < 0)
391         return 0;
392     len = ifc.ifc_len / sizeof(struct ifreq);
393     if (len > NIFS)
394         len = NIFS;
395 #if    defined(AFS_AIX41_ENV) || defined (AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
396     if (ifc.ifc_len > sizeof(ifs))      /* safety check */
397         ifc.ifc_len = sizeof(ifs);
398     for (cp = (char *)ifc.ifc_buf, cplim = ifc.ifc_buf + ifc.ifc_len;
399          cp < cplim;
400 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
401          cp += _SIZEOF_ADDR_IFREQ(*ifr)
402 #else
403 #ifdef AFS_AIX51_ENV
404          cp = cpnext
405 #else
406          cp += sizeof(ifr->ifr_name) + MAX(a->sin_len, sizeof(*a))
407 #endif
408 #endif
409         ) 
410 #else
411     for (i = 0; i < len; ++i) 
412 #endif
413     {
414 #if    defined(AFS_AIX41_ENV) || defined (AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
415         ifr = (struct ifreq *)cp;
416 #else
417         ifr = &ifs[i];
418 #endif
419         a = (struct sockaddr_in *)&ifr->ifr_addr;
420 #ifdef AFS_AIX51_ENV
421         cpnext = cp + sizeof(ifr->ifr_name) + MAX(a->sin_len, sizeof(*a));
422 #endif
423         if (a->sin_family != AF_INET)
424             continue;
425         if (ioctl(s, SIOCGIFFLAGS, ifr) < 0) {
426             perror("SIOCGIFFLAGS");
427             continue;           /* ignore this address */
428         }
429         if (a->sin_addr.s_addr != 0) {
430             if (!loopbacks) {
431                 if (a->sin_addr.s_addr == htonl(0x7f000001)) 
432                     continue;   /* skip loopback address as well. */
433             } else {
434                 if (ifr->ifr_flags & IFF_LOOPBACK) 
435                     continue;   /* skip aliased loopbacks as well. */
436             }
437             if (count >= maxSize)       /* no more space */
438                 dpf(("Too many interfaces..ignoring 0x%x\n",
439                        a->sin_addr.s_addr));
440             else
441                 buffer[count++] = a->sin_addr.s_addr;
442         }
443     }
444     close(s);
445     return count;
446 }
447
448 int
449 rx_getAllAddr(afs_uint32 buffer[], int maxSize)
450 {
451     return rx_getAllAddr_internal(buffer, maxSize, 0);
452 }
453
454 /* this function returns the total number of interface addresses
455  * the buffer has to be passed in by the caller. It also returns
456  * the interface mask. If AFS_USERSPACE_IP_ADDR is defined, it
457  * gets the mask which is then passed into the kernel and is used
458  * by afsi_SetServerIPRank().
459  */
460 int
461 rx_getAllAddrMaskMtu(afs_uint32 addrBuffer[], afs_uint32 maskBuffer[],
462                      afs_uint32 mtuBuffer[], int maxSize)
463 {
464     int i, count = 0;
465 #if defined(AFS_USERSPACE_IP_ADDR)
466     int s, len;
467     struct ifconf ifc;
468     struct ifreq ifs[NIFS], *ifr;
469     struct sockaddr_in *a;
470 #endif
471
472 #if     defined(AFS_AIX41_ENV) || defined(AFS_USR_AIX_ENV)
473     char *cp, *cplim;           /* used only for AIX 41 */
474 #endif
475
476 #if !defined(AFS_USERSPACE_IP_ADDR)
477     count = rx_getAllAddr_internal(addrBuffer, 1024, 0);
478     for (i = 0; i < count; i++) {
479         maskBuffer[i] = htonl(0xffffffff);
480         mtuBuffer[i] = htonl(1500);
481     }
482     return count;
483 #else /* AFS_USERSPACE_IP_ADDR */
484     s = socket(AF_INET, SOCK_DGRAM, 0);
485     if (s < 0)
486         return 0;
487
488     ifc.ifc_len = sizeof(ifs);
489     ifc.ifc_buf = (caddr_t) ifs;
490     i = ioctl(s, SIOCGIFCONF, &ifc);
491     if (i < 0) {
492         close(s);
493         return 0;
494     }
495     len = ifc.ifc_len / sizeof(struct ifreq);
496     if (len > NIFS)
497         len = NIFS;
498
499 #if     defined(AFS_AIX41_ENV) || defined(AFS_USR_AIX_ENV)
500     if (ifc.ifc_len > sizeof(ifs))      /* safety check */
501         ifc.ifc_len = sizeof(ifs);
502     for (cp = (char *)ifc.ifc_buf, cplim = ifc.ifc_buf + ifc.ifc_len;
503          cp < cplim;
504          cp += sizeof(ifr->ifr_name) + MAX(a->sin_len, sizeof(*a))) {
505         ifr = (struct ifreq *)cp;
506 #else
507     for (i = 0; i < len; ++i) {
508         ifr = &ifs[i];
509 #endif
510         a = (struct sockaddr_in *)&ifr->ifr_addr;
511         if (a->sin_addr.s_addr != 0 && a->sin_family == AF_INET) {
512
513             if (ioctl(s, SIOCGIFFLAGS, ifr) < 0) {
514                 perror("SIOCGIFFLAGS");
515                 continue;       /* ignore this address */
516             }
517
518             if (a->sin_addr.s_addr == htonl(0x7f000001) )
519                 continue;   /* skip loopback address as well. */
520
521             if (count >= maxSize) {     /* no more space */
522                 dpf(("Too many interfaces..ignoring 0x%x\n",
523                        a->sin_addr.s_addr));
524                 continue;
525             }
526
527             addrBuffer[count] = a->sin_addr.s_addr;
528
529             if (ioctl(s, SIOCGIFNETMASK, (caddr_t) ifr) < 0) {
530                 perror("SIOCGIFNETMASK");
531                 maskBuffer[count] = htonl(0xffffffff);
532             } else {
533                 maskBuffer[count] = (((struct sockaddr_in *)
534                                       (&ifr->ifr_addr))->sin_addr).s_addr;
535             }
536
537             mtuBuffer[count] = htonl(1500);
538 #ifdef SIOCGIFMTU
539             if (ioctl(s, SIOCGIFMTU, (caddr_t) ifr) < 0) {
540                 perror("SIOCGIFMTU");
541             } else {
542                 mtuBuffer[count] = htonl(ifr->ifr_metric);
543             }
544 #endif /* SIOCGIFMTU */
545 #ifdef SIOCRIPMTU
546             if (ioctl(s, SIOCRIPMTU, (caddr_t) ifr) < 0) {
547                 perror("SIOCRIPMTU");
548             } else {
549                 mtuBuffer[count] = htonl(ifr->ifr_metric);
550             }
551 #endif /* SIOCRIPMTU */
552
553             count++;
554         }
555     }
556     close(s);
557     return count;
558 #endif /* AFS_USERSPACE_IP_ADDR */
559 }
560 #endif
561
562 #endif /* ! AFS_NT40_ENV */
563 #endif /* !KERNEL || UKERNEL */