blob: 871eaf2cb85e4d4bec7dd01292c4241dff82c29e [file] [log] [blame]
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -07001/*
2 * File: pn_netlink.c
3 *
4 * Phonet netlink interface
5 *
6 * Copyright (C) 2008 Nokia Corporation.
7 *
Rémi Denis-Courmont31fdc552012-06-13 22:29:03 +00008 * Authors: Sakari Ailus <sakari.ailus@nokia.com>
9 * Remi Denis-Courmont
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -070010 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * version 2 as published by the Free Software Foundation.
14 *
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23 * 02110-1301 USA
24 */
25
26#include <linux/kernel.h>
27#include <linux/netlink.h>
28#include <linux/phonet.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090029#include <linux/slab.h>
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -070030#include <net/sock.h>
31#include <net/phonet/pn_dev.h>
32
Rémi Denis-Courmontf062f412009-10-14 00:48:29 +000033/* Device address handling */
34
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -070035static int fill_addr(struct sk_buff *skb, struct net_device *dev, u8 addr,
Eric W. Biederman15e47302012-09-07 20:12:54 +000036 u32 portid, u32 seq, int event);
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -070037
Rémi Denis-Courmontc7a1a4c2009-06-24 01:07:44 +000038void phonet_address_notify(int event, struct net_device *dev, u8 addr)
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -070039{
40 struct sk_buff *skb;
41 int err = -ENOBUFS;
42
43 skb = nlmsg_new(NLMSG_ALIGN(sizeof(struct ifaddrmsg)) +
44 nla_total_size(1), GFP_KERNEL);
45 if (skb == NULL)
46 goto errout;
47 err = fill_addr(skb, dev, addr, 0, 0, event);
48 if (err < 0) {
49 WARN_ON(err == -EMSGSIZE);
50 kfree_skb(skb);
51 goto errout;
52 }
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -080053 rtnl_notify(skb, dev_net(dev), 0,
54 RTNLGRP_PHONET_IFADDR, NULL, GFP_KERNEL);
55 return;
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -070056errout:
Rémi Denis-Courmont4b7673a2009-11-02 22:41:28 +000057 rtnl_set_sk_err(dev_net(dev), RTNLGRP_PHONET_IFADDR, err);
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -070058}
59
Rémi Denis-Courmont89807132008-09-30 02:51:18 -070060static const struct nla_policy ifa_phonet_policy[IFA_MAX+1] = {
61 [IFA_LOCAL] = { .type = NLA_U8 },
62};
63
David Ahernc21ef3e2017-04-16 09:48:24 -070064static int addr_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
65 struct netlink_ext_ack *extack)
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -070066{
Rémi Denis-Courmont89807132008-09-30 02:51:18 -070067 struct net *net = sock_net(skb->sk);
68 struct nlattr *tb[IFA_MAX+1];
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -070069 struct net_device *dev;
Rémi Denis-Courmont89807132008-09-30 02:51:18 -070070 struct ifaddrmsg *ifm;
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -070071 int err;
72 u8 pnaddr;
73
Eric W. Biederman90f62cf2014-04-23 14:29:27 -070074 if (!netlink_capable(skb, CAP_NET_ADMIN))
Eric W. Biedermandfc47ef2012-11-16 03:03:00 +000075 return -EPERM;
76
Eric W. Biederman90f62cf2014-04-23 14:29:27 -070077 if (!netlink_capable(skb, CAP_SYS_ADMIN))
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -070078 return -EPERM;
79
80 ASSERT_RTNL();
81
Johannes Bergfceb6432017-04-12 14:34:07 +020082 err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_phonet_policy,
David Ahernc21ef3e2017-04-16 09:48:24 -070083 extack);
Rémi Denis-Courmont89807132008-09-30 02:51:18 -070084 if (err < 0)
85 return err;
86
87 ifm = nlmsg_data(nlh);
88 if (tb[IFA_LOCAL] == NULL)
89 return -EINVAL;
90 pnaddr = nla_get_u8(tb[IFA_LOCAL]);
91 if (pnaddr & 3)
92 /* Phonet addresses only have 6 high-order bits */
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -070093 return -EINVAL;
94
Rémi Denis-Courmont89807132008-09-30 02:51:18 -070095 dev = __dev_get_by_index(net, ifm->ifa_index);
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -070096 if (dev == NULL)
97 return -ENODEV;
98
Rémi Denis-Courmont89807132008-09-30 02:51:18 -070099 if (nlh->nlmsg_type == RTM_NEWADDR)
100 err = phonet_address_add(dev, pnaddr);
101 else
102 err = phonet_address_del(dev, pnaddr);
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -0700103 if (!err)
Rémi Denis-Courmontc7a1a4c2009-06-24 01:07:44 +0000104 phonet_address_notify(nlh->nlmsg_type, dev, pnaddr);
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -0700105 return err;
106}
107
108static int fill_addr(struct sk_buff *skb, struct net_device *dev, u8 addr,
Eric W. Biederman15e47302012-09-07 20:12:54 +0000109 u32 portid, u32 seq, int event)
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -0700110{
111 struct ifaddrmsg *ifm;
112 struct nlmsghdr *nlh;
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -0700113
Eric W. Biederman15e47302012-09-07 20:12:54 +0000114 nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), 0);
Rémi Denis-Courmont89807132008-09-30 02:51:18 -0700115 if (nlh == NULL)
116 return -EMSGSIZE;
117
118 ifm = nlmsg_data(nlh);
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -0700119 ifm->ifa_family = AF_PHONET;
120 ifm->ifa_prefixlen = 0;
121 ifm->ifa_flags = IFA_F_PERMANENT;
Rémi Denis-Courmont89807132008-09-30 02:51:18 -0700122 ifm->ifa_scope = RT_SCOPE_LINK;
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -0700123 ifm->ifa_index = dev->ifindex;
David S. Miller7f116b52012-03-29 23:15:10 -0400124 if (nla_put_u8(skb, IFA_LOCAL, addr))
125 goto nla_put_failure;
Johannes Berg053c0952015-01-16 22:09:00 +0100126 nlmsg_end(skb, nlh);
127 return 0;
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -0700128
Rémi Denis-Courmont89807132008-09-30 02:51:18 -0700129nla_put_failure:
130 nlmsg_cancel(skb, nlh);
131 return -EMSGSIZE;
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -0700132}
133
134static int getaddr_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
135{
remi.denis-courmont@nokia9a3b7a42009-01-23 03:00:30 +0000136 struct phonet_device_list *pndevs;
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -0700137 struct phonet_device *pnd;
138 int dev_idx = 0, dev_start_idx = cb->args[0];
139 int addr_idx = 0, addr_start_idx = cb->args[1];
140
remi.denis-courmont@nokia9a3b7a42009-01-23 03:00:30 +0000141 pndevs = phonet_device_list(sock_net(skb->sk));
Rémi Denis-Courmonteeb74a9d2009-11-18 10:08:26 -0800142 rcu_read_lock();
143 list_for_each_entry_rcu(pnd, &pndevs->list, list) {
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -0700144 u8 addr;
145
146 if (dev_idx > dev_start_idx)
147 addr_start_idx = 0;
148 if (dev_idx++ < dev_start_idx)
149 continue;
150
151 addr_idx = 0;
Akinobu Mitaa1ca14a2010-03-11 12:07:49 +0000152 for_each_set_bit(addr, pnd->addrs, 64) {
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -0700153 if (addr_idx++ < addr_start_idx)
154 continue;
155
156 if (fill_addr(skb, pnd->netdev, addr << 2,
Eric W. Biederman15e47302012-09-07 20:12:54 +0000157 NETLINK_CB(cb->skb).portid,
Rémi Denis-Courmont998ec752009-09-08 23:59:51 +0000158 cb->nlh->nlmsg_seq, RTM_NEWADDR) < 0)
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -0700159 goto out;
160 }
161 }
162
163out:
Rémi Denis-Courmonteeb74a9d2009-11-18 10:08:26 -0800164 rcu_read_unlock();
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -0700165 cb->args[0] = dev_idx;
166 cb->args[1] = addr_idx;
167
168 return skb->len;
169}
170
Rémi Denis-Courmontf062f412009-10-14 00:48:29 +0000171/* Routes handling */
172
173static int fill_route(struct sk_buff *skb, struct net_device *dev, u8 dst,
Eric W. Biederman15e47302012-09-07 20:12:54 +0000174 u32 portid, u32 seq, int event)
Rémi Denis-Courmontf062f412009-10-14 00:48:29 +0000175{
176 struct rtmsg *rtm;
177 struct nlmsghdr *nlh;
178
Eric W. Biederman15e47302012-09-07 20:12:54 +0000179 nlh = nlmsg_put(skb, portid, seq, event, sizeof(*rtm), 0);
Rémi Denis-Courmontf062f412009-10-14 00:48:29 +0000180 if (nlh == NULL)
181 return -EMSGSIZE;
182
183 rtm = nlmsg_data(nlh);
184 rtm->rtm_family = AF_PHONET;
185 rtm->rtm_dst_len = 6;
186 rtm->rtm_src_len = 0;
187 rtm->rtm_tos = 0;
188 rtm->rtm_table = RT_TABLE_MAIN;
189 rtm->rtm_protocol = RTPROT_STATIC;
190 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
191 rtm->rtm_type = RTN_UNICAST;
192 rtm->rtm_flags = 0;
David S. Miller7f116b52012-03-29 23:15:10 -0400193 if (nla_put_u8(skb, RTA_DST, dst) ||
194 nla_put_u32(skb, RTA_OIF, dev->ifindex))
195 goto nla_put_failure;
Johannes Berg053c0952015-01-16 22:09:00 +0100196 nlmsg_end(skb, nlh);
197 return 0;
Rémi Denis-Courmontf062f412009-10-14 00:48:29 +0000198
199nla_put_failure:
200 nlmsg_cancel(skb, nlh);
201 return -EMSGSIZE;
202}
203
204void rtm_phonet_notify(int event, struct net_device *dev, u8 dst)
205{
206 struct sk_buff *skb;
207 int err = -ENOBUFS;
208
209 skb = nlmsg_new(NLMSG_ALIGN(sizeof(struct ifaddrmsg)) +
210 nla_total_size(1) + nla_total_size(4), GFP_KERNEL);
211 if (skb == NULL)
212 goto errout;
213 err = fill_route(skb, dev, dst, 0, 0, event);
214 if (err < 0) {
215 WARN_ON(err == -EMSGSIZE);
216 kfree_skb(skb);
217 goto errout;
218 }
219 rtnl_notify(skb, dev_net(dev), 0,
220 RTNLGRP_PHONET_ROUTE, NULL, GFP_KERNEL);
221 return;
222errout:
Rémi Denis-Courmont4b7673a2009-11-02 22:41:28 +0000223 rtnl_set_sk_err(dev_net(dev), RTNLGRP_PHONET_ROUTE, err);
Rémi Denis-Courmontf062f412009-10-14 00:48:29 +0000224}
225
226static const struct nla_policy rtm_phonet_policy[RTA_MAX+1] = {
227 [RTA_DST] = { .type = NLA_U8 },
228 [RTA_OIF] = { .type = NLA_U32 },
229};
230
David Ahernc21ef3e2017-04-16 09:48:24 -0700231static int route_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
232 struct netlink_ext_ack *extack)
Rémi Denis-Courmontf062f412009-10-14 00:48:29 +0000233{
234 struct net *net = sock_net(skb->sk);
235 struct nlattr *tb[RTA_MAX+1];
236 struct net_device *dev;
237 struct rtmsg *rtm;
238 int err;
239 u8 dst;
240
Eric W. Biederman90f62cf2014-04-23 14:29:27 -0700241 if (!netlink_capable(skb, CAP_NET_ADMIN))
Eric W. Biedermandfc47ef2012-11-16 03:03:00 +0000242 return -EPERM;
243
Eric W. Biederman90f62cf2014-04-23 14:29:27 -0700244 if (!netlink_capable(skb, CAP_SYS_ADMIN))
Rémi Denis-Courmontf062f412009-10-14 00:48:29 +0000245 return -EPERM;
246
247 ASSERT_RTNL();
248
Johannes Bergfceb6432017-04-12 14:34:07 +0200249 err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_phonet_policy,
David Ahernc21ef3e2017-04-16 09:48:24 -0700250 extack);
Rémi Denis-Courmontf062f412009-10-14 00:48:29 +0000251 if (err < 0)
252 return err;
253
254 rtm = nlmsg_data(nlh);
255 if (rtm->rtm_table != RT_TABLE_MAIN || rtm->rtm_type != RTN_UNICAST)
256 return -EINVAL;
257 if (tb[RTA_DST] == NULL || tb[RTA_OIF] == NULL)
258 return -EINVAL;
259 dst = nla_get_u8(tb[RTA_DST]);
260 if (dst & 3) /* Phonet addresses only have 6 high-order bits */
261 return -EINVAL;
262
263 dev = __dev_get_by_index(net, nla_get_u32(tb[RTA_OIF]));
264 if (dev == NULL)
265 return -ENODEV;
266
267 if (nlh->nlmsg_type == RTM_NEWROUTE)
268 err = phonet_route_add(dev, dst);
269 else
270 err = phonet_route_del(dev, dst);
271 if (!err)
272 rtm_phonet_notify(nlh->nlmsg_type, dev, dst);
273 return err;
274}
275
276static int route_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
277{
278 struct net *net = sock_net(skb->sk);
Johannes Berg926e9872015-01-19 12:15:24 +0100279 u8 addr;
Rémi Denis-Courmontf062f412009-10-14 00:48:29 +0000280
Eric Dumazete67f88d2011-04-27 22:56:07 +0000281 rcu_read_lock();
Johannes Berg926e9872015-01-19 12:15:24 +0100282 for (addr = cb->args[0]; addr < 64; addr++) {
283 struct net_device *dev = phonet_route_get_rcu(net, addr << 2);
Rémi Denis-Courmontf062f412009-10-14 00:48:29 +0000284
Rémi Denis-Courmontf062f412009-10-14 00:48:29 +0000285 if (!dev)
286 continue;
287
Johannes Berg926e9872015-01-19 12:15:24 +0100288 if (fill_route(skb, dev, addr << 2, NETLINK_CB(cb->skb).portid,
289 cb->nlh->nlmsg_seq, RTM_NEWROUTE) < 0)
290 goto out;
Rémi Denis-Courmontf062f412009-10-14 00:48:29 +0000291 }
292
293out:
Eric Dumazete67f88d2011-04-27 22:56:07 +0000294 rcu_read_unlock();
Johannes Berg926e9872015-01-19 12:15:24 +0100295 cb->args[0] = addr;
Rémi Denis-Courmontf062f412009-10-14 00:48:29 +0000296
297 return skb->len;
298}
299
remi.denis-courmont@nokia660f7062009-01-23 03:00:28 +0000300int __init phonet_netlink_register(void)
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -0700301{
Florian Westphalc1c502b2017-12-02 21:44:07 +0100302 int err = rtnl_register_module(THIS_MODULE, PF_PHONET, RTM_NEWADDR,
303 addr_doit, NULL, 0);
remi.denis-courmont@nokia660f7062009-01-23 03:00:28 +0000304 if (err)
305 return err;
306
Florian Westphalc1c502b2017-12-02 21:44:07 +0100307 /* Further rtnl_register_module() cannot fail */
308 rtnl_register_module(THIS_MODULE, PF_PHONET, RTM_DELADDR,
309 addr_doit, NULL, 0);
310 rtnl_register_module(THIS_MODULE, PF_PHONET, RTM_GETADDR,
311 NULL, getaddr_dumpit, 0);
312 rtnl_register_module(THIS_MODULE, PF_PHONET, RTM_NEWROUTE,
313 route_doit, NULL, 0);
314 rtnl_register_module(THIS_MODULE, PF_PHONET, RTM_DELROUTE,
315 route_doit, NULL, 0);
316 rtnl_register_module(THIS_MODULE, PF_PHONET, RTM_GETROUTE,
317 NULL, route_dumpit, 0);
remi.denis-courmont@nokia660f7062009-01-23 03:00:28 +0000318 return 0;
Remi Denis-Courmont8fb39742008-09-22 20:04:30 -0700319}