diff options
-rw-r--r-- | drivers/net/macvlan.c | 80 |
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 | ||
36 | enum macvlan_mode { | ||
37 | MACVLAN_MODE_PRIVATE = 1, | ||
38 | MACVLAN_MODE_VEPA = 2, | ||
39 | MACVLAN_MODE_BRIDGE = 4, | ||
40 | }; | ||
41 | |||
35 | struct macvlan_port { | 42 | struct 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 | ||
136 | static int macvlan_broadcast_one(struct sk_buff *skb, struct net_device *dev, | 144 | static 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 | ||
152 | static void macvlan_broadcast(struct sk_buff *skb, | 163 | static 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 | ||
251 | static 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 | |||
277 | xmit_world: | ||
278 | skb->dev = vlan->lowerdev; | ||
279 | return dev_queue_xmit(skb); | ||
280 | } | ||
281 | |||
215 | static netdev_tx_t macvlan_start_xmit(struct sk_buff *skb, | 282 | static 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; |