]> git.proxmox.com Git - mirror_frr.git/blob - zebra/if_ioctl_solaris.c
Merge branch 'frr/pull/546' ("bgpd: resolve issue with sending vpn labels")
[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 #include "if.h"
25 #include "sockunion.h"
26 #include "prefix.h"
27 #include "ioctl.h"
28 #include "connected.h"
29 #include "memory.h"
30 #include "zebra_memory.h"
31 #include "log.h"
32 #include "privs.h"
33 #include "vrf.h"
34 #include "vty.h"
35
36 #include "zebra/interface.h"
37 #include "zebra/ioctl_solaris.h"
38 #include "zebra/rib.h"
39
40 static int if_get_addr (struct interface *, struct sockaddr *, const char *);
41 static void interface_info_ioctl (struct interface *);
42 extern struct zebra_privs_t zserv_privs;
43
44 static int
45 interface_list_ioctl (int af)
46 {
47 int ret;
48 int sock;
49 #define IFNUM_BASE 32
50 struct lifnum lifn;
51 int ifnum;
52 struct lifreq *lifreq;
53 struct lifconf lifconf;
54 struct interface *ifp;
55 int n;
56 int save_errno;
57 size_t needed, lastneeded = 0;
58 char *buf = NULL;
59
60 if (zserv_privs.change(ZPRIVS_RAISE))
61 zlog_err("Can't raise privileges");
62
63 sock = socket (af, SOCK_DGRAM, 0);
64 if (sock < 0)
65 {
66 zlog_warn ("Can't make %s socket stream: %s",
67 (af == AF_INET ? "AF_INET" : "AF_INET6"), safe_strerror (errno));
68
69 if (zserv_privs.change(ZPRIVS_LOWER))
70 zlog_err("Can't lower privileges");
71
72 return -1;
73 }
74
75 calculate_lifc_len: /* must hold privileges to enter here */
76 lifn.lifn_family = af;
77 lifn.lifn_flags = LIFC_NOXMIT; /* we want NOXMIT interfaces too */
78 ret = ioctl (sock, SIOCGLIFNUM, &lifn);
79 save_errno = errno;
80
81 if (zserv_privs.change(ZPRIVS_LOWER))
82 zlog_err("Can't lower privileges");
83
84 if (ret < 0)
85 {
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 {
102 if (buf != NULL)
103 XFREE (MTYPE_TMP, buf);
104 if ((buf = XMALLOC (MTYPE_TMP, needed)) == NULL)
105 {
106 zlog_warn ("interface_list_ioctl: malloc failed");
107 close (sock);
108 return -1;
109 }
110 }
111 lastneeded = needed;
112
113 lifconf.lifc_family = af;
114 lifconf.lifc_flags = LIFC_NOXMIT;
115 lifconf.lifc_len = needed;
116 lifconf.lifc_buf = buf;
117
118 if (zserv_privs.change(ZPRIVS_RAISE))
119 zlog_err("Can't raise privileges");
120
121 ret = ioctl (sock, SIOCGLIFCONF, &lifconf);
122
123 if (ret < 0)
124 {
125 if (errno == EINVAL)
126 goto calculate_lifc_len; /* deliberately hold privileges */
127
128 zlog_warn ("SIOCGLIFCONF: %s", safe_strerror (errno));
129
130 if (zserv_privs.change(ZPRIVS_LOWER))
131 zlog_err("Can't lower privileges");
132
133 goto end;
134 }
135
136 if (zserv_privs.change(ZPRIVS_LOWER))
137 zlog_err("Can't lower privileges");
138
139 /* Allocate interface. */
140 lifreq = lifconf.lifc_req;
141
142 for (n = 0; n < lifconf.lifc_len; n += sizeof (struct lifreq))
143 {
144 /* we treat Solaris logical interfaces as addresses, because that is
145 * how PF_ROUTE on Solaris treats them. Hence we can not directly use
146 * the lifreq_name to get the ifp. We need to normalise the 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 them
156 * coming up later through RTM_NEWADDR message on the route socket.
157 */
158 if (if_get_flags_direct (lifreq->lifr_name, &lifflags,
159 lifreq->lifr_addr.ss_family)
160 || !CHECK_FLAG (lifflags, IFF_UP))
161 {
162 lifreq++;
163 continue;
164 }
165
166 /* Find the normalised name */
167 while ( (normallen < sizeof(lifreq->lifr_name))
168 && ( *(lifreq->lifr_name + normallen) != '\0')
169 && ( *(lifreq->lifr_name + normallen) != ':') )
170 normallen++;
171
172 ifp = if_get_by_name_len(lifreq->lifr_name, normallen, VRF_DEFAULT, 0);
173
174 if (lifreq->lifr_addr.ss_family == AF_INET)
175 ifp->flags |= IFF_IPV4;
176
177 if (lifreq->lifr_addr.ss_family == AF_INET6)
178 {
179 ifp->flags |= IFF_IPV6;
180 }
181
182 if_add_update (ifp);
183
184 interface_info_ioctl (ifp);
185
186 /* If a logical interface pass the full name so it can be
187 * as a label on the address
188 */
189 if ( *(lifreq->lifr_name + normallen) != '\0')
190 if_get_addr (ifp, (struct sockaddr *) &lifreq->lifr_addr,
191 lifreq->lifr_name);
192 else
193 if_get_addr (ifp, (struct sockaddr *) &lifreq->lifr_addr, NULL);
194
195 /* Poke the interface flags. Lets IFF_UP mangling kick in */
196 if_flags_update (ifp, ifp->flags);
197
198 lifreq++;
199 }
200
201 end:
202 close (sock);
203 XFREE (MTYPE_TMP, lifconf.lifc_buf);
204 return ret;
205 }
206
207 /* Get interface's index by ioctl. */
208 static int
209 if_get_index (struct interface *ifp)
210 {
211 int ret;
212 struct lifreq lifreq;
213
214 lifreq_set_name (&lifreq, ifp->name);
215
216 if (ifp->flags & IFF_IPV4)
217 ret = AF_IOCTL (AF_INET, SIOCGLIFINDEX, (caddr_t) & lifreq);
218 else if (ifp->flags & IFF_IPV6)
219 ret = AF_IOCTL (AF_INET6, SIOCGLIFINDEX, (caddr_t) & lifreq);
220 else
221 ret = -1;
222
223 if (ret < 0)
224 {
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
240 /* Interface address lookup by ioctl. This function only looks up
241 IPv4 address. */
242 #define ADDRLEN(sa) (((sa)->sa_family == AF_INET ? \
243 sizeof (struct sockaddr_in) : 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
250 if_get_addr (struct interface *ifp, struct sockaddr *addr, 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 {
275 memcpy (&dest, &lifreq.lifr_dstaddr, ADDRLEN (addr));
276 if (af == AF_INET)
277 dest_pnt = (char *) &(SIN (&dest)->sin_addr);
278 else
279 dest_pnt = (char *) &(SIN6 (&dest)->sin6_addr);
280 flags = ZEBRA_IFA_PEER;
281 }
282
283 if (af == AF_INET)
284 {
285 ret = if_ioctl (SIOCGLIFNETMASK, (caddr_t) & lifreq);
286
287 if (ret < 0)
288 {
289 if (errno != EADDRNOTAVAIL)
290 {
291 zlog_warn ("SIOCGLIFNETMASK (%s) fail: %s", ifp->name,
292 safe_strerror (errno));
293 return ret;
294 }
295 return 0;
296 }
297 memcpy (&mask, &lifreq.lifr_addr, ADDRLEN (addr));
298
299 prefixlen = ip_masklen (SIN (&mask)->sin_addr);
300 if (!dest_pnt && (if_ioctl (SIOCGLIFBRDADDR, (caddr_t) & lifreq) >= 0))
301 {
302 memcpy (&dest, &lifreq.lifr_broadaddr, sizeof (struct sockaddr_in));
303 dest_pnt = (char *) &SIN (&dest)->sin_addr;
304 }
305 }
306 else if (af == AF_INET6)
307 {
308 if (if_ioctl_ipv6 (SIOCGLIFSUBNET, (caddr_t) & lifreq) < 0)
309 {
310 if (ifp->flags & IFF_POINTOPOINT)
311 prefixlen = IPV6_MAX_BITLEN;
312 else
313 zlog_warn ("SIOCGLIFSUBNET (%s) fail: %s",
314 ifp->name, safe_strerror (errno));
315 }
316 else
317 {
318 prefixlen = lifreq.lifr_addrlen;
319 }
320 }
321
322 /* Set address to the interface. */
323 if (af == AF_INET)
324 connected_add_ipv4 (ifp, flags, &SIN (addr)->sin_addr, prefixlen,
325 (struct in_addr *) dest_pnt, label);
326 else if (af == AF_INET6)
327 connected_add_ipv6 (ifp, flags, &SIN6 (addr)->sin6_addr, prefixlen,
328 (struct in6_addr *) dest_pnt, label);
329
330 return 0;
331 }
332
333 /* Fetch interface information via ioctl(). */
334 static void
335 interface_info_ioctl (struct interface *ifp)
336 {
337 if_get_index (ifp);
338 if_get_flags (ifp);
339 if_get_mtu (ifp);
340 if_get_metric (ifp);
341 }
342
343 /* Lookup all interface information. */
344 void
345 interface_list (struct zebra_ns *zns)
346 {
347 if (zns->ns_id != NS_DEFAULT)
348 {
349 zlog_warn ("interface_list: ignore NS %u", zns->ns_id);
350 return;
351 }
352 interface_list_ioctl (AF_INET);
353 interface_list_ioctl (AF_INET6);
354 interface_list_ioctl (AF_UNSPEC);
355 }
356
357 struct connected *
358 if_lookup_linklocal (struct interface *ifp)
359 {
360 struct listnode *node;
361 struct connected *ifc;
362
363 if (ifp == NULL)
364 return NULL;
365
366 for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc))
367 {
368 if ((ifc->address->family == AF_INET6) &&
369 (IN6_IS_ADDR_LINKLOCAL (&ifc->address->u.prefix6)))
370 return ifc;
371 }
372
373 return NULL;
374 }