Blame view

net/phonet/pn_netlink.c 7.53 KB
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
1
2
3
4
5
6
7
  /*
   * File: pn_netlink.c
   *
   * Phonet netlink interface
   *
   * Copyright (C) 2008 Nokia Corporation.
   *
31fdc5553   Rémi Denis-Courmont   net: remove my fu...
8
9
   * Authors: Sakari Ailus <sakari.ailus@nokia.com>
   *          Remi Denis-Courmont
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * version 2 as published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
   * 02110-1301 USA
   */
  
  #include <linux/kernel.h>
  #include <linux/netlink.h>
  #include <linux/phonet.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
29
  #include <linux/slab.h>
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
30
31
  #include <net/sock.h>
  #include <net/phonet/pn_dev.h>
f062f41d0   Rémi Denis-Courmont   Phonet: routing t...
32
  /* Device address handling */
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
33
  static int fill_addr(struct sk_buff *skb, struct net_device *dev, u8 addr,
15e473046   Eric W. Biederman   netlink: Rename p...
34
  		     u32 portid, u32 seq, int event);
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
35

c7a1a4c80   Rémi Denis-Courmont   Phonet: publicize...
36
  void phonet_address_notify(int event, struct net_device *dev, u8 addr)
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
37
38
39
40
41
42
43
44
45
46
47
48
49
50
  {
  	struct sk_buff *skb;
  	int err = -ENOBUFS;
  
  	skb = nlmsg_new(NLMSG_ALIGN(sizeof(struct ifaddrmsg)) +
  			nla_total_size(1), GFP_KERNEL);
  	if (skb == NULL)
  		goto errout;
  	err = fill_addr(skb, dev, addr, 0, 0, event);
  	if (err < 0) {
  		WARN_ON(err == -EMSGSIZE);
  		kfree_skb(skb);
  		goto errout;
  	}
1ce85fe40   Pablo Neira Ayuso   netlink: change n...
51
52
53
  	rtnl_notify(skb, dev_net(dev), 0,
  		    RTNLGRP_PHONET_IFADDR, NULL, GFP_KERNEL);
  	return;
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
54
  errout:
4b7673a04   Rémi Denis-Courmont   Phonet: remove ta...
55
  	rtnl_set_sk_err(dev_net(dev), RTNLGRP_PHONET_IFADDR, err);
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
56
  }
8980713b9   Rémi Denis-Courmont   Phonet: Netlink f...
57
58
59
  static const struct nla_policy ifa_phonet_policy[IFA_MAX+1] = {
  	[IFA_LOCAL] = { .type = NLA_U8 },
  };
c21ef3e34   David Ahern   net: rtnetlink: p...
60
61
  static int addr_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
  		     struct netlink_ext_ack *extack)
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
62
  {
8980713b9   Rémi Denis-Courmont   Phonet: Netlink f...
63
64
  	struct net *net = sock_net(skb->sk);
  	struct nlattr *tb[IFA_MAX+1];
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
65
  	struct net_device *dev;
8980713b9   Rémi Denis-Courmont   Phonet: Netlink f...
66
  	struct ifaddrmsg *ifm;
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
67
68
  	int err;
  	u8 pnaddr;
90f62cf30   Eric W. Biederman   net: Use netlink_...
69
  	if (!netlink_capable(skb, CAP_NET_ADMIN))
dfc47ef86   Eric W. Biederman   net: Push capable...
70
  		return -EPERM;
90f62cf30   Eric W. Biederman   net: Use netlink_...
71
  	if (!netlink_capable(skb, CAP_SYS_ADMIN))
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
72
73
74
  		return -EPERM;
  
  	ASSERT_RTNL();
fceb6435e   Johannes Berg   netlink: pass ext...
75
  	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_phonet_policy,
c21ef3e34   David Ahern   net: rtnetlink: p...
76
  			  extack);
8980713b9   Rémi Denis-Courmont   Phonet: Netlink f...
77
78
  	if (err < 0)
  		return err;
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
79

8980713b9   Rémi Denis-Courmont   Phonet: Netlink f...
80
81
  	ifm = nlmsg_data(nlh);
  	if (tb[IFA_LOCAL] == NULL)
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
82
  		return -EINVAL;
8980713b9   Rémi Denis-Courmont   Phonet: Netlink f...
83
84
85
  	pnaddr = nla_get_u8(tb[IFA_LOCAL]);
  	if (pnaddr & 3)
  		/* Phonet addresses only have 6 high-order bits */
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
86
  		return -EINVAL;
8980713b9   Rémi Denis-Courmont   Phonet: Netlink f...
87
  	dev = __dev_get_by_index(net, ifm->ifa_index);
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
88
89
  	if (dev == NULL)
  		return -ENODEV;
8980713b9   Rémi Denis-Courmont   Phonet: Netlink f...
90
91
92
93
  	if (nlh->nlmsg_type == RTM_NEWADDR)
  		err = phonet_address_add(dev, pnaddr);
  	else
  		err = phonet_address_del(dev, pnaddr);
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
94
  	if (!err)
c7a1a4c80   Rémi Denis-Courmont   Phonet: publicize...
95
  		phonet_address_notify(nlh->nlmsg_type, dev, pnaddr);
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
96
97
98
99
  	return err;
  }
  
  static int fill_addr(struct sk_buff *skb, struct net_device *dev, u8 addr,
15e473046   Eric W. Biederman   netlink: Rename p...
100
  			u32 portid, u32 seq, int event)
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
101
102
103
  {
  	struct ifaddrmsg *ifm;
  	struct nlmsghdr *nlh;
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
104

15e473046   Eric W. Biederman   netlink: Rename p...
105
  	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), 0);
8980713b9   Rémi Denis-Courmont   Phonet: Netlink f...
106
107
108
109
  	if (nlh == NULL)
  		return -EMSGSIZE;
  
  	ifm = nlmsg_data(nlh);
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
110
111
112
  	ifm->ifa_family = AF_PHONET;
  	ifm->ifa_prefixlen = 0;
  	ifm->ifa_flags = IFA_F_PERMANENT;
8980713b9   Rémi Denis-Courmont   Phonet: Netlink f...
113
  	ifm->ifa_scope = RT_SCOPE_LINK;
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
114
  	ifm->ifa_index = dev->ifindex;
7f116b5b6   David S. Miller   phonet: Stop usin...
115
116
  	if (nla_put_u8(skb, IFA_LOCAL, addr))
  		goto nla_put_failure;
053c095a8   Johannes Berg   netlink: make nlm...
117
118
  	nlmsg_end(skb, nlh);
  	return 0;
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
119

8980713b9   Rémi Denis-Courmont   Phonet: Netlink f...
120
121
122
  nla_put_failure:
  	nlmsg_cancel(skb, nlh);
  	return -EMSGSIZE;
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
123
124
125
126
  }
  
  static int getaddr_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
  {
9a3b7a42b   remi.denis-courmont@nokia   Phonet: use per-n...
127
  	struct phonet_device_list *pndevs;
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
128
129
130
  	struct phonet_device *pnd;
  	int dev_idx = 0, dev_start_idx = cb->args[0];
  	int addr_idx = 0, addr_start_idx = cb->args[1];
9a3b7a42b   remi.denis-courmont@nokia   Phonet: use per-n...
131
  	pndevs = phonet_device_list(sock_net(skb->sk));
eeb74a9d4   Rémi Denis-Courmont   Phonet: convert d...
132
133
  	rcu_read_lock();
  	list_for_each_entry_rcu(pnd, &pndevs->list, list) {
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
134
135
136
137
138
139
140
141
  		u8 addr;
  
  		if (dev_idx > dev_start_idx)
  			addr_start_idx = 0;
  		if (dev_idx++ < dev_start_idx)
  			continue;
  
  		addr_idx = 0;
a1ca14ac5   Akinobu Mita   phonet: use for_e...
142
  		for_each_set_bit(addr, pnd->addrs, 64) {
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
143
144
145
146
  			if (addr_idx++ < addr_start_idx)
  				continue;
  
  			if (fill_addr(skb, pnd->netdev, addr << 2,
15e473046   Eric W. Biederman   netlink: Rename p...
147
  					 NETLINK_CB(cb->skb).portid,
998ec759e   Rémi Denis-Courmont   Phonet: fix netli...
148
  					cb->nlh->nlmsg_seq, RTM_NEWADDR) < 0)
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
149
150
151
152
153
  				goto out;
  		}
  	}
  
  out:
eeb74a9d4   Rémi Denis-Courmont   Phonet: convert d...
154
  	rcu_read_unlock();
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
155
156
157
158
159
  	cb->args[0] = dev_idx;
  	cb->args[1] = addr_idx;
  
  	return skb->len;
  }
f062f41d0   Rémi Denis-Courmont   Phonet: routing t...
160
161
162
  /* Routes handling */
  
  static int fill_route(struct sk_buff *skb, struct net_device *dev, u8 dst,
15e473046   Eric W. Biederman   netlink: Rename p...
163
  			u32 portid, u32 seq, int event)
f062f41d0   Rémi Denis-Courmont   Phonet: routing t...
164
165
166
  {
  	struct rtmsg *rtm;
  	struct nlmsghdr *nlh;
15e473046   Eric W. Biederman   netlink: Rename p...
167
  	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*rtm), 0);
f062f41d0   Rémi Denis-Courmont   Phonet: routing t...
168
169
170
171
172
173
174
175
176
177
178
179
180
  	if (nlh == NULL)
  		return -EMSGSIZE;
  
  	rtm = nlmsg_data(nlh);
  	rtm->rtm_family = AF_PHONET;
  	rtm->rtm_dst_len = 6;
  	rtm->rtm_src_len = 0;
  	rtm->rtm_tos = 0;
  	rtm->rtm_table = RT_TABLE_MAIN;
  	rtm->rtm_protocol = RTPROT_STATIC;
  	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
  	rtm->rtm_type = RTN_UNICAST;
  	rtm->rtm_flags = 0;
7f116b5b6   David S. Miller   phonet: Stop usin...
181
182
183
  	if (nla_put_u8(skb, RTA_DST, dst) ||
  	    nla_put_u32(skb, RTA_OIF, dev->ifindex))
  		goto nla_put_failure;
053c095a8   Johannes Berg   netlink: make nlm...
184
185
  	nlmsg_end(skb, nlh);
  	return 0;
f062f41d0   Rémi Denis-Courmont   Phonet: routing t...
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
  
  nla_put_failure:
  	nlmsg_cancel(skb, nlh);
  	return -EMSGSIZE;
  }
  
  void rtm_phonet_notify(int event, struct net_device *dev, u8 dst)
  {
  	struct sk_buff *skb;
  	int err = -ENOBUFS;
  
  	skb = nlmsg_new(NLMSG_ALIGN(sizeof(struct ifaddrmsg)) +
  			nla_total_size(1) + nla_total_size(4), GFP_KERNEL);
  	if (skb == NULL)
  		goto errout;
  	err = fill_route(skb, dev, dst, 0, 0, event);
  	if (err < 0) {
  		WARN_ON(err == -EMSGSIZE);
  		kfree_skb(skb);
  		goto errout;
  	}
  	rtnl_notify(skb, dev_net(dev), 0,
  			  RTNLGRP_PHONET_ROUTE, NULL, GFP_KERNEL);
  	return;
  errout:
4b7673a04   Rémi Denis-Courmont   Phonet: remove ta...
211
  	rtnl_set_sk_err(dev_net(dev), RTNLGRP_PHONET_ROUTE, err);
f062f41d0   Rémi Denis-Courmont   Phonet: routing t...
212
213
214
215
216
217
  }
  
  static const struct nla_policy rtm_phonet_policy[RTA_MAX+1] = {
  	[RTA_DST] = { .type = NLA_U8 },
  	[RTA_OIF] = { .type = NLA_U32 },
  };
c21ef3e34   David Ahern   net: rtnetlink: p...
218
219
  static int route_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
  		      struct netlink_ext_ack *extack)
f062f41d0   Rémi Denis-Courmont   Phonet: routing t...
220
221
222
223
224
225
226
  {
  	struct net *net = sock_net(skb->sk);
  	struct nlattr *tb[RTA_MAX+1];
  	struct net_device *dev;
  	struct rtmsg *rtm;
  	int err;
  	u8 dst;
90f62cf30   Eric W. Biederman   net: Use netlink_...
227
  	if (!netlink_capable(skb, CAP_NET_ADMIN))
dfc47ef86   Eric W. Biederman   net: Push capable...
228
  		return -EPERM;
90f62cf30   Eric W. Biederman   net: Use netlink_...
229
  	if (!netlink_capable(skb, CAP_SYS_ADMIN))
f062f41d0   Rémi Denis-Courmont   Phonet: routing t...
230
231
232
  		return -EPERM;
  
  	ASSERT_RTNL();
fceb6435e   Johannes Berg   netlink: pass ext...
233
  	err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_phonet_policy,
c21ef3e34   David Ahern   net: rtnetlink: p...
234
  			  extack);
f062f41d0   Rémi Denis-Courmont   Phonet: routing t...
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
  	if (err < 0)
  		return err;
  
  	rtm = nlmsg_data(nlh);
  	if (rtm->rtm_table != RT_TABLE_MAIN || rtm->rtm_type != RTN_UNICAST)
  		return -EINVAL;
  	if (tb[RTA_DST] == NULL || tb[RTA_OIF] == NULL)
  		return -EINVAL;
  	dst = nla_get_u8(tb[RTA_DST]);
  	if (dst & 3) /* Phonet addresses only have 6 high-order bits */
  		return -EINVAL;
  
  	dev = __dev_get_by_index(net, nla_get_u32(tb[RTA_OIF]));
  	if (dev == NULL)
  		return -ENODEV;
  
  	if (nlh->nlmsg_type == RTM_NEWROUTE)
  		err = phonet_route_add(dev, dst);
  	else
  		err = phonet_route_del(dev, dst);
  	if (!err)
  		rtm_phonet_notify(nlh->nlmsg_type, dev, dst);
  	return err;
  }
  
  static int route_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
  {
  	struct net *net = sock_net(skb->sk);
926e9878a   Johannes Berg   phonet netlink: a...
263
  	u8 addr;
f062f41d0   Rémi Denis-Courmont   Phonet: routing t...
264

e67f88dd1   Eric Dumazet   net: dont hold rt...
265
  	rcu_read_lock();
926e9878a   Johannes Berg   phonet netlink: a...
266
267
  	for (addr = cb->args[0]; addr < 64; addr++) {
  		struct net_device *dev = phonet_route_get_rcu(net, addr << 2);
f062f41d0   Rémi Denis-Courmont   Phonet: routing t...
268

f062f41d0   Rémi Denis-Courmont   Phonet: routing t...
269
270
  		if (!dev)
  			continue;
926e9878a   Johannes Berg   phonet netlink: a...
271
272
273
  		if (fill_route(skb, dev, addr << 2, NETLINK_CB(cb->skb).portid,
  			       cb->nlh->nlmsg_seq, RTM_NEWROUTE) < 0)
  			goto out;
f062f41d0   Rémi Denis-Courmont   Phonet: routing t...
274
275
276
  	}
  
  out:
e67f88dd1   Eric Dumazet   net: dont hold rt...
277
  	rcu_read_unlock();
926e9878a   Johannes Berg   phonet netlink: a...
278
  	cb->args[0] = addr;
f062f41d0   Rémi Denis-Courmont   Phonet: routing t...
279
280
281
  
  	return skb->len;
  }
660f706d9   remi.denis-courmont@nokia   Phonet: handle rt...
282
  int __init phonet_netlink_register(void)
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
283
  {
c7ac8679b   Greg Rose   rtnetlink: Comput...
284
  	int err = __rtnl_register(PF_PHONET, RTM_NEWADDR, addr_doit,
b97bac64a   Florian Westphal   rtnetlink: make r...
285
  				  NULL, 0);
660f706d9   remi.denis-courmont@nokia   Phonet: handle rt...
286
287
288
289
  	if (err)
  		return err;
  
  	/* Further __rtnl_register() cannot fail */
b97bac64a   Florian Westphal   rtnetlink: make r...
290
291
292
293
294
  	__rtnl_register(PF_PHONET, RTM_DELADDR, addr_doit, NULL, 0);
  	__rtnl_register(PF_PHONET, RTM_GETADDR, NULL, getaddr_dumpit, 0);
  	__rtnl_register(PF_PHONET, RTM_NEWROUTE, route_doit, NULL, 0);
  	__rtnl_register(PF_PHONET, RTM_DELROUTE, route_doit, NULL, 0);
  	__rtnl_register(PF_PHONET, RTM_GETROUTE, NULL, route_dumpit, 0);
660f706d9   remi.denis-courmont@nokia   Phonet: handle rt...
295
  	return 0;
8fb397406   Remi Denis-Courmont   Phonet: Netlink i...
296
  }