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