xref: /openbmc/linux/drivers/net/veth.c (revision 2efd32ee)
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 
191e314dbdcSPavel Emelyanov static int veth_open(struct net_device *dev)
192e314dbdcSPavel Emelyanov {
193d0e2c55eSEric Dumazet 	struct veth_priv *priv = netdev_priv(dev);
194d0e2c55eSEric Dumazet 	struct net_device *peer = rtnl_dereference(priv->peer);
195e314dbdcSPavel Emelyanov 
196d0e2c55eSEric Dumazet 	if (!peer)
197e314dbdcSPavel Emelyanov 		return -ENOTCONN;
198e314dbdcSPavel Emelyanov 
199d0e2c55eSEric Dumazet 	if (peer->flags & IFF_UP) {
200e314dbdcSPavel Emelyanov 		netif_carrier_on(dev);
201d0e2c55eSEric Dumazet 		netif_carrier_on(peer);
202e314dbdcSPavel Emelyanov 	}
203e314dbdcSPavel Emelyanov 	return 0;
204e314dbdcSPavel Emelyanov }
205e314dbdcSPavel Emelyanov 
2062cf48a10SEric W. Biederman static int veth_close(struct net_device *dev)
2072cf48a10SEric W. Biederman {
2082cf48a10SEric W. Biederman 	struct veth_priv *priv = netdev_priv(dev);
2092efd32eeSEric Dumazet 	struct net_device *peer = rtnl_dereference(priv->peer);
2102cf48a10SEric W. Biederman 
2112cf48a10SEric W. Biederman 	netif_carrier_off(dev);
2122efd32eeSEric Dumazet 	if (peer)
2132efd32eeSEric Dumazet 		netif_carrier_off(peer);
2142cf48a10SEric W. Biederman 
2152cf48a10SEric W. Biederman 	return 0;
2162cf48a10SEric W. Biederman }
2172cf48a10SEric W. Biederman 
21838d40815SEric Biederman static int is_valid_veth_mtu(int new_mtu)
21938d40815SEric Biederman {
220807540baSEric Dumazet 	return new_mtu >= MIN_MTU && new_mtu <= MAX_MTU;
22138d40815SEric Biederman }
22238d40815SEric Biederman 
22338d40815SEric Biederman static int veth_change_mtu(struct net_device *dev, int new_mtu)
22438d40815SEric Biederman {
22538d40815SEric Biederman 	if (!is_valid_veth_mtu(new_mtu))
22638d40815SEric Biederman 		return -EINVAL;
22738d40815SEric Biederman 	dev->mtu = new_mtu;
22838d40815SEric Biederman 	return 0;
22938d40815SEric Biederman }
23038d40815SEric Biederman 
231e314dbdcSPavel Emelyanov static int veth_dev_init(struct net_device *dev)
232e314dbdcSPavel Emelyanov {
2332681128fSEric Dumazet 	dev->vstats = alloc_percpu(struct pcpu_vstats);
2342681128fSEric Dumazet 	if (!dev->vstats)
235e314dbdcSPavel Emelyanov 		return -ENOMEM;
236e314dbdcSPavel Emelyanov 
237e314dbdcSPavel Emelyanov 	return 0;
238e314dbdcSPavel Emelyanov }
239e314dbdcSPavel Emelyanov 
24011687a10SDavid S. Miller static void veth_dev_free(struct net_device *dev)
24111687a10SDavid S. Miller {
2422681128fSEric Dumazet 	free_percpu(dev->vstats);
24311687a10SDavid S. Miller 	free_netdev(dev);
24411687a10SDavid S. Miller }
24511687a10SDavid S. Miller 
2464456e7bdSStephen Hemminger static const struct net_device_ops veth_netdev_ops = {
2474456e7bdSStephen Hemminger 	.ndo_init            = veth_dev_init,
2484456e7bdSStephen Hemminger 	.ndo_open            = veth_open,
2492cf48a10SEric W. Biederman 	.ndo_stop            = veth_close,
25000829823SStephen Hemminger 	.ndo_start_xmit      = veth_xmit,
25138d40815SEric Biederman 	.ndo_change_mtu      = veth_change_mtu,
2526311cc44Sstephen hemminger 	.ndo_get_stats64     = veth_get_stats64,
253ee923623SDaniel Lezcano 	.ndo_set_mac_address = eth_mac_addr,
2544456e7bdSStephen Hemminger };
2554456e7bdSStephen Hemminger 
2568093315aSEric Dumazet #define VETH_FEATURES (NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_ALL_TSO |    \
2578093315aSEric Dumazet 		       NETIF_F_HW_CSUM | NETIF_F_RXCSUM | NETIF_F_HIGHDMA | \
2588093315aSEric Dumazet 		       NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX)
2598093315aSEric Dumazet 
260e314dbdcSPavel Emelyanov static void veth_setup(struct net_device *dev)
261e314dbdcSPavel Emelyanov {
262e314dbdcSPavel Emelyanov 	ether_setup(dev);
263e314dbdcSPavel Emelyanov 
264550fd08cSNeil Horman 	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
26523ea5a96SHannes Frederic Sowa 	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
266550fd08cSNeil Horman 
2674456e7bdSStephen Hemminger 	dev->netdev_ops = &veth_netdev_ops;
268e314dbdcSPavel Emelyanov 	dev->ethtool_ops = &veth_ethtool_ops;
269e314dbdcSPavel Emelyanov 	dev->features |= NETIF_F_LLTX;
2708093315aSEric Dumazet 	dev->features |= VETH_FEATURES;
27111687a10SDavid S. Miller 	dev->destructor = veth_dev_free;
272a2c725faSMichał Mirosław 
2738093315aSEric Dumazet 	dev->hw_features = VETH_FEATURES;
274e314dbdcSPavel Emelyanov }
275e314dbdcSPavel Emelyanov 
276e314dbdcSPavel Emelyanov /*
277e314dbdcSPavel Emelyanov  * netlink interface
278e314dbdcSPavel Emelyanov  */
279e314dbdcSPavel Emelyanov 
280e314dbdcSPavel Emelyanov static int veth_validate(struct nlattr *tb[], struct nlattr *data[])
281e314dbdcSPavel Emelyanov {
282e314dbdcSPavel Emelyanov 	if (tb[IFLA_ADDRESS]) {
283e314dbdcSPavel Emelyanov 		if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
284e314dbdcSPavel Emelyanov 			return -EINVAL;
285e314dbdcSPavel Emelyanov 		if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
286e314dbdcSPavel Emelyanov 			return -EADDRNOTAVAIL;
287e314dbdcSPavel Emelyanov 	}
28838d40815SEric Biederman 	if (tb[IFLA_MTU]) {
28938d40815SEric Biederman 		if (!is_valid_veth_mtu(nla_get_u32(tb[IFLA_MTU])))
29038d40815SEric Biederman 			return -EINVAL;
29138d40815SEric Biederman 	}
292e314dbdcSPavel Emelyanov 	return 0;
293e314dbdcSPavel Emelyanov }
294e314dbdcSPavel Emelyanov 
295e314dbdcSPavel Emelyanov static struct rtnl_link_ops veth_link_ops;
296e314dbdcSPavel Emelyanov 
29781adee47SEric W. Biederman static int veth_newlink(struct net *src_net, struct net_device *dev,
298e314dbdcSPavel Emelyanov 			 struct nlattr *tb[], struct nlattr *data[])
299e314dbdcSPavel Emelyanov {
300e314dbdcSPavel Emelyanov 	int err;
301e314dbdcSPavel Emelyanov 	struct net_device *peer;
302e314dbdcSPavel Emelyanov 	struct veth_priv *priv;
303e314dbdcSPavel Emelyanov 	char ifname[IFNAMSIZ];
304e314dbdcSPavel Emelyanov 	struct nlattr *peer_tb[IFLA_MAX + 1], **tbp;
3053729d502SPatrick McHardy 	struct ifinfomsg *ifmp;
30681adee47SEric W. Biederman 	struct net *net;
307e314dbdcSPavel Emelyanov 
308e314dbdcSPavel Emelyanov 	/*
309e314dbdcSPavel Emelyanov 	 * create and register peer first
310e314dbdcSPavel Emelyanov 	 */
311e314dbdcSPavel Emelyanov 	if (data != NULL && data[VETH_INFO_PEER] != NULL) {
312e314dbdcSPavel Emelyanov 		struct nlattr *nla_peer;
313e314dbdcSPavel Emelyanov 
314e314dbdcSPavel Emelyanov 		nla_peer = data[VETH_INFO_PEER];
3153729d502SPatrick McHardy 		ifmp = nla_data(nla_peer);
316e314dbdcSPavel Emelyanov 		err = nla_parse(peer_tb, IFLA_MAX,
317e314dbdcSPavel Emelyanov 				nla_data(nla_peer) + sizeof(struct ifinfomsg),
318e314dbdcSPavel Emelyanov 				nla_len(nla_peer) - sizeof(struct ifinfomsg),
319e314dbdcSPavel Emelyanov 				ifla_policy);
320e314dbdcSPavel Emelyanov 		if (err < 0)
321e314dbdcSPavel Emelyanov 			return err;
322e314dbdcSPavel Emelyanov 
323e314dbdcSPavel Emelyanov 		err = veth_validate(peer_tb, NULL);
324e314dbdcSPavel Emelyanov 		if (err < 0)
325e314dbdcSPavel Emelyanov 			return err;
326e314dbdcSPavel Emelyanov 
327e314dbdcSPavel Emelyanov 		tbp = peer_tb;
3283729d502SPatrick McHardy 	} else {
3293729d502SPatrick McHardy 		ifmp = NULL;
330e314dbdcSPavel Emelyanov 		tbp = tb;
3313729d502SPatrick McHardy 	}
332e314dbdcSPavel Emelyanov 
333e314dbdcSPavel Emelyanov 	if (tbp[IFLA_IFNAME])
334e314dbdcSPavel Emelyanov 		nla_strlcpy(ifname, tbp[IFLA_IFNAME], IFNAMSIZ);
335e314dbdcSPavel Emelyanov 	else
336e314dbdcSPavel Emelyanov 		snprintf(ifname, IFNAMSIZ, DRV_NAME "%%d");
337e314dbdcSPavel Emelyanov 
33881adee47SEric W. Biederman 	net = rtnl_link_get_net(src_net, tbp);
33981adee47SEric W. Biederman 	if (IS_ERR(net))
34081adee47SEric W. Biederman 		return PTR_ERR(net);
34181adee47SEric W. Biederman 
342c0713563SRami Rosen 	peer = rtnl_create_link(net, ifname, &veth_link_ops, tbp);
34381adee47SEric W. Biederman 	if (IS_ERR(peer)) {
34481adee47SEric W. Biederman 		put_net(net);
345e314dbdcSPavel Emelyanov 		return PTR_ERR(peer);
34681adee47SEric W. Biederman 	}
347e314dbdcSPavel Emelyanov 
348e314dbdcSPavel Emelyanov 	if (tbp[IFLA_ADDRESS] == NULL)
349f2cedb63SDanny Kukawka 		eth_hw_addr_random(peer);
350e314dbdcSPavel Emelyanov 
351e6f8f1a7SPavel Emelyanov 	if (ifmp && (dev->ifindex != 0))
352e6f8f1a7SPavel Emelyanov 		peer->ifindex = ifmp->ifi_index;
353e6f8f1a7SPavel Emelyanov 
354e314dbdcSPavel Emelyanov 	err = register_netdevice(peer);
35581adee47SEric W. Biederman 	put_net(net);
35681adee47SEric W. Biederman 	net = NULL;
357e314dbdcSPavel Emelyanov 	if (err < 0)
358e314dbdcSPavel Emelyanov 		goto err_register_peer;
359e314dbdcSPavel Emelyanov 
360e314dbdcSPavel Emelyanov 	netif_carrier_off(peer);
361e314dbdcSPavel Emelyanov 
3623729d502SPatrick McHardy 	err = rtnl_configure_link(peer, ifmp);
3633729d502SPatrick McHardy 	if (err < 0)
3643729d502SPatrick McHardy 		goto err_configure_peer;
3653729d502SPatrick McHardy 
366e314dbdcSPavel Emelyanov 	/*
367e314dbdcSPavel Emelyanov 	 * register dev last
368e314dbdcSPavel Emelyanov 	 *
369e314dbdcSPavel Emelyanov 	 * note, that since we've registered new device the dev's name
370e314dbdcSPavel Emelyanov 	 * should be re-allocated
371e314dbdcSPavel Emelyanov 	 */
372e314dbdcSPavel Emelyanov 
373e314dbdcSPavel Emelyanov 	if (tb[IFLA_ADDRESS] == NULL)
374f2cedb63SDanny Kukawka 		eth_hw_addr_random(dev);
375e314dbdcSPavel Emelyanov 
3766c8c4446SJiri Pirko 	if (tb[IFLA_IFNAME])
3776c8c4446SJiri Pirko 		nla_strlcpy(dev->name, tb[IFLA_IFNAME], IFNAMSIZ);
3786c8c4446SJiri Pirko 	else
3796c8c4446SJiri Pirko 		snprintf(dev->name, IFNAMSIZ, DRV_NAME "%%d");
3806c8c4446SJiri Pirko 
3816c8c4446SJiri Pirko 	if (strchr(dev->name, '%')) {
3826c8c4446SJiri Pirko 		err = dev_alloc_name(dev, dev->name);
3836c8c4446SJiri Pirko 		if (err < 0)
3846c8c4446SJiri Pirko 			goto err_alloc_name;
3856c8c4446SJiri Pirko 	}
3866c8c4446SJiri Pirko 
387e314dbdcSPavel Emelyanov 	err = register_netdevice(dev);
388e314dbdcSPavel Emelyanov 	if (err < 0)
389e314dbdcSPavel Emelyanov 		goto err_register_dev;
390e314dbdcSPavel Emelyanov 
391e314dbdcSPavel Emelyanov 	netif_carrier_off(dev);
392e314dbdcSPavel Emelyanov 
393e314dbdcSPavel Emelyanov 	/*
394e314dbdcSPavel Emelyanov 	 * tie the deviced together
395e314dbdcSPavel Emelyanov 	 */
396e314dbdcSPavel Emelyanov 
397e314dbdcSPavel Emelyanov 	priv = netdev_priv(dev);
398d0e2c55eSEric Dumazet 	rcu_assign_pointer(priv->peer, peer);
399e314dbdcSPavel Emelyanov 
400e314dbdcSPavel Emelyanov 	priv = netdev_priv(peer);
401d0e2c55eSEric Dumazet 	rcu_assign_pointer(priv->peer, dev);
402e314dbdcSPavel Emelyanov 	return 0;
403e314dbdcSPavel Emelyanov 
404e314dbdcSPavel Emelyanov err_register_dev:
405e314dbdcSPavel Emelyanov 	/* nothing to do */
4066c8c4446SJiri Pirko err_alloc_name:
4073729d502SPatrick McHardy err_configure_peer:
408e314dbdcSPavel Emelyanov 	unregister_netdevice(peer);
409e314dbdcSPavel Emelyanov 	return err;
410e314dbdcSPavel Emelyanov 
411e314dbdcSPavel Emelyanov err_register_peer:
412e314dbdcSPavel Emelyanov 	free_netdev(peer);
413e314dbdcSPavel Emelyanov 	return err;
414e314dbdcSPavel Emelyanov }
415e314dbdcSPavel Emelyanov 
41623289a37SEric Dumazet static void veth_dellink(struct net_device *dev, struct list_head *head)
417e314dbdcSPavel Emelyanov {
418e314dbdcSPavel Emelyanov 	struct veth_priv *priv;
419e314dbdcSPavel Emelyanov 	struct net_device *peer;
420e314dbdcSPavel Emelyanov 
421e314dbdcSPavel Emelyanov 	priv = netdev_priv(dev);
422d0e2c55eSEric Dumazet 	peer = rtnl_dereference(priv->peer);
423d0e2c55eSEric Dumazet 
424d0e2c55eSEric Dumazet 	/* Note : dellink() is called from default_device_exit_batch(),
425d0e2c55eSEric Dumazet 	 * before a rcu_synchronize() point. The devices are guaranteed
426d0e2c55eSEric Dumazet 	 * not being freed before one RCU grace period.
427d0e2c55eSEric Dumazet 	 */
428d0e2c55eSEric Dumazet 	RCU_INIT_POINTER(priv->peer, NULL);
429d0e2c55eSEric Dumazet 
430d0e2c55eSEric Dumazet 	priv = netdev_priv(peer);
431d0e2c55eSEric Dumazet 	RCU_INIT_POINTER(priv->peer, NULL);
432e314dbdcSPavel Emelyanov 
43324540535SEric Dumazet 	unregister_netdevice_queue(dev, head);
43424540535SEric Dumazet 	unregister_netdevice_queue(peer, head);
435e314dbdcSPavel Emelyanov }
436e314dbdcSPavel Emelyanov 
43723711438SThomas Graf static const struct nla_policy veth_policy[VETH_INFO_MAX + 1] = {
43823711438SThomas Graf 	[VETH_INFO_PEER]	= { .len = sizeof(struct ifinfomsg) },
43923711438SThomas Graf };
440e314dbdcSPavel Emelyanov 
441e314dbdcSPavel Emelyanov static struct rtnl_link_ops veth_link_ops = {
442e314dbdcSPavel Emelyanov 	.kind		= DRV_NAME,
443e314dbdcSPavel Emelyanov 	.priv_size	= sizeof(struct veth_priv),
444e314dbdcSPavel Emelyanov 	.setup		= veth_setup,
445e314dbdcSPavel Emelyanov 	.validate	= veth_validate,
446e314dbdcSPavel Emelyanov 	.newlink	= veth_newlink,
447e314dbdcSPavel Emelyanov 	.dellink	= veth_dellink,
448e314dbdcSPavel Emelyanov 	.policy		= veth_policy,
449e314dbdcSPavel Emelyanov 	.maxtype	= VETH_INFO_MAX,
450e314dbdcSPavel Emelyanov };
451e314dbdcSPavel Emelyanov 
452e314dbdcSPavel Emelyanov /*
453e314dbdcSPavel Emelyanov  * init/fini
454e314dbdcSPavel Emelyanov  */
455e314dbdcSPavel Emelyanov 
456e314dbdcSPavel Emelyanov static __init int veth_init(void)
457e314dbdcSPavel Emelyanov {
458e314dbdcSPavel Emelyanov 	return rtnl_link_register(&veth_link_ops);
459e314dbdcSPavel Emelyanov }
460e314dbdcSPavel Emelyanov 
461e314dbdcSPavel Emelyanov static __exit void veth_exit(void)
462e314dbdcSPavel Emelyanov {
46368365458SPatrick McHardy 	rtnl_link_unregister(&veth_link_ops);
464e314dbdcSPavel Emelyanov }
465e314dbdcSPavel Emelyanov 
466e314dbdcSPavel Emelyanov module_init(veth_init);
467e314dbdcSPavel Emelyanov module_exit(veth_exit);
468e314dbdcSPavel Emelyanov 
469e314dbdcSPavel Emelyanov MODULE_DESCRIPTION("Virtual Ethernet Tunnel");
470e314dbdcSPavel Emelyanov MODULE_LICENSE("GPL v2");
471e314dbdcSPavel Emelyanov MODULE_ALIAS_RTNL_LINK(DRV_NAME);
472