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 along
18 * with this program; see the file COPYING; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 #include "sockunion.h"
28 #include "connected.h"
30 #include "zebra_memory.h"
36 #include "zebra/interface.h"
37 #include "zebra/ioctl_solaris.h"
38 #include "zebra/rib.h"
40 static int if_get_addr(struct interface
*, struct sockaddr
*, const char *);
41 static void interface_info_ioctl(struct interface
*);
42 extern struct zebra_privs_t zserv_privs
;
44 static int interface_list_ioctl(int af
)
51 struct lifreq
*lifreq
;
52 struct lifconf lifconf
;
53 struct interface
*ifp
;
56 size_t needed
, lastneeded
= 0;
59 if (zserv_privs
.change(ZPRIVS_RAISE
))
60 zlog_err("Can't raise privileges");
62 sock
= socket(af
, SOCK_DGRAM
, 0);
64 zlog_warn("Can't make %s socket stream: %s",
65 (af
== AF_INET
? "AF_INET" : "AF_INET6"),
66 safe_strerror(errno
));
68 if (zserv_privs
.change(ZPRIVS_LOWER
))
69 zlog_err("Can't lower privileges");
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
);
80 if (zserv_privs
.change(ZPRIVS_LOWER
))
81 zlog_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) {
100 XFREE(MTYPE_TMP
, buf
);
101 if ((buf
= XMALLOC(MTYPE_TMP
, needed
)) == NULL
) {
102 zlog_warn("interface_list_ioctl: malloc failed");
109 lifconf
.lifc_family
= af
;
110 lifconf
.lifc_flags
= LIFC_NOXMIT
;
111 lifconf
.lifc_len
= needed
;
112 lifconf
.lifc_buf
= buf
;
114 if (zserv_privs
.change(ZPRIVS_RAISE
))
115 zlog_err("Can't raise privileges");
117 ret
= ioctl(sock
, SIOCGLIFCONF
, &lifconf
);
121 goto calculate_lifc_len
; /* deliberately hold privileges
124 zlog_warn("SIOCGLIFCONF: %s", safe_strerror(errno
));
126 if (zserv_privs
.change(ZPRIVS_LOWER
))
127 zlog_err("Can't lower privileges");
132 if (zserv_privs
.change(ZPRIVS_LOWER
))
133 zlog_err("Can't lower privileges");
135 /* Allocate interface. */
136 lifreq
= lifconf
.lifc_req
;
138 for (n
= 0; n
< lifconf
.lifc_len
; n
+= sizeof(struct lifreq
)) {
139 /* we treat Solaris logical interfaces as addresses, because
141 * how PF_ROUTE on Solaris treats them. Hence we can not
143 * the lifreq_name to get the ifp. We need to normalise the
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
155 * coming up later through RTM_NEWADDR message on the route
158 if (if_get_flags_direct(lifreq
->lifr_name
, &lifflags
,
159 lifreq
->lifr_addr
.ss_family
)
160 || !CHECK_FLAG(lifflags
, IFF_UP
)) {
165 /* Find the normalised name */
166 while ((normallen
< sizeof(lifreq
->lifr_name
))
167 && (*(lifreq
->lifr_name
+ normallen
) != '\0')
168 && (*(lifreq
->lifr_name
+ normallen
) != ':'))
171 ifp
= if_get_by_name_len(lifreq
->lifr_name
, normallen
,
174 if (lifreq
->lifr_addr
.ss_family
== AF_INET
)
175 ifp
->flags
|= IFF_IPV4
;
177 if (lifreq
->lifr_addr
.ss_family
== AF_INET6
) {
178 ifp
->flags
|= IFF_IPV6
;
183 interface_info_ioctl(ifp
);
185 /* If a logical interface pass the full name so it can be
186 * as a label on the address
188 if (*(lifreq
->lifr_name
+ normallen
) != '\0')
189 if_get_addr(ifp
, (struct sockaddr
*)&lifreq
->lifr_addr
,
192 if_get_addr(ifp
, (struct sockaddr
*)&lifreq
->lifr_addr
,
195 /* Poke the interface flags. Lets IFF_UP mangling kick in */
196 if_flags_update(ifp
, ifp
->flags
);
203 XFREE(MTYPE_TMP
, lifconf
.lifc_buf
);
207 /* Get interface's index by ioctl. */
208 static int if_get_index(struct interface
*ifp
)
211 struct lifreq lifreq
;
213 lifreq_set_name(&lifreq
, ifp
->name
);
215 if (ifp
->flags
& IFF_IPV4
)
216 ret
= AF_IOCTL(AF_INET
, SIOCGLIFINDEX
, (caddr_t
)&lifreq
);
217 else if (ifp
->flags
& IFF_IPV6
)
218 ret
= AF_IOCTL(AF_INET6
, SIOCGLIFINDEX
, (caddr_t
)&lifreq
);
223 zlog_warn("SIOCGLIFINDEX(%s) failed", ifp
->name
);
227 /* OK we got interface index. */
229 ifp
->ifindex
= lifreq
.lifr_ifindex
;
231 ifp
->ifindex
= lifreq
.lifr_index
;
237 /* Interface address lookup by ioctl. This function only looks up
239 #define ADDRLEN(sa) \
240 (((sa)->sa_family == AF_INET ? sizeof(struct sockaddr_in) \
241 : sizeof(struct sockaddr_in6)))
243 #define SIN(s) ((struct sockaddr_in *)(s))
244 #define SIN6(s) ((struct sockaddr_in6 *)(s))
246 /* Retrieve address information for the given ifp */
247 static int if_get_addr(struct interface
*ifp
, struct sockaddr
*addr
,
251 struct lifreq lifreq
;
252 struct sockaddr_storage mask
, dest
;
253 char *dest_pnt
= NULL
;
254 u_char prefixlen
= 0;
258 /* Interface's name and address family.
259 * We need to use the logical interface name / label, if we've been
260 * given one, in order to get the right address
262 strncpy(lifreq
.lifr_name
, (label
? label
: ifp
->name
), IFNAMSIZ
);
264 /* Interface's address. */
265 memcpy(&lifreq
.lifr_addr
, addr
, ADDRLEN(addr
));
266 af
= addr
->sa_family
;
268 /* Point to point or broad cast address pointer init. */
271 if (AF_IOCTL(af
, SIOCGLIFDSTADDR
, (caddr_t
)&lifreq
) >= 0) {
272 memcpy(&dest
, &lifreq
.lifr_dstaddr
, ADDRLEN(addr
));
274 dest_pnt
= (char *)&(SIN(&dest
)->sin_addr
);
276 dest_pnt
= (char *)&(SIN6(&dest
)->sin6_addr
);
277 flags
= ZEBRA_IFA_PEER
;
281 ret
= if_ioctl(SIOCGLIFNETMASK
, (caddr_t
)&lifreq
);
284 if (errno
!= EADDRNOTAVAIL
) {
285 zlog_warn("SIOCGLIFNETMASK (%s) fail: %s",
286 ifp
->name
, safe_strerror(errno
));
291 memcpy(&mask
, &lifreq
.lifr_addr
, ADDRLEN(addr
));
293 prefixlen
= ip_masklen(SIN(&mask
)->sin_addr
);
295 && (if_ioctl(SIOCGLIFBRDADDR
, (caddr_t
)&lifreq
) >= 0)) {
296 memcpy(&dest
, &lifreq
.lifr_broadaddr
,
297 sizeof(struct sockaddr_in
));
298 dest_pnt
= (char *)&SIN(&dest
)->sin_addr
;
300 } else if (af
== AF_INET6
) {
301 if (if_ioctl_ipv6(SIOCGLIFSUBNET
, (caddr_t
)&lifreq
) < 0) {
302 if (ifp
->flags
& IFF_POINTOPOINT
)
303 prefixlen
= IPV6_MAX_BITLEN
;
305 zlog_warn("SIOCGLIFSUBNET (%s) fail: %s",
306 ifp
->name
, safe_strerror(errno
));
308 prefixlen
= lifreq
.lifr_addrlen
;
312 /* Set address to the interface. */
314 connected_add_ipv4(ifp
, flags
, &SIN(addr
)->sin_addr
, prefixlen
,
315 (struct in_addr
*)dest_pnt
, label
);
316 else if (af
== AF_INET6
)
317 connected_add_ipv6(ifp
, flags
, &SIN6(addr
)->sin6_addr
,
318 prefixlen
, (struct in6_addr
*)dest_pnt
,
324 /* Fetch interface information via ioctl(). */
325 static void interface_info_ioctl(struct interface
*ifp
)
333 /* Lookup all interface information. */
334 void interface_list(struct zebra_ns
*zns
)
336 if (zns
->ns_id
!= NS_DEFAULT
) {
337 zlog_warn("interface_list: ignore NS %u", zns
->ns_id
);
340 interface_list_ioctl(AF_INET
);
341 interface_list_ioctl(AF_INET6
);
342 interface_list_ioctl(AF_UNSPEC
);
345 struct connected
*if_lookup_linklocal(struct interface
*ifp
)
347 struct listnode
*node
;
348 struct connected
*ifc
;
353 for (ALL_LIST_ELEMENTS_RO(ifp
->connected
, node
, ifc
)) {
354 if ((ifc
->address
->family
== AF_INET6
)
355 && (IN6_IS_ADDR_LINKLOCAL(&ifc
->address
->u
.prefix6
)))