blob: d54ed6a020925bb8c307c70a1a6edf622a379cf9 [file] [log] [blame]
paul88424682004-05-09 18:21:35 +00001/*
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"
31#include "log.h"
paul48a46fa2004-05-11 10:55:22 +000032#include "privs.h"
Feng Lu758fb8f2014-07-03 18:23:09 +080033#include "vrf.h"
paul88424682004-05-09 18:21:35 +000034
35#include "zebra/interface.h"
David Lamparter285ed0c2015-09-15 02:46:53 -070036#include "zebra/ioctl_solaris.h"
Feng Lu758fb8f2014-07-03 18:23:09 +080037#include "zebra/rib.h"
paul88424682004-05-09 18:21:35 +000038
paul0752ef02005-11-03 12:35:21 +000039static int if_get_addr (struct interface *, struct sockaddr *, const char *);
paul88424682004-05-09 18:21:35 +000040static void interface_info_ioctl (struct interface *);
paul48a46fa2004-05-11 10:55:22 +000041extern struct zebra_privs_t zserv_privs;
paul88424682004-05-09 18:21:35 +000042
43int
44interface_list_ioctl (int af)
45{
46 int ret;
47 int sock;
48#define IFNUM_BASE 32
49 struct lifnum lifn;
50 int ifnum;
51 struct lifreq *lifreq;
52 struct lifconf lifconf;
53 struct interface *ifp;
54 int n;
ajs4460e7a2005-01-29 17:07:40 +000055 int save_errno;
paul88424682004-05-09 18:21:35 +000056 size_t needed, lastneeded = 0;
57 char *buf = NULL;
58
59 if (zserv_privs.change(ZPRIVS_RAISE))
60 zlog (NULL, LOG_ERR, "Can't raise privileges");
61
62 sock = socket (af, SOCK_DGRAM, 0);
63 if (sock < 0)
64 {
65 zlog_warn ("Can't make %s socket stream: %s",
ajs6099b3b2004-11-20 02:06:59 +000066 (af == AF_INET ? "AF_INET" : "AF_INET6"), safe_strerror (errno));
paul88424682004-05-09 18:21:35 +000067
68 if (zserv_privs.change(ZPRIVS_LOWER))
69 zlog (NULL, LOG_ERR, "Can't lower privileges");
70
71 return -1;
72 }
73
74calculate_lifc_len: /* must hold privileges to enter here */
75 lifn.lifn_family = af;
paul0752ef02005-11-03 12:35:21 +000076 lifn.lifn_flags = LIFC_NOXMIT; /* we want NOXMIT interfaces too */
paul88424682004-05-09 18:21:35 +000077 ret = ioctl (sock, SIOCGLIFNUM, &lifn);
ajs4460e7a2005-01-29 17:07:40 +000078 save_errno = errno;
paul88424682004-05-09 18:21:35 +000079
80 if (zserv_privs.change(ZPRIVS_LOWER))
81 zlog (NULL, LOG_ERR, "Can't lower privileges");
82
83 if (ret < 0)
84 {
85 zlog_warn ("interface_list_ioctl: SIOCGLIFNUM failed %s",
ajs4460e7a2005-01-29 17:07:40 +000086 safe_strerror (save_errno));
paul88424682004-05-09 18:21:35 +000087 close (sock);
88 return -1;
89 }
90 ifnum = lifn.lifn_count;
91
92 /*
93 * When calculating the buffer size needed, add a small number
94 * of interfaces to those we counted. We do this to capture
95 * the interface status of potential interfaces which may have
96 * been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF.
97 */
98 needed = (ifnum + 4) * sizeof (struct lifreq);
99 if (needed > lastneeded || needed < lastneeded / 2)
100 {
101 if (buf != NULL)
102 XFREE (MTYPE_TMP, buf);
103 if ((buf = XMALLOC (MTYPE_TMP, needed)) == NULL)
104 {
105 zlog_warn ("interface_list_ioctl: malloc failed");
106 close (sock);
107 return -1;
108 }
109 }
110 lastneeded = needed;
111
112 lifconf.lifc_family = af;
paul0752ef02005-11-03 12:35:21 +0000113 lifconf.lifc_flags = LIFC_NOXMIT;
paul88424682004-05-09 18:21:35 +0000114 lifconf.lifc_len = needed;
115 lifconf.lifc_buf = buf;
116
117 if (zserv_privs.change(ZPRIVS_RAISE))
118 zlog (NULL, LOG_ERR, "Can't raise privileges");
119
120 ret = ioctl (sock, SIOCGLIFCONF, &lifconf);
121
122 if (ret < 0)
123 {
124 if (errno == EINVAL)
125 goto calculate_lifc_len; /* deliberately hold privileges */
126
ajs6099b3b2004-11-20 02:06:59 +0000127 zlog_warn ("SIOCGLIFCONF: %s", safe_strerror (errno));
paul88424682004-05-09 18:21:35 +0000128
129 if (zserv_privs.change(ZPRIVS_LOWER))
130 zlog (NULL, LOG_ERR, "Can't lower privileges");
131
132 goto end;
133 }
134
135 if (zserv_privs.change(ZPRIVS_LOWER))
136 zlog (NULL, LOG_ERR, "Can't lower privileges");
137
138 /* Allocate interface. */
139 lifreq = lifconf.lifc_req;
140
141 for (n = 0; n < lifconf.lifc_len; n += sizeof (struct lifreq))
142 {
paul0752ef02005-11-03 12:35:21 +0000143 /* we treat Solaris logical interfaces as addresses, because that is
144 * how PF_ROUTE on Solaris treats them. Hence we can not directly use
145 * the lifreq_name to get the ifp. We need to normalise the name
146 * before attempting get.
147 *
148 * Solaris logical interface names are in the form of:
149 * <interface name>:<logical interface id>
150 */
151 unsigned int normallen = 0;
paul5c78b3d2006-01-25 04:31:40 +0000152 uint64_t lifflags;
paul0752ef02005-11-03 12:35:21 +0000153
paul5c78b3d2006-01-25 04:31:40 +0000154 /* We should exclude ~IFF_UP interfaces, as we'll find out about them
155 * coming up later through RTM_NEWADDR message on the route socket.
156 */
157 if (if_get_flags_direct (lifreq->lifr_name, &lifflags,
158 lifreq->lifr_addr.ss_family)
159 || !CHECK_FLAG (lifflags, IFF_UP))
160 {
161 lifreq++;
162 continue;
163 }
164
165 /* Find the normalised name */
paul0752ef02005-11-03 12:35:21 +0000166 while ( (normallen < sizeof(lifreq->lifr_name))
167 && ( *(lifreq->lifr_name + normallen) != '\0')
168 && ( *(lifreq->lifr_name + normallen) != ':') )
169 normallen++;
170
171 ifp = if_get_by_name_len(lifreq->lifr_name, normallen);
paul5b73a672004-07-23 15:26:14 +0000172
paul88424682004-05-09 18:21:35 +0000173 if (lifreq->lifr_addr.ss_family == AF_INET)
174 ifp->flags |= IFF_IPV4;
paul5b73a672004-07-23 15:26:14 +0000175
paul88424682004-05-09 18:21:35 +0000176 if (lifreq->lifr_addr.ss_family == AF_INET6)
paul5b73a672004-07-23 15:26:14 +0000177 {
178#ifdef HAVE_IPV6
179 ifp->flags |= IFF_IPV6;
180#else
181 lifreq++;
182 continue;
183#endif /* HAVE_IPV6 */
184 }
185
paul88424682004-05-09 18:21:35 +0000186 if_add_update (ifp);
paul5b73a672004-07-23 15:26:14 +0000187
paul88424682004-05-09 18:21:35 +0000188 interface_info_ioctl (ifp);
paul0752ef02005-11-03 12:35:21 +0000189
190 /* If a logical interface pass the full name so it can be
191 * as a label on the address
192 */
193 if ( *(lifreq->lifr_name + normallen) != '\0')
194 if_get_addr (ifp, (struct sockaddr *) &lifreq->lifr_addr,
195 lifreq->lifr_name);
196 else
197 if_get_addr (ifp, (struct sockaddr *) &lifreq->lifr_addr, NULL);
paul5c78b3d2006-01-25 04:31:40 +0000198
199 /* Poke the interface flags. Lets IFF_UP mangling kick in */
200 if_flags_update (ifp, ifp->flags);
201
paul88424682004-05-09 18:21:35 +0000202 lifreq++;
203 }
204
205end:
206 close (sock);
207 XFREE (MTYPE_TMP, lifconf.lifc_buf);
208 return ret;
209}
210
211/* Get interface's index by ioctl. */
212int
213if_get_index (struct interface *ifp)
214{
215 int ret;
216 struct lifreq lifreq;
217
Paul Jakmae8eb8342007-04-29 17:32:08 +0000218 lifreq_set_name (&lifreq, ifp->name);
paul88424682004-05-09 18:21:35 +0000219
paul88424682004-05-09 18:21:35 +0000220 if (ifp->flags & IFF_IPV4)
221 ret = AF_IOCTL (AF_INET, SIOCGLIFINDEX, (caddr_t) & lifreq);
222 else if (ifp->flags & IFF_IPV6)
223 ret = AF_IOCTL (AF_INET6, SIOCGLIFINDEX, (caddr_t) & lifreq);
224 else
225 ret = -1;
226
paul88424682004-05-09 18:21:35 +0000227 if (ret < 0)
228 {
229 zlog_warn ("SIOCGLIFINDEX(%s) failed", ifp->name);
230 return ret;
231 }
232
233 /* OK we got interface index. */
234#ifdef ifr_ifindex
235 ifp->ifindex = lifreq.lifr_ifindex;
236#else
237 ifp->ifindex = lifreq.lifr_index;
238#endif
239 return ifp->ifindex;
240
241}
242
243
244/* Interface address lookup by ioctl. This function only looks up
245 IPv4 address. */
246#define ADDRLEN(sa) (((sa)->sa_family == AF_INET ? \
247 sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6)))
248
249#define SIN(s) ((struct sockaddr_in *)(s))
250#define SIN6(s) ((struct sockaddr_in6 *)(s))
251
paul0752ef02005-11-03 12:35:21 +0000252/* Retrieve address information for the given ifp */
paul88424682004-05-09 18:21:35 +0000253static int
paul0752ef02005-11-03 12:35:21 +0000254if_get_addr (struct interface *ifp, struct sockaddr *addr, const char *label)
paul88424682004-05-09 18:21:35 +0000255{
256 int ret;
257 struct lifreq lifreq;
258 struct sockaddr_storage mask, dest;
259 char *dest_pnt = NULL;
260 u_char prefixlen = 0;
261 afi_t af;
Andrew J. Schorre4529632006-12-12 19:18:21 +0000262 int flags = 0;
paul88424682004-05-09 18:21:35 +0000263
paul0752ef02005-11-03 12:35:21 +0000264 /* Interface's name and address family.
265 * We need to use the logical interface name / label, if we've been
266 * given one, in order to get the right address
267 */
paul11967e92005-11-03 13:09:10 +0000268 strncpy (lifreq.lifr_name, (label ? label : ifp->name), IFNAMSIZ);
paul88424682004-05-09 18:21:35 +0000269
270 /* Interface's address. */
271 memcpy (&lifreq.lifr_addr, addr, ADDRLEN (addr));
272 af = addr->sa_family;
273
274 /* Point to point or broad cast address pointer init. */
275 dest_pnt = NULL;
276
Andrew J. Schorre4529632006-12-12 19:18:21 +0000277 if (AF_IOCTL (af, SIOCGLIFDSTADDR, (caddr_t) & lifreq) >= 0)
paul88424682004-05-09 18:21:35 +0000278 {
paul88424682004-05-09 18:21:35 +0000279 memcpy (&dest, &lifreq.lifr_dstaddr, ADDRLEN (addr));
280 if (af == AF_INET)
281 dest_pnt = (char *) &(SIN (&dest)->sin_addr);
282 else
283 dest_pnt = (char *) &(SIN6 (&dest)->sin6_addr);
Andrew J. Schorre4529632006-12-12 19:18:21 +0000284 flags = ZEBRA_IFA_PEER;
paul88424682004-05-09 18:21:35 +0000285 }
286
287 if (af == AF_INET)
288 {
289 ret = if_ioctl (SIOCGLIFNETMASK, (caddr_t) & lifreq);
290
291 if (ret < 0)
292 {
293 if (errno != EADDRNOTAVAIL)
294 {
295 zlog_warn ("SIOCGLIFNETMASK (%s) fail: %s", ifp->name,
ajs6099b3b2004-11-20 02:06:59 +0000296 safe_strerror (errno));
paul88424682004-05-09 18:21:35 +0000297 return ret;
298 }
299 return 0;
300 }
301 memcpy (&mask, &lifreq.lifr_addr, ADDRLEN (addr));
302
303 prefixlen = ip_masklen (SIN (&mask)->sin_addr);
Andrew J. Schorre4529632006-12-12 19:18:21 +0000304 if (!dest_pnt && (if_ioctl (SIOCGLIFBRDADDR, (caddr_t) & lifreq) >= 0))
305 {
paul88424682004-05-09 18:21:35 +0000306 memcpy (&dest, &lifreq.lifr_broadaddr, sizeof (struct sockaddr_in));
307 dest_pnt = (char *) &SIN (&dest)->sin_addr;
308 }
309 }
paul5b73a672004-07-23 15:26:14 +0000310#ifdef HAVE_IPV6
311 else if (af == AF_INET6)
paul88424682004-05-09 18:21:35 +0000312 {
Andrew J. Schorrfb6724a2006-12-13 15:44:15 +0000313 if (if_ioctl_ipv6 (SIOCGLIFSUBNET, (caddr_t) & lifreq) < 0)
314 {
315 if (ifp->flags & IFF_POINTOPOINT)
316 prefixlen = IPV6_MAX_BITLEN;
317 else
318 zlog_warn ("SIOCGLIFSUBNET (%s) fail: %s",
319 ifp->name, safe_strerror (errno));
320 }
paul88424682004-05-09 18:21:35 +0000321 else
Andrew J. Schorrfb6724a2006-12-13 15:44:15 +0000322 {
323 prefixlen = lifreq.lifr_addrlen;
324 }
paul88424682004-05-09 18:21:35 +0000325 }
paul5b73a672004-07-23 15:26:14 +0000326#endif /* HAVE_IPV6 */
paul88424682004-05-09 18:21:35 +0000327
328 /* Set address to the interface. */
329 if (af == AF_INET)
Andrew J. Schorre4529632006-12-12 19:18:21 +0000330 connected_add_ipv4 (ifp, flags, &SIN (addr)->sin_addr, prefixlen,
paul0752ef02005-11-03 12:35:21 +0000331 (struct in_addr *) dest_pnt, label);
paul5b73a672004-07-23 15:26:14 +0000332#ifdef HAVE_IPV6
333 else if (af == AF_INET6)
Andrew J. Schorre4529632006-12-12 19:18:21 +0000334 connected_add_ipv6 (ifp, flags, &SIN6 (addr)->sin6_addr, prefixlen,
paul0752ef02005-11-03 12:35:21 +0000335 (struct in6_addr *) dest_pnt, label);
paul5b73a672004-07-23 15:26:14 +0000336#endif /* HAVE_IPV6 */
paul88424682004-05-09 18:21:35 +0000337
338 return 0;
339}
340
341/* Fetch interface information via ioctl(). */
342static void
343interface_info_ioctl (struct interface *ifp)
344{
345 if_get_index (ifp);
346 if_get_flags (ifp);
347 if_get_mtu (ifp);
348 if_get_metric (ifp);
349}
350
351/* Lookup all interface information. */
352void
Feng Lu758fb8f2014-07-03 18:23:09 +0800353interface_list (struct zebra_vrf *zvrf)
paul88424682004-05-09 18:21:35 +0000354{
Feng Lu758fb8f2014-07-03 18:23:09 +0800355 if (zvrf->vrf_id != VRF_DEFAULT)
356 {
357 zlog_warn ("interface_list: ignore VRF %u", zvrf->vrf_id);
358 return;
359 }
paul88424682004-05-09 18:21:35 +0000360 interface_list_ioctl (AF_INET);
361 interface_list_ioctl (AF_INET6);
paul0752ef02005-11-03 12:35:21 +0000362 interface_list_ioctl (AF_UNSPEC);
paul88424682004-05-09 18:21:35 +0000363}
364
365struct connected *
366if_lookup_linklocal (struct interface *ifp)
367{
paul5b73a672004-07-23 15:26:14 +0000368#ifdef HAVE_IPV6
paul0c0f9112004-09-24 08:24:42 +0000369 struct listnode *node;
paul88424682004-05-09 18:21:35 +0000370 struct connected *ifc;
371
372 if (ifp == NULL)
373 return NULL;
374
paulcf460ef2005-04-10 16:54:26 +0000375 for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc))
paul88424682004-05-09 18:21:35 +0000376 {
paul88424682004-05-09 18:21:35 +0000377 if ((ifc->address->family == AF_INET6) &&
378 (IN6_IS_ADDR_LINKLOCAL (&ifc->address->u.prefix6)))
379 return ifc;
380 }
paul5b73a672004-07-23 15:26:14 +0000381#endif /* HAVE_IPV6 */
382
paul88424682004-05-09 18:21:35 +0000383 return NULL;
384}