History

sched: consistently handle layer3 header accesses in the presence of VLANs

There are a couple of places in net/sched/ that check skb->protocol and act on the value there. However, in the presence of VLAN tags, the value stored in skb->protocol can be inconsistent based on whether VLAN acceleration is enabled. The commit quoted in the Fixes tag below fixed the users of skb->protocol to use a helper that will always see the VLAN ethertype. However, most of the callers don't actually handle the VLAN ethertype, but expect to find the IP header type in the protocol field. This means that things like changing the ECN field, or parsing diffserv values, stops working if there's a VLAN tag, or if there are multiple nested VLAN tags (QinQ). To fix this, change the helper to take an argument that indicates whether the caller wants to skip the VLAN tags or not. When skipping VLAN tags, we make sure to skip all of them, so behaviour is consistent even in QinQ mode. To make the helper usable from the ECN code, move it to if_vlan.h instead of pkt_sched.h. v3: - Remove empty lines - Move vlan variable definitions inside loop in skb_protocol() - Also use skb_protocol() helper in IP{,6}_ECN_decapsulate() and bpf_skb_ecn_set_ce() v2: - Use eth_type_vlan() helper in skb_protocol() - Also fix code that reads skb->protocol directly - Change a couple of 'if/else if' statements to switch constructs to avoid calling the helper twice

Toke Høiland-Jørgensen committed 6 years ago

Commit d7bf2eb1 parent ad4e2b619 files · +86 −51Browse files

Unified · Split

19 files
  1. include/linux/if_vlan.h +28 −0
  2. include/net/inet_ecn.h +17 −8
  3. include/net/pkt_sched.h +0 −11
  4. net/core/filter.c +7 −3
  5. net/sched/act_connmark.c +6 −3
  6. net/sched/act_csum.c +1 −1
  7. net/sched/act_ct.c +4 −5
  8. net/sched/act_ctinfo.c +6 −3
  9. net/sched/act_mpls.c +1 −1
  10. net/sched/act_skbedit.c +1 −1
  11. net/sched/cls_api.c +1 −1
  12. net/sched/cls_flow.c +4 −4
  13. net/sched/cls_flower.c +1 −1
  14. net/sched/em_ipset.c +1 −1
  15. net/sched/em_ipt.c +1 −1
  16. net/sched/em_meta.c +1 −1
  17. net/sched/sch_cake.c +2 −2
  18. net/sched/sch_dsmark.c +3 −3
  19. net/sched/sch_teql.c +1 −1
include/linux/if_vlan.h+28−0
@@ -308,6 +308,34 @@ static inline bool eth_type_vlan(__be16 ethertype) · 307 unmodified lines
308308 }
309309 }
310310
311+/* A getter for the SKB protocol field which will handle VLAN tags consistently
312+ * whether VLAN acceleration is enabled or not.
313+ */
314+static inline __be16 skb_protocol(const struct sk_buff *skb, bool skip_vlan)
315+{
316+ unsigned int offset = skb_mac_offset(skb) + sizeof(struct ethhdr);
317+ __be16 proto = skb->protocol;
318+
319+ if (!skip_vlan)
320+ /* VLAN acceleration strips the VLAN header from the skb and
321+ * moves it to skb->vlan_proto
322+ */
323+ return skb_vlan_tag_present(skb) ? skb->vlan_proto : proto;
324+
325+ while (eth_type_vlan(proto)) {
326+ struct vlan_hdr vhdr, *vh;
327+
328+ vh = skb_header_pointer(skb, offset, sizeof(vhdr), &vhdr);
329+ if (!vh)
330+ break;
331+
332+ proto = vh->h_vlan_encapsulated_proto;
333+ offset += sizeof(vhdr);
334+ }
335+
336+ return proto;
337+}
338+
311339 static inline bool vlan_hw_offload_capable(netdev_features_t features,
312340 __be16 proto)
313341 {
include/net/inet_ecn.h+17−8
@@ -4,6 +4,7 @@ · 3 unmodified lines
44
55 #include <linux/ip.h>
66 #include <linux/skbuff.h>
7+#include <linux/if_vlan.h>
78
89 #include <net/inet_sock.h>
910 #include <net/dsfield.h>
@@ -172,7 +173,7 @@ static inline void ipv6_copy_dscp(unsigned int dscp, struct ipv6hdr *inner) · 162 unmodified lines
172173
173174 static inline int INET_ECN_set_ce(struct sk_buff *skb)
174175 {
175− switch (skb->protocol) {
176+ switch (skb_protocol(skb, true)) {
176177 case cpu_to_be16(ETH_P_IP):
177178 if (skb_network_header(skb) + sizeof(struct iphdr) <=
178179 skb_tail_pointer(skb))
@@ -191,7 +192,7 @@ static inline int INET_ECN_set_ce(struct sk_buff *skb) · 12 unmodified lines
191192
192193 static inline int INET_ECN_set_ect1(struct sk_buff *skb)
193194 {
194− switch (skb->protocol) {
195+ switch (skb_protocol(skb, true)) {
195196 case cpu_to_be16(ETH_P_IP):
196197 if (skb_network_header(skb) + sizeof(struct iphdr) <=
197198 skb_tail_pointer(skb))
@@ -272,12 +273,16 @@ static inline int IP_ECN_decapsulate(const struct iphdr *oiph, · 74 unmodified lines
272273 {
273274 __u8 inner;
274275
275− if (skb->protocol == htons(ETH_P_IP))
276+ switch (skb_protocol(skb, true)) {
277+ case htons(ETH_P_IP):
276278 inner = ip_hdr(skb)->tos;
277− else if (skb->protocol == htons(ETH_P_IPV6))
279+ break;
280+ case htons(ETH_P_IPV6):
278281 inner = ipv6_get_dsfield(ipv6_hdr(skb));
279− else
282+ break;
283+ default:
280284 return 0;
285+ }
281286
282287 return INET_ECN_decapsulate(skb, oiph->tos, inner);
283288 }
@@ -287,12 +292,16 @@ static inline int IP6_ECN_decapsulate(const struct ipv6hdr *oipv6h, · 3 unmodified lines
287292 {
288293 __u8 inner;
289294
290− if (skb->protocol == htons(ETH_P_IP))
295+ switch (skb_protocol(skb, true)) {
296+ case htons(ETH_P_IP):
291297 inner = ip_hdr(skb)->tos;
292− else if (skb->protocol == htons(ETH_P_IPV6))
298+ break;
299+ case htons(ETH_P_IPV6):
293300 inner = ipv6_get_dsfield(ipv6_hdr(skb));
294− else
301+ break;
302+ default:
295303 return 0;
304+ }
296305
297306 return INET_ECN_decapsulate(skb, ipv6_get_dsfield(oipv6h), inner);
298307 }
include/net/pkt_sched.h+0−11
@@ -136,17 +136,6 @@ static inline void qdisc_run(struct Qdisc *q) · 135 unmodified lines
136136 }
137137 }
138138
139−static inline __be16 tc_skb_protocol(const struct sk_buff *skb)
140−{
141− /* We need to take extra care in case the skb came via
142− * vlan accelerated path. In that case, use skb->vlan_proto
143− * as the original vlan header was already stripped.
144− */
145− if (skb_vlan_tag_present(skb))
146− return skb->vlan_proto;
147− return skb->protocol;
148−}
149−
150139 /* Calculate maximal size of packet seen by hard_start_xmit
151140 routine of this device.
152141 */
net/core/filter.c+7−3
@@ -5853,12 +5853,16 @@ BPF_CALL_1(bpf_skb_ecn_set_ce, struct sk_buff *, skb) · 5852 unmodified lines
58535853 {
58545854 unsigned int iphdr_len;
58555855
5856− if (skb->protocol == cpu_to_be16(ETH_P_IP))
5856+ switch (skb_protocol(skb, true)) {
5857+ case cpu_to_be16(ETH_P_IP):
58575858 iphdr_len = sizeof(struct iphdr);
5858− else if (skb->protocol == cpu_to_be16(ETH_P_IPV6))
5859+ break;
5860+ case cpu_to_be16(ETH_P_IPV6):
58595861 iphdr_len = sizeof(struct ipv6hdr);
5860− else
5862+ break;
5863+ default:
58615864 return 0;
5865+ }
58625866
58635867 if (skb_headlen(skb) < iphdr_len)
58645868 return 0;
net/sched/act_connmark.c+6−3
@@ -43,17 +43,20 @@ static int tcf_connmark_act(struct sk_buff *skb, const struct tc_action *a, · 42 unmodified lines
4343 tcf_lastuse_update(&ca->tcf_tm);
4444 bstats_update(&ca->tcf_bstats, skb);
4545
46− if (skb->protocol == htons(ETH_P_IP)) {
46+ switch (skb_protocol(skb, true)) {
47+ case htons(ETH_P_IP):
4748 if (skb->len < sizeof(struct iphdr))
4849 goto out;
4950
5051 proto = NFPROTO_IPV4;
51− } else if (skb->protocol == htons(ETH_P_IPV6)) {
52+ break;
53+ case htons(ETH_P_IPV6):
5254 if (skb->len < sizeof(struct ipv6hdr))
5355 goto out;
5456
5557 proto = NFPROTO_IPV6;
56− } else {
58+ break;
59+ default:
5760 goto out;
5861 }
5962
net/sched/act_csum.c+1−1
@@ -587,7 +587,7 @@ static int tcf_csum_act(struct sk_buff *skb, const struct tc_action *a, · 586 unmodified lines
587587 goto drop;
588588
589589 update_flags = params->update_flags;
590− protocol = tc_skb_protocol(skb);
590+ protocol = skb_protocol(skb, false);
591591 again:
592592 switch (protocol) {
593593 case cpu_to_be16(ETH_P_IP):
net/sched/act_ct.c+4−5
@@ -624,7 +624,7 @@ static u8 tcf_ct_skb_nf_family(struct sk_buff *skb) · 623 unmodified lines
624624 {
625625 u8 family = NFPROTO_UNSPEC;
626626
627− switch (skb->protocol) {
627+ switch (skb_protocol(skb, true)) {
628628 case htons(ETH_P_IP):
629629 family = NFPROTO_IPV4;
630630 break;
@@ -748,6 +748,7 @@ static int ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct, · 117 unmodified lines
748748 const struct nf_nat_range2 *range,
749749 enum nf_nat_manip_type maniptype)
750750 {
751+ __be16 proto = skb_protocol(skb, true);
751752 int hooknum, err = NF_ACCEPT;
752753
753754 /* See HOOK2MANIP(). */
@@ -759,14 +760,13 @@ static int ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct, · 5 unmodified lines
759760 switch (ctinfo) {
760761 case IP_CT_RELATED:
761762 case IP_CT_RELATED_REPLY:
762− if (skb->protocol == htons(ETH_P_IP) &&
763+ if (proto == htons(ETH_P_IP) &&
763764 ip_hdr(skb)->protocol == IPPROTO_ICMP) {
764765 if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo,
765766 hooknum))
766767 err = NF_DROP;
767768 goto out;
768− } else if (IS_ENABLED(CONFIG_IPV6) &&
769− skb->protocol == htons(ETH_P_IPV6)) {
769+ } else if (IS_ENABLED(CONFIG_IPV6) && proto == htons(ETH_P_IPV6)) {
770770 __be16 frag_off;
771771 u8 nexthdr = ipv6_hdr(skb)->nexthdr;
772772 int hdrlen = ipv6_skip_exthdr(skb,
@@ -1550,4 +1550,3 @@ MODULE_AUTHOR("Yossi Kuperman <yossiku@mellanox.com>"); · 777 unmodified lines
15501550 MODULE_AUTHOR("Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>");
15511551 MODULE_DESCRIPTION("Connection tracking action");
15521552 MODULE_LICENSE("GPL v2");
1553−
net/sched/act_ctinfo.c+6−3
@@ -96,19 +96,22 @@ static int tcf_ctinfo_act(struct sk_buff *skb, const struct tc_action *a, · 95 unmodified lines
9696 action = READ_ONCE(ca->tcf_action);
9797
9898 wlen = skb_network_offset(skb);
99− if (tc_skb_protocol(skb) == htons(ETH_P_IP)) {
99+ switch (skb_protocol(skb, true)) {
100+ case htons(ETH_P_IP):
100101 wlen += sizeof(struct iphdr);
101102 if (!pskb_may_pull(skb, wlen))
102103 goto out;
103104
104105 proto = NFPROTO_IPV4;
105− } else if (tc_skb_protocol(skb) == htons(ETH_P_IPV6)) {
106+ break;
107+ case htons(ETH_P_IPV6):
106108 wlen += sizeof(struct ipv6hdr);
107109 if (!pskb_may_pull(skb, wlen))
108110 goto out;
109111
110112 proto = NFPROTO_IPV6;
111− } else {
113+ break;
114+ default:
112115 goto out;
113116 }
114117
net/sched/act_mpls.c+1−1
@@ -82,7 +82,7 @@ static int tcf_mpls_act(struct sk_buff *skb, const struct tc_action *a, · 81 unmodified lines
8282 goto drop;
8383 break;
8484 case TCA_MPLS_ACT_PUSH:
85− new_lse = tcf_mpls_get_lse(NULL, p, !eth_p_mpls(skb->protocol));
85+ new_lse = tcf_mpls_get_lse(NULL, p, !eth_p_mpls(skb_protocol(skb, true)));
8686 if (skb_mpls_push(skb, new_lse, p->tcfm_proto, mac_len,
8787 skb->dev && skb->dev->type == ARPHRD_ETHER))
8888 goto drop;
net/sched/act_skbedit.c+1−1
@@ -41,7 +41,7 @@ static int tcf_skbedit_act(struct sk_buff *skb, const struct tc_action *a, · 40 unmodified lines
4141 if (params->flags & SKBEDIT_F_INHERITDSFIELD) {
4242 int wlen = skb_network_offset(skb);
4343
44− switch (tc_skb_protocol(skb)) {
44+ switch (skb_protocol(skb, true)) {
4545 case htons(ETH_P_IP):
4646 wlen += sizeof(struct iphdr);
4747 if (!pskb_may_pull(skb, wlen))
net/sched/cls_api.c+1−1
@@ -1538,7 +1538,7 @@ static inline int __tcf_classify(struct sk_buff *skb, · 1537 unmodified lines
15381538 reclassify:
15391539 #endif
15401540 for (; tp; tp = rcu_dereference_bh(tp->next)) {
1541− __be16 protocol = tc_skb_protocol(skb);
1541+ __be16 protocol = skb_protocol(skb, false);
15421542 int err;
15431543
15441544 if (tp->protocol != protocol &&
net/sched/cls_flow.c+4−4
@@ -80,7 +80,7 @@ static u32 flow_get_dst(const struct sk_buff *skb, const struct flow_keys *flow) · 79 unmodified lines
8080 if (dst)
8181 return ntohl(dst);
8282
83− return addr_fold(skb_dst(skb)) ^ (__force u16) tc_skb_protocol(skb);
83+ return addr_fold(skb_dst(skb)) ^ (__force u16)skb_protocol(skb, true);
8484 }
8585
8686 static u32 flow_get_proto(const struct sk_buff *skb,
@@ -104,7 +104,7 @@ static u32 flow_get_proto_dst(const struct sk_buff *skb, · 17 unmodified lines
104104 if (flow->ports.ports)
105105 return ntohs(flow->ports.dst);
106106
107− return addr_fold(skb_dst(skb)) ^ (__force u16) tc_skb_protocol(skb);
107+ return addr_fold(skb_dst(skb)) ^ (__force u16)skb_protocol(skb, true);
108108 }
109109
110110 static u32 flow_get_iif(const struct sk_buff *skb)
@@ -151,7 +151,7 @@ static u32 flow_get_nfct(const struct sk_buff *skb) · 40 unmodified lines
151151 static u32 flow_get_nfct_src(const struct sk_buff *skb,
152152 const struct flow_keys *flow)
153153 {
154− switch (tc_skb_protocol(skb)) {
154+ switch (skb_protocol(skb, true)) {
155155 case htons(ETH_P_IP):
156156 return ntohl(CTTUPLE(skb, src.u3.ip));
157157 case htons(ETH_P_IPV6):
@@ -164,7 +164,7 @@ fallback: · 6 unmodified lines
164164 static u32 flow_get_nfct_dst(const struct sk_buff *skb,
165165 const struct flow_keys *flow)
166166 {
167− switch (tc_skb_protocol(skb)) {
167+ switch (skb_protocol(skb, true)) {
168168 case htons(ETH_P_IP):
169169 return ntohl(CTTUPLE(skb, dst.u3.ip));
170170 case htons(ETH_P_IPV6):
net/sched/cls_flower.c+1−1
@@ -313,7 +313,7 @@ static int fl_classify(struct sk_buff *skb, const struct tcf_proto *tp, · 312 unmodified lines
313313 /* skb_flow_dissect() does not set n_proto in case an unknown
314314 * protocol, so do it rather here.
315315 */
316− skb_key.basic.n_proto = skb->protocol;
316+ skb_key.basic.n_proto = skb_protocol(skb, false);
317317 skb_flow_dissect_tunnel_info(skb, &mask->dissector, &skb_key);
318318 skb_flow_dissect_ct(skb, &mask->dissector, &skb_key,
319319 fl_ct_info_to_flower_map,
net/sched/em_ipset.c+1−1
@@ -59,7 +59,7 @@ static int em_ipset_match(struct sk_buff *skb, struct tcf_ematch *em, · 58 unmodified lines
5959 };
6060 int ret, network_offset;
6161
62− switch (tc_skb_protocol(skb)) {
62+ switch (skb_protocol(skb, true)) {
6363 case htons(ETH_P_IP):
6464 state.pf = NFPROTO_IPV4;
6565 if (!pskb_network_may_pull(skb, sizeof(struct iphdr)))
net/sched/em_ipt.c+1−1
@@ -212,7 +212,7 @@ static int em_ipt_match(struct sk_buff *skb, struct tcf_ematch *em, · 211 unmodified lines
212212 struct nf_hook_state state;
213213 int ret;
214214
215− switch (tc_skb_protocol(skb)) {
215+ switch (skb_protocol(skb, true)) {
216216 case htons(ETH_P_IP):
217217 if (!pskb_network_may_pull(skb, sizeof(struct iphdr)))
218218 return 0;
net/sched/em_meta.c+1−1
@@ -195,7 +195,7 @@ META_COLLECTOR(int_priority) · 194 unmodified lines
195195 META_COLLECTOR(int_protocol)
196196 {
197197 /* Let userspace take care of the byte ordering */
198− dst->value = tc_skb_protocol(skb);
198+ dst->value = skb_protocol(skb, false);
199199 }
200200
201201 META_COLLECTOR(int_pkttype)
net/sched/sch_cake.c+2−2
@@ -592,7 +592,7 @@ static bool cake_update_flowkeys(struct flow_keys *keys, · 591 unmodified lines
592592 bool rev = !skb->_nfct, upd = false;
593593 __be32 ip;
594594
595− if (tc_skb_protocol(skb) != htons(ETH_P_IP))
595+ if (skb_protocol(skb, true) != htons(ETH_P_IP))
596596 return false;
597597
598598 if (!nf_ct_get_tuple_skb(&tuple, skb))
@@ -1557,7 +1557,7 @@ static u8 cake_handle_diffserv(struct sk_buff *skb, bool wash) · 958 unmodified lines
15571557 u16 *buf, buf_;
15581558 u8 dscp;
15591559
1560− switch (tc_skb_protocol(skb)) {
1560+ switch (skb_protocol(skb, true)) {
15611561 case htons(ETH_P_IP):
15621562 buf = skb_header_pointer(skb, offset, sizeof(buf_), &buf_);
15631563 if (unlikely(!buf))
net/sched/sch_dsmark.c+3−3
@@ -210,7 +210,7 @@ static int dsmark_enqueue(struct sk_buff *skb, struct Qdisc *sch, · 209 unmodified lines
210210 if (p->set_tc_index) {
211211 int wlen = skb_network_offset(skb);
212212
213− switch (tc_skb_protocol(skb)) {
213+ switch (skb_protocol(skb, true)) {
214214 case htons(ETH_P_IP):
215215 wlen += sizeof(struct iphdr);
216216 if (!pskb_may_pull(skb, wlen) ||
@@ -303,7 +303,7 @@ static struct sk_buff *dsmark_dequeue(struct Qdisc *sch) · 86 unmodified lines
303303 index = skb->tc_index & (p->indices - 1);
304304 pr_debug("index %d->%d\n", skb->tc_index, index);
305305
306− switch (tc_skb_protocol(skb)) {
306+ switch (skb_protocol(skb, true)) {
307307 case htons(ETH_P_IP):
308308 ipv4_change_dsfield(ip_hdr(skb), p->mv[index].mask,
309309 p->mv[index].value);
@@ -320,7 +320,7 @@ static struct sk_buff *dsmark_dequeue(struct Qdisc *sch) · 10 unmodified lines
320320 */
321321 if (p->mv[index].mask != 0xff || p->mv[index].value)
322322 pr_warn("%s: unsupported protocol %d\n",
323− __func__, ntohs(tc_skb_protocol(skb)));
323+ __func__, ntohs(skb_protocol(skb, true)));
324324 break;
325325 }
326326
net/sched/sch_teql.c+1−1
@@ -239,7 +239,7 @@ __teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res, · 238 unmodified lines
239239 char haddr[MAX_ADDR_LEN];
240240
241241 neigh_ha_snapshot(haddr, n, dev);
242− err = dev_hard_header(skb, dev, ntohs(tc_skb_protocol(skb)),
242+ err = dev_hard_header(skb, dev, ntohs(skb_protocol(skb, false)),
243243 haddr, NULL, skb->len);
244244
245245 if (err < 0)