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