]> git.proxmox.com Git - mirror_frr.git/blob - zebra/if_ioctl.c
2004-11-19 Andrew J. Schorr <ajschorr@alumni.princeton.edu>
[mirror_frr.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
33 #include "zebra/interface.h"
34
35 /* Interface looking up using infamous SIOCGIFCONF. */
36 int
37 interface_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", 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 (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. */
130 int
131 if_get_index (struct interface *ifp)
132 {
133 #if defined(HAVE_IF_NAMETOINDEX)
134 /* Modern systems should have if_nametoindex(3). */
135 ifp->ifindex = if_nametoindex(ifp->name);
136 #elif defined(SIOCGIFINDEX) && !defined(HAVE_BROKEN_ALIASES)
137 /* Fall-back for older linuxes. */
138 int ret;
139 struct ifreq ifreq;
140 static int if_fake_index;
141
142 ifreq_set_name (&ifreq, ifp);
143
144 ret = if_ioctl (SIOCGIFINDEX, (caddr_t) &ifreq);
145 if (ret < 0)
146 {
147 /* Linux 2.0.X does not have interface index. */
148 ifp->ifindex = if_fake_index++;
149 return ifp->ifindex;
150 }
151
152 /* OK we got interface index. */
153 #ifdef ifr_ifindex
154 ifp->ifindex = ifreq.ifr_ifindex;
155 #else
156 ifp->ifindex = ifreq.ifr_index;
157 #endif
158
159 #else
160 /* Linux 2.2.X does not provide individual interface index
161 for aliases and we know it. For others issue a warning. */
162 #if !defined(HAVE_BROKEN_ALIASES)
163 #warning "Using if_fake_index. You may want to add appropriate"
164 #warning "mapping from ifname to ifindex for your system..."
165 #endif
166 /* This branch probably won't provide usable results, but anyway... */
167 static int if_fake_index = 1;
168 ifp->ifindex = if_fake_index++;
169 #endif
170
171 return ifp->ifindex;
172 }
173
174 #ifdef SIOCGIFHWADDR
175 int
176 if_get_hwaddr (struct interface *ifp)
177 {
178 int ret;
179 struct ifreq ifreq;
180 int i;
181
182 strncpy (ifreq.ifr_name, ifp->name, IFNAMSIZ);
183 ifreq.ifr_addr.sa_family = AF_INET;
184
185 /* Fetch Hardware address if available. */
186 ret = if_ioctl (SIOCGIFHWADDR, (caddr_t) &ifreq);
187 if (ret < 0)
188 ifp->hw_addr_len = 0;
189 else
190 {
191 memcpy (ifp->hw_addr, ifreq.ifr_hwaddr.sa_data, 6);
192
193 for (i = 0; i < 6; i++)
194 if (ifp->hw_addr[i] != 0)
195 break;
196
197 if (i == 6)
198 ifp->hw_addr_len = 0;
199 else
200 ifp->hw_addr_len = 6;
201 }
202 return 0;
203 }
204 #endif /* SIOCGIFHWADDR */
205
206 #ifdef HAVE_GETIFADDRS
207 #include <ifaddrs.h>
208
209 int
210 if_getaddrs ()
211 {
212 int ret;
213 struct ifaddrs *ifap;
214 struct ifaddrs *ifapfree;
215 struct interface *ifp;
216 int prefixlen;
217
218 ret = getifaddrs (&ifap);
219 if (ret != 0)
220 {
221 zlog_err ("getifaddrs(): %s", safe_strerror (errno));
222 return -1;
223 }
224
225 for (ifapfree = ifap; ifap; ifap = ifap->ifa_next)
226 {
227 ifp = if_lookup_by_name (ifap->ifa_name);
228 if (ifp == NULL)
229 {
230 zlog_err ("if_getaddrs(): Can't lookup interface %s\n",
231 ifap->ifa_name);
232 continue;
233 }
234
235 if (ifap->ifa_addr->sa_family == AF_INET)
236 {
237 struct sockaddr_in *addr;
238 struct sockaddr_in *mask;
239 struct sockaddr_in *dest;
240 struct in_addr *dest_pnt;
241
242 addr = (struct sockaddr_in *) ifap->ifa_addr;
243 mask = (struct sockaddr_in *) ifap->ifa_netmask;
244 prefixlen = ip_masklen (mask->sin_addr);
245
246 dest_pnt = NULL;
247
248 if (ifap->ifa_flags & IFF_POINTOPOINT)
249 {
250 dest = (struct sockaddr_in *) ifap->ifa_dstaddr;
251 dest_pnt = &dest->sin_addr;
252 }
253
254 if (ifap->ifa_flags & IFF_BROADCAST)
255 {
256 dest = (struct sockaddr_in *) ifap->ifa_broadaddr;
257 dest_pnt = &dest->sin_addr;
258 }
259
260 connected_add_ipv4 (ifp, 0, &addr->sin_addr,
261 prefixlen, dest_pnt, NULL);
262 }
263 #ifdef HAVE_IPV6
264 if (ifap->ifa_addr->sa_family == AF_INET6)
265 {
266 struct sockaddr_in6 *addr;
267 struct sockaddr_in6 *mask;
268 struct sockaddr_in6 *dest;
269 struct in6_addr *dest_pnt;
270
271 addr = (struct sockaddr_in6 *) ifap->ifa_addr;
272 mask = (struct sockaddr_in6 *) ifap->ifa_netmask;
273 prefixlen = ip6_masklen (mask->sin6_addr);
274
275 dest_pnt = NULL;
276
277 if (ifap->ifa_flags & IFF_POINTOPOINT)
278 {
279 if (ifap->ifa_dstaddr)
280 {
281 dest = (struct sockaddr_in6 *) ifap->ifa_dstaddr;
282 dest_pnt = &dest->sin6_addr;
283 }
284 }
285
286 if (ifap->ifa_flags & IFF_BROADCAST)
287 {
288 if (ifap->ifa_broadaddr)
289 {
290 dest = (struct sockaddr_in6 *) ifap->ifa_broadaddr;
291 dest_pnt = &dest->sin6_addr;
292 }
293 }
294
295 #if defined(KAME)
296 if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr))
297 {
298 addr->sin6_scope_id =
299 ntohs(*(u_int16_t *)&addr->sin6_addr.s6_addr[2]);
300 addr->sin6_addr.s6_addr[2] = addr->sin6_addr.s6_addr[3] = 0;
301 }
302 #endif
303
304 connected_add_ipv6 (ifp, &addr->sin6_addr, prefixlen, dest_pnt);
305 }
306 #endif /* HAVE_IPV6 */
307 }
308
309 freeifaddrs (ifapfree);
310
311 return 0;
312 }
313 #else /* HAVE_GETIFADDRS */
314 /* Interface address lookup by ioctl. This function only looks up
315 IPv4 address. */
316 int
317 if_get_addr (struct interface *ifp)
318 {
319 int ret;
320 struct ifreq ifreq;
321 struct sockaddr_in addr;
322 struct sockaddr_in mask;
323 struct sockaddr_in dest;
324 struct in_addr *dest_pnt;
325 u_char prefixlen;
326
327 /* Interface's name and address family. */
328 strncpy (ifreq.ifr_name, ifp->name, IFNAMSIZ);
329 ifreq.ifr_addr.sa_family = AF_INET;
330
331 /* Interface's address. */
332 ret = if_ioctl (SIOCGIFADDR, (caddr_t) &ifreq);
333 if (ret < 0)
334 {
335 if (errno != EADDRNOTAVAIL)
336 {
337 zlog_warn ("SIOCGIFADDR fail: %s", safe_strerror (errno));
338 return ret;
339 }
340 return 0;
341 }
342 memcpy (&addr, &ifreq.ifr_addr, sizeof (struct sockaddr_in));
343
344 /* Interface's network mask. */
345 ret = if_ioctl (SIOCGIFNETMASK, (caddr_t) &ifreq);
346 if (ret < 0)
347 {
348 if (errno != EADDRNOTAVAIL)
349 {
350 zlog_warn ("SIOCGIFNETMASK fail: %s", safe_strerror (errno));
351 return ret;
352 }
353 return 0;
354 }
355 #ifdef ifr_netmask
356 memcpy (&mask, &ifreq.ifr_netmask, sizeof (struct sockaddr_in));
357 #else
358 memcpy (&mask, &ifreq.ifr_addr, sizeof (struct sockaddr_in));
359 #endif /* ifr_netmask */
360 prefixlen = ip_masklen (mask.sin_addr);
361
362 /* Point to point or borad cast address pointer init. */
363 dest_pnt = NULL;
364
365 if (ifp->flags & IFF_POINTOPOINT)
366 {
367 ret = if_ioctl (SIOCGIFDSTADDR, (caddr_t) &ifreq);
368 if (ret < 0)
369 {
370 if (errno != EADDRNOTAVAIL)
371 {
372 zlog_warn ("SIOCGIFDSTADDR fail: %s", safe_strerror (errno));
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 zlog_warn ("SIOCGIFBRDADDR fail: %s", safe_strerror (errno));
388 return ret;
389 }
390 return 0;
391 }
392 memcpy (&dest, &ifreq.ifr_broadaddr, sizeof (struct sockaddr_in));
393 dest_pnt = &dest.sin_addr;
394 }
395
396
397 /* Set address to the interface. */
398 connected_add_ipv4 (ifp, 0, &addr.sin_addr, prefixlen, dest_pnt, NULL);
399
400 return 0;
401 }
402 #endif /* HAVE_GETIFADDRS */
403
404 /* Fetch interface information via ioctl(). */
405 static void
406 interface_info_ioctl ()
407 {
408 struct listnode *node;
409 struct interface *ifp;
410
411 LIST_LOOP (iflist, ifp, node)
412 {
413 ifp = getdata (node);
414
415 if_get_index (ifp);
416 #ifdef SIOCGIFHWADDR
417 if_get_hwaddr (ifp);
418 #endif /* SIOCGIFHWADDR */
419 if_get_flags (ifp);
420 #ifndef HAVE_GETIFADDRS
421 if_get_addr (ifp);
422 #endif /* ! HAVE_GETIFADDRS */
423 if_get_mtu (ifp);
424 if_get_metric (ifp);
425 }
426 }
427
428 /* Lookup all interface information. */
429 void
430 interface_list ()
431 {
432 /* Linux can do both proc & ioctl, ioctl is the only way to get
433 interface aliases in 2.2 series kernels. */
434 #ifdef HAVE_PROC_NET_DEV
435 interface_list_proc ();
436 #endif /* HAVE_PROC_NET_DEV */
437 interface_list_ioctl ();
438
439 /* After listing is done, get index, address, flags and other
440 interface's information. */
441 interface_info_ioctl ();
442
443 #ifdef HAVE_GETIFADDRS
444 if_getaddrs ();
445 #endif /* HAVE_GETIFADDRS */
446
447 #if defined(HAVE_IPV6) && defined(HAVE_PROC_NET_IF_INET6)
448 /* Linux provides interface's IPv6 address via
449 /proc/net/if_inet6. */
450 ifaddr_proc_ipv6 ();
451 #endif /* HAVE_IPV6 && HAVE_PROC_NET_IF_INET6 */
452 }