aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorEric Dumazet <edumazet@google.com>2016-12-21 21:00:24 -0500
committerDavid S. Miller <davem@davemloft.net>2016-12-23 17:53:47 -0500
commitb1227d019fa98c43381ad8827baf7efbe2923ed1 (patch)
tree576fad624349c35feded5a554103bf1c4cf5a68e /drivers
parent50b17cfb1917b207612327d354e9043dbcbde431 (diff)
ipvlan: fix various issues in ipvlan_process_multicast()
1) netif_rx() / dev_forward_skb() should not be called from process context. 2) ipvlan_count_rx() should be called with preemption disabled. 3) We should check if ipvlan->dev is up before feeding packets to netif_rx() 4) We need to prevent device from disappearing if some packets are in the multicast backlog. 5) One kfree_skb() should be a consume_skb() eventually Fixes: ba35f8588f47 ("ipvlan: Defer multicast / broadcast processing to a work-queue") Signed-off-by: Eric Dumazet <edumazet@google.com> Cc: Mahesh Bandewar <maheshb@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/ipvlan/ipvlan_core.c38
-rw-r--r--drivers/net/ipvlan/ipvlan_main.c7
2 files changed, 31 insertions, 14 deletions
diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c
index b4e990743e1d..ea6bc1e12cdf 100644
--- a/drivers/net/ipvlan/ipvlan_core.c
+++ b/drivers/net/ipvlan/ipvlan_core.c
@@ -207,6 +207,9 @@ void ipvlan_process_multicast(struct work_struct *work)
207 spin_unlock_bh(&port->backlog.lock); 207 spin_unlock_bh(&port->backlog.lock);
208 208
209 while ((skb = __skb_dequeue(&list)) != NULL) { 209 while ((skb = __skb_dequeue(&list)) != NULL) {
210 struct net_device *dev = skb->dev;
211 bool consumed = false;
212
210 ethh = eth_hdr(skb); 213 ethh = eth_hdr(skb);
211 hlocal = ether_addr_equal(ethh->h_source, port->dev->dev_addr); 214 hlocal = ether_addr_equal(ethh->h_source, port->dev->dev_addr);
212 mac_hash = ipvlan_mac_hash(ethh->h_dest); 215 mac_hash = ipvlan_mac_hash(ethh->h_dest);
@@ -219,27 +222,29 @@ void ipvlan_process_multicast(struct work_struct *work)
219 dlocal = false; 222 dlocal = false;
220 rcu_read_lock(); 223 rcu_read_lock();
221 list_for_each_entry_rcu(ipvlan, &port->ipvlans, pnode) { 224 list_for_each_entry_rcu(ipvlan, &port->ipvlans, pnode) {
222 if (hlocal && (ipvlan->dev == skb->dev)) { 225 if (hlocal && (ipvlan->dev == dev)) {
223 dlocal = true; 226 dlocal = true;
224 continue; 227 continue;
225 } 228 }
226 if (!test_bit(mac_hash, ipvlan->mac_filters)) 229 if (!test_bit(mac_hash, ipvlan->mac_filters))
227 continue; 230 continue;
228 231 if (!(ipvlan->dev->flags & IFF_UP))
232 continue;
229 ret = NET_RX_DROP; 233 ret = NET_RX_DROP;
230 len = skb->len + ETH_HLEN; 234 len = skb->len + ETH_HLEN;
231 nskb = skb_clone(skb, GFP_ATOMIC); 235 nskb = skb_clone(skb, GFP_ATOMIC);
232 if (!nskb) 236 local_bh_disable();
233 goto acct; 237 if (nskb) {
234 238 consumed = true;
235 nskb->pkt_type = pkt_type; 239 nskb->pkt_type = pkt_type;
236 nskb->dev = ipvlan->dev; 240 nskb->dev = ipvlan->dev;
237 if (hlocal) 241 if (hlocal)
238 ret = dev_forward_skb(ipvlan->dev, nskb); 242 ret = dev_forward_skb(ipvlan->dev, nskb);
239 else 243 else
240 ret = netif_rx(nskb); 244 ret = netif_rx(nskb);
241acct: 245 }
242 ipvlan_count_rx(ipvlan, len, ret == NET_RX_SUCCESS, true); 246 ipvlan_count_rx(ipvlan, len, ret == NET_RX_SUCCESS, true);
247 local_bh_enable();
243 } 248 }
244 rcu_read_unlock(); 249 rcu_read_unlock();
245 250
@@ -249,8 +254,13 @@ acct:
249 skb->pkt_type = pkt_type; 254 skb->pkt_type = pkt_type;
250 dev_queue_xmit(skb); 255 dev_queue_xmit(skb);
251 } else { 256 } else {
252 kfree_skb(skb); 257 if (consumed)
258 consume_skb(skb);
259 else
260 kfree_skb(skb);
253 } 261 }
262 if (dev)
263 dev_put(dev);
254 } 264 }
255} 265}
256 266
@@ -479,6 +489,8 @@ static void ipvlan_multicast_enqueue(struct ipvl_port *port,
479 489
480 spin_lock(&port->backlog.lock); 490 spin_lock(&port->backlog.lock);
481 if (skb_queue_len(&port->backlog) < IPVLAN_QBACKLOG_LIMIT) { 491 if (skb_queue_len(&port->backlog) < IPVLAN_QBACKLOG_LIMIT) {
492 if (skb->dev)
493 dev_hold(skb->dev);
482 __skb_queue_tail(&port->backlog, skb); 494 __skb_queue_tail(&port->backlog, skb);
483 spin_unlock(&port->backlog.lock); 495 spin_unlock(&port->backlog.lock);
484 schedule_work(&port->wq); 496 schedule_work(&port->wq);
diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c
index 693ec5b66222..8b0f99300cbc 100644
--- a/drivers/net/ipvlan/ipvlan_main.c
+++ b/drivers/net/ipvlan/ipvlan_main.c
@@ -135,6 +135,7 @@ err:
135static void ipvlan_port_destroy(struct net_device *dev) 135static void ipvlan_port_destroy(struct net_device *dev)
136{ 136{
137 struct ipvl_port *port = ipvlan_port_get_rtnl(dev); 137 struct ipvl_port *port = ipvlan_port_get_rtnl(dev);
138 struct sk_buff *skb;
138 139
139 dev->priv_flags &= ~IFF_IPVLAN_MASTER; 140 dev->priv_flags &= ~IFF_IPVLAN_MASTER;
140 if (port->mode == IPVLAN_MODE_L3S) { 141 if (port->mode == IPVLAN_MODE_L3S) {
@@ -144,7 +145,11 @@ static void ipvlan_port_destroy(struct net_device *dev)
144 } 145 }
145 netdev_rx_handler_unregister(dev); 146 netdev_rx_handler_unregister(dev);
146 cancel_work_sync(&port->wq); 147 cancel_work_sync(&port->wq);
147 __skb_queue_purge(&port->backlog); 148 while ((skb = __skb_dequeue(&port->backlog)) != NULL) {
149 if (skb->dev)
150 dev_put(skb->dev);
151 kfree_skb(skb);
152 }
148 kfree(port); 153 kfree(port);
149} 154}
150 155