Commit 5f4a9d1b authored by Patrick McHardy's avatar Patrick McHardy Committed by Adrian Bunk

[IFB]: Fix crash on input device removal

The input_device pointer is not refcounted, which means the device may
disappear while packets are queued, causing a crash when ifb passes packets
with a stale skb->dev pointer to netif_rx().

Fix by storing the interface index instead and do a lookup where neccessary.
Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarAdrian Bunk <bunk@stusta.de>
parent cceec518
...@@ -97,17 +97,24 @@ static void ri_tasklet(unsigned long dev) ...@@ -97,17 +97,24 @@ static void ri_tasklet(unsigned long dev)
skb->tc_verd = SET_TC_NCLS(skb->tc_verd); skb->tc_verd = SET_TC_NCLS(skb->tc_verd);
stats->tx_packets++; stats->tx_packets++;
stats->tx_bytes +=skb->len; stats->tx_bytes +=skb->len;
skb->dev = __dev_get_by_index(skb->iif);
if (!skb->dev) {
dev_kfree_skb(skb);
stats->tx_dropped++;
break;
}
skb->iif = _dev->ifindex;
if (from & AT_EGRESS) { if (from & AT_EGRESS) {
dp->st_rx_frm_egr++; dp->st_rx_frm_egr++;
dev_queue_xmit(skb); dev_queue_xmit(skb);
} else if (from & AT_INGRESS) { } else if (from & AT_INGRESS) {
dp->st_rx_frm_ing++; dp->st_rx_frm_ing++;
skb_pull(skb, skb->dev->hard_header_len);
netif_rx(skb); netif_rx(skb);
} else { } else
dev_kfree_skb(skb); BUG();
stats->tx_dropped++;
}
} }
if (spin_trylock(&_dev->xmit_lock)) { if (spin_trylock(&_dev->xmit_lock)) {
...@@ -158,26 +165,10 @@ static int ifb_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -158,26 +165,10 @@ static int ifb_xmit(struct sk_buff *skb, struct net_device *dev)
stats->tx_packets++; stats->tx_packets++;
stats->tx_bytes+=skb->len; stats->tx_bytes+=skb->len;
if (!from || !skb->input_dev) { if (!(from & (AT_INGRESS|AT_EGRESS)) || !skb->iif) {
dropped:
dev_kfree_skb(skb); dev_kfree_skb(skb);
stats->rx_dropped++; stats->rx_dropped++;
return ret; return ret;
} else {
/*
* note we could be going
* ingress -> egress or
* egress -> ingress
*/
skb->dev = skb->input_dev;
skb->input_dev = dev;
if (from & AT_INGRESS) {
skb_pull(skb, skb->dev->hard_header_len);
} else {
if (!(from & AT_EGRESS)) {
goto dropped;
}
}
} }
if (skb_queue_len(&dp->rq) >= dev->tx_queue_len) { if (skb_queue_len(&dp->rq) >= dev->tx_queue_len) {
......
...@@ -175,7 +175,7 @@ enum { ...@@ -175,7 +175,7 @@ enum {
* @sk: Socket we are owned by * @sk: Socket we are owned by
* @tstamp: Time we arrived * @tstamp: Time we arrived
* @dev: Device we arrived on/are leaving by * @dev: Device we arrived on/are leaving by
* @input_dev: Device we arrived on * @iif: ifindex of device we arrived on
* @h: Transport layer header * @h: Transport layer header
* @nh: Network layer header * @nh: Network layer header
* @mac: Link layer header * @mac: Link layer header
...@@ -219,7 +219,8 @@ struct sk_buff { ...@@ -219,7 +219,8 @@ struct sk_buff {
struct sock *sk; struct sock *sk;
struct skb_timeval tstamp; struct skb_timeval tstamp;
struct net_device *dev; struct net_device *dev;
struct net_device *input_dev; int iif;
/* 4 byte hole on 64 bit*/
union { union {
struct tcphdr *th; struct tcphdr *th;
......
...@@ -352,10 +352,13 @@ tcf_change_indev(struct tcf_proto *tp, char *indev, struct rtattr *indev_tlv) ...@@ -352,10 +352,13 @@ tcf_change_indev(struct tcf_proto *tp, char *indev, struct rtattr *indev_tlv)
static inline int static inline int
tcf_match_indev(struct sk_buff *skb, char *indev) tcf_match_indev(struct sk_buff *skb, char *indev)
{ {
struct net_device *dev;
if (indev[0]) { if (indev[0]) {
if (!skb->input_dev) if (!skb->iif)
return 0; return 0;
if (strcmp(indev, skb->input_dev->name)) dev = __dev_get_by_index(skb->iif);
if (!dev || strcmp(indev, dev->name))
return 0; return 0;
} }
......
...@@ -1557,8 +1557,8 @@ static int ing_filter(struct sk_buff *skb) ...@@ -1557,8 +1557,8 @@ static int ing_filter(struct sk_buff *skb)
if (dev->qdisc_ingress) { if (dev->qdisc_ingress) {
__u32 ttl = (__u32) G_TC_RTTL(skb->tc_verd); __u32 ttl = (__u32) G_TC_RTTL(skb->tc_verd);
if (MAX_RED_LOOP < ttl++) { if (MAX_RED_LOOP < ttl++) {
printk("Redir loop detected Dropping packet (%s->%s)\n", printk("Redir loop detected Dropping packet (%d->%d)\n",
skb->input_dev->name, skb->dev->name); skb->iif, skb->dev->ifindex);
return TC_ACT_SHOT; return TC_ACT_SHOT;
} }
...@@ -1591,8 +1591,8 @@ int netif_receive_skb(struct sk_buff *skb) ...@@ -1591,8 +1591,8 @@ int netif_receive_skb(struct sk_buff *skb)
if (!skb->tstamp.off_sec) if (!skb->tstamp.off_sec)
net_timestamp(skb); net_timestamp(skb);
if (!skb->input_dev) if (!skb->iif)
skb->input_dev = skb->dev; skb->iif = skb->dev->ifindex;
orig_dev = skb_bond(skb); orig_dev = skb_bond(skb);
......
...@@ -443,7 +443,7 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask) ...@@ -443,7 +443,7 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
n->tc_verd = SET_TC_VERD(skb->tc_verd,0); n->tc_verd = SET_TC_VERD(skb->tc_verd,0);
n->tc_verd = CLR_TC_OK2MUNGE(n->tc_verd); n->tc_verd = CLR_TC_OK2MUNGE(n->tc_verd);
n->tc_verd = CLR_TC_MUNGED(n->tc_verd); n->tc_verd = CLR_TC_MUNGED(n->tc_verd);
C(input_dev); C(iif);
#endif #endif
#endif #endif
......
...@@ -207,7 +207,7 @@ tcf_mirred(struct sk_buff *skb, struct tc_action *a, struct tcf_result *res) ...@@ -207,7 +207,7 @@ tcf_mirred(struct sk_buff *skb, struct tc_action *a, struct tcf_result *res)
skb2->tc_verd = SET_TC_FROM(skb2->tc_verd, at); skb2->tc_verd = SET_TC_FROM(skb2->tc_verd, at);
skb2->dev = dev; skb2->dev = dev;
skb2->input_dev = skb->dev; skb2->iif = skb->dev->ifindex;
dev_queue_xmit(skb2); dev_queue_xmit(skb2);
spin_unlock(&p->lock); spin_unlock(&p->lock);
return p->action; return p->action;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment