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