blob: b07580c22fc901e0a85adca51681095c0d1842c2 [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"
32
33#include "zebra/interface.h"
34
35void lifreq_set_name (struct lifreq *, struct interface *);
36static int if_get_addr (struct interface *, struct sockaddr *);
37static void interface_info_ioctl (struct interface *);
38
39
40int
41interface_list_ioctl (int af)
42{
43 int ret;
44 int sock;
45#define IFNUM_BASE 32
46 struct lifnum lifn;
47 int ifnum;
48 struct lifreq *lifreq;
49 struct lifconf lifconf;
50 struct interface *ifp;
51 int n;
52 size_t needed, lastneeded = 0;
53 char *buf = NULL;
54
55 if (zserv_privs.change(ZPRIVS_RAISE))
56 zlog (NULL, LOG_ERR, "Can't raise privileges");
57
58 sock = socket (af, SOCK_DGRAM, 0);
59 if (sock < 0)
60 {
61 zlog_warn ("Can't make %s socket stream: %s",
62 (af == AF_INET ? "AF_INET" : "AF_INET6"), strerror (errno));
63
64 if (zserv_privs.change(ZPRIVS_LOWER))
65 zlog (NULL, LOG_ERR, "Can't lower privileges");
66
67 return -1;
68 }
69
70calculate_lifc_len: /* must hold privileges to enter here */
71 lifn.lifn_family = af;
72 lifn.lifn_flags = 0;
73 ret = ioctl (sock, SIOCGLIFNUM, &lifn);
74
75 if (zserv_privs.change(ZPRIVS_LOWER))
76 zlog (NULL, LOG_ERR, "Can't lower privileges");
77
78 if (ret < 0)
79 {
80 zlog_warn ("interface_list_ioctl: SIOCGLIFNUM failed %s",
81 strerror (errno));
82 close (sock);
83 return -1;
84 }
85 ifnum = lifn.lifn_count;
86
87 /*
88 * When calculating the buffer size needed, add a small number
89 * of interfaces to those we counted. We do this to capture
90 * the interface status of potential interfaces which may have
91 * been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF.
92 */
93 needed = (ifnum + 4) * sizeof (struct lifreq);
94 if (needed > lastneeded || needed < lastneeded / 2)
95 {
96 if (buf != NULL)
97 XFREE (MTYPE_TMP, buf);
98 if ((buf = XMALLOC (MTYPE_TMP, needed)) == NULL)
99 {
100 zlog_warn ("interface_list_ioctl: malloc failed");
101 close (sock);
102 return -1;
103 }
104 }
105 lastneeded = needed;
106
107 lifconf.lifc_family = af;
108 lifconf.lifc_flags = 0;
109 lifconf.lifc_len = needed;
110 lifconf.lifc_buf = buf;
111
112 if (zserv_privs.change(ZPRIVS_RAISE))
113 zlog (NULL, LOG_ERR, "Can't raise privileges");
114
115 ret = ioctl (sock, SIOCGLIFCONF, &lifconf);
116
117 if (ret < 0)
118 {
119 if (errno == EINVAL)
120 goto calculate_lifc_len; /* deliberately hold privileges */
121
122 zlog_warn ("SIOCGLIFCONF: %s", strerror (errno));
123
124 if (zserv_privs.change(ZPRIVS_LOWER))
125 zlog (NULL, LOG_ERR, "Can't lower privileges");
126
127 goto end;
128 }
129
130 if (zserv_privs.change(ZPRIVS_LOWER))
131 zlog (NULL, LOG_ERR, "Can't lower privileges");
132
133 /* Allocate interface. */
134 lifreq = lifconf.lifc_req;
135
136 for (n = 0; n < lifconf.lifc_len; n += sizeof (struct lifreq))
137 {
138 ifp = if_get_by_name (lifreq->lifr_name);
139 if (lifreq->lifr_addr.ss_family == AF_INET)
140 ifp->flags |= IFF_IPV4;
141 if (lifreq->lifr_addr.ss_family == AF_INET6)
142 ifp->flags |= IFF_IPV6;
143 if_add_update (ifp);
144 interface_info_ioctl (ifp);
145 if_get_addr (ifp, (struct sockaddr *) &lifreq->lifr_addr);
146 lifreq++;
147 }
148
149end:
150 close (sock);
151 XFREE (MTYPE_TMP, lifconf.lifc_buf);
152 return ret;
153}
154
155/* Get interface's index by ioctl. */
156int
157if_get_index (struct interface *ifp)
158{
159 int ret;
160 struct lifreq lifreq;
161
162 lifreq_set_name (&lifreq, ifp);
163
164 if (zserv_privs.change(ZPRIVS_RAISE))
165 zlog (NULL, LOG_ERR, "Can't raise privileges");
166
167 if (ifp->flags & IFF_IPV4)
168 ret = AF_IOCTL (AF_INET, SIOCGLIFINDEX, (caddr_t) & lifreq);
169 else if (ifp->flags & IFF_IPV6)
170 ret = AF_IOCTL (AF_INET6, SIOCGLIFINDEX, (caddr_t) & lifreq);
171 else
172 ret = -1;
173
174 if (zserv_privs.change(ZPRIVS_LOWER))
175 zlog (NULL, LOG_ERR, "Can't lower privileges");
176
177 if (ret < 0)
178 {
179 zlog_warn ("SIOCGLIFINDEX(%s) failed", ifp->name);
180 return ret;
181 }
182
183 /* OK we got interface index. */
184#ifdef ifr_ifindex
185 ifp->ifindex = lifreq.lifr_ifindex;
186#else
187 ifp->ifindex = lifreq.lifr_index;
188#endif
189 return ifp->ifindex;
190
191}
192
193
194/* Interface address lookup by ioctl. This function only looks up
195 IPv4 address. */
196#define ADDRLEN(sa) (((sa)->sa_family == AF_INET ? \
197 sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6)))
198
199#define SIN(s) ((struct sockaddr_in *)(s))
200#define SIN6(s) ((struct sockaddr_in6 *)(s))
201
202static int
203if_get_addr (struct interface *ifp, struct sockaddr *addr)
204{
205 int ret;
206 struct lifreq lifreq;
207 struct sockaddr_storage mask, dest;
208 char *dest_pnt = NULL;
209 u_char prefixlen = 0;
210 afi_t af;
211
212 /* Interface's name and address family. */
213 strncpy (lifreq.lifr_name, ifp->name, IFNAMSIZ);
214
215 /* Interface's address. */
216 memcpy (&lifreq.lifr_addr, addr, ADDRLEN (addr));
217 af = addr->sa_family;
218
219 /* Point to point or broad cast address pointer init. */
220 dest_pnt = NULL;
221
222 if (ifp->flags & IFF_POINTOPOINT)
223 {
224 if (zserv_privs.change(ZPRIVS_RAISE))
225 zlog (NULL, LOG_ERR, "Can't raise privileges");
226
227 ret = AF_IOCTL (af, SIOCGLIFDSTADDR, (caddr_t) & lifreq);
228
229 if (zserv_privs.change(ZPRIVS_LOWER))
230 zlog (NULL, LOG_ERR, "Can't lower privileges");
231
232 if (ret < 0)
233 {
234 zlog_warn ("SIOCGLIFDSTADDR (%s) fail: %s",
235 ifp->name, strerror (errno));
236 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,
254 strerror (errno));
255 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",
270 ifp->name, strerror (errno));
271 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 }
279 else
280 {
281 if (ifp->flags & IFF_POINTOPOINT)
282 {
283 prefixlen = IPV6_MAX_BITLEN;
284 }
285 else
286 {
287 ret = if_ioctl_ipv6 (SIOCGLIFSUBNET, (caddr_t) & lifreq);
288 if (ret < 0)
289 {
290 zlog_warn ("SIOCGLIFSUBNET (%s) fail: %s",
291 ifp->name, strerror (errno));
292 }
293 else
294 {
295 prefixlen = lifreq.lifr_addrlen;
296 }
297 }
298 }
299
300
301 /* Set address to the interface. */
302 if (af == AF_INET)
303 connected_add_ipv4 (ifp, 0, &SIN (addr)->sin_addr, prefixlen,
304 (struct in_addr *) dest_pnt, NULL);
305 else
306 connected_add_ipv6 (ifp, &SIN6 (addr)->sin6_addr, prefixlen,
307 (struct in6_addr *) dest_pnt);
308
309 return 0;
310}
311
312/* Fetch interface information via ioctl(). */
313static void
314interface_info_ioctl (struct interface *ifp)
315{
316 if_get_index (ifp);
317 if_get_flags (ifp);
318 if_get_mtu (ifp);
319 if_get_metric (ifp);
320}
321
322/* Lookup all interface information. */
323void
324interface_list ()
325{
326 interface_list_ioctl (AF_INET);
327 interface_list_ioctl (AF_INET6);
328}
329
330struct connected *
331if_lookup_linklocal (struct interface *ifp)
332{
333 listnode node;
334 struct connected *ifc;
335
336 if (ifp == NULL)
337 return NULL;
338
339 for (node = listhead (ifp->connected); node; node = nextnode (node))
340 {
341 ifc = getdata (node);
342
343 if ((ifc->address->family == AF_INET6) &&
344 (IN6_IS_ADDR_LINKLOCAL (&ifc->address->u.prefix6)))
345 return ifc;
346 }
347 return NULL;
348}