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> 12e314dbdcSPavel Emelyanov #include <linux/ethtool.h> 13e314dbdcSPavel Emelyanov #include <linux/etherdevice.h> 14e314dbdcSPavel Emelyanov 15e314dbdcSPavel Emelyanov #include <net/dst.h> 16e314dbdcSPavel Emelyanov #include <net/xfrm.h> 17ecef969eSStephen Hemminger #include <linux/veth.h> 18e314dbdcSPavel Emelyanov 19e314dbdcSPavel Emelyanov #define DRV_NAME "veth" 20e314dbdcSPavel Emelyanov #define DRV_VERSION "1.0" 21e314dbdcSPavel Emelyanov 2238d40815SEric Biederman #define MIN_MTU 68 /* Min L3 MTU */ 2338d40815SEric Biederman #define MAX_MTU 65535 /* Max L3 MTU (arbitrary) */ 2438d40815SEric Biederman #define MTU_PAD (ETH_HLEN + 4) /* Max difference between L2 and L3 size MTU */ 2538d40815SEric Biederman 26e314dbdcSPavel Emelyanov struct veth_net_stats { 27e314dbdcSPavel Emelyanov unsigned long rx_packets; 28e314dbdcSPavel Emelyanov unsigned long tx_packets; 29e314dbdcSPavel Emelyanov unsigned long rx_bytes; 30e314dbdcSPavel Emelyanov unsigned long tx_bytes; 31e314dbdcSPavel Emelyanov unsigned long tx_dropped; 3238d40815SEric Biederman unsigned long rx_dropped; 33e314dbdcSPavel Emelyanov }; 34e314dbdcSPavel Emelyanov 35e314dbdcSPavel Emelyanov struct veth_priv { 36e314dbdcSPavel Emelyanov struct net_device *peer; 37e314dbdcSPavel Emelyanov struct veth_net_stats *stats; 38e314dbdcSPavel Emelyanov unsigned ip_summed; 39e314dbdcSPavel Emelyanov }; 40e314dbdcSPavel Emelyanov 41e314dbdcSPavel Emelyanov /* 42e314dbdcSPavel Emelyanov * ethtool interface 43e314dbdcSPavel Emelyanov */ 44e314dbdcSPavel Emelyanov 45e314dbdcSPavel Emelyanov static struct { 46e314dbdcSPavel Emelyanov const char string[ETH_GSTRING_LEN]; 47e314dbdcSPavel Emelyanov } ethtool_stats_keys[] = { 48e314dbdcSPavel Emelyanov { "peer_ifindex" }, 49e314dbdcSPavel Emelyanov }; 50e314dbdcSPavel Emelyanov 51e314dbdcSPavel Emelyanov static int veth_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) 52e314dbdcSPavel Emelyanov { 53e314dbdcSPavel Emelyanov cmd->supported = 0; 54e314dbdcSPavel Emelyanov cmd->advertising = 0; 55e314dbdcSPavel Emelyanov cmd->speed = SPEED_10000; 56e314dbdcSPavel Emelyanov cmd->duplex = DUPLEX_FULL; 57e314dbdcSPavel Emelyanov cmd->port = PORT_TP; 58e314dbdcSPavel Emelyanov cmd->phy_address = 0; 59e314dbdcSPavel Emelyanov cmd->transceiver = XCVR_INTERNAL; 60e314dbdcSPavel Emelyanov cmd->autoneg = AUTONEG_DISABLE; 61e314dbdcSPavel Emelyanov cmd->maxtxpkt = 0; 62e314dbdcSPavel Emelyanov cmd->maxrxpkt = 0; 63e314dbdcSPavel Emelyanov return 0; 64e314dbdcSPavel Emelyanov } 65e314dbdcSPavel Emelyanov 66e314dbdcSPavel Emelyanov static void veth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) 67e314dbdcSPavel Emelyanov { 68e314dbdcSPavel Emelyanov strcpy(info->driver, DRV_NAME); 69e314dbdcSPavel Emelyanov strcpy(info->version, DRV_VERSION); 70e314dbdcSPavel Emelyanov strcpy(info->fw_version, "N/A"); 71e314dbdcSPavel Emelyanov } 72e314dbdcSPavel Emelyanov 73e314dbdcSPavel Emelyanov static void veth_get_strings(struct net_device *dev, u32 stringset, u8 *buf) 74e314dbdcSPavel Emelyanov { 75e314dbdcSPavel Emelyanov switch(stringset) { 76e314dbdcSPavel Emelyanov case ETH_SS_STATS: 77e314dbdcSPavel Emelyanov memcpy(buf, ðtool_stats_keys, sizeof(ethtool_stats_keys)); 78e314dbdcSPavel Emelyanov break; 79e314dbdcSPavel Emelyanov } 80e314dbdcSPavel Emelyanov } 81e314dbdcSPavel Emelyanov 82b9f2c044SJeff Garzik static int veth_get_sset_count(struct net_device *dev, int sset) 83e314dbdcSPavel Emelyanov { 84b9f2c044SJeff Garzik switch (sset) { 85b9f2c044SJeff Garzik case ETH_SS_STATS: 86e314dbdcSPavel Emelyanov return ARRAY_SIZE(ethtool_stats_keys); 87b9f2c044SJeff Garzik default: 88b9f2c044SJeff Garzik return -EOPNOTSUPP; 89b9f2c044SJeff Garzik } 90e314dbdcSPavel Emelyanov } 91e314dbdcSPavel Emelyanov 92e314dbdcSPavel Emelyanov static void veth_get_ethtool_stats(struct net_device *dev, 93e314dbdcSPavel Emelyanov struct ethtool_stats *stats, u64 *data) 94e314dbdcSPavel Emelyanov { 95e314dbdcSPavel Emelyanov struct veth_priv *priv; 96e314dbdcSPavel Emelyanov 97e314dbdcSPavel Emelyanov priv = netdev_priv(dev); 98e314dbdcSPavel Emelyanov data[0] = priv->peer->ifindex; 99e314dbdcSPavel Emelyanov } 100e314dbdcSPavel Emelyanov 101e314dbdcSPavel Emelyanov static u32 veth_get_rx_csum(struct net_device *dev) 102e314dbdcSPavel Emelyanov { 103e314dbdcSPavel Emelyanov struct veth_priv *priv; 104e314dbdcSPavel Emelyanov 105e314dbdcSPavel Emelyanov priv = netdev_priv(dev); 106e314dbdcSPavel Emelyanov return priv->ip_summed == CHECKSUM_UNNECESSARY; 107e314dbdcSPavel Emelyanov } 108e314dbdcSPavel Emelyanov 109e314dbdcSPavel Emelyanov static int veth_set_rx_csum(struct net_device *dev, u32 data) 110e314dbdcSPavel Emelyanov { 111e314dbdcSPavel Emelyanov struct veth_priv *priv; 112e314dbdcSPavel Emelyanov 113e314dbdcSPavel Emelyanov priv = netdev_priv(dev); 114e314dbdcSPavel Emelyanov priv->ip_summed = data ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE; 115e314dbdcSPavel Emelyanov return 0; 116e314dbdcSPavel Emelyanov } 117e314dbdcSPavel Emelyanov 118e314dbdcSPavel Emelyanov static u32 veth_get_tx_csum(struct net_device *dev) 119e314dbdcSPavel Emelyanov { 120e314dbdcSPavel Emelyanov return (dev->features & NETIF_F_NO_CSUM) != 0; 121e314dbdcSPavel Emelyanov } 122e314dbdcSPavel Emelyanov 123e314dbdcSPavel Emelyanov static int veth_set_tx_csum(struct net_device *dev, u32 data) 124e314dbdcSPavel Emelyanov { 125e314dbdcSPavel Emelyanov if (data) 126e314dbdcSPavel Emelyanov dev->features |= NETIF_F_NO_CSUM; 127e314dbdcSPavel Emelyanov else 128e314dbdcSPavel Emelyanov dev->features &= ~NETIF_F_NO_CSUM; 129e314dbdcSPavel Emelyanov return 0; 130e314dbdcSPavel Emelyanov } 131e314dbdcSPavel Emelyanov 132e314dbdcSPavel Emelyanov static struct ethtool_ops veth_ethtool_ops = { 133e314dbdcSPavel Emelyanov .get_settings = veth_get_settings, 134e314dbdcSPavel Emelyanov .get_drvinfo = veth_get_drvinfo, 135e314dbdcSPavel Emelyanov .get_link = ethtool_op_get_link, 136e314dbdcSPavel Emelyanov .get_rx_csum = veth_get_rx_csum, 137e314dbdcSPavel Emelyanov .set_rx_csum = veth_set_rx_csum, 138e314dbdcSPavel Emelyanov .get_tx_csum = veth_get_tx_csum, 139e314dbdcSPavel Emelyanov .set_tx_csum = veth_set_tx_csum, 140e314dbdcSPavel Emelyanov .get_sg = ethtool_op_get_sg, 141e314dbdcSPavel Emelyanov .set_sg = ethtool_op_set_sg, 142e314dbdcSPavel Emelyanov .get_strings = veth_get_strings, 143b9f2c044SJeff Garzik .get_sset_count = veth_get_sset_count, 144e314dbdcSPavel Emelyanov .get_ethtool_stats = veth_get_ethtool_stats, 145e314dbdcSPavel Emelyanov }; 146e314dbdcSPavel Emelyanov 147e314dbdcSPavel Emelyanov /* 148e314dbdcSPavel Emelyanov * xmit 149e314dbdcSPavel Emelyanov */ 150e314dbdcSPavel Emelyanov 151e314dbdcSPavel Emelyanov static int veth_xmit(struct sk_buff *skb, struct net_device *dev) 152e314dbdcSPavel Emelyanov { 153e314dbdcSPavel Emelyanov struct net_device *rcv = NULL; 154e314dbdcSPavel Emelyanov struct veth_priv *priv, *rcv_priv; 15538d40815SEric Biederman struct veth_net_stats *stats, *rcv_stats; 156e314dbdcSPavel Emelyanov int length, cpu; 157e314dbdcSPavel Emelyanov 158e314dbdcSPavel Emelyanov skb_orphan(skb); 159e314dbdcSPavel Emelyanov 160e314dbdcSPavel Emelyanov priv = netdev_priv(dev); 161e314dbdcSPavel Emelyanov rcv = priv->peer; 162e314dbdcSPavel Emelyanov rcv_priv = netdev_priv(rcv); 163e314dbdcSPavel Emelyanov 164e314dbdcSPavel Emelyanov cpu = smp_processor_id(); 165e314dbdcSPavel Emelyanov stats = per_cpu_ptr(priv->stats, cpu); 16638d40815SEric Biederman rcv_stats = per_cpu_ptr(rcv_priv->stats, cpu); 167e314dbdcSPavel Emelyanov 168e314dbdcSPavel Emelyanov if (!(rcv->flags & IFF_UP)) 16938d40815SEric Biederman goto tx_drop; 17038d40815SEric Biederman 17138d40815SEric Biederman if (skb->len > (rcv->mtu + MTU_PAD)) 17238d40815SEric Biederman goto rx_drop; 173e314dbdcSPavel Emelyanov 17427a242e9SBen Greear skb->tstamp.tv64 = 0; 175e314dbdcSPavel Emelyanov skb->pkt_type = PACKET_HOST; 176e314dbdcSPavel Emelyanov skb->protocol = eth_type_trans(skb, rcv); 177e314dbdcSPavel Emelyanov if (dev->features & NETIF_F_NO_CSUM) 178e314dbdcSPavel Emelyanov skb->ip_summed = rcv_priv->ip_summed; 179e314dbdcSPavel Emelyanov 180e314dbdcSPavel Emelyanov skb->mark = 0; 181e314dbdcSPavel Emelyanov secpath_reset(skb); 182e314dbdcSPavel Emelyanov nf_reset(skb); 183e314dbdcSPavel Emelyanov 184e314dbdcSPavel Emelyanov length = skb->len; 185e314dbdcSPavel Emelyanov 186e314dbdcSPavel Emelyanov stats->tx_bytes += length; 187e314dbdcSPavel Emelyanov stats->tx_packets++; 188e314dbdcSPavel Emelyanov 18938d40815SEric Biederman rcv_stats->rx_bytes += length; 19038d40815SEric Biederman rcv_stats->rx_packets++; 191e314dbdcSPavel Emelyanov 192e314dbdcSPavel Emelyanov netif_rx(skb); 1936ed10654SPatrick McHardy return NETDEV_TX_OK; 194e314dbdcSPavel Emelyanov 19538d40815SEric Biederman tx_drop: 196e314dbdcSPavel Emelyanov kfree_skb(skb); 197e314dbdcSPavel Emelyanov stats->tx_dropped++; 1986ed10654SPatrick McHardy return NETDEV_TX_OK; 19938d40815SEric Biederman 20038d40815SEric Biederman rx_drop: 20138d40815SEric Biederman kfree_skb(skb); 20238d40815SEric Biederman rcv_stats->rx_dropped++; 2036ed10654SPatrick McHardy return NETDEV_TX_OK; 204e314dbdcSPavel Emelyanov } 205e314dbdcSPavel Emelyanov 206e314dbdcSPavel Emelyanov /* 207e314dbdcSPavel Emelyanov * general routines 208e314dbdcSPavel Emelyanov */ 209e314dbdcSPavel Emelyanov 210e314dbdcSPavel Emelyanov static struct net_device_stats *veth_get_stats(struct net_device *dev) 211e314dbdcSPavel Emelyanov { 21211687a10SDavid S. Miller struct veth_priv *priv; 21311687a10SDavid S. Miller struct net_device_stats *dev_stats; 21411687a10SDavid S. Miller int cpu; 215e314dbdcSPavel Emelyanov struct veth_net_stats *stats; 216e314dbdcSPavel Emelyanov 21711687a10SDavid S. Miller priv = netdev_priv(dev); 21811687a10SDavid S. Miller dev_stats = &dev->stats; 21911687a10SDavid S. Miller 220e314dbdcSPavel Emelyanov dev_stats->rx_packets = 0; 221e314dbdcSPavel Emelyanov dev_stats->tx_packets = 0; 222e314dbdcSPavel Emelyanov dev_stats->rx_bytes = 0; 223e314dbdcSPavel Emelyanov dev_stats->tx_bytes = 0; 224e314dbdcSPavel Emelyanov dev_stats->tx_dropped = 0; 22538d40815SEric Biederman dev_stats->rx_dropped = 0; 226e314dbdcSPavel Emelyanov 227e314dbdcSPavel Emelyanov for_each_online_cpu(cpu) { 228e314dbdcSPavel Emelyanov stats = per_cpu_ptr(priv->stats, cpu); 229e314dbdcSPavel Emelyanov 230e314dbdcSPavel Emelyanov dev_stats->rx_packets += stats->rx_packets; 231e314dbdcSPavel Emelyanov dev_stats->tx_packets += stats->tx_packets; 232e314dbdcSPavel Emelyanov dev_stats->rx_bytes += stats->rx_bytes; 233e314dbdcSPavel Emelyanov dev_stats->tx_bytes += stats->tx_bytes; 234e314dbdcSPavel Emelyanov dev_stats->tx_dropped += stats->tx_dropped; 23538d40815SEric Biederman dev_stats->rx_dropped += stats->rx_dropped; 236e314dbdcSPavel Emelyanov } 237e314dbdcSPavel Emelyanov 238e314dbdcSPavel Emelyanov return dev_stats; 239e314dbdcSPavel Emelyanov } 240e314dbdcSPavel Emelyanov 241e314dbdcSPavel Emelyanov static int veth_open(struct net_device *dev) 242e314dbdcSPavel Emelyanov { 243e314dbdcSPavel Emelyanov struct veth_priv *priv; 244e314dbdcSPavel Emelyanov 245e314dbdcSPavel Emelyanov priv = netdev_priv(dev); 246e314dbdcSPavel Emelyanov if (priv->peer == NULL) 247e314dbdcSPavel Emelyanov return -ENOTCONN; 248e314dbdcSPavel Emelyanov 249e314dbdcSPavel Emelyanov if (priv->peer->flags & IFF_UP) { 250e314dbdcSPavel Emelyanov netif_carrier_on(dev); 251e314dbdcSPavel Emelyanov netif_carrier_on(priv->peer); 252e314dbdcSPavel Emelyanov } 253e314dbdcSPavel Emelyanov return 0; 254e314dbdcSPavel Emelyanov } 255e314dbdcSPavel Emelyanov 2562cf48a10SEric W. Biederman static int veth_close(struct net_device *dev) 2572cf48a10SEric W. Biederman { 2582cf48a10SEric W. Biederman struct veth_priv *priv = netdev_priv(dev); 2592cf48a10SEric W. Biederman 2602cf48a10SEric W. Biederman netif_carrier_off(dev); 2612cf48a10SEric W. Biederman netif_carrier_off(priv->peer); 2622cf48a10SEric W. Biederman 2632cf48a10SEric W. Biederman return 0; 2642cf48a10SEric W. Biederman } 2652cf48a10SEric W. Biederman 26638d40815SEric Biederman static int is_valid_veth_mtu(int new_mtu) 26738d40815SEric Biederman { 26838d40815SEric Biederman return (new_mtu >= MIN_MTU && new_mtu <= MAX_MTU); 26938d40815SEric Biederman } 27038d40815SEric Biederman 27138d40815SEric Biederman static int veth_change_mtu(struct net_device *dev, int new_mtu) 27238d40815SEric Biederman { 27338d40815SEric Biederman if (!is_valid_veth_mtu(new_mtu)) 27438d40815SEric Biederman return -EINVAL; 27538d40815SEric Biederman dev->mtu = new_mtu; 27638d40815SEric Biederman return 0; 27738d40815SEric Biederman } 27838d40815SEric Biederman 279e314dbdcSPavel Emelyanov static int veth_dev_init(struct net_device *dev) 280e314dbdcSPavel Emelyanov { 281e314dbdcSPavel Emelyanov struct veth_net_stats *stats; 282e314dbdcSPavel Emelyanov struct veth_priv *priv; 283e314dbdcSPavel Emelyanov 284e314dbdcSPavel Emelyanov stats = alloc_percpu(struct veth_net_stats); 285e314dbdcSPavel Emelyanov if (stats == NULL) 286e314dbdcSPavel Emelyanov return -ENOMEM; 287e314dbdcSPavel Emelyanov 288e314dbdcSPavel Emelyanov priv = netdev_priv(dev); 289e314dbdcSPavel Emelyanov priv->stats = stats; 290e314dbdcSPavel Emelyanov return 0; 291e314dbdcSPavel Emelyanov } 292e314dbdcSPavel Emelyanov 29311687a10SDavid S. Miller static void veth_dev_free(struct net_device *dev) 29411687a10SDavid S. Miller { 29511687a10SDavid S. Miller struct veth_priv *priv; 29611687a10SDavid S. Miller 29711687a10SDavid S. Miller priv = netdev_priv(dev); 29811687a10SDavid S. Miller free_percpu(priv->stats); 29911687a10SDavid S. Miller free_netdev(dev); 30011687a10SDavid S. Miller } 30111687a10SDavid S. Miller 3024456e7bdSStephen Hemminger static const struct net_device_ops veth_netdev_ops = { 3034456e7bdSStephen Hemminger .ndo_init = veth_dev_init, 3044456e7bdSStephen Hemminger .ndo_open = veth_open, 3052cf48a10SEric W. Biederman .ndo_stop = veth_close, 30600829823SStephen Hemminger .ndo_start_xmit = veth_xmit, 30738d40815SEric Biederman .ndo_change_mtu = veth_change_mtu, 3084456e7bdSStephen Hemminger .ndo_get_stats = veth_get_stats, 309ee923623SDaniel Lezcano .ndo_set_mac_address = eth_mac_addr, 3104456e7bdSStephen Hemminger }; 3114456e7bdSStephen Hemminger 312e314dbdcSPavel Emelyanov static void veth_setup(struct net_device *dev) 313e314dbdcSPavel Emelyanov { 314e314dbdcSPavel Emelyanov ether_setup(dev); 315e314dbdcSPavel Emelyanov 3164456e7bdSStephen Hemminger dev->netdev_ops = &veth_netdev_ops; 317e314dbdcSPavel Emelyanov dev->ethtool_ops = &veth_ethtool_ops; 318e314dbdcSPavel Emelyanov dev->features |= NETIF_F_LLTX; 31911687a10SDavid S. Miller dev->destructor = veth_dev_free; 320e314dbdcSPavel Emelyanov } 321e314dbdcSPavel Emelyanov 322e314dbdcSPavel Emelyanov /* 323e314dbdcSPavel Emelyanov * netlink interface 324e314dbdcSPavel Emelyanov */ 325e314dbdcSPavel Emelyanov 326e314dbdcSPavel Emelyanov static int veth_validate(struct nlattr *tb[], struct nlattr *data[]) 327e314dbdcSPavel Emelyanov { 328e314dbdcSPavel Emelyanov if (tb[IFLA_ADDRESS]) { 329e314dbdcSPavel Emelyanov if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) 330e314dbdcSPavel Emelyanov return -EINVAL; 331e314dbdcSPavel Emelyanov if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) 332e314dbdcSPavel Emelyanov return -EADDRNOTAVAIL; 333e314dbdcSPavel Emelyanov } 33438d40815SEric Biederman if (tb[IFLA_MTU]) { 33538d40815SEric Biederman if (!is_valid_veth_mtu(nla_get_u32(tb[IFLA_MTU]))) 33638d40815SEric Biederman return -EINVAL; 33738d40815SEric Biederman } 338e314dbdcSPavel Emelyanov return 0; 339e314dbdcSPavel Emelyanov } 340e314dbdcSPavel Emelyanov 341e314dbdcSPavel Emelyanov static struct rtnl_link_ops veth_link_ops; 342e314dbdcSPavel Emelyanov 343e314dbdcSPavel Emelyanov static int veth_newlink(struct net_device *dev, 344e314dbdcSPavel Emelyanov struct nlattr *tb[], struct nlattr *data[]) 345e314dbdcSPavel Emelyanov { 346e314dbdcSPavel Emelyanov int err; 347e314dbdcSPavel Emelyanov struct net_device *peer; 348e314dbdcSPavel Emelyanov struct veth_priv *priv; 349e314dbdcSPavel Emelyanov char ifname[IFNAMSIZ]; 350e314dbdcSPavel Emelyanov struct nlattr *peer_tb[IFLA_MAX + 1], **tbp; 351e314dbdcSPavel Emelyanov 352e314dbdcSPavel Emelyanov /* 353e314dbdcSPavel Emelyanov * create and register peer first 354e314dbdcSPavel Emelyanov * 355e314dbdcSPavel Emelyanov * struct ifinfomsg is at the head of VETH_INFO_PEER, but we 356e314dbdcSPavel Emelyanov * skip it since no info from it is useful yet 357e314dbdcSPavel Emelyanov */ 358e314dbdcSPavel Emelyanov 359e314dbdcSPavel Emelyanov if (data != NULL && data[VETH_INFO_PEER] != NULL) { 360e314dbdcSPavel Emelyanov struct nlattr *nla_peer; 361e314dbdcSPavel Emelyanov 362e314dbdcSPavel Emelyanov nla_peer = data[VETH_INFO_PEER]; 363e314dbdcSPavel Emelyanov err = nla_parse(peer_tb, IFLA_MAX, 364e314dbdcSPavel Emelyanov nla_data(nla_peer) + sizeof(struct ifinfomsg), 365e314dbdcSPavel Emelyanov nla_len(nla_peer) - sizeof(struct ifinfomsg), 366e314dbdcSPavel Emelyanov ifla_policy); 367e314dbdcSPavel Emelyanov if (err < 0) 368e314dbdcSPavel Emelyanov return err; 369e314dbdcSPavel Emelyanov 370e314dbdcSPavel Emelyanov err = veth_validate(peer_tb, NULL); 371e314dbdcSPavel Emelyanov if (err < 0) 372e314dbdcSPavel Emelyanov return err; 373e314dbdcSPavel Emelyanov 374e314dbdcSPavel Emelyanov tbp = peer_tb; 375e314dbdcSPavel Emelyanov } else 376e314dbdcSPavel Emelyanov tbp = tb; 377e314dbdcSPavel Emelyanov 378e314dbdcSPavel Emelyanov if (tbp[IFLA_IFNAME]) 379e314dbdcSPavel Emelyanov nla_strlcpy(ifname, tbp[IFLA_IFNAME], IFNAMSIZ); 380e314dbdcSPavel Emelyanov else 381e314dbdcSPavel Emelyanov snprintf(ifname, IFNAMSIZ, DRV_NAME "%%d"); 382e314dbdcSPavel Emelyanov 383c346dca1SYOSHIFUJI Hideaki peer = rtnl_create_link(dev_net(dev), ifname, &veth_link_ops, tbp); 384e314dbdcSPavel Emelyanov if (IS_ERR(peer)) 385e314dbdcSPavel Emelyanov return PTR_ERR(peer); 386e314dbdcSPavel Emelyanov 387e314dbdcSPavel Emelyanov if (tbp[IFLA_ADDRESS] == NULL) 388e314dbdcSPavel Emelyanov random_ether_addr(peer->dev_addr); 389e314dbdcSPavel Emelyanov 390e314dbdcSPavel Emelyanov err = register_netdevice(peer); 391e314dbdcSPavel Emelyanov if (err < 0) 392e314dbdcSPavel Emelyanov goto err_register_peer; 393e314dbdcSPavel Emelyanov 394e314dbdcSPavel Emelyanov netif_carrier_off(peer); 395e314dbdcSPavel Emelyanov 396e314dbdcSPavel Emelyanov /* 397e314dbdcSPavel Emelyanov * register dev last 398e314dbdcSPavel Emelyanov * 399e314dbdcSPavel Emelyanov * note, that since we've registered new device the dev's name 400e314dbdcSPavel Emelyanov * should be re-allocated 401e314dbdcSPavel Emelyanov */ 402e314dbdcSPavel Emelyanov 403e314dbdcSPavel Emelyanov if (tb[IFLA_ADDRESS] == NULL) 404e314dbdcSPavel Emelyanov random_ether_addr(dev->dev_addr); 405e314dbdcSPavel Emelyanov 406e314dbdcSPavel Emelyanov if (tb[IFLA_IFNAME]) 407e314dbdcSPavel Emelyanov nla_strlcpy(dev->name, tb[IFLA_IFNAME], IFNAMSIZ); 408e314dbdcSPavel Emelyanov else 409e314dbdcSPavel Emelyanov snprintf(dev->name, IFNAMSIZ, DRV_NAME "%%d"); 410e314dbdcSPavel Emelyanov 411e314dbdcSPavel Emelyanov if (strchr(dev->name, '%')) { 412e314dbdcSPavel Emelyanov err = dev_alloc_name(dev, dev->name); 413e314dbdcSPavel Emelyanov if (err < 0) 414e314dbdcSPavel Emelyanov goto err_alloc_name; 415e314dbdcSPavel Emelyanov } 416e314dbdcSPavel Emelyanov 417e314dbdcSPavel Emelyanov err = register_netdevice(dev); 418e314dbdcSPavel Emelyanov if (err < 0) 419e314dbdcSPavel Emelyanov goto err_register_dev; 420e314dbdcSPavel Emelyanov 421e314dbdcSPavel Emelyanov netif_carrier_off(dev); 422e314dbdcSPavel Emelyanov 423e314dbdcSPavel Emelyanov /* 424e314dbdcSPavel Emelyanov * tie the deviced together 425e314dbdcSPavel Emelyanov */ 426e314dbdcSPavel Emelyanov 427e314dbdcSPavel Emelyanov priv = netdev_priv(dev); 428e314dbdcSPavel Emelyanov priv->peer = peer; 429e314dbdcSPavel Emelyanov 430e314dbdcSPavel Emelyanov priv = netdev_priv(peer); 431e314dbdcSPavel Emelyanov priv->peer = dev; 432e314dbdcSPavel Emelyanov return 0; 433e314dbdcSPavel Emelyanov 434e314dbdcSPavel Emelyanov err_register_dev: 435e314dbdcSPavel Emelyanov /* nothing to do */ 436e314dbdcSPavel Emelyanov err_alloc_name: 437e314dbdcSPavel Emelyanov unregister_netdevice(peer); 438e314dbdcSPavel Emelyanov return err; 439e314dbdcSPavel Emelyanov 440e314dbdcSPavel Emelyanov err_register_peer: 441e314dbdcSPavel Emelyanov free_netdev(peer); 442e314dbdcSPavel Emelyanov return err; 443e314dbdcSPavel Emelyanov } 444e314dbdcSPavel Emelyanov 445e314dbdcSPavel Emelyanov static void veth_dellink(struct net_device *dev) 446e314dbdcSPavel Emelyanov { 447e314dbdcSPavel Emelyanov struct veth_priv *priv; 448e314dbdcSPavel Emelyanov struct net_device *peer; 449e314dbdcSPavel Emelyanov 450e314dbdcSPavel Emelyanov priv = netdev_priv(dev); 451e314dbdcSPavel Emelyanov peer = priv->peer; 452e314dbdcSPavel Emelyanov 453e314dbdcSPavel Emelyanov unregister_netdevice(dev); 454e314dbdcSPavel Emelyanov unregister_netdevice(peer); 455e314dbdcSPavel Emelyanov } 456e314dbdcSPavel Emelyanov 457e314dbdcSPavel Emelyanov static const struct nla_policy veth_policy[VETH_INFO_MAX + 1]; 458e314dbdcSPavel Emelyanov 459e314dbdcSPavel Emelyanov static struct rtnl_link_ops veth_link_ops = { 460e314dbdcSPavel Emelyanov .kind = DRV_NAME, 461e314dbdcSPavel Emelyanov .priv_size = sizeof(struct veth_priv), 462e314dbdcSPavel Emelyanov .setup = veth_setup, 463e314dbdcSPavel Emelyanov .validate = veth_validate, 464e314dbdcSPavel Emelyanov .newlink = veth_newlink, 465e314dbdcSPavel Emelyanov .dellink = veth_dellink, 466e314dbdcSPavel Emelyanov .policy = veth_policy, 467e314dbdcSPavel Emelyanov .maxtype = VETH_INFO_MAX, 468e314dbdcSPavel Emelyanov }; 469e314dbdcSPavel Emelyanov 470e314dbdcSPavel Emelyanov /* 471e314dbdcSPavel Emelyanov * init/fini 472e314dbdcSPavel Emelyanov */ 473e314dbdcSPavel Emelyanov 474e314dbdcSPavel Emelyanov static __init int veth_init(void) 475e314dbdcSPavel Emelyanov { 476e314dbdcSPavel Emelyanov return rtnl_link_register(&veth_link_ops); 477e314dbdcSPavel Emelyanov } 478e314dbdcSPavel Emelyanov 479e314dbdcSPavel Emelyanov static __exit void veth_exit(void) 480e314dbdcSPavel Emelyanov { 48168365458SPatrick McHardy rtnl_link_unregister(&veth_link_ops); 482e314dbdcSPavel Emelyanov } 483e314dbdcSPavel Emelyanov 484e314dbdcSPavel Emelyanov module_init(veth_init); 485e314dbdcSPavel Emelyanov module_exit(veth_exit); 486e314dbdcSPavel Emelyanov 487e314dbdcSPavel Emelyanov MODULE_DESCRIPTION("Virtual Ethernet Tunnel"); 488e314dbdcSPavel Emelyanov MODULE_LICENSE("GPL v2"); 489e314dbdcSPavel Emelyanov MODULE_ALIAS_RTNL_LINK(DRV_NAME); 490