1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Common ioctl functions.
4 * Copyright (C) 1997, 98 Kunihiro Ishiguro
15 #include "lib_errors.h"
18 #include "zebra/rib.h"
20 #include "zebra/interface.h"
21 #include "zebra/zebra_errors.h"
22 #include "zebra/debug.h"
24 #ifdef HAVE_BSD_LINK_DETECT
25 #include <net/if_media.h>
26 #endif /* HAVE_BSD_LINK_DETECT*/
28 extern struct zebra_privs_t zserv_privs
;
30 /* clear and set interface name string */
31 void ifreq_set_name(struct ifreq
*ifreq
, struct interface
*ifp
)
33 strlcpy(ifreq
->ifr_name
, ifp
->name
, sizeof(ifreq
->ifr_name
));
37 /* call ioctl system call */
38 int if_ioctl(unsigned long request
, caddr_t buffer
)
44 frr_with_privs(&zserv_privs
) {
45 sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
47 zlog_err("Cannot create UDP socket: %s",
48 safe_strerror(errno
));
51 if ((ret
= ioctl(sock
, request
, buffer
)) < 0)
64 /* call ioctl system call */
65 int vrf_if_ioctl(unsigned long request
, caddr_t buffer
, vrf_id_t vrf_id
)
71 frr_with_privs(&zserv_privs
) {
72 sock
= vrf_socket(AF_INET
, SOCK_DGRAM
, 0, vrf_id
, NULL
);
74 zlog_err("Cannot create UDP socket: %s",
75 safe_strerror(errno
));
78 ret
= vrf_ioctl(vrf_id
, sock
, request
, buffer
);
92 static int if_ioctl_ipv6(unsigned long request
, caddr_t buffer
)
98 frr_with_privs(&zserv_privs
) {
99 sock
= socket(AF_INET6
, SOCK_DGRAM
, 0);
101 zlog_err("Cannot create IPv6 datagram socket: %s",
102 safe_strerror(errno
));
106 if ((ret
= ioctl(sock
, request
, buffer
)) < 0)
119 * get interface metric
120 * -- if value is not avaliable set -1
122 void if_get_metric(struct interface
*ifp
)
125 struct ifreq ifreq
= {};
127 ifreq_set_name(&ifreq
, ifp
);
129 if (vrf_if_ioctl(SIOCGIFMETRIC
, (caddr_t
)&ifreq
, ifp
->vrf
->vrf_id
) < 0)
131 ifp
->metric
= ifreq
.ifr_metric
;
132 if (ifp
->metric
== 0)
134 #else /* SIOCGIFMETRIC */
136 #endif /* SIOCGIFMETRIC */
139 /* get interface MTU */
140 void if_get_mtu(struct interface
*ifp
)
142 struct ifreq ifreq
= {};
144 ifreq_set_name(&ifreq
, ifp
);
146 #if defined(SIOCGIFMTU)
147 if (vrf_if_ioctl(SIOCGIFMTU
, (caddr_t
)&ifreq
, ifp
->vrf
->vrf_id
) < 0) {
148 zlog_info("Can't lookup mtu by ioctl(SIOCGIFMTU) for %s(%u)",
149 ifp
->name
, ifp
->vrf
->vrf_id
);
150 ifp
->mtu6
= ifp
->mtu
= -1;
154 ifp
->mtu6
= ifp
->mtu
= ifreq
.ifr_mtu
;
157 zebra_interface_up_update(ifp
);
160 zlog_info("Can't lookup mtu on this system for %s(%u)", ifp
->name
,
162 ifp
->mtu6
= ifp
->mtu
= -1;
165 #endif /* ! HAVE_NETLINK */
168 * Handler for interface address programming via the zebra dplane,
169 * for non-netlink platforms. This handler dispatches to per-platform
170 * helpers, based on the operation requested.
174 /* Prototypes: these are placed in this block so that they're only seen
175 * on non-netlink platforms.
177 static int if_set_prefix_ctx(const struct zebra_dplane_ctx
*ctx
);
178 static int if_unset_prefix_ctx(const struct zebra_dplane_ctx
*ctx
);
179 static int if_set_prefix6_ctx(const struct zebra_dplane_ctx
*ctx
);
180 static int if_unset_prefix6_ctx(const struct zebra_dplane_ctx
*ctx
);
182 enum zebra_dplane_result
kernel_address_update_ctx(
183 struct zebra_dplane_ctx
*ctx
)
186 const struct prefix
*p
;
188 p
= dplane_ctx_get_intf_addr(ctx
);
190 if (dplane_ctx_get_op(ctx
) == DPLANE_OP_ADDR_INSTALL
) {
191 if (p
->family
== AF_INET
)
192 ret
= if_set_prefix_ctx(ctx
);
194 ret
= if_set_prefix6_ctx(ctx
);
195 } else if (dplane_ctx_get_op(ctx
) == DPLANE_OP_ADDR_UNINSTALL
) {
196 if (p
->family
== AF_INET
)
197 ret
= if_unset_prefix_ctx(ctx
);
199 ret
= if_unset_prefix6_ctx(ctx
);
201 if (IS_ZEBRA_DEBUG_DPLANE
)
202 zlog_debug("Invalid op in interface-addr install");
206 ZEBRA_DPLANE_REQUEST_SUCCESS
: ZEBRA_DPLANE_REQUEST_FAILURE
);
209 #ifdef HAVE_STRUCT_IFALIASREQ
212 * Helper for interface-addr install, non-netlink
214 static int if_set_prefix_ctx(const struct zebra_dplane_ctx
*ctx
)
217 struct ifaliasreq addreq
;
218 struct sockaddr_in addr
, mask
, peer
;
219 struct prefix_ipv4
*p
;
221 p
= (struct prefix_ipv4
*)dplane_ctx_get_intf_addr(ctx
);
223 memset(&addreq
, 0, sizeof(addreq
));
224 strlcpy((char *)&addreq
.ifra_name
, dplane_ctx_get_ifname(ctx
),
225 sizeof(addreq
.ifra_name
));
227 memset(&addr
, 0, sizeof(addr
));
228 addr
.sin_addr
= p
->prefix
;
229 addr
.sin_family
= p
->family
;
230 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
231 addr
.sin_len
= sizeof(struct sockaddr_in
);
233 memcpy(&addreq
.ifra_addr
, &addr
, sizeof(struct sockaddr_in
));
235 if (dplane_ctx_intf_is_connected(ctx
)) {
236 p
= (struct prefix_ipv4
*)dplane_ctx_get_intf_dest(ctx
);
237 memset(&mask
, 0, sizeof(mask
));
238 peer
.sin_addr
= p
->prefix
;
239 peer
.sin_family
= p
->family
;
240 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
241 peer
.sin_len
= sizeof(struct sockaddr_in
);
243 memcpy(&addreq
.ifra_broadaddr
, &peer
,
244 sizeof(struct sockaddr_in
));
247 memset(&mask
, 0, sizeof(mask
));
248 masklen2ip(p
->prefixlen
, &mask
.sin_addr
);
249 mask
.sin_family
= p
->family
;
250 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
251 mask
.sin_len
= sizeof(struct sockaddr_in
);
253 memcpy(&addreq
.ifra_mask
, &mask
, sizeof(struct sockaddr_in
));
255 ret
= if_ioctl(SIOCAIFADDR
, (caddr_t
)&addreq
);
263 * Helper for interface-addr un-install, non-netlink
265 static int if_unset_prefix_ctx(const struct zebra_dplane_ctx
*ctx
)
268 struct ifaliasreq addreq
;
269 struct sockaddr_in addr
, mask
, peer
;
270 struct prefix_ipv4
*p
;
272 p
= (struct prefix_ipv4
*)dplane_ctx_get_intf_addr(ctx
);
274 memset(&addreq
, 0, sizeof(addreq
));
275 strlcpy((char *)&addreq
.ifra_name
, dplane_ctx_get_ifname(ctx
),
276 sizeof(addreq
.ifra_name
));
278 memset(&addr
, 0, sizeof(addr
));
279 addr
.sin_addr
= p
->prefix
;
280 addr
.sin_family
= p
->family
;
281 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
282 addr
.sin_len
= sizeof(struct sockaddr_in
);
284 memcpy(&addreq
.ifra_addr
, &addr
, sizeof(struct sockaddr_in
));
286 if (dplane_ctx_intf_is_connected(ctx
)) {
287 p
= (struct prefix_ipv4
*)dplane_ctx_get_intf_dest(ctx
);
288 memset(&mask
, 0, sizeof(mask
));
289 peer
.sin_addr
= p
->prefix
;
290 peer
.sin_family
= p
->family
;
291 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
292 peer
.sin_len
= sizeof(struct sockaddr_in
);
294 memcpy(&addreq
.ifra_broadaddr
, &peer
,
295 sizeof(struct sockaddr_in
));
298 memset(&mask
, 0, sizeof(mask
));
299 masklen2ip(p
->prefixlen
, &mask
.sin_addr
);
300 mask
.sin_family
= p
->family
;
301 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
302 mask
.sin_len
= sizeof(struct sockaddr_in
);
304 memcpy(&addreq
.ifra_mask
, &mask
, sizeof(struct sockaddr_in
));
306 ret
= if_ioctl(SIOCDIFADDR
, (caddr_t
)&addreq
);
312 /* Set up interface's address, netmask (and broadcas? ). Linux or
313 Solaris uses ifname:number semantics to set IP address aliases. */
314 int if_set_prefix_ctx(const struct zebra_dplane_ctx
*ctx
)
318 struct sockaddr_in addr
;
319 struct sockaddr_in broad
;
320 struct sockaddr_in mask
;
321 struct prefix_ipv4 ifaddr
;
322 struct prefix_ipv4
*p
;
324 p
= (struct prefix_ipv4
*)dplane_ctx_get_intf_addr(ctx
);
328 strlcpy(ifreq
.ifr_name
, dplane_ctx_get_ifname(ctx
),
329 sizeof(ifreq
.ifr_name
));
331 addr
.sin_addr
= p
->prefix
;
332 addr
.sin_family
= p
->family
;
333 memcpy(&ifreq
.ifr_addr
, &addr
, sizeof(struct sockaddr_in
));
334 ret
= if_ioctl(SIOCSIFADDR
, (caddr_t
)&ifreq
);
338 /* We need mask for make broadcast addr. */
339 masklen2ip(p
->prefixlen
, &mask
.sin_addr
);
341 if (dplane_ctx_intf_is_broadcast(ctx
)) {
342 apply_mask_ipv4(&ifaddr
);
343 addr
.sin_addr
= ifaddr
.prefix
;
345 broad
.sin_addr
.s_addr
=
346 (addr
.sin_addr
.s_addr
| ~mask
.sin_addr
.s_addr
);
347 broad
.sin_family
= p
->family
;
349 memcpy(&ifreq
.ifr_broadaddr
, &broad
,
350 sizeof(struct sockaddr_in
));
351 ret
= if_ioctl(SIOCSIFBRDADDR
, (caddr_t
)&ifreq
);
356 mask
.sin_family
= p
->family
;
357 memcpy(&ifreq
.ifr_addr
, &mask
, sizeof(struct sockaddr_in
));
358 ret
= if_ioctl(SIOCSIFNETMASK
, (caddr_t
)&ifreq
);
365 /* Set up interface's address, netmask (and broadcas? ). Linux or
366 Solaris uses ifname:number semantics to set IP address aliases. */
367 int if_unset_prefix_ctx(const struct zebra_dplane_ctx
*ctx
)
371 struct sockaddr_in addr
;
372 struct prefix_ipv4
*p
;
374 p
= (struct prefix_ipv4
*)dplane_ctx_get_intf_addr(ctx
);
376 strlcpy(ifreq
.ifr_name
, dplane_ctx_get_ifname(ctx
),
377 sizeof(ifreq
.ifr_name
));
379 memset(&addr
, 0, sizeof(addr
));
380 addr
.sin_family
= p
->family
;
381 memcpy(&ifreq
.ifr_addr
, &addr
, sizeof(struct sockaddr_in
));
382 ret
= if_ioctl(SIOCSIFADDR
, (caddr_t
)&ifreq
);
388 #endif /* HAVE_STRUCT_IFALIASREQ */
389 #endif /* HAVE_NETLINK */
391 /* get interface flags */
392 void if_get_flags(struct interface
*ifp
)
395 struct ifreq ifreqflags
= {};
396 struct ifreq ifreqdata
= {};
398 ifreq_set_name(&ifreqflags
, ifp
);
399 ifreq_set_name(&ifreqdata
, ifp
);
401 ret
= vrf_if_ioctl(SIOCGIFFLAGS
, (caddr_t
)&ifreqflags
,
404 flog_err_sys(EC_LIB_SYSTEM_CALL
,
405 "vrf_if_ioctl(SIOCGIFFLAGS %s) failed: %s",
406 ifp
->name
, safe_strerror(errno
));
410 if (!CHECK_FLAG(ifp
->status
, ZEBRA_INTERFACE_LINKDETECTION
))
413 /* Per-default, IFF_RUNNING is held high, unless link-detect
414 * says otherwise - we abuse IFF_RUNNING inside zebra as a
415 * link-state flag, following practice on Linux and Solaris
421 * BSD gets link state from ifi_link_link in struct if_data.
422 * All BSD's have this in getifaddrs(3) ifa_data for AF_LINK
423 * addresses. We can also access it via SIOCGIFDATA.
427 struct ifdatareq ifdr
= {.ifdr_data
.ifi_link_state
= 0};
428 struct if_data
*ifdata
= &ifdr
.ifdr_data
;
430 strlcpy(ifdr
.ifdr_name
, ifp
->name
, sizeof(ifdr
.ifdr_name
));
431 ret
= vrf_if_ioctl(SIOCGIFDATA
, (caddr_t
)&ifdr
, ifp
->vrf
->vrf_id
);
433 struct if_data ifd
= {.ifi_link_state
= 0};
434 struct if_data
*ifdata
= &ifd
;
436 ifreqdata
.ifr_data
= (caddr_t
)ifdata
;
437 ret
= vrf_if_ioctl(SIOCGIFDATA
, (caddr_t
)&ifreqdata
, ifp
->vrf
->vrf_id
);
441 /* Very unlikely. Did the interface disappear? */
442 flog_err_sys(EC_LIB_SYSTEM_CALL
,
443 "if_ioctl(SIOCGIFDATA %s) failed: %s", ifp
->name
,
444 safe_strerror(errno
));
446 if (ifdata
->ifi_link_state
>= LINK_STATE_UP
)
447 SET_FLAG(ifreqflags
.ifr_flags
, IFF_RUNNING
);
448 else if (ifdata
->ifi_link_state
== LINK_STATE_UNKNOWN
)
449 /* BSD traditionally treats UNKNOWN as UP */
450 SET_FLAG(ifreqflags
.ifr_flags
, IFF_RUNNING
);
452 UNSET_FLAG(ifreqflags
.ifr_flags
, IFF_RUNNING
);
455 #elif defined(HAVE_BSD_LINK_DETECT)
457 * This is only needed for FreeBSD older than FreeBSD-13.
458 * Valid and active media generally means the link state is
459 * up, but this is not always the case.
460 * For example, some BSD's with a net80211 interface in MONITOR
461 * mode will treat the media as valid and active but the
462 * link state is down - because we cannot send anything.
463 * Also, virtual interfaces such as PPP, VLAN, etc generally
464 * don't support media at all, so the ioctl will just fail.
466 struct ifmediareq ifmr
= {.ifm_status
= 0};
468 strlcpy(ifmr
.ifm_name
, ifp
->name
, sizeof(ifmr
.ifm_name
));
470 if (if_ioctl(SIOCGIFMEDIA
, (caddr_t
)&ifmr
) == -1) {
472 flog_err_sys(EC_LIB_SYSTEM_CALL
,
473 "if_ioctl(SIOCGIFMEDIA %s) failed: %s",
474 ifp
->name
, safe_strerror(errno
));
475 } else if (ifmr
.ifm_status
& IFM_AVALID
) { /* media state is valid */
476 if (ifmr
.ifm_status
& IFM_ACTIVE
) /* media is active */
477 SET_FLAG(ifreqflags
.ifr_flags
, IFF_RUNNING
);
479 UNSET_FLAG(ifreqflags
.ifr_flags
, IFF_RUNNING
);
481 #endif /* HAVE_BSD_LINK_DETECT */
484 if_flags_update(ifp
, (ifreqflags
.ifr_flags
& 0x0000ffff));
487 /* Set interface flags */
488 int if_set_flags(struct interface
*ifp
, uint64_t flags
)
493 memset(&ifreq
, 0, sizeof(ifreq
));
494 ifreq_set_name(&ifreq
, ifp
);
496 ifreq
.ifr_flags
= ifp
->flags
;
497 ifreq
.ifr_flags
|= flags
;
499 ret
= vrf_if_ioctl(SIOCSIFFLAGS
, (caddr_t
)&ifreq
, ifp
->vrf
->vrf_id
);
502 zlog_info("can't set interface %s(%u) flags %" PRIu64
,
503 ifp
->name
, ifp
->vrf
->vrf_id
, flags
);
509 /* Unset interface's flag. */
510 int if_unset_flags(struct interface
*ifp
, uint64_t flags
)
515 memset(&ifreq
, 0, sizeof(ifreq
));
516 ifreq_set_name(&ifreq
, ifp
);
518 ifreq
.ifr_flags
= ifp
->flags
;
519 ifreq
.ifr_flags
&= ~flags
;
521 ret
= vrf_if_ioctl(SIOCSIFFLAGS
, (caddr_t
)&ifreq
, ifp
->vrf
->vrf_id
);
524 zlog_warn("can't unset interface %s(%u) flags %" PRIu64
,
525 ifp
->name
, ifp
->vrf
->vrf_id
, flags
);
531 #ifndef LINUX_IPV6 /* Netlink has its own code */
533 #ifdef HAVE_STRUCT_IN6_ALIASREQ
534 #ifndef ND6_INFINITE_LIFETIME
535 #define ND6_INFINITE_LIFETIME 0xffffffffL
536 #endif /* ND6_INFINITE_LIFETIME */
539 * Helper for interface-addr install, non-netlink
541 static int if_set_prefix6_ctx(const struct zebra_dplane_ctx
*ctx
)
544 struct in6_aliasreq addreq
;
545 struct sockaddr_in6 addr
;
546 struct sockaddr_in6 mask
;
547 struct prefix_ipv6
*p
;
549 p
= (struct prefix_ipv6
*)dplane_ctx_get_intf_addr(ctx
);
551 memset(&addreq
, 0, sizeof(addreq
));
552 strlcpy((char *)&addreq
.ifra_name
,
553 dplane_ctx_get_ifname(ctx
), sizeof(addreq
.ifra_name
));
555 memset(&addr
, 0, sizeof(addr
));
556 addr
.sin6_addr
= p
->prefix
;
557 addr
.sin6_family
= p
->family
;
558 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
559 addr
.sin6_len
= sizeof(struct sockaddr_in6
);
561 memcpy(&addreq
.ifra_addr
, &addr
, sizeof(struct sockaddr_in6
));
563 memset(&mask
, 0, sizeof(mask
));
564 masklen2ip6(p
->prefixlen
, &mask
.sin6_addr
);
565 mask
.sin6_family
= p
->family
;
566 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
567 mask
.sin6_len
= sizeof(struct sockaddr_in6
);
569 memcpy(&addreq
.ifra_prefixmask
, &mask
, sizeof(struct sockaddr_in6
));
571 addreq
.ifra_lifetime
.ia6t_vltime
= 0xffffffff;
572 addreq
.ifra_lifetime
.ia6t_pltime
= 0xffffffff;
574 #ifdef HAVE_STRUCT_IF6_ALIASREQ_IFRA_LIFETIME
575 addreq
.ifra_lifetime
.ia6t_pltime
= ND6_INFINITE_LIFETIME
;
576 addreq
.ifra_lifetime
.ia6t_vltime
= ND6_INFINITE_LIFETIME
;
579 ret
= if_ioctl_ipv6(SIOCAIFADDR_IN6
, (caddr_t
)&addreq
);
586 * Helper for interface-addr un-install, non-netlink
588 static int if_unset_prefix6_ctx(const struct zebra_dplane_ctx
*ctx
)
591 struct in6_aliasreq addreq
;
592 struct sockaddr_in6 addr
;
593 struct sockaddr_in6 mask
;
594 struct prefix_ipv6
*p
;
596 p
= (struct prefix_ipv6
*)dplane_ctx_get_intf_addr(ctx
);
598 memset(&addreq
, 0, sizeof(addreq
));
599 strlcpy((char *)&addreq
.ifra_name
,
600 dplane_ctx_get_ifname(ctx
), sizeof(addreq
.ifra_name
));
602 memset(&addr
, 0, sizeof(addr
));
603 addr
.sin6_addr
= p
->prefix
;
604 addr
.sin6_family
= p
->family
;
605 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
606 addr
.sin6_len
= sizeof(struct sockaddr_in6
);
608 memcpy(&addreq
.ifra_addr
, &addr
, sizeof(struct sockaddr_in6
));
610 memset(&mask
, 0, sizeof(mask
));
611 masklen2ip6(p
->prefixlen
, &mask
.sin6_addr
);
612 mask
.sin6_family
= p
->family
;
613 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
614 mask
.sin6_len
= sizeof(struct sockaddr_in6
);
616 memcpy(&addreq
.ifra_prefixmask
, &mask
, sizeof(struct sockaddr_in6
));
618 #ifdef HAVE_STRUCT_IF6_ALIASREQ_IFRA_LIFETIME
619 addreq
.ifra_lifetime
.ia6t_pltime
= ND6_INFINITE_LIFETIME
;
620 addreq
.ifra_lifetime
.ia6t_vltime
= ND6_INFINITE_LIFETIME
;
623 ret
= if_ioctl_ipv6(SIOCDIFADDR_IN6
, (caddr_t
)&addreq
);
629 /* The old, pre-dataplane code here just returned, so we're retaining that
632 static int if_set_prefix6_ctx(const struct zebra_dplane_ctx
*ctx
)
637 static int if_unset_prefix6_ctx(const struct zebra_dplane_ctx
*ctx
)
641 #endif /* HAVE_STRUCT_IN6_ALIASREQ */
643 #endif /* LINUX_IPV6 */