diff options
Diffstat (limited to 'net/sched/act_mirred.c')
-rw-r--r-- | net/sched/act_mirred.c | 107 |
1 files changed, 56 insertions, 51 deletions
diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index b9aaab4e0354..d329170243cb 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c | |||
@@ -65,48 +65,53 @@ static int tcf_mirred_init(struct nlattr *nla, struct nlattr *est, | |||
65 | struct tc_mirred *parm; | 65 | struct tc_mirred *parm; |
66 | struct tcf_mirred *m; | 66 | struct tcf_mirred *m; |
67 | struct tcf_common *pc; | 67 | struct tcf_common *pc; |
68 | struct net_device *dev = NULL; | 68 | struct net_device *dev; |
69 | int ret = 0, err; | 69 | int ret, ok_push = 0; |
70 | int ok_push = 0; | ||
71 | 70 | ||
72 | if (nla == NULL) | 71 | if (nla == NULL) |
73 | return -EINVAL; | 72 | return -EINVAL; |
74 | 73 | ret = nla_parse_nested(tb, TCA_MIRRED_MAX, nla, mirred_policy); | |
75 | err = nla_parse_nested(tb, TCA_MIRRED_MAX, nla, mirred_policy); | 74 | if (ret < 0) |
76 | if (err < 0) | 75 | return ret; |
77 | return err; | ||
78 | |||
79 | if (tb[TCA_MIRRED_PARMS] == NULL) | 76 | if (tb[TCA_MIRRED_PARMS] == NULL) |
80 | return -EINVAL; | 77 | return -EINVAL; |
81 | parm = nla_data(tb[TCA_MIRRED_PARMS]); | 78 | parm = nla_data(tb[TCA_MIRRED_PARMS]); |
82 | 79 | switch (parm->eaction) { | |
80 | case TCA_EGRESS_MIRROR: | ||
81 | case TCA_EGRESS_REDIR: | ||
82 | break; | ||
83 | default: | ||
84 | return -EINVAL; | ||
85 | } | ||
83 | if (parm->ifindex) { | 86 | if (parm->ifindex) { |
84 | dev = __dev_get_by_index(&init_net, parm->ifindex); | 87 | dev = __dev_get_by_index(&init_net, parm->ifindex); |
85 | if (dev == NULL) | 88 | if (dev == NULL) |
86 | return -ENODEV; | 89 | return -ENODEV; |
87 | switch (dev->type) { | 90 | switch (dev->type) { |
88 | case ARPHRD_TUNNEL: | 91 | case ARPHRD_TUNNEL: |
89 | case ARPHRD_TUNNEL6: | 92 | case ARPHRD_TUNNEL6: |
90 | case ARPHRD_SIT: | 93 | case ARPHRD_SIT: |
91 | case ARPHRD_IPGRE: | 94 | case ARPHRD_IPGRE: |
92 | case ARPHRD_VOID: | 95 | case ARPHRD_VOID: |
93 | case ARPHRD_NONE: | 96 | case ARPHRD_NONE: |
94 | ok_push = 0; | 97 | ok_push = 0; |
95 | break; | 98 | break; |
96 | default: | 99 | default: |
97 | ok_push = 1; | 100 | ok_push = 1; |
98 | break; | 101 | break; |
99 | } | 102 | } |
103 | } else { | ||
104 | dev = NULL; | ||
100 | } | 105 | } |
101 | 106 | ||
102 | pc = tcf_hash_check(parm->index, a, bind, &mirred_hash_info); | 107 | pc = tcf_hash_check(parm->index, a, bind, &mirred_hash_info); |
103 | if (!pc) { | 108 | if (!pc) { |
104 | if (!parm->ifindex) | 109 | if (dev == NULL) |
105 | return -EINVAL; | 110 | return -EINVAL; |
106 | pc = tcf_hash_create(parm->index, est, a, sizeof(*m), bind, | 111 | pc = tcf_hash_create(parm->index, est, a, sizeof(*m), bind, |
107 | &mirred_idx_gen, &mirred_hash_info); | 112 | &mirred_idx_gen, &mirred_hash_info); |
108 | if (IS_ERR(pc)) | 113 | if (IS_ERR(pc)) |
109 | return PTR_ERR(pc); | 114 | return PTR_ERR(pc); |
110 | ret = ACT_P_CREATED; | 115 | ret = ACT_P_CREATED; |
111 | } else { | 116 | } else { |
112 | if (!ovr) { | 117 | if (!ovr) { |
@@ -119,12 +124,12 @@ static int tcf_mirred_init(struct nlattr *nla, struct nlattr *est, | |||
119 | spin_lock_bh(&m->tcf_lock); | 124 | spin_lock_bh(&m->tcf_lock); |
120 | m->tcf_action = parm->action; | 125 | m->tcf_action = parm->action; |
121 | m->tcfm_eaction = parm->eaction; | 126 | m->tcfm_eaction = parm->eaction; |
122 | if (parm->ifindex) { | 127 | if (dev != NULL) { |
123 | m->tcfm_ifindex = parm->ifindex; | 128 | m->tcfm_ifindex = parm->ifindex; |
124 | if (ret != ACT_P_CREATED) | 129 | if (ret != ACT_P_CREATED) |
125 | dev_put(m->tcfm_dev); | 130 | dev_put(m->tcfm_dev); |
126 | m->tcfm_dev = dev; | ||
127 | dev_hold(dev); | 131 | dev_hold(dev); |
132 | m->tcfm_dev = dev; | ||
128 | m->tcfm_ok_push = ok_push; | 133 | m->tcfm_ok_push = ok_push; |
129 | } | 134 | } |
130 | spin_unlock_bh(&m->tcf_lock); | 135 | spin_unlock_bh(&m->tcf_lock); |
@@ -148,57 +153,57 @@ static int tcf_mirred(struct sk_buff *skb, struct tc_action *a, | |||
148 | { | 153 | { |
149 | struct tcf_mirred *m = a->priv; | 154 | struct tcf_mirred *m = a->priv; |
150 | struct net_device *dev; | 155 | struct net_device *dev; |
151 | struct sk_buff *skb2 = NULL; | 156 | struct sk_buff *skb2; |
152 | u32 at = G_TC_AT(skb->tc_verd); | 157 | u32 at; |
158 | int retval, err = 1; | ||
153 | 159 | ||
154 | spin_lock(&m->tcf_lock); | 160 | spin_lock(&m->tcf_lock); |
155 | |||
156 | dev = m->tcfm_dev; | ||
157 | m->tcf_tm.lastuse = jiffies; | 161 | m->tcf_tm.lastuse = jiffies; |
158 | 162 | ||
159 | if (!(dev->flags&IFF_UP) ) { | 163 | dev = m->tcfm_dev; |
164 | if (!(dev->flags & IFF_UP)) { | ||
160 | if (net_ratelimit()) | 165 | if (net_ratelimit()) |
161 | printk("mirred to Houston: device %s is gone!\n", | 166 | printk("mirred to Houston: device %s is gone!\n", |
162 | dev->name); | 167 | dev->name); |
163 | bad_mirred: | 168 | goto out; |
164 | if (skb2 != NULL) | ||
165 | kfree_skb(skb2); | ||
166 | m->tcf_qstats.overlimits++; | ||
167 | m->tcf_bstats.bytes += qdisc_pkt_len(skb); | ||
168 | m->tcf_bstats.packets++; | ||
169 | spin_unlock(&m->tcf_lock); | ||
170 | /* should we be asking for packet to be dropped? | ||
171 | * may make sense for redirect case only | ||
172 | */ | ||
173 | return TC_ACT_SHOT; | ||
174 | } | 169 | } |
175 | 170 | ||
176 | skb2 = skb_act_clone(skb, GFP_ATOMIC); | 171 | skb2 = skb_act_clone(skb, GFP_ATOMIC); |
177 | if (skb2 == NULL) | 172 | if (skb2 == NULL) |
178 | goto bad_mirred; | 173 | goto out; |
179 | if (m->tcfm_eaction != TCA_EGRESS_MIRROR && | ||
180 | m->tcfm_eaction != TCA_EGRESS_REDIR) { | ||
181 | if (net_ratelimit()) | ||
182 | printk("tcf_mirred unknown action %d\n", | ||
183 | m->tcfm_eaction); | ||
184 | goto bad_mirred; | ||
185 | } | ||
186 | 174 | ||
187 | m->tcf_bstats.bytes += qdisc_pkt_len(skb2); | 175 | m->tcf_bstats.bytes += qdisc_pkt_len(skb2); |
188 | m->tcf_bstats.packets++; | 176 | m->tcf_bstats.packets++; |
189 | if (!(at & AT_EGRESS)) | 177 | at = G_TC_AT(skb->tc_verd); |
178 | if (!(at & AT_EGRESS)) { | ||
190 | if (m->tcfm_ok_push) | 179 | if (m->tcfm_ok_push) |
191 | skb_push(skb2, skb2->dev->hard_header_len); | 180 | skb_push(skb2, skb2->dev->hard_header_len); |
181 | } | ||
192 | 182 | ||
193 | /* mirror is always swallowed */ | 183 | /* mirror is always swallowed */ |
194 | if (m->tcfm_eaction != TCA_EGRESS_MIRROR) | 184 | if (m->tcfm_eaction != TCA_EGRESS_MIRROR) |
195 | skb2->tc_verd = SET_TC_FROM(skb2->tc_verd, at); | 185 | skb2->tc_verd = SET_TC_FROM(skb2->tc_verd, at); |
196 | 186 | ||
197 | skb2->dev = dev; | 187 | skb2->dev = dev; |
198 | skb2->iif = skb->dev->ifindex; | 188 | skb2->skb_iif = skb->dev->ifindex; |
199 | dev_queue_xmit(skb2); | 189 | dev_queue_xmit(skb2); |
190 | err = 0; | ||
191 | |||
192 | out: | ||
193 | if (err) { | ||
194 | m->tcf_qstats.overlimits++; | ||
195 | m->tcf_bstats.bytes += qdisc_pkt_len(skb); | ||
196 | m->tcf_bstats.packets++; | ||
197 | /* should we be asking for packet to be dropped? | ||
198 | * may make sense for redirect case only | ||
199 | */ | ||
200 | retval = TC_ACT_SHOT; | ||
201 | } else { | ||
202 | retval = m->tcf_action; | ||
203 | } | ||
200 | spin_unlock(&m->tcf_lock); | 204 | spin_unlock(&m->tcf_lock); |
201 | return m->tcf_action; | 205 | |
206 | return retval; | ||
202 | } | 207 | } |
203 | 208 | ||
204 | static int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) | 209 | static int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) |