]>
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. */ | |
a1ac18c4 | 36 | static int |
37 | interface_list_ioctl (void) | |
718e3744 | 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. */ | |
a1ac18c4 | 134 | static int |
718e3744 | 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 | |
a1ac18c4 | 179 | static int |
718e3744 | 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 | ||
a1ac18c4 | 213 | static int |
214 | if_getaddrs (void) | |
718e3744 | 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 | { | |
61f42aed | 231 | if (ifap->ifa_addr == NULL) |
232 | { | |
233 | zlog_err ("%s: nonsensical ifaddr with NULL ifa_addr, ifname %s", | |
234 | __func__, (ifap->ifa_name ? ifap->ifa_name : "(null)")); | |
235 | continue; | |
236 | } | |
237 | ||
718e3744 | 238 | ifp = if_lookup_by_name (ifap->ifa_name); |
239 | if (ifp == NULL) | |
240 | { | |
241 | zlog_err ("if_getaddrs(): Can't lookup interface %s\n", | |
242 | ifap->ifa_name); | |
243 | continue; | |
244 | } | |
245 | ||
246 | if (ifap->ifa_addr->sa_family == AF_INET) | |
247 | { | |
248 | struct sockaddr_in *addr; | |
249 | struct sockaddr_in *mask; | |
250 | struct sockaddr_in *dest; | |
251 | struct in_addr *dest_pnt; | |
e4529636 | 252 | int flags = 0; |
718e3744 | 253 | |
254 | addr = (struct sockaddr_in *) ifap->ifa_addr; | |
255 | mask = (struct sockaddr_in *) ifap->ifa_netmask; | |
256 | prefixlen = ip_masklen (mask->sin_addr); | |
257 | ||
258 | dest_pnt = NULL; | |
259 | ||
e4529636 AS |
260 | if (ifap->ifa_dstaddr && |
261 | !IPV4_ADDR_SAME(&addr->sin_addr, | |
262 | &((struct sockaddr_in *) | |
263 | ifap->ifa_dstaddr)->sin_addr)) | |
718e3744 | 264 | { |
265 | dest = (struct sockaddr_in *) ifap->ifa_dstaddr; | |
266 | dest_pnt = &dest->sin_addr; | |
e4529636 | 267 | flags = ZEBRA_IFA_PEER; |
718e3744 | 268 | } |
e4529636 AS |
269 | else if (ifap->ifa_broadaddr && |
270 | !IPV4_ADDR_SAME(&addr->sin_addr, | |
271 | &((struct sockaddr_in *) | |
272 | ifap->ifa_broadaddr)->sin_addr)) | |
718e3744 | 273 | { |
274 | dest = (struct sockaddr_in *) ifap->ifa_broadaddr; | |
275 | dest_pnt = &dest->sin_addr; | |
276 | } | |
277 | ||
e4529636 | 278 | connected_add_ipv4 (ifp, flags, &addr->sin_addr, |
718e3744 | 279 | prefixlen, dest_pnt, NULL); |
280 | } | |
281 | #ifdef HAVE_IPV6 | |
282 | if (ifap->ifa_addr->sa_family == AF_INET6) | |
283 | { | |
284 | struct sockaddr_in6 *addr; | |
285 | struct sockaddr_in6 *mask; | |
286 | struct sockaddr_in6 *dest; | |
287 | struct in6_addr *dest_pnt; | |
e4529636 | 288 | int flags = 0; |
718e3744 | 289 | |
290 | addr = (struct sockaddr_in6 *) ifap->ifa_addr; | |
291 | mask = (struct sockaddr_in6 *) ifap->ifa_netmask; | |
292 | prefixlen = ip6_masklen (mask->sin6_addr); | |
293 | ||
294 | dest_pnt = NULL; | |
295 | ||
e4529636 AS |
296 | if (ifap->ifa_dstaddr && |
297 | !IPV6_ADDR_SAME(&addr->sin6_addr, | |
298 | &((struct sockaddr_in6 *) | |
299 | ifap->ifa_dstaddr)->sin6_addr)) | |
718e3744 | 300 | { |
e4529636 AS |
301 | dest = (struct sockaddr_in6 *) ifap->ifa_dstaddr; |
302 | dest_pnt = &dest->sin6_addr; | |
303 | flags = ZEBRA_IFA_PEER; | |
718e3744 | 304 | } |
e4529636 AS |
305 | else if (ifap->ifa_broadaddr && |
306 | !IPV6_ADDR_SAME(&addr->sin6_addr, | |
307 | &((struct sockaddr_in6 *) | |
308 | ifap->ifa_broadaddr)->sin6_addr)) | |
718e3744 | 309 | { |
e4529636 AS |
310 | dest = (struct sockaddr_in6 *) ifap->ifa_broadaddr; |
311 | dest_pnt = &dest->sin6_addr; | |
718e3744 | 312 | } |
313 | ||
ab836aab | 314 | #if defined(KAME) |
315 | if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)) | |
316 | { | |
317 | addr->sin6_scope_id = | |
318 | ntohs(*(u_int16_t *)&addr->sin6_addr.s6_addr[2]); | |
319 | addr->sin6_addr.s6_addr[2] = addr->sin6_addr.s6_addr[3] = 0; | |
320 | } | |
321 | #endif | |
322 | ||
e4529636 | 323 | connected_add_ipv6 (ifp, flags, &addr->sin6_addr, prefixlen, |
0752ef0b | 324 | dest_pnt, NULL); |
718e3744 | 325 | } |
326 | #endif /* HAVE_IPV6 */ | |
327 | } | |
328 | ||
329 | freeifaddrs (ifapfree); | |
330 | ||
331 | return 0; | |
332 | } | |
333 | #else /* HAVE_GETIFADDRS */ | |
334 | /* Interface address lookup by ioctl. This function only looks up | |
335 | IPv4 address. */ | |
336 | int | |
337 | if_get_addr (struct interface *ifp) | |
338 | { | |
339 | int ret; | |
340 | struct ifreq ifreq; | |
341 | struct sockaddr_in addr; | |
342 | struct sockaddr_in mask; | |
343 | struct sockaddr_in dest; | |
344 | struct in_addr *dest_pnt; | |
345 | u_char prefixlen; | |
e4529636 | 346 | int flags = 0; |
718e3744 | 347 | |
348 | /* Interface's name and address family. */ | |
349 | strncpy (ifreq.ifr_name, ifp->name, IFNAMSIZ); | |
350 | ifreq.ifr_addr.sa_family = AF_INET; | |
351 | ||
352 | /* Interface's address. */ | |
353 | ret = if_ioctl (SIOCGIFADDR, (caddr_t) &ifreq); | |
354 | if (ret < 0) | |
355 | { | |
356 | if (errno != EADDRNOTAVAIL) | |
357 | { | |
6099b3b5 | 358 | zlog_warn ("SIOCGIFADDR fail: %s", safe_strerror (errno)); |
718e3744 | 359 | return ret; |
360 | } | |
361 | return 0; | |
362 | } | |
363 | memcpy (&addr, &ifreq.ifr_addr, sizeof (struct sockaddr_in)); | |
364 | ||
365 | /* Interface's network mask. */ | |
366 | ret = if_ioctl (SIOCGIFNETMASK, (caddr_t) &ifreq); | |
367 | if (ret < 0) | |
368 | { | |
369 | if (errno != EADDRNOTAVAIL) | |
370 | { | |
6099b3b5 | 371 | zlog_warn ("SIOCGIFNETMASK fail: %s", safe_strerror (errno)); |
718e3744 | 372 | return ret; |
373 | } | |
374 | return 0; | |
375 | } | |
376 | #ifdef ifr_netmask | |
377 | memcpy (&mask, &ifreq.ifr_netmask, sizeof (struct sockaddr_in)); | |
378 | #else | |
379 | memcpy (&mask, &ifreq.ifr_addr, sizeof (struct sockaddr_in)); | |
380 | #endif /* ifr_netmask */ | |
381 | prefixlen = ip_masklen (mask.sin_addr); | |
382 | ||
383 | /* Point to point or borad cast address pointer init. */ | |
384 | dest_pnt = NULL; | |
385 | ||
e4529636 AS |
386 | ret = if_ioctl (SIOCGIFDSTADDR, (caddr_t) &ifreq); |
387 | if (ret < 0) | |
388 | { | |
389 | if (errno != EADDRNOTAVAIL) | |
390 | zlog_warn ("SIOCGIFDSTADDR fail: %s", safe_strerror (errno)); | |
391 | } | |
392 | else if (!IPV4_ADDR_SAME(&addr.sin_addr, &ifreq.ifr_dstaddr.sin_addr)) | |
718e3744 | 393 | { |
718e3744 | 394 | memcpy (&dest, &ifreq.ifr_dstaddr, sizeof (struct sockaddr_in)); |
395 | dest_pnt = &dest.sin_addr; | |
e4529636 | 396 | flags = ZEBRA_IFA_PEER; |
718e3744 | 397 | } |
e4529636 | 398 | if (!dest_pnt) |
718e3744 | 399 | { |
400 | ret = if_ioctl (SIOCGIFBRDADDR, (caddr_t) &ifreq); | |
401 | if (ret < 0) | |
402 | { | |
403 | if (errno != EADDRNOTAVAIL) | |
e4529636 | 404 | zlog_warn ("SIOCGIFBRDADDR fail: %s", safe_strerror (errno)); |
718e3744 | 405 | } |
e4529636 AS |
406 | else if (!IPV4_ADDR_SAME(&addr.sin_addr, &ifreq.ifr_broadaddr.sin_addr)) |
407 | { | |
408 | memcpy (&dest, &ifreq.ifr_broadaddr, sizeof (struct sockaddr_in)); | |
409 | dest_pnt = &dest.sin_addr; | |
410 | } | |
718e3744 | 411 | } |
412 | ||
413 | ||
414 | /* Set address to the interface. */ | |
e4529636 | 415 | connected_add_ipv4 (ifp, flags, &addr.sin_addr, prefixlen, dest_pnt, NULL); |
718e3744 | 416 | |
417 | return 0; | |
418 | } | |
419 | #endif /* HAVE_GETIFADDRS */ | |
420 | ||
421 | /* Fetch interface information via ioctl(). */ | |
422 | static void | |
423 | interface_info_ioctl () | |
424 | { | |
1eb8ef25 | 425 | struct listnode *node, *nnode; |
718e3744 | 426 | struct interface *ifp; |
427 | ||
a1ac18c4 | 428 | for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp)) |
718e3744 | 429 | { |
718e3744 | 430 | if_get_index (ifp); |
431 | #ifdef SIOCGIFHWADDR | |
432 | if_get_hwaddr (ifp); | |
433 | #endif /* SIOCGIFHWADDR */ | |
434 | if_get_flags (ifp); | |
435 | #ifndef HAVE_GETIFADDRS | |
436 | if_get_addr (ifp); | |
437 | #endif /* ! HAVE_GETIFADDRS */ | |
438 | if_get_mtu (ifp); | |
439 | if_get_metric (ifp); | |
440 | } | |
441 | } | |
442 | ||
443 | /* Lookup all interface information. */ | |
444 | void | |
445 | interface_list () | |
446 | { | |
447 | /* Linux can do both proc & ioctl, ioctl is the only way to get | |
448 | interface aliases in 2.2 series kernels. */ | |
449 | #ifdef HAVE_PROC_NET_DEV | |
450 | interface_list_proc (); | |
451 | #endif /* HAVE_PROC_NET_DEV */ | |
452 | interface_list_ioctl (); | |
453 | ||
454 | /* After listing is done, get index, address, flags and other | |
455 | interface's information. */ | |
456 | interface_info_ioctl (); | |
457 | ||
458 | #ifdef HAVE_GETIFADDRS | |
459 | if_getaddrs (); | |
460 | #endif /* HAVE_GETIFADDRS */ | |
461 | ||
462 | #if defined(HAVE_IPV6) && defined(HAVE_PROC_NET_IF_INET6) | |
463 | /* Linux provides interface's IPv6 address via | |
464 | /proc/net/if_inet6. */ | |
465 | ifaddr_proc_ipv6 (); | |
466 | #endif /* HAVE_IPV6 && HAVE_PROC_NET_IF_INET6 */ | |
467 | } |