History

net/sched: Handle TC_ACT_REDIRECT from qdisc filter chains

When a TC filter attached to a qdisc filter chain returns TC_ACT_REDIRECT (ex: via an eBPF program calling bpf_redirect() or an act_bpf action), the redirect was silently lost i.e no qdisc classify function handled TC_ACT_REDIRECT, so the packet fell through the switch and was enqueued normally instead of being redirected. This has been broken since bpf_redirect() was introduced for TC in commit 27b29f63058d ("bpf: add bpf_redirect() helper"). We got lucky for a long time because bpf_net_context was a per-CPU variable that was always available. commit 401cb7dae813 ("net: Reference bpf_redirect_info via task_struct on PREEMPT_RT.") turned bpf_net_context into a task_struct member that is only set up by explicit callers. Without a caller setting it up, bpf_redirect() itself crashes with a NULL pointer dereference in bpf_net_ctx_get_ri(). However, even with bpf_net_context available, TC_ACT_REDIRECT from qdisc filter chains cannot be honored without adding skb_do_redirect() calls to every qdisc classify function, which would require changes across net/sched/. Isolate it to ebpf core where it belongs. Instead, add a tcf_classify_qdisc() inline helper in pkt_cls.h, as a wrapper around tcf_classify() for use by qdisc classify functions and tcf_qevent_handle(). When the classify verdict is TC_ACT_REDIRECT, the wrapper converts it to TC_ACT_SHOT, dropping the packet rather than letting it continue silently. Dropping is preferred over letting the packet through because the user immediately sees packet loss. Silently passing the packet through would hide the problem and leave the user wondering why their redirect is not working. The clsact fast path, tc_run() continues to call tcf_classify() directly and is unaffected: TC_ACT_REDIRECT is returned as-is and handled by sch_handle_egress/ingress() calling skb_do_redirect() as before.

Jamal Hadi Salim committed 4 months ago

Commit ec48b3b1 parent 3f4920d15 files · +27 −17Browse files

Unified · Split

15 files
  1. include/net/pkt_cls.h +13 −1
  2. net/sched/cls_api.c +1 −3
  3. net/sched/sch_cake.c +1 −1
  4. net/sched/sch_drr.c +1 −1
  5. net/sched/sch_dualpi2.c +1 −1
  6. net/sched/sch_ets.c +1 −1
  7. net/sched/sch_fq_codel.c +1 −1
  8. net/sched/sch_fq_pie.c +1 −1
  9. net/sched/sch_hfsc.c +1 −1
  10. net/sched/sch_htb.c +1 −1
  11. net/sched/sch_multiq.c +1 −1
  12. net/sched/sch_prio.c +1 −1
  13. net/sched/sch_qfq.c +1 −1
  14. net/sched/sch_sfb.c +1 −1
  15. net/sched/sch_sfq.c +1 −1
include/net/pkt_cls.h+13−1
@@ -156,8 +156,20 @@ static inline int tcf_classify(struct sk_buff *skb, · 155 unmodified lines
156{156{
157 return TC_ACT_UNSPEC;157 return TC_ACT_UNSPEC;
158}158}
159
160#endif159#endif
160static inline int tcf_classify_qdisc(struct sk_buff *skb,
161 const struct tcf_proto *tp,
162 struct tcf_result *res, bool compat_mode)
163{
164 int ret = tcf_classify(skb, NULL, tp, res, compat_mode);
165
166 /* TC_ACT_REDIRECT from qdisc filter chains is not supported.
167 * Use BPF via tcx or mirred redirect instead.
168 */
169 if (unlikely(ret == TC_ACT_REDIRECT))
170 ret = TC_ACT_SHOT;
171 return ret;
172}
161 173
162static inline unsigned long174static inline unsigned long
163__cls_set_class(unsigned long *clp, unsigned long cl)175__cls_set_class(unsigned long *clp, unsigned long cl)
net/sched/cls_api.c+1−3
@@ -4045,9 +4045,7 @@ struct sk_buff *tcf_qevent_handle(struct tcf_qevent *qe, struct Qdisc *sch, stru · 4044 unmodified lines
4045 4045
4046 fl = rcu_dereference_bh(qe->filter_chain);4046 fl = rcu_dereference_bh(qe->filter_chain);
4047 4047
4048 switch (tcf_classify(skb, NULL, fl, &cl_res, false)) {4048 switch (tcf_classify_qdisc(skb, fl, &cl_res, false)) {
4049 case TC_ACT_REDIRECT:
4050 fallthrough;
4051 case TC_ACT_SHOT:4049 case TC_ACT_SHOT:
4052 qdisc_qstats_drop(sch);4050 qdisc_qstats_drop(sch);
4053 __qdisc_drop(skb, to_free);4051 __qdisc_drop(skb, to_free);
net/sched/sch_cake.c+1−1
@@ -1727,7 +1727,7 @@ static u32 cake_classify(struct Qdisc *sch, struct cake_tin_data **t, · 1726 unmodified lines
1727 goto hash;1727 goto hash;
1728 1728
1729 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;1729 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
1730 result = tcf_classify(skb, NULL, filter, &res, false);1730 result = tcf_classify_qdisc(skb, filter, &res, false);
1731 1731
1732 if (result >= 0) {1732 if (result >= 0) {
1733#ifdef CONFIG_NET_CLS_ACT1733#ifdef CONFIG_NET_CLS_ACT
net/sched/sch_drr.c+1−1
@@ -312,7 +312,7 @@ static struct drr_class *drr_classify(struct sk_buff *skb, struct Qdisc *sch, · 311 unmodified lines
312 312
313 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;313 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
314 fl = rcu_dereference_bh(q->filter_list);314 fl = rcu_dereference_bh(q->filter_list);
315 result = tcf_classify(skb, NULL, fl, &res, false);315 result = tcf_classify_qdisc(skb, fl, &res, false);
316 if (result >= 0) {316 if (result >= 0) {
317#ifdef CONFIG_NET_CLS_ACT317#ifdef CONFIG_NET_CLS_ACT
318 switch (result) {318 switch (result) {
net/sched/sch_dualpi2.c+1−1
@@ -364,7 +364,7 @@ static int dualpi2_skb_classify(struct dualpi2_sched_data *q, · 363 unmodified lines
364 if (!fl)364 if (!fl)
365 return NET_XMIT_SUCCESS;365 return NET_XMIT_SUCCESS;
366 366
367 result = tcf_classify(skb, NULL, fl, &res, false);367 result = tcf_classify_qdisc(skb, fl, &res, false);
368 if (result >= 0) {368 if (result >= 0) {
369#ifdef CONFIG_NET_CLS_ACT369#ifdef CONFIG_NET_CLS_ACT
370 switch (result) {370 switch (result) {
net/sched/sch_ets.c+1−1
@@ -391,7 +391,7 @@ static struct ets_class *ets_classify(struct sk_buff *skb, struct Qdisc *sch, · 390 unmodified lines
391 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;391 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
392 if (TC_H_MAJ(skb->priority) != sch->handle) {392 if (TC_H_MAJ(skb->priority) != sch->handle) {
393 fl = rcu_dereference_bh(q->filter_list);393 fl = rcu_dereference_bh(q->filter_list);
394 err = tcf_classify(skb, NULL, fl, &res, false);394 err = tcf_classify_qdisc(skb, fl, &res, false);
395#ifdef CONFIG_NET_CLS_ACT395#ifdef CONFIG_NET_CLS_ACT
396 switch (err) {396 switch (err) {
397 case TC_ACT_STOLEN:397 case TC_ACT_STOLEN:
net/sched/sch_fq_codel.c+1−1
@@ -91,7 +91,7 @@ static unsigned int fq_codel_classify(struct sk_buff *skb, struct Qdisc *sch, · 90 unmodified lines
91 return fq_codel_hash(q, skb) + 1;91 return fq_codel_hash(q, skb) + 1;
92 92
93 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;93 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
94 result = tcf_classify(skb, NULL, filter, &res, false);94 result = tcf_classify_qdisc(skb, filter, &res, false);
95 if (result >= 0) {95 if (result >= 0) {
96#ifdef CONFIG_NET_CLS_ACT96#ifdef CONFIG_NET_CLS_ACT
97 switch (result) {97 switch (result) {
net/sched/sch_fq_pie.c+1−1
@@ -96,7 +96,7 @@ static unsigned int fq_pie_classify(struct sk_buff *skb, struct Qdisc *sch, · 95 unmodified lines
96 return fq_pie_hash(q, skb) + 1;96 return fq_pie_hash(q, skb) + 1;
97 97
98 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;98 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
99 result = tcf_classify(skb, NULL, filter, &res, false);99 result = tcf_classify_qdisc(skb, filter, &res, false);
100 if (result >= 0) {100 if (result >= 0) {
101#ifdef CONFIG_NET_CLS_ACT101#ifdef CONFIG_NET_CLS_ACT
102 switch (result) {102 switch (result) {
net/sched/sch_hfsc.c+1−1
@@ -1143,7 +1143,7 @@ hfsc_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) · 1142 unmodified lines
1143 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;1143 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
1144 head = &q->root;1144 head = &q->root;
1145 tcf = rcu_dereference_bh(q->root.filter_list);1145 tcf = rcu_dereference_bh(q->root.filter_list);
1146 while (tcf && (result = tcf_classify(skb, NULL, tcf, &res, false)) >= 0) {1146 while (tcf && (result = tcf_classify_qdisc(skb, tcf, &res, false)) >= 0) {
1147#ifdef CONFIG_NET_CLS_ACT1147#ifdef CONFIG_NET_CLS_ACT
1148 switch (result) {1148 switch (result) {
1149 case TC_ACT_QUEUED:1149 case TC_ACT_QUEUED:
net/sched/sch_htb.c+1−1
@@ -243,7 +243,7 @@ static struct htb_class *htb_classify(struct sk_buff *skb, struct Qdisc *sch, · 242 unmodified lines
243 }243 }
244 244
245 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;245 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
246 while (tcf && (result = tcf_classify(skb, NULL, tcf, &res, false)) >= 0) {246 while (tcf && (result = tcf_classify_qdisc(skb, tcf, &res, false)) >= 0) {
247#ifdef CONFIG_NET_CLS_ACT247#ifdef CONFIG_NET_CLS_ACT
248 switch (result) {248 switch (result) {
249 case TC_ACT_QUEUED:249 case TC_ACT_QUEUED:
net/sched/sch_multiq.c+1−1
@@ -36,7 +36,7 @@ multiq_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) · 35 unmodified lines
36 int err;36 int err;
37 37
38 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;38 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
39 err = tcf_classify(skb, NULL, fl, &res, false);39 err = tcf_classify_qdisc(skb, fl, &res, false);
40#ifdef CONFIG_NET_CLS_ACT40#ifdef CONFIG_NET_CLS_ACT
41 switch (err) {41 switch (err) {
42 case TC_ACT_STOLEN:42 case TC_ACT_STOLEN:
net/sched/sch_prio.c+1−1
@@ -39,7 +39,7 @@ prio_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) · 38 unmodified lines
39 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;39 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
40 if (TC_H_MAJ(skb->priority) != sch->handle) {40 if (TC_H_MAJ(skb->priority) != sch->handle) {
41 fl = rcu_dereference_bh(q->filter_list);41 fl = rcu_dereference_bh(q->filter_list);
42 err = tcf_classify(skb, NULL, fl, &res, false);42 err = tcf_classify_qdisc(skb, fl, &res, false);
43#ifdef CONFIG_NET_CLS_ACT43#ifdef CONFIG_NET_CLS_ACT
44 switch (err) {44 switch (err) {
45 case TC_ACT_STOLEN:45 case TC_ACT_STOLEN:
net/sched/sch_qfq.c+1−1
@@ -709,7 +709,7 @@ static struct qfq_class *qfq_classify(struct sk_buff *skb, struct Qdisc *sch, · 708 unmodified lines
709 709
710 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;710 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
711 fl = rcu_dereference_bh(q->filter_list);711 fl = rcu_dereference_bh(q->filter_list);
712 result = tcf_classify(skb, NULL, fl, &res, false);712 result = tcf_classify_qdisc(skb, fl, &res, false);
713 if (result >= 0) {713 if (result >= 0) {
714#ifdef CONFIG_NET_CLS_ACT714#ifdef CONFIG_NET_CLS_ACT
715 switch (result) {715 switch (result) {
net/sched/sch_sfb.c+1−1
@@ -260,7 +260,7 @@ static bool sfb_classify(struct sk_buff *skb, struct tcf_proto *fl, · 259 unmodified lines
260 struct tcf_result res;260 struct tcf_result res;
261 int result;261 int result;
262 262
263 result = tcf_classify(skb, NULL, fl, &res, false);263 result = tcf_classify_qdisc(skb, fl, &res, false);
264 if (result >= 0) {264 if (result >= 0) {
265#ifdef CONFIG_NET_CLS_ACT265#ifdef CONFIG_NET_CLS_ACT
266 switch (result) {266 switch (result) {
net/sched/sch_sfq.c+1−1
@@ -171,7 +171,7 @@ static unsigned int sfq_classify(struct sk_buff *skb, struct Qdisc *sch, · 170 unmodified lines
171 return sfq_hash(q, skb) + 1;171 return sfq_hash(q, skb) + 1;
172 172
173 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;173 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
174 result = tcf_classify(skb, NULL, fl, &res, false);174 result = tcf_classify_qdisc(skb, fl, &res, false);
175 if (result >= 0) {175 if (result >= 0) {
176#ifdef CONFIG_NET_CLS_ACT176#ifdef CONFIG_NET_CLS_ACT
177 switch (result) {177 switch (result) {

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t