1: /*
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: #include "privs.h"
33: #include "vrf.h"
34:
35: #include "zebra/interface.h"
36: #include "zebra/ioctl_solaris.h"
37: #include "zebra/rib.h"
38:
39: static int if_get_addr (struct interface *, struct sockaddr *, const char *);
40: static void interface_info_ioctl (struct interface *);
41: extern struct zebra_privs_t zserv_privs;
42:
43: static int
44: interface_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;
55: int save_errno;
56: 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",
66: (af == AF_INET ? "AF_INET" : "AF_INET6"), safe_strerror (errno));
67:
68: if (zserv_privs.change(ZPRIVS_LOWER))
69: zlog (NULL, LOG_ERR, "Can't lower privileges");
70:
71: return -1;
72: }
73:
74: calculate_lifc_len: /* must hold privileges to enter here */
75: lifn.lifn_family = af;
76: lifn.lifn_flags = LIFC_NOXMIT; /* we want NOXMIT interfaces too */
77: ret = ioctl (sock, SIOCGLIFNUM, &lifn);
78: save_errno = errno;
79:
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",
86: safe_strerror (save_errno));
87: 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;
113: lifconf.lifc_flags = LIFC_NOXMIT;
114: 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:
127: zlog_warn ("SIOCGLIFCONF: %s", safe_strerror (errno));
128:
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: {
143: /* 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;
152: uint64_t lifflags;
153:
154: /* 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 */
166: 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);
172:
173: if (lifreq->lifr_addr.ss_family == AF_INET)
174: ifp->flags |= IFF_IPV4;
175:
176: if (lifreq->lifr_addr.ss_family == AF_INET6)
177: {
178: #ifdef HAVE_IPV6
179: ifp->flags |= IFF_IPV6;
180: #else
181: lifreq++;
182: continue;
183: #endif /* HAVE_IPV6 */
184: }
185:
186: if_add_update (ifp);
187:
188: interface_info_ioctl (ifp);
189:
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);
198:
199: /* Poke the interface flags. Lets IFF_UP mangling kick in */
200: if_flags_update (ifp, ifp->flags);
201:
202: lifreq++;
203: }
204:
205: end:
206: close (sock);
207: XFREE (MTYPE_TMP, lifconf.lifc_buf);
208: return ret;
209: }
210:
211: /* Get interface's index by ioctl. */
212: static int
213: if_get_index (struct interface *ifp)
214: {
215: int ret;
216: struct lifreq lifreq;
217:
218: lifreq_set_name (&lifreq, ifp->name);
219:
220: 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:
227: 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:
252: /* Retrieve address information for the given ifp */
253: static int
254: if_get_addr (struct interface *ifp, struct sockaddr *addr, const char *label)
255: {
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;
262: int flags = 0;
263:
264: /* 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: */
268: strncpy (lifreq.lifr_name, (label ? label : ifp->name), IFNAMSIZ);
269:
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:
277: if (AF_IOCTL (af, SIOCGLIFDSTADDR, (caddr_t) & lifreq) >= 0)
278: {
279: 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);
284: flags = ZEBRA_IFA_PEER;
285: }
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,
296: safe_strerror (errno));
297: return ret;
298: }
299: return 0;
300: }
301: memcpy (&mask, &lifreq.lifr_addr, ADDRLEN (addr));
302:
303: prefixlen = ip_masklen (SIN (&mask)->sin_addr);
304: if (!dest_pnt && (if_ioctl (SIOCGLIFBRDADDR, (caddr_t) & lifreq) >= 0))
305: {
306: memcpy (&dest, &lifreq.lifr_broadaddr, sizeof (struct sockaddr_in));
307: dest_pnt = (char *) &SIN (&dest)->sin_addr;
308: }
309: }
310: #ifdef HAVE_IPV6
311: else if (af == AF_INET6)
312: {
313: 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: }
321: else
322: {
323: prefixlen = lifreq.lifr_addrlen;
324: }
325: }
326: #endif /* HAVE_IPV6 */
327:
328: /* Set address to the interface. */
329: if (af == AF_INET)
330: connected_add_ipv4 (ifp, flags, &SIN (addr)->sin_addr, prefixlen,
331: (struct in_addr *) dest_pnt, label);
332: #ifdef HAVE_IPV6
333: else if (af == AF_INET6)
334: connected_add_ipv6 (ifp, flags, &SIN6 (addr)->sin6_addr, prefixlen,
335: (struct in6_addr *) dest_pnt, label);
336: #endif /* HAVE_IPV6 */
337:
338: return 0;
339: }
340:
341: /* Fetch interface information via ioctl(). */
342: static void
343: interface_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. */
352: void
353: interface_list (struct zebra_vrf *zvrf)
354: {
355: if (zvrf->vrf_id != VRF_DEFAULT)
356: {
357: zlog_warn ("interface_list: ignore VRF %u", zvrf->vrf_id);
358: return;
359: }
360: interface_list_ioctl (AF_INET);
361: interface_list_ioctl (AF_INET6);
362: interface_list_ioctl (AF_UNSPEC);
363: }
364:
365: struct connected *
366: if_lookup_linklocal (struct interface *ifp)
367: {
368: #ifdef HAVE_IPV6
369: struct listnode *node;
370: struct connected *ifc;
371:
372: if (ifp == NULL)
373: return NULL;
374:
375: for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc))
376: {
377: if ((ifc->address->family == AF_INET6) &&
378: (IN6_IS_ADDR_LINKLOCAL (&ifc->address->u.prefix6)))
379: return ifc;
380: }
381: #endif /* HAVE_IPV6 */
382:
383: return NULL;
384: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>