162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * RMNET configuration engine
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <net/sock.h>
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/netlink.h>
1062306a36Sopenharmony_ci#include <linux/netdevice.h>
1162306a36Sopenharmony_ci#include "rmnet_config.h"
1262306a36Sopenharmony_ci#include "rmnet_handlers.h"
1362306a36Sopenharmony_ci#include "rmnet_vnd.h"
1462306a36Sopenharmony_ci#include "rmnet_private.h"
1562306a36Sopenharmony_ci#include "rmnet_map.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/* Local Definitions and Declarations */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic const struct nla_policy rmnet_policy[IFLA_RMNET_MAX + 1] = {
2062306a36Sopenharmony_ci	[IFLA_RMNET_MUX_ID]	= { .type = NLA_U16 },
2162306a36Sopenharmony_ci	[IFLA_RMNET_FLAGS]	= { .len = sizeof(struct ifla_rmnet_flags) },
2262306a36Sopenharmony_ci};
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic int rmnet_is_real_dev_registered(const struct net_device *real_dev)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	return rcu_access_pointer(real_dev->rx_handler) == rmnet_rx_handler;
2762306a36Sopenharmony_ci}
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/* Needs rtnl lock */
3062306a36Sopenharmony_cistruct rmnet_port*
3162306a36Sopenharmony_cirmnet_get_port_rtnl(const struct net_device *real_dev)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	return rtnl_dereference(real_dev->rx_handler_data);
3462306a36Sopenharmony_ci}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic int rmnet_unregister_real_device(struct net_device *real_dev)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	struct rmnet_port *port = rmnet_get_port_rtnl(real_dev);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	if (port->nr_rmnet_devs)
4162306a36Sopenharmony_ci		return -EINVAL;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	rmnet_map_tx_aggregate_exit(port);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	netdev_rx_handler_unregister(real_dev);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	kfree(port);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	netdev_dbg(real_dev, "Removed from rmnet\n");
5062306a36Sopenharmony_ci	return 0;
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic int rmnet_register_real_device(struct net_device *real_dev,
5462306a36Sopenharmony_ci				      struct netlink_ext_ack *extack)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	struct rmnet_port *port;
5762306a36Sopenharmony_ci	int rc, entry;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	ASSERT_RTNL();
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	if (rmnet_is_real_dev_registered(real_dev)) {
6262306a36Sopenharmony_ci		port = rmnet_get_port_rtnl(real_dev);
6362306a36Sopenharmony_ci		if (port->rmnet_mode != RMNET_EPMODE_VND) {
6462306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "bridge device already exists");
6562306a36Sopenharmony_ci			return -EINVAL;
6662306a36Sopenharmony_ci		}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci		return 0;
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	port = kzalloc(sizeof(*port), GFP_KERNEL);
7262306a36Sopenharmony_ci	if (!port)
7362306a36Sopenharmony_ci		return -ENOMEM;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	port->dev = real_dev;
7662306a36Sopenharmony_ci	rc = netdev_rx_handler_register(real_dev, rmnet_rx_handler, port);
7762306a36Sopenharmony_ci	if (rc) {
7862306a36Sopenharmony_ci		kfree(port);
7962306a36Sopenharmony_ci		return -EBUSY;
8062306a36Sopenharmony_ci	}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	for (entry = 0; entry < RMNET_MAX_LOGICAL_EP; entry++)
8362306a36Sopenharmony_ci		INIT_HLIST_HEAD(&port->muxed_ep[entry]);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	rmnet_map_tx_aggregate_init(port);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	netdev_dbg(real_dev, "registered with rmnet\n");
8862306a36Sopenharmony_ci	return 0;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic void rmnet_unregister_bridge(struct rmnet_port *port)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	struct net_device *bridge_dev, *real_dev, *rmnet_dev;
9462306a36Sopenharmony_ci	struct rmnet_port *real_port;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	if (port->rmnet_mode != RMNET_EPMODE_BRIDGE)
9762306a36Sopenharmony_ci		return;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	rmnet_dev = port->rmnet_dev;
10062306a36Sopenharmony_ci	if (!port->nr_rmnet_devs) {
10162306a36Sopenharmony_ci		/* bridge device */
10262306a36Sopenharmony_ci		real_dev = port->bridge_ep;
10362306a36Sopenharmony_ci		bridge_dev = port->dev;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci		real_port = rmnet_get_port_rtnl(real_dev);
10662306a36Sopenharmony_ci		real_port->bridge_ep = NULL;
10762306a36Sopenharmony_ci		real_port->rmnet_mode = RMNET_EPMODE_VND;
10862306a36Sopenharmony_ci	} else {
10962306a36Sopenharmony_ci		/* real device */
11062306a36Sopenharmony_ci		bridge_dev = port->bridge_ep;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci		port->bridge_ep = NULL;
11362306a36Sopenharmony_ci		port->rmnet_mode = RMNET_EPMODE_VND;
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	netdev_upper_dev_unlink(bridge_dev, rmnet_dev);
11762306a36Sopenharmony_ci	rmnet_unregister_real_device(bridge_dev);
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic int rmnet_newlink(struct net *src_net, struct net_device *dev,
12162306a36Sopenharmony_ci			 struct nlattr *tb[], struct nlattr *data[],
12262306a36Sopenharmony_ci			 struct netlink_ext_ack *extack)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	u32 data_format = RMNET_FLAGS_INGRESS_DEAGGREGATION;
12562306a36Sopenharmony_ci	struct net_device *real_dev;
12662306a36Sopenharmony_ci	int mode = RMNET_EPMODE_VND;
12762306a36Sopenharmony_ci	struct rmnet_endpoint *ep;
12862306a36Sopenharmony_ci	struct rmnet_port *port;
12962306a36Sopenharmony_ci	int err = 0;
13062306a36Sopenharmony_ci	u16 mux_id;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	if (!tb[IFLA_LINK]) {
13362306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "link not specified");
13462306a36Sopenharmony_ci		return -EINVAL;
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	real_dev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK]));
13862306a36Sopenharmony_ci	if (!real_dev) {
13962306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "link does not exist");
14062306a36Sopenharmony_ci		return -ENODEV;
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	ep = kzalloc(sizeof(*ep), GFP_KERNEL);
14462306a36Sopenharmony_ci	if (!ep)
14562306a36Sopenharmony_ci		return -ENOMEM;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	mux_id = nla_get_u16(data[IFLA_RMNET_MUX_ID]);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	err = rmnet_register_real_device(real_dev, extack);
15062306a36Sopenharmony_ci	if (err)
15162306a36Sopenharmony_ci		goto err0;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	port = rmnet_get_port_rtnl(real_dev);
15462306a36Sopenharmony_ci	err = rmnet_vnd_newlink(mux_id, dev, port, real_dev, ep, extack);
15562306a36Sopenharmony_ci	if (err)
15662306a36Sopenharmony_ci		goto err1;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	err = netdev_upper_dev_link(real_dev, dev, extack);
15962306a36Sopenharmony_ci	if (err < 0)
16062306a36Sopenharmony_ci		goto err2;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	port->rmnet_mode = mode;
16362306a36Sopenharmony_ci	port->rmnet_dev = dev;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	hlist_add_head_rcu(&ep->hlnode, &port->muxed_ep[mux_id]);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if (data[IFLA_RMNET_FLAGS]) {
16862306a36Sopenharmony_ci		struct ifla_rmnet_flags *flags;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci		flags = nla_data(data[IFLA_RMNET_FLAGS]);
17162306a36Sopenharmony_ci		data_format &= ~flags->mask;
17262306a36Sopenharmony_ci		data_format |= flags->flags & flags->mask;
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	netdev_dbg(dev, "data format [0x%08X]\n", data_format);
17662306a36Sopenharmony_ci	port->data_format = data_format;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	return 0;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cierr2:
18162306a36Sopenharmony_ci	unregister_netdevice(dev);
18262306a36Sopenharmony_ci	rmnet_vnd_dellink(mux_id, port, ep);
18362306a36Sopenharmony_cierr1:
18462306a36Sopenharmony_ci	rmnet_unregister_real_device(real_dev);
18562306a36Sopenharmony_cierr0:
18662306a36Sopenharmony_ci	kfree(ep);
18762306a36Sopenharmony_ci	return err;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic void rmnet_dellink(struct net_device *dev, struct list_head *head)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	struct rmnet_priv *priv = netdev_priv(dev);
19362306a36Sopenharmony_ci	struct net_device *real_dev, *bridge_dev;
19462306a36Sopenharmony_ci	struct rmnet_port *real_port, *bridge_port;
19562306a36Sopenharmony_ci	struct rmnet_endpoint *ep;
19662306a36Sopenharmony_ci	u8 mux_id = priv->mux_id;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	real_dev = priv->real_dev;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if (!rmnet_is_real_dev_registered(real_dev))
20162306a36Sopenharmony_ci		return;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	real_port = rmnet_get_port_rtnl(real_dev);
20462306a36Sopenharmony_ci	bridge_dev = real_port->bridge_ep;
20562306a36Sopenharmony_ci	if (bridge_dev) {
20662306a36Sopenharmony_ci		bridge_port = rmnet_get_port_rtnl(bridge_dev);
20762306a36Sopenharmony_ci		rmnet_unregister_bridge(bridge_port);
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	ep = rmnet_get_endpoint(real_port, mux_id);
21162306a36Sopenharmony_ci	if (ep) {
21262306a36Sopenharmony_ci		hlist_del_init_rcu(&ep->hlnode);
21362306a36Sopenharmony_ci		rmnet_vnd_dellink(mux_id, real_port, ep);
21462306a36Sopenharmony_ci		kfree(ep);
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	netdev_upper_dev_unlink(real_dev, dev);
21862306a36Sopenharmony_ci	rmnet_unregister_real_device(real_dev);
21962306a36Sopenharmony_ci	unregister_netdevice_queue(dev, head);
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic void rmnet_force_unassociate_device(struct net_device *real_dev)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	struct hlist_node *tmp_ep;
22562306a36Sopenharmony_ci	struct rmnet_endpoint *ep;
22662306a36Sopenharmony_ci	struct rmnet_port *port;
22762306a36Sopenharmony_ci	unsigned long bkt_ep;
22862306a36Sopenharmony_ci	LIST_HEAD(list);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	port = rmnet_get_port_rtnl(real_dev);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	if (port->nr_rmnet_devs) {
23362306a36Sopenharmony_ci		/* real device */
23462306a36Sopenharmony_ci		rmnet_unregister_bridge(port);
23562306a36Sopenharmony_ci		hash_for_each_safe(port->muxed_ep, bkt_ep, tmp_ep, ep, hlnode) {
23662306a36Sopenharmony_ci			unregister_netdevice_queue(ep->egress_dev, &list);
23762306a36Sopenharmony_ci			netdev_upper_dev_unlink(real_dev, ep->egress_dev);
23862306a36Sopenharmony_ci			rmnet_vnd_dellink(ep->mux_id, port, ep);
23962306a36Sopenharmony_ci			hlist_del_init_rcu(&ep->hlnode);
24062306a36Sopenharmony_ci			kfree(ep);
24162306a36Sopenharmony_ci		}
24262306a36Sopenharmony_ci		rmnet_unregister_real_device(real_dev);
24362306a36Sopenharmony_ci		unregister_netdevice_many(&list);
24462306a36Sopenharmony_ci	} else {
24562306a36Sopenharmony_ci		rmnet_unregister_bridge(port);
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic int rmnet_config_notify_cb(struct notifier_block *nb,
25062306a36Sopenharmony_ci				  unsigned long event, void *data)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	struct net_device *real_dev = netdev_notifier_info_to_dev(data);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	if (!rmnet_is_real_dev_registered(real_dev))
25562306a36Sopenharmony_ci		return NOTIFY_DONE;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	switch (event) {
25862306a36Sopenharmony_ci	case NETDEV_UNREGISTER:
25962306a36Sopenharmony_ci		netdev_dbg(real_dev, "Kernel unregister\n");
26062306a36Sopenharmony_ci		rmnet_force_unassociate_device(real_dev);
26162306a36Sopenharmony_ci		break;
26262306a36Sopenharmony_ci	case NETDEV_CHANGEMTU:
26362306a36Sopenharmony_ci		if (rmnet_vnd_validate_real_dev_mtu(real_dev))
26462306a36Sopenharmony_ci			return NOTIFY_BAD;
26562306a36Sopenharmony_ci		break;
26662306a36Sopenharmony_ci	default:
26762306a36Sopenharmony_ci		break;
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return NOTIFY_DONE;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic struct notifier_block rmnet_dev_notifier __read_mostly = {
27462306a36Sopenharmony_ci	.notifier_call = rmnet_config_notify_cb,
27562306a36Sopenharmony_ci};
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic int rmnet_rtnl_validate(struct nlattr *tb[], struct nlattr *data[],
27862306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	u16 mux_id;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (!data || !data[IFLA_RMNET_MUX_ID]) {
28362306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "MUX ID not specified");
28462306a36Sopenharmony_ci		return -EINVAL;
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	mux_id = nla_get_u16(data[IFLA_RMNET_MUX_ID]);
28862306a36Sopenharmony_ci	if (mux_id > (RMNET_MAX_LOGICAL_EP - 1)) {
28962306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "invalid MUX ID");
29062306a36Sopenharmony_ci		return -ERANGE;
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	return 0;
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic int rmnet_changelink(struct net_device *dev, struct nlattr *tb[],
29762306a36Sopenharmony_ci			    struct nlattr *data[],
29862306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	struct rmnet_priv *priv = netdev_priv(dev);
30162306a36Sopenharmony_ci	struct net_device *real_dev;
30262306a36Sopenharmony_ci	struct rmnet_port *port;
30362306a36Sopenharmony_ci	u16 mux_id;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	if (!dev)
30662306a36Sopenharmony_ci		return -ENODEV;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	real_dev = priv->real_dev;
30962306a36Sopenharmony_ci	if (!rmnet_is_real_dev_registered(real_dev))
31062306a36Sopenharmony_ci		return -ENODEV;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	port = rmnet_get_port_rtnl(real_dev);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	if (data[IFLA_RMNET_MUX_ID]) {
31562306a36Sopenharmony_ci		mux_id = nla_get_u16(data[IFLA_RMNET_MUX_ID]);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci		if (mux_id != priv->mux_id) {
31862306a36Sopenharmony_ci			struct rmnet_endpoint *ep;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci			ep = rmnet_get_endpoint(port, priv->mux_id);
32162306a36Sopenharmony_ci			if (!ep)
32262306a36Sopenharmony_ci				return -ENODEV;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci			if (rmnet_get_endpoint(port, mux_id)) {
32562306a36Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack,
32662306a36Sopenharmony_ci						   "MUX ID already exists");
32762306a36Sopenharmony_ci				return -EINVAL;
32862306a36Sopenharmony_ci			}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci			hlist_del_init_rcu(&ep->hlnode);
33162306a36Sopenharmony_ci			hlist_add_head_rcu(&ep->hlnode,
33262306a36Sopenharmony_ci					   &port->muxed_ep[mux_id]);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci			ep->mux_id = mux_id;
33562306a36Sopenharmony_ci			priv->mux_id = mux_id;
33662306a36Sopenharmony_ci		}
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	if (data[IFLA_RMNET_FLAGS]) {
34062306a36Sopenharmony_ci		struct ifla_rmnet_flags *flags;
34162306a36Sopenharmony_ci		u32 old_data_format;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci		old_data_format = port->data_format;
34462306a36Sopenharmony_ci		flags = nla_data(data[IFLA_RMNET_FLAGS]);
34562306a36Sopenharmony_ci		port->data_format &= ~flags->mask;
34662306a36Sopenharmony_ci		port->data_format |= flags->flags & flags->mask;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci		if (rmnet_vnd_update_dev_mtu(port, real_dev)) {
34962306a36Sopenharmony_ci			port->data_format = old_data_format;
35062306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Invalid MTU on real dev");
35162306a36Sopenharmony_ci			return -EINVAL;
35262306a36Sopenharmony_ci		}
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	return 0;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic size_t rmnet_get_size(const struct net_device *dev)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	return
36162306a36Sopenharmony_ci		/* IFLA_RMNET_MUX_ID */
36262306a36Sopenharmony_ci		nla_total_size(2) +
36362306a36Sopenharmony_ci		/* IFLA_RMNET_FLAGS */
36462306a36Sopenharmony_ci		nla_total_size(sizeof(struct ifla_rmnet_flags));
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cistatic int rmnet_fill_info(struct sk_buff *skb, const struct net_device *dev)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	struct rmnet_priv *priv = netdev_priv(dev);
37062306a36Sopenharmony_ci	struct net_device *real_dev;
37162306a36Sopenharmony_ci	struct ifla_rmnet_flags f;
37262306a36Sopenharmony_ci	struct rmnet_port *port;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	real_dev = priv->real_dev;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	if (nla_put_u16(skb, IFLA_RMNET_MUX_ID, priv->mux_id))
37762306a36Sopenharmony_ci		goto nla_put_failure;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	if (rmnet_is_real_dev_registered(real_dev)) {
38062306a36Sopenharmony_ci		port = rmnet_get_port_rtnl(real_dev);
38162306a36Sopenharmony_ci		f.flags = port->data_format;
38262306a36Sopenharmony_ci	} else {
38362306a36Sopenharmony_ci		f.flags = 0;
38462306a36Sopenharmony_ci	}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	f.mask  = ~0;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	if (nla_put(skb, IFLA_RMNET_FLAGS, sizeof(f), &f))
38962306a36Sopenharmony_ci		goto nla_put_failure;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	return 0;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cinla_put_failure:
39462306a36Sopenharmony_ci	return -EMSGSIZE;
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistruct rtnl_link_ops rmnet_link_ops __read_mostly = {
39862306a36Sopenharmony_ci	.kind		= "rmnet",
39962306a36Sopenharmony_ci	.maxtype	= IFLA_RMNET_MAX,
40062306a36Sopenharmony_ci	.priv_size	= sizeof(struct rmnet_priv),
40162306a36Sopenharmony_ci	.setup		= rmnet_vnd_setup,
40262306a36Sopenharmony_ci	.validate	= rmnet_rtnl_validate,
40362306a36Sopenharmony_ci	.newlink	= rmnet_newlink,
40462306a36Sopenharmony_ci	.dellink	= rmnet_dellink,
40562306a36Sopenharmony_ci	.get_size	= rmnet_get_size,
40662306a36Sopenharmony_ci	.changelink     = rmnet_changelink,
40762306a36Sopenharmony_ci	.policy		= rmnet_policy,
40862306a36Sopenharmony_ci	.fill_info	= rmnet_fill_info,
40962306a36Sopenharmony_ci};
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_cistruct rmnet_port *rmnet_get_port_rcu(struct net_device *real_dev)
41262306a36Sopenharmony_ci{
41362306a36Sopenharmony_ci	if (rmnet_is_real_dev_registered(real_dev))
41462306a36Sopenharmony_ci		return rcu_dereference_bh(real_dev->rx_handler_data);
41562306a36Sopenharmony_ci	else
41662306a36Sopenharmony_ci		return NULL;
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_cistruct rmnet_endpoint *rmnet_get_endpoint(struct rmnet_port *port, u8 mux_id)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	struct rmnet_endpoint *ep;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	hlist_for_each_entry_rcu(ep, &port->muxed_ep[mux_id], hlnode) {
42462306a36Sopenharmony_ci		if (ep->mux_id == mux_id)
42562306a36Sopenharmony_ci			return ep;
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	return NULL;
42962306a36Sopenharmony_ci}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ciint rmnet_add_bridge(struct net_device *rmnet_dev,
43262306a36Sopenharmony_ci		     struct net_device *slave_dev,
43362306a36Sopenharmony_ci		     struct netlink_ext_ack *extack)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	struct rmnet_priv *priv = netdev_priv(rmnet_dev);
43662306a36Sopenharmony_ci	struct net_device *real_dev = priv->real_dev;
43762306a36Sopenharmony_ci	struct rmnet_port *port, *slave_port;
43862306a36Sopenharmony_ci	int err;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	port = rmnet_get_port_rtnl(real_dev);
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	/* If there is more than one rmnet dev attached, its probably being
44362306a36Sopenharmony_ci	 * used for muxing. Skip the briding in that case
44462306a36Sopenharmony_ci	 */
44562306a36Sopenharmony_ci	if (port->nr_rmnet_devs > 1) {
44662306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "more than one rmnet dev attached");
44762306a36Sopenharmony_ci		return -EINVAL;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	if (port->rmnet_mode != RMNET_EPMODE_VND) {
45162306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "more than one bridge dev attached");
45262306a36Sopenharmony_ci		return -EINVAL;
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	if (rmnet_is_real_dev_registered(slave_dev)) {
45662306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
45762306a36Sopenharmony_ci				   "slave cannot be another rmnet dev");
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci		return -EBUSY;
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	err = rmnet_register_real_device(slave_dev, extack);
46362306a36Sopenharmony_ci	if (err)
46462306a36Sopenharmony_ci		return -EBUSY;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	err = netdev_master_upper_dev_link(slave_dev, rmnet_dev, NULL, NULL,
46762306a36Sopenharmony_ci					   extack);
46862306a36Sopenharmony_ci	if (err) {
46962306a36Sopenharmony_ci		rmnet_unregister_real_device(slave_dev);
47062306a36Sopenharmony_ci		return err;
47162306a36Sopenharmony_ci	}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	slave_port = rmnet_get_port_rtnl(slave_dev);
47462306a36Sopenharmony_ci	slave_port->rmnet_mode = RMNET_EPMODE_BRIDGE;
47562306a36Sopenharmony_ci	slave_port->bridge_ep = real_dev;
47662306a36Sopenharmony_ci	slave_port->rmnet_dev = rmnet_dev;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	port->rmnet_mode = RMNET_EPMODE_BRIDGE;
47962306a36Sopenharmony_ci	port->bridge_ep = slave_dev;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	netdev_dbg(slave_dev, "registered with rmnet as slave\n");
48262306a36Sopenharmony_ci	return 0;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ciint rmnet_del_bridge(struct net_device *rmnet_dev,
48662306a36Sopenharmony_ci		     struct net_device *slave_dev)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	struct rmnet_port *port = rmnet_get_port_rtnl(slave_dev);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	rmnet_unregister_bridge(port);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	netdev_dbg(slave_dev, "removed from rmnet as slave\n");
49362306a36Sopenharmony_ci	return 0;
49462306a36Sopenharmony_ci}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci/* Startup/Shutdown */
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_cistatic int __init rmnet_init(void)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	int rc;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	rc = register_netdevice_notifier(&rmnet_dev_notifier);
50362306a36Sopenharmony_ci	if (rc != 0)
50462306a36Sopenharmony_ci		return rc;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	rc = rtnl_link_register(&rmnet_link_ops);
50762306a36Sopenharmony_ci	if (rc != 0) {
50862306a36Sopenharmony_ci		unregister_netdevice_notifier(&rmnet_dev_notifier);
50962306a36Sopenharmony_ci		return rc;
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci	return rc;
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_cistatic void __exit rmnet_exit(void)
51562306a36Sopenharmony_ci{
51662306a36Sopenharmony_ci	rtnl_link_unregister(&rmnet_link_ops);
51762306a36Sopenharmony_ci	unregister_netdevice_notifier(&rmnet_dev_notifier);
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_cimodule_init(rmnet_init)
52162306a36Sopenharmony_cimodule_exit(rmnet_exit)
52262306a36Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("rmnet");
52362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
524