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