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. */
36static int
37interface_list_ioctl (void)
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    {
53      zlog_warn ("Can't make AF_INET socket stream: %s", safe_strerror (errno));
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	{
82	  zlog_warn ("SIOCGIFCONF: %s", safe_strerror(errno));
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);
105      ifp = if_get_by_name_len(ifreq->ifr_name,
106			       strnlen(ifreq->ifr_name,
107				       sizeof(ifreq->ifr_name)));
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    {
118      ifp = if_get_by_name_len(ifreq->ifr_name,
119			       strnlen(ifreq->ifr_name,
120				       sizeof(ifreq->ifr_name)));
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. */
134static int
135if_get_index (struct interface *ifp)
136{
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. */
142  int ret;
143  struct ifreq ifreq;
144  static int if_fake_index;
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
162
163#else
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;
172  ifp->ifindex = if_fake_index++;
173#endif
174
175  return ifp->ifindex;
176}
177
178#ifdef SIOCGIFHWADDR
179static int
180if_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
213static int
214if_getaddrs (void)
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    {
225      zlog_err ("getifaddrs(): %s", safe_strerror (errno));
226      return -1;
227    }
228
229  for (ifapfree = ifap; ifap; ifap = ifap->ifa_next)
230    {
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
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;
252	  int flags = 0;
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
260	  if (ifap->ifa_dstaddr &&
261	      !IPV4_ADDR_SAME(&addr->sin_addr,
262			      &((struct sockaddr_in *)
263			      	ifap->ifa_dstaddr)->sin_addr))
264	    {
265	      dest = (struct sockaddr_in *) ifap->ifa_dstaddr;
266	      dest_pnt = &dest->sin_addr;
267	      flags = ZEBRA_IFA_PEER;
268	    }
269	  else if (ifap->ifa_broadaddr &&
270		   !IPV4_ADDR_SAME(&addr->sin_addr,
271				   &((struct sockaddr_in *)
272				     ifap->ifa_broadaddr)->sin_addr))
273	    {
274	      dest = (struct sockaddr_in *) ifap->ifa_broadaddr;
275	      dest_pnt = &dest->sin_addr;
276	    }
277
278	  connected_add_ipv4 (ifp, flags, &addr->sin_addr,
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;
288	  int flags = 0;
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
296	  if (ifap->ifa_dstaddr &&
297	      !IPV6_ADDR_SAME(&addr->sin6_addr,
298			      &((struct sockaddr_in6 *)
299			      	ifap->ifa_dstaddr)->sin6_addr))
300	    {
301	      dest = (struct sockaddr_in6 *) ifap->ifa_dstaddr;
302	      dest_pnt = &dest->sin6_addr;
303	      flags = ZEBRA_IFA_PEER;
304	    }
305	  else if (ifap->ifa_broadaddr &&
306		   !IPV6_ADDR_SAME(&addr->sin6_addr,
307				   &((struct sockaddr_in6 *)
308				     ifap->ifa_broadaddr)->sin6_addr))
309	    {
310	      dest = (struct sockaddr_in6 *) ifap->ifa_broadaddr;
311	      dest_pnt = &dest->sin6_addr;
312	    }
313
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
323	  connected_add_ipv6 (ifp, flags, &addr->sin6_addr, prefixlen,
324	                      dest_pnt, NULL);
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. */
336int
337if_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;
346  int flags = 0;
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	{
358	  zlog_warn ("SIOCGIFADDR fail: %s", safe_strerror (errno));
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	{
371	  zlog_warn ("SIOCGIFNETMASK fail: %s", safe_strerror (errno));
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
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))
393    {
394      memcpy (&dest, &ifreq.ifr_dstaddr, sizeof (struct sockaddr_in));
395      dest_pnt = &dest.sin_addr;
396      flags = ZEBRA_IFA_PEER;
397    }
398  if (!dest_pnt)
399    {
400      ret = if_ioctl (SIOCGIFBRDADDR, (caddr_t) &ifreq);
401      if (ret < 0)
402	{
403	  if (errno != EADDRNOTAVAIL)
404	    zlog_warn ("SIOCGIFBRDADDR fail: %s", safe_strerror (errno));
405	}
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        }
411    }
412
413
414  /* Set address to the interface. */
415  connected_add_ipv4 (ifp, flags, &addr.sin_addr, prefixlen, dest_pnt, NULL);
416
417  return 0;
418}
419#endif /* HAVE_GETIFADDRS */
420
421/* Fetch interface information via ioctl(). */
422static void
423interface_info_ioctl ()
424{
425  struct listnode *node, *nnode;
426  struct interface *ifp;
427
428  for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp))
429    {
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. */
444void
445interface_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}
468