blob: 1cbf555eb5c54a459930bae60221c742efd3d0d8 [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 *);
37static int if_get_addr (struct interface *, struct sockaddr *);
38static 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;
53 size_t needed, lastneeded = 0;
54 char *buf = NULL;
55
56 if (zserv_privs.change(ZPRIVS_RAISE))
57 zlog (NULL, LOG_ERR, "Can't raise privileges");
58
59 sock = socket (af, SOCK_DGRAM, 0);
60 if (sock < 0)
61 {
62 zlog_warn ("Can't make %s socket stream: %s",
ajs6099b3b2004-11-20 02:06:59 +000063 (af == AF_INET ? "AF_INET" : "AF_INET6"), safe_strerror (errno));
paul88424682004-05-09 18:21:35 +000064
65 if (zserv_privs.change(ZPRIVS_LOWER))
66 zlog (NULL, LOG_ERR, "Can't lower privileges");
67
68 return -1;
69 }
70
71calculate_lifc_len: /* must hold privileges to enter here */
72 lifn.lifn_family = af;
73 lifn.lifn_flags = 0;
74 ret = ioctl (sock, SIOCGLIFNUM, &lifn);
75
76 if (zserv_privs.change(ZPRIVS_LOWER))
77 zlog (NULL, LOG_ERR, "Can't lower privileges");
78
79 if (ret < 0)
80 {
81 zlog_warn ("interface_list_ioctl: SIOCGLIFNUM failed %s",
ajs6099b3b2004-11-20 02:06:59 +000082 safe_strerror (errno));
paul88424682004-05-09 18:21:35 +000083 close (sock);
84 return -1;
85 }
86 ifnum = lifn.lifn_count;
87
88 /*
89 * When calculating the buffer size needed, add a small number
90 * of interfaces to those we counted. We do this to capture
91 * the interface status of potential interfaces which may have
92 * been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF.
93 */
94 needed = (ifnum + 4) * sizeof (struct lifreq);
95 if (needed > lastneeded || needed < lastneeded / 2)
96 {
97 if (buf != NULL)
98 XFREE (MTYPE_TMP, buf);
99 if ((buf = XMALLOC (MTYPE_TMP, needed)) == NULL)
100 {
101 zlog_warn ("interface_list_ioctl: malloc failed");
102 close (sock);
103 return -1;
104 }
105 }
106 lastneeded = needed;
107
108 lifconf.lifc_family = af;
109 lifconf.lifc_flags = 0;
110 lifconf.lifc_len = needed;
111 lifconf.lifc_buf = buf;
112
113 if (zserv_privs.change(ZPRIVS_RAISE))
114 zlog (NULL, LOG_ERR, "Can't raise privileges");
115
116 ret = ioctl (sock, SIOCGLIFCONF, &lifconf);
117
118 if (ret < 0)
119 {
120 if (errno == EINVAL)
121 goto calculate_lifc_len; /* deliberately hold privileges */
122
ajs6099b3b2004-11-20 02:06:59 +0000123 zlog_warn ("SIOCGLIFCONF: %s", safe_strerror (errno));
paul88424682004-05-09 18:21:35 +0000124
125 if (zserv_privs.change(ZPRIVS_LOWER))
126 zlog (NULL, LOG_ERR, "Can't lower privileges");
127
128 goto end;
129 }
130
131 if (zserv_privs.change(ZPRIVS_LOWER))
132 zlog (NULL, LOG_ERR, "Can't lower privileges");
133
134 /* Allocate interface. */
135 lifreq = lifconf.lifc_req;
136
137 for (n = 0; n < lifconf.lifc_len; n += sizeof (struct lifreq))
138 {
139 ifp = if_get_by_name (lifreq->lifr_name);
paul5b73a672004-07-23 15:26:14 +0000140
paul88424682004-05-09 18:21:35 +0000141 if (lifreq->lifr_addr.ss_family == AF_INET)
142 ifp->flags |= IFF_IPV4;
paul5b73a672004-07-23 15:26:14 +0000143
paul88424682004-05-09 18:21:35 +0000144 if (lifreq->lifr_addr.ss_family == AF_INET6)
paul5b73a672004-07-23 15:26:14 +0000145 {
146#ifdef HAVE_IPV6
147 ifp->flags |= IFF_IPV6;
148#else
149 lifreq++;
150 continue;
151#endif /* HAVE_IPV6 */
152 }
153
paul88424682004-05-09 18:21:35 +0000154 if_add_update (ifp);
paul5b73a672004-07-23 15:26:14 +0000155
paul88424682004-05-09 18:21:35 +0000156 interface_info_ioctl (ifp);
157 if_get_addr (ifp, (struct sockaddr *) &lifreq->lifr_addr);
158 lifreq++;
159 }
160
161end:
162 close (sock);
163 XFREE (MTYPE_TMP, lifconf.lifc_buf);
164 return ret;
165}
166
167/* Get interface's index by ioctl. */
168int
169if_get_index (struct interface *ifp)
170{
171 int ret;
172 struct lifreq lifreq;
173
174 lifreq_set_name (&lifreq, ifp);
175
paul88424682004-05-09 18:21:35 +0000176 if (ifp->flags & IFF_IPV4)
177 ret = AF_IOCTL (AF_INET, SIOCGLIFINDEX, (caddr_t) & lifreq);
178 else if (ifp->flags & IFF_IPV6)
179 ret = AF_IOCTL (AF_INET6, SIOCGLIFINDEX, (caddr_t) & lifreq);
180 else
181 ret = -1;
182
paul88424682004-05-09 18:21:35 +0000183 if (ret < 0)
184 {
185 zlog_warn ("SIOCGLIFINDEX(%s) failed", ifp->name);
186 return ret;
187 }
188
189 /* OK we got interface index. */
190#ifdef ifr_ifindex
191 ifp->ifindex = lifreq.lifr_ifindex;
192#else
193 ifp->ifindex = lifreq.lifr_index;
194#endif
195 return ifp->ifindex;
196
197}
198
199
200/* Interface address lookup by ioctl. This function only looks up
201 IPv4 address. */
202#define ADDRLEN(sa) (((sa)->sa_family == AF_INET ? \
203 sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6)))
204
205#define SIN(s) ((struct sockaddr_in *)(s))
206#define SIN6(s) ((struct sockaddr_in6 *)(s))
207
208static int
209if_get_addr (struct interface *ifp, struct sockaddr *addr)
210{
211 int ret;
212 struct lifreq lifreq;
213 struct sockaddr_storage mask, dest;
214 char *dest_pnt = NULL;
215 u_char prefixlen = 0;
216 afi_t af;
217
218 /* Interface's name and address family. */
219 strncpy (lifreq.lifr_name, ifp->name, IFNAMSIZ);
220
221 /* Interface's address. */
222 memcpy (&lifreq.lifr_addr, addr, ADDRLEN (addr));
223 af = addr->sa_family;
224
225 /* Point to point or broad cast address pointer init. */
226 dest_pnt = NULL;
227
228 if (ifp->flags & IFF_POINTOPOINT)
229 {
paul88424682004-05-09 18:21:35 +0000230 ret = AF_IOCTL (af, SIOCGLIFDSTADDR, (caddr_t) & lifreq);
paul88424682004-05-09 18:21:35 +0000231
232 if (ret < 0)
233 {
234 zlog_warn ("SIOCGLIFDSTADDR (%s) fail: %s",
ajs6099b3b2004-11-20 02:06:59 +0000235 ifp->name, safe_strerror (errno));
paul88424682004-05-09 18:21:35 +0000236 return ret;
237 }
238 memcpy (&dest, &lifreq.lifr_dstaddr, ADDRLEN (addr));
239 if (af == AF_INET)
240 dest_pnt = (char *) &(SIN (&dest)->sin_addr);
241 else
242 dest_pnt = (char *) &(SIN6 (&dest)->sin6_addr);
243 }
244
245 if (af == AF_INET)
246 {
247 ret = if_ioctl (SIOCGLIFNETMASK, (caddr_t) & lifreq);
248
249 if (ret < 0)
250 {
251 if (errno != EADDRNOTAVAIL)
252 {
253 zlog_warn ("SIOCGLIFNETMASK (%s) fail: %s", ifp->name,
ajs6099b3b2004-11-20 02:06:59 +0000254 safe_strerror (errno));
paul88424682004-05-09 18:21:35 +0000255 return ret;
256 }
257 return 0;
258 }
259 memcpy (&mask, &lifreq.lifr_addr, ADDRLEN (addr));
260
261 prefixlen = ip_masklen (SIN (&mask)->sin_addr);
262 if (ifp->flags & IFF_BROADCAST)
263 {
264 ret = if_ioctl (SIOCGLIFBRDADDR, (caddr_t) & lifreq);
265 if (ret < 0)
266 {
267 if (errno != EADDRNOTAVAIL)
268 {
269 zlog_warn ("SIOCGLIFBRDADDR (%s) fail: %s",
ajs6099b3b2004-11-20 02:06:59 +0000270 ifp->name, safe_strerror (errno));
paul88424682004-05-09 18:21:35 +0000271 return ret;
272 }
273 return 0;
274 }
275 memcpy (&dest, &lifreq.lifr_broadaddr, sizeof (struct sockaddr_in));
276 dest_pnt = (char *) &SIN (&dest)->sin_addr;
277 }
278 }
paul5b73a672004-07-23 15:26:14 +0000279#ifdef HAVE_IPV6
280 else if (af == AF_INET6)
paul88424682004-05-09 18:21:35 +0000281 {
282 if (ifp->flags & IFF_POINTOPOINT)
283 {
284 prefixlen = IPV6_MAX_BITLEN;
285 }
286 else
287 {
288 ret = if_ioctl_ipv6 (SIOCGLIFSUBNET, (caddr_t) & lifreq);
289 if (ret < 0)
290 {
291 zlog_warn ("SIOCGLIFSUBNET (%s) fail: %s",
ajs6099b3b2004-11-20 02:06:59 +0000292 ifp->name, safe_strerror (errno));
paul88424682004-05-09 18:21:35 +0000293 }
294 else
295 {
296 prefixlen = lifreq.lifr_addrlen;
297 }
298 }
299 }
paul5b73a672004-07-23 15:26:14 +0000300#endif /* HAVE_IPV6 */
paul88424682004-05-09 18:21:35 +0000301
302 /* Set address to the interface. */
303 if (af == AF_INET)
304 connected_add_ipv4 (ifp, 0, &SIN (addr)->sin_addr, prefixlen,
305 (struct in_addr *) dest_pnt, NULL);
paul5b73a672004-07-23 15:26:14 +0000306#ifdef HAVE_IPV6
307 else if (af == AF_INET6)
paul88424682004-05-09 18:21:35 +0000308 connected_add_ipv6 (ifp, &SIN6 (addr)->sin6_addr, prefixlen,
309 (struct in6_addr *) dest_pnt);
paul5b73a672004-07-23 15:26:14 +0000310#endif /* HAVE_IPV6 */
paul88424682004-05-09 18:21:35 +0000311
312 return 0;
313}
314
315/* Fetch interface information via ioctl(). */
316static void
317interface_info_ioctl (struct interface *ifp)
318{
319 if_get_index (ifp);
320 if_get_flags (ifp);
321 if_get_mtu (ifp);
322 if_get_metric (ifp);
323}
324
325/* Lookup all interface information. */
326void
327interface_list ()
328{
329 interface_list_ioctl (AF_INET);
330 interface_list_ioctl (AF_INET6);
331}
332
333struct connected *
334if_lookup_linklocal (struct interface *ifp)
335{
paul5b73a672004-07-23 15:26:14 +0000336#ifdef HAVE_IPV6
paul0c0f9112004-09-24 08:24:42 +0000337 struct listnode *node;
paul88424682004-05-09 18:21:35 +0000338 struct connected *ifc;
339
340 if (ifp == NULL)
341 return NULL;
342
paul0c0f9112004-09-24 08:24:42 +0000343 LIST_LOOP (ifp->connected, ifc, node)
paul88424682004-05-09 18:21:35 +0000344 {
paul88424682004-05-09 18:21:35 +0000345 if ((ifc->address->family == AF_INET6) &&
346 (IN6_IS_ADDR_LINKLOCAL (&ifc->address->u.prefix6)))
347 return ifc;
348 }
paul5b73a672004-07-23 15:26:14 +0000349#endif /* HAVE_IPV6 */
350
paul88424682004-05-09 18:21:35 +0000351 return NULL;
352}