]> git.proxmox.com Git - mirror_frr.git/blob - zebra/if_ioctl.c
*: change if_lookup_by_name() api with vrf
[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 along
18 * with this program; see the file COPYING; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #include <zebra.h>
23
24 #ifdef OPEN_BSD
25
26 #include "if.h"
27 #include "sockunion.h"
28 #include "prefix.h"
29 #include "ioctl.h"
30 #include "connected.h"
31 #include "memory.h"
32 #include "zebra_memory.h"
33 #include "log.h"
34 #include "vrf.h"
35 #include "vty.h"
36 #include "lib_errors.h"
37
38 #include "zebra/interface.h"
39 #include "zebra/rib.h"
40 #include "zebra/rt.h"
41 #include "zebra/zebra_errors.h"
42
43 #include <ifaddrs.h>
44
45 /* Interface looking up using infamous SIOCGIFCONF. */
46 static int interface_list_ioctl(void)
47 {
48 int ret;
49 int sock;
50 #define IFNUM_BASE 32
51 int ifnum;
52 struct ifreq *ifreq;
53 struct ifconf ifconf;
54 struct interface *ifp;
55 int n;
56 int lastlen;
57 struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
58
59 /* Normally SIOCGIFCONF works with AF_INET socket. */
60 sock = socket(AF_INET, SOCK_DGRAM, 0);
61 if (sock < 0) {
62 flog_err_sys(EC_LIB_SOCKET,
63 "Can't make AF_INET socket stream: %s",
64 safe_strerror(errno));
65 return -1;
66 }
67
68 /* Set initial ifreq count. This will be double when SIOCGIFCONF
69 fail. Solaris has SIOCGIFNUM. */
70 #ifdef SIOCGIFNUM
71 ret = ioctl(sock, SIOCGIFNUM, &ifnum);
72 if (ret < 0)
73 ifnum = IFNUM_BASE;
74 else
75 ifnum++;
76 #else
77 ifnum = IFNUM_BASE;
78 #endif /* SIOCGIFNUM */
79
80 ifconf.ifc_buf = NULL;
81
82 lastlen = 0;
83 /* Loop until SIOCGIFCONF success. */
84 for (;;) {
85 ifconf.ifc_len = sizeof(struct ifreq) * ifnum;
86 ifconf.ifc_buf =
87 XREALLOC(MTYPE_TMP, ifconf.ifc_buf, ifconf.ifc_len);
88
89 ret = ioctl(sock, SIOCGIFCONF, &ifconf);
90
91 if (ret < 0) {
92 flog_err_sys(EC_LIB_SYSTEM_CALL, "SIOCGIFCONF: %s",
93 safe_strerror(errno));
94 goto end;
95 }
96 /* Repeatedly get info til buffer fails to grow. */
97 if (ifconf.ifc_len > lastlen) {
98 lastlen = ifconf.ifc_len;
99 ifnum += 10;
100 continue;
101 }
102 /* Success. */
103 break;
104 }
105
106 /* Allocate interface. */
107 ifreq = ifconf.ifc_req;
108
109 #ifdef OPEN_BSD
110 for (n = 0; n < ifconf.ifc_len;) {
111 unsigned int size;
112
113 ifreq = (struct ifreq *)((caddr_t)ifconf.ifc_req + n);
114 ifp = if_get_by_name(ifreq->ifr_name, vrf);
115 if_add_update(ifp);
116 size = ifreq->ifr_addr.sa_len;
117 if (size < sizeof(ifreq->ifr_addr))
118 size = sizeof(ifreq->ifr_addr);
119 size += sizeof(ifreq->ifr_name);
120 n += size;
121 }
122 #else
123 for (n = 0; n < ifconf.ifc_len; n += sizeof(struct ifreq)) {
124 ifp = if_get_by_name(ifreq->ifr_name, vrf);
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 if_get_index(struct interface *ifp)
139 {
140 if_set_index(ifp, if_nametoindex(ifp->name));
141 return ifp->ifindex;
142 }
143
144 #ifdef SIOCGIFHWADDR
145 static int if_get_hwaddr(struct interface *ifp)
146 {
147 int ret;
148 struct ifreq ifreq;
149 int i;
150
151 strlcpy(ifreq.ifr_name, ifp->name, sizeof(ifreq.ifr_name));
152 ifreq.ifr_addr.sa_family = AF_INET;
153
154 /* Fetch Hardware address if available. */
155 ret = vrf_if_ioctl(SIOCGIFHWADDR, (caddr_t)&ifreq, ifp->vrf_id);
156 if (ret < 0)
157 ifp->hw_addr_len = 0;
158 else {
159 memcpy(ifp->hw_addr, ifreq.ifr_hwaddr.sa_data, 6);
160
161 for (i = 0; i < 6; i++)
162 if (ifp->hw_addr[i] != 0)
163 break;
164
165 if (i == 6)
166 ifp->hw_addr_len = 0;
167 else
168 ifp->hw_addr_len = 6;
169 }
170 return 0;
171 }
172 #endif /* SIOCGIFHWADDR */
173
174 static int if_getaddrs(void)
175 {
176 int ret;
177 struct ifaddrs *ifap;
178 struct ifaddrs *ifapfree;
179 struct interface *ifp;
180 int prefixlen;
181
182 ret = getifaddrs(&ifap);
183 if (ret != 0) {
184 flog_err_sys(EC_LIB_SYSTEM_CALL, "getifaddrs(): %s",
185 safe_strerror(errno));
186 return -1;
187 }
188
189 for (ifapfree = ifap; ifap; ifap = ifap->ifa_next) {
190 if (ifap->ifa_addr == NULL) {
191 flog_err(
192 EC_LIB_INTERFACE,
193 "%s: nonsensical ifaddr with NULL ifa_addr, ifname %s",
194 __func__,
195 (ifap->ifa_name ? ifap->ifa_name : "(null)"));
196 continue;
197 }
198
199 ifp = if_lookup_by_name(ifap->ifa_name,
200 vrf_lookup_by_id(VRF_DEFAULT));
201 if (ifp == NULL) {
202 flog_err(EC_LIB_INTERFACE,
203 "if_getaddrs(): Can't lookup interface %s\n",
204 ifap->ifa_name);
205 continue;
206 }
207
208 if (ifap->ifa_addr->sa_family == AF_INET) {
209 struct sockaddr_in *addr;
210 struct sockaddr_in *mask;
211 struct sockaddr_in *dest;
212 struct in_addr *dest_pnt;
213 int flags = 0;
214
215 addr = (struct sockaddr_in *)ifap->ifa_addr;
216 mask = (struct sockaddr_in *)ifap->ifa_netmask;
217 prefixlen = ip_masklen(mask->sin_addr);
218
219 dest_pnt = NULL;
220
221 if (if_is_pointopoint(ifp) && ifap->ifa_dstaddr
222 && !IPV4_ADDR_SAME(&addr->sin_addr,
223 &((struct sockaddr_in *)
224 ifap->ifa_dstaddr)
225 ->sin_addr)) {
226 dest = (struct sockaddr_in *)ifap->ifa_dstaddr;
227 dest_pnt = &dest->sin_addr;
228 flags = ZEBRA_IFA_PEER;
229 } else if (ifap->ifa_broadaddr
230 && !IPV4_ADDR_SAME(
231 &addr->sin_addr,
232 &((struct sockaddr_in *)
233 ifap->ifa_broadaddr)
234 ->sin_addr)) {
235 dest = (struct sockaddr_in *)
236 ifap->ifa_broadaddr;
237 dest_pnt = &dest->sin_addr;
238 }
239
240 connected_add_ipv4(ifp, flags, &addr->sin_addr,
241 prefixlen, dest_pnt, NULL,
242 METRIC_MAX);
243 }
244 if (ifap->ifa_addr->sa_family == AF_INET6) {
245 struct sockaddr_in6 *addr;
246 struct sockaddr_in6 *mask;
247 int flags = 0;
248
249 addr = (struct sockaddr_in6 *)ifap->ifa_addr;
250 mask = (struct sockaddr_in6 *)ifap->ifa_netmask;
251 prefixlen = ip6_masklen(mask->sin6_addr);
252
253 #if defined(KAME)
254 if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)) {
255 addr->sin6_scope_id =
256 ntohs(*(uint16_t *)&addr->sin6_addr
257 .s6_addr[2]);
258 addr->sin6_addr.s6_addr[2] =
259 addr->sin6_addr.s6_addr[3] = 0;
260 }
261 #endif
262
263 connected_add_ipv6(ifp, flags, &addr->sin6_addr, NULL,
264 prefixlen, NULL, METRIC_MAX);
265 }
266 }
267
268 freeifaddrs(ifapfree);
269
270 return 0;
271 }
272
273 /* Fetch interface information via ioctl(). */
274 static void interface_info_ioctl()
275 {
276 struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
277 struct interface *ifp;
278
279 FOR_ALL_INTERFACES (vrf, ifp) {
280 if_get_index(ifp);
281 #ifdef SIOCGIFHWADDR
282 if_get_hwaddr(ifp);
283 #endif /* SIOCGIFHWADDR */
284 if_get_flags(ifp);
285 if_get_mtu(ifp);
286 if_get_metric(ifp);
287 }
288 }
289
290 /* Lookup all interface information. */
291 void interface_list(struct zebra_ns *zns)
292 {
293
294 zlog_info("interface_list: NS %u", zns->ns_id);
295
296 /* Linux can do both proc & ioctl, ioctl is the only way to get
297 interface aliases in 2.2 series kernels. */
298 #ifdef HAVE_PROC_NET_DEV
299 interface_list_proc();
300 #endif /* HAVE_PROC_NET_DEV */
301 interface_list_ioctl();
302
303 /* After listing is done, get index, address, flags and other
304 interface's information. */
305 interface_info_ioctl();
306
307 if_getaddrs();
308
309 #if defined(HAVE_PROC_NET_IF_INET6)
310 /* Linux provides interface's IPv6 address via
311 /proc/net/if_inet6. */
312 ifaddr_proc_ipv6();
313 #endif /* HAVE_PROC_NET_IF_INET6 */
314 }
315
316 #endif /* OPEN_BSD */