2 * Common ioctl functions.
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
30 #include "lib_errors.h"
33 #include "zebra/rib.h"
35 #include "zebra/interface.h"
36 #include "zebra/zebra_errors.h"
37 #include "zebra/debug.h"
39 #ifdef HAVE_BSD_LINK_DETECT
40 #include <net/if_media.h>
41 #endif /* HAVE_BSD_LINK_DETECT*/
43 extern struct zebra_privs_t zserv_privs
;
45 /* clear and set interface name string */
46 void ifreq_set_name(struct ifreq
*ifreq
, struct interface
*ifp
)
48 strlcpy(ifreq
->ifr_name
, ifp
->name
, sizeof(ifreq
->ifr_name
));
51 /* call ioctl system call */
52 int if_ioctl(unsigned long request
, caddr_t buffer
)
58 frr_with_privs(&zserv_privs
) {
59 sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
61 zlog_err("Cannot create UDP socket: %s",
62 safe_strerror(errno
));
65 if ((ret
= ioctl(sock
, request
, buffer
)) < 0)
77 /* call ioctl system call */
78 int vrf_if_ioctl(unsigned long request
, caddr_t buffer
, vrf_id_t vrf_id
)
84 frr_with_privs(&zserv_privs
) {
85 sock
= vrf_socket(AF_INET
, SOCK_DGRAM
, 0, vrf_id
, NULL
);
87 zlog_err("Cannot create UDP socket: %s",
88 safe_strerror(errno
));
91 ret
= vrf_ioctl(vrf_id
, sock
, request
, buffer
);
105 static int if_ioctl_ipv6(unsigned long request
, caddr_t buffer
)
111 frr_with_privs(&zserv_privs
) {
112 sock
= socket(AF_INET6
, SOCK_DGRAM
, 0);
114 zlog_err("Cannot create IPv6 datagram socket: %s",
115 safe_strerror(errno
));
119 if ((ret
= ioctl(sock
, request
, buffer
)) < 0)
130 #endif /* ! HAVE_NETLINK */
133 * get interface metric
134 * -- if value is not avaliable set -1
136 void if_get_metric(struct interface
*ifp
)
139 struct ifreq ifreq
= {};
141 ifreq_set_name(&ifreq
, ifp
);
143 if (vrf_if_ioctl(SIOCGIFMETRIC
, (caddr_t
)&ifreq
, ifp
->vrf
->vrf_id
) < 0)
145 ifp
->metric
= ifreq
.ifr_metric
;
146 if (ifp
->metric
== 0)
148 #else /* SIOCGIFMETRIC */
150 #endif /* SIOCGIFMETRIC */
153 /* get interface MTU */
154 void if_get_mtu(struct interface
*ifp
)
156 struct ifreq ifreq
= {};
158 ifreq_set_name(&ifreq
, ifp
);
160 #if defined(SIOCGIFMTU)
161 if (vrf_if_ioctl(SIOCGIFMTU
, (caddr_t
)&ifreq
, ifp
->vrf
->vrf_id
) < 0) {
162 zlog_info("Can't lookup mtu by ioctl(SIOCGIFMTU)");
163 ifp
->mtu6
= ifp
->mtu
= -1;
167 ifp
->mtu6
= ifp
->mtu
= ifreq
.ifr_mtu
;
170 zebra_interface_up_update(ifp
);
173 zlog_info("Can't lookup mtu on this system");
174 ifp
->mtu6
= ifp
->mtu
= -1;
179 * Handler for interface address programming via the zebra dplane,
180 * for non-netlink platforms. This handler dispatches to per-platform
181 * helpers, based on the operation requested.
185 /* Prototypes: these are placed in this block so that they're only seen
186 * on non-netlink platforms.
188 static int if_set_prefix_ctx(const struct zebra_dplane_ctx
*ctx
);
189 static int if_unset_prefix_ctx(const struct zebra_dplane_ctx
*ctx
);
190 static int if_set_prefix6_ctx(const struct zebra_dplane_ctx
*ctx
);
191 static int if_unset_prefix6_ctx(const struct zebra_dplane_ctx
*ctx
);
193 enum zebra_dplane_result
kernel_address_update_ctx(
194 struct zebra_dplane_ctx
*ctx
)
197 const struct prefix
*p
;
199 p
= dplane_ctx_get_intf_addr(ctx
);
201 if (dplane_ctx_get_op(ctx
) == DPLANE_OP_ADDR_INSTALL
) {
202 if (p
->family
== AF_INET
)
203 ret
= if_set_prefix_ctx(ctx
);
205 ret
= if_set_prefix6_ctx(ctx
);
206 } else if (dplane_ctx_get_op(ctx
) == DPLANE_OP_ADDR_UNINSTALL
) {
207 if (p
->family
== AF_INET
)
208 ret
= if_unset_prefix_ctx(ctx
);
210 ret
= if_unset_prefix6_ctx(ctx
);
212 if (IS_ZEBRA_DEBUG_DPLANE
)
213 zlog_debug("Invalid op in interface-addr install");
217 ZEBRA_DPLANE_REQUEST_SUCCESS
: ZEBRA_DPLANE_REQUEST_FAILURE
);
220 #endif /* !HAVE_NETLINK */
224 /* TODO -- remove; no use of these apis with netlink any longer */
226 #else /* ! HAVE_NETLINK */
227 #ifdef HAVE_STRUCT_IFALIASREQ
230 * Helper for interface-addr install, non-netlink
232 static int if_set_prefix_ctx(const struct zebra_dplane_ctx
*ctx
)
235 struct ifaliasreq addreq
;
236 struct sockaddr_in addr
, mask
, peer
;
237 struct prefix_ipv4
*p
;
239 p
= (struct prefix_ipv4
*)dplane_ctx_get_intf_addr(ctx
);
241 memset(&addreq
, 0, sizeof(addreq
));
242 strlcpy((char *)&addreq
.ifra_name
, dplane_ctx_get_ifname(ctx
),
243 sizeof(addreq
.ifra_name
));
245 memset(&addr
, 0, sizeof(struct sockaddr_in
));
246 addr
.sin_addr
= p
->prefix
;
247 addr
.sin_family
= p
->family
;
248 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
249 addr
.sin_len
= sizeof(struct sockaddr_in
);
251 memcpy(&addreq
.ifra_addr
, &addr
, sizeof(struct sockaddr_in
));
253 if (dplane_ctx_intf_is_connected(ctx
)) {
254 p
= (struct prefix_ipv4
*)dplane_ctx_get_intf_dest(ctx
);
255 memset(&mask
, 0, sizeof(struct sockaddr_in
));
256 peer
.sin_addr
= p
->prefix
;
257 peer
.sin_family
= p
->family
;
258 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
259 peer
.sin_len
= sizeof(struct sockaddr_in
);
261 memcpy(&addreq
.ifra_broadaddr
, &peer
,
262 sizeof(struct sockaddr_in
));
265 memset(&mask
, 0, sizeof(struct sockaddr_in
));
266 masklen2ip(p
->prefixlen
, &mask
.sin_addr
);
267 mask
.sin_family
= p
->family
;
268 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
269 mask
.sin_len
= sizeof(struct sockaddr_in
);
271 memcpy(&addreq
.ifra_mask
, &mask
, sizeof(struct sockaddr_in
));
273 ret
= if_ioctl(SIOCAIFADDR
, (caddr_t
)&addreq
);
281 * Helper for interface-addr un-install, non-netlink
283 static int if_unset_prefix_ctx(const struct zebra_dplane_ctx
*ctx
)
286 struct ifaliasreq addreq
;
287 struct sockaddr_in addr
, mask
, peer
;
288 struct prefix_ipv4
*p
;
290 p
= (struct prefix_ipv4
*)dplane_ctx_get_intf_addr(ctx
);
292 memset(&addreq
, 0, sizeof(addreq
));
293 strlcpy((char *)&addreq
.ifra_name
, dplane_ctx_get_ifname(ctx
),
294 sizeof(addreq
.ifra_name
));
296 memset(&addr
, 0, sizeof(struct sockaddr_in
));
297 addr
.sin_addr
= p
->prefix
;
298 addr
.sin_family
= p
->family
;
299 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
300 addr
.sin_len
= sizeof(struct sockaddr_in
);
302 memcpy(&addreq
.ifra_addr
, &addr
, sizeof(struct sockaddr_in
));
304 if (dplane_ctx_intf_is_connected(ctx
)) {
305 p
= (struct prefix_ipv4
*)dplane_ctx_get_intf_dest(ctx
);
306 memset(&mask
, 0, sizeof(struct sockaddr_in
));
307 peer
.sin_addr
= p
->prefix
;
308 peer
.sin_family
= p
->family
;
309 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
310 peer
.sin_len
= sizeof(struct sockaddr_in
);
312 memcpy(&addreq
.ifra_broadaddr
, &peer
,
313 sizeof(struct sockaddr_in
));
316 memset(&mask
, 0, sizeof(struct sockaddr_in
));
317 masklen2ip(p
->prefixlen
, &mask
.sin_addr
);
318 mask
.sin_family
= p
->family
;
319 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
320 mask
.sin_len
= sizeof(struct sockaddr_in
);
322 memcpy(&addreq
.ifra_mask
, &mask
, sizeof(struct sockaddr_in
));
324 ret
= if_ioctl(SIOCDIFADDR
, (caddr_t
)&addreq
);
330 /* Set up interface's address, netmask (and broadcas? ). Linux or
331 Solaris uses ifname:number semantics to set IP address aliases. */
332 int if_set_prefix_ctx(const struct zebra_dplane_ctx
*ctx
)
336 struct sockaddr_in addr
;
337 struct sockaddr_in broad
;
338 struct sockaddr_in mask
;
339 struct prefix_ipv4 ifaddr
;
340 struct prefix_ipv4
*p
;
342 p
= (struct prefix_ipv4
*)dplane_ctx_get_intf_addr(ctx
);
346 strlcpy(ifreq
.ifr_name
, dplane_ctx_get_ifname(ctx
),
347 sizeof(ifreq
.ifr_name
));
349 addr
.sin_addr
= p
->prefix
;
350 addr
.sin_family
= p
->family
;
351 memcpy(&ifreq
.ifr_addr
, &addr
, sizeof(struct sockaddr_in
));
352 ret
= if_ioctl(SIOCSIFADDR
, (caddr_t
)&ifreq
);
356 /* We need mask for make broadcast addr. */
357 masklen2ip(p
->prefixlen
, &mask
.sin_addr
);
359 if (dplane_ctx_intf_is_broadcast(ctx
)) {
360 apply_mask_ipv4(&ifaddr
);
361 addr
.sin_addr
= ifaddr
.prefix
;
363 broad
.sin_addr
.s_addr
=
364 (addr
.sin_addr
.s_addr
| ~mask
.sin_addr
.s_addr
);
365 broad
.sin_family
= p
->family
;
367 memcpy(&ifreq
.ifr_broadaddr
, &broad
,
368 sizeof(struct sockaddr_in
));
369 ret
= if_ioctl(SIOCSIFBRDADDR
, (caddr_t
)&ifreq
);
374 mask
.sin_family
= p
->family
;
375 memcpy(&ifreq
.ifr_addr
, &mask
, sizeof(struct sockaddr_in
));
376 ret
= if_ioctl(SIOCSIFNETMASK
, (caddr_t
)&ifreq
);
383 /* Set up interface's address, netmask (and broadcas? ). Linux or
384 Solaris uses ifname:number semantics to set IP address aliases. */
385 int if_unset_prefix_ctx(const struct zebra_dplane_ctx
*ctx
)
389 struct sockaddr_in addr
;
390 struct prefix_ipv4
*p
;
392 p
= (struct prefix_ipv4
*)dplane_ctx_get_intf_addr(ctx
);
394 strlcpy(ifreq
.ifr_name
, dplane_ctx_get_ifname(ctx
),
395 sizeof(ifreq
.ifr_name
));
397 memset(&addr
, 0, sizeof(struct sockaddr_in
));
398 addr
.sin_family
= p
->family
;
399 memcpy(&ifreq
.ifr_addr
, &addr
, sizeof(struct sockaddr_in
));
400 ret
= if_ioctl(SIOCSIFADDR
, (caddr_t
)&ifreq
);
406 #endif /* HAVE_STRUCT_IFALIASREQ */
407 #endif /* HAVE_NETLINK */
409 /* get interface flags */
410 void if_get_flags(struct interface
*ifp
)
413 struct ifreq ifreqflags
= {};
414 struct ifreq ifreqdata
= {};
416 ifreq_set_name(&ifreqflags
, ifp
);
417 ifreq_set_name(&ifreqdata
, ifp
);
419 ret
= vrf_if_ioctl(SIOCGIFFLAGS
, (caddr_t
)&ifreqflags
,
422 flog_err_sys(EC_LIB_SYSTEM_CALL
,
423 "vrf_if_ioctl(SIOCGIFFLAGS %s) failed: %s",
424 ifp
->name
, safe_strerror(errno
));
428 if (!CHECK_FLAG(ifp
->status
, ZEBRA_INTERFACE_LINKDETECTION
))
431 /* Per-default, IFF_RUNNING is held high, unless link-detect
432 * says otherwise - we abuse IFF_RUNNING inside zebra as a
433 * link-state flag, following practice on Linux and Solaris
439 * BSD gets link state from ifi_link_link in struct if_data.
440 * All BSD's have this in getifaddrs(3) ifa_data for AF_LINK
441 * addresses. We can also access it via SIOCGIFDATA.
445 struct ifdatareq ifdr
= {.ifdr_data
.ifi_link_state
= 0};
446 struct if_data
*ifdata
= &ifdr
.ifdr_data
;
448 strlcpy(ifdr
.ifdr_name
, ifp
->name
, sizeof(ifdr
.ifdr_name
));
449 ret
= vrf_if_ioctl(SIOCGIFDATA
, (caddr_t
)&ifdr
, ifp
->vrf
->vrf_id
);
451 struct if_data ifd
= {.ifi_link_state
= 0};
452 struct if_data
*ifdata
= &ifd
;
454 ifreqdata
.ifr_data
= (caddr_t
)ifdata
;
455 ret
= vrf_if_ioctl(SIOCGIFDATA
, (caddr_t
)&ifreqdata
, ifp
->vrf
->vrf_id
);
459 /* Very unlikely. Did the interface disappear? */
460 flog_err_sys(EC_LIB_SYSTEM_CALL
,
461 "if_ioctl(SIOCGIFDATA %s) failed: %s", ifp
->name
,
462 safe_strerror(errno
));
464 if (ifdata
->ifi_link_state
>= LINK_STATE_UP
)
465 SET_FLAG(ifreqflags
.ifr_flags
, IFF_RUNNING
);
466 else if (ifdata
->ifi_link_state
== LINK_STATE_UNKNOWN
)
467 /* BSD traditionally treats UNKNOWN as UP */
468 SET_FLAG(ifreqflags
.ifr_flags
, IFF_RUNNING
);
470 UNSET_FLAG(ifreqflags
.ifr_flags
, IFF_RUNNING
);
473 #elif defined(HAVE_BSD_LINK_DETECT)
475 * This is only needed for FreeBSD older than FreeBSD-13.
476 * Valid and active media generally means the link state is
477 * up, but this is not always the case.
478 * For example, some BSD's with a net80211 interface in MONITOR
479 * mode will treat the media as valid and active but the
480 * link state is down - because we cannot send anything.
481 * Also, virtual interfaces such as PPP, VLAN, etc generally
482 * don't support media at all, so the ioctl will just fail.
484 struct ifmediareq ifmr
= {.ifm_status
= 0};
486 strlcpy(ifmr
.ifm_name
, ifp
->name
, sizeof(ifmr
.ifm_name
));
488 if (if_ioctl(SIOCGIFMEDIA
, (caddr_t
)&ifmr
) == -1) {
490 flog_err_sys(EC_LIB_SYSTEM_CALL
,
491 "if_ioctl(SIOCGIFMEDIA %s) failed: %s",
492 ifp
->name
, safe_strerror(errno
));
493 } else if (ifmr
.ifm_status
& IFM_AVALID
) { /* media state is valid */
494 if (ifmr
.ifm_status
& IFM_ACTIVE
) /* media is active */
495 SET_FLAG(ifreqflags
.ifr_flags
, IFF_RUNNING
);
497 UNSET_FLAG(ifreqflags
.ifr_flags
, IFF_RUNNING
);
499 #endif /* HAVE_BSD_LINK_DETECT */
502 if_flags_update(ifp
, (ifreqflags
.ifr_flags
& 0x0000ffff));
505 /* Set interface flags */
506 int if_set_flags(struct interface
*ifp
, uint64_t flags
)
511 memset(&ifreq
, 0, sizeof(struct ifreq
));
512 ifreq_set_name(&ifreq
, ifp
);
514 ifreq
.ifr_flags
= ifp
->flags
;
515 ifreq
.ifr_flags
|= flags
;
517 ret
= vrf_if_ioctl(SIOCSIFFLAGS
, (caddr_t
)&ifreq
, ifp
->vrf
->vrf_id
);
520 zlog_info("can't set interface flags");
526 /* Unset interface's flag. */
527 int if_unset_flags(struct interface
*ifp
, uint64_t flags
)
532 memset(&ifreq
, 0, sizeof(struct ifreq
));
533 ifreq_set_name(&ifreq
, ifp
);
535 ifreq
.ifr_flags
= ifp
->flags
;
536 ifreq
.ifr_flags
&= ~flags
;
538 ret
= vrf_if_ioctl(SIOCSIFFLAGS
, (caddr_t
)&ifreq
, ifp
->vrf
->vrf_id
);
541 zlog_info("can't unset interface flags");
547 #ifndef LINUX_IPV6 /* Netlink has its own code */
549 #ifdef HAVE_STRUCT_IN6_ALIASREQ
550 #ifndef ND6_INFINITE_LIFETIME
551 #define ND6_INFINITE_LIFETIME 0xffffffffL
552 #endif /* ND6_INFINITE_LIFETIME */
555 * Helper for interface-addr install, non-netlink
557 static int if_set_prefix6_ctx(const struct zebra_dplane_ctx
*ctx
)
560 struct in6_aliasreq addreq
;
561 struct sockaddr_in6 addr
;
562 struct sockaddr_in6 mask
;
563 struct prefix_ipv6
*p
;
565 p
= (struct prefix_ipv6
*)dplane_ctx_get_intf_addr(ctx
);
567 memset(&addreq
, 0, sizeof(addreq
));
568 strlcpy((char *)&addreq
.ifra_name
,
569 dplane_ctx_get_ifname(ctx
), sizeof(addreq
.ifra_name
));
571 memset(&addr
, 0, sizeof(struct sockaddr_in6
));
572 addr
.sin6_addr
= p
->prefix
;
573 addr
.sin6_family
= p
->family
;
574 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
575 addr
.sin6_len
= sizeof(struct sockaddr_in6
);
577 memcpy(&addreq
.ifra_addr
, &addr
, sizeof(struct sockaddr_in6
));
579 memset(&mask
, 0, sizeof(struct sockaddr_in6
));
580 masklen2ip6(p
->prefixlen
, &mask
.sin6_addr
);
581 mask
.sin6_family
= p
->family
;
582 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
583 mask
.sin6_len
= sizeof(struct sockaddr_in6
);
585 memcpy(&addreq
.ifra_prefixmask
, &mask
, sizeof(struct sockaddr_in6
));
587 addreq
.ifra_lifetime
.ia6t_vltime
= 0xffffffff;
588 addreq
.ifra_lifetime
.ia6t_pltime
= 0xffffffff;
590 #ifdef HAVE_STRUCT_IF6_ALIASREQ_IFRA_LIFETIME
591 addreq
.ifra_lifetime
.ia6t_pltime
= ND6_INFINITE_LIFETIME
;
592 addreq
.ifra_lifetime
.ia6t_vltime
= ND6_INFINITE_LIFETIME
;
595 ret
= if_ioctl_ipv6(SIOCAIFADDR_IN6
, (caddr_t
)&addreq
);
602 * Helper for interface-addr un-install, non-netlink
604 static int if_unset_prefix6_ctx(const struct zebra_dplane_ctx
*ctx
)
607 struct in6_aliasreq addreq
;
608 struct sockaddr_in6 addr
;
609 struct sockaddr_in6 mask
;
610 struct prefix_ipv6
*p
;
612 p
= (struct prefix_ipv6
*)dplane_ctx_get_intf_addr(ctx
);
614 memset(&addreq
, 0, sizeof(addreq
));
615 strlcpy((char *)&addreq
.ifra_name
,
616 dplane_ctx_get_ifname(ctx
), sizeof(addreq
.ifra_name
));
618 memset(&addr
, 0, sizeof(struct sockaddr_in6
));
619 addr
.sin6_addr
= p
->prefix
;
620 addr
.sin6_family
= p
->family
;
621 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
622 addr
.sin6_len
= sizeof(struct sockaddr_in6
);
624 memcpy(&addreq
.ifra_addr
, &addr
, sizeof(struct sockaddr_in6
));
626 memset(&mask
, 0, sizeof(struct sockaddr_in6
));
627 masklen2ip6(p
->prefixlen
, &mask
.sin6_addr
);
628 mask
.sin6_family
= p
->family
;
629 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
630 mask
.sin6_len
= sizeof(struct sockaddr_in6
);
632 memcpy(&addreq
.ifra_prefixmask
, &mask
, sizeof(struct sockaddr_in6
));
634 #ifdef HAVE_STRUCT_IF6_ALIASREQ_IFRA_LIFETIME
635 addreq
.ifra_lifetime
.ia6t_pltime
= ND6_INFINITE_LIFETIME
;
636 addreq
.ifra_lifetime
.ia6t_vltime
= ND6_INFINITE_LIFETIME
;
639 ret
= if_ioctl_ipv6(SIOCDIFADDR_IN6
, (caddr_t
)&addreq
);
645 /* The old, pre-dataplane code here just returned, so we're retaining that
648 static int if_set_prefix6_ctx(const struct zebra_dplane_ctx
*ctx
)
653 static int if_unset_prefix6_ctx(const struct zebra_dplane_ctx
*ctx
)
657 #endif /* HAVE_STRUCT_IN6_ALIASREQ */
659 #endif /* LINUX_IPV6 */