aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2007-03-29 14:46:52 -0400
committerDavid S. Miller <davem@davemloft.net>2007-03-29 14:46:52 -0400
commitc01003c20563d1e75ec9828d21743919d2b43977 (patch)
tree21cae8933e8a4908d8e8c24244a627bf0c997e77
parentdb8b22550d4b83f0910d27a34d05aa16f7f7159f (diff)
[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: Patrick McHardy <kaber@trash.net> Acked-by: Jamal Hadi Salim <hadi@cyberus.ca> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ifb.c35
-rw-r--r--include/linux/skbuff.h5
-rw-r--r--include/net/pkt_cls.h7
-rw-r--r--net/core/dev.c8
-rw-r--r--net/core/skbuff.c2
-rw-r--r--net/sched/act_mirred.c2
6 files changed, 27 insertions, 32 deletions
diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c
index ca2b21f9d444..07b4c0d7a75c 100644
--- a/drivers/net/ifb.c
+++ b/drivers/net/ifb.c
@@ -96,17 +96,24 @@ static void ri_tasklet(unsigned long dev)
96 skb->tc_verd = SET_TC_NCLS(skb->tc_verd); 96 skb->tc_verd = SET_TC_NCLS(skb->tc_verd);
97 stats->tx_packets++; 97 stats->tx_packets++;
98 stats->tx_bytes +=skb->len; 98 stats->tx_bytes +=skb->len;
99
100 skb->dev = __dev_get_by_index(skb->iif);
101 if (!skb->dev) {
102 dev_kfree_skb(skb);
103 stats->tx_dropped++;
104 break;
105 }
106 skb->iif = _dev->ifindex;
107
99 if (from & AT_EGRESS) { 108 if (from & AT_EGRESS) {
100 dp->st_rx_frm_egr++; 109 dp->st_rx_frm_egr++;
101 dev_queue_xmit(skb); 110 dev_queue_xmit(skb);
102 } else if (from & AT_INGRESS) { 111 } else if (from & AT_INGRESS) {
103
104 dp->st_rx_frm_ing++; 112 dp->st_rx_frm_ing++;
113 skb_pull(skb, skb->dev->hard_header_len);
105 netif_rx(skb); 114 netif_rx(skb);
106 } else { 115 } else
107 dev_kfree_skb(skb); 116 BUG();
108 stats->tx_dropped++;
109 }
110 } 117 }
111 118
112 if (netif_tx_trylock(_dev)) { 119 if (netif_tx_trylock(_dev)) {
@@ -157,26 +164,10 @@ static int ifb_xmit(struct sk_buff *skb, struct net_device *dev)
157 stats->rx_packets++; 164 stats->rx_packets++;
158 stats->rx_bytes+=skb->len; 165 stats->rx_bytes+=skb->len;
159 166
160 if (!from || !skb->input_dev) { 167 if (!(from & (AT_INGRESS|AT_EGRESS)) || !skb->iif) {
161dropped:
162 dev_kfree_skb(skb); 168 dev_kfree_skb(skb);
163 stats->rx_dropped++; 169 stats->rx_dropped++;
164 return ret; 170 return ret;
165 } else {
166 /*
167 * note we could be going
168 * ingress -> egress or
169 * egress -> ingress
170 */
171 skb->dev = skb->input_dev;
172 skb->input_dev = dev;
173 if (from & AT_INGRESS) {
174 skb_pull(skb, skb->dev->hard_header_len);
175 } else {
176 if (!(from & AT_EGRESS)) {
177 goto dropped;
178 }
179 }
180 } 171 }
181 172
182 if (skb_queue_len(&dp->rq) >= dev->tx_queue_len) { 173 if (skb_queue_len(&dp->rq) >= dev->tx_queue_len) {
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 4ff3940210d8..82f43ad478c7 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -188,7 +188,7 @@ enum {
188 * @sk: Socket we are owned by 188 * @sk: Socket we are owned by
189 * @tstamp: Time we arrived 189 * @tstamp: Time we arrived
190 * @dev: Device we arrived on/are leaving by 190 * @dev: Device we arrived on/are leaving by
191 * @input_dev: Device we arrived on 191 * @iif: ifindex of device we arrived on
192 * @h: Transport layer header 192 * @h: Transport layer header
193 * @nh: Network layer header 193 * @nh: Network layer header
194 * @mac: Link layer header 194 * @mac: Link layer header
@@ -235,7 +235,8 @@ struct sk_buff {
235 struct sock *sk; 235 struct sock *sk;
236 struct skb_timeval tstamp; 236 struct skb_timeval tstamp;
237 struct net_device *dev; 237 struct net_device *dev;
238 struct net_device *input_dev; 238 int iif;
239 /* 4 byte hole on 64 bit*/
239 240
240 union { 241 union {
241 struct tcphdr *th; 242 struct tcphdr *th;
diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h
index b902d24a3256..02647fe3d74b 100644
--- a/include/net/pkt_cls.h
+++ b/include/net/pkt_cls.h
@@ -352,10 +352,13 @@ tcf_change_indev(struct tcf_proto *tp, char *indev, struct rtattr *indev_tlv)
352static inline int 352static inline int
353tcf_match_indev(struct sk_buff *skb, char *indev) 353tcf_match_indev(struct sk_buff *skb, char *indev)
354{ 354{
355 struct net_device *dev;
356
355 if (indev[0]) { 357 if (indev[0]) {
356 if (!skb->input_dev) 358 if (!skb->iif)
357 return 0; 359 return 0;
358 if (strcmp(indev, skb->input_dev->name)) 360 dev = __dev_get_by_index(skb->iif);
361 if (!dev || strcmp(indev, dev->name))
359 return 0; 362 return 0;
360 } 363 }
361 364
diff --git a/net/core/dev.c b/net/core/dev.c
index 5984b55311a1..d44b8f1964fa 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1741,8 +1741,8 @@ static int ing_filter(struct sk_buff *skb)
1741 if (dev->qdisc_ingress) { 1741 if (dev->qdisc_ingress) {
1742 __u32 ttl = (__u32) G_TC_RTTL(skb->tc_verd); 1742 __u32 ttl = (__u32) G_TC_RTTL(skb->tc_verd);
1743 if (MAX_RED_LOOP < ttl++) { 1743 if (MAX_RED_LOOP < ttl++) {
1744 printk(KERN_WARNING "Redir loop detected Dropping packet (%s->%s)\n", 1744 printk(KERN_WARNING "Redir loop detected Dropping packet (%d->%d)\n",
1745 skb->input_dev->name, skb->dev->name); 1745 skb->iif, skb->dev->ifindex);
1746 return TC_ACT_SHOT; 1746 return TC_ACT_SHOT;
1747 } 1747 }
1748 1748
@@ -1775,8 +1775,8 @@ int netif_receive_skb(struct sk_buff *skb)
1775 if (!skb->tstamp.off_sec) 1775 if (!skb->tstamp.off_sec)
1776 net_timestamp(skb); 1776 net_timestamp(skb);
1777 1777
1778 if (!skb->input_dev) 1778 if (!skb->iif)
1779 skb->input_dev = skb->dev; 1779 skb->iif = skb->dev->ifindex;
1780 1780
1781 orig_dev = skb_bond(skb); 1781 orig_dev = skb_bond(skb);
1782 1782
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 702fa8f08747..87573ae35b02 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -496,7 +496,7 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
496 n->tc_verd = SET_TC_VERD(skb->tc_verd,0); 496 n->tc_verd = SET_TC_VERD(skb->tc_verd,0);
497 n->tc_verd = CLR_TC_OK2MUNGE(n->tc_verd); 497 n->tc_verd = CLR_TC_OK2MUNGE(n->tc_verd);
498 n->tc_verd = CLR_TC_MUNGED(n->tc_verd); 498 n->tc_verd = CLR_TC_MUNGED(n->tc_verd);
499 C(input_dev); 499 C(iif);
500#endif 500#endif
501 skb_copy_secmark(n, skb); 501 skb_copy_secmark(n, skb);
502#endif 502#endif
diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
index 68f26cb278f9..3e93683e9ab3 100644
--- a/net/sched/act_mirred.c
+++ b/net/sched/act_mirred.c
@@ -198,7 +198,7 @@ bad_mirred:
198 skb2->tc_verd = SET_TC_FROM(skb2->tc_verd, at); 198 skb2->tc_verd = SET_TC_FROM(skb2->tc_verd, at);
199 199
200 skb2->dev = dev; 200 skb2->dev = dev;
201 skb2->input_dev = skb->dev; 201 skb2->iif = skb->dev->ifindex;
202 dev_queue_xmit(skb2); 202 dev_queue_xmit(skb2);
203 spin_unlock(&m->tcf_lock); 203 spin_unlock(&m->tcf_lock);
204 return m->tcf_action; 204 return m->tcf_action;