197fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29dcaec04SSubash Abhinov Kasiviswanathan /* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
3ceed73a2SSubash Abhinov Kasiviswanathan *
4ceed73a2SSubash Abhinov Kasiviswanathan * RMNET configuration engine
5ceed73a2SSubash Abhinov Kasiviswanathan */
6ceed73a2SSubash Abhinov Kasiviswanathan
7ceed73a2SSubash Abhinov Kasiviswanathan #include <net/sock.h>
8ceed73a2SSubash Abhinov Kasiviswanathan #include <linux/module.h>
9ceed73a2SSubash Abhinov Kasiviswanathan #include <linux/netlink.h>
10ceed73a2SSubash Abhinov Kasiviswanathan #include <linux/netdevice.h>
11ceed73a2SSubash Abhinov Kasiviswanathan #include "rmnet_config.h"
12ceed73a2SSubash Abhinov Kasiviswanathan #include "rmnet_handlers.h"
13ceed73a2SSubash Abhinov Kasiviswanathan #include "rmnet_vnd.h"
14ceed73a2SSubash Abhinov Kasiviswanathan #include "rmnet_private.h"
1564b5d1f8SDaniele Palmas #include "rmnet_map.h"
16ceed73a2SSubash Abhinov Kasiviswanathan
17ceed73a2SSubash Abhinov Kasiviswanathan /* Local Definitions and Declarations */
18ceed73a2SSubash Abhinov Kasiviswanathan
1914452ca3SSubash Abhinov Kasiviswanathan static const struct nla_policy rmnet_policy[IFLA_RMNET_MAX + 1] = {
2014452ca3SSubash Abhinov Kasiviswanathan [IFLA_RMNET_MUX_ID] = { .type = NLA_U16 },
2114452ca3SSubash Abhinov Kasiviswanathan [IFLA_RMNET_FLAGS] = { .len = sizeof(struct ifla_rmnet_flags) },
2214452ca3SSubash Abhinov Kasiviswanathan };
2314452ca3SSubash Abhinov Kasiviswanathan
rmnet_is_real_dev_registered(const struct net_device * real_dev)24ceed73a2SSubash Abhinov Kasiviswanathan static int rmnet_is_real_dev_registered(const struct net_device *real_dev)
25ceed73a2SSubash Abhinov Kasiviswanathan {
265c346525SSubash Abhinov Kasiviswanathan return rcu_access_pointer(real_dev->rx_handler) == rmnet_rx_handler;
27ceed73a2SSubash Abhinov Kasiviswanathan }
28ceed73a2SSubash Abhinov Kasiviswanathan
29ceed73a2SSubash Abhinov Kasiviswanathan /* Needs rtnl lock */
30b7f5eb6bSSubash Abhinov Kasiviswanathan struct rmnet_port*
rmnet_get_port_rtnl(const struct net_device * real_dev)31b665f4f8SSubash Abhinov Kasiviswanathan rmnet_get_port_rtnl(const struct net_device *real_dev)
32ceed73a2SSubash Abhinov Kasiviswanathan {
33ceed73a2SSubash Abhinov Kasiviswanathan return rtnl_dereference(real_dev->rx_handler_data);
34ceed73a2SSubash Abhinov Kasiviswanathan }
35ceed73a2SSubash Abhinov Kasiviswanathan
rmnet_unregister_real_device(struct net_device * real_dev)36d939b6d3STaehee Yoo static int rmnet_unregister_real_device(struct net_device *real_dev)
37ceed73a2SSubash Abhinov Kasiviswanathan {
38d939b6d3STaehee Yoo struct rmnet_port *port = rmnet_get_port_rtnl(real_dev);
39d939b6d3STaehee Yoo
40b665f4f8SSubash Abhinov Kasiviswanathan if (port->nr_rmnet_devs)
41ceed73a2SSubash Abhinov Kasiviswanathan return -EINVAL;
42ceed73a2SSubash Abhinov Kasiviswanathan
4364b5d1f8SDaniele Palmas rmnet_map_tx_aggregate_exit(port);
4464b5d1f8SDaniele Palmas
45ceed73a2SSubash Abhinov Kasiviswanathan netdev_rx_handler_unregister(real_dev);
46ceed73a2SSubash Abhinov Kasiviswanathan
47e7a86c68SSean Tranchetti kfree(port);
48e7a86c68SSean Tranchetti
49ceed73a2SSubash Abhinov Kasiviswanathan netdev_dbg(real_dev, "Removed from rmnet\n");
50ceed73a2SSubash Abhinov Kasiviswanathan return 0;
51ceed73a2SSubash Abhinov Kasiviswanathan }
52ceed73a2SSubash Abhinov Kasiviswanathan
rmnet_register_real_device(struct net_device * real_dev,struct netlink_ext_ack * extack)532a762e9eSTaehee Yoo static int rmnet_register_real_device(struct net_device *real_dev,
542a762e9eSTaehee Yoo struct netlink_ext_ack *extack)
55ceed73a2SSubash Abhinov Kasiviswanathan {
56b665f4f8SSubash Abhinov Kasiviswanathan struct rmnet_port *port;
573352e6c4SSubash Abhinov Kasiviswanathan int rc, entry;
58ceed73a2SSubash Abhinov Kasiviswanathan
59ceed73a2SSubash Abhinov Kasiviswanathan ASSERT_RTNL();
60ceed73a2SSubash Abhinov Kasiviswanathan
612a762e9eSTaehee Yoo if (rmnet_is_real_dev_registered(real_dev)) {
622a762e9eSTaehee Yoo port = rmnet_get_port_rtnl(real_dev);
632a762e9eSTaehee Yoo if (port->rmnet_mode != RMNET_EPMODE_VND) {
642a762e9eSTaehee Yoo NL_SET_ERR_MSG_MOD(extack, "bridge device already exists");
652a762e9eSTaehee Yoo return -EINVAL;
662a762e9eSTaehee Yoo }
672a762e9eSTaehee Yoo
68ceed73a2SSubash Abhinov Kasiviswanathan return 0;
692a762e9eSTaehee Yoo }
70ceed73a2SSubash Abhinov Kasiviswanathan
719c9cc918STaehee Yoo port = kzalloc(sizeof(*port), GFP_KERNEL);
72b665f4f8SSubash Abhinov Kasiviswanathan if (!port)
73ceed73a2SSubash Abhinov Kasiviswanathan return -ENOMEM;
74ceed73a2SSubash Abhinov Kasiviswanathan
75b665f4f8SSubash Abhinov Kasiviswanathan port->dev = real_dev;
76b665f4f8SSubash Abhinov Kasiviswanathan rc = netdev_rx_handler_register(real_dev, rmnet_rx_handler, port);
77ceed73a2SSubash Abhinov Kasiviswanathan if (rc) {
78b665f4f8SSubash Abhinov Kasiviswanathan kfree(port);
79ceed73a2SSubash Abhinov Kasiviswanathan return -EBUSY;
80ceed73a2SSubash Abhinov Kasiviswanathan }
81ceed73a2SSubash Abhinov Kasiviswanathan
823352e6c4SSubash Abhinov Kasiviswanathan for (entry = 0; entry < RMNET_MAX_LOGICAL_EP; entry++)
833352e6c4SSubash Abhinov Kasiviswanathan INIT_HLIST_HEAD(&port->muxed_ep[entry]);
843352e6c4SSubash Abhinov Kasiviswanathan
8564b5d1f8SDaniele Palmas rmnet_map_tx_aggregate_init(port);
8664b5d1f8SDaniele Palmas
87ceed73a2SSubash Abhinov Kasiviswanathan netdev_dbg(real_dev, "registered with rmnet\n");
88ceed73a2SSubash Abhinov Kasiviswanathan return 0;
89ceed73a2SSubash Abhinov Kasiviswanathan }
90ceed73a2SSubash Abhinov Kasiviswanathan
rmnet_unregister_bridge(struct rmnet_port * port)91d939b6d3STaehee Yoo static void rmnet_unregister_bridge(struct rmnet_port *port)
9260d58f97SSubash Abhinov Kasiviswanathan {
93d939b6d3STaehee Yoo struct net_device *bridge_dev, *real_dev, *rmnet_dev;
94d939b6d3STaehee Yoo struct rmnet_port *real_port;
9560d58f97SSubash Abhinov Kasiviswanathan
9660d58f97SSubash Abhinov Kasiviswanathan if (port->rmnet_mode != RMNET_EPMODE_BRIDGE)
9760d58f97SSubash Abhinov Kasiviswanathan return;
9860d58f97SSubash Abhinov Kasiviswanathan
99d939b6d3STaehee Yoo rmnet_dev = port->rmnet_dev;
10060d58f97SSubash Abhinov Kasiviswanathan if (!port->nr_rmnet_devs) {
101d939b6d3STaehee Yoo /* bridge device */
102d939b6d3STaehee Yoo real_dev = port->bridge_ep;
103d939b6d3STaehee Yoo bridge_dev = port->dev;
10460d58f97SSubash Abhinov Kasiviswanathan
105d939b6d3STaehee Yoo real_port = rmnet_get_port_rtnl(real_dev);
106d939b6d3STaehee Yoo real_port->bridge_ep = NULL;
107d939b6d3STaehee Yoo real_port->rmnet_mode = RMNET_EPMODE_VND;
10860d58f97SSubash Abhinov Kasiviswanathan } else {
109d939b6d3STaehee Yoo /* real device */
11060d58f97SSubash Abhinov Kasiviswanathan bridge_dev = port->bridge_ep;
11160d58f97SSubash Abhinov Kasiviswanathan
112d939b6d3STaehee Yoo port->bridge_ep = NULL;
113d939b6d3STaehee Yoo port->rmnet_mode = RMNET_EPMODE_VND;
11460d58f97SSubash Abhinov Kasiviswanathan }
115d939b6d3STaehee Yoo
116d939b6d3STaehee Yoo netdev_upper_dev_unlink(bridge_dev, rmnet_dev);
117d939b6d3STaehee Yoo rmnet_unregister_real_device(bridge_dev);
11860d58f97SSubash Abhinov Kasiviswanathan }
11960d58f97SSubash Abhinov Kasiviswanathan
rmnet_newlink(struct net * src_net,struct net_device * dev,struct nlattr * tb[],struct nlattr * data[],struct netlink_ext_ack * extack)120ceed73a2SSubash Abhinov Kasiviswanathan static int rmnet_newlink(struct net *src_net, struct net_device *dev,
121ceed73a2SSubash Abhinov Kasiviswanathan struct nlattr *tb[], struct nlattr *data[],
122ceed73a2SSubash Abhinov Kasiviswanathan struct netlink_ext_ack *extack)
123ceed73a2SSubash Abhinov Kasiviswanathan {
12414452ca3SSubash Abhinov Kasiviswanathan u32 data_format = RMNET_FLAGS_INGRESS_DEAGGREGATION;
125ceed73a2SSubash Abhinov Kasiviswanathan struct net_device *real_dev;
126ceed73a2SSubash Abhinov Kasiviswanathan int mode = RMNET_EPMODE_VND;
1273352e6c4SSubash Abhinov Kasiviswanathan struct rmnet_endpoint *ep;
128b665f4f8SSubash Abhinov Kasiviswanathan struct rmnet_port *port;
129ceed73a2SSubash Abhinov Kasiviswanathan int err = 0;
130ceed73a2SSubash Abhinov Kasiviswanathan u16 mux_id;
131ceed73a2SSubash Abhinov Kasiviswanathan
13293b5cbfaSTaehee Yoo if (!tb[IFLA_LINK]) {
13393b5cbfaSTaehee Yoo NL_SET_ERR_MSG_MOD(extack, "link not specified");
13493b5cbfaSTaehee Yoo return -EINVAL;
13593b5cbfaSTaehee Yoo }
13693b5cbfaSTaehee Yoo
137ceed73a2SSubash Abhinov Kasiviswanathan real_dev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK]));
138fcf8f4ebSTaehee Yoo if (!real_dev) {
139fcf8f4ebSTaehee Yoo NL_SET_ERR_MSG_MOD(extack, "link does not exist");
140ceed73a2SSubash Abhinov Kasiviswanathan return -ENODEV;
141fcf8f4ebSTaehee Yoo }
142ceed73a2SSubash Abhinov Kasiviswanathan
1439c9cc918STaehee Yoo ep = kzalloc(sizeof(*ep), GFP_KERNEL);
1443352e6c4SSubash Abhinov Kasiviswanathan if (!ep)
1453352e6c4SSubash Abhinov Kasiviswanathan return -ENOMEM;
1463352e6c4SSubash Abhinov Kasiviswanathan
14714452ca3SSubash Abhinov Kasiviswanathan mux_id = nla_get_u16(data[IFLA_RMNET_MUX_ID]);
148ceed73a2SSubash Abhinov Kasiviswanathan
1492a762e9eSTaehee Yoo err = rmnet_register_real_device(real_dev, extack);
150ceed73a2SSubash Abhinov Kasiviswanathan if (err)
151ceed73a2SSubash Abhinov Kasiviswanathan goto err0;
152ceed73a2SSubash Abhinov Kasiviswanathan
153b665f4f8SSubash Abhinov Kasiviswanathan port = rmnet_get_port_rtnl(real_dev);
154fcf8f4ebSTaehee Yoo err = rmnet_vnd_newlink(mux_id, dev, port, real_dev, ep, extack);
155ceed73a2SSubash Abhinov Kasiviswanathan if (err)
156ceed73a2SSubash Abhinov Kasiviswanathan goto err1;
157ceed73a2SSubash Abhinov Kasiviswanathan
158037f9cdfSTaehee Yoo err = netdev_upper_dev_link(real_dev, dev, extack);
159037f9cdfSTaehee Yoo if (err < 0)
160037f9cdfSTaehee Yoo goto err2;
161037f9cdfSTaehee Yoo
16291489632SSubash Abhinov Kasiviswanathan port->rmnet_mode = mode;
163d939b6d3STaehee Yoo port->rmnet_dev = dev;
164032ee468SSubash Abhinov Kasiviswanathan
1653352e6c4SSubash Abhinov Kasiviswanathan hlist_add_head_rcu(&ep->hlnode, &port->muxed_ep[mux_id]);
1666b8ecc23SSubash Abhinov Kasiviswanathan
16714452ca3SSubash Abhinov Kasiviswanathan if (data[IFLA_RMNET_FLAGS]) {
16814452ca3SSubash Abhinov Kasiviswanathan struct ifla_rmnet_flags *flags;
1696b8ecc23SSubash Abhinov Kasiviswanathan
17014452ca3SSubash Abhinov Kasiviswanathan flags = nla_data(data[IFLA_RMNET_FLAGS]);
171d917c35aSBjorn Andersson data_format &= ~flags->mask;
172d917c35aSBjorn Andersson data_format |= flags->flags & flags->mask;
1736b8ecc23SSubash Abhinov Kasiviswanathan }
1746b8ecc23SSubash Abhinov Kasiviswanathan
175b23e722eSSubash Abhinov Kasiviswanathan netdev_dbg(dev, "data format [0x%08X]\n", data_format);
176b23e722eSSubash Abhinov Kasiviswanathan port->data_format = data_format;
1776b8ecc23SSubash Abhinov Kasiviswanathan
178ceed73a2SSubash Abhinov Kasiviswanathan return 0;
179ceed73a2SSubash Abhinov Kasiviswanathan
180037f9cdfSTaehee Yoo err2:
181037f9cdfSTaehee Yoo unregister_netdevice(dev);
182d939b6d3STaehee Yoo rmnet_vnd_dellink(mux_id, port, ep);
183ceed73a2SSubash Abhinov Kasiviswanathan err1:
184d939b6d3STaehee Yoo rmnet_unregister_real_device(real_dev);
185ceed73a2SSubash Abhinov Kasiviswanathan err0:
1866296928fSSubash Abhinov Kasiviswanathan kfree(ep);
187ceed73a2SSubash Abhinov Kasiviswanathan return err;
188ceed73a2SSubash Abhinov Kasiviswanathan }
189ceed73a2SSubash Abhinov Kasiviswanathan
rmnet_dellink(struct net_device * dev,struct list_head * head)190ceed73a2SSubash Abhinov Kasiviswanathan static void rmnet_dellink(struct net_device *dev, struct list_head *head)
191ceed73a2SSubash Abhinov Kasiviswanathan {
192b37f78f2SSubash Abhinov Kasiviswanathan struct rmnet_priv *priv = netdev_priv(dev);
193d939b6d3STaehee Yoo struct net_device *real_dev, *bridge_dev;
194d939b6d3STaehee Yoo struct rmnet_port *real_port, *bridge_port;
1953352e6c4SSubash Abhinov Kasiviswanathan struct rmnet_endpoint *ep;
196d939b6d3STaehee Yoo u8 mux_id = priv->mux_id;
197ceed73a2SSubash Abhinov Kasiviswanathan
198b37f78f2SSubash Abhinov Kasiviswanathan real_dev = priv->real_dev;
199ceed73a2SSubash Abhinov Kasiviswanathan
200d939b6d3STaehee Yoo if (!rmnet_is_real_dev_registered(real_dev))
201ceed73a2SSubash Abhinov Kasiviswanathan return;
202ceed73a2SSubash Abhinov Kasiviswanathan
203d939b6d3STaehee Yoo real_port = rmnet_get_port_rtnl(real_dev);
204d939b6d3STaehee Yoo bridge_dev = real_port->bridge_ep;
205d939b6d3STaehee Yoo if (bridge_dev) {
206d939b6d3STaehee Yoo bridge_port = rmnet_get_port_rtnl(bridge_dev);
207d939b6d3STaehee Yoo rmnet_unregister_bridge(bridge_port);
208d939b6d3STaehee Yoo }
209ceed73a2SSubash Abhinov Kasiviswanathan
210d939b6d3STaehee Yoo ep = rmnet_get_endpoint(real_port, mux_id);
2113352e6c4SSubash Abhinov Kasiviswanathan if (ep) {
2123352e6c4SSubash Abhinov Kasiviswanathan hlist_del_init_rcu(&ep->hlnode);
213d939b6d3STaehee Yoo rmnet_vnd_dellink(mux_id, real_port, ep);
2143352e6c4SSubash Abhinov Kasiviswanathan kfree(ep);
2153352e6c4SSubash Abhinov Kasiviswanathan }
216ceed73a2SSubash Abhinov Kasiviswanathan
217d939b6d3STaehee Yoo netdev_upper_dev_unlink(real_dev, dev);
218d939b6d3STaehee Yoo rmnet_unregister_real_device(real_dev);
219ceed73a2SSubash Abhinov Kasiviswanathan unregister_netdevice_queue(dev, head);
220ceed73a2SSubash Abhinov Kasiviswanathan }
221ceed73a2SSubash Abhinov Kasiviswanathan
rmnet_force_unassociate_device(struct net_device * real_dev)222037f9cdfSTaehee Yoo static void rmnet_force_unassociate_device(struct net_device *real_dev)
223ceed73a2SSubash Abhinov Kasiviswanathan {
224b37f78f2SSubash Abhinov Kasiviswanathan struct hlist_node *tmp_ep;
225b37f78f2SSubash Abhinov Kasiviswanathan struct rmnet_endpoint *ep;
226b665f4f8SSubash Abhinov Kasiviswanathan struct rmnet_port *port;
227b37f78f2SSubash Abhinov Kasiviswanathan unsigned long bkt_ep;
228ceed73a2SSubash Abhinov Kasiviswanathan LIST_HEAD(list);
229ceed73a2SSubash Abhinov Kasiviswanathan
230037f9cdfSTaehee Yoo port = rmnet_get_port_rtnl(real_dev);
231ceed73a2SSubash Abhinov Kasiviswanathan
232d939b6d3STaehee Yoo if (port->nr_rmnet_devs) {
233d939b6d3STaehee Yoo /* real device */
234d939b6d3STaehee Yoo rmnet_unregister_bridge(port);
235b37f78f2SSubash Abhinov Kasiviswanathan hash_for_each_safe(port->muxed_ep, bkt_ep, tmp_ep, ep, hlnode) {
236b37f78f2SSubash Abhinov Kasiviswanathan unregister_netdevice_queue(ep->egress_dev, &list);
237d939b6d3STaehee Yoo netdev_upper_dev_unlink(real_dev, ep->egress_dev);
238b37f78f2SSubash Abhinov Kasiviswanathan rmnet_vnd_dellink(ep->mux_id, port, ep);
239b37f78f2SSubash Abhinov Kasiviswanathan hlist_del_init_rcu(&ep->hlnode);
240b37f78f2SSubash Abhinov Kasiviswanathan kfree(ep);
241b37f78f2SSubash Abhinov Kasiviswanathan }
242d939b6d3STaehee Yoo rmnet_unregister_real_device(real_dev);
243ceed73a2SSubash Abhinov Kasiviswanathan unregister_netdevice_many(&list);
244d939b6d3STaehee Yoo } else {
245d939b6d3STaehee Yoo rmnet_unregister_bridge(port);
246d939b6d3STaehee Yoo }
247ceed73a2SSubash Abhinov Kasiviswanathan }
248ceed73a2SSubash Abhinov Kasiviswanathan
rmnet_config_notify_cb(struct notifier_block * nb,unsigned long event,void * data)249ceed73a2SSubash Abhinov Kasiviswanathan static int rmnet_config_notify_cb(struct notifier_block *nb,
250ceed73a2SSubash Abhinov Kasiviswanathan unsigned long event, void *data)
251ceed73a2SSubash Abhinov Kasiviswanathan {
252037f9cdfSTaehee Yoo struct net_device *real_dev = netdev_notifier_info_to_dev(data);
253ceed73a2SSubash Abhinov Kasiviswanathan
254037f9cdfSTaehee Yoo if (!rmnet_is_real_dev_registered(real_dev))
255ceed73a2SSubash Abhinov Kasiviswanathan return NOTIFY_DONE;
256ceed73a2SSubash Abhinov Kasiviswanathan
257ceed73a2SSubash Abhinov Kasiviswanathan switch (event) {
258ceed73a2SSubash Abhinov Kasiviswanathan case NETDEV_UNREGISTER:
259037f9cdfSTaehee Yoo netdev_dbg(real_dev, "Kernel unregister\n");
260037f9cdfSTaehee Yoo rmnet_force_unassociate_device(real_dev);
261ceed73a2SSubash Abhinov Kasiviswanathan break;
262b7f5eb6bSSubash Abhinov Kasiviswanathan case NETDEV_CHANGEMTU:
263b7f5eb6bSSubash Abhinov Kasiviswanathan if (rmnet_vnd_validate_real_dev_mtu(real_dev))
264b7f5eb6bSSubash Abhinov Kasiviswanathan return NOTIFY_BAD;
265b7f5eb6bSSubash Abhinov Kasiviswanathan break;
266ceed73a2SSubash Abhinov Kasiviswanathan default:
267ceed73a2SSubash Abhinov Kasiviswanathan break;
268ceed73a2SSubash Abhinov Kasiviswanathan }
269ceed73a2SSubash Abhinov Kasiviswanathan
270ceed73a2SSubash Abhinov Kasiviswanathan return NOTIFY_DONE;
271ceed73a2SSubash Abhinov Kasiviswanathan }
272ceed73a2SSubash Abhinov Kasiviswanathan
273ceed73a2SSubash Abhinov Kasiviswanathan static struct notifier_block rmnet_dev_notifier __read_mostly = {
274ceed73a2SSubash Abhinov Kasiviswanathan .notifier_call = rmnet_config_notify_cb,
275ceed73a2SSubash Abhinov Kasiviswanathan };
276ceed73a2SSubash Abhinov Kasiviswanathan
rmnet_rtnl_validate(struct nlattr * tb[],struct nlattr * data[],struct netlink_ext_ack * extack)277ceed73a2SSubash Abhinov Kasiviswanathan static int rmnet_rtnl_validate(struct nlattr *tb[], struct nlattr *data[],
278ceed73a2SSubash Abhinov Kasiviswanathan struct netlink_ext_ack *extack)
279ceed73a2SSubash Abhinov Kasiviswanathan {
280ceed73a2SSubash Abhinov Kasiviswanathan u16 mux_id;
281ceed73a2SSubash Abhinov Kasiviswanathan
282fcf8f4ebSTaehee Yoo if (!data || !data[IFLA_RMNET_MUX_ID]) {
283fcf8f4ebSTaehee Yoo NL_SET_ERR_MSG_MOD(extack, "MUX ID not specified");
284ceed73a2SSubash Abhinov Kasiviswanathan return -EINVAL;
285fcf8f4ebSTaehee Yoo }
286ceed73a2SSubash Abhinov Kasiviswanathan
28714452ca3SSubash Abhinov Kasiviswanathan mux_id = nla_get_u16(data[IFLA_RMNET_MUX_ID]);
288fcf8f4ebSTaehee Yoo if (mux_id > (RMNET_MAX_LOGICAL_EP - 1)) {
289fcf8f4ebSTaehee Yoo NL_SET_ERR_MSG_MOD(extack, "invalid MUX ID");
290ceed73a2SSubash Abhinov Kasiviswanathan return -ERANGE;
291fcf8f4ebSTaehee Yoo }
292ceed73a2SSubash Abhinov Kasiviswanathan
293ceed73a2SSubash Abhinov Kasiviswanathan return 0;
294ceed73a2SSubash Abhinov Kasiviswanathan }
295ceed73a2SSubash Abhinov Kasiviswanathan
rmnet_changelink(struct net_device * dev,struct nlattr * tb[],struct nlattr * data[],struct netlink_ext_ack * extack)29623790ef1SSubash Abhinov Kasiviswanathan static int rmnet_changelink(struct net_device *dev, struct nlattr *tb[],
29723790ef1SSubash Abhinov Kasiviswanathan struct nlattr *data[],
29823790ef1SSubash Abhinov Kasiviswanathan struct netlink_ext_ack *extack)
29923790ef1SSubash Abhinov Kasiviswanathan {
30023790ef1SSubash Abhinov Kasiviswanathan struct rmnet_priv *priv = netdev_priv(dev);
30123790ef1SSubash Abhinov Kasiviswanathan struct net_device *real_dev;
30223790ef1SSubash Abhinov Kasiviswanathan struct rmnet_port *port;
30323790ef1SSubash Abhinov Kasiviswanathan u16 mux_id;
30423790ef1SSubash Abhinov Kasiviswanathan
3053c18aa14SColin Ian King if (!dev)
3063c18aa14SColin Ian King return -ENODEV;
3073c18aa14SColin Ian King
3081eb1f43aSTaehee Yoo real_dev = priv->real_dev;
3091eb1f43aSTaehee Yoo if (!rmnet_is_real_dev_registered(real_dev))
31023790ef1SSubash Abhinov Kasiviswanathan return -ENODEV;
31123790ef1SSubash Abhinov Kasiviswanathan
31223790ef1SSubash Abhinov Kasiviswanathan port = rmnet_get_port_rtnl(real_dev);
31323790ef1SSubash Abhinov Kasiviswanathan
31414452ca3SSubash Abhinov Kasiviswanathan if (data[IFLA_RMNET_MUX_ID]) {
31514452ca3SSubash Abhinov Kasiviswanathan mux_id = nla_get_u16(data[IFLA_RMNET_MUX_ID]);
3162abb5792SSubash Abhinov Kasiviswanathan
3172abb5792SSubash Abhinov Kasiviswanathan if (mux_id != priv->mux_id) {
3182abb5792SSubash Abhinov Kasiviswanathan struct rmnet_endpoint *ep;
3192abb5792SSubash Abhinov Kasiviswanathan
32023790ef1SSubash Abhinov Kasiviswanathan ep = rmnet_get_endpoint(port, priv->mux_id);
3210c29ba1bSColin Ian King if (!ep)
3220c29ba1bSColin Ian King return -ENODEV;
32323790ef1SSubash Abhinov Kasiviswanathan
3242abb5792SSubash Abhinov Kasiviswanathan if (rmnet_get_endpoint(port, mux_id)) {
3252abb5792SSubash Abhinov Kasiviswanathan NL_SET_ERR_MSG_MOD(extack,
3262abb5792SSubash Abhinov Kasiviswanathan "MUX ID already exists");
3272abb5792SSubash Abhinov Kasiviswanathan return -EINVAL;
3282abb5792SSubash Abhinov Kasiviswanathan }
3292abb5792SSubash Abhinov Kasiviswanathan
33023790ef1SSubash Abhinov Kasiviswanathan hlist_del_init_rcu(&ep->hlnode);
3312abb5792SSubash Abhinov Kasiviswanathan hlist_add_head_rcu(&ep->hlnode,
3322abb5792SSubash Abhinov Kasiviswanathan &port->muxed_ep[mux_id]);
33323790ef1SSubash Abhinov Kasiviswanathan
33423790ef1SSubash Abhinov Kasiviswanathan ep->mux_id = mux_id;
33523790ef1SSubash Abhinov Kasiviswanathan priv->mux_id = mux_id;
33623790ef1SSubash Abhinov Kasiviswanathan }
3372abb5792SSubash Abhinov Kasiviswanathan }
33823790ef1SSubash Abhinov Kasiviswanathan
33914452ca3SSubash Abhinov Kasiviswanathan if (data[IFLA_RMNET_FLAGS]) {
34014452ca3SSubash Abhinov Kasiviswanathan struct ifla_rmnet_flags *flags;
341b7f5eb6bSSubash Abhinov Kasiviswanathan u32 old_data_format;
34223790ef1SSubash Abhinov Kasiviswanathan
343b7f5eb6bSSubash Abhinov Kasiviswanathan old_data_format = port->data_format;
34414452ca3SSubash Abhinov Kasiviswanathan flags = nla_data(data[IFLA_RMNET_FLAGS]);
345d917c35aSBjorn Andersson port->data_format &= ~flags->mask;
346d917c35aSBjorn Andersson port->data_format |= flags->flags & flags->mask;
347b7f5eb6bSSubash Abhinov Kasiviswanathan
348b7f5eb6bSSubash Abhinov Kasiviswanathan if (rmnet_vnd_update_dev_mtu(port, real_dev)) {
349b7f5eb6bSSubash Abhinov Kasiviswanathan port->data_format = old_data_format;
350b7f5eb6bSSubash Abhinov Kasiviswanathan NL_SET_ERR_MSG_MOD(extack, "Invalid MTU on real dev");
351b7f5eb6bSSubash Abhinov Kasiviswanathan return -EINVAL;
352b7f5eb6bSSubash Abhinov Kasiviswanathan }
35323790ef1SSubash Abhinov Kasiviswanathan }
35423790ef1SSubash Abhinov Kasiviswanathan
35523790ef1SSubash Abhinov Kasiviswanathan return 0;
35623790ef1SSubash Abhinov Kasiviswanathan }
35723790ef1SSubash Abhinov Kasiviswanathan
rmnet_get_size(const struct net_device * dev)358ceed73a2SSubash Abhinov Kasiviswanathan static size_t rmnet_get_size(const struct net_device *dev)
359ceed73a2SSubash Abhinov Kasiviswanathan {
36014452ca3SSubash Abhinov Kasiviswanathan return
36114452ca3SSubash Abhinov Kasiviswanathan /* IFLA_RMNET_MUX_ID */
36214452ca3SSubash Abhinov Kasiviswanathan nla_total_size(2) +
36314452ca3SSubash Abhinov Kasiviswanathan /* IFLA_RMNET_FLAGS */
36414452ca3SSubash Abhinov Kasiviswanathan nla_total_size(sizeof(struct ifla_rmnet_flags));
365ceed73a2SSubash Abhinov Kasiviswanathan }
366ceed73a2SSubash Abhinov Kasiviswanathan
rmnet_fill_info(struct sk_buff * skb,const struct net_device * dev)367be81a85fSSubash Abhinov Kasiviswanathan static int rmnet_fill_info(struct sk_buff *skb, const struct net_device *dev)
368be81a85fSSubash Abhinov Kasiviswanathan {
369be81a85fSSubash Abhinov Kasiviswanathan struct rmnet_priv *priv = netdev_priv(dev);
370be81a85fSSubash Abhinov Kasiviswanathan struct net_device *real_dev;
371be81a85fSSubash Abhinov Kasiviswanathan struct ifla_rmnet_flags f;
372be81a85fSSubash Abhinov Kasiviswanathan struct rmnet_port *port;
373be81a85fSSubash Abhinov Kasiviswanathan
374be81a85fSSubash Abhinov Kasiviswanathan real_dev = priv->real_dev;
375be81a85fSSubash Abhinov Kasiviswanathan
376be81a85fSSubash Abhinov Kasiviswanathan if (nla_put_u16(skb, IFLA_RMNET_MUX_ID, priv->mux_id))
377be81a85fSSubash Abhinov Kasiviswanathan goto nla_put_failure;
378be81a85fSSubash Abhinov Kasiviswanathan
37964e86fecSSubash Abhinov Kasiviswanathan if (rmnet_is_real_dev_registered(real_dev)) {
380be81a85fSSubash Abhinov Kasiviswanathan port = rmnet_get_port_rtnl(real_dev);
381be81a85fSSubash Abhinov Kasiviswanathan f.flags = port->data_format;
38264e86fecSSubash Abhinov Kasiviswanathan } else {
38364e86fecSSubash Abhinov Kasiviswanathan f.flags = 0;
38464e86fecSSubash Abhinov Kasiviswanathan }
38564e86fecSSubash Abhinov Kasiviswanathan
386be81a85fSSubash Abhinov Kasiviswanathan f.mask = ~0;
387be81a85fSSubash Abhinov Kasiviswanathan
388be81a85fSSubash Abhinov Kasiviswanathan if (nla_put(skb, IFLA_RMNET_FLAGS, sizeof(f), &f))
389be81a85fSSubash Abhinov Kasiviswanathan goto nla_put_failure;
390be81a85fSSubash Abhinov Kasiviswanathan
391be81a85fSSubash Abhinov Kasiviswanathan return 0;
392be81a85fSSubash Abhinov Kasiviswanathan
393be81a85fSSubash Abhinov Kasiviswanathan nla_put_failure:
394be81a85fSSubash Abhinov Kasiviswanathan return -EMSGSIZE;
395be81a85fSSubash Abhinov Kasiviswanathan }
396be81a85fSSubash Abhinov Kasiviswanathan
397ceed73a2SSubash Abhinov Kasiviswanathan struct rtnl_link_ops rmnet_link_ops __read_mostly = {
398ceed73a2SSubash Abhinov Kasiviswanathan .kind = "rmnet",
399*c4734535SLin Ma .maxtype = IFLA_RMNET_MAX,
400ceed73a2SSubash Abhinov Kasiviswanathan .priv_size = sizeof(struct rmnet_priv),
401ceed73a2SSubash Abhinov Kasiviswanathan .setup = rmnet_vnd_setup,
402ceed73a2SSubash Abhinov Kasiviswanathan .validate = rmnet_rtnl_validate,
403ceed73a2SSubash Abhinov Kasiviswanathan .newlink = rmnet_newlink,
404ceed73a2SSubash Abhinov Kasiviswanathan .dellink = rmnet_dellink,
405ceed73a2SSubash Abhinov Kasiviswanathan .get_size = rmnet_get_size,
40623790ef1SSubash Abhinov Kasiviswanathan .changelink = rmnet_changelink,
40714452ca3SSubash Abhinov Kasiviswanathan .policy = rmnet_policy,
408be81a85fSSubash Abhinov Kasiviswanathan .fill_info = rmnet_fill_info,
409ceed73a2SSubash Abhinov Kasiviswanathan };
410ceed73a2SSubash Abhinov Kasiviswanathan
rmnet_get_port_rcu(struct net_device * real_dev)411102210f7STaehee Yoo struct rmnet_port *rmnet_get_port_rcu(struct net_device *real_dev)
412ceed73a2SSubash Abhinov Kasiviswanathan {
413032ee468SSubash Abhinov Kasiviswanathan if (rmnet_is_real_dev_registered(real_dev))
414102210f7STaehee Yoo return rcu_dereference_bh(real_dev->rx_handler_data);
415032ee468SSubash Abhinov Kasiviswanathan else
416032ee468SSubash Abhinov Kasiviswanathan return NULL;
417ceed73a2SSubash Abhinov Kasiviswanathan }
418ceed73a2SSubash Abhinov Kasiviswanathan
rmnet_get_endpoint(struct rmnet_port * port,u8 mux_id)4193352e6c4SSubash Abhinov Kasiviswanathan struct rmnet_endpoint *rmnet_get_endpoint(struct rmnet_port *port, u8 mux_id)
4203352e6c4SSubash Abhinov Kasiviswanathan {
4213352e6c4SSubash Abhinov Kasiviswanathan struct rmnet_endpoint *ep;
4223352e6c4SSubash Abhinov Kasiviswanathan
4233352e6c4SSubash Abhinov Kasiviswanathan hlist_for_each_entry_rcu(ep, &port->muxed_ep[mux_id], hlnode) {
4243352e6c4SSubash Abhinov Kasiviswanathan if (ep->mux_id == mux_id)
4253352e6c4SSubash Abhinov Kasiviswanathan return ep;
4263352e6c4SSubash Abhinov Kasiviswanathan }
4273352e6c4SSubash Abhinov Kasiviswanathan
4283352e6c4SSubash Abhinov Kasiviswanathan return NULL;
4293352e6c4SSubash Abhinov Kasiviswanathan }
4303352e6c4SSubash Abhinov Kasiviswanathan
rmnet_add_bridge(struct net_device * rmnet_dev,struct net_device * slave_dev,struct netlink_ext_ack * extack)43160d58f97SSubash Abhinov Kasiviswanathan int rmnet_add_bridge(struct net_device *rmnet_dev,
43260d58f97SSubash Abhinov Kasiviswanathan struct net_device *slave_dev,
43360d58f97SSubash Abhinov Kasiviswanathan struct netlink_ext_ack *extack)
43460d58f97SSubash Abhinov Kasiviswanathan {
43560d58f97SSubash Abhinov Kasiviswanathan struct rmnet_priv *priv = netdev_priv(rmnet_dev);
43660d58f97SSubash Abhinov Kasiviswanathan struct net_device *real_dev = priv->real_dev;
43760d58f97SSubash Abhinov Kasiviswanathan struct rmnet_port *port, *slave_port;
43860d58f97SSubash Abhinov Kasiviswanathan int err;
43960d58f97SSubash Abhinov Kasiviswanathan
440102210f7STaehee Yoo port = rmnet_get_port_rtnl(real_dev);
44160d58f97SSubash Abhinov Kasiviswanathan
44260d58f97SSubash Abhinov Kasiviswanathan /* If there is more than one rmnet dev attached, its probably being
44360d58f97SSubash Abhinov Kasiviswanathan * used for muxing. Skip the briding in that case
44460d58f97SSubash Abhinov Kasiviswanathan */
445fcf8f4ebSTaehee Yoo if (port->nr_rmnet_devs > 1) {
446fcf8f4ebSTaehee Yoo NL_SET_ERR_MSG_MOD(extack, "more than one rmnet dev attached");
44760d58f97SSubash Abhinov Kasiviswanathan return -EINVAL;
448fcf8f4ebSTaehee Yoo }
44960d58f97SSubash Abhinov Kasiviswanathan
4502fb2799aSTaehee Yoo if (port->rmnet_mode != RMNET_EPMODE_VND) {
4512fb2799aSTaehee Yoo NL_SET_ERR_MSG_MOD(extack, "more than one bridge dev attached");
4522fb2799aSTaehee Yoo return -EINVAL;
4532fb2799aSTaehee Yoo }
4542fb2799aSTaehee Yoo
455fcf8f4ebSTaehee Yoo if (rmnet_is_real_dev_registered(slave_dev)) {
456fcf8f4ebSTaehee Yoo NL_SET_ERR_MSG_MOD(extack,
457fcf8f4ebSTaehee Yoo "slave cannot be another rmnet dev");
458fcf8f4ebSTaehee Yoo
45960d58f97SSubash Abhinov Kasiviswanathan return -EBUSY;
460fcf8f4ebSTaehee Yoo }
46160d58f97SSubash Abhinov Kasiviswanathan
4622a762e9eSTaehee Yoo err = rmnet_register_real_device(slave_dev, extack);
46360d58f97SSubash Abhinov Kasiviswanathan if (err)
46460d58f97SSubash Abhinov Kasiviswanathan return -EBUSY;
46560d58f97SSubash Abhinov Kasiviswanathan
466d939b6d3STaehee Yoo err = netdev_master_upper_dev_link(slave_dev, rmnet_dev, NULL, NULL,
467d939b6d3STaehee Yoo extack);
468d939b6d3STaehee Yoo if (err) {
469d939b6d3STaehee Yoo rmnet_unregister_real_device(slave_dev);
470d939b6d3STaehee Yoo return err;
471d939b6d3STaehee Yoo }
472d939b6d3STaehee Yoo
473102210f7STaehee Yoo slave_port = rmnet_get_port_rtnl(slave_dev);
47460d58f97SSubash Abhinov Kasiviswanathan slave_port->rmnet_mode = RMNET_EPMODE_BRIDGE;
47560d58f97SSubash Abhinov Kasiviswanathan slave_port->bridge_ep = real_dev;
476d939b6d3STaehee Yoo slave_port->rmnet_dev = rmnet_dev;
47760d58f97SSubash Abhinov Kasiviswanathan
47860d58f97SSubash Abhinov Kasiviswanathan port->rmnet_mode = RMNET_EPMODE_BRIDGE;
47960d58f97SSubash Abhinov Kasiviswanathan port->bridge_ep = slave_dev;
48060d58f97SSubash Abhinov Kasiviswanathan
48160d58f97SSubash Abhinov Kasiviswanathan netdev_dbg(slave_dev, "registered with rmnet as slave\n");
48260d58f97SSubash Abhinov Kasiviswanathan return 0;
48360d58f97SSubash Abhinov Kasiviswanathan }
48460d58f97SSubash Abhinov Kasiviswanathan
rmnet_del_bridge(struct net_device * rmnet_dev,struct net_device * slave_dev)48560d58f97SSubash Abhinov Kasiviswanathan int rmnet_del_bridge(struct net_device *rmnet_dev,
48660d58f97SSubash Abhinov Kasiviswanathan struct net_device *slave_dev)
48760d58f97SSubash Abhinov Kasiviswanathan {
488d939b6d3STaehee Yoo struct rmnet_port *port = rmnet_get_port_rtnl(slave_dev);
48960d58f97SSubash Abhinov Kasiviswanathan
490d939b6d3STaehee Yoo rmnet_unregister_bridge(port);
49160d58f97SSubash Abhinov Kasiviswanathan
49260d58f97SSubash Abhinov Kasiviswanathan netdev_dbg(slave_dev, "removed from rmnet as slave\n");
49360d58f97SSubash Abhinov Kasiviswanathan return 0;
49460d58f97SSubash Abhinov Kasiviswanathan }
49560d58f97SSubash Abhinov Kasiviswanathan
496ceed73a2SSubash Abhinov Kasiviswanathan /* Startup/Shutdown */
497ceed73a2SSubash Abhinov Kasiviswanathan
rmnet_init(void)498ceed73a2SSubash Abhinov Kasiviswanathan static int __init rmnet_init(void)
499ceed73a2SSubash Abhinov Kasiviswanathan {
500ceed73a2SSubash Abhinov Kasiviswanathan int rc;
501ceed73a2SSubash Abhinov Kasiviswanathan
502ceed73a2SSubash Abhinov Kasiviswanathan rc = register_netdevice_notifier(&rmnet_dev_notifier);
503ceed73a2SSubash Abhinov Kasiviswanathan if (rc != 0)
504ceed73a2SSubash Abhinov Kasiviswanathan return rc;
505ceed73a2SSubash Abhinov Kasiviswanathan
506ceed73a2SSubash Abhinov Kasiviswanathan rc = rtnl_link_register(&rmnet_link_ops);
507ceed73a2SSubash Abhinov Kasiviswanathan if (rc != 0) {
508ceed73a2SSubash Abhinov Kasiviswanathan unregister_netdevice_notifier(&rmnet_dev_notifier);
509ceed73a2SSubash Abhinov Kasiviswanathan return rc;
510ceed73a2SSubash Abhinov Kasiviswanathan }
511ceed73a2SSubash Abhinov Kasiviswanathan return rc;
512ceed73a2SSubash Abhinov Kasiviswanathan }
513ceed73a2SSubash Abhinov Kasiviswanathan
rmnet_exit(void)514ceed73a2SSubash Abhinov Kasiviswanathan static void __exit rmnet_exit(void)
515ceed73a2SSubash Abhinov Kasiviswanathan {
516ceed73a2SSubash Abhinov Kasiviswanathan rtnl_link_unregister(&rmnet_link_ops);
517037f9cdfSTaehee Yoo unregister_netdevice_notifier(&rmnet_dev_notifier);
518ceed73a2SSubash Abhinov Kasiviswanathan }
519ceed73a2SSubash Abhinov Kasiviswanathan
520ceed73a2SSubash Abhinov Kasiviswanathan module_init(rmnet_init)
521ceed73a2SSubash Abhinov Kasiviswanathan module_exit(rmnet_exit)
522eed22a06STaehee Yoo MODULE_ALIAS_RTNL_LINK("rmnet");
523ceed73a2SSubash Abhinov Kasiviswanathan MODULE_LICENSE("GPL v2");
524