xref: /openbmc/linux/drivers/net/veth.c (revision 1c213bd2)
1e314dbdcSPavel Emelyanov /*
2e314dbdcSPavel Emelyanov  *  drivers/net/veth.c
3e314dbdcSPavel Emelyanov  *
4e314dbdcSPavel Emelyanov  *  Copyright (C) 2007 OpenVZ http://openvz.org, SWsoft Inc
5e314dbdcSPavel Emelyanov  *
6e314dbdcSPavel Emelyanov  * Author: Pavel Emelianov <xemul@openvz.org>
7e314dbdcSPavel Emelyanov  * Ethtool interface from: Eric W. Biederman <ebiederm@xmission.com>
8e314dbdcSPavel Emelyanov  *
9e314dbdcSPavel Emelyanov  */
10e314dbdcSPavel Emelyanov 
11e314dbdcSPavel Emelyanov #include <linux/netdevice.h>
125a0e3ad6STejun Heo #include <linux/slab.h>
13e314dbdcSPavel Emelyanov #include <linux/ethtool.h>
14e314dbdcSPavel Emelyanov #include <linux/etherdevice.h>
15cf05c700SEric Dumazet #include <linux/u64_stats_sync.h>
16e314dbdcSPavel Emelyanov 
17e314dbdcSPavel Emelyanov #include <net/dst.h>
18e314dbdcSPavel Emelyanov #include <net/xfrm.h>
19ecef969eSStephen Hemminger #include <linux/veth.h>
209d9779e7SPaul Gortmaker #include <linux/module.h>
21e314dbdcSPavel Emelyanov 
22e314dbdcSPavel Emelyanov #define DRV_NAME	"veth"
23e314dbdcSPavel Emelyanov #define DRV_VERSION	"1.0"
24e314dbdcSPavel Emelyanov 
2538d40815SEric Biederman #define MIN_MTU 68		/* Min L3 MTU */
2638d40815SEric Biederman #define MAX_MTU 65535		/* Max L3 MTU (arbitrary) */
2738d40815SEric Biederman 
282681128fSEric Dumazet struct pcpu_vstats {
292681128fSEric Dumazet 	u64			packets;
302681128fSEric Dumazet 	u64			bytes;
31cf05c700SEric Dumazet 	struct u64_stats_sync	syncp;
32e314dbdcSPavel Emelyanov };
33e314dbdcSPavel Emelyanov 
34e314dbdcSPavel Emelyanov struct veth_priv {
35d0e2c55eSEric Dumazet 	struct net_device __rcu	*peer;
362681128fSEric Dumazet 	atomic64_t		dropped;
37e314dbdcSPavel Emelyanov };
38e314dbdcSPavel Emelyanov 
39e314dbdcSPavel Emelyanov /*
40e314dbdcSPavel Emelyanov  * ethtool interface
41e314dbdcSPavel Emelyanov  */
42e314dbdcSPavel Emelyanov 
43e314dbdcSPavel Emelyanov static struct {
44e314dbdcSPavel Emelyanov 	const char string[ETH_GSTRING_LEN];
45e314dbdcSPavel Emelyanov } ethtool_stats_keys[] = {
46e314dbdcSPavel Emelyanov 	{ "peer_ifindex" },
47e314dbdcSPavel Emelyanov };
48e314dbdcSPavel Emelyanov 
49e314dbdcSPavel Emelyanov static int veth_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
50e314dbdcSPavel Emelyanov {
51e314dbdcSPavel Emelyanov 	cmd->supported		= 0;
52e314dbdcSPavel Emelyanov 	cmd->advertising	= 0;
5370739497SDavid Decotigny 	ethtool_cmd_speed_set(cmd, SPEED_10000);
54e314dbdcSPavel Emelyanov 	cmd->duplex		= DUPLEX_FULL;
55e314dbdcSPavel Emelyanov 	cmd->port		= PORT_TP;
56e314dbdcSPavel Emelyanov 	cmd->phy_address	= 0;
57e314dbdcSPavel Emelyanov 	cmd->transceiver	= XCVR_INTERNAL;
58e314dbdcSPavel Emelyanov 	cmd->autoneg		= AUTONEG_DISABLE;
59e314dbdcSPavel Emelyanov 	cmd->maxtxpkt		= 0;
60e314dbdcSPavel Emelyanov 	cmd->maxrxpkt		= 0;
61e314dbdcSPavel Emelyanov 	return 0;
62e314dbdcSPavel Emelyanov }
63e314dbdcSPavel Emelyanov 
64e314dbdcSPavel Emelyanov static void veth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
65e314dbdcSPavel Emelyanov {
6633a5ba14SRick Jones 	strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
6733a5ba14SRick Jones 	strlcpy(info->version, DRV_VERSION, sizeof(info->version));
68e314dbdcSPavel Emelyanov }
69e314dbdcSPavel Emelyanov 
70e314dbdcSPavel Emelyanov static void veth_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
71e314dbdcSPavel Emelyanov {
72e314dbdcSPavel Emelyanov 	switch(stringset) {
73e314dbdcSPavel Emelyanov 	case ETH_SS_STATS:
74e314dbdcSPavel Emelyanov 		memcpy(buf, &ethtool_stats_keys, sizeof(ethtool_stats_keys));
75e314dbdcSPavel Emelyanov 		break;
76e314dbdcSPavel Emelyanov 	}
77e314dbdcSPavel Emelyanov }
78e314dbdcSPavel Emelyanov 
79b9f2c044SJeff Garzik static int veth_get_sset_count(struct net_device *dev, int sset)
80e314dbdcSPavel Emelyanov {
81b9f2c044SJeff Garzik 	switch (sset) {
82b9f2c044SJeff Garzik 	case ETH_SS_STATS:
83e314dbdcSPavel Emelyanov 		return ARRAY_SIZE(ethtool_stats_keys);
84b9f2c044SJeff Garzik 	default:
85b9f2c044SJeff Garzik 		return -EOPNOTSUPP;
86b9f2c044SJeff Garzik 	}
87e314dbdcSPavel Emelyanov }
88e314dbdcSPavel Emelyanov 
89e314dbdcSPavel Emelyanov static void veth_get_ethtool_stats(struct net_device *dev,
90e314dbdcSPavel Emelyanov 		struct ethtool_stats *stats, u64 *data)
91e314dbdcSPavel Emelyanov {
92d0e2c55eSEric Dumazet 	struct veth_priv *priv = netdev_priv(dev);
93d0e2c55eSEric Dumazet 	struct net_device *peer = rtnl_dereference(priv->peer);
94e314dbdcSPavel Emelyanov 
95d0e2c55eSEric Dumazet 	data[0] = peer ? peer->ifindex : 0;
96e314dbdcSPavel Emelyanov }
97e314dbdcSPavel Emelyanov 
980fc0b732SStephen Hemminger static const struct ethtool_ops veth_ethtool_ops = {
99e314dbdcSPavel Emelyanov 	.get_settings		= veth_get_settings,
100e314dbdcSPavel Emelyanov 	.get_drvinfo		= veth_get_drvinfo,
101e314dbdcSPavel Emelyanov 	.get_link		= ethtool_op_get_link,
102e314dbdcSPavel Emelyanov 	.get_strings		= veth_get_strings,
103b9f2c044SJeff Garzik 	.get_sset_count		= veth_get_sset_count,
104e314dbdcSPavel Emelyanov 	.get_ethtool_stats	= veth_get_ethtool_stats,
105e314dbdcSPavel Emelyanov };
106e314dbdcSPavel Emelyanov 
107424efe9cSStephen Hemminger static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
108e314dbdcSPavel Emelyanov {
1092681128fSEric Dumazet 	struct veth_priv *priv = netdev_priv(dev);
110d0e2c55eSEric Dumazet 	struct net_device *rcv;
1112681128fSEric Dumazet 	int length = skb->len;
112e314dbdcSPavel Emelyanov 
113d0e2c55eSEric Dumazet 	rcu_read_lock();
114d0e2c55eSEric Dumazet 	rcv = rcu_dereference(priv->peer);
115d0e2c55eSEric Dumazet 	if (unlikely(!rcv)) {
116d0e2c55eSEric Dumazet 		kfree_skb(skb);
117d0e2c55eSEric Dumazet 		goto drop;
118d0e2c55eSEric Dumazet 	}
1190b796750SMichał Mirosław 	/* don't change ip_summed == CHECKSUM_PARTIAL, as that
1202681128fSEric Dumazet 	 * will cause bad checksum on forwarded packets
1212681128fSEric Dumazet 	 */
122a2c725faSMichał Mirosław 	if (skb->ip_summed == CHECKSUM_NONE &&
123a2c725faSMichał Mirosław 	    rcv->features & NETIF_F_RXCSUM)
124a2c725faSMichał Mirosław 		skb->ip_summed = CHECKSUM_UNNECESSARY;
125e314dbdcSPavel Emelyanov 
1262681128fSEric Dumazet 	if (likely(dev_forward_skb(rcv, skb) == NET_RX_SUCCESS)) {
1272681128fSEric Dumazet 		struct pcpu_vstats *stats = this_cpu_ptr(dev->vstats);
128e314dbdcSPavel Emelyanov 
129cf05c700SEric Dumazet 		u64_stats_update_begin(&stats->syncp);
1302681128fSEric Dumazet 		stats->bytes += length;
1312681128fSEric Dumazet 		stats->packets++;
132cf05c700SEric Dumazet 		u64_stats_update_end(&stats->syncp);
1332681128fSEric Dumazet 	} else {
134d0e2c55eSEric Dumazet drop:
1352681128fSEric Dumazet 		atomic64_inc(&priv->dropped);
1362681128fSEric Dumazet 	}
137d0e2c55eSEric Dumazet 	rcu_read_unlock();
1386ed10654SPatrick McHardy 	return NETDEV_TX_OK;
139e314dbdcSPavel Emelyanov }
140e314dbdcSPavel Emelyanov 
141e314dbdcSPavel Emelyanov /*
142e314dbdcSPavel Emelyanov  * general routines
143e314dbdcSPavel Emelyanov  */
144e314dbdcSPavel Emelyanov 
1452681128fSEric Dumazet static u64 veth_stats_one(struct pcpu_vstats *result, struct net_device *dev)
146e314dbdcSPavel Emelyanov {
147cf05c700SEric Dumazet 	struct veth_priv *priv = netdev_priv(dev);
14811687a10SDavid S. Miller 	int cpu;
14911687a10SDavid S. Miller 
1502681128fSEric Dumazet 	result->packets = 0;
1512681128fSEric Dumazet 	result->bytes = 0;
1522b1c8b0fSEric Dumazet 	for_each_possible_cpu(cpu) {
1532681128fSEric Dumazet 		struct pcpu_vstats *stats = per_cpu_ptr(dev->vstats, cpu);
1542681128fSEric Dumazet 		u64 packets, bytes;
155cf05c700SEric Dumazet 		unsigned int start;
156e314dbdcSPavel Emelyanov 
157cf05c700SEric Dumazet 		do {
158cf05c700SEric Dumazet 			start = u64_stats_fetch_begin_bh(&stats->syncp);
1592681128fSEric Dumazet 			packets = stats->packets;
1602681128fSEric Dumazet 			bytes = stats->bytes;
161cf05c700SEric Dumazet 		} while (u64_stats_fetch_retry_bh(&stats->syncp, start));
1622681128fSEric Dumazet 		result->packets += packets;
1632681128fSEric Dumazet 		result->bytes += bytes;
164e314dbdcSPavel Emelyanov 	}
1652681128fSEric Dumazet 	return atomic64_read(&priv->dropped);
1662681128fSEric Dumazet }
1672681128fSEric Dumazet 
1682681128fSEric Dumazet static struct rtnl_link_stats64 *veth_get_stats64(struct net_device *dev,
1692681128fSEric Dumazet 						  struct rtnl_link_stats64 *tot)
1702681128fSEric Dumazet {
1712681128fSEric Dumazet 	struct veth_priv *priv = netdev_priv(dev);
172d0e2c55eSEric Dumazet 	struct net_device *peer;
1732681128fSEric Dumazet 	struct pcpu_vstats one;
1742681128fSEric Dumazet 
1752681128fSEric Dumazet 	tot->tx_dropped = veth_stats_one(&one, dev);
1762681128fSEric Dumazet 	tot->tx_bytes = one.bytes;
1772681128fSEric Dumazet 	tot->tx_packets = one.packets;
1782681128fSEric Dumazet 
179d0e2c55eSEric Dumazet 	rcu_read_lock();
180d0e2c55eSEric Dumazet 	peer = rcu_dereference(priv->peer);
181d0e2c55eSEric Dumazet 	if (peer) {
182d0e2c55eSEric Dumazet 		tot->rx_dropped = veth_stats_one(&one, peer);
1832681128fSEric Dumazet 		tot->rx_bytes = one.bytes;
1842681128fSEric Dumazet 		tot->rx_packets = one.packets;
185d0e2c55eSEric Dumazet 	}
186d0e2c55eSEric Dumazet 	rcu_read_unlock();
187e314dbdcSPavel Emelyanov 
1886311cc44Sstephen hemminger 	return tot;
189e314dbdcSPavel Emelyanov }
190e314dbdcSPavel Emelyanov 
1915c70ef85SGao feng /* fake multicast ability */
1925c70ef85SGao feng static void veth_set_multicast_list(struct net_device *dev)
1935c70ef85SGao feng {
1945c70ef85SGao feng }
1955c70ef85SGao feng 
196e314dbdcSPavel Emelyanov static int veth_open(struct net_device *dev)
197e314dbdcSPavel Emelyanov {
198d0e2c55eSEric Dumazet 	struct veth_priv *priv = netdev_priv(dev);
199d0e2c55eSEric Dumazet 	struct net_device *peer = rtnl_dereference(priv->peer);
200e314dbdcSPavel Emelyanov 
201d0e2c55eSEric Dumazet 	if (!peer)
202e314dbdcSPavel Emelyanov 		return -ENOTCONN;
203e314dbdcSPavel Emelyanov 
204d0e2c55eSEric Dumazet 	if (peer->flags & IFF_UP) {
205e314dbdcSPavel Emelyanov 		netif_carrier_on(dev);
206d0e2c55eSEric Dumazet 		netif_carrier_on(peer);
207e314dbdcSPavel Emelyanov 	}
208e314dbdcSPavel Emelyanov 	return 0;
209e314dbdcSPavel Emelyanov }
210e314dbdcSPavel Emelyanov 
2112cf48a10SEric W. Biederman static int veth_close(struct net_device *dev)
2122cf48a10SEric W. Biederman {
2132cf48a10SEric W. Biederman 	struct veth_priv *priv = netdev_priv(dev);
2142efd32eeSEric Dumazet 	struct net_device *peer = rtnl_dereference(priv->peer);
2152cf48a10SEric W. Biederman 
2162cf48a10SEric W. Biederman 	netif_carrier_off(dev);
2172efd32eeSEric Dumazet 	if (peer)
2182efd32eeSEric Dumazet 		netif_carrier_off(peer);
2192cf48a10SEric W. Biederman 
2202cf48a10SEric W. Biederman 	return 0;
2212cf48a10SEric W. Biederman }
2222cf48a10SEric W. Biederman 
22338d40815SEric Biederman static int is_valid_veth_mtu(int new_mtu)
22438d40815SEric Biederman {
225807540baSEric Dumazet 	return new_mtu >= MIN_MTU && new_mtu <= MAX_MTU;
22638d40815SEric Biederman }
22738d40815SEric Biederman 
22838d40815SEric Biederman static int veth_change_mtu(struct net_device *dev, int new_mtu)
22938d40815SEric Biederman {
23038d40815SEric Biederman 	if (!is_valid_veth_mtu(new_mtu))
23138d40815SEric Biederman 		return -EINVAL;
23238d40815SEric Biederman 	dev->mtu = new_mtu;
23338d40815SEric Biederman 	return 0;
23438d40815SEric Biederman }
23538d40815SEric Biederman 
236e314dbdcSPavel Emelyanov static int veth_dev_init(struct net_device *dev)
237e314dbdcSPavel Emelyanov {
2381c213bd2SWANG Cong 	dev->vstats = netdev_alloc_pcpu_stats(struct pcpu_vstats);
2392681128fSEric Dumazet 	if (!dev->vstats)
240e314dbdcSPavel Emelyanov 		return -ENOMEM;
241e314dbdcSPavel Emelyanov 	return 0;
242e314dbdcSPavel Emelyanov }
243e314dbdcSPavel Emelyanov 
24411687a10SDavid S. Miller static void veth_dev_free(struct net_device *dev)
24511687a10SDavid S. Miller {
2462681128fSEric Dumazet 	free_percpu(dev->vstats);
24711687a10SDavid S. Miller 	free_netdev(dev);
24811687a10SDavid S. Miller }
24911687a10SDavid S. Miller 
2504456e7bdSStephen Hemminger static const struct net_device_ops veth_netdev_ops = {
2514456e7bdSStephen Hemminger 	.ndo_init            = veth_dev_init,
2524456e7bdSStephen Hemminger 	.ndo_open            = veth_open,
2532cf48a10SEric W. Biederman 	.ndo_stop            = veth_close,
25400829823SStephen Hemminger 	.ndo_start_xmit      = veth_xmit,
25538d40815SEric Biederman 	.ndo_change_mtu      = veth_change_mtu,
2566311cc44Sstephen hemminger 	.ndo_get_stats64     = veth_get_stats64,
2575c70ef85SGao feng 	.ndo_set_rx_mode     = veth_set_multicast_list,
258ee923623SDaniel Lezcano 	.ndo_set_mac_address = eth_mac_addr,
2594456e7bdSStephen Hemminger };
2604456e7bdSStephen Hemminger 
2618093315aSEric Dumazet #define VETH_FEATURES (NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_ALL_TSO |    \
2628093315aSEric Dumazet 		       NETIF_F_HW_CSUM | NETIF_F_RXCSUM | NETIF_F_HIGHDMA | \
26382d81898SEric Dumazet 		       NETIF_F_GSO_GRE | NETIF_F_GSO_UDP_TUNNEL |	    \
26482d81898SEric Dumazet 		       NETIF_F_GSO_IPIP | NETIF_F_GSO_SIT | NETIF_F_UFO	|   \
26528d2b136SPatrick McHardy 		       NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | \
26628d2b136SPatrick McHardy 		       NETIF_F_HW_VLAN_STAG_TX | NETIF_F_HW_VLAN_STAG_RX )
2678093315aSEric Dumazet 
268e314dbdcSPavel Emelyanov static void veth_setup(struct net_device *dev)
269e314dbdcSPavel Emelyanov {
270e314dbdcSPavel Emelyanov 	ether_setup(dev);
271e314dbdcSPavel Emelyanov 
272550fd08cSNeil Horman 	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
27323ea5a96SHannes Frederic Sowa 	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
274550fd08cSNeil Horman 
2754456e7bdSStephen Hemminger 	dev->netdev_ops = &veth_netdev_ops;
276e314dbdcSPavel Emelyanov 	dev->ethtool_ops = &veth_ethtool_ops;
277e314dbdcSPavel Emelyanov 	dev->features |= NETIF_F_LLTX;
2788093315aSEric Dumazet 	dev->features |= VETH_FEATURES;
279b69bbddfSFlavio Leitner 	dev->vlan_features = dev->features;
28011687a10SDavid S. Miller 	dev->destructor = veth_dev_free;
281a2c725faSMichał Mirosław 
2828093315aSEric Dumazet 	dev->hw_features = VETH_FEATURES;
28382d81898SEric Dumazet 	dev->hw_enc_features = VETH_FEATURES;
284e314dbdcSPavel Emelyanov }
285e314dbdcSPavel Emelyanov 
286e314dbdcSPavel Emelyanov /*
287e314dbdcSPavel Emelyanov  * netlink interface
288e314dbdcSPavel Emelyanov  */
289e314dbdcSPavel Emelyanov 
290e314dbdcSPavel Emelyanov static int veth_validate(struct nlattr *tb[], struct nlattr *data[])
291e314dbdcSPavel Emelyanov {
292e314dbdcSPavel Emelyanov 	if (tb[IFLA_ADDRESS]) {
293e314dbdcSPavel Emelyanov 		if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
294e314dbdcSPavel Emelyanov 			return -EINVAL;
295e314dbdcSPavel Emelyanov 		if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
296e314dbdcSPavel Emelyanov 			return -EADDRNOTAVAIL;
297e314dbdcSPavel Emelyanov 	}
29838d40815SEric Biederman 	if (tb[IFLA_MTU]) {
29938d40815SEric Biederman 		if (!is_valid_veth_mtu(nla_get_u32(tb[IFLA_MTU])))
30038d40815SEric Biederman 			return -EINVAL;
30138d40815SEric Biederman 	}
302e314dbdcSPavel Emelyanov 	return 0;
303e314dbdcSPavel Emelyanov }
304e314dbdcSPavel Emelyanov 
305e314dbdcSPavel Emelyanov static struct rtnl_link_ops veth_link_ops;
306e314dbdcSPavel Emelyanov 
30781adee47SEric W. Biederman static int veth_newlink(struct net *src_net, struct net_device *dev,
308e314dbdcSPavel Emelyanov 			 struct nlattr *tb[], struct nlattr *data[])
309e314dbdcSPavel Emelyanov {
310e314dbdcSPavel Emelyanov 	int err;
311e314dbdcSPavel Emelyanov 	struct net_device *peer;
312e314dbdcSPavel Emelyanov 	struct veth_priv *priv;
313e314dbdcSPavel Emelyanov 	char ifname[IFNAMSIZ];
314e314dbdcSPavel Emelyanov 	struct nlattr *peer_tb[IFLA_MAX + 1], **tbp;
3153729d502SPatrick McHardy 	struct ifinfomsg *ifmp;
31681adee47SEric W. Biederman 	struct net *net;
317e314dbdcSPavel Emelyanov 
318e314dbdcSPavel Emelyanov 	/*
319e314dbdcSPavel Emelyanov 	 * create and register peer first
320e314dbdcSPavel Emelyanov 	 */
321e314dbdcSPavel Emelyanov 	if (data != NULL && data[VETH_INFO_PEER] != NULL) {
322e314dbdcSPavel Emelyanov 		struct nlattr *nla_peer;
323e314dbdcSPavel Emelyanov 
324e314dbdcSPavel Emelyanov 		nla_peer = data[VETH_INFO_PEER];
3253729d502SPatrick McHardy 		ifmp = nla_data(nla_peer);
326e314dbdcSPavel Emelyanov 		err = nla_parse(peer_tb, IFLA_MAX,
327e314dbdcSPavel Emelyanov 				nla_data(nla_peer) + sizeof(struct ifinfomsg),
328e314dbdcSPavel Emelyanov 				nla_len(nla_peer) - sizeof(struct ifinfomsg),
329e314dbdcSPavel Emelyanov 				ifla_policy);
330e314dbdcSPavel Emelyanov 		if (err < 0)
331e314dbdcSPavel Emelyanov 			return err;
332e314dbdcSPavel Emelyanov 
333e314dbdcSPavel Emelyanov 		err = veth_validate(peer_tb, NULL);
334e314dbdcSPavel Emelyanov 		if (err < 0)
335e314dbdcSPavel Emelyanov 			return err;
336e314dbdcSPavel Emelyanov 
337e314dbdcSPavel Emelyanov 		tbp = peer_tb;
3383729d502SPatrick McHardy 	} else {
3393729d502SPatrick McHardy 		ifmp = NULL;
340e314dbdcSPavel Emelyanov 		tbp = tb;
3413729d502SPatrick McHardy 	}
342e314dbdcSPavel Emelyanov 
343e314dbdcSPavel Emelyanov 	if (tbp[IFLA_IFNAME])
344e314dbdcSPavel Emelyanov 		nla_strlcpy(ifname, tbp[IFLA_IFNAME], IFNAMSIZ);
345e314dbdcSPavel Emelyanov 	else
346e314dbdcSPavel Emelyanov 		snprintf(ifname, IFNAMSIZ, DRV_NAME "%%d");
347e314dbdcSPavel Emelyanov 
34881adee47SEric W. Biederman 	net = rtnl_link_get_net(src_net, tbp);
34981adee47SEric W. Biederman 	if (IS_ERR(net))
35081adee47SEric W. Biederman 		return PTR_ERR(net);
35181adee47SEric W. Biederman 
352c0713563SRami Rosen 	peer = rtnl_create_link(net, ifname, &veth_link_ops, tbp);
35381adee47SEric W. Biederman 	if (IS_ERR(peer)) {
35481adee47SEric W. Biederman 		put_net(net);
355e314dbdcSPavel Emelyanov 		return PTR_ERR(peer);
35681adee47SEric W. Biederman 	}
357e314dbdcSPavel Emelyanov 
358e314dbdcSPavel Emelyanov 	if (tbp[IFLA_ADDRESS] == NULL)
359f2cedb63SDanny Kukawka 		eth_hw_addr_random(peer);
360e314dbdcSPavel Emelyanov 
361e6f8f1a7SPavel Emelyanov 	if (ifmp && (dev->ifindex != 0))
362e6f8f1a7SPavel Emelyanov 		peer->ifindex = ifmp->ifi_index;
363e6f8f1a7SPavel Emelyanov 
364e314dbdcSPavel Emelyanov 	err = register_netdevice(peer);
36581adee47SEric W. Biederman 	put_net(net);
36681adee47SEric W. Biederman 	net = NULL;
367e314dbdcSPavel Emelyanov 	if (err < 0)
368e314dbdcSPavel Emelyanov 		goto err_register_peer;
369e314dbdcSPavel Emelyanov 
370e314dbdcSPavel Emelyanov 	netif_carrier_off(peer);
371e314dbdcSPavel Emelyanov 
3723729d502SPatrick McHardy 	err = rtnl_configure_link(peer, ifmp);
3733729d502SPatrick McHardy 	if (err < 0)
3743729d502SPatrick McHardy 		goto err_configure_peer;
3753729d502SPatrick McHardy 
376e314dbdcSPavel Emelyanov 	/*
377e314dbdcSPavel Emelyanov 	 * register dev last
378e314dbdcSPavel Emelyanov 	 *
379e314dbdcSPavel Emelyanov 	 * note, that since we've registered new device the dev's name
380e314dbdcSPavel Emelyanov 	 * should be re-allocated
381e314dbdcSPavel Emelyanov 	 */
382e314dbdcSPavel Emelyanov 
383e314dbdcSPavel Emelyanov 	if (tb[IFLA_ADDRESS] == NULL)
384f2cedb63SDanny Kukawka 		eth_hw_addr_random(dev);
385e314dbdcSPavel Emelyanov 
3866c8c4446SJiri Pirko 	if (tb[IFLA_IFNAME])
3876c8c4446SJiri Pirko 		nla_strlcpy(dev->name, tb[IFLA_IFNAME], IFNAMSIZ);
3886c8c4446SJiri Pirko 	else
3896c8c4446SJiri Pirko 		snprintf(dev->name, IFNAMSIZ, DRV_NAME "%%d");
3906c8c4446SJiri Pirko 
391e314dbdcSPavel Emelyanov 	err = register_netdevice(dev);
392e314dbdcSPavel Emelyanov 	if (err < 0)
393e314dbdcSPavel Emelyanov 		goto err_register_dev;
394e314dbdcSPavel Emelyanov 
395e314dbdcSPavel Emelyanov 	netif_carrier_off(dev);
396e314dbdcSPavel Emelyanov 
397e314dbdcSPavel Emelyanov 	/*
398e314dbdcSPavel Emelyanov 	 * tie the deviced together
399e314dbdcSPavel Emelyanov 	 */
400e314dbdcSPavel Emelyanov 
401e314dbdcSPavel Emelyanov 	priv = netdev_priv(dev);
402d0e2c55eSEric Dumazet 	rcu_assign_pointer(priv->peer, peer);
403e314dbdcSPavel Emelyanov 
404e314dbdcSPavel Emelyanov 	priv = netdev_priv(peer);
405d0e2c55eSEric Dumazet 	rcu_assign_pointer(priv->peer, dev);
406e314dbdcSPavel Emelyanov 	return 0;
407e314dbdcSPavel Emelyanov 
408e314dbdcSPavel Emelyanov err_register_dev:
409e314dbdcSPavel Emelyanov 	/* nothing to do */
4103729d502SPatrick McHardy err_configure_peer:
411e314dbdcSPavel Emelyanov 	unregister_netdevice(peer);
412e314dbdcSPavel Emelyanov 	return err;
413e314dbdcSPavel Emelyanov 
414e314dbdcSPavel Emelyanov err_register_peer:
415e314dbdcSPavel Emelyanov 	free_netdev(peer);
416e314dbdcSPavel Emelyanov 	return err;
417e314dbdcSPavel Emelyanov }
418e314dbdcSPavel Emelyanov 
41923289a37SEric Dumazet static void veth_dellink(struct net_device *dev, struct list_head *head)
420e314dbdcSPavel Emelyanov {
421e314dbdcSPavel Emelyanov 	struct veth_priv *priv;
422e314dbdcSPavel Emelyanov 	struct net_device *peer;
423e314dbdcSPavel Emelyanov 
424e314dbdcSPavel Emelyanov 	priv = netdev_priv(dev);
425d0e2c55eSEric Dumazet 	peer = rtnl_dereference(priv->peer);
426d0e2c55eSEric Dumazet 
427d0e2c55eSEric Dumazet 	/* Note : dellink() is called from default_device_exit_batch(),
428d0e2c55eSEric Dumazet 	 * before a rcu_synchronize() point. The devices are guaranteed
429d0e2c55eSEric Dumazet 	 * not being freed before one RCU grace period.
430d0e2c55eSEric Dumazet 	 */
431d0e2c55eSEric Dumazet 	RCU_INIT_POINTER(priv->peer, NULL);
432f45a5c26SEric Dumazet 	unregister_netdevice_queue(dev, head);
433d0e2c55eSEric Dumazet 
434f45a5c26SEric Dumazet 	if (peer) {
435d0e2c55eSEric Dumazet 		priv = netdev_priv(peer);
436d0e2c55eSEric Dumazet 		RCU_INIT_POINTER(priv->peer, NULL);
43724540535SEric Dumazet 		unregister_netdevice_queue(peer, head);
438e314dbdcSPavel Emelyanov 	}
439f45a5c26SEric Dumazet }
440e314dbdcSPavel Emelyanov 
44123711438SThomas Graf static const struct nla_policy veth_policy[VETH_INFO_MAX + 1] = {
44223711438SThomas Graf 	[VETH_INFO_PEER]	= { .len = sizeof(struct ifinfomsg) },
44323711438SThomas Graf };
444e314dbdcSPavel Emelyanov 
445e314dbdcSPavel Emelyanov static struct rtnl_link_ops veth_link_ops = {
446e314dbdcSPavel Emelyanov 	.kind		= DRV_NAME,
447e314dbdcSPavel Emelyanov 	.priv_size	= sizeof(struct veth_priv),
448e314dbdcSPavel Emelyanov 	.setup		= veth_setup,
449e314dbdcSPavel Emelyanov 	.validate	= veth_validate,
450e314dbdcSPavel Emelyanov 	.newlink	= veth_newlink,
451e314dbdcSPavel Emelyanov 	.dellink	= veth_dellink,
452e314dbdcSPavel Emelyanov 	.policy		= veth_policy,
453e314dbdcSPavel Emelyanov 	.maxtype	= VETH_INFO_MAX,
454e314dbdcSPavel Emelyanov };
455e314dbdcSPavel Emelyanov 
456e314dbdcSPavel Emelyanov /*
457e314dbdcSPavel Emelyanov  * init/fini
458e314dbdcSPavel Emelyanov  */
459e314dbdcSPavel Emelyanov 
460e314dbdcSPavel Emelyanov static __init int veth_init(void)
461e314dbdcSPavel Emelyanov {
462e314dbdcSPavel Emelyanov 	return rtnl_link_register(&veth_link_ops);
463e314dbdcSPavel Emelyanov }
464e314dbdcSPavel Emelyanov 
465e314dbdcSPavel Emelyanov static __exit void veth_exit(void)
466e314dbdcSPavel Emelyanov {
46768365458SPatrick McHardy 	rtnl_link_unregister(&veth_link_ops);
468e314dbdcSPavel Emelyanov }
469e314dbdcSPavel Emelyanov 
470e314dbdcSPavel Emelyanov module_init(veth_init);
471e314dbdcSPavel Emelyanov module_exit(veth_exit);
472e314dbdcSPavel Emelyanov 
473e314dbdcSPavel Emelyanov MODULE_DESCRIPTION("Virtual Ethernet Tunnel");
474e314dbdcSPavel Emelyanov MODULE_LICENSE("GPL v2");
475e314dbdcSPavel Emelyanov MODULE_ALIAS_RTNL_LINK(DRV_NAME);
476