2006-02-24 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, 2003 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;
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       struct ifreq *ifr;
68       int i;
69
70       storage = malloc (nifs * sizeof storage[0]);
71       if (storage == NULL)
72         {
73           __close (fd);
74           __if_freereq (ifreqs, nifs);
75           return -1;
76         }
77
78       i = 0;
79       ifr = ifreqs;
80       do
81         {
82           /* Fill in pointers to the storage we've already allocated.  */
83           storage[i].ia.ifa_next = &storage[i + 1].ia;
84           storage[i].ia.ifa_addr = &storage[i].addr;
85
86           /* Now copy the information we already have from SIOCGIFCONF.  */
87           storage[i].ia.ifa_name = strncpy (storage[i].name, ifr->ifr_name,
88                                             sizeof storage[i].name);
89           storage[i].addr = ifr->ifr_addr;
90
91           /* The SIOCGIFCONF call filled in only the name and address.
92              Now we must also ask for the other information we need.  */
93
94           if (__ioctl (fd, SIOCGIFFLAGS, ifr) < 0)
95             break;
96           storage[i].ia.ifa_flags = ifr->ifr_flags;
97
98           ifr->ifr_addr = storage[i].addr;
99
100           if (__ioctl (fd, SIOCGIFNETMASK, ifr) < 0)
101             storage[i].ia.ifa_netmask = NULL;
102           else
103             {
104               storage[i].ia.ifa_netmask = &storage[i].netmask;
105               storage[i].netmask = ifr->ifr_netmask;
106             }
107
108           if (ifr->ifr_flags & IFF_BROADCAST)
109             {
110               ifr->ifr_addr = storage[i].addr;
111               if (__ioctl (fd, SIOCGIFBRDADDR, ifr) < 0)
112                 storage[i].ia.ifa_broadaddr = NULL;
113               {
114                 storage[i].ia.ifa_broadaddr = &storage[i].broadaddr;
115                 storage[i].broadaddr = ifr->ifr_broadaddr;
116               }
117             }
118           else if (ifr->ifr_flags & IFF_POINTOPOINT)
119             {
120               ifr->ifr_addr = storage[i].addr;
121               if (__ioctl (fd, SIOCGIFDSTADDR, ifr) < 0)
122                 storage[i].ia.ifa_broadaddr = NULL;
123               else
124                 {
125                   storage[i].ia.ifa_broadaddr = &storage[i].broadaddr;
126                   storage[i].broadaddr = ifr->ifr_dstaddr;
127                 }
128             }
129           else
130             storage[i].ia.ifa_broadaddr = NULL;
131
132           storage[i].ia.ifa_data = NULL; /* Nothing here for now.  */
133
134           ifr = __if_nextreq (ifr);
135         } while (++i < nifs);
136       if (i < nifs)             /* Broke out early on error.  */
137         {
138           __close (fd);
139           free (storage);
140           __if_freereq (ifreqs, nifs);
141           return -1;
142         }
143
144       storage[i - 1].ia.ifa_next = NULL;
145
146       *ifap = &storage[0].ia;
147
148       __close (fd);
149       __if_freereq (ifreqs, nifs);
150     }
151
152   return 0;
153 }
154 #ifndef getifaddrs
155 libc_hidden_def (getifaddrs)
156 #endif
157
158 void
159 freeifaddrs (struct ifaddrs *ifa)
160 {
161   free (ifa);
162 }
163 libc_hidden_def (freeifaddrs)