blob: 898c16109dc4bfeb0cf5ea0223b7f981db059a4d [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
36void lifreq_set_name (struct lifreq *, struct interface *);
paul0752ef02005-11-03 12:35:21 +000037static int if_get_addr (struct interface *, struct sockaddr *, const char *);
paul88424682004-05-09 18:21:35 +000038static void interface_info_ioctl (struct interface *);
paul48a46fa2004-05-11 10:55:22 +000039extern struct zebra_privs_t zserv_privs;
paul88424682004-05-09 18:21:35 +000040
41int
42interface_list_ioctl (int af)
43{
44 int ret;
45 int sock;
46#define IFNUM_BASE 32
47 struct lifnum lifn;
48 int ifnum;
49 struct lifreq *lifreq;
50 struct lifconf lifconf;
51 struct interface *ifp;
52 int n;
ajs4460e7a2005-01-29 17:07:40 +000053 int save_errno;
paul88424682004-05-09 18:21:35 +000054 size_t needed, lastneeded = 0;
55 char *buf = NULL;
56
57 if (zserv_privs.change(ZPRIVS_RAISE))
58 zlog (NULL, LOG_ERR, "Can't raise privileges");
59
60 sock = socket (af, SOCK_DGRAM, 0);
61 if (sock < 0)
62 {
63 zlog_warn ("Can't make %s socket stream: %s",
ajs6099b3b2004-11-20 02:06:59 +000064 (af == AF_INET ? "AF_INET" : "AF_INET6"), safe_strerror (errno));
paul88424682004-05-09 18:21:35 +000065
66 if (zserv_privs.change(ZPRIVS_LOWER))
67 zlog (NULL, LOG_ERR, "Can't lower privileges");
68
69 return -1;
70 }
71
72calculate_lifc_len: /* must hold privileges to enter here */
73 lifn.lifn_family = af;
paul0752ef02005-11-03 12:35:21 +000074 lifn.lifn_flags = LIFC_NOXMIT; /* we want NOXMIT interfaces too */
paul88424682004-05-09 18:21:35 +000075 ret = ioctl (sock, SIOCGLIFNUM, &lifn);
ajs4460e7a2005-01-29 17:07:40 +000076 save_errno = errno;
paul88424682004-05-09 18:21:35 +000077
78 if (zserv_privs.change(ZPRIVS_LOWER))
79 zlog (NULL, LOG_ERR, "Can't lower privileges");
80
81 if (ret < 0)
82 {
83 zlog_warn ("interface_list_ioctl: SIOCGLIFNUM failed %s",
ajs4460e7a2005-01-29 17:07:40 +000084 safe_strerror (save_errno));
paul88424682004-05-09 18:21:35 +000085 close (sock);
86 return -1;
87 }
88 ifnum = lifn.lifn_count;
89
90 /*
91 * When calculating the buffer size needed, add a small number
92 * of interfaces to those we counted. We do this to capture
93 * the interface status of potential interfaces which may have
94 * been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF.
95 */
96 needed = (ifnum + 4) * sizeof (struct lifreq);
97 if (needed > lastneeded || needed < lastneeded / 2)
98 {
99 if (buf != NULL)
100 XFREE (MTYPE_TMP, buf);
101 if ((buf = XMALLOC (MTYPE_TMP, needed)) == NULL)
102 {
103 zlog_warn ("interface_list_ioctl: malloc failed");
104 close (sock);
105 return -1;
106 }
107 }
108 lastneeded = needed;
109
110 lifconf.lifc_family = af;
paul0752ef02005-11-03 12:35:21 +0000111 lifconf.lifc_flags = LIFC_NOXMIT;
paul88424682004-05-09 18:21:35 +0000112 lifconf.lifc_len = needed;
113 lifconf.lifc_buf = buf;
114
115 if (zserv_privs.change(ZPRIVS_RAISE))
116 zlog (NULL, LOG_ERR, "Can't raise privileges");
117
118 ret = ioctl (sock, SIOCGLIFCONF, &lifconf);
119
120 if (ret < 0)
121 {
122 if (errno == EINVAL)
123 goto calculate_lifc_len; /* deliberately hold privileges */
124
ajs6099b3b2004-11-20 02:06:59 +0000125 zlog_warn ("SIOCGLIFCONF: %s", safe_strerror (errno));
paul88424682004-05-09 18:21:35 +0000126
127 if (zserv_privs.change(ZPRIVS_LOWER))
128 zlog (NULL, LOG_ERR, "Can't lower privileges");
129
130 goto end;
131 }
132
133 if (zserv_privs.change(ZPRIVS_LOWER))
134 zlog (NULL, LOG_ERR, "Can't lower privileges");
135
136 /* Allocate interface. */
137 lifreq = lifconf.lifc_req;
138
139 for (n = 0; n < lifconf.lifc_len; n += sizeof (struct lifreq))
140 {
paul0752ef02005-11-03 12:35:21 +0000141 /* we treat Solaris logical interfaces as addresses, because that is
142 * how PF_ROUTE on Solaris treats them. Hence we can not directly use
143 * the lifreq_name to get the ifp. We need to normalise the name
144 * before attempting get.
145 *
146 * Solaris logical interface names are in the form of:
147 * <interface name>:<logical interface id>
148 */
149 unsigned int normallen = 0;
150
151 while ( (normallen < sizeof(lifreq->lifr_name))
152 && ( *(lifreq->lifr_name + normallen) != '\0')
153 && ( *(lifreq->lifr_name + normallen) != ':') )
154 normallen++;
155
156 ifp = if_get_by_name_len(lifreq->lifr_name, normallen);
paul5b73a672004-07-23 15:26:14 +0000157
paul88424682004-05-09 18:21:35 +0000158 if (lifreq->lifr_addr.ss_family == AF_INET)
159 ifp->flags |= IFF_IPV4;
paul5b73a672004-07-23 15:26:14 +0000160
paul88424682004-05-09 18:21:35 +0000161 if (lifreq->lifr_addr.ss_family == AF_INET6)
paul5b73a672004-07-23 15:26:14 +0000162 {
163#ifdef HAVE_IPV6
164 ifp->flags |= IFF_IPV6;
165#else
166 lifreq++;
167 continue;
168#endif /* HAVE_IPV6 */
169 }
170
paul88424682004-05-09 18:21:35 +0000171 if_add_update (ifp);
paul5b73a672004-07-23 15:26:14 +0000172
paul88424682004-05-09 18:21:35 +0000173 interface_info_ioctl (ifp);
paul0752ef02005-11-03 12:35:21 +0000174
175 /* If a logical interface pass the full name so it can be
176 * as a label on the address
177 */
178 if ( *(lifreq->lifr_name + normallen) != '\0')
179 if_get_addr (ifp, (struct sockaddr *) &lifreq->lifr_addr,
180 lifreq->lifr_name);
181 else
182 if_get_addr (ifp, (struct sockaddr *) &lifreq->lifr_addr, NULL);
paul88424682004-05-09 18:21:35 +0000183 lifreq++;
184 }
185
186end:
187 close (sock);
188 XFREE (MTYPE_TMP, lifconf.lifc_buf);
189 return ret;
190}
191
192/* Get interface's index by ioctl. */
193int
194if_get_index (struct interface *ifp)
195{
196 int ret;
197 struct lifreq lifreq;
198
199 lifreq_set_name (&lifreq, ifp);
200
paul88424682004-05-09 18:21:35 +0000201 if (ifp->flags & IFF_IPV4)
202 ret = AF_IOCTL (AF_INET, SIOCGLIFINDEX, (caddr_t) & lifreq);
203 else if (ifp->flags & IFF_IPV6)
204 ret = AF_IOCTL (AF_INET6, SIOCGLIFINDEX, (caddr_t) & lifreq);
205 else
206 ret = -1;
207
paul88424682004-05-09 18:21:35 +0000208 if (ret < 0)
209 {
210 zlog_warn ("SIOCGLIFINDEX(%s) failed", ifp->name);
211 return ret;
212 }
213
214 /* OK we got interface index. */
215#ifdef ifr_ifindex
216 ifp->ifindex = lifreq.lifr_ifindex;
217#else
218 ifp->ifindex = lifreq.lifr_index;
219#endif
220 return ifp->ifindex;
221
222}
223
224
225/* Interface address lookup by ioctl. This function only looks up
226 IPv4 address. */
227#define ADDRLEN(sa) (((sa)->sa_family == AF_INET ? \
228 sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6)))
229
230#define SIN(s) ((struct sockaddr_in *)(s))
231#define SIN6(s) ((struct sockaddr_in6 *)(s))
232
paul0752ef02005-11-03 12:35:21 +0000233/* Retrieve address information for the given ifp */
paul88424682004-05-09 18:21:35 +0000234static int
paul0752ef02005-11-03 12:35:21 +0000235if_get_addr (struct interface *ifp, struct sockaddr *addr, const char *label)
paul88424682004-05-09 18:21:35 +0000236{
237 int ret;
238 struct lifreq lifreq;
239 struct sockaddr_storage mask, dest;
240 char *dest_pnt = NULL;
241 u_char prefixlen = 0;
242 afi_t af;
243
paul0752ef02005-11-03 12:35:21 +0000244 /* Interface's name and address family.
245 * We need to use the logical interface name / label, if we've been
246 * given one, in order to get the right address
247 */
248 strncpy (lifreq.lifr_name, (label : label ? ifp->name), IFNAMSIZ);
paul88424682004-05-09 18:21:35 +0000249
250 /* Interface's address. */
251 memcpy (&lifreq.lifr_addr, addr, ADDRLEN (addr));
252 af = addr->sa_family;
253
254 /* Point to point or broad cast address pointer init. */
255 dest_pnt = NULL;
256
257 if (ifp->flags & IFF_POINTOPOINT)
258 {
paul88424682004-05-09 18:21:35 +0000259 ret = AF_IOCTL (af, SIOCGLIFDSTADDR, (caddr_t) & lifreq);
paul88424682004-05-09 18:21:35 +0000260
261 if (ret < 0)
262 {
263 zlog_warn ("SIOCGLIFDSTADDR (%s) fail: %s",
ajs6099b3b2004-11-20 02:06:59 +0000264 ifp->name, safe_strerror (errno));
paul88424682004-05-09 18:21:35 +0000265 return ret;
266 }
267 memcpy (&dest, &lifreq.lifr_dstaddr, ADDRLEN (addr));
268 if (af == AF_INET)
269 dest_pnt = (char *) &(SIN (&dest)->sin_addr);
270 else
271 dest_pnt = (char *) &(SIN6 (&dest)->sin6_addr);
272 }
273
274 if (af == AF_INET)
275 {
276 ret = if_ioctl (SIOCGLIFNETMASK, (caddr_t) & lifreq);
277
278 if (ret < 0)
279 {
280 if (errno != EADDRNOTAVAIL)
281 {
282 zlog_warn ("SIOCGLIFNETMASK (%s) fail: %s", ifp->name,
ajs6099b3b2004-11-20 02:06:59 +0000283 safe_strerror (errno));
paul88424682004-05-09 18:21:35 +0000284 return ret;
285 }
286 return 0;
287 }
288 memcpy (&mask, &lifreq.lifr_addr, ADDRLEN (addr));
289
290 prefixlen = ip_masklen (SIN (&mask)->sin_addr);
291 if (ifp->flags & IFF_BROADCAST)
292 {
293 ret = if_ioctl (SIOCGLIFBRDADDR, (caddr_t) & lifreq);
294 if (ret < 0)
295 {
296 if (errno != EADDRNOTAVAIL)
297 {
298 zlog_warn ("SIOCGLIFBRDADDR (%s) fail: %s",
ajs6099b3b2004-11-20 02:06:59 +0000299 ifp->name, safe_strerror (errno));
paul88424682004-05-09 18:21:35 +0000300 return ret;
301 }
302 return 0;
303 }
304 memcpy (&dest, &lifreq.lifr_broadaddr, sizeof (struct sockaddr_in));
305 dest_pnt = (char *) &SIN (&dest)->sin_addr;
306 }
307 }
paul5b73a672004-07-23 15:26:14 +0000308#ifdef HAVE_IPV6
309 else if (af == AF_INET6)
paul88424682004-05-09 18:21:35 +0000310 {
311 if (ifp->flags & IFF_POINTOPOINT)
312 {
313 prefixlen = IPV6_MAX_BITLEN;
314 }
315 else
316 {
317 ret = if_ioctl_ipv6 (SIOCGLIFSUBNET, (caddr_t) & lifreq);
318 if (ret < 0)
319 {
320 zlog_warn ("SIOCGLIFSUBNET (%s) fail: %s",
ajs6099b3b2004-11-20 02:06:59 +0000321 ifp->name, safe_strerror (errno));
paul88424682004-05-09 18:21:35 +0000322 }
323 else
324 {
325 prefixlen = lifreq.lifr_addrlen;
326 }
327 }
328 }
paul5b73a672004-07-23 15:26:14 +0000329#endif /* HAVE_IPV6 */
paul88424682004-05-09 18:21:35 +0000330
331 /* Set address to the interface. */
332 if (af == AF_INET)
333 connected_add_ipv4 (ifp, 0, &SIN (addr)->sin_addr, prefixlen,
paul0752ef02005-11-03 12:35:21 +0000334 (struct in_addr *) dest_pnt, label);
paul5b73a672004-07-23 15:26:14 +0000335#ifdef HAVE_IPV6
336 else if (af == AF_INET6)
paul88424682004-05-09 18:21:35 +0000337 connected_add_ipv6 (ifp, &SIN6 (addr)->sin6_addr, prefixlen,
paul0752ef02005-11-03 12:35:21 +0000338 (struct in6_addr *) dest_pnt, label);
paul5b73a672004-07-23 15:26:14 +0000339#endif /* HAVE_IPV6 */
paul88424682004-05-09 18:21:35 +0000340
341 return 0;
342}
343
344/* Fetch interface information via ioctl(). */
345static void
346interface_info_ioctl (struct interface *ifp)
347{
348 if_get_index (ifp);
349 if_get_flags (ifp);
350 if_get_mtu (ifp);
351 if_get_metric (ifp);
352}
353
354/* Lookup all interface information. */
355void
356interface_list ()
357{
358 interface_list_ioctl (AF_INET);
359 interface_list_ioctl (AF_INET6);
paul0752ef02005-11-03 12:35:21 +0000360 interface_list_ioctl (AF_UNSPEC);
paul88424682004-05-09 18:21:35 +0000361}
362
363struct connected *
364if_lookup_linklocal (struct interface *ifp)
365{
paul5b73a672004-07-23 15:26:14 +0000366#ifdef HAVE_IPV6
paul0c0f9112004-09-24 08:24:42 +0000367 struct listnode *node;
paul88424682004-05-09 18:21:35 +0000368 struct connected *ifc;
369
370 if (ifp == NULL)
371 return NULL;
372
paulcf460ef2005-04-10 16:54:26 +0000373 for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc))
paul88424682004-05-09 18:21:35 +0000374 {
paul88424682004-05-09 18:21:35 +0000375 if ((ifc->address->family == AF_INET6) &&
376 (IN6_IS_ADDR_LINKLOCAL (&ifc->address->u.prefix6)))
377 return ifc;
378 }
paul5b73a672004-07-23 15:26:14 +0000379#endif /* HAVE_IPV6 */
380
paul88424682004-05-09 18:21:35 +0000381 return NULL;
382}