aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Braun <michael-dev@fami-braun.de>2014-09-25 10:31:08 -0400
committerDavid S. Miller <davem@davemloft.net>2014-09-29 15:37:01 -0400
commit79cf79abce71eb7dbc40e2f3121048ca5405cb47 (patch)
tree81e5b392884d9b0c2f7e4bbd925f4c7c55229834
parent852248449c73b5ffe109a33d65485c71d3d398a7 (diff)
macvlan: add source mode
This patch adds a new mode of operation to macvlan, called "source". It allows one to set a list of allowed mac address, which is used to match against source mac address from received frames on underlying interface. This enables creating mac based VLAN associations, instead of standard port or tag based. The feature is useful to deploy 802.1x mac based behavior, where drivers of underlying interfaces doesn't allows that. Configuration is done through the netlink interface using e.g.: ip link add link eth0 name macvlan0 type macvlan mode source ip link add link eth0 name macvlan1 type macvlan mode source ip link set link dev macvlan0 type macvlan macaddr add 00:11:11:11:11:11 ip link set link dev macvlan0 type macvlan macaddr add 00:22:22:22:22:22 ip link set link dev macvlan0 type macvlan macaddr add 00:33:33:33:33:33 ip link set link dev macvlan1 type macvlan macaddr add 00:33:33:33:33:33 ip link set link dev macvlan1 type macvlan macaddr add 00:44:44:44:44:44 This allows clients with MAC addresses 00:11:11:11:11:11, 00:22:22:22:22:22 to be part of only VLAN associated with macvlan0 interface. Clients with MAC addresses 00:44:44:44:44:44 with only VLAN associated with macvlan1 interface. And client with MAC address 00:33:33:33:33:33 to be associated with both VLANs. Based on work of Stefan Gula <steweg@gmail.com> v8: last version of Stefan Gula for Kernel 3.2.1 v9: rework onto linux-next 2014-03-12 by Michael Braun add MACADDR_SET command, enable to configure mac for source mode while creating interface v10: - reduce indention level - rename source_list to source_entry - use aligned 64bit ether address - use hash_64 instead of addr[5] v11: - rebase for 3.14 / linux-next 20.04.2014 v12 - rebase for linux-next 2014-09-25 Signed-off-by: Michael Braun <michael-dev@fami-braun.de> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/macvlan.c304
-rw-r--r--include/linux/if_macvlan.h1
-rw-r--r--include/uapi/linux/if_link.h12
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
41struct macvlan_port { 42struct 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
54struct 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
52struct macvlan_skb_cb { 61struct macvlan_skb_cb {
@@ -57,6 +66,20 @@ struct macvlan_skb_cb {
57 66
58static void macvlan_port_destroy(struct net_device *dev); 67static void macvlan_port_destroy(struct net_device *dev);
59 68
69/* Hash Ethernet address */
70static 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
60static struct macvlan_port *macvlan_port_get_rcu(const struct net_device *dev) 83static 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
108static 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
124static 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
84static void macvlan_hash_add(struct macvlan_dev *vlan) 148static 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
157static 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
92static void macvlan_hash_del(struct macvlan_dev *vlan, bool sync) 163static 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
341static 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
361static 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
385static 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)