2 * Interface looking up by ioctl () on Solaris.
3 * Copyright (C) 1997, 98 Kunihiro Ishiguro
5 * This file is part of GNU Zebra.
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
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.
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
26 #include "sockunion.h"
29 #include "connected.h"
34 #include "zebra/interface.h"
36 void lifreq_set_name (struct lifreq
*, const char *);
37 int if_get_flags_direct (const char *, uint64_t *, unsigned int af
);
38 static int if_get_addr (struct interface
*, struct sockaddr
*, const char *);
39 static void interface_info_ioctl (struct interface
*);
40 extern struct zebra_privs_t zserv_privs
;
43 interface_list_ioctl (int af
)
50 struct lifreq
*lifreq
;
51 struct lifconf lifconf
;
52 struct interface
*ifp
;
55 size_t needed
, lastneeded
= 0;
58 if (zserv_privs
.change(ZPRIVS_RAISE
))
59 zlog (NULL
, LOG_ERR
, "Can't raise privileges");
61 sock
= socket (af
, SOCK_DGRAM
, 0);
64 zlog_warn ("Can't make %s socket stream: %s",
65 (af
== AF_INET
? "AF_INET" : "AF_INET6"), safe_strerror (errno
));
67 if (zserv_privs
.change(ZPRIVS_LOWER
))
68 zlog (NULL
, LOG_ERR
, "Can't lower privileges");
73 calculate_lifc_len
: /* must hold privileges to enter here */
74 lifn
.lifn_family
= af
;
75 lifn
.lifn_flags
= LIFC_NOXMIT
; /* we want NOXMIT interfaces too */
76 ret
= ioctl (sock
, SIOCGLIFNUM
, &lifn
);
79 if (zserv_privs
.change(ZPRIVS_LOWER
))
80 zlog (NULL
, LOG_ERR
, "Can't lower privileges");
84 zlog_warn ("interface_list_ioctl: SIOCGLIFNUM failed %s",
85 safe_strerror (save_errno
));
89 ifnum
= lifn
.lifn_count
;
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.
97 needed
= (ifnum
+ 4) * sizeof (struct lifreq
);
98 if (needed
> lastneeded
|| needed
< lastneeded
/ 2)
101 XFREE (MTYPE_TMP
, buf
);
102 if ((buf
= XMALLOC (MTYPE_TMP
, needed
)) == NULL
)
104 zlog_warn ("interface_list_ioctl: malloc failed");
111 lifconf
.lifc_family
= af
;
112 lifconf
.lifc_flags
= LIFC_NOXMIT
;
113 lifconf
.lifc_len
= needed
;
114 lifconf
.lifc_buf
= buf
;
116 if (zserv_privs
.change(ZPRIVS_RAISE
))
117 zlog (NULL
, LOG_ERR
, "Can't raise privileges");
119 ret
= ioctl (sock
, SIOCGLIFCONF
, &lifconf
);
124 goto calculate_lifc_len
; /* deliberately hold privileges */
126 zlog_warn ("SIOCGLIFCONF: %s", safe_strerror (errno
));
128 if (zserv_privs
.change(ZPRIVS_LOWER
))
129 zlog (NULL
, LOG_ERR
, "Can't lower privileges");
134 if (zserv_privs
.change(ZPRIVS_LOWER
))
135 zlog (NULL
, LOG_ERR
, "Can't lower privileges");
137 /* Allocate interface. */
138 lifreq
= lifconf
.lifc_req
;
140 for (n
= 0; n
< lifconf
.lifc_len
; n
+= sizeof (struct lifreq
))
142 /* 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.
147 * Solaris logical interface names are in the form of:
148 * <interface name>:<logical interface id>
150 unsigned int normallen
= 0;
153 /* 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.
156 if (if_get_flags_direct (lifreq
->lifr_name
, &lifflags
,
157 lifreq
->lifr_addr
.ss_family
)
158 || !CHECK_FLAG (lifflags
, IFF_UP
))
164 /* Find the normalised name */
165 while ( (normallen
< sizeof(lifreq
->lifr_name
))
166 && ( *(lifreq
->lifr_name
+ normallen
) != '\0')
167 && ( *(lifreq
->lifr_name
+ normallen
) != ':') )
170 ifp
= if_get_by_name_len(lifreq
->lifr_name
, normallen
);
172 if (lifreq
->lifr_addr
.ss_family
== AF_INET
)
173 ifp
->flags
|= IFF_IPV4
;
175 if (lifreq
->lifr_addr
.ss_family
== AF_INET6
)
178 ifp
->flags
|= IFF_IPV6
;
182 #endif /* HAVE_IPV6 */
187 interface_info_ioctl (ifp
);
189 /* If a logical interface pass the full name so it can be
190 * as a label on the address
192 if ( *(lifreq
->lifr_name
+ normallen
) != '\0')
193 if_get_addr (ifp
, (struct sockaddr
*) &lifreq
->lifr_addr
,
196 if_get_addr (ifp
, (struct sockaddr
*) &lifreq
->lifr_addr
, NULL
);
198 /* Poke the interface flags. Lets IFF_UP mangling kick in */
199 if_flags_update (ifp
, ifp
->flags
);
206 XFREE (MTYPE_TMP
, lifconf
.lifc_buf
);
210 /* Get interface's index by ioctl. */
212 if_get_index (struct interface
*ifp
)
215 struct lifreq lifreq
;
217 lifreq_set_name (&lifreq
, ifp
->name
);
219 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
);
228 zlog_warn ("SIOCGLIFINDEX(%s) failed", ifp
->name
);
232 /* OK we got interface index. */
234 ifp
->ifindex
= lifreq
.lifr_ifindex
;
236 ifp
->ifindex
= lifreq
.lifr_index
;
243 /* Interface address lookup by ioctl. This function only looks up
245 #define ADDRLEN(sa) (((sa)->sa_family == AF_INET ? \
246 sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6)))
248 #define SIN(s) ((struct sockaddr_in *)(s))
249 #define SIN6(s) ((struct sockaddr_in6 *)(s))
251 /* Retrieve address information for the given ifp */
253 if_get_addr (struct interface
*ifp
, struct sockaddr
*addr
, const char *label
)
256 struct lifreq lifreq
;
257 struct sockaddr_storage mask
, dest
;
258 char *dest_pnt
= NULL
;
259 u_char prefixlen
= 0;
263 /* Interface's name and address family.
264 * We need to use the logical interface name / label, if we've been
265 * given one, in order to get the right address
267 strncpy (lifreq
.lifr_name
, (label
? label
: ifp
->name
), IFNAMSIZ
);
269 /* Interface's address. */
270 memcpy (&lifreq
.lifr_addr
, addr
, ADDRLEN (addr
));
271 af
= addr
->sa_family
;
273 /* Point to point or broad cast address pointer init. */
276 if (AF_IOCTL (af
, SIOCGLIFDSTADDR
, (caddr_t
) & lifreq
) >= 0)
278 memcpy (&dest
, &lifreq
.lifr_dstaddr
, ADDRLEN (addr
));
280 dest_pnt
= (char *) &(SIN (&dest
)->sin_addr
);
282 dest_pnt
= (char *) &(SIN6 (&dest
)->sin6_addr
);
283 flags
= ZEBRA_IFA_PEER
;
288 ret
= if_ioctl (SIOCGLIFNETMASK
, (caddr_t
) & lifreq
);
292 if (errno
!= EADDRNOTAVAIL
)
294 zlog_warn ("SIOCGLIFNETMASK (%s) fail: %s", ifp
->name
,
295 safe_strerror (errno
));
300 memcpy (&mask
, &lifreq
.lifr_addr
, ADDRLEN (addr
));
302 prefixlen
= ip_masklen (SIN (&mask
)->sin_addr
);
303 if (!dest_pnt
&& (if_ioctl (SIOCGLIFBRDADDR
, (caddr_t
) & lifreq
) >= 0))
305 memcpy (&dest
, &lifreq
.lifr_broadaddr
, sizeof (struct sockaddr_in
));
306 dest_pnt
= (char *) &SIN (&dest
)->sin_addr
;
310 else if (af
== AF_INET6
)
312 if (if_ioctl_ipv6 (SIOCGLIFSUBNET
, (caddr_t
) & lifreq
) < 0)
314 if (ifp
->flags
& IFF_POINTOPOINT
)
315 prefixlen
= IPV6_MAX_BITLEN
;
317 zlog_warn ("SIOCGLIFSUBNET (%s) fail: %s",
318 ifp
->name
, safe_strerror (errno
));
322 prefixlen
= lifreq
.lifr_addrlen
;
325 #endif /* HAVE_IPV6 */
327 /* Set address to the interface. */
329 connected_add_ipv4 (ifp
, flags
, &SIN (addr
)->sin_addr
, prefixlen
,
330 (struct in_addr
*) dest_pnt
, label
);
332 else if (af
== AF_INET6
)
333 connected_add_ipv6 (ifp
, flags
, &SIN6 (addr
)->sin6_addr
, prefixlen
,
334 (struct in6_addr
*) dest_pnt
, label
);
335 #endif /* HAVE_IPV6 */
340 /* Fetch interface information via ioctl(). */
342 interface_info_ioctl (struct interface
*ifp
)
350 /* Lookup all interface information. */
354 interface_list_ioctl (AF_INET
);
355 interface_list_ioctl (AF_INET6
);
356 interface_list_ioctl (AF_UNSPEC
);
360 if_lookup_linklocal (struct interface
*ifp
)
363 struct listnode
*node
;
364 struct connected
*ifc
;
369 for (ALL_LIST_ELEMENTS_RO(ifp
->connected
, node
, ifc
))
371 if ((ifc
->address
->family
== AF_INET6
) &&
372 (IN6_IS_ADDR_LINKLOCAL (&ifc
->address
->u
.prefix6
)))
375 #endif /* HAVE_IPV6 */