1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * netconf_netlink.c - netconf interaction with the kernel using
5 * Copyright (C) 2021 Nvidia, Inc.
10 #ifdef HAVE_NETLINK /* Netlink OSes only */
14 #include "linux/netconf.h"
16 #include "lib/lib_errors.h"
17 #include "zebra/zebra_ns.h"
18 #include "zebra/zebra_dplane.h"
19 #include "zebra/kernel_netlink.h"
20 #include "zebra/netconf_netlink.h"
21 #include "zebra/debug.h"
23 static struct rtattr
*netconf_rta(struct netconfmsg
*ncm
)
25 return (struct rtattr
*)((char *)ncm
+
26 NLMSG_ALIGN(sizeof(struct netconfmsg
)));
30 * Handle netconf update about a single interface: create dplane
31 * context, and enqueue for processing in the main zebra pthread.
34 netlink_netconf_dplane_update(ns_id_t ns_id
, afi_t afi
, ifindex_t ifindex
,
35 enum dplane_netconf_status_e mpls_on
,
36 enum dplane_netconf_status_e mcast_on
,
37 enum dplane_netconf_status_e linkdown_on
)
39 struct zebra_dplane_ctx
*ctx
;
41 ctx
= dplane_ctx_alloc();
42 dplane_ctx_set_op(ctx
, DPLANE_OP_INTF_NETCONFIG
);
43 dplane_ctx_set_ns_id(ctx
, ns_id
);
44 dplane_ctx_set_afi(ctx
, afi
);
45 dplane_ctx_set_ifindex(ctx
, ifindex
);
47 dplane_ctx_set_netconf_mpls(ctx
, mpls_on
);
48 dplane_ctx_set_netconf_mcast(ctx
, mcast_on
);
49 dplane_ctx_set_netconf_linkdown(ctx
, linkdown_on
);
51 /* Enqueue ctx for main pthread to process */
52 dplane_provider_enqueue_to_zebra(ctx
);
58 * Parse and process an incoming netlink netconf update.
60 int netlink_netconf_change(struct nlmsghdr
*h
, ns_id_t ns_id
, int startup
)
62 struct netconfmsg
*ncm
;
63 struct rtattr
*tb
[NETCONFA_MAX
+ 1] = {};
68 enum dplane_netconf_status_e mpls_on
= DPLANE_NETCONF_STATUS_UNKNOWN
;
69 enum dplane_netconf_status_e mcast_on
= DPLANE_NETCONF_STATUS_UNKNOWN
;
70 enum dplane_netconf_status_e linkdown_on
=
71 DPLANE_NETCONF_STATUS_UNKNOWN
;
73 if (h
->nlmsg_type
!= RTM_NEWNETCONF
&& h
->nlmsg_type
!= RTM_DELNETCONF
)
76 len
= h
->nlmsg_len
- NLMSG_LENGTH(sizeof(struct netconfmsg
));
78 zlog_err("%s: Message received from netlink is of a broken size: %d, min %zu",
79 __func__
, h
->nlmsg_len
,
80 (size_t)NLMSG_LENGTH(sizeof(struct netconfmsg
)));
87 * FRR does not have an internal representation of afi_t for
88 * the MPLS Address Family that the kernel has. So let's
89 * just call it v4. This is ok because the kernel appears
90 * to do a good job of not sending data that is mixed/matched
94 if (ncm
->ncm_family
== AF_MPLS
)
98 afi
= family2afi(ncm
->ncm_family
);
100 netlink_parse_rtattr(tb
, NETCONFA_MAX
, netconf_rta(ncm
), len
);
102 if (!tb
[NETCONFA_IFINDEX
]) {
103 zlog_err("NETCONF message received from netlink without an ifindex");
107 ifindex
= *(ifindex_t
*)RTA_DATA(tb
[NETCONFA_IFINDEX
]);
109 if (tb
[NETCONFA_INPUT
]) {
110 ival
= *(uint32_t *)RTA_DATA(tb
[NETCONFA_INPUT
]);
112 mpls_on
= DPLANE_NETCONF_STATUS_ENABLED
;
114 mpls_on
= DPLANE_NETCONF_STATUS_DISABLED
;
117 if (tb
[NETCONFA_MC_FORWARDING
]) {
118 ival
= *(uint32_t *)RTA_DATA(tb
[NETCONFA_MC_FORWARDING
]);
120 mcast_on
= DPLANE_NETCONF_STATUS_ENABLED
;
122 mcast_on
= DPLANE_NETCONF_STATUS_DISABLED
;
125 if (tb
[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN
]) {
126 ival
= *(uint32_t *)RTA_DATA(
127 tb
[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN
]);
129 linkdown_on
= DPLANE_NETCONF_STATUS_ENABLED
;
131 linkdown_on
= DPLANE_NETCONF_STATUS_DISABLED
;
134 if (IS_ZEBRA_DEBUG_KERNEL
)
136 "%s: interface %u is mpls on: %d multicast on: %d linkdown: %d",
137 __func__
, ifindex
, mpls_on
, mcast_on
, linkdown_on
);
139 /* Create a dplane context and pass it along for processing */
140 netlink_netconf_dplane_update(ns_id
, afi
, ifindex
, mpls_on
, mcast_on
,
147 * Request info from the host OS. This only sends the request; any replies
148 * are processed asynchronously.
150 int netlink_request_netconf(int sockfd
)
155 struct netconfmsg ncm
;
159 nls
= kernel_netlink_nlsock_lookup(sockfd
);
161 if (IS_ZEBRA_DEBUG_KERNEL
)
162 zlog_debug("%s: nlsock %s", __func__
, nls
? nls
->name
: "NULL");
167 req
.n
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct netconfmsg
));
168 req
.n
.nlmsg_flags
= NLM_F_DUMP
| NLM_F_REQUEST
;
169 req
.n
.nlmsg_type
= RTM_GETNETCONF
;
170 req
.ncm
.ncm_family
= AF_UNSPEC
;
172 return netlink_request(nls
, &req
);
175 extern struct zebra_privs_t zserv_privs
;
177 * Currently netconf has no ability to set from netlink.
178 * So we've received a request to do this work in the data plane.
179 * as such we need to set the value via the /proc system
181 enum netlink_msg_status
netlink_put_intf_netconfig(struct nl_batch
*bth
,
182 struct zebra_dplane_ctx
*ctx
)
184 const char *ifname
= dplane_ctx_get_ifname(ctx
);
185 enum dplane_netconf_status_e mpls_on
= dplane_ctx_get_netconf_mpls(ctx
);
187 char mpls_proc
[PATH_MAX
];
188 int fd
, ret
= FRR_NETLINK_ERROR
;
190 snprintf(mpls_proc
, sizeof(mpls_proc
),
191 "/proc/sys/net/mpls/conf/%s/input", ifname
);
193 if (mpls_on
== DPLANE_NETCONF_STATUS_ENABLED
)
194 snprintf(set
, sizeof(set
), "1\n");
195 else if (mpls_on
== DPLANE_NETCONF_STATUS_DISABLED
)
196 snprintf(set
, sizeof(set
), "0\n");
200 "%s: Expected interface %s to be set to ENABLED or DISABLED was %d",
201 __func__
, ifname
, mpls_on
);
205 frr_with_privs (&zserv_privs
) {
206 fd
= open(mpls_proc
, O_WRONLY
);
210 "%s: Unable to open %s for writing: %s(%d)",
211 __func__
, mpls_proc
, safe_strerror(errno
),
215 if (write(fd
, set
, 2) == 2)
216 ret
= FRR_NETLINK_SUCCESS
;
218 flog_err_sys(EC_LIB_SOCKET
,
219 "%s: Unsuccessful write to %s: %s(%d)",
220 __func__
, mpls_proc
, safe_strerror(errno
),
227 #endif /* HAVE_NETLINK */