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