]> git.proxmox.com Git - mirror_frr.git/blob - zebra/if_ioctl_solaris.c
Merge pull request #964 from opensourcerouting/plist-trie-corruption-3.0
[mirror_frr.git] / zebra / if_ioctl_solaris.c
1 /*
2 * Interface looking up by ioctl () on Solaris.
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 SUNOS_5
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 "privs.h"
35 #include "vrf.h"
36 #include "vty.h"
37
38 #include "zebra/interface.h"
39 #include "zebra/ioctl_solaris.h"
40 #include "zebra/rib.h"
41
42 static int if_get_addr(struct interface *, struct sockaddr *, const char *);
43 static void interface_info_ioctl(struct interface *);
44 extern struct zebra_privs_t zserv_privs;
45
46 static int interface_list_ioctl(int af)
47 {
48 int ret;
49 int sock;
50 #define IFNUM_BASE 32
51 struct lifnum lifn;
52 int ifnum;
53 struct lifreq *lifreq;
54 struct lifconf lifconf;
55 struct interface *ifp;
56 int n;
57 int save_errno;
58 size_t needed, lastneeded = 0;
59 char *buf = NULL;
60
61 if (zserv_privs.change(ZPRIVS_RAISE))
62 zlog_err("Can't raise privileges");
63
64 sock = socket(af, SOCK_DGRAM, 0);
65 if (sock < 0) {
66 zlog_warn("Can't make %s socket stream: %s",
67 (af == AF_INET ? "AF_INET" : "AF_INET6"),
68 safe_strerror(errno));
69
70 if (zserv_privs.change(ZPRIVS_LOWER))
71 zlog_err("Can't lower privileges");
72
73 return -1;
74 }
75
76 calculate_lifc_len: /* must hold privileges to enter here */
77 lifn.lifn_family = af;
78 lifn.lifn_flags = LIFC_NOXMIT; /* we want NOXMIT interfaces too */
79 ret = ioctl(sock, SIOCGLIFNUM, &lifn);
80 save_errno = errno;
81
82 if (zserv_privs.change(ZPRIVS_LOWER))
83 zlog_err("Can't lower privileges");
84
85 if (ret < 0) {
86 zlog_warn("interface_list_ioctl: SIOCGLIFNUM failed %s",
87 safe_strerror(save_errno));
88 close(sock);
89 return -1;
90 }
91 ifnum = lifn.lifn_count;
92
93 /*
94 * When calculating the buffer size needed, add a small number
95 * of interfaces to those we counted. We do this to capture
96 * the interface status of potential interfaces which may have
97 * been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF.
98 */
99 needed = (ifnum + 4) * sizeof(struct lifreq);
100 if (needed > lastneeded || needed < lastneeded / 2) {
101 if (buf != NULL)
102 XFREE(MTYPE_TMP, buf);
103 if ((buf = XMALLOC(MTYPE_TMP, needed)) == NULL) {
104 zlog_warn("interface_list_ioctl: malloc failed");
105 close(sock);
106 return -1;
107 }
108 }
109 lastneeded = needed;
110
111 lifconf.lifc_family = af;
112 lifconf.lifc_flags = LIFC_NOXMIT;
113 lifconf.lifc_len = needed;
114 lifconf.lifc_buf = buf;
115
116 if (zserv_privs.change(ZPRIVS_RAISE))
117 zlog_err("Can't raise privileges");
118
119 ret = ioctl(sock, SIOCGLIFCONF, &lifconf);
120
121 if (ret < 0) {
122 if (errno == EINVAL)
123 goto calculate_lifc_len; /* deliberately hold privileges
124 */
125
126 zlog_warn("SIOCGLIFCONF: %s", safe_strerror(errno));
127
128 if (zserv_privs.change(ZPRIVS_LOWER))
129 zlog_err("Can't lower privileges");
130
131 goto end;
132 }
133
134 if (zserv_privs.change(ZPRIVS_LOWER))
135 zlog_err("Can't lower privileges");
136
137 /* Allocate interface. */
138 lifreq = lifconf.lifc_req;
139
140 for (n = 0; n < lifconf.lifc_len; n += sizeof(struct lifreq)) {
141 /* we treat Solaris logical interfaces as addresses, because
142 * that is
143 * how PF_ROUTE on Solaris treats them. Hence we can not
144 * directly use
145 * the lifreq_name to get the ifp. We need to normalise the
146 * name
147 * before attempting get.
148 *
149 * Solaris logical interface names are in the form of:
150 * <interface name>:<logical interface id>
151 */
152 unsigned int normallen = 0;
153 uint64_t lifflags;
154
155 /* We should exclude ~IFF_UP interfaces, as we'll find out about
156 * them
157 * coming up later through RTM_NEWADDR message on the route
158 * socket.
159 */
160 if (if_get_flags_direct(lifreq->lifr_name, &lifflags,
161 lifreq->lifr_addr.ss_family)
162 || !CHECK_FLAG(lifflags, IFF_UP)) {
163 lifreq++;
164 continue;
165 }
166
167 /* Find the normalised name */
168 while ((normallen < sizeof(lifreq->lifr_name))
169 && (*(lifreq->lifr_name + normallen) != '\0')
170 && (*(lifreq->lifr_name + normallen) != ':'))
171 normallen++;
172
173 ifp = if_get_by_name_len(lifreq->lifr_name, normallen,
174 VRF_DEFAULT, 0);
175
176 if (lifreq->lifr_addr.ss_family == AF_INET)
177 ifp->flags |= IFF_IPV4;
178
179 if (lifreq->lifr_addr.ss_family == AF_INET6) {
180 ifp->flags |= IFF_IPV6;
181 }
182
183 if_add_update(ifp);
184
185 interface_info_ioctl(ifp);
186
187 /* If a logical interface pass the full name so it can be
188 * as a label on the address
189 */
190 if (*(lifreq->lifr_name + normallen) != '\0')
191 if_get_addr(ifp, (struct sockaddr *)&lifreq->lifr_addr,
192 lifreq->lifr_name);
193 else
194 if_get_addr(ifp, (struct sockaddr *)&lifreq->lifr_addr,
195 NULL);
196
197 /* Poke the interface flags. Lets IFF_UP mangling kick in */
198 if_flags_update(ifp, ifp->flags);
199
200 lifreq++;
201 }
202
203 end:
204 close(sock);
205 XFREE(MTYPE_TMP, lifconf.lifc_buf);
206 return ret;
207 }
208
209 /* Get interface's index by ioctl. */
210 static int if_get_index(struct interface *ifp)
211 {
212 int ret;
213 struct lifreq lifreq;
214
215 lifreq_set_name(&lifreq, ifp->name);
216
217 if (ifp->flags & IFF_IPV4)
218 ret = AF_IOCTL(AF_INET, SIOCGLIFINDEX, (caddr_t)&lifreq);
219 else if (ifp->flags & IFF_IPV6)
220 ret = AF_IOCTL(AF_INET6, SIOCGLIFINDEX, (caddr_t)&lifreq);
221 else
222 ret = -1;
223
224 if (ret < 0) {
225 zlog_warn("SIOCGLIFINDEX(%s) failed", ifp->name);
226 return ret;
227 }
228
229 /* OK we got interface index. */
230 #ifdef ifr_ifindex
231 ifp->ifindex = lifreq.lifr_ifindex;
232 #else
233 ifp->ifindex = lifreq.lifr_index;
234 #endif
235 return ifp->ifindex;
236 }
237
238
239 /* Interface address lookup by ioctl. This function only looks up
240 IPv4 address. */
241 #define ADDRLEN(sa) \
242 (((sa)->sa_family == AF_INET ? sizeof(struct sockaddr_in) \
243 : sizeof(struct sockaddr_in6)))
244
245 #define SIN(s) ((struct sockaddr_in *)(s))
246 #define SIN6(s) ((struct sockaddr_in6 *)(s))
247
248 /* Retrieve address information for the given ifp */
249 static int if_get_addr(struct interface *ifp, struct sockaddr *addr,
250 const char *label)
251 {
252 int ret;
253 struct lifreq lifreq;
254 struct sockaddr_storage mask, dest;
255 char *dest_pnt = NULL;
256 u_char prefixlen = 0;
257 afi_t af;
258 int flags = 0;
259
260 /* Interface's name and address family.
261 * We need to use the logical interface name / label, if we've been
262 * given one, in order to get the right address
263 */
264 strncpy(lifreq.lifr_name, (label ? label : ifp->name), IFNAMSIZ);
265
266 /* Interface's address. */
267 memcpy(&lifreq.lifr_addr, addr, ADDRLEN(addr));
268 af = addr->sa_family;
269
270 /* Point to point or broad cast address pointer init. */
271 dest_pnt = NULL;
272
273 if (AF_IOCTL(af, SIOCGLIFDSTADDR, (caddr_t)&lifreq) >= 0) {
274 memcpy(&dest, &lifreq.lifr_dstaddr, ADDRLEN(addr));
275 if (af == AF_INET)
276 dest_pnt = (char *)&(SIN(&dest)->sin_addr);
277 else
278 dest_pnt = (char *)&(SIN6(&dest)->sin6_addr);
279 flags = ZEBRA_IFA_PEER;
280 }
281
282 if (af == AF_INET) {
283 ret = if_ioctl(SIOCGLIFNETMASK, (caddr_t)&lifreq);
284
285 if (ret < 0) {
286 if (errno != EADDRNOTAVAIL) {
287 zlog_warn("SIOCGLIFNETMASK (%s) fail: %s",
288 ifp->name, safe_strerror(errno));
289 return ret;
290 }
291 return 0;
292 }
293 memcpy(&mask, &lifreq.lifr_addr, ADDRLEN(addr));
294
295 prefixlen = ip_masklen(SIN(&mask)->sin_addr);
296 if (!dest_pnt
297 && (if_ioctl(SIOCGLIFBRDADDR, (caddr_t)&lifreq) >= 0)) {
298 memcpy(&dest, &lifreq.lifr_broadaddr,
299 sizeof(struct sockaddr_in));
300 dest_pnt = (char *)&SIN(&dest)->sin_addr;
301 }
302 } else if (af == AF_INET6) {
303 if (if_ioctl_ipv6(SIOCGLIFSUBNET, (caddr_t)&lifreq) < 0) {
304 if (ifp->flags & IFF_POINTOPOINT)
305 prefixlen = IPV6_MAX_BITLEN;
306 else
307 zlog_warn("SIOCGLIFSUBNET (%s) fail: %s",
308 ifp->name, safe_strerror(errno));
309 } else {
310 prefixlen = lifreq.lifr_addrlen;
311 }
312 }
313
314 /* Set address to the interface. */
315 if (af == AF_INET)
316 connected_add_ipv4(ifp, flags, &SIN(addr)->sin_addr, prefixlen,
317 (struct in_addr *)dest_pnt, label);
318 else if (af == AF_INET6)
319 connected_add_ipv6(ifp, flags, &SIN6(addr)->sin6_addr,
320 prefixlen, (struct in6_addr *)dest_pnt,
321 label);
322
323 return 0;
324 }
325
326 /* Fetch interface information via ioctl(). */
327 static void interface_info_ioctl(struct interface *ifp)
328 {
329 if_get_index(ifp);
330 if_get_flags(ifp);
331 if_get_mtu(ifp);
332 if_get_metric(ifp);
333 }
334
335 /* Lookup all interface information. */
336 void interface_list(struct zebra_ns *zns)
337 {
338 if (zns->ns_id != NS_DEFAULT) {
339 zlog_warn("interface_list: ignore NS %u", zns->ns_id);
340 return;
341 }
342 interface_list_ioctl(AF_INET);
343 interface_list_ioctl(AF_INET6);
344 interface_list_ioctl(AF_UNSPEC);
345 }
346
347 struct connected *if_lookup_linklocal(struct interface *ifp)
348 {
349 struct listnode *node;
350 struct connected *ifc;
351
352 if (ifp == NULL)
353 return NULL;
354
355 for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) {
356 if ((ifc->address->family == AF_INET6)
357 && (IN6_IS_ADDR_LINKLOCAL(&ifc->address->u.prefix6)))
358 return ifc;
359 }
360
361 return NULL;
362 }
363
364 #endif /* SUNOS_5 */