]>
Commit | Line | Data |
---|---|---|
718e3744 | 1 | /* |
2 | * Interface looking up by ioctl (). | |
3 | * Copyright (C) 1997, 98 Kunihiro Ishiguro | |
4 | * | |
5 | * This file is part of GNU Zebra. | |
6 | * | |
7 | * GNU Zebra is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License as published by the | |
9 | * Free Software Foundation; either version 2, or (at your option) any | |
10 | * later version. | |
11 | * | |
12 | * GNU Zebra is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with GNU Zebra; see the file COPYING. If not, write to the Free | |
19 | * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | |
20 | * 02111-1307, USA. | |
21 | */ | |
22 | ||
23 | #include <zebra.h> | |
24 | ||
25 | #include "if.h" | |
26 | #include "sockunion.h" | |
27 | #include "prefix.h" | |
28 | #include "ioctl.h" | |
29 | #include "connected.h" | |
30 | #include "memory.h" | |
31 | #include "log.h" | |
32 | ||
33 | #include "zebra/interface.h" | |
34 | ||
35 | /* Interface looking up using infamous SIOCGIFCONF. */ | |
36 | int | |
37 | interface_list_ioctl () | |
38 | { | |
39 | int ret; | |
40 | int sock; | |
41 | #define IFNUM_BASE 32 | |
42 | int ifnum; | |
43 | struct ifreq *ifreq; | |
44 | struct ifconf ifconf; | |
45 | struct interface *ifp; | |
46 | int n; | |
47 | int lastlen; | |
48 | ||
49 | /* Normally SIOCGIFCONF works with AF_INET socket. */ | |
50 | sock = socket (AF_INET, SOCK_DGRAM, 0); | |
51 | if (sock < 0) | |
52 | { | |
6099b3b5 | 53 | zlog_warn ("Can't make AF_INET socket stream: %s", safe_strerror (errno)); |
718e3744 | 54 | return -1; |
55 | } | |
56 | ||
57 | /* Set initial ifreq count. This will be double when SIOCGIFCONF | |
58 | fail. Solaris has SIOCGIFNUM. */ | |
59 | #ifdef SIOCGIFNUM | |
60 | ret = ioctl (sock, SIOCGIFNUM, &ifnum); | |
61 | if (ret < 0) | |
62 | ifnum = IFNUM_BASE; | |
63 | else | |
64 | ifnum++; | |
65 | #else | |
66 | ifnum = IFNUM_BASE; | |
67 | #endif /* SIOCGIFNUM */ | |
68 | ||
69 | ifconf.ifc_buf = NULL; | |
70 | ||
71 | lastlen = 0; | |
72 | /* Loop until SIOCGIFCONF success. */ | |
73 | for (;;) | |
74 | { | |
75 | ifconf.ifc_len = sizeof (struct ifreq) * ifnum; | |
76 | ifconf.ifc_buf = XREALLOC(MTYPE_TMP, ifconf.ifc_buf, ifconf.ifc_len); | |
77 | ||
78 | ret = ioctl(sock, SIOCGIFCONF, &ifconf); | |
79 | ||
80 | if (ret < 0) | |
81 | { | |
6099b3b5 | 82 | zlog_warn ("SIOCGIFCONF: %s", safe_strerror(errno)); |
718e3744 | 83 | goto end; |
84 | } | |
85 | /* Repeatedly get info til buffer fails to grow. */ | |
86 | if (ifconf.ifc_len > lastlen) | |
87 | { | |
88 | lastlen = ifconf.ifc_len; | |
89 | ifnum += 10; | |
90 | continue; | |
91 | } | |
92 | /* Success. */ | |
93 | break; | |
94 | } | |
95 | ||
96 | /* Allocate interface. */ | |
97 | ifreq = ifconf.ifc_req; | |
98 | ||
99 | #ifdef OPEN_BSD | |
100 | for (n = 0; n < ifconf.ifc_len; ) | |
101 | { | |
102 | int size; | |
103 | ||
104 | ifreq = (struct ifreq *)((caddr_t) ifconf.ifc_req + n); | |
08dbfb69 | 105 | ifp = if_get_by_name_len(ifreq->ifr_name, |
106 | strnlen(ifreq->ifr_name, | |
107 | sizeof(ifreq->ifr_name))); | |
718e3744 | 108 | if_add_update (ifp); |
109 | size = ifreq->ifr_addr.sa_len; | |
110 | if (size < sizeof (ifreq->ifr_addr)) | |
111 | size = sizeof (ifreq->ifr_addr); | |
112 | size += sizeof (ifreq->ifr_name); | |
113 | n += size; | |
114 | } | |
115 | #else | |
116 | for (n = 0; n < ifconf.ifc_len; n += sizeof(struct ifreq)) | |
117 | { | |
08dbfb69 | 118 | ifp = if_get_by_name_len(ifreq->ifr_name, |
119 | strnlen(ifreq->ifr_name, | |
120 | sizeof(ifreq->ifr_name))); | |
718e3744 | 121 | if_add_update (ifp); |
122 | ifreq++; | |
123 | } | |
124 | #endif /* OPEN_BSD */ | |
125 | ||
126 | end: | |
127 | close (sock); | |
128 | XFREE (MTYPE_TMP, ifconf.ifc_buf); | |
129 | ||
130 | return ret; | |
131 | } | |
132 | ||
133 | /* Get interface's index by ioctl. */ | |
134 | int | |
135 | if_get_index (struct interface *ifp) | |
136 | { | |
ab836aab | 137 | #if defined(HAVE_IF_NAMETOINDEX) |
138 | /* Modern systems should have if_nametoindex(3). */ | |
139 | ifp->ifindex = if_nametoindex(ifp->name); | |
140 | #elif defined(SIOCGIFINDEX) && !defined(HAVE_BROKEN_ALIASES) | |
141 | /* Fall-back for older linuxes. */ | |
718e3744 | 142 | int ret; |
143 | struct ifreq ifreq; | |
53db0fee | 144 | static int if_fake_index; |
718e3744 | 145 | |
146 | ifreq_set_name (&ifreq, ifp); | |
147 | ||
148 | ret = if_ioctl (SIOCGIFINDEX, (caddr_t) &ifreq); | |
149 | if (ret < 0) | |
150 | { | |
151 | /* Linux 2.0.X does not have interface index. */ | |
152 | ifp->ifindex = if_fake_index++; | |
153 | return ifp->ifindex; | |
154 | } | |
155 | ||
156 | /* OK we got interface index. */ | |
157 | #ifdef ifr_ifindex | |
158 | ifp->ifindex = ifreq.ifr_ifindex; | |
159 | #else | |
160 | ifp->ifindex = ifreq.ifr_index; | |
161 | #endif | |
718e3744 | 162 | |
163 | #else | |
ab836aab | 164 | /* Linux 2.2.X does not provide individual interface index |
165 | for aliases and we know it. For others issue a warning. */ | |
166 | #if !defined(HAVE_BROKEN_ALIASES) | |
167 | #warning "Using if_fake_index. You may want to add appropriate" | |
168 | #warning "mapping from ifname to ifindex for your system..." | |
169 | #endif | |
170 | /* This branch probably won't provide usable results, but anyway... */ | |
171 | static int if_fake_index = 1; | |
718e3744 | 172 | ifp->ifindex = if_fake_index++; |
ab836aab | 173 | #endif |
174 | ||
718e3744 | 175 | return ifp->ifindex; |
718e3744 | 176 | } |
177 | ||
178 | #ifdef SIOCGIFHWADDR | |
179 | int | |
180 | if_get_hwaddr (struct interface *ifp) | |
181 | { | |
182 | int ret; | |
183 | struct ifreq ifreq; | |
184 | int i; | |
185 | ||
186 | strncpy (ifreq.ifr_name, ifp->name, IFNAMSIZ); | |
187 | ifreq.ifr_addr.sa_family = AF_INET; | |
188 | ||
189 | /* Fetch Hardware address if available. */ | |
190 | ret = if_ioctl (SIOCGIFHWADDR, (caddr_t) &ifreq); | |
191 | if (ret < 0) | |
192 | ifp->hw_addr_len = 0; | |
193 | else | |
194 | { | |
195 | memcpy (ifp->hw_addr, ifreq.ifr_hwaddr.sa_data, 6); | |
196 | ||
197 | for (i = 0; i < 6; i++) | |
198 | if (ifp->hw_addr[i] != 0) | |
199 | break; | |
200 | ||
201 | if (i == 6) | |
202 | ifp->hw_addr_len = 0; | |
203 | else | |
204 | ifp->hw_addr_len = 6; | |
205 | } | |
206 | return 0; | |
207 | } | |
208 | #endif /* SIOCGIFHWADDR */ | |
209 | ||
210 | #ifdef HAVE_GETIFADDRS | |
211 | #include <ifaddrs.h> | |
212 | ||
213 | int | |
214 | if_getaddrs () | |
215 | { | |
216 | int ret; | |
217 | struct ifaddrs *ifap; | |
218 | struct ifaddrs *ifapfree; | |
219 | struct interface *ifp; | |
220 | int prefixlen; | |
221 | ||
222 | ret = getifaddrs (&ifap); | |
223 | if (ret != 0) | |
224 | { | |
6099b3b5 | 225 | zlog_err ("getifaddrs(): %s", safe_strerror (errno)); |
718e3744 | 226 | return -1; |
227 | } | |
228 | ||
229 | for (ifapfree = ifap; ifap; ifap = ifap->ifa_next) | |
230 | { | |
231 | ifp = if_lookup_by_name (ifap->ifa_name); | |
232 | if (ifp == NULL) | |
233 | { | |
234 | zlog_err ("if_getaddrs(): Can't lookup interface %s\n", | |
235 | ifap->ifa_name); | |
236 | continue; | |
237 | } | |
238 | ||
239 | if (ifap->ifa_addr->sa_family == AF_INET) | |
240 | { | |
241 | struct sockaddr_in *addr; | |
242 | struct sockaddr_in *mask; | |
243 | struct sockaddr_in *dest; | |
244 | struct in_addr *dest_pnt; | |
245 | ||
246 | addr = (struct sockaddr_in *) ifap->ifa_addr; | |
247 | mask = (struct sockaddr_in *) ifap->ifa_netmask; | |
248 | prefixlen = ip_masklen (mask->sin_addr); | |
249 | ||
250 | dest_pnt = NULL; | |
251 | ||
252 | if (ifap->ifa_flags & IFF_POINTOPOINT) | |
253 | { | |
254 | dest = (struct sockaddr_in *) ifap->ifa_dstaddr; | |
255 | dest_pnt = &dest->sin_addr; | |
256 | } | |
257 | ||
258 | if (ifap->ifa_flags & IFF_BROADCAST) | |
259 | { | |
260 | dest = (struct sockaddr_in *) ifap->ifa_broadaddr; | |
261 | dest_pnt = &dest->sin_addr; | |
262 | } | |
263 | ||
264 | connected_add_ipv4 (ifp, 0, &addr->sin_addr, | |
265 | prefixlen, dest_pnt, NULL); | |
266 | } | |
267 | #ifdef HAVE_IPV6 | |
268 | if (ifap->ifa_addr->sa_family == AF_INET6) | |
269 | { | |
270 | struct sockaddr_in6 *addr; | |
271 | struct sockaddr_in6 *mask; | |
272 | struct sockaddr_in6 *dest; | |
273 | struct in6_addr *dest_pnt; | |
274 | ||
275 | addr = (struct sockaddr_in6 *) ifap->ifa_addr; | |
276 | mask = (struct sockaddr_in6 *) ifap->ifa_netmask; | |
277 | prefixlen = ip6_masklen (mask->sin6_addr); | |
278 | ||
279 | dest_pnt = NULL; | |
280 | ||
281 | if (ifap->ifa_flags & IFF_POINTOPOINT) | |
282 | { | |
283 | if (ifap->ifa_dstaddr) | |
284 | { | |
285 | dest = (struct sockaddr_in6 *) ifap->ifa_dstaddr; | |
286 | dest_pnt = &dest->sin6_addr; | |
287 | } | |
288 | } | |
289 | ||
290 | if (ifap->ifa_flags & IFF_BROADCAST) | |
291 | { | |
292 | if (ifap->ifa_broadaddr) | |
293 | { | |
294 | dest = (struct sockaddr_in6 *) ifap->ifa_broadaddr; | |
295 | dest_pnt = &dest->sin6_addr; | |
296 | } | |
297 | } | |
298 | ||
ab836aab | 299 | #if defined(KAME) |
300 | if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)) | |
301 | { | |
302 | addr->sin6_scope_id = | |
303 | ntohs(*(u_int16_t *)&addr->sin6_addr.s6_addr[2]); | |
304 | addr->sin6_addr.s6_addr[2] = addr->sin6_addr.s6_addr[3] = 0; | |
305 | } | |
306 | #endif | |
307 | ||
718e3744 | 308 | connected_add_ipv6 (ifp, &addr->sin6_addr, prefixlen, dest_pnt); |
309 | } | |
310 | #endif /* HAVE_IPV6 */ | |
311 | } | |
312 | ||
313 | freeifaddrs (ifapfree); | |
314 | ||
315 | return 0; | |
316 | } | |
317 | #else /* HAVE_GETIFADDRS */ | |
318 | /* Interface address lookup by ioctl. This function only looks up | |
319 | IPv4 address. */ | |
320 | int | |
321 | if_get_addr (struct interface *ifp) | |
322 | { | |
323 | int ret; | |
324 | struct ifreq ifreq; | |
325 | struct sockaddr_in addr; | |
326 | struct sockaddr_in mask; | |
327 | struct sockaddr_in dest; | |
328 | struct in_addr *dest_pnt; | |
329 | u_char prefixlen; | |
330 | ||
331 | /* Interface's name and address family. */ | |
332 | strncpy (ifreq.ifr_name, ifp->name, IFNAMSIZ); | |
333 | ifreq.ifr_addr.sa_family = AF_INET; | |
334 | ||
335 | /* Interface's address. */ | |
336 | ret = if_ioctl (SIOCGIFADDR, (caddr_t) &ifreq); | |
337 | if (ret < 0) | |
338 | { | |
339 | if (errno != EADDRNOTAVAIL) | |
340 | { | |
6099b3b5 | 341 | zlog_warn ("SIOCGIFADDR fail: %s", safe_strerror (errno)); |
718e3744 | 342 | return ret; |
343 | } | |
344 | return 0; | |
345 | } | |
346 | memcpy (&addr, &ifreq.ifr_addr, sizeof (struct sockaddr_in)); | |
347 | ||
348 | /* Interface's network mask. */ | |
349 | ret = if_ioctl (SIOCGIFNETMASK, (caddr_t) &ifreq); | |
350 | if (ret < 0) | |
351 | { | |
352 | if (errno != EADDRNOTAVAIL) | |
353 | { | |
6099b3b5 | 354 | zlog_warn ("SIOCGIFNETMASK fail: %s", safe_strerror (errno)); |
718e3744 | 355 | return ret; |
356 | } | |
357 | return 0; | |
358 | } | |
359 | #ifdef ifr_netmask | |
360 | memcpy (&mask, &ifreq.ifr_netmask, sizeof (struct sockaddr_in)); | |
361 | #else | |
362 | memcpy (&mask, &ifreq.ifr_addr, sizeof (struct sockaddr_in)); | |
363 | #endif /* ifr_netmask */ | |
364 | prefixlen = ip_masklen (mask.sin_addr); | |
365 | ||
366 | /* Point to point or borad cast address pointer init. */ | |
367 | dest_pnt = NULL; | |
368 | ||
369 | if (ifp->flags & IFF_POINTOPOINT) | |
370 | { | |
371 | ret = if_ioctl (SIOCGIFDSTADDR, (caddr_t) &ifreq); | |
372 | if (ret < 0) | |
373 | { | |
374 | if (errno != EADDRNOTAVAIL) | |
375 | { | |
6099b3b5 | 376 | zlog_warn ("SIOCGIFDSTADDR fail: %s", safe_strerror (errno)); |
718e3744 | 377 | return ret; |
378 | } | |
379 | return 0; | |
380 | } | |
381 | memcpy (&dest, &ifreq.ifr_dstaddr, sizeof (struct sockaddr_in)); | |
382 | dest_pnt = &dest.sin_addr; | |
383 | } | |
384 | if (ifp->flags & IFF_BROADCAST) | |
385 | { | |
386 | ret = if_ioctl (SIOCGIFBRDADDR, (caddr_t) &ifreq); | |
387 | if (ret < 0) | |
388 | { | |
389 | if (errno != EADDRNOTAVAIL) | |
390 | { | |
6099b3b5 | 391 | zlog_warn ("SIOCGIFBRDADDR fail: %s", safe_strerror (errno)); |
718e3744 | 392 | return ret; |
393 | } | |
394 | return 0; | |
395 | } | |
396 | memcpy (&dest, &ifreq.ifr_broadaddr, sizeof (struct sockaddr_in)); | |
397 | dest_pnt = &dest.sin_addr; | |
398 | } | |
399 | ||
400 | ||
401 | /* Set address to the interface. */ | |
402 | connected_add_ipv4 (ifp, 0, &addr.sin_addr, prefixlen, dest_pnt, NULL); | |
403 | ||
404 | return 0; | |
405 | } | |
406 | #endif /* HAVE_GETIFADDRS */ | |
407 | ||
408 | /* Fetch interface information via ioctl(). */ | |
409 | static void | |
410 | interface_info_ioctl () | |
411 | { | |
1eb8ef25 | 412 | struct listnode *node, *nnode; |
718e3744 | 413 | struct interface *ifp; |
414 | ||
1eb8ef25 | 415 | for (ALL_LIST_ELEMENTS (iflist, ifp, node, nnode)) |
718e3744 | 416 | { |
718e3744 | 417 | if_get_index (ifp); |
418 | #ifdef SIOCGIFHWADDR | |
419 | if_get_hwaddr (ifp); | |
420 | #endif /* SIOCGIFHWADDR */ | |
421 | if_get_flags (ifp); | |
422 | #ifndef HAVE_GETIFADDRS | |
423 | if_get_addr (ifp); | |
424 | #endif /* ! HAVE_GETIFADDRS */ | |
425 | if_get_mtu (ifp); | |
426 | if_get_metric (ifp); | |
427 | } | |
428 | } | |
429 | ||
430 | /* Lookup all interface information. */ | |
431 | void | |
432 | interface_list () | |
433 | { | |
434 | /* Linux can do both proc & ioctl, ioctl is the only way to get | |
435 | interface aliases in 2.2 series kernels. */ | |
436 | #ifdef HAVE_PROC_NET_DEV | |
437 | interface_list_proc (); | |
438 | #endif /* HAVE_PROC_NET_DEV */ | |
439 | interface_list_ioctl (); | |
440 | ||
441 | /* After listing is done, get index, address, flags and other | |
442 | interface's information. */ | |
443 | interface_info_ioctl (); | |
444 | ||
445 | #ifdef HAVE_GETIFADDRS | |
446 | if_getaddrs (); | |
447 | #endif /* HAVE_GETIFADDRS */ | |
448 | ||
449 | #if defined(HAVE_IPV6) && defined(HAVE_PROC_NET_IF_INET6) | |
450 | /* Linux provides interface's IPv6 address via | |
451 | /proc/net/if_inet6. */ | |
452 | ifaddr_proc_ipv6 (); | |
453 | #endif /* HAVE_IPV6 && HAVE_PROC_NET_IF_INET6 */ | |
454 | } |