]> git.proxmox.com Git - mirror_frr.git/blob - zebra/if_ioctl_solaris.c
Revert "*: reindent pt. 2"
[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 #include "if.h"
25 #include "sockunion.h"
26 #include "prefix.h"
27 #include "ioctl.h"
28 #include "connected.h"
29 #include "memory.h"
30 #include "zebra_memory.h"
31 #include "log.h"
32 #include "privs.h"
33 #include "vrf.h"
34 #include "vty.h"
35
36 #include "zebra/interface.h"
37 #include "zebra/ioctl_solaris.h"
38 #include "zebra/rib.h"
39
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;
43
44 static int 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_err("Can't raise privileges");
61
62 sock = socket(af, SOCK_DGRAM, 0);
63 if (sock < 0) {
64 zlog_warn("Can't make %s socket stream: %s",
65 (af == AF_INET ? "AF_INET" : "AF_INET6"),
66 safe_strerror(errno));
67
68 if (zserv_privs.change(ZPRIVS_LOWER))
69 zlog_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_err("Can't lower privileges");
82
83 if (ret < 0) {
84 zlog_warn("interface_list_ioctl: SIOCGLIFNUM failed %s",
85 safe_strerror(save_errno));
86 close(sock);
87 return -1;
88 }
89 ifnum = lifn.lifn_count;
90
91 /*
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.
96 */
97 needed = (ifnum + 4) * sizeof(struct lifreq);
98 if (needed > lastneeded || needed < lastneeded / 2) {
99 if (buf != NULL)
100 XFREE(MTYPE_TMP, buf);
101 if ((buf = XMALLOC(MTYPE_TMP, needed)) == NULL) {
102 zlog_warn("interface_list_ioctl: malloc failed");
103 close(sock);
104 return -1;
105 }
106 }
107 lastneeded = needed;
108
109 lifconf.lifc_family = af;
110 lifconf.lifc_flags = LIFC_NOXMIT;
111 lifconf.lifc_len = needed;
112 lifconf.lifc_buf = buf;
113
114 if (zserv_privs.change(ZPRIVS_RAISE))
115 zlog_err("Can't raise privileges");
116
117 ret = ioctl(sock, SIOCGLIFCONF, &lifconf);
118
119 if (ret < 0) {
120 if (errno == EINVAL)
121 goto calculate_lifc_len; /* deliberately hold privileges
122 */
123
124 zlog_warn("SIOCGLIFCONF: %s", safe_strerror(errno));
125
126 if (zserv_privs.change(ZPRIVS_LOWER))
127 zlog_err("Can't lower privileges");
128
129 goto end;
130 }
131
132 if (zserv_privs.change(ZPRIVS_LOWER))
133 zlog_err("Can't lower privileges");
134
135 /* Allocate interface. */
136 lifreq = lifconf.lifc_req;
137
138 for (n = 0; n < lifconf.lifc_len; n += sizeof(struct lifreq)) {
139 /* we treat Solaris logical interfaces as addresses, because
140 * that is
141 * how PF_ROUTE on Solaris treats them. Hence we can not
142 * directly use
143 * the lifreq_name to get the ifp. We need to normalise the
144 * name
145 * before attempting get.
146 *
147 * Solaris logical interface names are in the form of:
148 * <interface name>:<logical interface id>
149 */
150 unsigned int normallen = 0;
151 uint64_t lifflags;
152
153 /* We should exclude ~IFF_UP interfaces, as we'll find out about
154 * them
155 * coming up later through RTM_NEWADDR message on the route
156 * socket.
157 */
158 if (if_get_flags_direct(lifreq->lifr_name, &lifflags,
159 lifreq->lifr_addr.ss_family)
160 || !CHECK_FLAG(lifflags, IFF_UP)) {
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 VRF_DEFAULT, 0);
173
174 if (lifreq->lifr_addr.ss_family == AF_INET)
175 ifp->flags |= IFF_IPV4;
176
177 if (lifreq->lifr_addr.ss_family == AF_INET6) {
178 ifp->flags |= IFF_IPV6;
179 }
180
181 if_add_update(ifp);
182
183 interface_info_ioctl(ifp);
184
185 /* If a logical interface pass the full name so it can be
186 * as a label on the address
187 */
188 if (*(lifreq->lifr_name + normallen) != '\0')
189 if_get_addr(ifp, (struct sockaddr *)&lifreq->lifr_addr,
190 lifreq->lifr_name);
191 else
192 if_get_addr(ifp, (struct sockaddr *)&lifreq->lifr_addr,
193 NULL);
194
195 /* Poke the interface flags. Lets IFF_UP mangling kick in */
196 if_flags_update(ifp, ifp->flags);
197
198 lifreq++;
199 }
200
201 end:
202 close(sock);
203 XFREE(MTYPE_TMP, lifconf.lifc_buf);
204 return ret;
205 }
206
207 /* Get interface's index by ioctl. */
208 static int if_get_index(struct interface *ifp)
209 {
210 int ret;
211 struct lifreq lifreq;
212
213 lifreq_set_name(&lifreq, ifp->name);
214
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);
219 else
220 ret = -1;
221
222 if (ret < 0) {
223 zlog_warn("SIOCGLIFINDEX(%s) failed", ifp->name);
224 return ret;
225 }
226
227 /* OK we got interface index. */
228 #ifdef ifr_ifindex
229 ifp->ifindex = lifreq.lifr_ifindex;
230 #else
231 ifp->ifindex = lifreq.lifr_index;
232 #endif
233 return ifp->ifindex;
234 }
235
236
237 /* Interface address lookup by ioctl. This function only looks up
238 IPv4 address. */
239 #define ADDRLEN(sa) \
240 (((sa)->sa_family == AF_INET ? sizeof(struct sockaddr_in) \
241 : sizeof(struct sockaddr_in6)))
242
243 #define SIN(s) ((struct sockaddr_in *)(s))
244 #define SIN6(s) ((struct sockaddr_in6 *)(s))
245
246 /* Retrieve address information for the given ifp */
247 static int if_get_addr(struct interface *ifp, struct sockaddr *addr,
248 const char *label)
249 {
250 int ret;
251 struct lifreq lifreq;
252 struct sockaddr_storage mask, dest;
253 char *dest_pnt = NULL;
254 u_char prefixlen = 0;
255 afi_t af;
256 int flags = 0;
257
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
261 */
262 strncpy(lifreq.lifr_name, (label ? label : ifp->name), IFNAMSIZ);
263
264 /* Interface's address. */
265 memcpy(&lifreq.lifr_addr, addr, ADDRLEN(addr));
266 af = addr->sa_family;
267
268 /* Point to point or broad cast address pointer init. */
269 dest_pnt = NULL;
270
271 if (AF_IOCTL(af, SIOCGLIFDSTADDR, (caddr_t)&lifreq) >= 0) {
272 memcpy(&dest, &lifreq.lifr_dstaddr, ADDRLEN(addr));
273 if (af == AF_INET)
274 dest_pnt = (char *)&(SIN(&dest)->sin_addr);
275 else
276 dest_pnt = (char *)&(SIN6(&dest)->sin6_addr);
277 flags = ZEBRA_IFA_PEER;
278 }
279
280 if (af == AF_INET) {
281 ret = if_ioctl(SIOCGLIFNETMASK, (caddr_t)&lifreq);
282
283 if (ret < 0) {
284 if (errno != EADDRNOTAVAIL) {
285 zlog_warn("SIOCGLIFNETMASK (%s) fail: %s",
286 ifp->name, safe_strerror(errno));
287 return ret;
288 }
289 return 0;
290 }
291 memcpy(&mask, &lifreq.lifr_addr, ADDRLEN(addr));
292
293 prefixlen = ip_masklen(SIN(&mask)->sin_addr);
294 if (!dest_pnt
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;
299 }
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;
304 else
305 zlog_warn("SIOCGLIFSUBNET (%s) fail: %s",
306 ifp->name, safe_strerror(errno));
307 } else {
308 prefixlen = lifreq.lifr_addrlen;
309 }
310 }
311
312 /* Set address to the interface. */
313 if (af == AF_INET)
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,
319 label);
320
321 return 0;
322 }
323
324 /* Fetch interface information via ioctl(). */
325 static void interface_info_ioctl(struct interface *ifp)
326 {
327 if_get_index(ifp);
328 if_get_flags(ifp);
329 if_get_mtu(ifp);
330 if_get_metric(ifp);
331 }
332
333 /* Lookup all interface information. */
334 void interface_list(struct zebra_ns *zns)
335 {
336 if (zns->ns_id != NS_DEFAULT) {
337 zlog_warn("interface_list: ignore NS %u", zns->ns_id);
338 return;
339 }
340 interface_list_ioctl(AF_INET);
341 interface_list_ioctl(AF_INET6);
342 interface_list_ioctl(AF_UNSPEC);
343 }
344
345 struct connected *if_lookup_linklocal(struct interface *ifp)
346 {
347 struct listnode *node;
348 struct connected *ifc;
349
350 if (ifp == NULL)
351 return NULL;
352
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)))
356 return ifc;
357 }
358
359 return NULL;
360 }