]> git.proxmox.com Git - mirror_frr.git/blame - zebra/netconf_netlink.c
Merge pull request #12798 from donaldsharp/rib_match_multicast
[mirror_frr.git] / zebra / netconf_netlink.c
CommitLineData
acddc0ed 1// SPDX-License-Identifier: GPL-2.0-or-later
ebb61fca
DS
2/*
3 * netconf_netlink.c - netconf interaction with the kernel using
4 * netlink
5 * Copyright (C) 2021 Nvidia, Inc.
6 * Donald Sharp
ebb61fca
DS
7 */
8#include <zebra.h>
9
10#ifdef HAVE_NETLINK /* Netlink OSes only */
11
12#include <ns.h>
13
14#include "linux/netconf.h"
15
39ffa8e8 16#include "lib/lib_errors.h"
ebb61fca
DS
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"
22
23static struct rtattr *netconf_rta(struct netconfmsg *ncm)
24{
cd787a8a
MS
25 return (struct rtattr *)((char *)ncm +
26 NLMSG_ALIGN(sizeof(struct netconfmsg)));
ebb61fca
DS
27}
28
cd787a8a
MS
29/*
30 * Handle netconf update about a single interface: create dplane
31 * context, and enqueue for processing in the main zebra pthread.
32 */
3689905d 33static int
d53dc9bd 34netlink_netconf_dplane_update(ns_id_t ns_id, afi_t afi, ifindex_t ifindex,
3689905d
DS
35 enum dplane_netconf_status_e mpls_on,
36 enum dplane_netconf_status_e mcast_on,
37 enum dplane_netconf_status_e linkdown_on)
cd787a8a
MS
38{
39 struct zebra_dplane_ctx *ctx;
40
41 ctx = dplane_ctx_alloc();
42 dplane_ctx_set_op(ctx, DPLANE_OP_INTF_NETCONFIG);
2b9dc841 43 dplane_ctx_set_ns_id(ctx, ns_id);
d53dc9bd 44 dplane_ctx_set_afi(ctx, afi);
2b9dc841 45 dplane_ctx_set_ifindex(ctx, ifindex);
cd787a8a
MS
46
47 dplane_ctx_set_netconf_mpls(ctx, mpls_on);
48 dplane_ctx_set_netconf_mcast(ctx, mcast_on);
3689905d 49 dplane_ctx_set_netconf_linkdown(ctx, linkdown_on);
cd787a8a
MS
50
51 /* Enqueue ctx for main pthread to process */
52 dplane_provider_enqueue_to_zebra(ctx);
53
54 return 0;
55}
56
57/*
58 * Parse and process an incoming netlink netconf update.
59 */
ebb61fca
DS
60int netlink_netconf_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
61{
62 struct netconfmsg *ncm;
63 struct rtattr *tb[NETCONFA_MAX + 1] = {};
64 int len;
65 ifindex_t ifindex;
cd787a8a 66 uint32_t ival;
d53dc9bd 67 afi_t afi;
cd787a8a
MS
68 enum dplane_netconf_status_e mpls_on = DPLANE_NETCONF_STATUS_UNKNOWN;
69 enum dplane_netconf_status_e mcast_on = DPLANE_NETCONF_STATUS_UNKNOWN;
3689905d
DS
70 enum dplane_netconf_status_e linkdown_on =
71 DPLANE_NETCONF_STATUS_UNKNOWN;
ebb61fca
DS
72
73 if (h->nlmsg_type != RTM_NEWNETCONF && h->nlmsg_type != RTM_DELNETCONF)
74 return 0;
75
76 len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct netconfmsg));
77 if (len < 0) {
cd787a8a 78 zlog_err("%s: Message received from netlink is of a broken size: %d, min %zu",
ebb61fca
DS
79 __func__, h->nlmsg_len,
80 (size_t)NLMSG_LENGTH(sizeof(struct netconfmsg)));
81 return -1;
82 }
83
84 ncm = NLMSG_DATA(h);
85
d53dc9bd
DS
86 /*
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
91 * across families
92 */
fa60f2c2 93#ifdef AF_MPLS
d53dc9bd
DS
94 if (ncm->ncm_family == AF_MPLS)
95 afi = AFI_IP;
96 else
fa60f2c2 97#endif /* AF_MPLS */
d53dc9bd
DS
98 afi = family2afi(ncm->ncm_family);
99
ebb61fca
DS
100 netlink_parse_rtattr(tb, NETCONFA_MAX, netconf_rta(ncm), len);
101
102 if (!tb[NETCONFA_IFINDEX]) {
cd787a8a 103 zlog_err("NETCONF message received from netlink without an ifindex");
ebb61fca
DS
104 return 0;
105 }
106
107 ifindex = *(ifindex_t *)RTA_DATA(tb[NETCONFA_IFINDEX]);
108
ebb61fca 109 if (tb[NETCONFA_INPUT]) {
cd787a8a
MS
110 ival = *(uint32_t *)RTA_DATA(tb[NETCONFA_INPUT]);
111 if (ival != 0)
112 mpls_on = DPLANE_NETCONF_STATUS_ENABLED;
113 else
114 mpls_on = DPLANE_NETCONF_STATUS_DISABLED;
ebb61fca
DS
115 }
116
117 if (tb[NETCONFA_MC_FORWARDING]) {
cd787a8a
MS
118 ival = *(uint32_t *)RTA_DATA(tb[NETCONFA_MC_FORWARDING]);
119 if (ival != 0)
120 mcast_on = DPLANE_NETCONF_STATUS_ENABLED;
121 else
122 mcast_on = DPLANE_NETCONF_STATUS_DISABLED;
ebb61fca
DS
123 }
124
3689905d
DS
125 if (tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]) {
126 ival = *(uint32_t *)RTA_DATA(
127 tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]);
128 if (ival != 0)
129 linkdown_on = DPLANE_NETCONF_STATUS_ENABLED;
130 else
131 linkdown_on = DPLANE_NETCONF_STATUS_DISABLED;
132 }
133
ebb61fca 134 if (IS_ZEBRA_DEBUG_KERNEL)
3689905d
DS
135 zlog_debug(
136 "%s: interface %u is mpls on: %d multicast on: %d linkdown: %d",
137 __func__, ifindex, mpls_on, mcast_on, linkdown_on);
cd787a8a
MS
138
139 /* Create a dplane context and pass it along for processing */
d53dc9bd 140 netlink_netconf_dplane_update(ns_id, afi, ifindex, mpls_on, mcast_on,
3689905d 141 linkdown_on);
ebb61fca
DS
142
143 return 0;
144}
145
cd787a8a
MS
146/*
147 * Request info from the host OS. This only sends the request; any replies
148 * are processed asynchronously.
149 */
150int netlink_request_netconf(int sockfd)
ebb61fca 151{
cd787a8a 152 struct nlsock *nls;
ebb61fca
DS
153 struct {
154 struct nlmsghdr n;
155 struct netconfmsg ncm;
156 char buf[1024];
cd787a8a
MS
157 } req = {};
158
159 nls = kernel_netlink_nlsock_lookup(sockfd);
160
161 if (IS_ZEBRA_DEBUG_KERNEL)
162 zlog_debug("%s: nlsock %s", __func__, nls ? nls->name : "NULL");
163
164 if (nls == NULL)
165 return -1;
ebb61fca 166
ebb61fca
DS
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;
171
cd787a8a 172 return netlink_request(nls, &req);
ebb61fca
DS
173}
174
39ffa8e8
DS
175extern struct zebra_privs_t zserv_privs;
176/*
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
180 */
181enum netlink_msg_status netlink_put_intf_netconfig(struct nl_batch *bth,
182 struct zebra_dplane_ctx *ctx)
183{
184 const char *ifname = dplane_ctx_get_ifname(ctx);
185 enum dplane_netconf_status_e mpls_on = dplane_ctx_get_netconf_mpls(ctx);
186 char set[64];
187 char mpls_proc[PATH_MAX];
188 int fd, ret = FRR_NETLINK_ERROR;
189
190 snprintf(mpls_proc, sizeof(mpls_proc),
191 "/proc/sys/net/mpls/conf/%s/input", ifname);
192
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");
197 else {
198 flog_err_sys(
199 EC_LIB_DEVELOPMENT,
200 "%s: Expected interface %s to be set to ENABLED or DISABLED was %d",
201 __func__, ifname, mpls_on);
202 return ret;
203 }
204
205 frr_with_privs (&zserv_privs) {
206 fd = open(mpls_proc, O_WRONLY);
207 if (fd < 0) {
208 flog_err_sys(
209 EC_LIB_SOCKET,
210 "%s: Unable to open %s for writing: %s(%d)",
211 __func__, mpls_proc, safe_strerror(errno),
212 errno);
213 return ret;
214 }
215 if (write(fd, set, 2) == 2)
216 ret = FRR_NETLINK_SUCCESS;
217 else
218 flog_err_sys(EC_LIB_SOCKET,
219 "%s: Unsuccessful write to %s: %s(%d)",
220 __func__, mpls_proc, safe_strerror(errno),
221 errno);
222 close(fd);
223 }
224 return ret;
225}
226
ebb61fca 227#endif /* HAVE_NETLINK */