aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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;