1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (C) B.A.T.M.A.N. contributors: |
3 | * |
4 | * Matthias Schiffer |
5 | */ |
6 | |
7 | #include "netlink.h" |
8 | #include "main.h" |
9 | |
10 | #include <linux/array_size.h> |
11 | #include <linux/atomic.h> |
12 | #include <linux/bitops.h> |
13 | #include <linux/bug.h> |
14 | #include <linux/byteorder/generic.h> |
15 | #include <linux/cache.h> |
16 | #include <linux/err.h> |
17 | #include <linux/errno.h> |
18 | #include <linux/genetlink.h> |
19 | #include <linux/gfp.h> |
20 | #include <linux/if_ether.h> |
21 | #include <linux/if_vlan.h> |
22 | #include <linux/init.h> |
23 | #include <linux/limits.h> |
24 | #include <linux/list.h> |
25 | #include <linux/minmax.h> |
26 | #include <linux/netdevice.h> |
27 | #include <linux/netlink.h> |
28 | #include <linux/printk.h> |
29 | #include <linux/rtnetlink.h> |
30 | #include <linux/skbuff.h> |
31 | #include <linux/stddef.h> |
32 | #include <linux/types.h> |
33 | #include <net/genetlink.h> |
34 | #include <net/net_namespace.h> |
35 | #include <net/netlink.h> |
36 | #include <net/sock.h> |
37 | #include <uapi/linux/batadv_packet.h> |
38 | #include <uapi/linux/batman_adv.h> |
39 | |
40 | #include "bat_algo.h" |
41 | #include "bridge_loop_avoidance.h" |
42 | #include "distributed-arp-table.h" |
43 | #include "gateway_client.h" |
44 | #include "gateway_common.h" |
45 | #include "hard-interface.h" |
46 | #include "log.h" |
47 | #include "multicast.h" |
48 | #include "network-coding.h" |
49 | #include "originator.h" |
50 | #include "soft-interface.h" |
51 | #include "tp_meter.h" |
52 | #include "translation-table.h" |
53 | |
54 | struct genl_family batadv_netlink_family; |
55 | |
56 | /* multicast groups */ |
57 | enum batadv_netlink_multicast_groups { |
58 | BATADV_NL_MCGRP_CONFIG, |
59 | BATADV_NL_MCGRP_TPMETER, |
60 | }; |
61 | |
62 | /** |
63 | * enum batadv_genl_ops_flags - flags for genl_ops's internal_flags |
64 | */ |
65 | enum batadv_genl_ops_flags { |
66 | /** |
67 | * @BATADV_FLAG_NEED_MESH: request requires valid soft interface in |
68 | * attribute BATADV_ATTR_MESH_IFINDEX and expects a pointer to it to be |
69 | * saved in info->user_ptr[0] |
70 | */ |
71 | BATADV_FLAG_NEED_MESH = BIT(0), |
72 | |
73 | /** |
74 | * @BATADV_FLAG_NEED_HARDIF: request requires valid hard interface in |
75 | * attribute BATADV_ATTR_HARD_IFINDEX and expects a pointer to it to be |
76 | * saved in info->user_ptr[1] |
77 | */ |
78 | BATADV_FLAG_NEED_HARDIF = BIT(1), |
79 | |
80 | /** |
81 | * @BATADV_FLAG_NEED_VLAN: request requires valid vlan in |
82 | * attribute BATADV_ATTR_VLANID and expects a pointer to it to be |
83 | * saved in info->user_ptr[1] |
84 | */ |
85 | BATADV_FLAG_NEED_VLAN = BIT(2), |
86 | }; |
87 | |
88 | static const struct genl_multicast_group batadv_netlink_mcgrps[] = { |
89 | [BATADV_NL_MCGRP_CONFIG] = { .name = BATADV_NL_MCAST_GROUP_CONFIG }, |
90 | [BATADV_NL_MCGRP_TPMETER] = { .name = BATADV_NL_MCAST_GROUP_TPMETER }, |
91 | }; |
92 | |
93 | static const struct nla_policy batadv_netlink_policy[NUM_BATADV_ATTR] = { |
94 | [BATADV_ATTR_VERSION] = { .type = NLA_STRING }, |
95 | [BATADV_ATTR_ALGO_NAME] = { .type = NLA_STRING }, |
96 | [BATADV_ATTR_MESH_IFINDEX] = { .type = NLA_U32 }, |
97 | [BATADV_ATTR_MESH_IFNAME] = { .type = NLA_STRING }, |
98 | [BATADV_ATTR_MESH_ADDRESS] = { .len = ETH_ALEN }, |
99 | [BATADV_ATTR_HARD_IFINDEX] = { .type = NLA_U32 }, |
100 | [BATADV_ATTR_HARD_IFNAME] = { .type = NLA_STRING }, |
101 | [BATADV_ATTR_HARD_ADDRESS] = { .len = ETH_ALEN }, |
102 | [BATADV_ATTR_ORIG_ADDRESS] = { .len = ETH_ALEN }, |
103 | [BATADV_ATTR_TPMETER_RESULT] = { .type = NLA_U8 }, |
104 | [BATADV_ATTR_TPMETER_TEST_TIME] = { .type = NLA_U32 }, |
105 | [BATADV_ATTR_TPMETER_BYTES] = { .type = NLA_U64 }, |
106 | [BATADV_ATTR_TPMETER_COOKIE] = { .type = NLA_U32 }, |
107 | [BATADV_ATTR_ACTIVE] = { .type = NLA_FLAG }, |
108 | [BATADV_ATTR_TT_ADDRESS] = { .len = ETH_ALEN }, |
109 | [BATADV_ATTR_TT_TTVN] = { .type = NLA_U8 }, |
110 | [BATADV_ATTR_TT_LAST_TTVN] = { .type = NLA_U8 }, |
111 | [BATADV_ATTR_TT_CRC32] = { .type = NLA_U32 }, |
112 | [BATADV_ATTR_TT_VID] = { .type = NLA_U16 }, |
113 | [BATADV_ATTR_TT_FLAGS] = { .type = NLA_U32 }, |
114 | [BATADV_ATTR_FLAG_BEST] = { .type = NLA_FLAG }, |
115 | [BATADV_ATTR_LAST_SEEN_MSECS] = { .type = NLA_U32 }, |
116 | [BATADV_ATTR_NEIGH_ADDRESS] = { .len = ETH_ALEN }, |
117 | [BATADV_ATTR_TQ] = { .type = NLA_U8 }, |
118 | [BATADV_ATTR_THROUGHPUT] = { .type = NLA_U32 }, |
119 | [BATADV_ATTR_BANDWIDTH_UP] = { .type = NLA_U32 }, |
120 | [BATADV_ATTR_BANDWIDTH_DOWN] = { .type = NLA_U32 }, |
121 | [BATADV_ATTR_ROUTER] = { .len = ETH_ALEN }, |
122 | [BATADV_ATTR_BLA_OWN] = { .type = NLA_FLAG }, |
123 | [BATADV_ATTR_BLA_ADDRESS] = { .len = ETH_ALEN }, |
124 | [BATADV_ATTR_BLA_VID] = { .type = NLA_U16 }, |
125 | [BATADV_ATTR_BLA_BACKBONE] = { .len = ETH_ALEN }, |
126 | [BATADV_ATTR_BLA_CRC] = { .type = NLA_U16 }, |
127 | [BATADV_ATTR_DAT_CACHE_IP4ADDRESS] = { .type = NLA_U32 }, |
128 | [BATADV_ATTR_DAT_CACHE_HWADDRESS] = { .len = ETH_ALEN }, |
129 | [BATADV_ATTR_DAT_CACHE_VID] = { .type = NLA_U16 }, |
130 | [BATADV_ATTR_MCAST_FLAGS] = { .type = NLA_U32 }, |
131 | [BATADV_ATTR_MCAST_FLAGS_PRIV] = { .type = NLA_U32 }, |
132 | [BATADV_ATTR_VLANID] = { .type = NLA_U16 }, |
133 | [BATADV_ATTR_AGGREGATED_OGMS_ENABLED] = { .type = NLA_U8 }, |
134 | [BATADV_ATTR_AP_ISOLATION_ENABLED] = { .type = NLA_U8 }, |
135 | [BATADV_ATTR_ISOLATION_MARK] = { .type = NLA_U32 }, |
136 | [BATADV_ATTR_ISOLATION_MASK] = { .type = NLA_U32 }, |
137 | [BATADV_ATTR_BONDING_ENABLED] = { .type = NLA_U8 }, |
138 | [BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED] = { .type = NLA_U8 }, |
139 | [BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED] = { .type = NLA_U8 }, |
140 | [BATADV_ATTR_FRAGMENTATION_ENABLED] = { .type = NLA_U8 }, |
141 | [BATADV_ATTR_GW_BANDWIDTH_DOWN] = { .type = NLA_U32 }, |
142 | [BATADV_ATTR_GW_BANDWIDTH_UP] = { .type = NLA_U32 }, |
143 | [BATADV_ATTR_GW_MODE] = { .type = NLA_U8 }, |
144 | [BATADV_ATTR_GW_SEL_CLASS] = { .type = NLA_U32 }, |
145 | [BATADV_ATTR_HOP_PENALTY] = { .type = NLA_U8 }, |
146 | [BATADV_ATTR_LOG_LEVEL] = { .type = NLA_U32 }, |
147 | [BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED] = { .type = NLA_U8 }, |
148 | [BATADV_ATTR_MULTICAST_FANOUT] = { .type = NLA_U32 }, |
149 | [BATADV_ATTR_NETWORK_CODING_ENABLED] = { .type = NLA_U8 }, |
150 | [BATADV_ATTR_ORIG_INTERVAL] = { .type = NLA_U32 }, |
151 | [BATADV_ATTR_ELP_INTERVAL] = { .type = NLA_U32 }, |
152 | [BATADV_ATTR_THROUGHPUT_OVERRIDE] = { .type = NLA_U32 }, |
153 | }; |
154 | |
155 | /** |
156 | * batadv_netlink_get_ifindex() - Extract an interface index from a message |
157 | * @nlh: Message header |
158 | * @attrtype: Attribute which holds an interface index |
159 | * |
160 | * Return: interface index, or 0. |
161 | */ |
162 | int |
163 | batadv_netlink_get_ifindex(const struct nlmsghdr *nlh, int attrtype) |
164 | { |
165 | struct nlattr *attr = nlmsg_find_attr(nlh, GENL_HDRLEN, attrtype); |
166 | |
167 | return (attr && nla_len(nla: attr) == sizeof(u32)) ? nla_get_u32(nla: attr) : 0; |
168 | } |
169 | |
170 | /** |
171 | * batadv_netlink_mesh_fill_ap_isolation() - Add ap_isolation softif attribute |
172 | * @msg: Netlink message to dump into |
173 | * @bat_priv: the bat priv with all the soft interface information |
174 | * |
175 | * Return: 0 on success or negative error number in case of failure |
176 | */ |
177 | static int batadv_netlink_mesh_fill_ap_isolation(struct sk_buff *msg, |
178 | struct batadv_priv *bat_priv) |
179 | { |
180 | struct batadv_softif_vlan *vlan; |
181 | u8 ap_isolation; |
182 | |
183 | vlan = batadv_softif_vlan_get(bat_priv, BATADV_NO_FLAGS); |
184 | if (!vlan) |
185 | return 0; |
186 | |
187 | ap_isolation = atomic_read(v: &vlan->ap_isolation); |
188 | batadv_softif_vlan_put(vlan); |
189 | |
190 | return nla_put_u8(skb: msg, attrtype: BATADV_ATTR_AP_ISOLATION_ENABLED, |
191 | value: !!ap_isolation); |
192 | } |
193 | |
194 | /** |
195 | * batadv_netlink_set_mesh_ap_isolation() - Set ap_isolation from genl msg |
196 | * @attr: parsed BATADV_ATTR_AP_ISOLATION_ENABLED attribute |
197 | * @bat_priv: the bat priv with all the soft interface information |
198 | * |
199 | * Return: 0 on success or negative error number in case of failure |
200 | */ |
201 | static int batadv_netlink_set_mesh_ap_isolation(struct nlattr *attr, |
202 | struct batadv_priv *bat_priv) |
203 | { |
204 | struct batadv_softif_vlan *vlan; |
205 | |
206 | vlan = batadv_softif_vlan_get(bat_priv, BATADV_NO_FLAGS); |
207 | if (!vlan) |
208 | return -ENOENT; |
209 | |
210 | atomic_set(v: &vlan->ap_isolation, i: !!nla_get_u8(nla: attr)); |
211 | batadv_softif_vlan_put(vlan); |
212 | |
213 | return 0; |
214 | } |
215 | |
216 | /** |
217 | * batadv_netlink_mesh_fill() - Fill message with mesh attributes |
218 | * @msg: Netlink message to dump into |
219 | * @bat_priv: the bat priv with all the soft interface information |
220 | * @cmd: type of message to generate |
221 | * @portid: Port making netlink request |
222 | * @seq: sequence number for message |
223 | * @flags: Additional flags for message |
224 | * |
225 | * Return: 0 on success or negative error number in case of failure |
226 | */ |
227 | static int batadv_netlink_mesh_fill(struct sk_buff *msg, |
228 | struct batadv_priv *bat_priv, |
229 | enum batadv_nl_commands cmd, |
230 | u32 portid, u32 seq, int flags) |
231 | { |
232 | struct net_device *soft_iface = bat_priv->soft_iface; |
233 | struct batadv_hard_iface *primary_if = NULL; |
234 | struct net_device *hard_iface; |
235 | void *hdr; |
236 | |
237 | hdr = genlmsg_put(skb: msg, portid, seq, family: &batadv_netlink_family, flags, cmd); |
238 | if (!hdr) |
239 | return -ENOBUFS; |
240 | |
241 | if (nla_put_string(skb: msg, attrtype: BATADV_ATTR_VERSION, BATADV_SOURCE_VERSION) || |
242 | nla_put_string(skb: msg, attrtype: BATADV_ATTR_ALGO_NAME, |
243 | str: bat_priv->algo_ops->name) || |
244 | nla_put_u32(skb: msg, attrtype: BATADV_ATTR_MESH_IFINDEX, value: soft_iface->ifindex) || |
245 | nla_put_string(skb: msg, attrtype: BATADV_ATTR_MESH_IFNAME, str: soft_iface->name) || |
246 | nla_put(skb: msg, attrtype: BATADV_ATTR_MESH_ADDRESS, ETH_ALEN, |
247 | data: soft_iface->dev_addr) || |
248 | nla_put_u8(skb: msg, attrtype: BATADV_ATTR_TT_TTVN, |
249 | value: (u8)atomic_read(v: &bat_priv->tt.vn))) |
250 | goto nla_put_failure; |
251 | |
252 | #ifdef CONFIG_BATMAN_ADV_BLA |
253 | if (nla_put_u16(skb: msg, attrtype: BATADV_ATTR_BLA_CRC, |
254 | ntohs(bat_priv->bla.claim_dest.group))) |
255 | goto nla_put_failure; |
256 | #endif |
257 | |
258 | if (batadv_mcast_mesh_info_put(msg, bat_priv)) |
259 | goto nla_put_failure; |
260 | |
261 | primary_if = batadv_primary_if_get_selected(bat_priv); |
262 | if (primary_if && primary_if->if_status == BATADV_IF_ACTIVE) { |
263 | hard_iface = primary_if->net_dev; |
264 | |
265 | if (nla_put_u32(skb: msg, attrtype: BATADV_ATTR_HARD_IFINDEX, |
266 | value: hard_iface->ifindex) || |
267 | nla_put_string(skb: msg, attrtype: BATADV_ATTR_HARD_IFNAME, |
268 | str: hard_iface->name) || |
269 | nla_put(skb: msg, attrtype: BATADV_ATTR_HARD_ADDRESS, ETH_ALEN, |
270 | data: hard_iface->dev_addr)) |
271 | goto nla_put_failure; |
272 | } |
273 | |
274 | if (nla_put_u8(skb: msg, attrtype: BATADV_ATTR_AGGREGATED_OGMS_ENABLED, |
275 | value: !!atomic_read(v: &bat_priv->aggregated_ogms))) |
276 | goto nla_put_failure; |
277 | |
278 | if (batadv_netlink_mesh_fill_ap_isolation(msg, bat_priv)) |
279 | goto nla_put_failure; |
280 | |
281 | if (nla_put_u32(skb: msg, attrtype: BATADV_ATTR_ISOLATION_MARK, |
282 | value: bat_priv->isolation_mark)) |
283 | goto nla_put_failure; |
284 | |
285 | if (nla_put_u32(skb: msg, attrtype: BATADV_ATTR_ISOLATION_MASK, |
286 | value: bat_priv->isolation_mark_mask)) |
287 | goto nla_put_failure; |
288 | |
289 | if (nla_put_u8(skb: msg, attrtype: BATADV_ATTR_BONDING_ENABLED, |
290 | value: !!atomic_read(v: &bat_priv->bonding))) |
291 | goto nla_put_failure; |
292 | |
293 | #ifdef CONFIG_BATMAN_ADV_BLA |
294 | if (nla_put_u8(skb: msg, attrtype: BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED, |
295 | value: !!atomic_read(v: &bat_priv->bridge_loop_avoidance))) |
296 | goto nla_put_failure; |
297 | #endif /* CONFIG_BATMAN_ADV_BLA */ |
298 | |
299 | #ifdef CONFIG_BATMAN_ADV_DAT |
300 | if (nla_put_u8(skb: msg, attrtype: BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED, |
301 | value: !!atomic_read(v: &bat_priv->distributed_arp_table))) |
302 | goto nla_put_failure; |
303 | #endif /* CONFIG_BATMAN_ADV_DAT */ |
304 | |
305 | if (nla_put_u8(skb: msg, attrtype: BATADV_ATTR_FRAGMENTATION_ENABLED, |
306 | value: !!atomic_read(v: &bat_priv->fragmentation))) |
307 | goto nla_put_failure; |
308 | |
309 | if (nla_put_u32(skb: msg, attrtype: BATADV_ATTR_GW_BANDWIDTH_DOWN, |
310 | value: atomic_read(v: &bat_priv->gw.bandwidth_down))) |
311 | goto nla_put_failure; |
312 | |
313 | if (nla_put_u32(skb: msg, attrtype: BATADV_ATTR_GW_BANDWIDTH_UP, |
314 | value: atomic_read(v: &bat_priv->gw.bandwidth_up))) |
315 | goto nla_put_failure; |
316 | |
317 | if (nla_put_u8(skb: msg, attrtype: BATADV_ATTR_GW_MODE, |
318 | value: atomic_read(v: &bat_priv->gw.mode))) |
319 | goto nla_put_failure; |
320 | |
321 | if (bat_priv->algo_ops->gw.get_best_gw_node && |
322 | bat_priv->algo_ops->gw.is_eligible) { |
323 | /* GW selection class is not available if the routing algorithm |
324 | * in use does not implement the GW API |
325 | */ |
326 | if (nla_put_u32(skb: msg, attrtype: BATADV_ATTR_GW_SEL_CLASS, |
327 | value: atomic_read(v: &bat_priv->gw.sel_class))) |
328 | goto nla_put_failure; |
329 | } |
330 | |
331 | if (nla_put_u8(skb: msg, attrtype: BATADV_ATTR_HOP_PENALTY, |
332 | value: atomic_read(v: &bat_priv->hop_penalty))) |
333 | goto nla_put_failure; |
334 | |
335 | #ifdef CONFIG_BATMAN_ADV_DEBUG |
336 | if (nla_put_u32(skb: msg, attrtype: BATADV_ATTR_LOG_LEVEL, |
337 | value: atomic_read(v: &bat_priv->log_level))) |
338 | goto nla_put_failure; |
339 | #endif /* CONFIG_BATMAN_ADV_DEBUG */ |
340 | |
341 | #ifdef CONFIG_BATMAN_ADV_MCAST |
342 | if (nla_put_u8(skb: msg, attrtype: BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED, |
343 | value: !atomic_read(v: &bat_priv->multicast_mode))) |
344 | goto nla_put_failure; |
345 | |
346 | if (nla_put_u32(skb: msg, attrtype: BATADV_ATTR_MULTICAST_FANOUT, |
347 | value: atomic_read(v: &bat_priv->multicast_fanout))) |
348 | goto nla_put_failure; |
349 | #endif /* CONFIG_BATMAN_ADV_MCAST */ |
350 | |
351 | #ifdef CONFIG_BATMAN_ADV_NC |
352 | if (nla_put_u8(skb: msg, attrtype: BATADV_ATTR_NETWORK_CODING_ENABLED, |
353 | value: !!atomic_read(v: &bat_priv->network_coding))) |
354 | goto nla_put_failure; |
355 | #endif /* CONFIG_BATMAN_ADV_NC */ |
356 | |
357 | if (nla_put_u32(skb: msg, attrtype: BATADV_ATTR_ORIG_INTERVAL, |
358 | value: atomic_read(v: &bat_priv->orig_interval))) |
359 | goto nla_put_failure; |
360 | |
361 | batadv_hardif_put(hard_iface: primary_if); |
362 | |
363 | genlmsg_end(skb: msg, hdr); |
364 | return 0; |
365 | |
366 | nla_put_failure: |
367 | batadv_hardif_put(hard_iface: primary_if); |
368 | |
369 | genlmsg_cancel(skb: msg, hdr); |
370 | return -EMSGSIZE; |
371 | } |
372 | |
373 | /** |
374 | * batadv_netlink_notify_mesh() - send softif attributes to listener |
375 | * @bat_priv: the bat priv with all the soft interface information |
376 | * |
377 | * Return: 0 on success, < 0 on error |
378 | */ |
379 | static int batadv_netlink_notify_mesh(struct batadv_priv *bat_priv) |
380 | { |
381 | struct sk_buff *msg; |
382 | int ret; |
383 | |
384 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
385 | if (!msg) |
386 | return -ENOMEM; |
387 | |
388 | ret = batadv_netlink_mesh_fill(msg, bat_priv, cmd: BATADV_CMD_SET_MESH, |
389 | portid: 0, seq: 0, flags: 0); |
390 | if (ret < 0) { |
391 | nlmsg_free(skb: msg); |
392 | return ret; |
393 | } |
394 | |
395 | genlmsg_multicast_netns(family: &batadv_netlink_family, |
396 | net: dev_net(dev: bat_priv->soft_iface), skb: msg, portid: 0, |
397 | group: BATADV_NL_MCGRP_CONFIG, GFP_KERNEL); |
398 | |
399 | return 0; |
400 | } |
401 | |
402 | /** |
403 | * batadv_netlink_get_mesh() - Get softif attributes |
404 | * @skb: Netlink message with request data |
405 | * @info: receiver information |
406 | * |
407 | * Return: 0 on success or negative error number in case of failure |
408 | */ |
409 | static int batadv_netlink_get_mesh(struct sk_buff *skb, struct genl_info *info) |
410 | { |
411 | struct batadv_priv *bat_priv = info->user_ptr[0]; |
412 | struct sk_buff *msg; |
413 | int ret; |
414 | |
415 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
416 | if (!msg) |
417 | return -ENOMEM; |
418 | |
419 | ret = batadv_netlink_mesh_fill(msg, bat_priv, cmd: BATADV_CMD_GET_MESH, |
420 | portid: info->snd_portid, seq: info->snd_seq, flags: 0); |
421 | if (ret < 0) { |
422 | nlmsg_free(skb: msg); |
423 | return ret; |
424 | } |
425 | |
426 | ret = genlmsg_reply(skb: msg, info); |
427 | |
428 | return ret; |
429 | } |
430 | |
431 | /** |
432 | * batadv_netlink_set_mesh() - Set softif attributes |
433 | * @skb: Netlink message with request data |
434 | * @info: receiver information |
435 | * |
436 | * Return: 0 on success or negative error number in case of failure |
437 | */ |
438 | static int batadv_netlink_set_mesh(struct sk_buff *skb, struct genl_info *info) |
439 | { |
440 | struct batadv_priv *bat_priv = info->user_ptr[0]; |
441 | struct nlattr *attr; |
442 | |
443 | if (info->attrs[BATADV_ATTR_AGGREGATED_OGMS_ENABLED]) { |
444 | attr = info->attrs[BATADV_ATTR_AGGREGATED_OGMS_ENABLED]; |
445 | |
446 | atomic_set(v: &bat_priv->aggregated_ogms, i: !!nla_get_u8(nla: attr)); |
447 | } |
448 | |
449 | if (info->attrs[BATADV_ATTR_AP_ISOLATION_ENABLED]) { |
450 | attr = info->attrs[BATADV_ATTR_AP_ISOLATION_ENABLED]; |
451 | |
452 | batadv_netlink_set_mesh_ap_isolation(attr, bat_priv); |
453 | } |
454 | |
455 | if (info->attrs[BATADV_ATTR_ISOLATION_MARK]) { |
456 | attr = info->attrs[BATADV_ATTR_ISOLATION_MARK]; |
457 | |
458 | bat_priv->isolation_mark = nla_get_u32(nla: attr); |
459 | } |
460 | |
461 | if (info->attrs[BATADV_ATTR_ISOLATION_MASK]) { |
462 | attr = info->attrs[BATADV_ATTR_ISOLATION_MASK]; |
463 | |
464 | bat_priv->isolation_mark_mask = nla_get_u32(nla: attr); |
465 | } |
466 | |
467 | if (info->attrs[BATADV_ATTR_BONDING_ENABLED]) { |
468 | attr = info->attrs[BATADV_ATTR_BONDING_ENABLED]; |
469 | |
470 | atomic_set(v: &bat_priv->bonding, i: !!nla_get_u8(nla: attr)); |
471 | } |
472 | |
473 | #ifdef CONFIG_BATMAN_ADV_BLA |
474 | if (info->attrs[BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED]) { |
475 | attr = info->attrs[BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED]; |
476 | |
477 | atomic_set(v: &bat_priv->bridge_loop_avoidance, |
478 | i: !!nla_get_u8(nla: attr)); |
479 | batadv_bla_status_update(net_dev: bat_priv->soft_iface); |
480 | } |
481 | #endif /* CONFIG_BATMAN_ADV_BLA */ |
482 | |
483 | #ifdef CONFIG_BATMAN_ADV_DAT |
484 | if (info->attrs[BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED]) { |
485 | attr = info->attrs[BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED]; |
486 | |
487 | atomic_set(v: &bat_priv->distributed_arp_table, |
488 | i: !!nla_get_u8(nla: attr)); |
489 | batadv_dat_status_update(net_dev: bat_priv->soft_iface); |
490 | } |
491 | #endif /* CONFIG_BATMAN_ADV_DAT */ |
492 | |
493 | if (info->attrs[BATADV_ATTR_FRAGMENTATION_ENABLED]) { |
494 | attr = info->attrs[BATADV_ATTR_FRAGMENTATION_ENABLED]; |
495 | |
496 | atomic_set(v: &bat_priv->fragmentation, i: !!nla_get_u8(nla: attr)); |
497 | |
498 | rtnl_lock(); |
499 | batadv_update_min_mtu(soft_iface: bat_priv->soft_iface); |
500 | rtnl_unlock(); |
501 | } |
502 | |
503 | if (info->attrs[BATADV_ATTR_GW_BANDWIDTH_DOWN]) { |
504 | attr = info->attrs[BATADV_ATTR_GW_BANDWIDTH_DOWN]; |
505 | |
506 | atomic_set(v: &bat_priv->gw.bandwidth_down, i: nla_get_u32(nla: attr)); |
507 | batadv_gw_tvlv_container_update(bat_priv); |
508 | } |
509 | |
510 | if (info->attrs[BATADV_ATTR_GW_BANDWIDTH_UP]) { |
511 | attr = info->attrs[BATADV_ATTR_GW_BANDWIDTH_UP]; |
512 | |
513 | atomic_set(v: &bat_priv->gw.bandwidth_up, i: nla_get_u32(nla: attr)); |
514 | batadv_gw_tvlv_container_update(bat_priv); |
515 | } |
516 | |
517 | if (info->attrs[BATADV_ATTR_GW_MODE]) { |
518 | u8 gw_mode; |
519 | |
520 | attr = info->attrs[BATADV_ATTR_GW_MODE]; |
521 | gw_mode = nla_get_u8(nla: attr); |
522 | |
523 | if (gw_mode <= BATADV_GW_MODE_SERVER) { |
524 | /* Invoking batadv_gw_reselect() is not enough to really |
525 | * de-select the current GW. It will only instruct the |
526 | * gateway client code to perform a re-election the next |
527 | * time that this is needed. |
528 | * |
529 | * When gw client mode is being switched off the current |
530 | * GW must be de-selected explicitly otherwise no GW_ADD |
531 | * uevent is thrown on client mode re-activation. This |
532 | * is operation is performed in |
533 | * batadv_gw_check_client_stop(). |
534 | */ |
535 | batadv_gw_reselect(bat_priv); |
536 | |
537 | /* always call batadv_gw_check_client_stop() before |
538 | * changing the gateway state |
539 | */ |
540 | batadv_gw_check_client_stop(bat_priv); |
541 | atomic_set(v: &bat_priv->gw.mode, i: gw_mode); |
542 | batadv_gw_tvlv_container_update(bat_priv); |
543 | } |
544 | } |
545 | |
546 | if (info->attrs[BATADV_ATTR_GW_SEL_CLASS] && |
547 | bat_priv->algo_ops->gw.get_best_gw_node && |
548 | bat_priv->algo_ops->gw.is_eligible) { |
549 | /* setting the GW selection class is allowed only if the routing |
550 | * algorithm in use implements the GW API |
551 | */ |
552 | |
553 | u32 sel_class_max = bat_priv->algo_ops->gw.sel_class_max; |
554 | u32 sel_class; |
555 | |
556 | attr = info->attrs[BATADV_ATTR_GW_SEL_CLASS]; |
557 | sel_class = nla_get_u32(nla: attr); |
558 | |
559 | if (sel_class >= 1 && sel_class <= sel_class_max) { |
560 | atomic_set(v: &bat_priv->gw.sel_class, i: sel_class); |
561 | batadv_gw_reselect(bat_priv); |
562 | } |
563 | } |
564 | |
565 | if (info->attrs[BATADV_ATTR_HOP_PENALTY]) { |
566 | attr = info->attrs[BATADV_ATTR_HOP_PENALTY]; |
567 | |
568 | atomic_set(v: &bat_priv->hop_penalty, i: nla_get_u8(nla: attr)); |
569 | } |
570 | |
571 | #ifdef CONFIG_BATMAN_ADV_DEBUG |
572 | if (info->attrs[BATADV_ATTR_LOG_LEVEL]) { |
573 | attr = info->attrs[BATADV_ATTR_LOG_LEVEL]; |
574 | |
575 | atomic_set(v: &bat_priv->log_level, |
576 | i: nla_get_u32(nla: attr) & BATADV_DBG_ALL); |
577 | } |
578 | #endif /* CONFIG_BATMAN_ADV_DEBUG */ |
579 | |
580 | #ifdef CONFIG_BATMAN_ADV_MCAST |
581 | if (info->attrs[BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED]) { |
582 | attr = info->attrs[BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED]; |
583 | |
584 | atomic_set(v: &bat_priv->multicast_mode, i: !nla_get_u8(nla: attr)); |
585 | } |
586 | |
587 | if (info->attrs[BATADV_ATTR_MULTICAST_FANOUT]) { |
588 | attr = info->attrs[BATADV_ATTR_MULTICAST_FANOUT]; |
589 | |
590 | atomic_set(v: &bat_priv->multicast_fanout, i: nla_get_u32(nla: attr)); |
591 | } |
592 | #endif /* CONFIG_BATMAN_ADV_MCAST */ |
593 | |
594 | #ifdef CONFIG_BATMAN_ADV_NC |
595 | if (info->attrs[BATADV_ATTR_NETWORK_CODING_ENABLED]) { |
596 | attr = info->attrs[BATADV_ATTR_NETWORK_CODING_ENABLED]; |
597 | |
598 | atomic_set(v: &bat_priv->network_coding, i: !!nla_get_u8(nla: attr)); |
599 | batadv_nc_status_update(net_dev: bat_priv->soft_iface); |
600 | } |
601 | #endif /* CONFIG_BATMAN_ADV_NC */ |
602 | |
603 | if (info->attrs[BATADV_ATTR_ORIG_INTERVAL]) { |
604 | u32 orig_interval; |
605 | |
606 | attr = info->attrs[BATADV_ATTR_ORIG_INTERVAL]; |
607 | orig_interval = nla_get_u32(nla: attr); |
608 | |
609 | orig_interval = min_t(u32, orig_interval, INT_MAX); |
610 | orig_interval = max_t(u32, orig_interval, 2 * BATADV_JITTER); |
611 | |
612 | atomic_set(v: &bat_priv->orig_interval, i: orig_interval); |
613 | } |
614 | |
615 | batadv_netlink_notify_mesh(bat_priv); |
616 | |
617 | return 0; |
618 | } |
619 | |
620 | /** |
621 | * batadv_netlink_tp_meter_put() - Fill information of started tp_meter session |
622 | * @msg: netlink message to be sent back |
623 | * @cookie: tp meter session cookie |
624 | * |
625 | * Return: 0 on success, < 0 on error |
626 | */ |
627 | static int |
628 | batadv_netlink_tp_meter_put(struct sk_buff *msg, u32 cookie) |
629 | { |
630 | if (nla_put_u32(skb: msg, attrtype: BATADV_ATTR_TPMETER_COOKIE, value: cookie)) |
631 | return -ENOBUFS; |
632 | |
633 | return 0; |
634 | } |
635 | |
636 | /** |
637 | * batadv_netlink_tpmeter_notify() - send tp_meter result via netlink to client |
638 | * @bat_priv: the bat priv with all the soft interface information |
639 | * @dst: destination of tp_meter session |
640 | * @result: reason for tp meter session stop |
641 | * @test_time: total time of the tp_meter session |
642 | * @total_bytes: bytes acked to the receiver |
643 | * @cookie: cookie of tp_meter session |
644 | * |
645 | * Return: 0 on success, < 0 on error |
646 | */ |
647 | int batadv_netlink_tpmeter_notify(struct batadv_priv *bat_priv, const u8 *dst, |
648 | u8 result, u32 test_time, u64 total_bytes, |
649 | u32 cookie) |
650 | { |
651 | struct sk_buff *msg; |
652 | void *hdr; |
653 | int ret; |
654 | |
655 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
656 | if (!msg) |
657 | return -ENOMEM; |
658 | |
659 | hdr = genlmsg_put(skb: msg, portid: 0, seq: 0, family: &batadv_netlink_family, flags: 0, |
660 | cmd: BATADV_CMD_TP_METER); |
661 | if (!hdr) { |
662 | ret = -ENOBUFS; |
663 | goto err_genlmsg; |
664 | } |
665 | |
666 | if (nla_put_u32(skb: msg, attrtype: BATADV_ATTR_TPMETER_COOKIE, value: cookie)) |
667 | goto nla_put_failure; |
668 | |
669 | if (nla_put_u32(skb: msg, attrtype: BATADV_ATTR_TPMETER_TEST_TIME, value: test_time)) |
670 | goto nla_put_failure; |
671 | |
672 | if (nla_put_u64_64bit(skb: msg, attrtype: BATADV_ATTR_TPMETER_BYTES, value: total_bytes, |
673 | padattr: BATADV_ATTR_PAD)) |
674 | goto nla_put_failure; |
675 | |
676 | if (nla_put_u8(skb: msg, attrtype: BATADV_ATTR_TPMETER_RESULT, value: result)) |
677 | goto nla_put_failure; |
678 | |
679 | if (nla_put(skb: msg, attrtype: BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN, data: dst)) |
680 | goto nla_put_failure; |
681 | |
682 | genlmsg_end(skb: msg, hdr); |
683 | |
684 | genlmsg_multicast_netns(family: &batadv_netlink_family, |
685 | net: dev_net(dev: bat_priv->soft_iface), skb: msg, portid: 0, |
686 | group: BATADV_NL_MCGRP_TPMETER, GFP_KERNEL); |
687 | |
688 | return 0; |
689 | |
690 | nla_put_failure: |
691 | genlmsg_cancel(skb: msg, hdr); |
692 | ret = -EMSGSIZE; |
693 | |
694 | err_genlmsg: |
695 | nlmsg_free(skb: msg); |
696 | return ret; |
697 | } |
698 | |
699 | /** |
700 | * batadv_netlink_tp_meter_start() - Start a new tp_meter session |
701 | * @skb: received netlink message |
702 | * @info: receiver information |
703 | * |
704 | * Return: 0 on success, < 0 on error |
705 | */ |
706 | static int |
707 | batadv_netlink_tp_meter_start(struct sk_buff *skb, struct genl_info *info) |
708 | { |
709 | struct batadv_priv *bat_priv = info->user_ptr[0]; |
710 | struct sk_buff *msg = NULL; |
711 | u32 test_length; |
712 | void *msg_head; |
713 | u32 cookie; |
714 | u8 *dst; |
715 | int ret; |
716 | |
717 | if (!info->attrs[BATADV_ATTR_ORIG_ADDRESS]) |
718 | return -EINVAL; |
719 | |
720 | if (!info->attrs[BATADV_ATTR_TPMETER_TEST_TIME]) |
721 | return -EINVAL; |
722 | |
723 | dst = nla_data(nla: info->attrs[BATADV_ATTR_ORIG_ADDRESS]); |
724 | |
725 | test_length = nla_get_u32(nla: info->attrs[BATADV_ATTR_TPMETER_TEST_TIME]); |
726 | |
727 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
728 | if (!msg) { |
729 | ret = -ENOMEM; |
730 | goto out; |
731 | } |
732 | |
733 | msg_head = genlmsg_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, |
734 | family: &batadv_netlink_family, flags: 0, |
735 | cmd: BATADV_CMD_TP_METER); |
736 | if (!msg_head) { |
737 | ret = -ENOBUFS; |
738 | goto out; |
739 | } |
740 | |
741 | batadv_tp_start(bat_priv, dst, test_length, cookie: &cookie); |
742 | |
743 | ret = batadv_netlink_tp_meter_put(msg, cookie); |
744 | |
745 | out: |
746 | if (ret) { |
747 | if (msg) |
748 | nlmsg_free(skb: msg); |
749 | return ret; |
750 | } |
751 | |
752 | genlmsg_end(skb: msg, hdr: msg_head); |
753 | return genlmsg_reply(skb: msg, info); |
754 | } |
755 | |
756 | /** |
757 | * batadv_netlink_tp_meter_cancel() - Cancel a running tp_meter session |
758 | * @skb: received netlink message |
759 | * @info: receiver information |
760 | * |
761 | * Return: 0 on success, < 0 on error |
762 | */ |
763 | static int |
764 | batadv_netlink_tp_meter_cancel(struct sk_buff *skb, struct genl_info *info) |
765 | { |
766 | struct batadv_priv *bat_priv = info->user_ptr[0]; |
767 | u8 *dst; |
768 | int ret = 0; |
769 | |
770 | if (!info->attrs[BATADV_ATTR_ORIG_ADDRESS]) |
771 | return -EINVAL; |
772 | |
773 | dst = nla_data(nla: info->attrs[BATADV_ATTR_ORIG_ADDRESS]); |
774 | |
775 | batadv_tp_stop(bat_priv, dst, return_value: BATADV_TP_REASON_CANCEL); |
776 | |
777 | return ret; |
778 | } |
779 | |
780 | /** |
781 | * batadv_netlink_hardif_fill() - Fill message with hardif attributes |
782 | * @msg: Netlink message to dump into |
783 | * @bat_priv: the bat priv with all the soft interface information |
784 | * @hard_iface: hard interface which was modified |
785 | * @cmd: type of message to generate |
786 | * @portid: Port making netlink request |
787 | * @seq: sequence number for message |
788 | * @flags: Additional flags for message |
789 | * @cb: Control block containing additional options |
790 | * |
791 | * Return: 0 on success or negative error number in case of failure |
792 | */ |
793 | static int batadv_netlink_hardif_fill(struct sk_buff *msg, |
794 | struct batadv_priv *bat_priv, |
795 | struct batadv_hard_iface *hard_iface, |
796 | enum batadv_nl_commands cmd, |
797 | u32 portid, u32 seq, int flags, |
798 | struct netlink_callback *cb) |
799 | { |
800 | struct net_device *net_dev = hard_iface->net_dev; |
801 | void *hdr; |
802 | |
803 | hdr = genlmsg_put(skb: msg, portid, seq, family: &batadv_netlink_family, flags, cmd); |
804 | if (!hdr) |
805 | return -ENOBUFS; |
806 | |
807 | if (cb) |
808 | genl_dump_check_consistent(cb, user_hdr: hdr); |
809 | |
810 | if (nla_put_u32(skb: msg, attrtype: BATADV_ATTR_MESH_IFINDEX, |
811 | value: bat_priv->soft_iface->ifindex)) |
812 | goto nla_put_failure; |
813 | |
814 | if (nla_put_string(skb: msg, attrtype: BATADV_ATTR_MESH_IFNAME, |
815 | str: bat_priv->soft_iface->name)) |
816 | goto nla_put_failure; |
817 | |
818 | if (nla_put_u32(skb: msg, attrtype: BATADV_ATTR_HARD_IFINDEX, |
819 | value: net_dev->ifindex) || |
820 | nla_put_string(skb: msg, attrtype: BATADV_ATTR_HARD_IFNAME, |
821 | str: net_dev->name) || |
822 | nla_put(skb: msg, attrtype: BATADV_ATTR_HARD_ADDRESS, ETH_ALEN, |
823 | data: net_dev->dev_addr)) |
824 | goto nla_put_failure; |
825 | |
826 | if (hard_iface->if_status == BATADV_IF_ACTIVE) { |
827 | if (nla_put_flag(skb: msg, attrtype: BATADV_ATTR_ACTIVE)) |
828 | goto nla_put_failure; |
829 | } |
830 | |
831 | if (nla_put_u8(skb: msg, attrtype: BATADV_ATTR_HOP_PENALTY, |
832 | value: atomic_read(v: &hard_iface->hop_penalty))) |
833 | goto nla_put_failure; |
834 | |
835 | #ifdef CONFIG_BATMAN_ADV_BATMAN_V |
836 | if (nla_put_u32(skb: msg, attrtype: BATADV_ATTR_ELP_INTERVAL, |
837 | value: atomic_read(v: &hard_iface->bat_v.elp_interval))) |
838 | goto nla_put_failure; |
839 | |
840 | if (nla_put_u32(skb: msg, attrtype: BATADV_ATTR_THROUGHPUT_OVERRIDE, |
841 | value: atomic_read(v: &hard_iface->bat_v.throughput_override))) |
842 | goto nla_put_failure; |
843 | #endif /* CONFIG_BATMAN_ADV_BATMAN_V */ |
844 | |
845 | genlmsg_end(skb: msg, hdr); |
846 | return 0; |
847 | |
848 | nla_put_failure: |
849 | genlmsg_cancel(skb: msg, hdr); |
850 | return -EMSGSIZE; |
851 | } |
852 | |
853 | /** |
854 | * batadv_netlink_notify_hardif() - send hardif attributes to listener |
855 | * @bat_priv: the bat priv with all the soft interface information |
856 | * @hard_iface: hard interface which was modified |
857 | * |
858 | * Return: 0 on success, < 0 on error |
859 | */ |
860 | static int batadv_netlink_notify_hardif(struct batadv_priv *bat_priv, |
861 | struct batadv_hard_iface *hard_iface) |
862 | { |
863 | struct sk_buff *msg; |
864 | int ret; |
865 | |
866 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
867 | if (!msg) |
868 | return -ENOMEM; |
869 | |
870 | ret = batadv_netlink_hardif_fill(msg, bat_priv, hard_iface, |
871 | cmd: BATADV_CMD_SET_HARDIF, portid: 0, seq: 0, flags: 0, NULL); |
872 | if (ret < 0) { |
873 | nlmsg_free(skb: msg); |
874 | return ret; |
875 | } |
876 | |
877 | genlmsg_multicast_netns(family: &batadv_netlink_family, |
878 | net: dev_net(dev: bat_priv->soft_iface), skb: msg, portid: 0, |
879 | group: BATADV_NL_MCGRP_CONFIG, GFP_KERNEL); |
880 | |
881 | return 0; |
882 | } |
883 | |
884 | /** |
885 | * batadv_netlink_get_hardif() - Get hardif attributes |
886 | * @skb: Netlink message with request data |
887 | * @info: receiver information |
888 | * |
889 | * Return: 0 on success or negative error number in case of failure |
890 | */ |
891 | static int batadv_netlink_get_hardif(struct sk_buff *skb, |
892 | struct genl_info *info) |
893 | { |
894 | struct batadv_hard_iface *hard_iface = info->user_ptr[1]; |
895 | struct batadv_priv *bat_priv = info->user_ptr[0]; |
896 | struct sk_buff *msg; |
897 | int ret; |
898 | |
899 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
900 | if (!msg) |
901 | return -ENOMEM; |
902 | |
903 | ret = batadv_netlink_hardif_fill(msg, bat_priv, hard_iface, |
904 | cmd: BATADV_CMD_GET_HARDIF, |
905 | portid: info->snd_portid, seq: info->snd_seq, flags: 0, |
906 | NULL); |
907 | if (ret < 0) { |
908 | nlmsg_free(skb: msg); |
909 | return ret; |
910 | } |
911 | |
912 | ret = genlmsg_reply(skb: msg, info); |
913 | |
914 | return ret; |
915 | } |
916 | |
917 | /** |
918 | * batadv_netlink_set_hardif() - Set hardif attributes |
919 | * @skb: Netlink message with request data |
920 | * @info: receiver information |
921 | * |
922 | * Return: 0 on success or negative error number in case of failure |
923 | */ |
924 | static int batadv_netlink_set_hardif(struct sk_buff *skb, |
925 | struct genl_info *info) |
926 | { |
927 | struct batadv_hard_iface *hard_iface = info->user_ptr[1]; |
928 | struct batadv_priv *bat_priv = info->user_ptr[0]; |
929 | struct nlattr *attr; |
930 | |
931 | if (info->attrs[BATADV_ATTR_HOP_PENALTY]) { |
932 | attr = info->attrs[BATADV_ATTR_HOP_PENALTY]; |
933 | |
934 | atomic_set(v: &hard_iface->hop_penalty, i: nla_get_u8(nla: attr)); |
935 | } |
936 | |
937 | #ifdef CONFIG_BATMAN_ADV_BATMAN_V |
938 | |
939 | if (info->attrs[BATADV_ATTR_ELP_INTERVAL]) { |
940 | attr = info->attrs[BATADV_ATTR_ELP_INTERVAL]; |
941 | |
942 | atomic_set(v: &hard_iface->bat_v.elp_interval, i: nla_get_u32(nla: attr)); |
943 | } |
944 | |
945 | if (info->attrs[BATADV_ATTR_THROUGHPUT_OVERRIDE]) { |
946 | attr = info->attrs[BATADV_ATTR_THROUGHPUT_OVERRIDE]; |
947 | |
948 | atomic_set(v: &hard_iface->bat_v.throughput_override, |
949 | i: nla_get_u32(nla: attr)); |
950 | } |
951 | #endif /* CONFIG_BATMAN_ADV_BATMAN_V */ |
952 | |
953 | batadv_netlink_notify_hardif(bat_priv, hard_iface); |
954 | |
955 | return 0; |
956 | } |
957 | |
958 | /** |
959 | * batadv_netlink_dump_hardif() - Dump all hard interface into a messages |
960 | * @msg: Netlink message to dump into |
961 | * @cb: Parameters from query |
962 | * |
963 | * Return: error code, or length of reply message on success |
964 | */ |
965 | static int |
966 | batadv_netlink_dump_hardif(struct sk_buff *msg, struct netlink_callback *cb) |
967 | { |
968 | struct net *net = sock_net(sk: cb->skb->sk); |
969 | struct net_device *soft_iface; |
970 | struct batadv_hard_iface *hard_iface; |
971 | struct batadv_priv *bat_priv; |
972 | int ifindex; |
973 | int portid = NETLINK_CB(cb->skb).portid; |
974 | int skip = cb->args[0]; |
975 | int i = 0; |
976 | |
977 | ifindex = batadv_netlink_get_ifindex(nlh: cb->nlh, |
978 | attrtype: BATADV_ATTR_MESH_IFINDEX); |
979 | if (!ifindex) |
980 | return -EINVAL; |
981 | |
982 | soft_iface = dev_get_by_index(net, ifindex); |
983 | if (!soft_iface) |
984 | return -ENODEV; |
985 | |
986 | if (!batadv_softif_is_valid(net_dev: soft_iface)) { |
987 | dev_put(dev: soft_iface); |
988 | return -ENODEV; |
989 | } |
990 | |
991 | bat_priv = netdev_priv(dev: soft_iface); |
992 | |
993 | rtnl_lock(); |
994 | cb->seq = batadv_hardif_generation << 1 | 1; |
995 | |
996 | list_for_each_entry(hard_iface, &batadv_hardif_list, list) { |
997 | if (hard_iface->soft_iface != soft_iface) |
998 | continue; |
999 | |
1000 | if (i++ < skip) |
1001 | continue; |
1002 | |
1003 | if (batadv_netlink_hardif_fill(msg, bat_priv, hard_iface, |
1004 | cmd: BATADV_CMD_GET_HARDIF, |
1005 | portid, seq: cb->nlh->nlmsg_seq, |
1006 | NLM_F_MULTI, cb)) { |
1007 | i--; |
1008 | break; |
1009 | } |
1010 | } |
1011 | |
1012 | rtnl_unlock(); |
1013 | |
1014 | dev_put(dev: soft_iface); |
1015 | |
1016 | cb->args[0] = i; |
1017 | |
1018 | return msg->len; |
1019 | } |
1020 | |
1021 | /** |
1022 | * batadv_netlink_vlan_fill() - Fill message with vlan attributes |
1023 | * @msg: Netlink message to dump into |
1024 | * @bat_priv: the bat priv with all the soft interface information |
1025 | * @vlan: vlan which was modified |
1026 | * @cmd: type of message to generate |
1027 | * @portid: Port making netlink request |
1028 | * @seq: sequence number for message |
1029 | * @flags: Additional flags for message |
1030 | * |
1031 | * Return: 0 on success or negative error number in case of failure |
1032 | */ |
1033 | static int batadv_netlink_vlan_fill(struct sk_buff *msg, |
1034 | struct batadv_priv *bat_priv, |
1035 | struct batadv_softif_vlan *vlan, |
1036 | enum batadv_nl_commands cmd, |
1037 | u32 portid, u32 seq, int flags) |
1038 | { |
1039 | void *hdr; |
1040 | |
1041 | hdr = genlmsg_put(skb: msg, portid, seq, family: &batadv_netlink_family, flags, cmd); |
1042 | if (!hdr) |
1043 | return -ENOBUFS; |
1044 | |
1045 | if (nla_put_u32(skb: msg, attrtype: BATADV_ATTR_MESH_IFINDEX, |
1046 | value: bat_priv->soft_iface->ifindex)) |
1047 | goto nla_put_failure; |
1048 | |
1049 | if (nla_put_string(skb: msg, attrtype: BATADV_ATTR_MESH_IFNAME, |
1050 | str: bat_priv->soft_iface->name)) |
1051 | goto nla_put_failure; |
1052 | |
1053 | if (nla_put_u32(skb: msg, attrtype: BATADV_ATTR_VLANID, value: vlan->vid & VLAN_VID_MASK)) |
1054 | goto nla_put_failure; |
1055 | |
1056 | if (nla_put_u8(skb: msg, attrtype: BATADV_ATTR_AP_ISOLATION_ENABLED, |
1057 | value: !!atomic_read(v: &vlan->ap_isolation))) |
1058 | goto nla_put_failure; |
1059 | |
1060 | genlmsg_end(skb: msg, hdr); |
1061 | return 0; |
1062 | |
1063 | nla_put_failure: |
1064 | genlmsg_cancel(skb: msg, hdr); |
1065 | return -EMSGSIZE; |
1066 | } |
1067 | |
1068 | /** |
1069 | * batadv_netlink_notify_vlan() - send vlan attributes to listener |
1070 | * @bat_priv: the bat priv with all the soft interface information |
1071 | * @vlan: vlan which was modified |
1072 | * |
1073 | * Return: 0 on success, < 0 on error |
1074 | */ |
1075 | static int batadv_netlink_notify_vlan(struct batadv_priv *bat_priv, |
1076 | struct batadv_softif_vlan *vlan) |
1077 | { |
1078 | struct sk_buff *msg; |
1079 | int ret; |
1080 | |
1081 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
1082 | if (!msg) |
1083 | return -ENOMEM; |
1084 | |
1085 | ret = batadv_netlink_vlan_fill(msg, bat_priv, vlan, |
1086 | cmd: BATADV_CMD_SET_VLAN, portid: 0, seq: 0, flags: 0); |
1087 | if (ret < 0) { |
1088 | nlmsg_free(skb: msg); |
1089 | return ret; |
1090 | } |
1091 | |
1092 | genlmsg_multicast_netns(family: &batadv_netlink_family, |
1093 | net: dev_net(dev: bat_priv->soft_iface), skb: msg, portid: 0, |
1094 | group: BATADV_NL_MCGRP_CONFIG, GFP_KERNEL); |
1095 | |
1096 | return 0; |
1097 | } |
1098 | |
1099 | /** |
1100 | * batadv_netlink_get_vlan() - Get vlan attributes |
1101 | * @skb: Netlink message with request data |
1102 | * @info: receiver information |
1103 | * |
1104 | * Return: 0 on success or negative error number in case of failure |
1105 | */ |
1106 | static int batadv_netlink_get_vlan(struct sk_buff *skb, struct genl_info *info) |
1107 | { |
1108 | struct batadv_softif_vlan *vlan = info->user_ptr[1]; |
1109 | struct batadv_priv *bat_priv = info->user_ptr[0]; |
1110 | struct sk_buff *msg; |
1111 | int ret; |
1112 | |
1113 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
1114 | if (!msg) |
1115 | return -ENOMEM; |
1116 | |
1117 | ret = batadv_netlink_vlan_fill(msg, bat_priv, vlan, cmd: BATADV_CMD_GET_VLAN, |
1118 | portid: info->snd_portid, seq: info->snd_seq, flags: 0); |
1119 | if (ret < 0) { |
1120 | nlmsg_free(skb: msg); |
1121 | return ret; |
1122 | } |
1123 | |
1124 | ret = genlmsg_reply(skb: msg, info); |
1125 | |
1126 | return ret; |
1127 | } |
1128 | |
1129 | /** |
1130 | * batadv_netlink_set_vlan() - Get vlan attributes |
1131 | * @skb: Netlink message with request data |
1132 | * @info: receiver information |
1133 | * |
1134 | * Return: 0 on success or negative error number in case of failure |
1135 | */ |
1136 | static int batadv_netlink_set_vlan(struct sk_buff *skb, struct genl_info *info) |
1137 | { |
1138 | struct batadv_softif_vlan *vlan = info->user_ptr[1]; |
1139 | struct batadv_priv *bat_priv = info->user_ptr[0]; |
1140 | struct nlattr *attr; |
1141 | |
1142 | if (info->attrs[BATADV_ATTR_AP_ISOLATION_ENABLED]) { |
1143 | attr = info->attrs[BATADV_ATTR_AP_ISOLATION_ENABLED]; |
1144 | |
1145 | atomic_set(v: &vlan->ap_isolation, i: !!nla_get_u8(nla: attr)); |
1146 | } |
1147 | |
1148 | batadv_netlink_notify_vlan(bat_priv, vlan); |
1149 | |
1150 | return 0; |
1151 | } |
1152 | |
1153 | /** |
1154 | * batadv_get_softif_from_info() - Retrieve soft interface from genl attributes |
1155 | * @net: the applicable net namespace |
1156 | * @info: receiver information |
1157 | * |
1158 | * Return: Pointer to soft interface (with increased refcnt) on success, error |
1159 | * pointer on error |
1160 | */ |
1161 | static struct net_device * |
1162 | batadv_get_softif_from_info(struct net *net, struct genl_info *info) |
1163 | { |
1164 | struct net_device *soft_iface; |
1165 | int ifindex; |
1166 | |
1167 | if (!info->attrs[BATADV_ATTR_MESH_IFINDEX]) |
1168 | return ERR_PTR(error: -EINVAL); |
1169 | |
1170 | ifindex = nla_get_u32(nla: info->attrs[BATADV_ATTR_MESH_IFINDEX]); |
1171 | |
1172 | soft_iface = dev_get_by_index(net, ifindex); |
1173 | if (!soft_iface) |
1174 | return ERR_PTR(error: -ENODEV); |
1175 | |
1176 | if (!batadv_softif_is_valid(net_dev: soft_iface)) |
1177 | goto err_put_softif; |
1178 | |
1179 | return soft_iface; |
1180 | |
1181 | err_put_softif: |
1182 | dev_put(dev: soft_iface); |
1183 | |
1184 | return ERR_PTR(error: -EINVAL); |
1185 | } |
1186 | |
1187 | /** |
1188 | * batadv_get_hardif_from_info() - Retrieve hardif from genl attributes |
1189 | * @bat_priv: the bat priv with all the soft interface information |
1190 | * @net: the applicable net namespace |
1191 | * @info: receiver information |
1192 | * |
1193 | * Return: Pointer to hard interface (with increased refcnt) on success, error |
1194 | * pointer on error |
1195 | */ |
1196 | static struct batadv_hard_iface * |
1197 | batadv_get_hardif_from_info(struct batadv_priv *bat_priv, struct net *net, |
1198 | struct genl_info *info) |
1199 | { |
1200 | struct batadv_hard_iface *hard_iface; |
1201 | struct net_device *hard_dev; |
1202 | unsigned int hardif_index; |
1203 | |
1204 | if (!info->attrs[BATADV_ATTR_HARD_IFINDEX]) |
1205 | return ERR_PTR(error: -EINVAL); |
1206 | |
1207 | hardif_index = nla_get_u32(nla: info->attrs[BATADV_ATTR_HARD_IFINDEX]); |
1208 | |
1209 | hard_dev = dev_get_by_index(net, ifindex: hardif_index); |
1210 | if (!hard_dev) |
1211 | return ERR_PTR(error: -ENODEV); |
1212 | |
1213 | hard_iface = batadv_hardif_get_by_netdev(net_dev: hard_dev); |
1214 | if (!hard_iface) |
1215 | goto err_put_harddev; |
1216 | |
1217 | if (hard_iface->soft_iface != bat_priv->soft_iface) |
1218 | goto err_put_hardif; |
1219 | |
1220 | /* hard_dev is referenced by hard_iface and not needed here */ |
1221 | dev_put(dev: hard_dev); |
1222 | |
1223 | return hard_iface; |
1224 | |
1225 | err_put_hardif: |
1226 | batadv_hardif_put(hard_iface); |
1227 | err_put_harddev: |
1228 | dev_put(dev: hard_dev); |
1229 | |
1230 | return ERR_PTR(error: -EINVAL); |
1231 | } |
1232 | |
1233 | /** |
1234 | * batadv_get_vlan_from_info() - Retrieve vlan from genl attributes |
1235 | * @bat_priv: the bat priv with all the soft interface information |
1236 | * @net: the applicable net namespace |
1237 | * @info: receiver information |
1238 | * |
1239 | * Return: Pointer to vlan on success (with increased refcnt), error pointer |
1240 | * on error |
1241 | */ |
1242 | static struct batadv_softif_vlan * |
1243 | batadv_get_vlan_from_info(struct batadv_priv *bat_priv, struct net *net, |
1244 | struct genl_info *info) |
1245 | { |
1246 | struct batadv_softif_vlan *vlan; |
1247 | u16 vid; |
1248 | |
1249 | if (!info->attrs[BATADV_ATTR_VLANID]) |
1250 | return ERR_PTR(error: -EINVAL); |
1251 | |
1252 | vid = nla_get_u16(nla: info->attrs[BATADV_ATTR_VLANID]); |
1253 | |
1254 | vlan = batadv_softif_vlan_get(bat_priv, vid: vid | BATADV_VLAN_HAS_TAG); |
1255 | if (!vlan) |
1256 | return ERR_PTR(error: -ENOENT); |
1257 | |
1258 | return vlan; |
1259 | } |
1260 | |
1261 | /** |
1262 | * batadv_pre_doit() - Prepare batman-adv genl doit request |
1263 | * @ops: requested netlink operation |
1264 | * @skb: Netlink message with request data |
1265 | * @info: receiver information |
1266 | * |
1267 | * Return: 0 on success or negative error number in case of failure |
1268 | */ |
1269 | static int batadv_pre_doit(const struct genl_split_ops *ops, |
1270 | struct sk_buff *skb, |
1271 | struct genl_info *info) |
1272 | { |
1273 | struct net *net = genl_info_net(info); |
1274 | struct batadv_hard_iface *hard_iface; |
1275 | struct batadv_priv *bat_priv = NULL; |
1276 | struct batadv_softif_vlan *vlan; |
1277 | struct net_device *soft_iface; |
1278 | u8 user_ptr1_flags; |
1279 | u8 mesh_dep_flags; |
1280 | int ret; |
1281 | |
1282 | user_ptr1_flags = BATADV_FLAG_NEED_HARDIF | BATADV_FLAG_NEED_VLAN; |
1283 | if (WARN_ON(hweight8(ops->internal_flags & user_ptr1_flags) > 1)) |
1284 | return -EINVAL; |
1285 | |
1286 | mesh_dep_flags = BATADV_FLAG_NEED_HARDIF | BATADV_FLAG_NEED_VLAN; |
1287 | if (WARN_ON((ops->internal_flags & mesh_dep_flags) && |
1288 | (~ops->internal_flags & BATADV_FLAG_NEED_MESH))) |
1289 | return -EINVAL; |
1290 | |
1291 | if (ops->internal_flags & BATADV_FLAG_NEED_MESH) { |
1292 | soft_iface = batadv_get_softif_from_info(net, info); |
1293 | if (IS_ERR(ptr: soft_iface)) |
1294 | return PTR_ERR(ptr: soft_iface); |
1295 | |
1296 | bat_priv = netdev_priv(dev: soft_iface); |
1297 | info->user_ptr[0] = bat_priv; |
1298 | } |
1299 | |
1300 | if (ops->internal_flags & BATADV_FLAG_NEED_HARDIF) { |
1301 | hard_iface = batadv_get_hardif_from_info(bat_priv, net, info); |
1302 | if (IS_ERR(ptr: hard_iface)) { |
1303 | ret = PTR_ERR(ptr: hard_iface); |
1304 | goto err_put_softif; |
1305 | } |
1306 | |
1307 | info->user_ptr[1] = hard_iface; |
1308 | } |
1309 | |
1310 | if (ops->internal_flags & BATADV_FLAG_NEED_VLAN) { |
1311 | vlan = batadv_get_vlan_from_info(bat_priv, net, info); |
1312 | if (IS_ERR(ptr: vlan)) { |
1313 | ret = PTR_ERR(ptr: vlan); |
1314 | goto err_put_softif; |
1315 | } |
1316 | |
1317 | info->user_ptr[1] = vlan; |
1318 | } |
1319 | |
1320 | return 0; |
1321 | |
1322 | err_put_softif: |
1323 | if (bat_priv) |
1324 | dev_put(dev: bat_priv->soft_iface); |
1325 | |
1326 | return ret; |
1327 | } |
1328 | |
1329 | /** |
1330 | * batadv_post_doit() - End batman-adv genl doit request |
1331 | * @ops: requested netlink operation |
1332 | * @skb: Netlink message with request data |
1333 | * @info: receiver information |
1334 | */ |
1335 | static void batadv_post_doit(const struct genl_split_ops *ops, |
1336 | struct sk_buff *skb, |
1337 | struct genl_info *info) |
1338 | { |
1339 | struct batadv_hard_iface *hard_iface; |
1340 | struct batadv_softif_vlan *vlan; |
1341 | struct batadv_priv *bat_priv; |
1342 | |
1343 | if (ops->internal_flags & BATADV_FLAG_NEED_HARDIF && |
1344 | info->user_ptr[1]) { |
1345 | hard_iface = info->user_ptr[1]; |
1346 | |
1347 | batadv_hardif_put(hard_iface); |
1348 | } |
1349 | |
1350 | if (ops->internal_flags & BATADV_FLAG_NEED_VLAN && info->user_ptr[1]) { |
1351 | vlan = info->user_ptr[1]; |
1352 | batadv_softif_vlan_put(vlan); |
1353 | } |
1354 | |
1355 | if (ops->internal_flags & BATADV_FLAG_NEED_MESH && info->user_ptr[0]) { |
1356 | bat_priv = info->user_ptr[0]; |
1357 | dev_put(dev: bat_priv->soft_iface); |
1358 | } |
1359 | } |
1360 | |
1361 | static const struct genl_small_ops batadv_netlink_ops[] = { |
1362 | { |
1363 | .cmd = BATADV_CMD_GET_MESH, |
1364 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
1365 | /* can be retrieved by unprivileged users */ |
1366 | .doit = batadv_netlink_get_mesh, |
1367 | .internal_flags = BATADV_FLAG_NEED_MESH, |
1368 | }, |
1369 | { |
1370 | .cmd = BATADV_CMD_TP_METER, |
1371 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
1372 | .flags = GENL_UNS_ADMIN_PERM, |
1373 | .doit = batadv_netlink_tp_meter_start, |
1374 | .internal_flags = BATADV_FLAG_NEED_MESH, |
1375 | }, |
1376 | { |
1377 | .cmd = BATADV_CMD_TP_METER_CANCEL, |
1378 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
1379 | .flags = GENL_UNS_ADMIN_PERM, |
1380 | .doit = batadv_netlink_tp_meter_cancel, |
1381 | .internal_flags = BATADV_FLAG_NEED_MESH, |
1382 | }, |
1383 | { |
1384 | .cmd = BATADV_CMD_GET_ROUTING_ALGOS, |
1385 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
1386 | .flags = GENL_UNS_ADMIN_PERM, |
1387 | .dumpit = batadv_algo_dump, |
1388 | }, |
1389 | { |
1390 | .cmd = BATADV_CMD_GET_HARDIF, |
1391 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
1392 | /* can be retrieved by unprivileged users */ |
1393 | .dumpit = batadv_netlink_dump_hardif, |
1394 | .doit = batadv_netlink_get_hardif, |
1395 | .internal_flags = BATADV_FLAG_NEED_MESH | |
1396 | BATADV_FLAG_NEED_HARDIF, |
1397 | }, |
1398 | { |
1399 | .cmd = BATADV_CMD_GET_TRANSTABLE_LOCAL, |
1400 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
1401 | .flags = GENL_UNS_ADMIN_PERM, |
1402 | .dumpit = batadv_tt_local_dump, |
1403 | }, |
1404 | { |
1405 | .cmd = BATADV_CMD_GET_TRANSTABLE_GLOBAL, |
1406 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
1407 | .flags = GENL_UNS_ADMIN_PERM, |
1408 | .dumpit = batadv_tt_global_dump, |
1409 | }, |
1410 | { |
1411 | .cmd = BATADV_CMD_GET_ORIGINATORS, |
1412 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
1413 | .flags = GENL_UNS_ADMIN_PERM, |
1414 | .dumpit = batadv_orig_dump, |
1415 | }, |
1416 | { |
1417 | .cmd = BATADV_CMD_GET_NEIGHBORS, |
1418 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
1419 | .flags = GENL_UNS_ADMIN_PERM, |
1420 | .dumpit = batadv_hardif_neigh_dump, |
1421 | }, |
1422 | { |
1423 | .cmd = BATADV_CMD_GET_GATEWAYS, |
1424 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
1425 | .flags = GENL_UNS_ADMIN_PERM, |
1426 | .dumpit = batadv_gw_dump, |
1427 | }, |
1428 | { |
1429 | .cmd = BATADV_CMD_GET_BLA_CLAIM, |
1430 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
1431 | .flags = GENL_UNS_ADMIN_PERM, |
1432 | .dumpit = batadv_bla_claim_dump, |
1433 | }, |
1434 | { |
1435 | .cmd = BATADV_CMD_GET_BLA_BACKBONE, |
1436 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
1437 | .flags = GENL_UNS_ADMIN_PERM, |
1438 | .dumpit = batadv_bla_backbone_dump, |
1439 | }, |
1440 | { |
1441 | .cmd = BATADV_CMD_GET_DAT_CACHE, |
1442 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
1443 | .flags = GENL_UNS_ADMIN_PERM, |
1444 | .dumpit = batadv_dat_cache_dump, |
1445 | }, |
1446 | { |
1447 | .cmd = BATADV_CMD_GET_MCAST_FLAGS, |
1448 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
1449 | .flags = GENL_UNS_ADMIN_PERM, |
1450 | .dumpit = batadv_mcast_flags_dump, |
1451 | }, |
1452 | { |
1453 | .cmd = BATADV_CMD_SET_MESH, |
1454 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
1455 | .flags = GENL_UNS_ADMIN_PERM, |
1456 | .doit = batadv_netlink_set_mesh, |
1457 | .internal_flags = BATADV_FLAG_NEED_MESH, |
1458 | }, |
1459 | { |
1460 | .cmd = BATADV_CMD_SET_HARDIF, |
1461 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
1462 | .flags = GENL_UNS_ADMIN_PERM, |
1463 | .doit = batadv_netlink_set_hardif, |
1464 | .internal_flags = BATADV_FLAG_NEED_MESH | |
1465 | BATADV_FLAG_NEED_HARDIF, |
1466 | }, |
1467 | { |
1468 | .cmd = BATADV_CMD_GET_VLAN, |
1469 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
1470 | /* can be retrieved by unprivileged users */ |
1471 | .doit = batadv_netlink_get_vlan, |
1472 | .internal_flags = BATADV_FLAG_NEED_MESH | |
1473 | BATADV_FLAG_NEED_VLAN, |
1474 | }, |
1475 | { |
1476 | .cmd = BATADV_CMD_SET_VLAN, |
1477 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
1478 | .flags = GENL_UNS_ADMIN_PERM, |
1479 | .doit = batadv_netlink_set_vlan, |
1480 | .internal_flags = BATADV_FLAG_NEED_MESH | |
1481 | BATADV_FLAG_NEED_VLAN, |
1482 | }, |
1483 | }; |
1484 | |
1485 | struct genl_family batadv_netlink_family __ro_after_init = { |
1486 | .hdrsize = 0, |
1487 | .name = BATADV_NL_NAME, |
1488 | .version = 1, |
1489 | .maxattr = BATADV_ATTR_MAX, |
1490 | .policy = batadv_netlink_policy, |
1491 | .netnsok = true, |
1492 | .pre_doit = batadv_pre_doit, |
1493 | .post_doit = batadv_post_doit, |
1494 | .module = THIS_MODULE, |
1495 | .small_ops = batadv_netlink_ops, |
1496 | .n_small_ops = ARRAY_SIZE(batadv_netlink_ops), |
1497 | .resv_start_op = BATADV_CMD_SET_VLAN + 1, |
1498 | .mcgrps = batadv_netlink_mcgrps, |
1499 | .n_mcgrps = ARRAY_SIZE(batadv_netlink_mcgrps), |
1500 | }; |
1501 | |
1502 | /** |
1503 | * batadv_netlink_register() - register batadv genl netlink family |
1504 | */ |
1505 | void __init batadv_netlink_register(void) |
1506 | { |
1507 | int ret; |
1508 | |
1509 | ret = genl_register_family(family: &batadv_netlink_family); |
1510 | if (ret) |
1511 | pr_warn("unable to register netlink family" ); |
1512 | } |
1513 | |
1514 | /** |
1515 | * batadv_netlink_unregister() - unregister batadv genl netlink family |
1516 | */ |
1517 | void batadv_netlink_unregister(void) |
1518 | { |
1519 | genl_unregister_family(family: &batadv_netlink_family); |
1520 | } |
1521 | |