18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * RMNET configuration engine
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <net/sock.h>
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/netlink.h>
108c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
118c2ecf20Sopenharmony_ci#include "rmnet_config.h"
128c2ecf20Sopenharmony_ci#include "rmnet_handlers.h"
138c2ecf20Sopenharmony_ci#include "rmnet_vnd.h"
148c2ecf20Sopenharmony_ci#include "rmnet_private.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/* Local Definitions and Declarations */
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic const struct nla_policy rmnet_policy[IFLA_RMNET_MAX + 1] = {
198c2ecf20Sopenharmony_ci	[IFLA_RMNET_MUX_ID]	= { .type = NLA_U16 },
208c2ecf20Sopenharmony_ci	[IFLA_RMNET_FLAGS]	= { .len = sizeof(struct ifla_rmnet_flags) },
218c2ecf20Sopenharmony_ci};
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic int rmnet_is_real_dev_registered(const struct net_device *real_dev)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	return rcu_access_pointer(real_dev->rx_handler) == rmnet_rx_handler;
268c2ecf20Sopenharmony_ci}
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* Needs rtnl lock */
298c2ecf20Sopenharmony_cistruct rmnet_port*
308c2ecf20Sopenharmony_cirmnet_get_port_rtnl(const struct net_device *real_dev)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	return rtnl_dereference(real_dev->rx_handler_data);
338c2ecf20Sopenharmony_ci}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic int rmnet_unregister_real_device(struct net_device *real_dev)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	struct rmnet_port *port = rmnet_get_port_rtnl(real_dev);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	if (port->nr_rmnet_devs)
408c2ecf20Sopenharmony_ci		return -EINVAL;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	netdev_rx_handler_unregister(real_dev);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	kfree(port);
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	netdev_dbg(real_dev, "Removed from rmnet\n");
478c2ecf20Sopenharmony_ci	return 0;
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic int rmnet_register_real_device(struct net_device *real_dev,
518c2ecf20Sopenharmony_ci				      struct netlink_ext_ack *extack)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	struct rmnet_port *port;
548c2ecf20Sopenharmony_ci	int rc, entry;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	ASSERT_RTNL();
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	if (rmnet_is_real_dev_registered(real_dev)) {
598c2ecf20Sopenharmony_ci		port = rmnet_get_port_rtnl(real_dev);
608c2ecf20Sopenharmony_ci		if (port->rmnet_mode != RMNET_EPMODE_VND) {
618c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "bridge device already exists");
628c2ecf20Sopenharmony_ci			return -EINVAL;
638c2ecf20Sopenharmony_ci		}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci		return 0;
668c2ecf20Sopenharmony_ci	}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	port = kzalloc(sizeof(*port), GFP_KERNEL);
698c2ecf20Sopenharmony_ci	if (!port)
708c2ecf20Sopenharmony_ci		return -ENOMEM;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	port->dev = real_dev;
738c2ecf20Sopenharmony_ci	rc = netdev_rx_handler_register(real_dev, rmnet_rx_handler, port);
748c2ecf20Sopenharmony_ci	if (rc) {
758c2ecf20Sopenharmony_ci		kfree(port);
768c2ecf20Sopenharmony_ci		return -EBUSY;
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	for (entry = 0; entry < RMNET_MAX_LOGICAL_EP; entry++)
808c2ecf20Sopenharmony_ci		INIT_HLIST_HEAD(&port->muxed_ep[entry]);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	netdev_dbg(real_dev, "registered with rmnet\n");
838c2ecf20Sopenharmony_ci	return 0;
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic void rmnet_unregister_bridge(struct rmnet_port *port)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	struct net_device *bridge_dev, *real_dev, *rmnet_dev;
898c2ecf20Sopenharmony_ci	struct rmnet_port *real_port;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	if (port->rmnet_mode != RMNET_EPMODE_BRIDGE)
928c2ecf20Sopenharmony_ci		return;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	rmnet_dev = port->rmnet_dev;
958c2ecf20Sopenharmony_ci	if (!port->nr_rmnet_devs) {
968c2ecf20Sopenharmony_ci		/* bridge device */
978c2ecf20Sopenharmony_ci		real_dev = port->bridge_ep;
988c2ecf20Sopenharmony_ci		bridge_dev = port->dev;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci		real_port = rmnet_get_port_rtnl(real_dev);
1018c2ecf20Sopenharmony_ci		real_port->bridge_ep = NULL;
1028c2ecf20Sopenharmony_ci		real_port->rmnet_mode = RMNET_EPMODE_VND;
1038c2ecf20Sopenharmony_ci	} else {
1048c2ecf20Sopenharmony_ci		/* real device */
1058c2ecf20Sopenharmony_ci		bridge_dev = port->bridge_ep;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci		port->bridge_ep = NULL;
1088c2ecf20Sopenharmony_ci		port->rmnet_mode = RMNET_EPMODE_VND;
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	netdev_upper_dev_unlink(bridge_dev, rmnet_dev);
1128c2ecf20Sopenharmony_ci	rmnet_unregister_real_device(bridge_dev);
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic int rmnet_newlink(struct net *src_net, struct net_device *dev,
1168c2ecf20Sopenharmony_ci			 struct nlattr *tb[], struct nlattr *data[],
1178c2ecf20Sopenharmony_ci			 struct netlink_ext_ack *extack)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	u32 data_format = RMNET_FLAGS_INGRESS_DEAGGREGATION;
1208c2ecf20Sopenharmony_ci	struct net_device *real_dev;
1218c2ecf20Sopenharmony_ci	int mode = RMNET_EPMODE_VND;
1228c2ecf20Sopenharmony_ci	struct rmnet_endpoint *ep;
1238c2ecf20Sopenharmony_ci	struct rmnet_port *port;
1248c2ecf20Sopenharmony_ci	int err = 0;
1258c2ecf20Sopenharmony_ci	u16 mux_id;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	if (!tb[IFLA_LINK]) {
1288c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "link not specified");
1298c2ecf20Sopenharmony_ci		return -EINVAL;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	real_dev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK]));
1338c2ecf20Sopenharmony_ci	if (!real_dev) {
1348c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "link does not exist");
1358c2ecf20Sopenharmony_ci		return -ENODEV;
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	ep = kzalloc(sizeof(*ep), GFP_KERNEL);
1398c2ecf20Sopenharmony_ci	if (!ep)
1408c2ecf20Sopenharmony_ci		return -ENOMEM;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	mux_id = nla_get_u16(data[IFLA_RMNET_MUX_ID]);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	err = rmnet_register_real_device(real_dev, extack);
1458c2ecf20Sopenharmony_ci	if (err)
1468c2ecf20Sopenharmony_ci		goto err0;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	port = rmnet_get_port_rtnl(real_dev);
1498c2ecf20Sopenharmony_ci	err = rmnet_vnd_newlink(mux_id, dev, port, real_dev, ep, extack);
1508c2ecf20Sopenharmony_ci	if (err)
1518c2ecf20Sopenharmony_ci		goto err1;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	err = netdev_upper_dev_link(real_dev, dev, extack);
1548c2ecf20Sopenharmony_ci	if (err < 0)
1558c2ecf20Sopenharmony_ci		goto err2;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	port->rmnet_mode = mode;
1588c2ecf20Sopenharmony_ci	port->rmnet_dev = dev;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	hlist_add_head_rcu(&ep->hlnode, &port->muxed_ep[mux_id]);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (data[IFLA_RMNET_FLAGS]) {
1638c2ecf20Sopenharmony_ci		struct ifla_rmnet_flags *flags;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci		flags = nla_data(data[IFLA_RMNET_FLAGS]);
1668c2ecf20Sopenharmony_ci		data_format = flags->flags & flags->mask;
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	netdev_dbg(dev, "data format [0x%08X]\n", data_format);
1708c2ecf20Sopenharmony_ci	port->data_format = data_format;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	return 0;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cierr2:
1758c2ecf20Sopenharmony_ci	unregister_netdevice(dev);
1768c2ecf20Sopenharmony_ci	rmnet_vnd_dellink(mux_id, port, ep);
1778c2ecf20Sopenharmony_cierr1:
1788c2ecf20Sopenharmony_ci	rmnet_unregister_real_device(real_dev);
1798c2ecf20Sopenharmony_cierr0:
1808c2ecf20Sopenharmony_ci	kfree(ep);
1818c2ecf20Sopenharmony_ci	return err;
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic void rmnet_dellink(struct net_device *dev, struct list_head *head)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	struct rmnet_priv *priv = netdev_priv(dev);
1878c2ecf20Sopenharmony_ci	struct net_device *real_dev, *bridge_dev;
1888c2ecf20Sopenharmony_ci	struct rmnet_port *real_port, *bridge_port;
1898c2ecf20Sopenharmony_ci	struct rmnet_endpoint *ep;
1908c2ecf20Sopenharmony_ci	u8 mux_id = priv->mux_id;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	real_dev = priv->real_dev;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	if (!rmnet_is_real_dev_registered(real_dev))
1958c2ecf20Sopenharmony_ci		return;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	real_port = rmnet_get_port_rtnl(real_dev);
1988c2ecf20Sopenharmony_ci	bridge_dev = real_port->bridge_ep;
1998c2ecf20Sopenharmony_ci	if (bridge_dev) {
2008c2ecf20Sopenharmony_ci		bridge_port = rmnet_get_port_rtnl(bridge_dev);
2018c2ecf20Sopenharmony_ci		rmnet_unregister_bridge(bridge_port);
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	ep = rmnet_get_endpoint(real_port, mux_id);
2058c2ecf20Sopenharmony_ci	if (ep) {
2068c2ecf20Sopenharmony_ci		hlist_del_init_rcu(&ep->hlnode);
2078c2ecf20Sopenharmony_ci		rmnet_vnd_dellink(mux_id, real_port, ep);
2088c2ecf20Sopenharmony_ci		kfree(ep);
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	netdev_upper_dev_unlink(real_dev, dev);
2128c2ecf20Sopenharmony_ci	rmnet_unregister_real_device(real_dev);
2138c2ecf20Sopenharmony_ci	unregister_netdevice_queue(dev, head);
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic void rmnet_force_unassociate_device(struct net_device *real_dev)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	struct hlist_node *tmp_ep;
2198c2ecf20Sopenharmony_ci	struct rmnet_endpoint *ep;
2208c2ecf20Sopenharmony_ci	struct rmnet_port *port;
2218c2ecf20Sopenharmony_ci	unsigned long bkt_ep;
2228c2ecf20Sopenharmony_ci	LIST_HEAD(list);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	port = rmnet_get_port_rtnl(real_dev);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	if (port->nr_rmnet_devs) {
2278c2ecf20Sopenharmony_ci		/* real device */
2288c2ecf20Sopenharmony_ci		rmnet_unregister_bridge(port);
2298c2ecf20Sopenharmony_ci		hash_for_each_safe(port->muxed_ep, bkt_ep, tmp_ep, ep, hlnode) {
2308c2ecf20Sopenharmony_ci			unregister_netdevice_queue(ep->egress_dev, &list);
2318c2ecf20Sopenharmony_ci			netdev_upper_dev_unlink(real_dev, ep->egress_dev);
2328c2ecf20Sopenharmony_ci			rmnet_vnd_dellink(ep->mux_id, port, ep);
2338c2ecf20Sopenharmony_ci			hlist_del_init_rcu(&ep->hlnode);
2348c2ecf20Sopenharmony_ci			kfree(ep);
2358c2ecf20Sopenharmony_ci		}
2368c2ecf20Sopenharmony_ci		rmnet_unregister_real_device(real_dev);
2378c2ecf20Sopenharmony_ci		unregister_netdevice_many(&list);
2388c2ecf20Sopenharmony_ci	} else {
2398c2ecf20Sopenharmony_ci		rmnet_unregister_bridge(port);
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic int rmnet_config_notify_cb(struct notifier_block *nb,
2448c2ecf20Sopenharmony_ci				  unsigned long event, void *data)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	struct net_device *real_dev = netdev_notifier_info_to_dev(data);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	if (!rmnet_is_real_dev_registered(real_dev))
2498c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	switch (event) {
2528c2ecf20Sopenharmony_ci	case NETDEV_UNREGISTER:
2538c2ecf20Sopenharmony_ci		netdev_dbg(real_dev, "Kernel unregister\n");
2548c2ecf20Sopenharmony_ci		rmnet_force_unassociate_device(real_dev);
2558c2ecf20Sopenharmony_ci		break;
2568c2ecf20Sopenharmony_ci	case NETDEV_CHANGEMTU:
2578c2ecf20Sopenharmony_ci		if (rmnet_vnd_validate_real_dev_mtu(real_dev))
2588c2ecf20Sopenharmony_ci			return NOTIFY_BAD;
2598c2ecf20Sopenharmony_ci		break;
2608c2ecf20Sopenharmony_ci	default:
2618c2ecf20Sopenharmony_ci		break;
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic struct notifier_block rmnet_dev_notifier __read_mostly = {
2688c2ecf20Sopenharmony_ci	.notifier_call = rmnet_config_notify_cb,
2698c2ecf20Sopenharmony_ci};
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic int rmnet_rtnl_validate(struct nlattr *tb[], struct nlattr *data[],
2728c2ecf20Sopenharmony_ci			       struct netlink_ext_ack *extack)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	u16 mux_id;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	if (!data || !data[IFLA_RMNET_MUX_ID]) {
2778c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "MUX ID not specified");
2788c2ecf20Sopenharmony_ci		return -EINVAL;
2798c2ecf20Sopenharmony_ci	}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	mux_id = nla_get_u16(data[IFLA_RMNET_MUX_ID]);
2828c2ecf20Sopenharmony_ci	if (mux_id > (RMNET_MAX_LOGICAL_EP - 1)) {
2838c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "invalid MUX ID");
2848c2ecf20Sopenharmony_ci		return -ERANGE;
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	return 0;
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic int rmnet_changelink(struct net_device *dev, struct nlattr *tb[],
2918c2ecf20Sopenharmony_ci			    struct nlattr *data[],
2928c2ecf20Sopenharmony_ci			    struct netlink_ext_ack *extack)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	struct rmnet_priv *priv = netdev_priv(dev);
2958c2ecf20Sopenharmony_ci	struct net_device *real_dev;
2968c2ecf20Sopenharmony_ci	struct rmnet_port *port;
2978c2ecf20Sopenharmony_ci	u16 mux_id;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	if (!dev)
3008c2ecf20Sopenharmony_ci		return -ENODEV;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	real_dev = priv->real_dev;
3038c2ecf20Sopenharmony_ci	if (!rmnet_is_real_dev_registered(real_dev))
3048c2ecf20Sopenharmony_ci		return -ENODEV;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	port = rmnet_get_port_rtnl(real_dev);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	if (data[IFLA_RMNET_MUX_ID]) {
3098c2ecf20Sopenharmony_ci		mux_id = nla_get_u16(data[IFLA_RMNET_MUX_ID]);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci		if (mux_id != priv->mux_id) {
3128c2ecf20Sopenharmony_ci			struct rmnet_endpoint *ep;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci			ep = rmnet_get_endpoint(port, priv->mux_id);
3158c2ecf20Sopenharmony_ci			if (!ep)
3168c2ecf20Sopenharmony_ci				return -ENODEV;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci			if (rmnet_get_endpoint(port, mux_id)) {
3198c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack,
3208c2ecf20Sopenharmony_ci						   "MUX ID already exists");
3218c2ecf20Sopenharmony_ci				return -EINVAL;
3228c2ecf20Sopenharmony_ci			}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci			hlist_del_init_rcu(&ep->hlnode);
3258c2ecf20Sopenharmony_ci			hlist_add_head_rcu(&ep->hlnode,
3268c2ecf20Sopenharmony_ci					   &port->muxed_ep[mux_id]);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci			ep->mux_id = mux_id;
3298c2ecf20Sopenharmony_ci			priv->mux_id = mux_id;
3308c2ecf20Sopenharmony_ci		}
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	if (data[IFLA_RMNET_FLAGS]) {
3348c2ecf20Sopenharmony_ci		struct ifla_rmnet_flags *flags;
3358c2ecf20Sopenharmony_ci		u32 old_data_format;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci		old_data_format = port->data_format;
3388c2ecf20Sopenharmony_ci		flags = nla_data(data[IFLA_RMNET_FLAGS]);
3398c2ecf20Sopenharmony_ci		port->data_format = flags->flags & flags->mask;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci		if (rmnet_vnd_update_dev_mtu(port, real_dev)) {
3428c2ecf20Sopenharmony_ci			port->data_format = old_data_format;
3438c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Invalid MTU on real dev");
3448c2ecf20Sopenharmony_ci			return -EINVAL;
3458c2ecf20Sopenharmony_ci		}
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	return 0;
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic size_t rmnet_get_size(const struct net_device *dev)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	return
3548c2ecf20Sopenharmony_ci		/* IFLA_RMNET_MUX_ID */
3558c2ecf20Sopenharmony_ci		nla_total_size(2) +
3568c2ecf20Sopenharmony_ci		/* IFLA_RMNET_FLAGS */
3578c2ecf20Sopenharmony_ci		nla_total_size(sizeof(struct ifla_rmnet_flags));
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic int rmnet_fill_info(struct sk_buff *skb, const struct net_device *dev)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	struct rmnet_priv *priv = netdev_priv(dev);
3638c2ecf20Sopenharmony_ci	struct net_device *real_dev;
3648c2ecf20Sopenharmony_ci	struct ifla_rmnet_flags f;
3658c2ecf20Sopenharmony_ci	struct rmnet_port *port;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	real_dev = priv->real_dev;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	if (nla_put_u16(skb, IFLA_RMNET_MUX_ID, priv->mux_id))
3708c2ecf20Sopenharmony_ci		goto nla_put_failure;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (rmnet_is_real_dev_registered(real_dev)) {
3738c2ecf20Sopenharmony_ci		port = rmnet_get_port_rtnl(real_dev);
3748c2ecf20Sopenharmony_ci		f.flags = port->data_format;
3758c2ecf20Sopenharmony_ci	} else {
3768c2ecf20Sopenharmony_ci		f.flags = 0;
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	f.mask  = ~0;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	if (nla_put(skb, IFLA_RMNET_FLAGS, sizeof(f), &f))
3828c2ecf20Sopenharmony_ci		goto nla_put_failure;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	return 0;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cinla_put_failure:
3878c2ecf20Sopenharmony_ci	return -EMSGSIZE;
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cistruct rtnl_link_ops rmnet_link_ops __read_mostly = {
3918c2ecf20Sopenharmony_ci	.kind		= "rmnet",
3928c2ecf20Sopenharmony_ci	.maxtype	= IFLA_RMNET_MAX,
3938c2ecf20Sopenharmony_ci	.priv_size	= sizeof(struct rmnet_priv),
3948c2ecf20Sopenharmony_ci	.setup		= rmnet_vnd_setup,
3958c2ecf20Sopenharmony_ci	.validate	= rmnet_rtnl_validate,
3968c2ecf20Sopenharmony_ci	.newlink	= rmnet_newlink,
3978c2ecf20Sopenharmony_ci	.dellink	= rmnet_dellink,
3988c2ecf20Sopenharmony_ci	.get_size	= rmnet_get_size,
3998c2ecf20Sopenharmony_ci	.changelink     = rmnet_changelink,
4008c2ecf20Sopenharmony_ci	.policy		= rmnet_policy,
4018c2ecf20Sopenharmony_ci	.fill_info	= rmnet_fill_info,
4028c2ecf20Sopenharmony_ci};
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistruct rmnet_port *rmnet_get_port_rcu(struct net_device *real_dev)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	if (rmnet_is_real_dev_registered(real_dev))
4078c2ecf20Sopenharmony_ci		return rcu_dereference_bh(real_dev->rx_handler_data);
4088c2ecf20Sopenharmony_ci	else
4098c2ecf20Sopenharmony_ci		return NULL;
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistruct rmnet_endpoint *rmnet_get_endpoint(struct rmnet_port *port, u8 mux_id)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	struct rmnet_endpoint *ep;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(ep, &port->muxed_ep[mux_id], hlnode) {
4178c2ecf20Sopenharmony_ci		if (ep->mux_id == mux_id)
4188c2ecf20Sopenharmony_ci			return ep;
4198c2ecf20Sopenharmony_ci	}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	return NULL;
4228c2ecf20Sopenharmony_ci}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ciint rmnet_add_bridge(struct net_device *rmnet_dev,
4258c2ecf20Sopenharmony_ci		     struct net_device *slave_dev,
4268c2ecf20Sopenharmony_ci		     struct netlink_ext_ack *extack)
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	struct rmnet_priv *priv = netdev_priv(rmnet_dev);
4298c2ecf20Sopenharmony_ci	struct net_device *real_dev = priv->real_dev;
4308c2ecf20Sopenharmony_ci	struct rmnet_port *port, *slave_port;
4318c2ecf20Sopenharmony_ci	int err;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	port = rmnet_get_port_rtnl(real_dev);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	/* If there is more than one rmnet dev attached, its probably being
4368c2ecf20Sopenharmony_ci	 * used for muxing. Skip the briding in that case
4378c2ecf20Sopenharmony_ci	 */
4388c2ecf20Sopenharmony_ci	if (port->nr_rmnet_devs > 1) {
4398c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "more than one rmnet dev attached");
4408c2ecf20Sopenharmony_ci		return -EINVAL;
4418c2ecf20Sopenharmony_ci	}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	if (port->rmnet_mode != RMNET_EPMODE_VND) {
4448c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "more than one bridge dev attached");
4458c2ecf20Sopenharmony_ci		return -EINVAL;
4468c2ecf20Sopenharmony_ci	}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	if (rmnet_is_real_dev_registered(slave_dev)) {
4498c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
4508c2ecf20Sopenharmony_ci				   "slave cannot be another rmnet dev");
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci		return -EBUSY;
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	err = rmnet_register_real_device(slave_dev, extack);
4568c2ecf20Sopenharmony_ci	if (err)
4578c2ecf20Sopenharmony_ci		return -EBUSY;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	err = netdev_master_upper_dev_link(slave_dev, rmnet_dev, NULL, NULL,
4608c2ecf20Sopenharmony_ci					   extack);
4618c2ecf20Sopenharmony_ci	if (err) {
4628c2ecf20Sopenharmony_ci		rmnet_unregister_real_device(slave_dev);
4638c2ecf20Sopenharmony_ci		return err;
4648c2ecf20Sopenharmony_ci	}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	slave_port = rmnet_get_port_rtnl(slave_dev);
4678c2ecf20Sopenharmony_ci	slave_port->rmnet_mode = RMNET_EPMODE_BRIDGE;
4688c2ecf20Sopenharmony_ci	slave_port->bridge_ep = real_dev;
4698c2ecf20Sopenharmony_ci	slave_port->rmnet_dev = rmnet_dev;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	port->rmnet_mode = RMNET_EPMODE_BRIDGE;
4728c2ecf20Sopenharmony_ci	port->bridge_ep = slave_dev;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	netdev_dbg(slave_dev, "registered with rmnet as slave\n");
4758c2ecf20Sopenharmony_ci	return 0;
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ciint rmnet_del_bridge(struct net_device *rmnet_dev,
4798c2ecf20Sopenharmony_ci		     struct net_device *slave_dev)
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci	struct rmnet_port *port = rmnet_get_port_rtnl(slave_dev);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	rmnet_unregister_bridge(port);
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	netdev_dbg(slave_dev, "removed from rmnet as slave\n");
4868c2ecf20Sopenharmony_ci	return 0;
4878c2ecf20Sopenharmony_ci}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci/* Startup/Shutdown */
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_cistatic int __init rmnet_init(void)
4928c2ecf20Sopenharmony_ci{
4938c2ecf20Sopenharmony_ci	int rc;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	rc = register_netdevice_notifier(&rmnet_dev_notifier);
4968c2ecf20Sopenharmony_ci	if (rc != 0)
4978c2ecf20Sopenharmony_ci		return rc;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	rc = rtnl_link_register(&rmnet_link_ops);
5008c2ecf20Sopenharmony_ci	if (rc != 0) {
5018c2ecf20Sopenharmony_ci		unregister_netdevice_notifier(&rmnet_dev_notifier);
5028c2ecf20Sopenharmony_ci		return rc;
5038c2ecf20Sopenharmony_ci	}
5048c2ecf20Sopenharmony_ci	return rc;
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistatic void __exit rmnet_exit(void)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	rtnl_link_unregister(&rmnet_link_ops);
5108c2ecf20Sopenharmony_ci	unregister_netdevice_notifier(&rmnet_dev_notifier);
5118c2ecf20Sopenharmony_ci}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_cimodule_init(rmnet_init)
5148c2ecf20Sopenharmony_cimodule_exit(rmnet_exit)
5158c2ecf20Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("rmnet");
5168c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
517