aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/macvlan.c
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2009-11-26 01:07:10 -0500
committerDavid S. Miller <davem@davemloft.net>2009-11-26 18:53:07 -0500
commit618e1b7482f7a8a4c6c6e8ccbe140e4c331df4e9 (patch)
tree2a7c40c7049f803593735ceccdc7d2d8948e00cb /drivers/net/macvlan.c
parenta1e514c5d0397b5581721aad9b303f7df83b103d (diff)
macvlan: implement bridge, VEPA and private mode
This allows each macvlan slave device to be in one of three modes, depending on the use case: MACVLAN_PRIVATE: The device never communicates with any other device on the same upper_dev. This even includes frames coming back from a reflective relay, where supported by the adjacent bridge. MACVLAN_VEPA: The new Virtual Ethernet Port Aggregator (VEPA) mode, we assume that the adjacent bridge returns all frames where both source and destination are local to the macvlan port, i.e. the bridge is set up as a reflective relay. Broadcast frames coming in from the upper_dev get flooded to all macvlan interfaces in VEPA mode. We never deliver any frames locally. MACVLAN_BRIDGE: We provide the behavior of a simple bridge between different macvlan interfaces on the same port. Frames from one interface to another one get delivered directly and are not sent out externally. Broadcast frames get flooded to all other bridge ports and to the external interface, but when they come back from a reflective relay, we don't deliver them again. Since we know all the MAC addresses, the macvlan bridge mode does not require learning or STP like the bridge module does. Based on an earlier patch "macvlan: Reflect macvlan packets meant for other macvlan devices" by Eric Biederman. Signed-off-by: Arnd Bergmann <arnd@arndb.de> Acked-by: Patrick McHardy <kaber@trash.net> Cc: Eric Biederman <ebiederm@xmission.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/macvlan.c')
-rw-r--r--drivers/net/macvlan.c80
1 files changed, 72 insertions, 8 deletions
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index 1e7faf9e87b2..d6bd84353f44 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -29,9 +29,16 @@
29#include <linux/if_link.h> 29#include <linux/if_link.h>
30#include <linux/if_macvlan.h> 30#include <linux/if_macvlan.h>
31#include <net/rtnetlink.h> 31#include <net/rtnetlink.h>
32#include <net/xfrm.h>
32 33
33#define MACVLAN_HASH_SIZE (1 << BITS_PER_BYTE) 34#define MACVLAN_HASH_SIZE (1 << BITS_PER_BYTE)
34 35
36enum macvlan_mode {
37 MACVLAN_MODE_PRIVATE = 1,
38 MACVLAN_MODE_VEPA = 2,
39 MACVLAN_MODE_BRIDGE = 4,
40};
41
35struct macvlan_port { 42struct macvlan_port {
36 struct net_device *dev; 43 struct net_device *dev;
37 struct hlist_head vlan_hash[MACVLAN_HASH_SIZE]; 44 struct hlist_head vlan_hash[MACVLAN_HASH_SIZE];
@@ -59,6 +66,7 @@ struct macvlan_dev {
59 struct macvlan_port *port; 66 struct macvlan_port *port;
60 struct net_device *lowerdev; 67 struct net_device *lowerdev;
61 struct macvlan_rx_stats *rx_stats; 68 struct macvlan_rx_stats *rx_stats;
69 enum macvlan_mode mode;
62}; 70};
63 71
64 72
@@ -134,11 +142,14 @@ static inline void macvlan_count_rx(const struct macvlan_dev *vlan,
134} 142}
135 143
136static int macvlan_broadcast_one(struct sk_buff *skb, struct net_device *dev, 144static int macvlan_broadcast_one(struct sk_buff *skb, struct net_device *dev,
137 const struct ethhdr *eth) 145 const struct ethhdr *eth, bool local)
138{ 146{
139 if (!skb) 147 if (!skb)
140 return NET_RX_DROP; 148 return NET_RX_DROP;
141 149
150 if (local)
151 return dev_forward_skb(dev, skb);
152
142 skb->dev = dev; 153 skb->dev = dev;
143 if (!compare_ether_addr_64bits(eth->h_dest, 154 if (!compare_ether_addr_64bits(eth->h_dest,
144 dev->broadcast)) 155 dev->broadcast))
@@ -150,7 +161,9 @@ static int macvlan_broadcast_one(struct sk_buff *skb, struct net_device *dev,
150} 161}
151 162
152static void macvlan_broadcast(struct sk_buff *skb, 163static void macvlan_broadcast(struct sk_buff *skb,
153 const struct macvlan_port *port) 164 const struct macvlan_port *port,
165 struct net_device *src,
166 enum macvlan_mode mode)
154{ 167{
155 const struct ethhdr *eth = eth_hdr(skb); 168 const struct ethhdr *eth = eth_hdr(skb);
156 const struct macvlan_dev *vlan; 169 const struct macvlan_dev *vlan;
@@ -164,8 +177,12 @@ static void macvlan_broadcast(struct sk_buff *skb,
164 177
165 for (i = 0; i < MACVLAN_HASH_SIZE; i++) { 178 for (i = 0; i < MACVLAN_HASH_SIZE; i++) {
166 hlist_for_each_entry_rcu(vlan, n, &port->vlan_hash[i], hlist) { 179 hlist_for_each_entry_rcu(vlan, n, &port->vlan_hash[i], hlist) {
180 if (vlan->dev == src || !(vlan->mode & mode))
181 continue;
182
167 nskb = skb_clone(skb, GFP_ATOMIC); 183 nskb = skb_clone(skb, GFP_ATOMIC);
168 err = macvlan_broadcast_one(nskb, vlan->dev, eth); 184 err = macvlan_broadcast_one(nskb, vlan->dev, eth,
185 mode == MACVLAN_MODE_BRIDGE);
169 macvlan_count_rx(vlan, skb->len + ETH_HLEN, 186 macvlan_count_rx(vlan, skb->len + ETH_HLEN,
170 err == NET_RX_SUCCESS, 1); 187 err == NET_RX_SUCCESS, 1);
171 } 188 }
@@ -178,6 +195,7 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb)
178 const struct ethhdr *eth = eth_hdr(skb); 195 const struct ethhdr *eth = eth_hdr(skb);
179 const struct macvlan_port *port; 196 const struct macvlan_port *port;
180 const struct macvlan_dev *vlan; 197 const struct macvlan_dev *vlan;
198 const struct macvlan_dev *src;
181 struct net_device *dev; 199 struct net_device *dev;
182 unsigned int len; 200 unsigned int len;
183 201
@@ -186,7 +204,25 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb)
186 return skb; 204 return skb;
187 205
188 if (is_multicast_ether_addr(eth->h_dest)) { 206 if (is_multicast_ether_addr(eth->h_dest)) {
189 macvlan_broadcast(skb, port); 207 src = macvlan_hash_lookup(port, eth->h_source);
208 if (!src)
209 /* frame comes from an external address */
210 macvlan_broadcast(skb, port, NULL,
211 MACVLAN_MODE_PRIVATE |
212 MACVLAN_MODE_VEPA |
213 MACVLAN_MODE_BRIDGE);
214 else if (src->mode == MACVLAN_MODE_VEPA)
215 /* flood to everyone except source */
216 macvlan_broadcast(skb, port, src->dev,
217 MACVLAN_MODE_VEPA |
218 MACVLAN_MODE_BRIDGE);
219 else if (src->mode == MACVLAN_MODE_BRIDGE)
220 /*
221 * flood only to VEPA ports, bridge ports
222 * already saw the frame on the way out.
223 */
224 macvlan_broadcast(skb, port, src->dev,
225 MACVLAN_MODE_VEPA);
190 return skb; 226 return skb;
191 } 227 }
192 228
@@ -212,18 +248,46 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb)
212 return NULL; 248 return NULL;
213} 249}
214 250
251static int macvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev)
252{
253 const struct macvlan_dev *vlan = netdev_priv(dev);
254 const struct macvlan_port *port = vlan->port;
255 const struct macvlan_dev *dest;
256
257 if (vlan->mode == MACVLAN_MODE_BRIDGE) {
258 const struct ethhdr *eth = (void *)skb->data;
259
260 /* send to other bridge ports directly */
261 if (is_multicast_ether_addr(eth->h_dest)) {
262 macvlan_broadcast(skb, port, dev, MACVLAN_MODE_BRIDGE);
263 goto xmit_world;
264 }
265
266 dest = macvlan_hash_lookup(port, eth->h_dest);
267 if (dest && dest->mode == MACVLAN_MODE_BRIDGE) {
268 unsigned int length = skb->len + ETH_HLEN;
269 int ret = dev_forward_skb(dest->dev, skb);
270 macvlan_count_rx(dest, length,
271 ret == NET_RX_SUCCESS, 0);
272
273 return NET_XMIT_SUCCESS;
274 }
275 }
276
277xmit_world:
278 skb->dev = vlan->lowerdev;
279 return dev_queue_xmit(skb);
280}
281
215static netdev_tx_t macvlan_start_xmit(struct sk_buff *skb, 282static netdev_tx_t macvlan_start_xmit(struct sk_buff *skb,
216 struct net_device *dev) 283 struct net_device *dev)
217{ 284{
218 int i = skb_get_queue_mapping(skb); 285 int i = skb_get_queue_mapping(skb);
219 struct netdev_queue *txq = netdev_get_tx_queue(dev, i); 286 struct netdev_queue *txq = netdev_get_tx_queue(dev, i);
220 const struct macvlan_dev *vlan = netdev_priv(dev);
221 unsigned int len = skb->len; 287 unsigned int len = skb->len;
222 int ret; 288 int ret;
223 289
224 skb->dev = vlan->lowerdev; 290 ret = macvlan_queue_xmit(skb, dev);
225 ret = dev_queue_xmit(skb);
226
227 if (likely(ret == NET_XMIT_SUCCESS)) { 291 if (likely(ret == NET_XMIT_SUCCESS)) {
228 txq->tx_packets++; 292 txq->tx_packets++;
229 txq->tx_bytes += len; 293 txq->tx_bytes += len;