diff options
| -rw-r--r-- | drivers/net/macvlan.c | 304 | ||||
| -rw-r--r-- | include/linux/if_macvlan.h | 1 | ||||
| -rw-r--r-- | include/uapi/linux/if_link.h | 12 |
3 files changed, 314 insertions, 3 deletions
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 726edabff26b..e8a453f1b458 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c | |||
| @@ -35,7 +35,8 @@ | |||
| 35 | #include <net/xfrm.h> | 35 | #include <net/xfrm.h> |
| 36 | #include <linux/netpoll.h> | 36 | #include <linux/netpoll.h> |
| 37 | 37 | ||
| 38 | #define MACVLAN_HASH_SIZE (1 << BITS_PER_BYTE) | 38 | #define MACVLAN_HASH_BITS 8 |
| 39 | #define MACVLAN_HASH_SIZE (1<<MACVLAN_HASH_BITS) | ||
| 39 | #define MACVLAN_BC_QUEUE_LEN 1000 | 40 | #define MACVLAN_BC_QUEUE_LEN 1000 |
| 40 | 41 | ||
| 41 | struct macvlan_port { | 42 | struct macvlan_port { |
| @@ -47,6 +48,14 @@ struct macvlan_port { | |||
| 47 | struct work_struct bc_work; | 48 | struct work_struct bc_work; |
| 48 | bool passthru; | 49 | bool passthru; |
| 49 | int count; | 50 | int count; |
| 51 | struct hlist_head vlan_source_hash[MACVLAN_HASH_SIZE]; | ||
| 52 | }; | ||
| 53 | |||
| 54 | struct macvlan_source_entry { | ||
| 55 | struct hlist_node hlist; | ||
| 56 | struct macvlan_dev *vlan; | ||
| 57 | unsigned char addr[6+2] __aligned(sizeof(u16)); | ||
| 58 | struct rcu_head rcu; | ||
| 50 | }; | 59 | }; |
| 51 | 60 | ||
| 52 | struct macvlan_skb_cb { | 61 | struct macvlan_skb_cb { |
| @@ -57,6 +66,20 @@ struct macvlan_skb_cb { | |||
| 57 | 66 | ||
| 58 | static void macvlan_port_destroy(struct net_device *dev); | 67 | static void macvlan_port_destroy(struct net_device *dev); |
| 59 | 68 | ||
| 69 | /* Hash Ethernet address */ | ||
| 70 | static u32 macvlan_eth_hash(const unsigned char *addr) | ||
| 71 | { | ||
| 72 | u64 value = get_unaligned((u64 *)addr); | ||
| 73 | |||
| 74 | /* only want 6 bytes */ | ||
| 75 | #ifdef __BIG_ENDIAN | ||
| 76 | value >>= 16; | ||
| 77 | #else | ||
| 78 | value <<= 16; | ||
| 79 | #endif | ||
| 80 | return hash_64(value, MACVLAN_HASH_BITS); | ||
| 81 | } | ||
| 82 | |||
| 60 | static struct macvlan_port *macvlan_port_get_rcu(const struct net_device *dev) | 83 | static struct macvlan_port *macvlan_port_get_rcu(const struct net_device *dev) |
| 61 | { | 84 | { |
| 62 | return rcu_dereference(dev->rx_handler_data); | 85 | return rcu_dereference(dev->rx_handler_data); |
| @@ -73,20 +96,68 @@ static struct macvlan_dev *macvlan_hash_lookup(const struct macvlan_port *port, | |||
| 73 | const unsigned char *addr) | 96 | const unsigned char *addr) |
| 74 | { | 97 | { |
| 75 | struct macvlan_dev *vlan; | 98 | struct macvlan_dev *vlan; |
| 99 | u32 idx = macvlan_eth_hash(addr); | ||
| 76 | 100 | ||
| 77 | hlist_for_each_entry_rcu(vlan, &port->vlan_hash[addr[5]], hlist) { | 101 | hlist_for_each_entry_rcu(vlan, &port->vlan_hash[idx], hlist) { |
| 78 | if (ether_addr_equal_64bits(vlan->dev->dev_addr, addr)) | 102 | if (ether_addr_equal_64bits(vlan->dev->dev_addr, addr)) |
| 79 | return vlan; | 103 | return vlan; |
| 80 | } | 104 | } |
| 81 | return NULL; | 105 | return NULL; |
| 82 | } | 106 | } |
| 83 | 107 | ||
| 108 | static struct macvlan_source_entry *macvlan_hash_lookup_source( | ||
| 109 | const struct macvlan_dev *vlan, | ||
| 110 | const unsigned char *addr) | ||
| 111 | { | ||
| 112 | struct macvlan_source_entry *entry; | ||
| 113 | u32 idx = macvlan_eth_hash(addr); | ||
| 114 | struct hlist_head *h = &vlan->port->vlan_source_hash[idx]; | ||
| 115 | |||
| 116 | hlist_for_each_entry_rcu(entry, h, hlist) { | ||
| 117 | if (ether_addr_equal_64bits(entry->addr, addr) && | ||
| 118 | entry->vlan == vlan) | ||
| 119 | return entry; | ||
| 120 | } | ||
| 121 | return NULL; | ||
| 122 | } | ||
| 123 | |||
| 124 | static int macvlan_hash_add_source(struct macvlan_dev *vlan, | ||
| 125 | const unsigned char *addr) | ||
| 126 | { | ||
| 127 | struct macvlan_port *port = vlan->port; | ||
| 128 | struct macvlan_source_entry *entry; | ||
| 129 | struct hlist_head *h; | ||
| 130 | |||
| 131 | entry = macvlan_hash_lookup_source(vlan, addr); | ||
| 132 | if (entry) | ||
| 133 | return 0; | ||
| 134 | |||
| 135 | entry = kmalloc(sizeof(*entry), GFP_KERNEL); | ||
| 136 | if (!entry) | ||
| 137 | return -ENOMEM; | ||
| 138 | |||
| 139 | ether_addr_copy(entry->addr, addr); | ||
| 140 | entry->vlan = vlan; | ||
| 141 | h = &port->vlan_source_hash[macvlan_eth_hash(addr)]; | ||
| 142 | hlist_add_head_rcu(&entry->hlist, h); | ||
| 143 | vlan->macaddr_count++; | ||
| 144 | |||
| 145 | return 0; | ||
| 146 | } | ||
| 147 | |||
| 84 | static void macvlan_hash_add(struct macvlan_dev *vlan) | 148 | static void macvlan_hash_add(struct macvlan_dev *vlan) |
| 85 | { | 149 | { |
| 86 | struct macvlan_port *port = vlan->port; | 150 | struct macvlan_port *port = vlan->port; |
| 87 | const unsigned char *addr = vlan->dev->dev_addr; | 151 | const unsigned char *addr = vlan->dev->dev_addr; |
| 152 | u32 idx = macvlan_eth_hash(addr); | ||
| 88 | 153 | ||
| 89 | hlist_add_head_rcu(&vlan->hlist, &port->vlan_hash[addr[5]]); | 154 | hlist_add_head_rcu(&vlan->hlist, &port->vlan_hash[idx]); |
| 155 | } | ||
| 156 | |||
| 157 | static void macvlan_hash_del_source(struct macvlan_source_entry *entry) | ||
| 158 | { | ||
| 159 | hlist_del_rcu(&entry->hlist); | ||
| 160 | kfree_rcu(entry, rcu); | ||
| 90 | } | 161 | } |
| 91 | 162 | ||
| 92 | static void macvlan_hash_del(struct macvlan_dev *vlan, bool sync) | 163 | static void macvlan_hash_del(struct macvlan_dev *vlan, bool sync) |
| @@ -267,6 +338,65 @@ err: | |||
| 267 | atomic_long_inc(&skb->dev->rx_dropped); | 338 | atomic_long_inc(&skb->dev->rx_dropped); |
| 268 | } | 339 | } |
| 269 | 340 | ||
| 341 | static void macvlan_flush_sources(struct macvlan_port *port, | ||
| 342 | struct macvlan_dev *vlan) | ||
| 343 | { | ||
| 344 | int i; | ||
| 345 | |||
| 346 | for (i = 0; i < MACVLAN_HASH_SIZE; i++) { | ||
| 347 | struct hlist_node *h, *n; | ||
| 348 | |||
| 349 | hlist_for_each_safe(h, n, &port->vlan_source_hash[i]) { | ||
| 350 | struct macvlan_source_entry *entry; | ||
| 351 | |||
| 352 | entry = hlist_entry(h, struct macvlan_source_entry, | ||
| 353 | hlist); | ||
| 354 | if (entry->vlan == vlan) | ||
| 355 | macvlan_hash_del_source(entry); | ||
| 356 | } | ||
| 357 | } | ||
| 358 | vlan->macaddr_count = 0; | ||
| 359 | } | ||
| 360 | |||
| 361 | static void macvlan_forward_source_one(struct sk_buff *skb, | ||
| 362 | struct macvlan_dev *vlan) | ||
| 363 | { | ||
| 364 | struct sk_buff *nskb; | ||
| 365 | struct net_device *dev; | ||
| 366 | int len; | ||
| 367 | int ret; | ||
| 368 | |||
| 369 | dev = vlan->dev; | ||
| 370 | if (unlikely(!(dev->flags & IFF_UP))) | ||
| 371 | return; | ||
| 372 | |||
| 373 | nskb = skb_clone(skb, GFP_ATOMIC); | ||
| 374 | if (!nskb) | ||
| 375 | return; | ||
| 376 | |||
| 377 | len = nskb->len + ETH_HLEN; | ||
| 378 | nskb->dev = dev; | ||
| 379 | nskb->pkt_type = PACKET_HOST; | ||
| 380 | |||
| 381 | ret = netif_rx(nskb); | ||
| 382 | macvlan_count_rx(vlan, len, ret == NET_RX_SUCCESS, 0); | ||
| 383 | } | ||
| 384 | |||
| 385 | static void macvlan_forward_source(struct sk_buff *skb, | ||
| 386 | struct macvlan_port *port, | ||
| 387 | const unsigned char *addr) | ||
| 388 | { | ||
| 389 | struct macvlan_source_entry *entry; | ||
| 390 | u32 idx = macvlan_eth_hash(addr); | ||
| 391 | struct hlist_head *h = &port->vlan_source_hash[idx]; | ||
| 392 | |||
| 393 | hlist_for_each_entry_rcu(entry, h, hlist) { | ||
| 394 | if (ether_addr_equal_64bits(entry->addr, addr)) | ||
| 395 | if (entry->vlan->dev->flags & IFF_UP) | ||
| 396 | macvlan_forward_source_one(skb, entry->vlan); | ||
| 397 | } | ||
| 398 | } | ||
| 399 | |||
| 270 | /* called under rcu_read_lock() from netif_receive_skb */ | 400 | /* called under rcu_read_lock() from netif_receive_skb */ |
| 271 | static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb) | 401 | static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb) |
| 272 | { | 402 | { |
| @@ -285,6 +415,7 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb) | |||
| 285 | if (!skb) | 415 | if (!skb) |
| 286 | return RX_HANDLER_CONSUMED; | 416 | return RX_HANDLER_CONSUMED; |
