162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * vxcan.c - Virtual CAN Tunnel for cross namespace communication
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * This code is derived from drivers/net/can/vcan.c for the virtual CAN
662306a36Sopenharmony_ci * specific parts and from drivers/net/veth.c to implement the netlink API
762306a36Sopenharmony_ci * for network interface pairs in a common and established way.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Copyright (c) 2017 Oliver Hartkopp <socketcan@hartkopp.net>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/ethtool.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/init.h>
1562306a36Sopenharmony_ci#include <linux/netdevice.h>
1662306a36Sopenharmony_ci#include <linux/if_arp.h>
1762306a36Sopenharmony_ci#include <linux/if_ether.h>
1862306a36Sopenharmony_ci#include <linux/can.h>
1962306a36Sopenharmony_ci#include <linux/can/dev.h>
2062306a36Sopenharmony_ci#include <linux/can/skb.h>
2162306a36Sopenharmony_ci#include <linux/can/vxcan.h>
2262306a36Sopenharmony_ci#include <linux/can/can-ml.h>
2362306a36Sopenharmony_ci#include <linux/slab.h>
2462306a36Sopenharmony_ci#include <net/rtnetlink.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define DRV_NAME "vxcan"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ciMODULE_DESCRIPTION("Virtual CAN Tunnel");
2962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
3062306a36Sopenharmony_ciMODULE_AUTHOR("Oliver Hartkopp <socketcan@hartkopp.net>");
3162306a36Sopenharmony_ciMODULE_ALIAS_RTNL_LINK(DRV_NAME);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistruct vxcan_priv {
3462306a36Sopenharmony_ci	struct net_device __rcu	*peer;
3562306a36Sopenharmony_ci};
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic netdev_tx_t vxcan_xmit(struct sk_buff *oskb, struct net_device *dev)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	struct vxcan_priv *priv = netdev_priv(dev);
4062306a36Sopenharmony_ci	struct net_device *peer;
4162306a36Sopenharmony_ci	struct net_device_stats *peerstats, *srcstats = &dev->stats;
4262306a36Sopenharmony_ci	struct sk_buff *skb;
4362306a36Sopenharmony_ci	unsigned int len;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	if (can_dropped_invalid_skb(dev, oskb))
4662306a36Sopenharmony_ci		return NETDEV_TX_OK;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	rcu_read_lock();
4962306a36Sopenharmony_ci	peer = rcu_dereference(priv->peer);
5062306a36Sopenharmony_ci	if (unlikely(!peer)) {
5162306a36Sopenharmony_ci		kfree_skb(oskb);
5262306a36Sopenharmony_ci		dev->stats.tx_dropped++;
5362306a36Sopenharmony_ci		goto out_unlock;
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	skb_tx_timestamp(oskb);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	skb = skb_clone(oskb, GFP_ATOMIC);
5962306a36Sopenharmony_ci	if (skb) {
6062306a36Sopenharmony_ci		consume_skb(oskb);
6162306a36Sopenharmony_ci	} else {
6262306a36Sopenharmony_ci		kfree_skb(oskb);
6362306a36Sopenharmony_ci		goto out_unlock;
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	/* reset CAN GW hop counter */
6762306a36Sopenharmony_ci	skb->csum_start = 0;
6862306a36Sopenharmony_ci	skb->pkt_type   = PACKET_BROADCAST;
6962306a36Sopenharmony_ci	skb->dev        = peer;
7062306a36Sopenharmony_ci	skb->ip_summed  = CHECKSUM_UNNECESSARY;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	len = can_skb_get_data_len(skb);
7362306a36Sopenharmony_ci	if (netif_rx(skb) == NET_RX_SUCCESS) {
7462306a36Sopenharmony_ci		srcstats->tx_packets++;
7562306a36Sopenharmony_ci		srcstats->tx_bytes += len;
7662306a36Sopenharmony_ci		peerstats = &peer->stats;
7762306a36Sopenharmony_ci		peerstats->rx_packets++;
7862306a36Sopenharmony_ci		peerstats->rx_bytes += len;
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ciout_unlock:
8262306a36Sopenharmony_ci	rcu_read_unlock();
8362306a36Sopenharmony_ci	return NETDEV_TX_OK;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic int vxcan_open(struct net_device *dev)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct vxcan_priv *priv = netdev_priv(dev);
9062306a36Sopenharmony_ci	struct net_device *peer = rtnl_dereference(priv->peer);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (!peer)
9362306a36Sopenharmony_ci		return -ENOTCONN;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (peer->flags & IFF_UP) {
9662306a36Sopenharmony_ci		netif_carrier_on(dev);
9762306a36Sopenharmony_ci		netif_carrier_on(peer);
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci	return 0;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic int vxcan_close(struct net_device *dev)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct vxcan_priv *priv = netdev_priv(dev);
10562306a36Sopenharmony_ci	struct net_device *peer = rtnl_dereference(priv->peer);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	netif_carrier_off(dev);
10862306a36Sopenharmony_ci	if (peer)
10962306a36Sopenharmony_ci		netif_carrier_off(peer);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	return 0;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic int vxcan_get_iflink(const struct net_device *dev)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	struct vxcan_priv *priv = netdev_priv(dev);
11762306a36Sopenharmony_ci	struct net_device *peer;
11862306a36Sopenharmony_ci	int iflink;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	rcu_read_lock();
12162306a36Sopenharmony_ci	peer = rcu_dereference(priv->peer);
12262306a36Sopenharmony_ci	iflink = peer ? peer->ifindex : 0;
12362306a36Sopenharmony_ci	rcu_read_unlock();
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	return iflink;
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic int vxcan_change_mtu(struct net_device *dev, int new_mtu)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	/* Do not allow changing the MTU while running */
13162306a36Sopenharmony_ci	if (dev->flags & IFF_UP)
13262306a36Sopenharmony_ci		return -EBUSY;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	if (new_mtu != CAN_MTU && new_mtu != CANFD_MTU &&
13562306a36Sopenharmony_ci	    !can_is_canxl_dev_mtu(new_mtu))
13662306a36Sopenharmony_ci		return -EINVAL;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	dev->mtu = new_mtu;
13962306a36Sopenharmony_ci	return 0;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic const struct net_device_ops vxcan_netdev_ops = {
14362306a36Sopenharmony_ci	.ndo_open	= vxcan_open,
14462306a36Sopenharmony_ci	.ndo_stop	= vxcan_close,
14562306a36Sopenharmony_ci	.ndo_start_xmit	= vxcan_xmit,
14662306a36Sopenharmony_ci	.ndo_get_iflink	= vxcan_get_iflink,
14762306a36Sopenharmony_ci	.ndo_change_mtu = vxcan_change_mtu,
14862306a36Sopenharmony_ci};
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic const struct ethtool_ops vxcan_ethtool_ops = {
15162306a36Sopenharmony_ci	.get_ts_info = ethtool_op_get_ts_info,
15262306a36Sopenharmony_ci};
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic void vxcan_setup(struct net_device *dev)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	struct can_ml_priv *can_ml;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	dev->type		= ARPHRD_CAN;
15962306a36Sopenharmony_ci	dev->mtu		= CANFD_MTU;
16062306a36Sopenharmony_ci	dev->hard_header_len	= 0;
16162306a36Sopenharmony_ci	dev->addr_len		= 0;
16262306a36Sopenharmony_ci	dev->tx_queue_len	= 0;
16362306a36Sopenharmony_ci	dev->flags		= IFF_NOARP;
16462306a36Sopenharmony_ci	dev->netdev_ops		= &vxcan_netdev_ops;
16562306a36Sopenharmony_ci	dev->ethtool_ops	= &vxcan_ethtool_ops;
16662306a36Sopenharmony_ci	dev->needs_free_netdev	= true;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	can_ml = netdev_priv(dev) + ALIGN(sizeof(struct vxcan_priv), NETDEV_ALIGN);
16962306a36Sopenharmony_ci	can_set_ml_priv(dev, can_ml);
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci/* forward declaration for rtnl_create_link() */
17362306a36Sopenharmony_cistatic struct rtnl_link_ops vxcan_link_ops;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic int vxcan_newlink(struct net *net, struct net_device *dev,
17662306a36Sopenharmony_ci			 struct nlattr *tb[], struct nlattr *data[],
17762306a36Sopenharmony_ci			 struct netlink_ext_ack *extack)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	struct vxcan_priv *priv;
18062306a36Sopenharmony_ci	struct net_device *peer;
18162306a36Sopenharmony_ci	struct net *peer_net;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	struct nlattr *peer_tb[IFLA_MAX + 1], **tbp = tb;
18462306a36Sopenharmony_ci	char ifname[IFNAMSIZ];
18562306a36Sopenharmony_ci	unsigned char name_assign_type;
18662306a36Sopenharmony_ci	struct ifinfomsg *ifmp = NULL;
18762306a36Sopenharmony_ci	int err;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/* register peer device */
19062306a36Sopenharmony_ci	if (data && data[VXCAN_INFO_PEER]) {
19162306a36Sopenharmony_ci		struct nlattr *nla_peer;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci		nla_peer = data[VXCAN_INFO_PEER];
19462306a36Sopenharmony_ci		ifmp = nla_data(nla_peer);
19562306a36Sopenharmony_ci		err = rtnl_nla_parse_ifinfomsg(peer_tb, nla_peer, extack);
19662306a36Sopenharmony_ci		if (err < 0)
19762306a36Sopenharmony_ci			return err;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci		tbp = peer_tb;
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if (ifmp && tbp[IFLA_IFNAME]) {
20362306a36Sopenharmony_ci		nla_strscpy(ifname, tbp[IFLA_IFNAME], IFNAMSIZ);
20462306a36Sopenharmony_ci		name_assign_type = NET_NAME_USER;
20562306a36Sopenharmony_ci	} else {
20662306a36Sopenharmony_ci		snprintf(ifname, IFNAMSIZ, DRV_NAME "%%d");
20762306a36Sopenharmony_ci		name_assign_type = NET_NAME_ENUM;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	peer_net = rtnl_link_get_net(net, tbp);
21162306a36Sopenharmony_ci	if (IS_ERR(peer_net))
21262306a36Sopenharmony_ci		return PTR_ERR(peer_net);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	peer = rtnl_create_link(peer_net, ifname, name_assign_type,
21562306a36Sopenharmony_ci				&vxcan_link_ops, tbp, extack);
21662306a36Sopenharmony_ci	if (IS_ERR(peer)) {
21762306a36Sopenharmony_ci		put_net(peer_net);
21862306a36Sopenharmony_ci		return PTR_ERR(peer);
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	if (ifmp && dev->ifindex)
22262306a36Sopenharmony_ci		peer->ifindex = ifmp->ifi_index;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	err = register_netdevice(peer);
22562306a36Sopenharmony_ci	put_net(peer_net);
22662306a36Sopenharmony_ci	peer_net = NULL;
22762306a36Sopenharmony_ci	if (err < 0) {
22862306a36Sopenharmony_ci		free_netdev(peer);
22962306a36Sopenharmony_ci		return err;
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	netif_carrier_off(peer);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	err = rtnl_configure_link(peer, ifmp, 0, NULL);
23562306a36Sopenharmony_ci	if (err < 0)
23662306a36Sopenharmony_ci		goto unregister_network_device;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/* register first device */
23962306a36Sopenharmony_ci	if (tb[IFLA_IFNAME])
24062306a36Sopenharmony_ci		nla_strscpy(dev->name, tb[IFLA_IFNAME], IFNAMSIZ);
24162306a36Sopenharmony_ci	else
24262306a36Sopenharmony_ci		snprintf(dev->name, IFNAMSIZ, DRV_NAME "%%d");
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	err = register_netdevice(dev);
24562306a36Sopenharmony_ci	if (err < 0)
24662306a36Sopenharmony_ci		goto unregister_network_device;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	netif_carrier_off(dev);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	/* cross link the device pair */
25162306a36Sopenharmony_ci	priv = netdev_priv(dev);
25262306a36Sopenharmony_ci	rcu_assign_pointer(priv->peer, peer);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	priv = netdev_priv(peer);
25562306a36Sopenharmony_ci	rcu_assign_pointer(priv->peer, dev);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	return 0;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ciunregister_network_device:
26062306a36Sopenharmony_ci	unregister_netdevice(peer);
26162306a36Sopenharmony_ci	return err;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic void vxcan_dellink(struct net_device *dev, struct list_head *head)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	struct vxcan_priv *priv;
26762306a36Sopenharmony_ci	struct net_device *peer;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	priv = netdev_priv(dev);
27062306a36Sopenharmony_ci	peer = rtnl_dereference(priv->peer);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	/* Note : dellink() is called from default_device_exit_batch(),
27362306a36Sopenharmony_ci	 * before a rcu_synchronize() point. The devices are guaranteed
27462306a36Sopenharmony_ci	 * not being freed before one RCU grace period.
27562306a36Sopenharmony_ci	 */
27662306a36Sopenharmony_ci	RCU_INIT_POINTER(priv->peer, NULL);
27762306a36Sopenharmony_ci	unregister_netdevice_queue(dev, head);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if (peer) {
28062306a36Sopenharmony_ci		priv = netdev_priv(peer);
28162306a36Sopenharmony_ci		RCU_INIT_POINTER(priv->peer, NULL);
28262306a36Sopenharmony_ci		unregister_netdevice_queue(peer, head);
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic const struct nla_policy vxcan_policy[VXCAN_INFO_MAX + 1] = {
28762306a36Sopenharmony_ci	[VXCAN_INFO_PEER] = { .len = sizeof(struct ifinfomsg) },
28862306a36Sopenharmony_ci};
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic struct net *vxcan_get_link_net(const struct net_device *dev)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	struct vxcan_priv *priv = netdev_priv(dev);
29362306a36Sopenharmony_ci	struct net_device *peer = rtnl_dereference(priv->peer);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	return peer ? dev_net(peer) : dev_net(dev);
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic struct rtnl_link_ops vxcan_link_ops = {
29962306a36Sopenharmony_ci	.kind		= DRV_NAME,
30062306a36Sopenharmony_ci	.priv_size	= ALIGN(sizeof(struct vxcan_priv), NETDEV_ALIGN) + sizeof(struct can_ml_priv),
30162306a36Sopenharmony_ci	.setup		= vxcan_setup,
30262306a36Sopenharmony_ci	.newlink	= vxcan_newlink,
30362306a36Sopenharmony_ci	.dellink	= vxcan_dellink,
30462306a36Sopenharmony_ci	.policy		= vxcan_policy,
30562306a36Sopenharmony_ci	.maxtype	= VXCAN_INFO_MAX,
30662306a36Sopenharmony_ci	.get_link_net	= vxcan_get_link_net,
30762306a36Sopenharmony_ci};
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic __init int vxcan_init(void)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	pr_info("vxcan: Virtual CAN Tunnel driver\n");
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	return rtnl_link_register(&vxcan_link_ops);
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cistatic __exit void vxcan_exit(void)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	rtnl_link_unregister(&vxcan_link_ops);
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cimodule_init(vxcan_init);
32262306a36Sopenharmony_cimodule_exit(vxcan_exit);
323