]> git.proxmox.com Git - mirror_frr.git/blob - zebra/if_ioctl_solaris.c
Merge pull request #2944 from thbtcllt/master
[mirror_frr.git] / zebra / if_ioctl_solaris.c
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 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
20 */
21
22 #include <zebra.h>
23
24 #ifdef SUNOS_5
25
26 #include "if.h"
27 #include "sockunion.h"
28 #include "prefix.h"
29 #include "ioctl.h"
30 #include "connected.h"
31 #include "memory.h"
32 #include "zebra_memory.h"
33 #include "log.h"
34 #include "privs.h"
35 #include "vrf.h"
36 #include "vty.h"
37 #include "lib_errors.h"
38
39 #include "zebra/interface.h"
40 #include "zebra/ioctl_solaris.h"
41 #include "zebra/rib.h"
42 #include "zebra/rt.h"
43
44 static int if_get_addr(struct interface *, struct sockaddr *, const char *);
45 static void interface_info_ioctl(struct interface *);
46 extern struct zebra_privs_t zserv_privs;
47
48 static int interface_list_ioctl(int af)
49 {
50 int ret;
51 int sock;
52 #define IFNUM_BASE 32
53 struct lifnum lifn;
54 int ifnum;
55 struct lifreq *lifreq;
56 struct lifconf lifconf;
57 struct interface *ifp;
58 int n;
59 size_t needed, lastneeded = 0;
60 char *buf = NULL;
61
62 frr_elevate_privs(&zserv_privs) {
63 sock = socket(af, SOCK_DGRAM, 0);
64 }
65
66 if (sock < 0) {
67 zlog_warn("Can't make %s socket stream: %s",
68 (af == AF_INET ? "AF_INET" : "AF_INET6"),
69 safe_strerror(errno));
70 return -1;
71 }
72
73 calculate_lifc_len:
74 frr_elevate_privs(&zserv_privs) {
75 lifn.lifn_family = af;
76 lifn.lifn_flags = LIFC_NOXMIT;
77 /* we want NOXMIT interfaces too */
78 ret = ioctl(sock, SIOCGLIFNUM, &lifn);
79 }
80
81 if (ret < 0) {
82 zlog_warn("interface_list_ioctl: SIOCGLIFNUM failed %s",
83 safe_strerror(errno));
84 close(sock);
85 return -1;
86 }
87 ifnum = lifn.lifn_count;
88
89 /*
90 * When calculating the buffer size needed, add a small number
91 * of interfaces to those we counted. We do this to capture
92 * the interface status of potential interfaces which may have
93 * been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF.
94 */
95 needed = (ifnum + 4) * sizeof(struct lifreq);
96 if (needed > lastneeded || needed < lastneeded / 2) {
97 if (buf != NULL)
98 XFREE(MTYPE_TMP, buf);
99 buf = XMALLOC(MTYPE_TMP, needed);
100 }
101 lastneeded = needed;
102
103 lifconf.lifc_family = af;
104 lifconf.lifc_flags = LIFC_NOXMIT;
105 lifconf.lifc_len = needed;
106 lifconf.lifc_buf = buf;
107
108 frr_elevate_privs(&zserv_privs) {
109 ret = ioctl(sock, SIOCGLIFCONF, &lifconf);
110 }
111
112 if (ret < 0) {
113 if (errno == EINVAL)
114 goto calculate_lifc_len;
115
116 zlog_warn("SIOCGLIFCONF: %s", safe_strerror(errno));
117 goto end;
118 }
119
120 /* Allocate interface. */
121 lifreq = lifconf.lifc_req;
122
123 for (n = 0; n < lifconf.lifc_len; n += sizeof(struct lifreq)) {
124 /* we treat Solaris logical interfaces as addresses, because
125 * that is
126 * how PF_ROUTE on Solaris treats them. Hence we can not
127 * directly use
128 * the lifreq_name to get the ifp. We need to normalise the
129 * name
130 * before attempting get.
131 *
132 * Solaris logical interface names are in the form of:
133 * <interface name>:<logical interface id>
134 */
135 unsigned int normallen = 0;
136 uint64_t lifflags;
137
138 /* We should exclude ~IFF_UP interfaces, as we'll find out about
139 * them
140 * coming up later through RTM_NEWADDR message on the route
141 * socket.
142 */
143 if (if_get_flags_direct(lifreq->lifr_name, &lifflags,
144 lifreq->lifr_addr.ss_family)
145 || !CHECK_FLAG(lifflags, IFF_UP)) {
146 lifreq++;
147 continue;
148 }
149
150 /* Find the normalised name */
151 while ((normallen < sizeof(lifreq->lifr_name))
152 && (*(lifreq->lifr_name + normallen) != '\0')
153 && (*(lifreq->lifr_name + normallen) != ':'))
154 normallen++;
155
156 ifp = if_get_by_name(lifreq->lifr_name, VRF_DEFAULT, 0);
157
158 if (lifreq->lifr_addr.ss_family == AF_INET)
159 ifp->flags |= IFF_IPV4;
160
161 if (lifreq->lifr_addr.ss_family == AF_INET6) {
162 ifp->flags |= IFF_IPV6;
163 }
164
165 if_add_update(ifp);
166
167 interface_info_ioctl(ifp);
168
169 /* If a logical interface pass the full name so it can be
170 * as a label on the address
171 */
172 if (*(lifreq->lifr_name + normallen) != '\0')
173 if_get_addr(ifp, (struct sockaddr *)&lifreq->lifr_addr,
174 lifreq->lifr_name);
175 else
176 if_get_addr(ifp, (struct sockaddr *)&lifreq->lifr_addr,
177 NULL);
178
179 /* Poke the interface flags. Lets IFF_UP mangling kick in */
180 if_flags_update(ifp, ifp->flags);
181
182 lifreq++;
183 }
184
185 end:
186 close(sock);
187 XFREE(MTYPE_TMP, lifconf.lifc_buf);
188 return ret;
189 }
190
191 /* Get interface's index by ioctl. */
192 static int if_get_index(struct interface *ifp)
193 {
194 int ret;
195 struct lifreq lifreq;
196
197 lifreq_set_name(&lifreq, ifp->name);
198
199 if (ifp->flags & IFF_IPV4)
200 ret = AF_IOCTL(AF_INET, SIOCGLIFINDEX, (caddr_t)&lifreq);
201 else if (ifp->flags & IFF_IPV6)
202 ret = AF_IOCTL(AF_INET6, SIOCGLIFINDEX, (caddr_t)&lifreq);
203 else
204 ret = -1;
205
206 if (ret < 0) {
207 zlog_warn("SIOCGLIFINDEX(%s) failed", ifp->name);
208 return ret;
209 }
210
211 /* OK we got interface index. */
212 #ifdef ifr_ifindex
213 if_set_index(ifp, lifreq.lifr_ifindex);
214 #else
215 if_set_index(ifp, lifreq.lifr_index);
216 #endif
217 return ifp->ifindex;
218 }
219
220
221 /* Interface address lookup by ioctl. This function only looks up
222 IPv4 address. */
223 #define ADDRLEN(sa) \
224 (((sa)->sa_family == AF_INET ? sizeof(struct sockaddr_in) \
225 : sizeof(struct sockaddr_in6)))
226
227 #define SIN(s) ((struct sockaddr_in *)(s))
228 #define SIN6(s) ((struct sockaddr_in6 *)(s))
229
230 /* Retrieve address information for the given ifp */
231 static int if_get_addr(struct interface *ifp, struct sockaddr *addr,
232 const char *label)
233 {
234 int ret;
235 struct lifreq lifreq;
236 struct sockaddr_storage mask, dest;
237 char *dest_pnt = NULL;
238 uint8_t prefixlen = 0;
239 afi_t af;
240 int flags = 0;
241
242 /* Interface's name and address family.
243 * We need to use the logical interface name / label, if we've been
244 * given one, in order to get the right address
245 */
246 strncpy(lifreq.lifr_name, (label ? label : ifp->name), IFNAMSIZ);
247
248 /* Interface's address. */
249 memcpy(&lifreq.lifr_addr, addr, ADDRLEN(addr));
250 af = addr->sa_family;
251
252 /* Point to point or broad cast address pointer init. */
253 dest_pnt = NULL;
254
255 if (AF_IOCTL(af, SIOCGLIFDSTADDR, (caddr_t)&lifreq) >= 0) {
256 memcpy(&dest, &lifreq.lifr_dstaddr, ADDRLEN(addr));
257 if (af == AF_INET)
258 dest_pnt = (char *)&(SIN(&dest)->sin_addr);
259 else
260 dest_pnt = (char *)&(SIN6(&dest)->sin6_addr);
261 flags = ZEBRA_IFA_PEER;
262 }
263
264 if (af == AF_INET) {
265 ret = if_ioctl(SIOCGLIFNETMASK, (caddr_t)&lifreq);
266
267 if (ret < 0) {
268 if (errno != EADDRNOTAVAIL) {
269 zlog_warn("SIOCGLIFNETMASK (%s) fail: %s",
270 ifp->name, safe_strerror(errno));
271 return ret;
272 }
273 return 0;
274 }
275 memcpy(&mask, &lifreq.lifr_addr, ADDRLEN(addr));
276
277 prefixlen = ip_masklen(SIN(&mask)->sin_addr);
278 if (!dest_pnt
279 && (if_ioctl(SIOCGLIFBRDADDR, (caddr_t)&lifreq) >= 0)) {
280 memcpy(&dest, &lifreq.lifr_broadaddr,
281 sizeof(struct sockaddr_in));
282 dest_pnt = (char *)&SIN(&dest)->sin_addr;
283 }
284 } else if (af == AF_INET6) {
285 if (if_ioctl_ipv6(SIOCGLIFSUBNET, (caddr_t)&lifreq) < 0) {
286 if (ifp->flags & IFF_POINTOPOINT)
287 prefixlen = IPV6_MAX_BITLEN;
288 else
289 zlog_warn("SIOCGLIFSUBNET (%s) fail: %s",
290 ifp->name, safe_strerror(errno));
291 } else {
292 prefixlen = lifreq.lifr_addrlen;
293 }
294 }
295
296 /* Set address to the interface. */
297 if (af == AF_INET)
298 connected_add_ipv4(ifp, flags, &SIN(addr)->sin_addr, prefixlen,
299 (struct in_addr *)dest_pnt, label);
300 else if (af == AF_INET6)
301 connected_add_ipv6(ifp, flags, &SIN6(addr)->sin6_addr, NULL,
302 prefixlen, label);
303
304 return 0;
305 }
306
307 /* Fetch interface information via ioctl(). */
308 static void interface_info_ioctl(struct interface *ifp)
309 {
310 if_get_index(ifp);
311 if_get_flags(ifp);
312 if_get_mtu(ifp);
313 if_get_metric(ifp);
314 }
315
316 /* Lookup all interface information. */
317 void interface_list(struct zebra_ns *zns)
318 {
319 if (zns->ns_id != NS_DEFAULT) {
320 zlog_warn("interface_list: ignore NS %u", zns->ns_id);
321 return;
322 }
323 interface_list_ioctl(AF_INET);
324 interface_list_ioctl(AF_INET6);
325 interface_list_ioctl(AF_UNSPEC);
326 }
327
328 struct connected *if_lookup_linklocal(struct interface *ifp)
329 {
330 struct listnode *node;
331 struct connected *ifc;
332
333 if (ifp == NULL)
334 return NULL;
335
336 for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) {
337 if ((ifc->address->family == AF_INET6)
338 && (IN6_IS_ADDR_LINKLOCAL(&ifc->address->u.prefix6)))
339 return ifc;
340 }
341
342 return NULL;
343 }
344
345 #endif /* SUNOS_5 */