blob: 19fb2657e274e2372be5a95021e11bdf05f62c9c [file] [log] [blame]
Matthias Schiffer09748a22016-05-09 18:41:08 +02001/* Copyright (C) 2016 B.A.T.M.A.N. contributors:
2 *
3 * Matthias Schiffer
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of version 2 of the GNU General Public
7 * License as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "netlink.h"
19#include "main.h"
20
Matthias Schiffer5da0aef2016-05-09 18:41:09 +020021#include <linux/errno.h>
22#include <linux/fs.h>
Matthias Schiffer09748a22016-05-09 18:41:08 +020023#include <linux/genetlink.h>
Matthias Schiffer5da0aef2016-05-09 18:41:09 +020024#include <linux/if_ether.h>
Matthias Schiffer09748a22016-05-09 18:41:08 +020025#include <linux/init.h>
Matthias Schiffer5da0aef2016-05-09 18:41:09 +020026#include <linux/netdevice.h>
27#include <linux/netlink.h>
Matthias Schiffer09748a22016-05-09 18:41:08 +020028#include <linux/printk.h>
Matthias Schiffer5da0aef2016-05-09 18:41:09 +020029#include <linux/stddef.h>
Antonio Quartulli33a3bb42016-05-05 13:09:43 +020030#include <linux/types.h>
Matthias Schiffer09748a22016-05-09 18:41:08 +020031#include <net/genetlink.h>
Matthias Schiffer5da0aef2016-05-09 18:41:09 +020032#include <net/netlink.h>
Matthias Schiffer09748a22016-05-09 18:41:08 +020033#include <uapi/linux/batman_adv.h>
34
Matthias Schiffer07a30612016-07-03 13:31:35 +020035#include "bat_algo.h"
Matthias Schiffer5da0aef2016-05-09 18:41:09 +020036#include "hard-interface.h"
37#include "soft-interface.h"
Antonio Quartulli33a3bb42016-05-05 13:09:43 +020038#include "tp_meter.h"
Matthias Schiffer5da0aef2016-05-09 18:41:09 +020039
40struct sk_buff;
41
Matthias Schiffer07a30612016-07-03 13:31:35 +020042struct genl_family batadv_netlink_family = {
Matthias Schiffer09748a22016-05-09 18:41:08 +020043 .id = GENL_ID_GENERATE,
44 .hdrsize = 0,
45 .name = BATADV_NL_NAME,
46 .version = 1,
47 .maxattr = BATADV_ATTR_MAX,
48};
49
Antonio Quartulli33a3bb42016-05-05 13:09:43 +020050/* multicast groups */
51enum batadv_netlink_multicast_groups {
52 BATADV_NL_MCGRP_TPMETER,
53};
54
55static struct genl_multicast_group batadv_netlink_mcgrps[] = {
56 [BATADV_NL_MCGRP_TPMETER] = { .name = BATADV_NL_MCAST_GROUP_TPMETER },
57};
58
Matthias Schiffer5da0aef2016-05-09 18:41:09 +020059static struct nla_policy batadv_netlink_policy[NUM_BATADV_ATTR] = {
60 [BATADV_ATTR_VERSION] = { .type = NLA_STRING },
61 [BATADV_ATTR_ALGO_NAME] = { .type = NLA_STRING },
62 [BATADV_ATTR_MESH_IFINDEX] = { .type = NLA_U32 },
63 [BATADV_ATTR_MESH_IFNAME] = { .type = NLA_STRING },
64 [BATADV_ATTR_MESH_ADDRESS] = { .len = ETH_ALEN },
65 [BATADV_ATTR_HARD_IFINDEX] = { .type = NLA_U32 },
66 [BATADV_ATTR_HARD_IFNAME] = { .type = NLA_STRING },
67 [BATADV_ATTR_HARD_ADDRESS] = { .len = ETH_ALEN },
Antonio Quartulli33a3bb42016-05-05 13:09:43 +020068 [BATADV_ATTR_ORIG_ADDRESS] = { .len = ETH_ALEN },
69 [BATADV_ATTR_TPMETER_RESULT] = { .type = NLA_U8 },
70 [BATADV_ATTR_TPMETER_TEST_TIME] = { .type = NLA_U32 },
71 [BATADV_ATTR_TPMETER_BYTES] = { .type = NLA_U64 },
72 [BATADV_ATTR_TPMETER_COOKIE] = { .type = NLA_U32 },
Matthias Schiffer5da0aef2016-05-09 18:41:09 +020073};
74
75/**
76 * batadv_netlink_mesh_info_put - fill in generic information about mesh
77 * interface
78 * @msg: netlink message to be sent back
79 * @soft_iface: interface for which the data should be taken
80 *
81 * Return: 0 on success, < 0 on error
82 */
83static int
84batadv_netlink_mesh_info_put(struct sk_buff *msg, struct net_device *soft_iface)
85{
86 struct batadv_priv *bat_priv = netdev_priv(soft_iface);
87 struct batadv_hard_iface *primary_if = NULL;
88 struct net_device *hard_iface;
89 int ret = -ENOBUFS;
90
91 if (nla_put_string(msg, BATADV_ATTR_VERSION, BATADV_SOURCE_VERSION) ||
92 nla_put_string(msg, BATADV_ATTR_ALGO_NAME,
Antonio Quartulli29824a52016-05-25 23:27:31 +080093 bat_priv->algo_ops->name) ||
Matthias Schiffer5da0aef2016-05-09 18:41:09 +020094 nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, soft_iface->ifindex) ||
95 nla_put_string(msg, BATADV_ATTR_MESH_IFNAME, soft_iface->name) ||
96 nla_put(msg, BATADV_ATTR_MESH_ADDRESS, ETH_ALEN,
97 soft_iface->dev_addr))
98 goto out;
99
100 primary_if = batadv_primary_if_get_selected(bat_priv);
101 if (primary_if && primary_if->if_status == BATADV_IF_ACTIVE) {
102 hard_iface = primary_if->net_dev;
103
104 if (nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX,
105 hard_iface->ifindex) ||
106 nla_put_string(msg, BATADV_ATTR_HARD_IFNAME,
107 hard_iface->name) ||
108 nla_put(msg, BATADV_ATTR_HARD_ADDRESS, ETH_ALEN,
109 hard_iface->dev_addr))
110 goto out;
111 }
112
113 ret = 0;
114
115 out:
116 if (primary_if)
117 batadv_hardif_put(primary_if);
118
119 return ret;
120}
121
122/**
123 * batadv_netlink_get_mesh_info - handle incoming BATADV_CMD_GET_MESH_INFO
124 * netlink request
125 * @skb: received netlink message
126 * @info: receiver information
127 *
128 * Return: 0 on success, < 0 on error
129 */
130static int
131batadv_netlink_get_mesh_info(struct sk_buff *skb, struct genl_info *info)
132{
133 struct net *net = genl_info_net(info);
134 struct net_device *soft_iface;
135 struct sk_buff *msg = NULL;
136 void *msg_head;
137 int ifindex;
138 int ret;
139
140 if (!info->attrs[BATADV_ATTR_MESH_IFINDEX])
141 return -EINVAL;
142
143 ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]);
144 if (!ifindex)
145 return -EINVAL;
146
147 soft_iface = dev_get_by_index(net, ifindex);
148 if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
149 ret = -ENODEV;
150 goto out;
151 }
152
153 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
154 if (!msg) {
155 ret = -ENOMEM;
156 goto out;
157 }
158
159 msg_head = genlmsg_put(msg, info->snd_portid, info->snd_seq,
160 &batadv_netlink_family, 0,
161 BATADV_CMD_GET_MESH_INFO);
162 if (!msg_head) {
163 ret = -ENOBUFS;
164 goto out;
165 }
166
167 ret = batadv_netlink_mesh_info_put(msg, soft_iface);
168
169 out:
170 if (soft_iface)
171 dev_put(soft_iface);
172
173 if (ret) {
174 if (msg)
175 nlmsg_free(msg);
176 return ret;
177 }
178
179 genlmsg_end(msg, msg_head);
180 return genlmsg_reply(msg, info);
181}
182
Antonio Quartulli33a3bb42016-05-05 13:09:43 +0200183/**
184 * batadv_netlink_tp_meter_put - Fill information of started tp_meter session
185 * @msg: netlink message to be sent back
186 * @cookie: tp meter session cookie
187 *
188 * Return: 0 on success, < 0 on error
189 */
190static int
191batadv_netlink_tp_meter_put(struct sk_buff *msg, u32 cookie)
192{
193 if (nla_put_u32(msg, BATADV_ATTR_TPMETER_COOKIE, cookie))
194 return -ENOBUFS;
195
196 return 0;
197}
198
199/**
200 * batadv_netlink_tpmeter_notify - send tp_meter result via netlink to client
201 * @bat_priv: the bat priv with all the soft interface information
202 * @dst: destination of tp_meter session
203 * @result: reason for tp meter session stop
204 * @test_time: total time ot the tp_meter session
205 * @total_bytes: bytes acked to the receiver
206 * @cookie: cookie of tp_meter session
207 *
208 * Return: 0 on success, < 0 on error
209 */
210int batadv_netlink_tpmeter_notify(struct batadv_priv *bat_priv, const u8 *dst,
211 u8 result, u32 test_time, u64 total_bytes,
212 u32 cookie)
213{
214 struct sk_buff *msg;
215 void *hdr;
216 int ret;
217
218 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
219 if (!msg)
220 return -ENOMEM;
221
222 hdr = genlmsg_put(msg, 0, 0, &batadv_netlink_family, 0,
223 BATADV_CMD_TP_METER);
224 if (!hdr) {
225 ret = -ENOBUFS;
226 goto err_genlmsg;
227 }
228
229 if (nla_put_u32(msg, BATADV_ATTR_TPMETER_COOKIE, cookie))
230 goto nla_put_failure;
231
232 if (nla_put_u32(msg, BATADV_ATTR_TPMETER_TEST_TIME, test_time))
233 goto nla_put_failure;
234
235 if (nla_put_u64_64bit(msg, BATADV_ATTR_TPMETER_BYTES, total_bytes,
236 BATADV_ATTR_PAD))
237 goto nla_put_failure;
238
239 if (nla_put_u8(msg, BATADV_ATTR_TPMETER_RESULT, result))
240 goto nla_put_failure;
241
242 if (nla_put(msg, BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN, dst))
243 goto nla_put_failure;
244
245 genlmsg_end(msg, hdr);
246
247 genlmsg_multicast_netns(&batadv_netlink_family,
248 dev_net(bat_priv->soft_iface), msg, 0,
249 BATADV_NL_MCGRP_TPMETER, GFP_KERNEL);
250
251 return 0;
252
253nla_put_failure:
254 genlmsg_cancel(msg, hdr);
255 ret = -EMSGSIZE;
256
257err_genlmsg:
258 nlmsg_free(msg);
259 return ret;
260}
261
262/**
263 * batadv_netlink_tp_meter_start - Start a new tp_meter session
264 * @skb: received netlink message
265 * @info: receiver information
266 *
267 * Return: 0 on success, < 0 on error
268 */
269static int
270batadv_netlink_tp_meter_start(struct sk_buff *skb, struct genl_info *info)
271{
272 struct net *net = genl_info_net(info);
273 struct net_device *soft_iface;
274 struct batadv_priv *bat_priv;
275 struct sk_buff *msg = NULL;
276 u32 test_length;
277 void *msg_head;
278 int ifindex;
279 u32 cookie;
280 u8 *dst;
281 int ret;
282
283 if (!info->attrs[BATADV_ATTR_MESH_IFINDEX])
284 return -EINVAL;
285
286 if (!info->attrs[BATADV_ATTR_ORIG_ADDRESS])
287 return -EINVAL;
288
289 if (!info->attrs[BATADV_ATTR_TPMETER_TEST_TIME])
290 return -EINVAL;
291
292 ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]);
293 if (!ifindex)
294 return -EINVAL;
295
296 dst = nla_data(info->attrs[BATADV_ATTR_ORIG_ADDRESS]);
297
298 test_length = nla_get_u32(info->attrs[BATADV_ATTR_TPMETER_TEST_TIME]);
299
300 soft_iface = dev_get_by_index(net, ifindex);
301 if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
302 ret = -ENODEV;
303 goto out;
304 }
305
306 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
307 if (!msg) {
308 ret = -ENOMEM;
309 goto out;
310 }
311
312 msg_head = genlmsg_put(msg, info->snd_portid, info->snd_seq,
313 &batadv_netlink_family, 0,
314 BATADV_CMD_TP_METER);
315 if (!msg_head) {
316 ret = -ENOBUFS;
317 goto out;
318 }
319
320 bat_priv = netdev_priv(soft_iface);
321 batadv_tp_start(bat_priv, dst, test_length, &cookie);
322
323 ret = batadv_netlink_tp_meter_put(msg, cookie);
324
325 out:
326 if (soft_iface)
327 dev_put(soft_iface);
328
329 if (ret) {
330 if (msg)
331 nlmsg_free(msg);
332 return ret;
333 }
334
335 genlmsg_end(msg, msg_head);
336 return genlmsg_reply(msg, info);
337}
338
339/**
340 * batadv_netlink_tp_meter_start - Cancel a running tp_meter session
341 * @skb: received netlink message
342 * @info: receiver information
343 *
344 * Return: 0 on success, < 0 on error
345 */
346static int
347batadv_netlink_tp_meter_cancel(struct sk_buff *skb, struct genl_info *info)
348{
349 struct net *net = genl_info_net(info);
350 struct net_device *soft_iface;
351 struct batadv_priv *bat_priv;
352 int ifindex;
353 u8 *dst;
354 int ret = 0;
355
356 if (!info->attrs[BATADV_ATTR_MESH_IFINDEX])
357 return -EINVAL;
358
359 if (!info->attrs[BATADV_ATTR_ORIG_ADDRESS])
360 return -EINVAL;
361
362 ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]);
363 if (!ifindex)
364 return -EINVAL;
365
366 dst = nla_data(info->attrs[BATADV_ATTR_ORIG_ADDRESS]);
367
368 soft_iface = dev_get_by_index(net, ifindex);
369 if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
370 ret = -ENODEV;
371 goto out;
372 }
373
374 bat_priv = netdev_priv(soft_iface);
375 batadv_tp_stop(bat_priv, dst, BATADV_TP_REASON_CANCEL);
376
377out:
378 if (soft_iface)
379 dev_put(soft_iface);
380
381 return ret;
382}
383
Matthias Schiffer09748a22016-05-09 18:41:08 +0200384static struct genl_ops batadv_netlink_ops[] = {
Matthias Schiffer5da0aef2016-05-09 18:41:09 +0200385 {
386 .cmd = BATADV_CMD_GET_MESH_INFO,
387 .flags = GENL_ADMIN_PERM,
388 .policy = batadv_netlink_policy,
389 .doit = batadv_netlink_get_mesh_info,
390 },
Antonio Quartulli33a3bb42016-05-05 13:09:43 +0200391 {
392 .cmd = BATADV_CMD_TP_METER,
393 .flags = GENL_ADMIN_PERM,
394 .policy = batadv_netlink_policy,
395 .doit = batadv_netlink_tp_meter_start,
396 },
397 {
398 .cmd = BATADV_CMD_TP_METER_CANCEL,
399 .flags = GENL_ADMIN_PERM,
400 .policy = batadv_netlink_policy,
401 .doit = batadv_netlink_tp_meter_cancel,
402 },
Matthias Schiffer07a30612016-07-03 13:31:35 +0200403 {
404 .cmd = BATADV_CMD_GET_ROUTING_ALGOS,
405 .flags = GENL_ADMIN_PERM,
406 .policy = batadv_netlink_policy,
407 .dumpit = batadv_algo_dump,
408 },
Matthias Schiffer09748a22016-05-09 18:41:08 +0200409};
410
411/**
412 * batadv_netlink_register - register batadv genl netlink family
413 */
414void __init batadv_netlink_register(void)
415{
416 int ret;
417
Antonio Quartulli33a3bb42016-05-05 13:09:43 +0200418 ret = genl_register_family_with_ops_groups(&batadv_netlink_family,
419 batadv_netlink_ops,
420 batadv_netlink_mcgrps);
Matthias Schiffer09748a22016-05-09 18:41:08 +0200421 if (ret)
422 pr_warn("unable to register netlink family");
423}
424
425/**
426 * batadv_netlink_unregister - unregister batadv genl netlink family
427 */
428void batadv_netlink_unregister(void)
429{
430 genl_unregister_family(&batadv_netlink_family);
431}