blob: 46f530114cecb7aa15579ad19305393be5fa1f0d [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. */
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 zlog_warn ("Can't make AF_INET socket stream: %s", 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", 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 (ifreq->ifr_name);
106 if_add_update (ifp);
107 size = ifreq->ifr_addr.sa_len;
108 if (size < sizeof (ifreq->ifr_addr))
109 size = sizeof (ifreq->ifr_addr);
110 size += sizeof (ifreq->ifr_name);
111 n += size;
112 }
113#else
114 for (n = 0; n < ifconf.ifc_len; n += sizeof(struct ifreq))
115 {
116 ifp = if_get_by_name (ifreq->ifr_name);
117 if_add_update (ifp);
118 ifreq++;
119 }
120#endif /* OPEN_BSD */
121
122 end:
123 close (sock);
124 XFREE (MTYPE_TMP, ifconf.ifc_buf);
125
126 return ret;
127}
128
129/* Get interface's index by ioctl. */
130int
131if_get_index (struct interface *ifp)
132{
133 static int if_fake_index = 1;
134
135#ifdef HAVE_BROKEN_ALIASES
136 /* Linux 2.2.X does not provide individual interface index for aliases. */
137 ifp->ifindex = if_fake_index++;
138 return ifp->ifindex;
139#else
140#ifdef SIOCGIFINDEX
141 int ret;
142 struct ifreq ifreq;
143
144 ifreq_set_name (&ifreq, ifp);
145
146 ret = if_ioctl (SIOCGIFINDEX, (caddr_t) &ifreq);
147 if (ret < 0)
148 {
149 /* Linux 2.0.X does not have interface index. */
150 ifp->ifindex = if_fake_index++;
151 return ifp->ifindex;
152 }
153
154 /* OK we got interface index. */
155#ifdef ifr_ifindex
156 ifp->ifindex = ifreq.ifr_ifindex;
157#else
158 ifp->ifindex = ifreq.ifr_index;
159#endif
160 return ifp->ifindex;
161
162#else
163 ifp->ifindex = if_fake_index++;
164 return ifp->ifindex;
165#endif /* SIOCGIFINDEX */
166#endif /* HAVE_BROKEN_ALIASES */
167}
168
169#ifdef SIOCGIFHWADDR
170int
171if_get_hwaddr (struct interface *ifp)
172{
173 int ret;
174 struct ifreq ifreq;
175 int i;
176
177 strncpy (ifreq.ifr_name, ifp->name, IFNAMSIZ);
178 ifreq.ifr_addr.sa_family = AF_INET;
179
180 /* Fetch Hardware address if available. */
181 ret = if_ioctl (SIOCGIFHWADDR, (caddr_t) &ifreq);
182 if (ret < 0)
183 ifp->hw_addr_len = 0;
184 else
185 {
186 memcpy (ifp->hw_addr, ifreq.ifr_hwaddr.sa_data, 6);
187
188 for (i = 0; i < 6; i++)
189 if (ifp->hw_addr[i] != 0)
190 break;
191
192 if (i == 6)
193 ifp->hw_addr_len = 0;
194 else
195 ifp->hw_addr_len = 6;
196 }
197 return 0;
198}
199#endif /* SIOCGIFHWADDR */
200
201#ifdef HAVE_GETIFADDRS
202#include <ifaddrs.h>
203
204int
205if_getaddrs ()
206{
207 int ret;
208 struct ifaddrs *ifap;
209 struct ifaddrs *ifapfree;
210 struct interface *ifp;
211 int prefixlen;
212
213 ret = getifaddrs (&ifap);
214 if (ret != 0)
215 {
216 zlog_err ("getifaddrs(): %s", strerror (errno));
217 return -1;
218 }
219
220 for (ifapfree = ifap; ifap; ifap = ifap->ifa_next)
221 {
222 ifp = if_lookup_by_name (ifap->ifa_name);
223 if (ifp == NULL)
224 {
225 zlog_err ("if_getaddrs(): Can't lookup interface %s\n",
226 ifap->ifa_name);
227 continue;
228 }
229
230 if (ifap->ifa_addr->sa_family == AF_INET)
231 {
232 struct sockaddr_in *addr;
233 struct sockaddr_in *mask;
234 struct sockaddr_in *dest;
235 struct in_addr *dest_pnt;
236
237 addr = (struct sockaddr_in *) ifap->ifa_addr;
238 mask = (struct sockaddr_in *) ifap->ifa_netmask;
239 prefixlen = ip_masklen (mask->sin_addr);
240
241 dest_pnt = NULL;
242
243 if (ifap->ifa_flags & IFF_POINTOPOINT)
244 {
245 dest = (struct sockaddr_in *) ifap->ifa_dstaddr;
246 dest_pnt = &dest->sin_addr;
247 }
248
249 if (ifap->ifa_flags & IFF_BROADCAST)
250 {
251 dest = (struct sockaddr_in *) ifap->ifa_broadaddr;
252 dest_pnt = &dest->sin_addr;
253 }
254
255 connected_add_ipv4 (ifp, 0, &addr->sin_addr,
256 prefixlen, dest_pnt, NULL);
257 }
258#ifdef HAVE_IPV6
259 if (ifap->ifa_addr->sa_family == AF_INET6)
260 {
261 struct sockaddr_in6 *addr;
262 struct sockaddr_in6 *mask;
263 struct sockaddr_in6 *dest;
264 struct in6_addr *dest_pnt;
265
266 addr = (struct sockaddr_in6 *) ifap->ifa_addr;
267 mask = (struct sockaddr_in6 *) ifap->ifa_netmask;
268 prefixlen = ip6_masklen (mask->sin6_addr);
269
270 dest_pnt = NULL;
271
272 if (ifap->ifa_flags & IFF_POINTOPOINT)
273 {
274 if (ifap->ifa_dstaddr)
275 {
276 dest = (struct sockaddr_in6 *) ifap->ifa_dstaddr;
277 dest_pnt = &dest->sin6_addr;
278 }
279 }
280
281 if (ifap->ifa_flags & IFF_BROADCAST)
282 {
283 if (ifap->ifa_broadaddr)
284 {
285 dest = (struct sockaddr_in6 *) ifap->ifa_broadaddr;
286 dest_pnt = &dest->sin6_addr;
287 }
288 }
289
290 connected_add_ipv6 (ifp, &addr->sin6_addr, prefixlen, dest_pnt);
291 }
292#endif /* HAVE_IPV6 */
293 }
294
295 freeifaddrs (ifapfree);
296
297 return 0;
298}
299#else /* HAVE_GETIFADDRS */
300/* Interface address lookup by ioctl. This function only looks up
301 IPv4 address. */
302int
303if_get_addr (struct interface *ifp)
304{
305 int ret;
306 struct ifreq ifreq;
307 struct sockaddr_in addr;
308 struct sockaddr_in mask;
309 struct sockaddr_in dest;
310 struct in_addr *dest_pnt;
311 u_char prefixlen;
312
313 /* Interface's name and address family. */
314 strncpy (ifreq.ifr_name, ifp->name, IFNAMSIZ);
315 ifreq.ifr_addr.sa_family = AF_INET;
316
317 /* Interface's address. */
318 ret = if_ioctl (SIOCGIFADDR, (caddr_t) &ifreq);
319 if (ret < 0)
320 {
321 if (errno != EADDRNOTAVAIL)
322 {
323 zlog_warn ("SIOCGIFADDR fail: %s", strerror (errno));
324 return ret;
325 }
326 return 0;
327 }
328 memcpy (&addr, &ifreq.ifr_addr, sizeof (struct sockaddr_in));
329
330 /* Interface's network mask. */
331 ret = if_ioctl (SIOCGIFNETMASK, (caddr_t) &ifreq);
332 if (ret < 0)
333 {
334 if (errno != EADDRNOTAVAIL)
335 {
336 zlog_warn ("SIOCGIFNETMASK fail: %s", strerror (errno));
337 return ret;
338 }
339 return 0;
340 }
341#ifdef ifr_netmask
342 memcpy (&mask, &ifreq.ifr_netmask, sizeof (struct sockaddr_in));
343#else
344 memcpy (&mask, &ifreq.ifr_addr, sizeof (struct sockaddr_in));
345#endif /* ifr_netmask */
346 prefixlen = ip_masklen (mask.sin_addr);
347
348 /* Point to point or borad cast address pointer init. */
349 dest_pnt = NULL;
350
351 if (ifp->flags & IFF_POINTOPOINT)
352 {
353 ret = if_ioctl (SIOCGIFDSTADDR, (caddr_t) &ifreq);
354 if (ret < 0)
355 {
356 if (errno != EADDRNOTAVAIL)
357 {
358 zlog_warn ("SIOCGIFDSTADDR fail: %s", strerror (errno));
359 return ret;
360 }
361 return 0;
362 }
363 memcpy (&dest, &ifreq.ifr_dstaddr, sizeof (struct sockaddr_in));
364 dest_pnt = &dest.sin_addr;
365 }
366 if (ifp->flags & IFF_BROADCAST)
367 {
368 ret = if_ioctl (SIOCGIFBRDADDR, (caddr_t) &ifreq);
369 if (ret < 0)
370 {
371 if (errno != EADDRNOTAVAIL)
372 {
373 zlog_warn ("SIOCGIFBRDADDR fail: %s", strerror (errno));
374 return ret;
375 }
376 return 0;
377 }
378 memcpy (&dest, &ifreq.ifr_broadaddr, sizeof (struct sockaddr_in));
379 dest_pnt = &dest.sin_addr;
380 }
381
382
383 /* Set address to the interface. */
384 connected_add_ipv4 (ifp, 0, &addr.sin_addr, prefixlen, dest_pnt, NULL);
385
386 return 0;
387}
388#endif /* HAVE_GETIFADDRS */
389
390/* Fetch interface information via ioctl(). */
391static void
392interface_info_ioctl ()
393{
394 listnode node;
395 struct interface *ifp;
396
397 for (node = listhead (iflist); node; node = nextnode (node))
398 {
399 ifp = getdata (node);
400
401 if_get_index (ifp);
402#ifdef SIOCGIFHWADDR
403 if_get_hwaddr (ifp);
404#endif /* SIOCGIFHWADDR */
405 if_get_flags (ifp);
406#ifndef HAVE_GETIFADDRS
407 if_get_addr (ifp);
408#endif /* ! HAVE_GETIFADDRS */
409 if_get_mtu (ifp);
410 if_get_metric (ifp);
411 }
412}
413
414/* Lookup all interface information. */
415void
416interface_list ()
417{
418 /* Linux can do both proc & ioctl, ioctl is the only way to get
419 interface aliases in 2.2 series kernels. */
420#ifdef HAVE_PROC_NET_DEV
421 interface_list_proc ();
422#endif /* HAVE_PROC_NET_DEV */
423 interface_list_ioctl ();
424
425 /* After listing is done, get index, address, flags and other
426 interface's information. */
427 interface_info_ioctl ();
428
429#ifdef HAVE_GETIFADDRS
430 if_getaddrs ();
431#endif /* HAVE_GETIFADDRS */
432
433#if defined(HAVE_IPV6) && defined(HAVE_PROC_NET_IF_INET6)
434 /* Linux provides interface's IPv6 address via
435 /proc/net/if_inet6. */
436 ifaddr_proc_ipv6 ();
437#endif /* HAVE_IPV6 && HAVE_PROC_NET_IF_INET6 */
438}