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> 20e314dbdcSPavel Emelyanov 21e314dbdcSPavel Emelyanov #define DRV_NAME "veth" 22e314dbdcSPavel Emelyanov #define DRV_VERSION "1.0" 23e314dbdcSPavel Emelyanov 2438d40815SEric Biederman #define MIN_MTU 68 /* Min L3 MTU */ 2538d40815SEric Biederman #define MAX_MTU 65535 /* Max L3 MTU (arbitrary) */ 2638d40815SEric Biederman 27e314dbdcSPavel Emelyanov struct veth_net_stats { 286311cc44Sstephen hemminger u64 rx_packets; 296311cc44Sstephen hemminger u64 tx_packets; 306311cc44Sstephen hemminger u64 rx_bytes; 316311cc44Sstephen hemminger u64 tx_bytes; 326311cc44Sstephen hemminger u64 rx_dropped; 33cf05c700SEric Dumazet u64 tx_dropped; 34cf05c700SEric Dumazet struct u64_stats_sync syncp; 35e314dbdcSPavel Emelyanov }; 36e314dbdcSPavel Emelyanov 37e314dbdcSPavel Emelyanov struct veth_priv { 38e314dbdcSPavel Emelyanov struct net_device *peer; 3947d74275STejun Heo struct veth_net_stats __percpu *stats; 40e314dbdcSPavel Emelyanov }; 41e314dbdcSPavel Emelyanov 42e314dbdcSPavel Emelyanov /* 43e314dbdcSPavel Emelyanov * ethtool interface 44e314dbdcSPavel Emelyanov */ 45e314dbdcSPavel Emelyanov 46e314dbdcSPavel Emelyanov static struct { 47e314dbdcSPavel Emelyanov const char string[ETH_GSTRING_LEN]; 48e314dbdcSPavel Emelyanov } ethtool_stats_keys[] = { 49e314dbdcSPavel Emelyanov { "peer_ifindex" }, 50e314dbdcSPavel Emelyanov }; 51e314dbdcSPavel Emelyanov 52e314dbdcSPavel Emelyanov static int veth_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) 53e314dbdcSPavel Emelyanov { 54e314dbdcSPavel Emelyanov cmd->supported = 0; 55e314dbdcSPavel Emelyanov cmd->advertising = 0; 5670739497SDavid Decotigny ethtool_cmd_speed_set(cmd, SPEED_10000); 57e314dbdcSPavel Emelyanov cmd->duplex = DUPLEX_FULL; 58e314dbdcSPavel Emelyanov cmd->port = PORT_TP; 59e314dbdcSPavel Emelyanov cmd->phy_address = 0; 60e314dbdcSPavel Emelyanov cmd->transceiver = XCVR_INTERNAL; 61e314dbdcSPavel Emelyanov cmd->autoneg = AUTONEG_DISABLE; 62e314dbdcSPavel Emelyanov cmd->maxtxpkt = 0; 63e314dbdcSPavel Emelyanov cmd->maxrxpkt = 0; 64e314dbdcSPavel Emelyanov return 0; 65e314dbdcSPavel Emelyanov } 66e314dbdcSPavel Emelyanov 67e314dbdcSPavel Emelyanov static void veth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) 68e314dbdcSPavel Emelyanov { 69e314dbdcSPavel Emelyanov strcpy(info->driver, DRV_NAME); 70e314dbdcSPavel Emelyanov strcpy(info->version, DRV_VERSION); 71e314dbdcSPavel Emelyanov strcpy(info->fw_version, "N/A"); 72e314dbdcSPavel Emelyanov } 73e314dbdcSPavel Emelyanov 74e314dbdcSPavel Emelyanov static void veth_get_strings(struct net_device *dev, u32 stringset, u8 *buf) 75e314dbdcSPavel Emelyanov { 76e314dbdcSPavel Emelyanov switch(stringset) { 77e314dbdcSPavel Emelyanov case ETH_SS_STATS: 78e314dbdcSPavel Emelyanov memcpy(buf, ðtool_stats_keys, sizeof(ethtool_stats_keys)); 79e314dbdcSPavel Emelyanov break; 80e314dbdcSPavel Emelyanov } 81e314dbdcSPavel Emelyanov } 82e314dbdcSPavel Emelyanov 83b9f2c044SJeff Garzik static int veth_get_sset_count(struct net_device *dev, int sset) 84e314dbdcSPavel Emelyanov { 85b9f2c044SJeff Garzik switch (sset) { 86b9f2c044SJeff Garzik case ETH_SS_STATS: 87e314dbdcSPavel Emelyanov return ARRAY_SIZE(ethtool_stats_keys); 88b9f2c044SJeff Garzik default: 89b9f2c044SJeff Garzik return -EOPNOTSUPP; 90b9f2c044SJeff Garzik } 91e314dbdcSPavel Emelyanov } 92e314dbdcSPavel Emelyanov 93e314dbdcSPavel Emelyanov static void veth_get_ethtool_stats(struct net_device *dev, 94e314dbdcSPavel Emelyanov struct ethtool_stats *stats, u64 *data) 95e314dbdcSPavel Emelyanov { 96e314dbdcSPavel Emelyanov struct veth_priv *priv; 97e314dbdcSPavel Emelyanov 98e314dbdcSPavel Emelyanov priv = netdev_priv(dev); 99e314dbdcSPavel Emelyanov data[0] = priv->peer->ifindex; 100e314dbdcSPavel Emelyanov } 101e314dbdcSPavel Emelyanov 1020fc0b732SStephen Hemminger static const struct ethtool_ops veth_ethtool_ops = { 103e314dbdcSPavel Emelyanov .get_settings = veth_get_settings, 104e314dbdcSPavel Emelyanov .get_drvinfo = veth_get_drvinfo, 105e314dbdcSPavel Emelyanov .get_link = ethtool_op_get_link, 106e314dbdcSPavel Emelyanov .get_strings = veth_get_strings, 107b9f2c044SJeff Garzik .get_sset_count = veth_get_sset_count, 108e314dbdcSPavel Emelyanov .get_ethtool_stats = veth_get_ethtool_stats, 109e314dbdcSPavel Emelyanov }; 110e314dbdcSPavel Emelyanov 111e314dbdcSPavel Emelyanov /* 112e314dbdcSPavel Emelyanov * xmit 113e314dbdcSPavel Emelyanov */ 114e314dbdcSPavel Emelyanov 115424efe9cSStephen Hemminger static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev) 116e314dbdcSPavel Emelyanov { 117e314dbdcSPavel Emelyanov struct net_device *rcv = NULL; 118e314dbdcSPavel Emelyanov struct veth_priv *priv, *rcv_priv; 11938d40815SEric Biederman struct veth_net_stats *stats, *rcv_stats; 120e7dcaa47SChristoph Lameter int length; 121e314dbdcSPavel Emelyanov 122e314dbdcSPavel Emelyanov priv = netdev_priv(dev); 123e314dbdcSPavel Emelyanov rcv = priv->peer; 124e314dbdcSPavel Emelyanov rcv_priv = netdev_priv(rcv); 125e314dbdcSPavel Emelyanov 126e7dcaa47SChristoph Lameter stats = this_cpu_ptr(priv->stats); 127e7dcaa47SChristoph Lameter rcv_stats = this_cpu_ptr(rcv_priv->stats); 128e314dbdcSPavel Emelyanov 129e314dbdcSPavel Emelyanov if (!(rcv->flags & IFF_UP)) 13038d40815SEric Biederman goto tx_drop; 13138d40815SEric Biederman 1320b796750SMichał Mirosław /* don't change ip_summed == CHECKSUM_PARTIAL, as that 1330b796750SMichał Mirosław will cause bad checksum on forwarded packets */ 134a2c725faSMichał Mirosław if (skb->ip_summed == CHECKSUM_NONE && 135a2c725faSMichał Mirosław rcv->features & NETIF_F_RXCSUM) 136a2c725faSMichał Mirosław skb->ip_summed = CHECKSUM_UNNECESSARY; 137e314dbdcSPavel Emelyanov 138675071a2SEric W. Biederman length = skb->len; 13944540960SArnd Bergmann if (dev_forward_skb(rcv, skb) != NET_RX_SUCCESS) 14044540960SArnd Bergmann goto rx_drop; 141e314dbdcSPavel Emelyanov 142cf05c700SEric Dumazet u64_stats_update_begin(&stats->syncp); 143e314dbdcSPavel Emelyanov stats->tx_bytes += length; 144e314dbdcSPavel Emelyanov stats->tx_packets++; 145cf05c700SEric Dumazet u64_stats_update_end(&stats->syncp); 146e314dbdcSPavel Emelyanov 147cf05c700SEric Dumazet u64_stats_update_begin(&rcv_stats->syncp); 14838d40815SEric Biederman rcv_stats->rx_bytes += length; 14938d40815SEric Biederman rcv_stats->rx_packets++; 150cf05c700SEric Dumazet u64_stats_update_end(&rcv_stats->syncp); 151e314dbdcSPavel Emelyanov 1526ed10654SPatrick McHardy return NETDEV_TX_OK; 153e314dbdcSPavel Emelyanov 15438d40815SEric Biederman tx_drop: 155e314dbdcSPavel Emelyanov kfree_skb(skb); 156cf05c700SEric Dumazet u64_stats_update_begin(&stats->syncp); 157e314dbdcSPavel Emelyanov stats->tx_dropped++; 158cf05c700SEric Dumazet u64_stats_update_end(&stats->syncp); 1596ed10654SPatrick McHardy return NETDEV_TX_OK; 16038d40815SEric Biederman 16138d40815SEric Biederman rx_drop: 162cf05c700SEric Dumazet u64_stats_update_begin(&rcv_stats->syncp); 16338d40815SEric Biederman rcv_stats->rx_dropped++; 164cf05c700SEric Dumazet u64_stats_update_end(&rcv_stats->syncp); 1656ed10654SPatrick McHardy return NETDEV_TX_OK; 166e314dbdcSPavel Emelyanov } 167e314dbdcSPavel Emelyanov 168e314dbdcSPavel Emelyanov /* 169e314dbdcSPavel Emelyanov * general routines 170e314dbdcSPavel Emelyanov */ 171e314dbdcSPavel Emelyanov 1726311cc44Sstephen hemminger static struct rtnl_link_stats64 *veth_get_stats64(struct net_device *dev, 1736311cc44Sstephen hemminger struct rtnl_link_stats64 *tot) 174e314dbdcSPavel Emelyanov { 175cf05c700SEric Dumazet struct veth_priv *priv = netdev_priv(dev); 17611687a10SDavid S. Miller int cpu; 17711687a10SDavid S. Miller 1782b1c8b0fSEric Dumazet for_each_possible_cpu(cpu) { 179cf05c700SEric Dumazet struct veth_net_stats *stats = per_cpu_ptr(priv->stats, cpu); 180cf05c700SEric Dumazet u64 rx_packets, rx_bytes, rx_dropped; 181cf05c700SEric Dumazet u64 tx_packets, tx_bytes, tx_dropped; 182cf05c700SEric Dumazet unsigned int start; 183e314dbdcSPavel Emelyanov 184cf05c700SEric Dumazet do { 185cf05c700SEric Dumazet start = u64_stats_fetch_begin_bh(&stats->syncp); 186cf05c700SEric Dumazet rx_packets = stats->rx_packets; 187cf05c700SEric Dumazet tx_packets = stats->tx_packets; 188cf05c700SEric Dumazet rx_bytes = stats->rx_bytes; 189cf05c700SEric Dumazet tx_bytes = stats->tx_bytes; 190cf05c700SEric Dumazet rx_dropped = stats->rx_dropped; 191cf05c700SEric Dumazet tx_dropped = stats->tx_dropped; 192cf05c700SEric Dumazet } while (u64_stats_fetch_retry_bh(&stats->syncp, start)); 193cf05c700SEric Dumazet tot->rx_packets += rx_packets; 194cf05c700SEric Dumazet tot->tx_packets += tx_packets; 195cf05c700SEric Dumazet tot->rx_bytes += rx_bytes; 196cf05c700SEric Dumazet tot->tx_bytes += tx_bytes; 197cf05c700SEric Dumazet tot->rx_dropped += rx_dropped; 198cf05c700SEric Dumazet tot->tx_dropped += tx_dropped; 199e314dbdcSPavel Emelyanov } 200e314dbdcSPavel Emelyanov 2016311cc44Sstephen hemminger return tot; 202e314dbdcSPavel Emelyanov } 203e314dbdcSPavel Emelyanov 204e314dbdcSPavel Emelyanov static int veth_open(struct net_device *dev) 205e314dbdcSPavel Emelyanov { 206e314dbdcSPavel Emelyanov struct veth_priv *priv; 207e314dbdcSPavel Emelyanov 208e314dbdcSPavel Emelyanov priv = netdev_priv(dev); 209e314dbdcSPavel Emelyanov if (priv->peer == NULL) 210e314dbdcSPavel Emelyanov return -ENOTCONN; 211e314dbdcSPavel Emelyanov 212e314dbdcSPavel Emelyanov if (priv->peer->flags & IFF_UP) { 213e314dbdcSPavel Emelyanov netif_carrier_on(dev); 214e314dbdcSPavel Emelyanov netif_carrier_on(priv->peer); 215e314dbdcSPavel Emelyanov } 216e314dbdcSPavel Emelyanov return 0; 217e314dbdcSPavel Emelyanov } 218e314dbdcSPavel Emelyanov 2192cf48a10SEric W. Biederman static int veth_close(struct net_device *dev) 2202cf48a10SEric W. Biederman { 2212cf48a10SEric W. Biederman struct veth_priv *priv = netdev_priv(dev); 2222cf48a10SEric W. Biederman 2232cf48a10SEric W. Biederman netif_carrier_off(dev); 2242cf48a10SEric W. Biederman netif_carrier_off(priv->peer); 2252cf48a10SEric W. Biederman 2262cf48a10SEric W. Biederman return 0; 2272cf48a10SEric W. Biederman } 2282cf48a10SEric W. Biederman 22938d40815SEric Biederman static int is_valid_veth_mtu(int new_mtu) 23038d40815SEric Biederman { 231807540baSEric Dumazet return new_mtu >= MIN_MTU && new_mtu <= MAX_MTU; 23238d40815SEric Biederman } 23338d40815SEric Biederman 23438d40815SEric Biederman static int veth_change_mtu(struct net_device *dev, int new_mtu) 23538d40815SEric Biederman { 23638d40815SEric Biederman if (!is_valid_veth_mtu(new_mtu)) 23738d40815SEric Biederman return -EINVAL; 23838d40815SEric Biederman dev->mtu = new_mtu; 23938d40815SEric Biederman return 0; 24038d40815SEric Biederman } 24138d40815SEric Biederman 242e314dbdcSPavel Emelyanov static int veth_dev_init(struct net_device *dev) 243e314dbdcSPavel Emelyanov { 24447d74275STejun Heo struct veth_net_stats __percpu *stats; 245e314dbdcSPavel Emelyanov struct veth_priv *priv; 246e314dbdcSPavel Emelyanov 247e314dbdcSPavel Emelyanov stats = alloc_percpu(struct veth_net_stats); 248e314dbdcSPavel Emelyanov if (stats == NULL) 249e314dbdcSPavel Emelyanov return -ENOMEM; 250e314dbdcSPavel Emelyanov 251e314dbdcSPavel Emelyanov priv = netdev_priv(dev); 252e314dbdcSPavel Emelyanov priv->stats = stats; 253e314dbdcSPavel Emelyanov return 0; 254e314dbdcSPavel Emelyanov } 255e314dbdcSPavel Emelyanov 25611687a10SDavid S. Miller static void veth_dev_free(struct net_device *dev) 25711687a10SDavid S. Miller { 25811687a10SDavid S. Miller struct veth_priv *priv; 25911687a10SDavid S. Miller 26011687a10SDavid S. Miller priv = netdev_priv(dev); 26111687a10SDavid S. Miller free_percpu(priv->stats); 26211687a10SDavid S. Miller free_netdev(dev); 26311687a10SDavid S. Miller } 26411687a10SDavid S. Miller 2654456e7bdSStephen Hemminger static const struct net_device_ops veth_netdev_ops = { 2664456e7bdSStephen Hemminger .ndo_init = veth_dev_init, 2674456e7bdSStephen Hemminger .ndo_open = veth_open, 2682cf48a10SEric W. Biederman .ndo_stop = veth_close, 26900829823SStephen Hemminger .ndo_start_xmit = veth_xmit, 27038d40815SEric Biederman .ndo_change_mtu = veth_change_mtu, 2716311cc44Sstephen hemminger .ndo_get_stats64 = veth_get_stats64, 272ee923623SDaniel Lezcano .ndo_set_mac_address = eth_mac_addr, 2734456e7bdSStephen Hemminger }; 2744456e7bdSStephen Hemminger 275e314dbdcSPavel Emelyanov static void veth_setup(struct net_device *dev) 276e314dbdcSPavel Emelyanov { 277e314dbdcSPavel Emelyanov ether_setup(dev); 278e314dbdcSPavel Emelyanov 2794456e7bdSStephen Hemminger dev->netdev_ops = &veth_netdev_ops; 280e314dbdcSPavel Emelyanov dev->ethtool_ops = &veth_ethtool_ops; 281e314dbdcSPavel Emelyanov dev->features |= NETIF_F_LLTX; 28211687a10SDavid S. Miller dev->destructor = veth_dev_free; 283a2c725faSMichał Mirosław 284a2c725faSMichał Mirosław dev->hw_features = NETIF_F_NO_CSUM | NETIF_F_SG | NETIF_F_RXCSUM; 285e314dbdcSPavel Emelyanov } 286e314dbdcSPavel Emelyanov 287e314dbdcSPavel Emelyanov /* 288e314dbdcSPavel Emelyanov * netlink interface 289e314dbdcSPavel Emelyanov */ 290e314dbdcSPavel Emelyanov 291e314dbdcSPavel Emelyanov static int veth_validate(struct nlattr *tb[], struct nlattr *data[]) 292e314dbdcSPavel Emelyanov { 293e314dbdcSPavel Emelyanov if (tb[IFLA_ADDRESS]) { 294e314dbdcSPavel Emelyanov if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) 295e314dbdcSPavel Emelyanov return -EINVAL; 296e314dbdcSPavel Emelyanov if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) 297e314dbdcSPavel Emelyanov return -EADDRNOTAVAIL; 298e314dbdcSPavel Emelyanov } 29938d40815SEric Biederman if (tb[IFLA_MTU]) { 30038d40815SEric Biederman if (!is_valid_veth_mtu(nla_get_u32(tb[IFLA_MTU]))) 30138d40815SEric Biederman return -EINVAL; 30238d40815SEric Biederman } 303e314dbdcSPavel Emelyanov return 0; 304e314dbdcSPavel Emelyanov } 305e314dbdcSPavel Emelyanov 306e314dbdcSPavel Emelyanov static struct rtnl_link_ops veth_link_ops; 307e314dbdcSPavel Emelyanov 30881adee47SEric W. Biederman static int veth_newlink(struct net *src_net, struct net_device *dev, 309e314dbdcSPavel Emelyanov struct nlattr *tb[], struct nlattr *data[]) 310e314dbdcSPavel Emelyanov { 311e314dbdcSPavel Emelyanov int err; 312e314dbdcSPavel Emelyanov struct net_device *peer; 313e314dbdcSPavel Emelyanov struct veth_priv *priv; 314e314dbdcSPavel Emelyanov char ifname[IFNAMSIZ]; 315e314dbdcSPavel Emelyanov struct nlattr *peer_tb[IFLA_MAX + 1], **tbp; 3163729d502SPatrick McHardy struct ifinfomsg *ifmp; 31781adee47SEric W. Biederman struct net *net; 318e314dbdcSPavel Emelyanov 319e314dbdcSPavel Emelyanov /* 320e314dbdcSPavel Emelyanov * create and register peer first 321e314dbdcSPavel Emelyanov */ 322e314dbdcSPavel Emelyanov if (data != NULL && data[VETH_INFO_PEER] != NULL) { 323e314dbdcSPavel Emelyanov struct nlattr *nla_peer; 324e314dbdcSPavel Emelyanov 325e314dbdcSPavel Emelyanov nla_peer = data[VETH_INFO_PEER]; 3263729d502SPatrick McHardy ifmp = nla_data(nla_peer); 327e314dbdcSPavel Emelyanov err = nla_parse(peer_tb, IFLA_MAX, 328e314dbdcSPavel Emelyanov nla_data(nla_peer) + sizeof(struct ifinfomsg), 329e314dbdcSPavel Emelyanov nla_len(nla_peer) - sizeof(struct ifinfomsg), 330e314dbdcSPavel Emelyanov ifla_policy); 331e314dbdcSPavel Emelyanov if (err < 0) 332e314dbdcSPavel Emelyanov return err; 333e314dbdcSPavel Emelyanov 334e314dbdcSPavel Emelyanov err = veth_validate(peer_tb, NULL); 335e314dbdcSPavel Emelyanov if (err < 0) 336e314dbdcSPavel Emelyanov return err; 337e314dbdcSPavel Emelyanov 338e314dbdcSPavel Emelyanov tbp = peer_tb; 3393729d502SPatrick McHardy } else { 3403729d502SPatrick McHardy ifmp = NULL; 341e314dbdcSPavel Emelyanov tbp = tb; 3423729d502SPatrick McHardy } 343e314dbdcSPavel Emelyanov 344e314dbdcSPavel Emelyanov if (tbp[IFLA_IFNAME]) 345e314dbdcSPavel Emelyanov nla_strlcpy(ifname, tbp[IFLA_IFNAME], IFNAMSIZ); 346e314dbdcSPavel Emelyanov else 347e314dbdcSPavel Emelyanov snprintf(ifname, IFNAMSIZ, DRV_NAME "%%d"); 348e314dbdcSPavel Emelyanov 34981adee47SEric W. Biederman net = rtnl_link_get_net(src_net, tbp); 35081adee47SEric W. Biederman if (IS_ERR(net)) 35181adee47SEric W. Biederman return PTR_ERR(net); 35281adee47SEric W. Biederman 35381adee47SEric W. Biederman peer = rtnl_create_link(src_net, net, ifname, &veth_link_ops, tbp); 35481adee47SEric W. Biederman if (IS_ERR(peer)) { 35581adee47SEric W. Biederman put_net(net); 356e314dbdcSPavel Emelyanov return PTR_ERR(peer); 35781adee47SEric W. Biederman } 358e314dbdcSPavel Emelyanov 359e314dbdcSPavel Emelyanov if (tbp[IFLA_ADDRESS] == NULL) 360e314dbdcSPavel Emelyanov random_ether_addr(peer->dev_addr); 361e314dbdcSPavel Emelyanov 362e314dbdcSPavel Emelyanov err = register_netdevice(peer); 36381adee47SEric W. Biederman put_net(net); 36481adee47SEric W. Biederman net = NULL; 365e314dbdcSPavel Emelyanov if (err < 0) 366e314dbdcSPavel Emelyanov goto err_register_peer; 367e314dbdcSPavel Emelyanov 368e314dbdcSPavel Emelyanov netif_carrier_off(peer); 369e314dbdcSPavel Emelyanov 3703729d502SPatrick McHardy err = rtnl_configure_link(peer, ifmp); 3713729d502SPatrick McHardy if (err < 0) 3723729d502SPatrick McHardy goto err_configure_peer; 3733729d502SPatrick McHardy 374e314dbdcSPavel Emelyanov /* 375e314dbdcSPavel Emelyanov * register dev last 376e314dbdcSPavel Emelyanov * 377e314dbdcSPavel Emelyanov * note, that since we've registered new device the dev's name 378e314dbdcSPavel Emelyanov * should be re-allocated 379e314dbdcSPavel Emelyanov */ 380e314dbdcSPavel Emelyanov 381e314dbdcSPavel Emelyanov if (tb[IFLA_ADDRESS] == NULL) 382e314dbdcSPavel Emelyanov random_ether_addr(dev->dev_addr); 383e314dbdcSPavel Emelyanov 3846c8c4446SJiri Pirko if (tb[IFLA_IFNAME]) 3856c8c4446SJiri Pirko nla_strlcpy(dev->name, tb[IFLA_IFNAME], IFNAMSIZ); 3866c8c4446SJiri Pirko else 3876c8c4446SJiri Pirko snprintf(dev->name, IFNAMSIZ, DRV_NAME "%%d"); 3886c8c4446SJiri Pirko 3896c8c4446SJiri Pirko if (strchr(dev->name, '%')) { 3906c8c4446SJiri Pirko err = dev_alloc_name(dev, dev->name); 3916c8c4446SJiri Pirko if (err < 0) 3926c8c4446SJiri Pirko goto err_alloc_name; 3936c8c4446SJiri Pirko } 3946c8c4446SJiri Pirko 395e314dbdcSPavel Emelyanov err = register_netdevice(dev); 396e314dbdcSPavel Emelyanov if (err < 0) 397e314dbdcSPavel Emelyanov goto err_register_dev; 398e314dbdcSPavel Emelyanov 399e314dbdcSPavel Emelyanov netif_carrier_off(dev); 400e314dbdcSPavel Emelyanov 401e314dbdcSPavel Emelyanov /* 402e314dbdcSPavel Emelyanov * tie the deviced together 403e314dbdcSPavel Emelyanov */ 404e314dbdcSPavel Emelyanov 405e314dbdcSPavel Emelyanov priv = netdev_priv(dev); 406e314dbdcSPavel Emelyanov priv->peer = peer; 407e314dbdcSPavel Emelyanov 408e314dbdcSPavel Emelyanov priv = netdev_priv(peer); 409e314dbdcSPavel Emelyanov priv->peer = dev; 410e314dbdcSPavel Emelyanov return 0; 411e314dbdcSPavel Emelyanov 412e314dbdcSPavel Emelyanov err_register_dev: 413e314dbdcSPavel Emelyanov /* nothing to do */ 4146c8c4446SJiri Pirko err_alloc_name: 4153729d502SPatrick McHardy err_configure_peer: 416e314dbdcSPavel Emelyanov unregister_netdevice(peer); 417e314dbdcSPavel Emelyanov return err; 418e314dbdcSPavel Emelyanov 419e314dbdcSPavel Emelyanov err_register_peer: 420e314dbdcSPavel Emelyanov free_netdev(peer); 421e314dbdcSPavel Emelyanov return err; 422e314dbdcSPavel Emelyanov } 423e314dbdcSPavel Emelyanov 42423289a37SEric Dumazet static void veth_dellink(struct net_device *dev, struct list_head *head) 425e314dbdcSPavel Emelyanov { 426e314dbdcSPavel Emelyanov struct veth_priv *priv; 427e314dbdcSPavel Emelyanov struct net_device *peer; 428e314dbdcSPavel Emelyanov 429e314dbdcSPavel Emelyanov priv = netdev_priv(dev); 430e314dbdcSPavel Emelyanov peer = priv->peer; 431e314dbdcSPavel Emelyanov 43224540535SEric Dumazet unregister_netdevice_queue(dev, head); 43324540535SEric Dumazet unregister_netdevice_queue(peer, head); 434e314dbdcSPavel Emelyanov } 435e314dbdcSPavel Emelyanov 436e314dbdcSPavel Emelyanov static const struct nla_policy veth_policy[VETH_INFO_MAX + 1]; 437e314dbdcSPavel Emelyanov 438e314dbdcSPavel Emelyanov static struct rtnl_link_ops veth_link_ops = { 439e314dbdcSPavel Emelyanov .kind = DRV_NAME, 440e314dbdcSPavel Emelyanov .priv_size = sizeof(struct veth_priv), 441e314dbdcSPavel Emelyanov .setup = veth_setup, 442e314dbdcSPavel Emelyanov .validate = veth_validate, 443e314dbdcSPavel Emelyanov .newlink = veth_newlink, 444e314dbdcSPavel Emelyanov .dellink = veth_dellink, 445e314dbdcSPavel Emelyanov .policy = veth_policy, 446e314dbdcSPavel Emelyanov .maxtype = VETH_INFO_MAX, 447e314dbdcSPavel Emelyanov }; 448e314dbdcSPavel Emelyanov 449e314dbdcSPavel Emelyanov /* 450e314dbdcSPavel Emelyanov * init/fini 451e314dbdcSPavel Emelyanov */ 452e314dbdcSPavel Emelyanov 453e314dbdcSPavel Emelyanov static __init int veth_init(void) 454e314dbdcSPavel Emelyanov { 455e314dbdcSPavel Emelyanov return rtnl_link_register(&veth_link_ops); 456e314dbdcSPavel Emelyanov } 457e314dbdcSPavel Emelyanov 458e314dbdcSPavel Emelyanov static __exit void veth_exit(void) 459e314dbdcSPavel Emelyanov { 46068365458SPatrick McHardy rtnl_link_unregister(&veth_link_ops); 461e314dbdcSPavel Emelyanov } 462e314dbdcSPavel Emelyanov 463e314dbdcSPavel Emelyanov module_init(veth_init); 464e314dbdcSPavel Emelyanov module_exit(veth_exit); 465e314dbdcSPavel Emelyanov 466e314dbdcSPavel Emelyanov MODULE_DESCRIPTION("Virtual Ethernet Tunnel"); 467e314dbdcSPavel Emelyanov MODULE_LICENSE("GPL v2"); 468e314dbdcSPavel Emelyanov MODULE_ALIAS_RTNL_LINK(DRV_NAME); 469