1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 #include <netdb.h>
27 #include <nss_dbdefs.h>
28 #include <netinet/in.h>
29 #include <sys/socket.h>
30 #include <string.h>
31 #include <stdio.h>
32 #include <sys/sockio.h>
33 #include <sys/types.h>
34 #include <stdlib.h>
35 #include <net/if.h>
36 #include <ifaddrs.h>
37 #include <libsocket_priv.h>
38
39 /*
40 * Create a linked list of `struct ifaddrs' structures, one for each
41 * address that is UP. If successful, store the list in *ifap and
42 * return 0. On errors, return -1 and set `errno'.
43 *
44 * The storage returned in *ifap is allocated dynamically and can
45 * only be properly freed by passing it to `freeifaddrs'.
46 */
47 int
getifaddrs(struct ifaddrs ** ifap)48 getifaddrs(struct ifaddrs **ifap)
49 {
50 int err;
51 char *cp;
52 struct ifaddrs *curr;
53
54 if (ifap == NULL) {
55 errno = EINVAL;
56 return (-1);
57 }
58 *ifap = NULL;
59 err = getallifaddrs(AF_UNSPEC, ifap, LIFC_ENABLED);
60 if (err == 0) {
61 for (curr = *ifap; curr != NULL; curr = curr->ifa_next) {
62 if ((cp = strchr(curr->ifa_name, ':')) != NULL)
63 *cp = '\0';
64 }
65 }
66 return (err);
67 }
68
69 void
freeifaddrs(struct ifaddrs * ifa)70 freeifaddrs(struct ifaddrs *ifa)
71 {
72 struct ifaddrs *curr;
73
74 while (ifa != NULL) {
75 curr = ifa;
76 ifa = ifa->ifa_next;
77 free(curr->ifa_name);
78 free(curr->ifa_addr);
79 free(curr->ifa_netmask);
80 free(curr->ifa_dstaddr);
81 free(curr);
82 }
83 }
84
85 /*
86 * Returns all addresses configured on the system. If flags contain
87 * LIFC_ENABLED, only the addresses that are UP are returned.
88 * Address list that is returned by this function must be freed
89 * using freeifaddrs().
90 */
91 int
getallifaddrs(sa_family_t af,struct ifaddrs ** ifap,int64_t flags)92 getallifaddrs(sa_family_t af, struct ifaddrs **ifap, int64_t flags)
93 {
94 struct lifreq *buf = NULL;
95 struct lifreq *lifrp;
96 struct lifreq lifrl;
97 int ret;
98 int s, n, numifs;
99 struct ifaddrs *curr, *prev;
100 sa_family_t lifr_af;
101 int sock4;
102 int sock6;
103 int err;
104
105 if ((sock4 = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
106 return (-1);
107 if ((sock6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
108 err = errno;
109 close(sock4);
110 errno = err;
111 return (-1);
112 }
113
114 retry:
115 /* Get all interfaces from SIOCGLIFCONF */
116 ret = getallifs(sock4, af, &buf, &numifs, (flags & ~LIFC_ENABLED));
117 if (ret != 0)
118 goto fail;
119
120 /*
121 * Loop through the interfaces obtained from SIOCGLIFCOMF
122 * and retrieve the addresses, netmask and flags.
123 */
124 prev = NULL;
125 lifrp = buf;
126 *ifap = NULL;
127 for (n = 0; n < numifs; n++, lifrp++) {
128
129 /* Prepare for the ioctl call */
130 (void) strncpy(lifrl.lifr_name, lifrp->lifr_name,
131 sizeof (lifrl.lifr_name));
132 lifr_af = lifrp->lifr_addr.ss_family;
133 if (af != AF_UNSPEC && lifr_af != af)
134 continue;
135
136 s = (lifr_af == AF_INET ? sock4 : sock6);
137
138 if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0)
139 goto fail;
140 if ((flags & LIFC_ENABLED) && !(lifrl.lifr_flags & IFF_UP))
141 continue;
142
143 /*
144 * Allocate the current list node. Each node contains data
145 * for one ifaddrs structure.
146 */
147 curr = calloc(1, sizeof (struct ifaddrs));
148 if (curr == NULL)
149 goto fail;
150
151 if (prev != NULL) {
152 prev->ifa_next = curr;
153 } else {
154 /* First node in the linked list */
155 *ifap = curr;
156 }
157 prev = curr;
158
159 curr->ifa_flags = lifrl.lifr_flags;
160 if ((curr->ifa_name = strdup(lifrp->lifr_name)) == NULL)
161 goto fail;
162
163 curr->ifa_addr = malloc(sizeof (struct sockaddr_storage));
164 if (curr->ifa_addr == NULL)
165 goto fail;
166 (void) memcpy(curr->ifa_addr, &lifrp->lifr_addr,
167 sizeof (struct sockaddr_storage));
168
169 /* Get the netmask */
170 if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifrl) < 0)
171 goto fail;
172 curr->ifa_netmask = malloc(sizeof (struct sockaddr_storage));
173 if (curr->ifa_netmask == NULL)
174 goto fail;
175 (void) memcpy(curr->ifa_netmask, &lifrl.lifr_addr,
176 sizeof (struct sockaddr_storage));
177
178 /* Get the destination for a pt-pt interface */
179 if (curr->ifa_flags & IFF_POINTOPOINT) {
180 if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifrl) < 0)
181 goto fail;
182 curr->ifa_dstaddr = malloc(
183 sizeof (struct sockaddr_storage));
184 if (curr->ifa_dstaddr == NULL)
185 goto fail;
186 (void) memcpy(curr->ifa_dstaddr, &lifrl.lifr_addr,
187 sizeof (struct sockaddr_storage));
188 } else if (curr->ifa_flags & IFF_BROADCAST) {
189 if (ioctl(s, SIOCGLIFBRDADDR, (caddr_t)&lifrl) < 0)
190 goto fail;
191 curr->ifa_broadaddr = malloc(
192 sizeof (struct sockaddr_storage));
193 if (curr->ifa_broadaddr == NULL)
194 goto fail;
195 (void) memcpy(curr->ifa_broadaddr, &lifrl.lifr_addr,
196 sizeof (struct sockaddr_storage));
197 }
198
199 }
200 free(buf);
201 close(sock4);
202 close(sock6);
203 return (0);
204 fail:
205 err = errno;
206 free(buf);
207 freeifaddrs(*ifap);
208 *ifap = NULL;
209 if (err == ENXIO)
210 goto retry;
211 close(sock4);
212 close(sock6);
213 errno = err;
214 return (-1);
215 }
216
217 /*
218 * Do a SIOCGLIFCONF and store all the interfaces in `buf'.
219 */
220 int
getallifs(int s,sa_family_t af,struct lifreq ** lifr,int * numifs,int64_t lifc_flags)221 getallifs(int s, sa_family_t af, struct lifreq **lifr, int *numifs,
222 int64_t lifc_flags)
223 {
224 struct lifnum lifn;
225 struct lifconf lifc;
226 size_t bufsize;
227 char *tmp;
228 caddr_t *buf = (caddr_t *)lifr;
229
230 lifn.lifn_family = af;
231 lifn.lifn_flags = lifc_flags;
232
233 *buf = NULL;
234 retry:
235 if (ioctl(s, SIOCGLIFNUM, &lifn) < 0)
236 goto fail;
237
238 /*
239 * When calculating the buffer size needed, add a small number
240 * of interfaces to those we counted. We do this to capture
241 * the interface status of potential interfaces which may have
242 * been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF.
243 */
244 bufsize = (lifn.lifn_count + 4) * sizeof (struct lifreq);
245
246 if ((tmp = realloc(*buf, bufsize)) == NULL)
247 goto fail;
248
249 *buf = tmp;
250 lifc.lifc_family = af;
251 lifc.lifc_flags = lifc_flags;
252 lifc.lifc_len = bufsize;
253 lifc.lifc_buf = *buf;
254 if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0)
255 goto fail;
256
257 *numifs = lifc.lifc_len / sizeof (struct lifreq);
258 if (*numifs >= (lifn.lifn_count + 4)) {
259 /*
260 * If every entry was filled, there are probably
261 * more interfaces than (lifn.lifn_count + 4).
262 * Redo the ioctls SIOCGLIFNUM and SIOCGLIFCONF to
263 * get all the interfaces.
264 */
265 goto retry;
266 }
267 return (0);
268 fail:
269 free(*buf);
270 *buf = NULL;
271 return (-1);
272 }
273