blob: 095bcab6394c80065d2b645ac8e8e0e6fa7f2e92 [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"
paul88424682004-05-09 18:21:35 +000033
34#include "zebra/interface.h"
35
paul5c78b3d2006-01-25 04:31:40 +000036void lifreq_set_name (struct lifreq *, const char *);
37int if_get_flags_direct (const char *, uint64_t *, unsigned int af);
paul0752ef02005-11-03 12:35:21 +000038static int if_get_addr (struct interface *, struct sockaddr *, const char *);
paul88424682004-05-09 18:21:35 +000039static void interface_info_ioctl (struct interface *);
paul48a46fa2004-05-11 10:55:22 +000040extern struct zebra_privs_t zserv_privs;
paul88424682004-05-09 18:21:35 +000041
42int
43interface_list_ioctl (int af)
44{
45 int ret;
46 int sock;
47#define IFNUM_BASE 32
48 struct lifnum lifn;
49 int ifnum;
50 struct lifreq *lifreq;
51 struct lifconf lifconf;
52 struct interface *ifp;
53 int n;
ajs4460e7a2005-01-29 17:07:40 +000054 int save_errno;
paul88424682004-05-09 18:21:35 +000055 size_t needed, lastneeded = 0;
56 char *buf = NULL;
57
58 if (zserv_privs.change(ZPRIVS_RAISE))
59 zlog (NULL, LOG_ERR, "Can't raise privileges");
60
61 sock = socket (af, SOCK_DGRAM, 0);
62 if (sock < 0)
63 {
64 zlog_warn ("Can't make %s socket stream: %s",
ajs6099b3b2004-11-20 02:06:59 +000065 (af == AF_INET ? "AF_INET" : "AF_INET6"), safe_strerror (errno));
paul88424682004-05-09 18:21:35 +000066
67 if (zserv_privs.change(ZPRIVS_LOWER))
68 zlog (NULL, LOG_ERR, "Can't lower privileges");
69
70 return -1;
71 }
72
73calculate_lifc_len: /* must hold privileges to enter here */
74 lifn.lifn_family = af;
paul0752ef02005-11-03 12:35:21 +000075 lifn.lifn_flags = LIFC_NOXMIT; /* we want NOXMIT interfaces too */
paul88424682004-05-09 18:21:35 +000076 ret = ioctl (sock, SIOCGLIFNUM, &lifn);
ajs4460e7a2005-01-29 17:07:40 +000077 save_errno = errno;
paul88424682004-05-09 18:21:35 +000078
79 if (zserv_privs.change(ZPRIVS_LOWER))
80 zlog (NULL, LOG_ERR, "Can't lower privileges");
81
82 if (ret < 0)
83 {
84 zlog_warn ("interface_list_ioctl: SIOCGLIFNUM failed %s",
ajs4460e7a2005-01-29 17:07:40 +000085 safe_strerror (save_errno));
paul88424682004-05-09 18:21:35 +000086 close (sock);
87 return -1;
88 }
89 ifnum = lifn.lifn_count;
90
91 /*
92 * When calculating the buffer size needed, add a small number
93 * of interfaces to those we counted. We do this to capture
94 * the interface status of potential interfaces which may have
95 * been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF.
96 */
97 needed = (ifnum + 4) * sizeof (struct lifreq);
98 if (needed > lastneeded || needed < lastneeded / 2)
99 {
100 if (buf != NULL)
101 XFREE (MTYPE_TMP, buf);
102 if ((buf = XMALLOC (MTYPE_TMP, needed)) == NULL)
103 {
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;
paul0752ef02005-11-03 12:35:21 +0000112 lifconf.lifc_flags = LIFC_NOXMIT;
paul88424682004-05-09 18:21:35 +0000113 lifconf.lifc_len = needed;
114 lifconf.lifc_buf = buf;
115
116 if (zserv_privs.change(ZPRIVS_RAISE))
117 zlog (NULL, LOG_ERR, "Can't raise privileges");
118
119 ret = ioctl (sock, SIOCGLIFCONF, &lifconf);
120
121 if (ret < 0)
122 {
123 if (errno == EINVAL)
124 goto calculate_lifc_len; /* deliberately hold privileges */
125
ajs6099b3b2004-11-20 02:06:59 +0000126 zlog_warn ("SIOCGLIFCONF: %s", safe_strerror (errno));
paul88424682004-05-09 18:21:35 +0000127
128 if (zserv_privs.change(ZPRIVS_LOWER))
129 zlog (NULL, LOG_ERR, "Can't lower privileges");
130
131 goto end;
132 }
133
134 if (zserv_privs.change(ZPRIVS_LOWER))
135 zlog (NULL, LOG_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 {
paul0752ef02005-11-03 12:35:21 +0000142 /* we treat Solaris logical interfaces as addresses, because that is
143 * how PF_ROUTE on Solaris treats them. Hence we can not directly use
144 * the lifreq_name to get the ifp. We need to normalise the name
145 * before attempting get.
146 *
147 * Solaris logical interface names are in the form of:
148 * <interface name>:<logical interface id>
149 */
150 unsigned int normallen = 0;
paul5c78b3d2006-01-25 04:31:40 +0000151 uint64_t lifflags;
paul0752ef02005-11-03 12:35:21 +0000152
paul5c78b3d2006-01-25 04:31:40 +0000153 /* We should exclude ~IFF_UP interfaces, as we'll find out about them
154 * coming up later through RTM_NEWADDR message on the route socket.
155 */
156 if (if_get_flags_direct (lifreq->lifr_name, &lifflags,
157 lifreq->lifr_addr.ss_family)
158 || !CHECK_FLAG (lifflags, IFF_UP))
159 {
160 lifreq++;
161 continue;
162 }
163
164 /* Find the normalised name */
paul0752ef02005-11-03 12:35:21 +0000165 while ( (normallen < sizeof(lifreq->lifr_name))
166 && ( *(lifreq->lifr_name + normallen) != '\0')
167 && ( *(lifreq->lifr_name + normallen) != ':') )
168 normallen++;
169
170 ifp = if_get_by_name_len(lifreq->lifr_name, normallen);
paul5b73a672004-07-23 15:26:14 +0000171
paul88424682004-05-09 18:21:35 +0000172 if (lifreq->lifr_addr.ss_family == AF_INET)
173 ifp->flags |= IFF_IPV4;
paul5b73a672004-07-23 15:26:14 +0000174
paul88424682004-05-09 18:21:35 +0000175 if (lifreq->lifr_addr.ss_family == AF_INET6)
paul5b73a672004-07-23 15:26:14 +0000176 {
177#ifdef HAVE_IPV6
178 ifp->flags |= IFF_IPV6;
179#else
180 lifreq++;
181 continue;
182#endif /* HAVE_IPV6 */
183 }
184
paul88424682004-05-09 18:21:35 +0000185 if_add_update (ifp);
paul5b73a672004-07-23 15:26:14 +0000186
paul88424682004-05-09 18:21:35 +0000187 interface_info_ioctl (ifp);
paul0752ef02005-11-03 12:35:21 +0000188
189 /* If a logical interface pass the full name so it can be
190 * as a label on the address
191 */
192 if ( *(lifreq->lifr_name + normallen) != '\0')
193 if_get_addr (ifp, (struct sockaddr *) &lifreq->lifr_addr,
194 lifreq->lifr_name);
195 else
196 if_get_addr (ifp, (struct sockaddr *) &lifreq->lifr_addr, NULL);
paul5c78b3d2006-01-25 04:31:40 +0000197
198 /* Poke the interface flags. Lets IFF_UP mangling kick in */
199 if_flags_update (ifp, ifp->flags);
200
paul88424682004-05-09 18:21:35 +0000201 lifreq++;
202 }
203
204end:
205 close (sock);
206 XFREE (MTYPE_TMP, lifconf.lifc_buf);
207 return ret;
208}
209
210/* Get interface's index by ioctl. */
211int
212if_get_index (struct interface *ifp)
213{
214 int ret;
215 struct lifreq lifreq;
216
217 lifreq_set_name (&lifreq, ifp);
218
paul88424682004-05-09 18:21:35 +0000219 if (ifp->flags & IFF_IPV4)
220 ret = AF_IOCTL (AF_INET, SIOCGLIFINDEX, (caddr_t) & lifreq);
221 else if (ifp->flags & IFF_IPV6)
222 ret = AF_IOCTL (AF_INET6, SIOCGLIFINDEX, (caddr_t) & lifreq);
223 else
224 ret = -1;
225
paul88424682004-05-09 18:21:35 +0000226 if (ret < 0)
227 {
228 zlog_warn ("SIOCGLIFINDEX(%s) failed", ifp->name);
229 return ret;
230 }
231
232 /* OK we got interface index. */
233#ifdef ifr_ifindex
234 ifp->ifindex = lifreq.lifr_ifindex;
235#else
236 ifp->ifindex = lifreq.lifr_index;
237#endif
238 return ifp->ifindex;
239
240}
241
242
243/* Interface address lookup by ioctl. This function only looks up
244 IPv4 address. */
245#define ADDRLEN(sa) (((sa)->sa_family == AF_INET ? \
246 sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6)))
247
248#define SIN(s) ((struct sockaddr_in *)(s))
249#define SIN6(s) ((struct sockaddr_in6 *)(s))
250
paul0752ef02005-11-03 12:35:21 +0000251/* Retrieve address information for the given ifp */
paul88424682004-05-09 18:21:35 +0000252static int
paul0752ef02005-11-03 12:35:21 +0000253if_get_addr (struct interface *ifp, struct sockaddr *addr, const char *label)
paul88424682004-05-09 18:21:35 +0000254{
255 int ret;
256 struct lifreq lifreq;
257 struct sockaddr_storage mask, dest;
258 char *dest_pnt = NULL;
259 u_char prefixlen = 0;
260 afi_t af;
261
paul0752ef02005-11-03 12:35:21 +0000262 /* Interface's name and address family.
263 * We need to use the logical interface name / label, if we've been
264 * given one, in order to get the right address
265 */
paul11967e92005-11-03 13:09:10 +0000266 strncpy (lifreq.lifr_name, (label ? label : ifp->name), IFNAMSIZ);
paul88424682004-05-09 18:21:35 +0000267
268 /* Interface's address. */
269 memcpy (&lifreq.lifr_addr, addr, ADDRLEN (addr));
270 af = addr->sa_family;
271
272 /* Point to point or broad cast address pointer init. */
273 dest_pnt = NULL;
274
275 if (ifp->flags & IFF_POINTOPOINT)
276 {
paul88424682004-05-09 18:21:35 +0000277 ret = AF_IOCTL (af, SIOCGLIFDSTADDR, (caddr_t) & lifreq);
paul88424682004-05-09 18:21:35 +0000278
279 if (ret < 0)
280 {
281 zlog_warn ("SIOCGLIFDSTADDR (%s) fail: %s",
ajs6099b3b2004-11-20 02:06:59 +0000282 ifp->name, safe_strerror (errno));
paul88424682004-05-09 18:21:35 +0000283 return ret;
284 }
285 memcpy (&dest, &lifreq.lifr_dstaddr, ADDRLEN (addr));
286 if (af == AF_INET)
287 dest_pnt = (char *) &(SIN (&dest)->sin_addr);
288 else
289 dest_pnt = (char *) &(SIN6 (&dest)->sin6_addr);
290 }
291
292 if (af == AF_INET)
293 {
294 ret = if_ioctl (SIOCGLIFNETMASK, (caddr_t) & lifreq);
295
296 if (ret < 0)
297 {
298 if (errno != EADDRNOTAVAIL)
299 {
300 zlog_warn ("SIOCGLIFNETMASK (%s) fail: %s", ifp->name,
ajs6099b3b2004-11-20 02:06:59 +0000301 safe_strerror (errno));
paul88424682004-05-09 18:21:35 +0000302 return ret;
303 }
304 return 0;
305 }
306 memcpy (&mask, &lifreq.lifr_addr, ADDRLEN (addr));
307
308 prefixlen = ip_masklen (SIN (&mask)->sin_addr);
309 if (ifp->flags & IFF_BROADCAST)
310 {
311 ret = if_ioctl (SIOCGLIFBRDADDR, (caddr_t) & lifreq);
312 if (ret < 0)
313 {
314 if (errno != EADDRNOTAVAIL)
315 {
316 zlog_warn ("SIOCGLIFBRDADDR (%s) fail: %s",
ajs6099b3b2004-11-20 02:06:59 +0000317 ifp->name, safe_strerror (errno));
paul88424682004-05-09 18:21:35 +0000318 return ret;
319 }
320 return 0;
321 }
322 memcpy (&dest, &lifreq.lifr_broadaddr, sizeof (struct sockaddr_in));
323 dest_pnt = (char *) &SIN (&dest)->sin_addr;
324 }
325 }
paul5b73a672004-07-23 15:26:14 +0000326#ifdef HAVE_IPV6
327 else if (af == AF_INET6)
paul88424682004-05-09 18:21:35 +0000328 {
329 if (ifp->flags & IFF_POINTOPOINT)
330 {
331 prefixlen = IPV6_MAX_BITLEN;
332 }
333 else
334 {
335 ret = if_ioctl_ipv6 (SIOCGLIFSUBNET, (caddr_t) & lifreq);
336 if (ret < 0)
337 {
338 zlog_warn ("SIOCGLIFSUBNET (%s) fail: %s",
ajs6099b3b2004-11-20 02:06:59 +0000339 ifp->name, safe_strerror (errno));
paul88424682004-05-09 18:21:35 +0000340 }
341 else
342 {
343 prefixlen = lifreq.lifr_addrlen;
344 }
345 }
346 }
paul5b73a672004-07-23 15:26:14 +0000347#endif /* HAVE_IPV6 */
paul88424682004-05-09 18:21:35 +0000348
349 /* Set address to the interface. */
350 if (af == AF_INET)
351 connected_add_ipv4 (ifp, 0, &SIN (addr)->sin_addr, prefixlen,
paul0752ef02005-11-03 12:35:21 +0000352 (struct in_addr *) dest_pnt, label);
paul5b73a672004-07-23 15:26:14 +0000353#ifdef HAVE_IPV6
354 else if (af == AF_INET6)
paul88424682004-05-09 18:21:35 +0000355 connected_add_ipv6 (ifp, &SIN6 (addr)->sin6_addr, prefixlen,
paul0752ef02005-11-03 12:35:21 +0000356 (struct in6_addr *) dest_pnt, label);
paul5b73a672004-07-23 15:26:14 +0000357#endif /* HAVE_IPV6 */
paul88424682004-05-09 18:21:35 +0000358
359 return 0;
360}
361
362/* Fetch interface information via ioctl(). */
363static void
364interface_info_ioctl (struct interface *ifp)
365{
366 if_get_index (ifp);
367 if_get_flags (ifp);
368 if_get_mtu (ifp);
369 if_get_metric (ifp);
370}
371
372/* Lookup all interface information. */
373void
374interface_list ()
375{
376 interface_list_ioctl (AF_INET);
377 interface_list_ioctl (AF_INET6);
paul0752ef02005-11-03 12:35:21 +0000378 interface_list_ioctl (AF_UNSPEC);
paul88424682004-05-09 18:21:35 +0000379}
380
381struct connected *
382if_lookup_linklocal (struct interface *ifp)
383{
paul5b73a672004-07-23 15:26:14 +0000384#ifdef HAVE_IPV6
paul0c0f9112004-09-24 08:24:42 +0000385 struct listnode *node;
paul88424682004-05-09 18:21:35 +0000386 struct connected *ifc;
387
388 if (ifp == NULL)
389 return NULL;
390
paulcf460ef2005-04-10 16:54:26 +0000391 for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc))
paul88424682004-05-09 18:21:35 +0000392 {
paul88424682004-05-09 18:21:35 +0000393 if ((ifc->address->family == AF_INET6) &&
394 (IN6_IS_ADDR_LINKLOCAL (&ifc->address->u.prefix6)))
395 return ifc;
396 }
paul5b73a672004-07-23 15:26:14 +0000397#endif /* HAVE_IPV6 */
398
paul88424682004-05-09 18:21:35 +0000399 return NULL;
400}