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