2002-08-26 Roland McGrath <roland@redhat.com>
[kopensolaris-gnu/glibc.git] / sysdeps / gnu / ifaddrs.c
1 /* getifaddrs -- get names and addresses of all network interfaces
2    Copyright (C) 2002 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <ifaddrs.h>
21 #include <net/if.h>
22 #include <sys/socket.h>
23 #include <sys/ioctl.h>
24 #include <unistd.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <netinet/in.h>
29
30 #include "ifreq.h"
31
32 /* Create a linked list of `struct ifaddrs' structures, one for each
33    network interface on the host machine.  If successful, store the
34    list in *IFAP and return 0.  On errors, return -1 and set `errno'.  */
35 int
36 getifaddrs (struct ifaddrs **ifap)
37 {
38   /* This implementation handles only IPv4 interfaces.
39      The various ioctls below will only work on an AF_INET socket.
40      Some different mechanism entirely must be used for IPv6.  */
41   int fd = __socket (AF_INET, SOCK_DGRAM, 0);
42   struct ifreq *ifreqs;
43   int nifs, i;
44
45   if (fd < 0)
46     return -1;
47
48   __ifreq (&ifreqs, &nifs, fd);
49   if (ifreqs == NULL)           /* XXX doesn't distinguish error vs none */
50     {
51       __close (fd);
52       return -1;
53     }
54
55   /* Now we have the list of interfaces and each one's address.
56      Put it into the expected format and fill in the remaining details.  */
57   if (nifs == 0)
58     *ifap = NULL;
59   else
60     {
61       struct
62       {
63         struct ifaddrs ia;
64         struct sockaddr addr, netmask, broadaddr;
65         char name[IF_NAMESIZE];
66       } *storage;
67
68       storage = malloc (nifs * sizeof storage[0]);
69       if (storage == NULL)
70         {
71           __close (fd);
72           __if_freereq (ifreqs, nifs);
73           return -1;
74         }
75
76       i = 0;
77       do
78         {
79           struct ifreq *const ifr = &ifreqs[i];
80
81           /* Fill in all pointers to the storage we've already allocated.  */
82           storage[i].ia.ifa_next = &storage[i + 1].ia;
83           storage[i].ia.ifa_addr = &storage[i].addr;
84           storage[i].ia.ifa_netmask = &storage[i].netmask;
85           storage[i].ia.ifa_broadaddr = &storage[i].broadaddr; /* & dstaddr */
86
87           /* Now copy the information we already have from SIOCGIFCONF.  */
88           storage[i].ia.ifa_name = strncpy (storage[i].name, ifr->ifr_name,
89                                             sizeof storage[i].name);
90           storage[i].addr = ifr->ifr_addr;
91
92           /* The SIOCGIFCONF call filled in only the name and address.
93              Now we must also ask for the other information we need.  */
94
95           if (__ioctl (fd, SIOCGIFFLAGS, ifr) < 0)
96             break;
97           storage[i].ia.ifa_flags = ifr->ifr_flags;
98
99           ifr->ifr_addr = storage[i].addr;
100           if (__ioctl (fd, SIOCGIFNETMASK, ifr) < 0)
101             break;
102           storage[i].netmask = ifr->ifr_netmask;
103
104           if (ifr->ifr_flags & IFF_BROADCAST)
105             {
106               ifr->ifr_addr = storage[i].addr;
107               if (__ioctl (fd, SIOCGIFBRDADDR, ifr) < 0)
108                 break;
109               storage[i].broadaddr = ifr->ifr_broadaddr;
110             }
111           else if (ifr->ifr_flags & IFF_POINTOPOINT)
112             {
113               ifr->ifr_addr = storage[i].addr;
114               if (__ioctl (fd, SIOCGIFDSTADDR, ifr) < 0)
115                 break;
116               storage[i].broadaddr = ifr->ifr_dstaddr;
117             }
118           else
119             /* Just 'cause.  */
120             memset (&storage[i].broadaddr, 0, sizeof storage[i].broadaddr);
121
122           storage[i].ia.ifa_data = NULL; /* Nothing here for now.  */
123
124         } while (++i < nifs);
125       if (i < nifs)             /* Broke out early on error.  */
126         {
127           __close (fd);
128           free (storage);
129           __if_freereq (ifreqs, nifs);
130           return -1;
131         }
132       storage[i - 1].ia.ifa_next = NULL;
133
134       *ifap = &storage[0].ia;
135
136       __close (fd);
137       __if_freereq (ifreqs, nifs);
138     }
139
140   return 0;
141 }
142
143 void
144 freeifaddrs (struct ifaddrs *ifa)
145 {
146   free (ifa);
147 }