blob: 788184f8f47c739f3c7d0c38acbe84d7e8ef4415 [file] [log] [blame]
paul718e3742002-12-13 20:15:29 +00001/*
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. */
paula1ac18c2005-06-28 17:17:12 +000036static int
37interface_list_ioctl (void)
paul718e3742002-12-13 20:15:29 +000038{
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 {
ajs6099b3b2004-11-20 02:06:59 +000053 zlog_warn ("Can't make AF_INET socket stream: %s", safe_strerror (errno));
paul718e3742002-12-13 20:15:29 +000054 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 {
ajs6099b3b2004-11-20 02:06:59 +000082 zlog_warn ("SIOCGIFCONF: %s", safe_strerror(errno));
paul718e3742002-12-13 20:15:29 +000083 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);
ajs08dbfb62005-04-03 03:40:52 +0000105 ifp = if_get_by_name_len(ifreq->ifr_name,
106 strnlen(ifreq->ifr_name,
107 sizeof(ifreq->ifr_name)));
paul718e3742002-12-13 20:15:29 +0000108 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 {
ajs08dbfb62005-04-03 03:40:52 +0000118 ifp = if_get_by_name_len(ifreq->ifr_name,
119 strnlen(ifreq->ifr_name,
120 sizeof(ifreq->ifr_name)));
paul718e3742002-12-13 20:15:29 +0000121 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. */
paula1ac18c2005-06-28 17:17:12 +0000134static int
paul718e3742002-12-13 20:15:29 +0000135if_get_index (struct interface *ifp)
136{
paulab836aa2002-12-13 21:19:02 +0000137#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. */
paul718e3742002-12-13 20:15:29 +0000142 int ret;
143 struct ifreq ifreq;
paul53db0fe2003-07-11 17:42:09 +0000144 static int if_fake_index;
paul718e3742002-12-13 20:15:29 +0000145
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
paul718e3742002-12-13 20:15:29 +0000162
163#else
paulab836aa2002-12-13 21:19:02 +0000164/* 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;
paul718e3742002-12-13 20:15:29 +0000172 ifp->ifindex = if_fake_index++;
paulab836aa2002-12-13 21:19:02 +0000173#endif
174
paul718e3742002-12-13 20:15:29 +0000175 return ifp->ifindex;
paul718e3742002-12-13 20:15:29 +0000176}
177
178#ifdef SIOCGIFHWADDR
paula1ac18c2005-06-28 17:17:12 +0000179static int
paul718e3742002-12-13 20:15:29 +0000180if_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
paula1ac18c2005-06-28 17:17:12 +0000213static int
214if_getaddrs (void)
paul718e3742002-12-13 20:15:29 +0000215{
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 {
ajs6099b3b2004-11-20 02:06:59 +0000225 zlog_err ("getifaddrs(): %s", safe_strerror (errno));
paul718e3742002-12-13 20:15:29 +0000226 return -1;
227 }
228
229 for (ifapfree = ifap; ifap; ifap = ifap->ifa_next)
230 {
paul61f42ae2006-01-17 17:59:11 +0000231 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
paul718e3742002-12-13 20:15:29 +0000238 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;
252
253 addr = (struct sockaddr_in *) ifap->ifa_addr;
254 mask = (struct sockaddr_in *) ifap->ifa_netmask;
255 prefixlen = ip_masklen (mask->sin_addr);
256
257 dest_pnt = NULL;
258
259 if (ifap->ifa_flags & IFF_POINTOPOINT)
260 {
261 dest = (struct sockaddr_in *) ifap->ifa_dstaddr;
262 dest_pnt = &dest->sin_addr;
263 }
264
265 if (ifap->ifa_flags & IFF_BROADCAST)
266 {
267 dest = (struct sockaddr_in *) ifap->ifa_broadaddr;
268 dest_pnt = &dest->sin_addr;
269 }
270
271 connected_add_ipv4 (ifp, 0, &addr->sin_addr,
272 prefixlen, dest_pnt, NULL);
273 }
274#ifdef HAVE_IPV6
275 if (ifap->ifa_addr->sa_family == AF_INET6)
276 {
277 struct sockaddr_in6 *addr;
278 struct sockaddr_in6 *mask;
279 struct sockaddr_in6 *dest;
280 struct in6_addr *dest_pnt;
281
282 addr = (struct sockaddr_in6 *) ifap->ifa_addr;
283 mask = (struct sockaddr_in6 *) ifap->ifa_netmask;
284 prefixlen = ip6_masklen (mask->sin6_addr);
285
286 dest_pnt = NULL;
287
288 if (ifap->ifa_flags & IFF_POINTOPOINT)
289 {
290 if (ifap->ifa_dstaddr)
291 {
292 dest = (struct sockaddr_in6 *) ifap->ifa_dstaddr;
293 dest_pnt = &dest->sin6_addr;
294 }
295 }
296
297 if (ifap->ifa_flags & IFF_BROADCAST)
298 {
299 if (ifap->ifa_broadaddr)
300 {
301 dest = (struct sockaddr_in6 *) ifap->ifa_broadaddr;
302 dest_pnt = &dest->sin6_addr;
303 }
304 }
305
paulab836aa2002-12-13 21:19:02 +0000306#if defined(KAME)
307 if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr))
308 {
309 addr->sin6_scope_id =
310 ntohs(*(u_int16_t *)&addr->sin6_addr.s6_addr[2]);
311 addr->sin6_addr.s6_addr[2] = addr->sin6_addr.s6_addr[3] = 0;
312 }
313#endif
314
paul0752ef02005-11-03 12:35:21 +0000315 connected_add_ipv6 (ifp, &addr->sin6_addr, prefixlen,
316 dest_pnt, NULL);
paul718e3742002-12-13 20:15:29 +0000317 }
318#endif /* HAVE_IPV6 */
319 }
320
321 freeifaddrs (ifapfree);
322
323 return 0;
324}
325#else /* HAVE_GETIFADDRS */
326/* Interface address lookup by ioctl. This function only looks up
327 IPv4 address. */
328int
329if_get_addr (struct interface *ifp)
330{
331 int ret;
332 struct ifreq ifreq;
333 struct sockaddr_in addr;
334 struct sockaddr_in mask;
335 struct sockaddr_in dest;
336 struct in_addr *dest_pnt;
337 u_char prefixlen;
338
339 /* Interface's name and address family. */
340 strncpy (ifreq.ifr_name, ifp->name, IFNAMSIZ);
341 ifreq.ifr_addr.sa_family = AF_INET;
342
343 /* Interface's address. */
344 ret = if_ioctl (SIOCGIFADDR, (caddr_t) &ifreq);
345 if (ret < 0)
346 {
347 if (errno != EADDRNOTAVAIL)
348 {
ajs6099b3b2004-11-20 02:06:59 +0000349 zlog_warn ("SIOCGIFADDR fail: %s", safe_strerror (errno));
paul718e3742002-12-13 20:15:29 +0000350 return ret;
351 }
352 return 0;
353 }
354 memcpy (&addr, &ifreq.ifr_addr, sizeof (struct sockaddr_in));
355
356 /* Interface's network mask. */
357 ret = if_ioctl (SIOCGIFNETMASK, (caddr_t) &ifreq);
358 if (ret < 0)
359 {
360 if (errno != EADDRNOTAVAIL)
361 {
ajs6099b3b2004-11-20 02:06:59 +0000362 zlog_warn ("SIOCGIFNETMASK fail: %s", safe_strerror (errno));
paul718e3742002-12-13 20:15:29 +0000363 return ret;
364 }
365 return 0;
366 }
367#ifdef ifr_netmask
368 memcpy (&mask, &ifreq.ifr_netmask, sizeof (struct sockaddr_in));
369#else
370 memcpy (&mask, &ifreq.ifr_addr, sizeof (struct sockaddr_in));
371#endif /* ifr_netmask */
372 prefixlen = ip_masklen (mask.sin_addr);
373
374 /* Point to point or borad cast address pointer init. */
375 dest_pnt = NULL;
376
377 if (ifp->flags & IFF_POINTOPOINT)
378 {
379 ret = if_ioctl (SIOCGIFDSTADDR, (caddr_t) &ifreq);
380 if (ret < 0)
381 {
382 if (errno != EADDRNOTAVAIL)
383 {
ajs6099b3b2004-11-20 02:06:59 +0000384 zlog_warn ("SIOCGIFDSTADDR fail: %s", safe_strerror (errno));
paul718e3742002-12-13 20:15:29 +0000385 return ret;
386 }
387 return 0;
388 }
389 memcpy (&dest, &ifreq.ifr_dstaddr, sizeof (struct sockaddr_in));
390 dest_pnt = &dest.sin_addr;
391 }
392 if (ifp->flags & IFF_BROADCAST)
393 {
394 ret = if_ioctl (SIOCGIFBRDADDR, (caddr_t) &ifreq);
395 if (ret < 0)
396 {
397 if (errno != EADDRNOTAVAIL)
398 {
ajs6099b3b2004-11-20 02:06:59 +0000399 zlog_warn ("SIOCGIFBRDADDR fail: %s", safe_strerror (errno));
paul718e3742002-12-13 20:15:29 +0000400 return ret;
401 }
402 return 0;
403 }
404 memcpy (&dest, &ifreq.ifr_broadaddr, sizeof (struct sockaddr_in));
405 dest_pnt = &dest.sin_addr;
406 }
407
408
409 /* Set address to the interface. */
410 connected_add_ipv4 (ifp, 0, &addr.sin_addr, prefixlen, dest_pnt, NULL);
411
412 return 0;
413}
414#endif /* HAVE_GETIFADDRS */
415
416/* Fetch interface information via ioctl(). */
417static void
418interface_info_ioctl ()
419{
paul1eb8ef22005-04-07 07:30:20 +0000420 struct listnode *node, *nnode;
paul718e3742002-12-13 20:15:29 +0000421 struct interface *ifp;
422
paula1ac18c2005-06-28 17:17:12 +0000423 for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp))
paul718e3742002-12-13 20:15:29 +0000424 {
paul718e3742002-12-13 20:15:29 +0000425 if_get_index (ifp);
426#ifdef SIOCGIFHWADDR
427 if_get_hwaddr (ifp);
428#endif /* SIOCGIFHWADDR */
429 if_get_flags (ifp);
430#ifndef HAVE_GETIFADDRS
431 if_get_addr (ifp);
432#endif /* ! HAVE_GETIFADDRS */
433 if_get_mtu (ifp);
434 if_get_metric (ifp);
435 }
436}
437
438/* Lookup all interface information. */
439void
440interface_list ()
441{
442 /* Linux can do both proc & ioctl, ioctl is the only way to get
443 interface aliases in 2.2 series kernels. */
444#ifdef HAVE_PROC_NET_DEV
445 interface_list_proc ();
446#endif /* HAVE_PROC_NET_DEV */
447 interface_list_ioctl ();
448
449 /* After listing is done, get index, address, flags and other
450 interface's information. */
451 interface_info_ioctl ();
452
453#ifdef HAVE_GETIFADDRS
454 if_getaddrs ();
455#endif /* HAVE_GETIFADDRS */
456
457#if defined(HAVE_IPV6) && defined(HAVE_PROC_NET_IF_INET6)
458 /* Linux provides interface's IPv6 address via
459 /proc/net/if_inet6. */
460 ifaddr_proc_ipv6 ();
461#endif /* HAVE_IPV6 && HAVE_PROC_NET_IF_INET6 */
462}