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