]>
Commit | Line | Data |
---|---|---|
718e3744 | 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 | * | |
896014f4 DL |
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 | |
718e3744 | 20 | */ |
21 | ||
22 | #include <zebra.h> | |
23 | ||
ddfeb486 DL |
24 | #ifdef OPEN_BSD |
25 | ||
718e3744 | 26 | #include "if.h" |
27 | #include "sockunion.h" | |
28 | #include "prefix.h" | |
29 | #include "ioctl.h" | |
30 | #include "connected.h" | |
31 | #include "memory.h" | |
4a1ab8e4 | 32 | #include "zebra_memory.h" |
718e3744 | 33 | #include "log.h" |
8f7d9fc0 | 34 | #include "vrf.h" |
82283592 | 35 | #include "vty.h" |
718e3744 | 36 | |
37 | #include "zebra/interface.h" | |
8f7d9fc0 | 38 | #include "zebra/rib.h" |
718e3744 | 39 | |
24f5e2fc DL |
40 | #include <ifaddrs.h> |
41 | ||
718e3744 | 42 | /* Interface looking up using infamous SIOCGIFCONF. */ |
d62a17ae | 43 | static int interface_list_ioctl(void) |
718e3744 | 44 | { |
d62a17ae | 45 | int ret; |
46 | int sock; | |
718e3744 | 47 | #define IFNUM_BASE 32 |
d62a17ae | 48 | int ifnum; |
49 | struct ifreq *ifreq; | |
50 | struct ifconf ifconf; | |
51 | struct interface *ifp; | |
52 | int n; | |
53 | int lastlen; | |
54 | ||
55 | /* Normally SIOCGIFCONF works with AF_INET socket. */ | |
56 | sock = socket(AF_INET, SOCK_DGRAM, 0); | |
57 | if (sock < 0) { | |
58 | zlog_warn("Can't make AF_INET socket stream: %s", | |
59 | safe_strerror(errno)); | |
60 | return -1; | |
61 | } | |
62 | ||
63 | /* Set initial ifreq count. This will be double when SIOCGIFCONF | |
64 | fail. Solaris has SIOCGIFNUM. */ | |
718e3744 | 65 | #ifdef SIOCGIFNUM |
d62a17ae | 66 | ret = ioctl(sock, SIOCGIFNUM, &ifnum); |
67 | if (ret < 0) | |
68 | ifnum = IFNUM_BASE; | |
69 | else | |
70 | ifnum++; | |
718e3744 | 71 | #else |
d62a17ae | 72 | ifnum = IFNUM_BASE; |
718e3744 | 73 | #endif /* SIOCGIFNUM */ |
74 | ||
d62a17ae | 75 | ifconf.ifc_buf = NULL; |
76 | ||
77 | lastlen = 0; | |
78 | /* Loop until SIOCGIFCONF success. */ | |
79 | for (;;) { | |
80 | ifconf.ifc_len = sizeof(struct ifreq) * ifnum; | |
81 | ifconf.ifc_buf = | |
82 | XREALLOC(MTYPE_TMP, ifconf.ifc_buf, ifconf.ifc_len); | |
83 | ||
84 | ret = ioctl(sock, SIOCGIFCONF, &ifconf); | |
85 | ||
86 | if (ret < 0) { | |
87 | zlog_warn("SIOCGIFCONF: %s", safe_strerror(errno)); | |
88 | goto end; | |
89 | } | |
90 | /* Repeatedly get info til buffer fails to grow. */ | |
91 | if (ifconf.ifc_len > lastlen) { | |
92 | lastlen = ifconf.ifc_len; | |
93 | ifnum += 10; | |
94 | continue; | |
95 | } | |
96 | /* Success. */ | |
97 | break; | |
718e3744 | 98 | } |
718e3744 | 99 | |
d62a17ae | 100 | /* Allocate interface. */ |
101 | ifreq = ifconf.ifc_req; | |
718e3744 | 102 | |
103 | #ifdef OPEN_BSD | |
d62a17ae | 104 | for (n = 0; n < ifconf.ifc_len;) { |
105 | unsigned int size; | |
106 | ||
107 | ifreq = (struct ifreq *)((caddr_t)ifconf.ifc_req + n); | |
108 | ifp = if_get_by_name_len( | |
109 | ifreq->ifr_name, | |
110 | strnlen(ifreq->ifr_name, sizeof(ifreq->ifr_name)), | |
111 | VRF_DEFAULT, 0); | |
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 | } | |
718e3744 | 119 | #else |
d62a17ae | 120 | for (n = 0; n < ifconf.ifc_len; n += sizeof(struct ifreq)) { |
121 | ifp = if_get_by_name_len( | |
122 | ifreq->ifr_name, | |
123 | strnlen(ifreq->ifr_name, sizeof(ifreq->ifr_name)), | |
124 | VRF_DEFAULT, 0); | |
125 | if_add_update(ifp); | |
126 | ifreq++; | |
127 | } | |
718e3744 | 128 | #endif /* OPEN_BSD */ |
129 | ||
d62a17ae | 130 | end: |
131 | close(sock); | |
132 | XFREE(MTYPE_TMP, ifconf.ifc_buf); | |
718e3744 | 133 | |
d62a17ae | 134 | return ret; |
718e3744 | 135 | } |
136 | ||
137 | /* Get interface's index by ioctl. */ | |
d62a17ae | 138 | static int if_get_index(struct interface *ifp) |
718e3744 | 139 | { |
d62a17ae | 140 | ifp->ifindex = if_nametoindex(ifp->name); |
141 | return ifp->ifindex; | |
718e3744 | 142 | } |
143 | ||
144 | #ifdef SIOCGIFHWADDR | |
d62a17ae | 145 | static int if_get_hwaddr(struct interface *ifp) |
718e3744 | 146 | { |
d62a17ae | 147 | int ret; |
148 | struct ifreq ifreq; | |
149 | int i; | |
150 | ||
151 | strncpy(ifreq.ifr_name, ifp->name, IFNAMSIZ); | |
152 | ifreq.ifr_addr.sa_family = AF_INET; | |
153 | ||
154 | /* Fetch Hardware address if available. */ | |
155 | ret = if_ioctl(SIOCGIFHWADDR, (caddr_t)&ifreq); | |
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; | |
718e3744 | 171 | } |
172 | #endif /* SIOCGIFHWADDR */ | |
173 | ||
d62a17ae | 174 | static int if_getaddrs(void) |
718e3744 | 175 | { |
d62a17ae | 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 | zlog_err("getifaddrs(): %s", safe_strerror(errno)); | |
185 | return -1; | |
718e3744 | 186 | } |
187 | ||
d62a17ae | 188 | for (ifapfree = ifap; ifap; ifap = ifap->ifa_next) { |
189 | if (ifap->ifa_addr == NULL) { | |
190 | zlog_err( | |
191 | "%s: nonsensical ifaddr with NULL ifa_addr, ifname %s", | |
192 | __func__, | |
193 | (ifap->ifa_name ? ifap->ifa_name : "(null)")); | |
194 | continue; | |
195 | } | |
196 | ||
197 | ifp = if_lookup_by_name(ifap->ifa_name, VRF_DEFAULT); | |
198 | if (ifp == NULL) { | |
199 | zlog_err("if_getaddrs(): Can't lookup interface %s\n", | |
200 | ifap->ifa_name); | |
201 | continue; | |
202 | } | |
203 | ||
204 | if (ifap->ifa_addr->sa_family == AF_INET) { | |
205 | struct sockaddr_in *addr; | |
206 | struct sockaddr_in *mask; | |
207 | struct sockaddr_in *dest; | |
208 | struct in_addr *dest_pnt; | |
209 | int flags = 0; | |
210 | ||
211 | addr = (struct sockaddr_in *)ifap->ifa_addr; | |
212 | mask = (struct sockaddr_in *)ifap->ifa_netmask; | |
213 | prefixlen = ip_masklen(mask->sin_addr); | |
214 | ||
215 | dest_pnt = NULL; | |
216 | ||
217 | if (ifap->ifa_dstaddr | |
218 | && !IPV4_ADDR_SAME(&addr->sin_addr, | |
219 | &((struct sockaddr_in *) | |
220 | ifap->ifa_dstaddr) | |
221 | ->sin_addr)) { | |
222 | dest = (struct sockaddr_in *)ifap->ifa_dstaddr; | |
223 | dest_pnt = &dest->sin_addr; | |
224 | flags = ZEBRA_IFA_PEER; | |
225 | } else if (ifap->ifa_broadaddr | |
226 | && !IPV4_ADDR_SAME( | |
227 | &addr->sin_addr, | |
228 | &((struct sockaddr_in *) | |
229 | ifap->ifa_broadaddr) | |
230 | ->sin_addr)) { | |
231 | dest = (struct sockaddr_in *) | |
232 | ifap->ifa_broadaddr; | |
233 | dest_pnt = &dest->sin_addr; | |
234 | } | |
235 | ||
236 | connected_add_ipv4(ifp, flags, &addr->sin_addr, | |
237 | prefixlen, dest_pnt, NULL); | |
238 | } | |
239 | if (ifap->ifa_addr->sa_family == AF_INET6) { | |
240 | struct sockaddr_in6 *addr; | |
241 | struct sockaddr_in6 *mask; | |
242 | struct sockaddr_in6 *dest; | |
243 | struct in6_addr *dest_pnt; | |
244 | int flags = 0; | |
245 | ||
246 | addr = (struct sockaddr_in6 *)ifap->ifa_addr; | |
247 | mask = (struct sockaddr_in6 *)ifap->ifa_netmask; | |
248 | prefixlen = ip6_masklen(mask->sin6_addr); | |
249 | ||
250 | dest_pnt = NULL; | |
251 | ||
252 | if (ifap->ifa_dstaddr | |
253 | && !IPV6_ADDR_SAME(&addr->sin6_addr, | |
254 | &((struct sockaddr_in6 *) | |
255 | ifap->ifa_dstaddr) | |
256 | ->sin6_addr)) { | |
257 | dest = (struct sockaddr_in6 *)ifap->ifa_dstaddr; | |
258 | dest_pnt = &dest->sin6_addr; | |
259 | flags = ZEBRA_IFA_PEER; | |
260 | } else if (ifap->ifa_broadaddr | |
261 | && !IPV6_ADDR_SAME( | |
262 | &addr->sin6_addr, | |
263 | &((struct sockaddr_in6 *) | |
264 | ifap->ifa_broadaddr) | |
265 | ->sin6_addr)) { | |
266 | dest = (struct sockaddr_in6 *) | |
267 | ifap->ifa_broadaddr; | |
268 | dest_pnt = &dest->sin6_addr; | |
269 | } | |
718e3744 | 270 | |
ab836aab | 271 | #if defined(KAME) |
d62a17ae | 272 | if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)) { |
273 | addr->sin6_scope_id = | |
274 | ntohs(*(u_int16_t *)&addr->sin6_addr | |
275 | .s6_addr[2]); | |
276 | addr->sin6_addr.s6_addr[2] = | |
277 | addr->sin6_addr.s6_addr[3] = 0; | |
278 | } | |
279 | #endif | |
280 | ||
281 | connected_add_ipv6(ifp, flags, &addr->sin6_addr, | |
282 | prefixlen, dest_pnt, NULL); | |
283 | } | |
718e3744 | 284 | } |
718e3744 | 285 | |
d62a17ae | 286 | freeifaddrs(ifapfree); |
718e3744 | 287 | |
d62a17ae | 288 | return 0; |
718e3744 | 289 | } |
718e3744 | 290 | |
291 | /* Fetch interface information via ioctl(). */ | |
d62a17ae | 292 | static void interface_info_ioctl() |
718e3744 | 293 | { |
d62a17ae | 294 | struct listnode *node, *nnode; |
295 | struct interface *ifp; | |
296 | ||
297 | for (ALL_LIST_ELEMENTS(vrf_iflist(VRF_DEFAULT), node, nnode, ifp)) { | |
298 | if_get_index(ifp); | |
718e3744 | 299 | #ifdef SIOCGIFHWADDR |
d62a17ae | 300 | if_get_hwaddr(ifp); |
718e3744 | 301 | #endif /* SIOCGIFHWADDR */ |
d62a17ae | 302 | if_get_flags(ifp); |
303 | if_get_mtu(ifp); | |
304 | if_get_metric(ifp); | |
305 | } | |
718e3744 | 306 | } |
307 | ||
308 | /* Lookup all interface information. */ | |
d62a17ae | 309 | void interface_list(struct zebra_ns *zns) |
718e3744 | 310 | { |
12f6fb97 | 311 | |
d62a17ae | 312 | zlog_info("interface_list: NS %u", zns->ns_id); |
12f6fb97 | 313 | |
d62a17ae | 314 | /* Linux can do both proc & ioctl, ioctl is the only way to get |
315 | interface aliases in 2.2 series kernels. */ | |
718e3744 | 316 | #ifdef HAVE_PROC_NET_DEV |
d62a17ae | 317 | interface_list_proc(); |
718e3744 | 318 | #endif /* HAVE_PROC_NET_DEV */ |
d62a17ae | 319 | interface_list_ioctl(); |
718e3744 | 320 | |
d62a17ae | 321 | /* After listing is done, get index, address, flags and other |
322 | interface's information. */ | |
323 | interface_info_ioctl(); | |
718e3744 | 324 | |
d62a17ae | 325 | if_getaddrs(); |
718e3744 | 326 | |
56c1f7d8 | 327 | #if defined(HAVE_PROC_NET_IF_INET6) |
d62a17ae | 328 | /* Linux provides interface's IPv6 address via |
329 | /proc/net/if_inet6. */ | |
330 | ifaddr_proc_ipv6(); | |
56c1f7d8 | 331 | #endif /* HAVE_PROC_NET_IF_INET6 */ |
718e3744 | 332 | } |
ddfeb486 DL |
333 | |
334 | #endif /* OPEN_BSD */ |