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