18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * vxcan.c - Virtual CAN Tunnel for cross namespace communication
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This code is derived from drivers/net/can/vcan.c for the virtual CAN
68c2ecf20Sopenharmony_ci * specific parts and from drivers/net/veth.c to implement the netlink API
78c2ecf20Sopenharmony_ci * for network interface pairs in a common and established way.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Copyright (c) 2017 Oliver Hartkopp <socketcan@hartkopp.net>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
158c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
168c2ecf20Sopenharmony_ci#include <linux/if_ether.h>
178c2ecf20Sopenharmony_ci#include <linux/can.h>
188c2ecf20Sopenharmony_ci#include <linux/can/dev.h>
198c2ecf20Sopenharmony_ci#include <linux/can/skb.h>
208c2ecf20Sopenharmony_ci#include <linux/can/vxcan.h>
218c2ecf20Sopenharmony_ci#include <linux/can/can-ml.h>
228c2ecf20Sopenharmony_ci#include <linux/slab.h>
238c2ecf20Sopenharmony_ci#include <net/rtnetlink.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define DRV_NAME "vxcan"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Virtual CAN Tunnel");
288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
298c2ecf20Sopenharmony_ciMODULE_AUTHOR("Oliver Hartkopp <socketcan@hartkopp.net>");
308c2ecf20Sopenharmony_ciMODULE_ALIAS_RTNL_LINK(DRV_NAME);
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct vxcan_priv {
338c2ecf20Sopenharmony_ci	struct net_device __rcu	*peer;
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic netdev_tx_t vxcan_xmit(struct sk_buff *skb, struct net_device *dev)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	struct vxcan_priv *priv = netdev_priv(dev);
398c2ecf20Sopenharmony_ci	struct net_device *peer;
408c2ecf20Sopenharmony_ci	struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
418c2ecf20Sopenharmony_ci	struct net_device_stats *peerstats, *srcstats = &dev->stats;
428c2ecf20Sopenharmony_ci	u8 len;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	if (can_dropped_invalid_skb(dev, skb))
458c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	rcu_read_lock();
488c2ecf20Sopenharmony_ci	peer = rcu_dereference(priv->peer);
498c2ecf20Sopenharmony_ci	if (unlikely(!peer)) {
508c2ecf20Sopenharmony_ci		kfree_skb(skb);
518c2ecf20Sopenharmony_ci		dev->stats.tx_dropped++;
528c2ecf20Sopenharmony_ci		goto out_unlock;
538c2ecf20Sopenharmony_ci	}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	skb = can_create_echo_skb(skb);
568c2ecf20Sopenharmony_ci	if (!skb)
578c2ecf20Sopenharmony_ci		goto out_unlock;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	/* reset CAN GW hop counter */
608c2ecf20Sopenharmony_ci	skb->csum_start = 0;
618c2ecf20Sopenharmony_ci	skb->pkt_type   = PACKET_BROADCAST;
628c2ecf20Sopenharmony_ci	skb->dev        = peer;
638c2ecf20Sopenharmony_ci	skb->ip_summed  = CHECKSUM_UNNECESSARY;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	len = cfd->len;
668c2ecf20Sopenharmony_ci	if (netif_rx_ni(skb) == NET_RX_SUCCESS) {
678c2ecf20Sopenharmony_ci		srcstats->tx_packets++;
688c2ecf20Sopenharmony_ci		srcstats->tx_bytes += len;
698c2ecf20Sopenharmony_ci		peerstats = &peer->stats;
708c2ecf20Sopenharmony_ci		peerstats->rx_packets++;
718c2ecf20Sopenharmony_ci		peerstats->rx_bytes += len;
728c2ecf20Sopenharmony_ci	}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ciout_unlock:
758c2ecf20Sopenharmony_ci	rcu_read_unlock();
768c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic int vxcan_open(struct net_device *dev)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	struct vxcan_priv *priv = netdev_priv(dev);
838c2ecf20Sopenharmony_ci	struct net_device *peer = rtnl_dereference(priv->peer);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	if (!peer)
868c2ecf20Sopenharmony_ci		return -ENOTCONN;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	if (peer->flags & IFF_UP) {
898c2ecf20Sopenharmony_ci		netif_carrier_on(dev);
908c2ecf20Sopenharmony_ci		netif_carrier_on(peer);
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci	return 0;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic int vxcan_close(struct net_device *dev)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	struct vxcan_priv *priv = netdev_priv(dev);
988c2ecf20Sopenharmony_ci	struct net_device *peer = rtnl_dereference(priv->peer);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	netif_carrier_off(dev);
1018c2ecf20Sopenharmony_ci	if (peer)
1028c2ecf20Sopenharmony_ci		netif_carrier_off(peer);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	return 0;
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic int vxcan_get_iflink(const struct net_device *dev)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	struct vxcan_priv *priv = netdev_priv(dev);
1108c2ecf20Sopenharmony_ci	struct net_device *peer;
1118c2ecf20Sopenharmony_ci	int iflink;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	rcu_read_lock();
1148c2ecf20Sopenharmony_ci	peer = rcu_dereference(priv->peer);
1158c2ecf20Sopenharmony_ci	iflink = peer ? peer->ifindex : 0;
1168c2ecf20Sopenharmony_ci	rcu_read_unlock();
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	return iflink;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic int vxcan_change_mtu(struct net_device *dev, int new_mtu)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	/* Do not allow changing the MTU while running */
1248c2ecf20Sopenharmony_ci	if (dev->flags & IFF_UP)
1258c2ecf20Sopenharmony_ci		return -EBUSY;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	if (new_mtu != CAN_MTU && new_mtu != CANFD_MTU)
1288c2ecf20Sopenharmony_ci		return -EINVAL;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	dev->mtu = new_mtu;
1318c2ecf20Sopenharmony_ci	return 0;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic const struct net_device_ops vxcan_netdev_ops = {
1358c2ecf20Sopenharmony_ci	.ndo_open	= vxcan_open,
1368c2ecf20Sopenharmony_ci	.ndo_stop	= vxcan_close,
1378c2ecf20Sopenharmony_ci	.ndo_start_xmit	= vxcan_xmit,
1388c2ecf20Sopenharmony_ci	.ndo_get_iflink	= vxcan_get_iflink,
1398c2ecf20Sopenharmony_ci	.ndo_change_mtu = vxcan_change_mtu,
1408c2ecf20Sopenharmony_ci};
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic void vxcan_setup(struct net_device *dev)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	struct can_ml_priv *can_ml;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	dev->type		= ARPHRD_CAN;
1478c2ecf20Sopenharmony_ci	dev->mtu		= CANFD_MTU;
1488c2ecf20Sopenharmony_ci	dev->hard_header_len	= 0;
1498c2ecf20Sopenharmony_ci	dev->addr_len		= 0;
1508c2ecf20Sopenharmony_ci	dev->tx_queue_len	= 0;
1518c2ecf20Sopenharmony_ci	dev->flags		= IFF_NOARP;
1528c2ecf20Sopenharmony_ci	dev->netdev_ops		= &vxcan_netdev_ops;
1538c2ecf20Sopenharmony_ci	dev->needs_free_netdev	= true;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	can_ml = netdev_priv(dev) + ALIGN(sizeof(struct vxcan_priv), NETDEV_ALIGN);
1568c2ecf20Sopenharmony_ci	can_set_ml_priv(dev, can_ml);
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci/* forward declaration for rtnl_create_link() */
1608c2ecf20Sopenharmony_cistatic struct rtnl_link_ops vxcan_link_ops;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic int vxcan_newlink(struct net *net, struct net_device *dev,
1638c2ecf20Sopenharmony_ci			 struct nlattr *tb[], struct nlattr *data[],
1648c2ecf20Sopenharmony_ci			 struct netlink_ext_ack *extack)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct vxcan_priv *priv;
1678c2ecf20Sopenharmony_ci	struct net_device *peer;
1688c2ecf20Sopenharmony_ci	struct net *peer_net;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	struct nlattr *peer_tb[IFLA_MAX + 1], **tbp = tb;
1718c2ecf20Sopenharmony_ci	char ifname[IFNAMSIZ];
1728c2ecf20Sopenharmony_ci	unsigned char name_assign_type;
1738c2ecf20Sopenharmony_ci	struct ifinfomsg *ifmp = NULL;
1748c2ecf20Sopenharmony_ci	int err;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	/* register peer device */
1778c2ecf20Sopenharmony_ci	if (data && data[VXCAN_INFO_PEER]) {
1788c2ecf20Sopenharmony_ci		struct nlattr *nla_peer;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci		nla_peer = data[VXCAN_INFO_PEER];
1818c2ecf20Sopenharmony_ci		ifmp = nla_data(nla_peer);
1828c2ecf20Sopenharmony_ci		err = rtnl_nla_parse_ifinfomsg(peer_tb, nla_peer, extack);
1838c2ecf20Sopenharmony_ci		if (err < 0)
1848c2ecf20Sopenharmony_ci			return err;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci		tbp = peer_tb;
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if (ifmp && tbp[IFLA_IFNAME]) {
1908c2ecf20Sopenharmony_ci		nla_strlcpy(ifname, tbp[IFLA_IFNAME], IFNAMSIZ);
1918c2ecf20Sopenharmony_ci		name_assign_type = NET_NAME_USER;
1928c2ecf20Sopenharmony_ci	} else {
1938c2ecf20Sopenharmony_ci		snprintf(ifname, IFNAMSIZ, DRV_NAME "%%d");
1948c2ecf20Sopenharmony_ci		name_assign_type = NET_NAME_ENUM;
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	peer_net = rtnl_link_get_net(net, tbp);
1988c2ecf20Sopenharmony_ci	if (IS_ERR(peer_net))
1998c2ecf20Sopenharmony_ci		return PTR_ERR(peer_net);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	peer = rtnl_create_link(peer_net, ifname, name_assign_type,
2028c2ecf20Sopenharmony_ci				&vxcan_link_ops, tbp, extack);
2038c2ecf20Sopenharmony_ci	if (IS_ERR(peer)) {
2048c2ecf20Sopenharmony_ci		put_net(peer_net);
2058c2ecf20Sopenharmony_ci		return PTR_ERR(peer);
2068c2ecf20Sopenharmony_ci	}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	if (ifmp && dev->ifindex)
2098c2ecf20Sopenharmony_ci		peer->ifindex = ifmp->ifi_index;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	err = register_netdevice(peer);
2128c2ecf20Sopenharmony_ci	put_net(peer_net);
2138c2ecf20Sopenharmony_ci	peer_net = NULL;
2148c2ecf20Sopenharmony_ci	if (err < 0) {
2158c2ecf20Sopenharmony_ci		free_netdev(peer);
2168c2ecf20Sopenharmony_ci		return err;
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	netif_carrier_off(peer);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	err = rtnl_configure_link(peer, ifmp);
2228c2ecf20Sopenharmony_ci	if (err < 0)
2238c2ecf20Sopenharmony_ci		goto unregister_network_device;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	/* register first device */
2268c2ecf20Sopenharmony_ci	if (tb[IFLA_IFNAME])
2278c2ecf20Sopenharmony_ci		nla_strlcpy(dev->name, tb[IFLA_IFNAME], IFNAMSIZ);
2288c2ecf20Sopenharmony_ci	else
2298c2ecf20Sopenharmony_ci		snprintf(dev->name, IFNAMSIZ, DRV_NAME "%%d");
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	err = register_netdevice(dev);
2328c2ecf20Sopenharmony_ci	if (err < 0)
2338c2ecf20Sopenharmony_ci		goto unregister_network_device;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	netif_carrier_off(dev);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	/* cross link the device pair */
2388c2ecf20Sopenharmony_ci	priv = netdev_priv(dev);
2398c2ecf20Sopenharmony_ci	rcu_assign_pointer(priv->peer, peer);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	priv = netdev_priv(peer);
2428c2ecf20Sopenharmony_ci	rcu_assign_pointer(priv->peer, dev);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	return 0;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ciunregister_network_device:
2478c2ecf20Sopenharmony_ci	unregister_netdevice(peer);
2488c2ecf20Sopenharmony_ci	return err;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic void vxcan_dellink(struct net_device *dev, struct list_head *head)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	struct vxcan_priv *priv;
2548c2ecf20Sopenharmony_ci	struct net_device *peer;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	priv = netdev_priv(dev);
2578c2ecf20Sopenharmony_ci	peer = rtnl_dereference(priv->peer);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	/* Note : dellink() is called from default_device_exit_batch(),
2608c2ecf20Sopenharmony_ci	 * before a rcu_synchronize() point. The devices are guaranteed
2618c2ecf20Sopenharmony_ci	 * not being freed before one RCU grace period.
2628c2ecf20Sopenharmony_ci	 */
2638c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(priv->peer, NULL);
2648c2ecf20Sopenharmony_ci	unregister_netdevice_queue(dev, head);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (peer) {
2678c2ecf20Sopenharmony_ci		priv = netdev_priv(peer);
2688c2ecf20Sopenharmony_ci		RCU_INIT_POINTER(priv->peer, NULL);
2698c2ecf20Sopenharmony_ci		unregister_netdevice_queue(peer, head);
2708c2ecf20Sopenharmony_ci	}
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic const struct nla_policy vxcan_policy[VXCAN_INFO_MAX + 1] = {
2748c2ecf20Sopenharmony_ci	[VXCAN_INFO_PEER] = { .len = sizeof(struct ifinfomsg) },
2758c2ecf20Sopenharmony_ci};
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic struct net *vxcan_get_link_net(const struct net_device *dev)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	struct vxcan_priv *priv = netdev_priv(dev);
2808c2ecf20Sopenharmony_ci	struct net_device *peer = rtnl_dereference(priv->peer);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	return peer ? dev_net(peer) : dev_net(dev);
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic struct rtnl_link_ops vxcan_link_ops = {
2868c2ecf20Sopenharmony_ci	.kind		= DRV_NAME,
2878c2ecf20Sopenharmony_ci	.priv_size	= ALIGN(sizeof(struct vxcan_priv), NETDEV_ALIGN) + sizeof(struct can_ml_priv),
2888c2ecf20Sopenharmony_ci	.setup		= vxcan_setup,
2898c2ecf20Sopenharmony_ci	.newlink	= vxcan_newlink,
2908c2ecf20Sopenharmony_ci	.dellink	= vxcan_dellink,
2918c2ecf20Sopenharmony_ci	.policy		= vxcan_policy,
2928c2ecf20Sopenharmony_ci	.maxtype	= VXCAN_INFO_MAX,
2938c2ecf20Sopenharmony_ci	.get_link_net	= vxcan_get_link_net,
2948c2ecf20Sopenharmony_ci};
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistatic __init int vxcan_init(void)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	pr_info("vxcan: Virtual CAN Tunnel driver\n");
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	return rtnl_link_register(&vxcan_link_ops);
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_cistatic __exit void vxcan_exit(void)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	rtnl_link_unregister(&vxcan_link_ops);
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cimodule_init(vxcan_init);
3098c2ecf20Sopenharmony_cimodule_exit(vxcan_exit);
310