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. */
36int
37interface_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    {
53#ifdef FOX_RIP_DEBUG
54      zlog_warn ("Can't make AF_INET socket stream: %s", strerror (errno));
55#endif /* FOX_RIP_DEBUG */
56      return -1;
57    }
58
59  /* Set initial ifreq count.  This will be double when SIOCGIFCONF
60     fail.  Solaris has SIOCGIFNUM. */
61#ifdef SIOCGIFNUM
62  ret = ioctl (sock, SIOCGIFNUM, &ifnum);
63  if (ret < 0)
64    ifnum = IFNUM_BASE;
65  else
66    ifnum++;
67#else
68  ifnum = IFNUM_BASE;
69#endif /* SIOCGIFNUM */
70
71  ifconf.ifc_buf = NULL;
72
73  lastlen = 0;
74  /* Loop until SIOCGIFCONF success. */
75  for (;;)
76    {
77      ifconf.ifc_len = sizeof (struct ifreq) * ifnum;
78      ifconf.ifc_buf = XREALLOC(MTYPE_TMP, ifconf.ifc_buf, ifconf.ifc_len);
79
80      ret = ioctl(sock, SIOCGIFCONF, &ifconf);
81
82      if (ret < 0)
83	{
84#ifdef FOX_RIP_DEBUG
85	  zlog_warn ("SIOCGIFCONF: %s", strerror(errno));
86#endif /* FOX_RIP_DEBUG */
87	  goto end;
88	}
89      /* Repeatedly get info til buffer fails to grow. */
90      if (ifconf.ifc_len > lastlen)
91	{
92          lastlen = ifconf.ifc_len;
93	  ifnum += 10;
94	  continue;
95	}
96      /* Success. */
97      break;
98    }
99
100  /* Allocate interface. */
101  ifreq = ifconf.ifc_req;
102
103#ifdef OPEN_BSD
104  for (n = 0; n < ifconf.ifc_len; )
105    {
106      int size;
107
108      ifreq = (struct ifreq *)((caddr_t) ifconf.ifc_req + n);
109      ifp = if_get_by_name (ifreq->ifr_name);
110      if_add_update (ifp);
111      size = ifreq->ifr_addr.sa_len;
112      if (size < sizeof (ifreq->ifr_addr))
113	size = sizeof (ifreq->ifr_addr);
114      size += sizeof (ifreq->ifr_name);
115      n += size;
116    }
117#else
118  for (n = 0; n < ifconf.ifc_len; n += sizeof(struct ifreq))
119    {
120      ifp = if_get_by_name (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. */
134int
135if_get_index (struct interface *ifp)
136{
137  static int if_fake_index = 1;
138
139#ifdef HAVE_BROKEN_ALIASES
140  /* Linux 2.2.X does not provide individual interface index for aliases. */
141  ifp->ifindex = if_fake_index++;
142  return ifp->ifindex;
143#else
144#ifdef SIOCGIFINDEX
145  int ret;
146  struct ifreq ifreq;
147
148  ifreq_set_name (&ifreq, ifp);
149
150  ret = if_ioctl (SIOCGIFINDEX, (caddr_t) &ifreq);
151  if (ret < 0)
152    {
153      /* Linux 2.0.X does not have interface index. */
154      ifp->ifindex = if_fake_index++;
155      return ifp->ifindex;
156    }
157
158  /* OK we got interface index. */
159#ifdef ifr_ifindex
160  ifp->ifindex = ifreq.ifr_ifindex;
161#else
162  ifp->ifindex = ifreq.ifr_index;
163#endif
164  return ifp->ifindex;
165
166#else
167  ifp->ifindex = if_fake_index++;
168  return ifp->ifindex;
169#endif /* SIOCGIFINDEX */
170#endif /* HAVE_BROKEN_ALIASES */
171}
172
173#ifdef SIOCGIFHWADDR
174int
175if_get_hwaddr (struct interface *ifp)
176{
177  int ret;
178  struct ifreq ifreq;
179  int i;
180
181  strncpy (ifreq.ifr_name, ifp->name, IFNAMSIZ);
182  ifreq.ifr_addr.sa_family = AF_INET;
183
184  /* Fetch Hardware address if available. */
185  ret = if_ioctl (SIOCGIFHWADDR, (caddr_t) &ifreq);
186  if (ret < 0)
187    ifp->hw_addr_len = 0;
188  else
189    {
190      memcpy (ifp->hw_addr, ifreq.ifr_hwaddr.sa_data, 6);
191
192      for (i = 0; i < 6; i++)
193	if (ifp->hw_addr[i] != 0)
194	  break;
195
196      if (i == 6)
197	ifp->hw_addr_len = 0;
198      else
199	ifp->hw_addr_len = 6;
200    }
201  return 0;
202}
203#endif /* SIOCGIFHWADDR */
204
205#ifdef HAVE_GETIFADDRS
206#include <ifaddrs.h>
207
208int
209if_getaddrs ()
210{
211  int ret;
212  struct ifaddrs *ifap;
213  struct ifaddrs *ifapfree;
214  struct interface *ifp;
215  int prefixlen;
216
217  ret = getifaddrs (&ifap);
218  if (ret != 0)
219    {
220#ifdef FOX_RIP_DEBUG
221      zlog_err ("getifaddrs(): %s", strerror (errno));
222#endif /* FOX_RIP_DEBUG */
223      return -1;
224    }
225
226  for (ifapfree = ifap; ifap; ifap = ifap->ifa_next)
227    {
228      ifp = if_lookup_by_name (ifap->ifa_name);
229      if (ifp == NULL)
230	{
231#ifdef FOX_RIP_DEBUG
232	  zlog_err ("if_getaddrs(): Can't lookup interface %s\n",
233		    ifap->ifa_name);
234#endif /* FOX_RIP_DEBUG */
235	  continue;
236	}
237
238      if (ifap->ifa_addr->sa_family == AF_INET)
239	{
240	  struct sockaddr_in *addr;
241	  struct sockaddr_in *mask;
242	  struct sockaddr_in *dest;
243	  struct in_addr *dest_pnt;
244
245	  addr = (struct sockaddr_in *) ifap->ifa_addr;
246	  mask = (struct sockaddr_in *) ifap->ifa_netmask;
247	  prefixlen = ip_masklen (mask->sin_addr);
248
249	  dest_pnt = NULL;
250
251	  if (ifap->ifa_flags & IFF_POINTOPOINT)
252	    {
253	      dest = (struct sockaddr_in *) ifap->ifa_dstaddr;
254	      dest_pnt = &dest->sin_addr;
255	    }
256
257	  if (ifap->ifa_flags & IFF_BROADCAST)
258	    {
259	      dest = (struct sockaddr_in *) ifap->ifa_broadaddr;
260	      dest_pnt = &dest->sin_addr;
261	    }
262
263	  connected_add_ipv4 (ifp, 0, &addr->sin_addr,
264			      prefixlen, dest_pnt, NULL);
265	}
266#ifdef HAVE_IPV6
267      if (ifap->ifa_addr->sa_family == AF_INET6)
268	{
269	  struct sockaddr_in6 *addr;
270	  struct sockaddr_in6 *mask;
271	  struct sockaddr_in6 *dest;
272	  struct in6_addr *dest_pnt;
273
274	  addr = (struct sockaddr_in6 *) ifap->ifa_addr;
275	  mask = (struct sockaddr_in6 *) ifap->ifa_netmask;
276	  prefixlen = ip6_masklen (mask->sin6_addr);
277
278	  dest_pnt = NULL;
279
280	  if (ifap->ifa_flags & IFF_POINTOPOINT)
281	    {
282	      if (ifap->ifa_dstaddr)
283		{
284		  dest = (struct sockaddr_in6 *) ifap->ifa_dstaddr;
285		  dest_pnt = &dest->sin6_addr;
286		}
287	    }
288
289	  if (ifap->ifa_flags & IFF_BROADCAST)
290	    {
291	      if (ifap->ifa_broadaddr)
292		{
293		  dest = (struct sockaddr_in6 *) ifap->ifa_broadaddr;
294		  dest_pnt = &dest->sin6_addr;
295		}
296	    }
297
298	  connected_add_ipv6 (ifp, &addr->sin6_addr, prefixlen, dest_pnt);
299	}
300#endif /* HAVE_IPV6 */
301    }
302
303  freeifaddrs (ifapfree);
304
305  return 0;
306}
307#else /* HAVE_GETIFADDRS */
308/* Interface address lookup by ioctl.  This function only looks up
309   IPv4 address. */
310int
311if_get_addr (struct interface *ifp)
312{
313  int ret;
314  struct ifreq ifreq;
315  struct sockaddr_in addr;
316  struct sockaddr_in mask;
317  struct sockaddr_in dest;
318  struct in_addr *dest_pnt;
319  u_char prefixlen;
320
321  /* Interface's name and address family. */
322  strncpy (ifreq.ifr_name, ifp->name, IFNAMSIZ);
323  ifreq.ifr_addr.sa_family = AF_INET;
324
325  /* Interface's address. */
326  ret = if_ioctl (SIOCGIFADDR, (caddr_t) &ifreq);
327  if (ret < 0)
328    {
329      if (errno != EADDRNOTAVAIL)
330	{
331#ifdef FOX_RIP_DEBUG
332	  zlog_warn ("SIOCGIFADDR fail: %s", strerror (errno));
333#endif /* FOX_RIP_DEBUG */
334	  return ret;
335	}
336      return 0;
337    }
338  memcpy (&addr, &ifreq.ifr_addr, sizeof (struct sockaddr_in));
339
340  /* Interface's network mask. */
341  ret = if_ioctl (SIOCGIFNETMASK, (caddr_t) &ifreq);
342  if (ret < 0)
343    {
344      if (errno != EADDRNOTAVAIL)
345	{
346#ifdef FOX_RIP_DEBUG
347	  zlog_warn ("SIOCGIFNETMASK fail: %s", strerror (errno));
348#endif /* FOX_RIP_DEBUG */
349	  return ret;
350	}
351      return 0;
352    }
353#ifdef ifr_netmask
354  memcpy (&mask, &ifreq.ifr_netmask, sizeof (struct sockaddr_in));
355#else
356  memcpy (&mask, &ifreq.ifr_addr, sizeof (struct sockaddr_in));
357#endif /* ifr_netmask */
358  prefixlen = ip_masklen (mask.sin_addr);
359
360  /* Point to point or borad cast address pointer init. */
361  dest_pnt = NULL;
362
363  if (ifp->flags & IFF_POINTOPOINT)
364    {
365      ret = if_ioctl (SIOCGIFDSTADDR, (caddr_t) &ifreq);
366      if (ret < 0)
367	{
368	  if (errno != EADDRNOTAVAIL)
369	    {
370#ifdef FOX_RIP_DEBUG
371	      zlog_warn ("SIOCGIFDSTADDR fail: %s", strerror (errno));
372#endif /* FOX_RIP_DEBUG */
373	      return ret;
374	    }
375	  return 0;
376	}
377      memcpy (&dest, &ifreq.ifr_dstaddr, sizeof (struct sockaddr_in));
378      dest_pnt = &dest.sin_addr;
379    }
380  if (ifp->flags & IFF_BROADCAST)
381    {
382      ret = if_ioctl (SIOCGIFBRDADDR, (caddr_t) &ifreq);
383      if (ret < 0)
384	{
385	  if (errno != EADDRNOTAVAIL)
386	    {
387#ifdef FOX_RIP_DEBUG
388	      zlog_warn ("SIOCGIFBRDADDR fail: %s", strerror (errno));
389#endif /* FOX_RIP_DEBUG */
390	      return ret;
391	    }
392	  return 0;
393	}
394      memcpy (&dest, &ifreq.ifr_broadaddr, sizeof (struct sockaddr_in));
395      dest_pnt = &dest.sin_addr;
396    }
397
398
399  /* Set address to the interface. */
400  connected_add_ipv4 (ifp, 0, &addr.sin_addr, prefixlen, dest_pnt, NULL);
401
402  return 0;
403}
404#endif /* HAVE_GETIFADDRS */
405
406/* Fetch interface information via ioctl(). */
407static void
408interface_info_ioctl ()
409{
410  listnode node;
411  struct interface *ifp;
412
413  for (node = listhead (iflist); node; node = nextnode (node))
414    {
415      ifp = getdata (node);
416
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. */
431void
432interface_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}
455