diff options
-rw-r--r-- | include/net/gue.h | 100 | ||||
-rw-r--r-- | net/ipv4/fou.c | 142 |
2 files changed, 189 insertions, 53 deletions
diff --git a/include/net/gue.h b/include/net/gue.h index b6c332788084..cb68ae843c77 100644 --- a/include/net/gue.h +++ b/include/net/gue.h | |||
@@ -1,23 +1,113 @@ | |||
1 | #ifndef __NET_GUE_H | 1 | #ifndef __NET_GUE_H |
2 | #define __NET_GUE_H | 2 | #define __NET_GUE_H |
3 | 3 | ||
4 | /* Definitions for the GUE header, standard and private flags, lengths | ||
5 | * of optional fields are below. | ||
6 | * | ||
7 | * Diagram of GUE header: | ||
8 | * | ||
9 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
10 | * |Ver|C| Hlen | Proto/ctype | Standard flags |P| | ||
11 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
12 | * | | | ||
13 | * ~ Fields (optional) ~ | ||
14 | * | | | ||
15 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
16 | * | Private flags (optional, P bit is set) | | ||
17 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
18 | * | | | ||
19 | * ~ Private fields (optional) ~ | ||
20 | * | | | ||
21 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
22 | * | ||
23 | * C bit indicates contol message when set, data message when unset. | ||
24 | * For a control message, proto/ctype is interpreted as a type of | ||
25 | * control message. For data messages, proto/ctype is the IP protocol | ||
26 | * of the next header. | ||
27 | * | ||
28 | * P bit indicates private flags field is present. The private flags | ||
29 | * may refer to options placed after this field. | ||
30 | */ | ||
31 | |||
4 | struct guehdr { | 32 | struct guehdr { |
5 | union { | 33 | union { |
6 | struct { | 34 | struct { |
7 | #if defined(__LITTLE_ENDIAN_BITFIELD) | 35 | #if defined(__LITTLE_ENDIAN_BITFIELD) |
8 | __u8 hlen:4, | 36 | __u8 hlen:5, |
9 | version:4; | 37 | control:1, |
38 | version:2; | ||
10 | #elif defined (__BIG_ENDIAN_BITFIELD) | 39 | #elif defined (__BIG_ENDIAN_BITFIELD) |
11 | __u8 version:4, | 40 | __u8 version:2, |
12 | hlen:4; | 41 | control:1, |
42 | hlen:5; | ||
13 | #else | 43 | #else |
14 | #error "Please fix <asm/byteorder.h>" | 44 | #error "Please fix <asm/byteorder.h>" |
15 | #endif | 45 | #endif |
16 | __u8 next_hdr; | 46 | __u8 proto_ctype; |
17 | __u16 flags; | 47 | __u16 flags; |
18 | }; | 48 | }; |
19 | __u32 word; | 49 | __u32 word; |
20 | }; | 50 | }; |
21 | }; | 51 | }; |
22 | 52 | ||
53 | /* Standard flags in GUE header */ | ||
54 | |||
55 | #define GUE_FLAG_PRIV htons(1<<0) /* Private flags are in options */ | ||
56 | #define GUE_LEN_PRIV 4 | ||
57 | |||
58 | #define GUE_FLAGS_ALL (GUE_FLAG_PRIV) | ||
59 | |||
60 | /* Private flags in the private option extension */ | ||
61 | |||
62 | #define GUE_PFLAGS_ALL (0) | ||
63 | |||
64 | /* Functions to compute options length corresponding to flags. | ||
65 | * If we ever have a lot of flags this can be potentially be | ||
66 | * converted to a more optimized algorithm (table lookup | ||
67 | * for instance). | ||
68 | */ | ||
69 | static inline size_t guehdr_flags_len(__be16 flags) | ||
70 | { | ||
71 | return ((flags & GUE_FLAG_PRIV) ? GUE_LEN_PRIV : 0); | ||
72 | } | ||
73 | |||
74 | static inline size_t guehdr_priv_flags_len(__be32 flags) | ||
75 | { | ||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | /* Validate standard and private flags. Returns non-zero (meaning invalid) | ||
80 | * if there is an unknown standard or private flags, or the options length for | ||
81 | * the flags exceeds the options length specific in hlen of the GUE header. | ||
82 | */ | ||
83 | static inline int validate_gue_flags(struct guehdr *guehdr, | ||
84 | size_t optlen) | ||
85 | { | ||
86 | size_t len; | ||
87 | __be32 flags = guehdr->flags; | ||
88 | |||
89 | if (flags & ~GUE_FLAGS_ALL) | ||
90 | return 1; | ||
91 | |||
92 | len = guehdr_flags_len(flags); | ||
93 | if (len > optlen) | ||
94 | return 1; | ||
95 | |||
96 | if (flags & GUE_FLAG_PRIV) { | ||
97 | /* Private flags are last four bytes accounted in | ||
98 | * guehdr_flags_len | ||
99 | */ | ||
100 | flags = *(__be32 *)((void *)&guehdr[1] + len - GUE_LEN_PRIV); | ||
101 | |||
102 | if (flags & ~GUE_PFLAGS_ALL) | ||
103 | return 1; | ||
104 | |||
105 | len += guehdr_priv_flags_len(flags); | ||
106 | if (len > optlen) | ||
107 | return 1; | ||
108 | } | ||
109 | |||
110 | return 0; | ||
111 | } | ||
112 | |||
23 | #endif | 113 | #endif |
diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c index 5446c1c8c26c..a3b8c5b36303 100644 --- a/net/ipv4/fou.c +++ b/net/ipv4/fou.c | |||
@@ -38,21 +38,17 @@ static inline struct fou *fou_from_sock(struct sock *sk) | |||
38 | return sk->sk_user_data; | 38 | return sk->sk_user_data; |
39 | } | 39 | } |
40 | 40 | ||
41 | static int fou_udp_encap_recv_deliver(struct sk_buff *skb, | 41 | static void fou_recv_pull(struct sk_buff *skb, size_t len) |
42 | u8 protocol, size_t len) | ||
43 | { | 42 | { |
44 | struct iphdr *iph = ip_hdr(skb); | 43 | struct iphdr *iph = ip_hdr(skb); |
45 | 44 | ||
46 | /* Remove 'len' bytes from the packet (UDP header and | 45 | /* Remove 'len' bytes from the packet (UDP header and |
47 | * FOU header if present), modify the protocol to the one | 46 | * FOU header if present). |
48 | * we found, and then call rcv_encap. | ||
49 | */ | 47 | */ |
50 | iph->tot_len = htons(ntohs(iph->tot_len) - len); | 48 | iph->tot_len = htons(ntohs(iph->tot_len) - len); |
51 | __skb_pull(skb, len); | 49 | __skb_pull(skb, len); |
52 | skb_postpull_rcsum(skb, udp_hdr(skb), len); | 50 | skb_postpull_rcsum(skb, udp_hdr(skb), len); |
53 | skb_reset_transport_header(skb); | 51 | skb_reset_transport_header(skb); |
54 | |||
55 | return -protocol; | ||
56 | } | 52 | } |
57 | 53 | ||
58 | static int fou_udp_recv(struct sock *sk, struct sk_buff *skb) | 54 | static int fou_udp_recv(struct sock *sk, struct sk_buff *skb) |
@@ -62,16 +58,24 @@ static int fou_udp_recv(struct sock *sk, struct sk_buff *skb) | |||
62 | if (!fou) | 58 | if (!fou) |
63 | return 1; | 59 | return 1; |
64 | 60 | ||
65 | return fou_udp_encap_recv_deliver(skb, fou->protocol, | 61 | fou_recv_pull(skb, sizeof(struct udphdr)); |
66 | sizeof(struct udphdr)); | 62 | |
63 | return -fou->protocol; | ||
64 | } | ||
65 | |||
66 | static int gue_control_message(struct sk_buff *skb, struct guehdr *guehdr) | ||
67 | { | ||
68 | /* No support yet */ | ||
69 | kfree_skb(skb); | ||
70 | return 0; | ||
67 | } | 71 | } |
68 | 72 | ||
69 | static int gue_udp_recv(struct sock *sk, struct sk_buff *skb) | 73 | static int gue_udp_recv(struct sock *sk, struct sk_buff *skb) |
70 | { | 74 | { |
71 | struct fou *fou = fou_from_sock(sk); | 75 | struct fou *fou = fou_from_sock(sk); |
72 | size_t len; | 76 | size_t len, optlen, hdrlen; |
73 | struct guehdr *guehdr; | 77 | struct guehdr *guehdr; |
74 | struct udphdr *uh; | 78 | void *data; |
75 | 79 | ||
76 | if (!fou) | 80 | if (!fou) |
77 | return 1; | 81 | return 1; |
@@ -80,25 +84,38 @@ static int gue_udp_recv(struct sock *sk, struct sk_buff *skb) | |||
80 | if (!pskb_may_pull(skb, len)) | 84 | if (!pskb_may_pull(skb, len)) |
81 | goto drop; | 85 | goto drop; |
82 | 86 | ||
83 | uh = udp_hdr(skb); | 87 | guehdr = (struct guehdr *)&udp_hdr(skb)[1]; |
84 | guehdr = (struct guehdr *)&uh[1]; | 88 | |
89 | optlen = guehdr->hlen << 2; | ||
90 | len += optlen; | ||
85 | 91 | ||
86 | len += guehdr->hlen << 2; | ||
87 | if (!pskb_may_pull(skb, len)) | 92 | if (!pskb_may_pull(skb, len)) |
88 | goto drop; | 93 | goto drop; |
89 | 94 | ||
90 | uh = udp_hdr(skb); | 95 | /* guehdr may change after pull */ |
91 | guehdr = (struct guehdr *)&uh[1]; | 96 | guehdr = (struct guehdr *)&udp_hdr(skb)[1]; |
92 | 97 | ||
93 | if (guehdr->version != 0) | 98 | hdrlen = sizeof(struct guehdr) + optlen; |
94 | goto drop; | ||
95 | 99 | ||
96 | if (guehdr->flags) { | 100 | if (guehdr->version != 0 || validate_gue_flags(guehdr, optlen)) |
97 | /* No support yet */ | ||
98 | goto drop; | 101 | goto drop; |
102 | |||
103 | /* Pull UDP and GUE headers */ | ||
104 | fou_recv_pull(skb, len); | ||
105 | |||
106 | data = &guehdr[1]; | ||
107 | |||
108 | if (guehdr->flags & GUE_FLAG_PRIV) { | ||
109 | data += GUE_LEN_PRIV; | ||
110 | |||
111 | /* Process private flags */ | ||
99 | } | 112 | } |
100 | 113 | ||
101 | return fou_udp_encap_recv_deliver(skb, guehdr->next_hdr, len); | 114 | if (unlikely(guehdr->control)) |
115 | return gue_control_message(skb, guehdr); | ||
116 | |||
117 | return -guehdr->proto_ctype; | ||
118 | |||
102 | drop: | 119 | drop: |
103 | kfree_skb(skb); | 120 | kfree_skb(skb); |
104 | return 0; | 121 | return 0; |
@@ -154,36 +171,47 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head, | |||
154 | const struct net_offload *ops; | 171 | const struct net_offload *ops; |
155 | struct sk_buff **pp = NULL; | 172 | struct sk_buff **pp = NULL; |
156 | struct sk_buff *p; | 173 | struct sk_buff *p; |
157 | u8 proto; | ||
158 | struct guehdr *guehdr; | 174 | struct guehdr *guehdr; |
159 | unsigned int hlen, guehlen; | 175 | size_t len, optlen, hdrlen, off; |
160 | unsigned int off; | 176 | void *data; |
161 | int flush = 1; | 177 | int flush = 1; |
162 | 178 | ||
163 | off = skb_gro_offset(skb); | 179 | off = skb_gro_offset(skb); |
164 | hlen = off + sizeof(*guehdr); | 180 | len = off + sizeof(*guehdr); |
181 | |||
165 | guehdr = skb_gro_header_fast(skb, off); | 182 | guehdr = skb_gro_header_fast(skb, off); |
166 | if (skb_gro_header_hard(skb, hlen)) { | 183 | if (skb_gro_header_hard(skb, len)) { |
167 | guehdr = skb_gro_header_slow(skb, hlen, off); | 184 | guehdr = skb_gro_header_slow(skb, len, off); |
168 | if (unlikely(!guehdr)) | 185 | if (unlikely(!guehdr)) |
169 | goto out; | 186 | goto out; |
170 | } | 187 | } |
171 | 188 | ||
172 | proto = guehdr->next_hdr; | 189 | optlen = guehdr->hlen << 2; |
190 | len += optlen; | ||
173 | 191 | ||
174 | rcu_read_lock(); | 192 | if (skb_gro_header_hard(skb, len)) { |
175 | offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads; | 193 | guehdr = skb_gro_header_slow(skb, len, off); |
176 | ops = rcu_dereference(offloads[proto]); | 194 | if (unlikely(!guehdr)) |
177 | if (WARN_ON(!ops || !ops->callbacks.gro_receive)) | 195 | goto out; |
178 | goto out_unlock; | 196 | } |
179 | 197 | ||
180 | guehlen = sizeof(*guehdr) + (guehdr->hlen << 2); | 198 | if (unlikely(guehdr->control) || guehdr->version != 0 || |
199 | validate_gue_flags(guehdr, optlen)) | ||
200 | goto out; | ||
181 | 201 | ||
182 | hlen = off + guehlen; | 202 | hdrlen = sizeof(*guehdr) + optlen; |
183 | if (skb_gro_header_hard(skb, hlen)) { | 203 | |
184 | guehdr = skb_gro_header_slow(skb, hlen, off); | 204 | skb_gro_pull(skb, hdrlen); |
185 | if (unlikely(!guehdr)) | 205 | |
186 | goto out_unlock; | 206 | /* Adjusted NAPI_GRO_CB(skb)->csum after skb_gro_pull()*/ |
207 | skb_gro_postpull_rcsum(skb, guehdr, hdrlen); | ||
208 | |||
209 | data = &guehdr[1]; | ||
210 | |||
211 | if (guehdr->flags & GUE_FLAG_PRIV) { | ||
212 | data += GUE_LEN_PRIV; | ||
213 | |||
214 | /* Process private flags */ | ||
187 | } | 215 | } |
188 | 216 | ||
189 | flush = 0; | 217 | flush = 0; |
@@ -197,7 +225,7 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head, | |||
197 | guehdr2 = (struct guehdr *)(p->data + off); | 225 | guehdr2 = (struct guehdr *)(p->data + off); |
198 | 226 | ||
199 | /* Compare base GUE header to be equal (covers | 227 | /* Compare base GUE header to be equal (covers |
200 | * hlen, version, next_hdr, and flags. | 228 | * hlen, version, proto_ctype, and flags. |
201 | */ | 229 | */ |
202 | if (guehdr->word != guehdr2->word) { | 230 | if (guehdr->word != guehdr2->word) { |
203 | NAPI_GRO_CB(p)->same_flow = 0; | 231 | NAPI_GRO_CB(p)->same_flow = 0; |
@@ -212,10 +240,11 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head, | |||
212 | } | 240 | } |
213 | } | 241 | } |
214 | 242 | ||
215 | skb_gro_pull(skb, guehlen); | 243 | rcu_read_lock(); |
216 | 244 | offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads; | |
217 | /* Adjusted NAPI_GRO_CB(skb)->csum after skb_gro_pull()*/ | 245 | ops = rcu_dereference(offloads[guehdr->proto_ctype]); |
218 | skb_gro_postpull_rcsum(skb, guehdr, guehlen); | 246 | if (WARN_ON(!ops || !ops->callbacks.gro_receive)) |
247 | goto out_unlock; | ||
219 | 248 | ||
220 | pp = ops->callbacks.gro_receive(head, skb); | 249 | pp = ops->callbacks.gro_receive(head, skb); |
221 | 250 | ||
@@ -236,7 +265,7 @@ static int gue_gro_complete(struct sk_buff *skb, int nhoff) | |||
236 | u8 proto; | 265 | u8 proto; |
237 | int err = -ENOENT; | 266 | int err = -ENOENT; |
238 | 267 | ||
239 | proto = guehdr->next_hdr; | 268 | proto = guehdr->proto_ctype; |
240 | 269 | ||
241 | guehlen = sizeof(*guehdr) + (guehdr->hlen << 2); | 270 | guehlen = sizeof(*guehdr) + (guehdr->hlen << 2); |
242 | 271 | ||
@@ -533,8 +562,12 @@ int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, | |||
533 | bool csum = !!(e->flags & TUNNEL_ENCAP_FLAG_CSUM); | 562 | bool csum = !!(e->flags & TUNNEL_ENCAP_FLAG_CSUM); |
534 | int type = csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL; | 563 | int type = csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL; |
535 | struct guehdr *guehdr; | 564 | struct guehdr *guehdr; |
536 | size_t hdr_len = sizeof(struct guehdr); | 565 | size_t optlen = 0; |
537 | __be16 sport; | 566 | __be16 sport; |
567 | void *data; | ||
568 | bool need_priv = false; | ||
569 | |||
570 | optlen += need_priv ? GUE_LEN_PRIV : 0; | ||
538 | 571 | ||
539 | skb = iptunnel_handle_offloads(skb, csum, type); | 572 | skb = iptunnel_handle_offloads(skb, csum, type); |
540 | 573 | ||
@@ -545,14 +578,27 @@ int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, | |||
545 | sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev), | 578 | sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev), |
546 | skb, 0, 0, false); | 579 | skb, 0, 0, false); |
547 | 580 | ||
548 | skb_push(skb, hdr_len); | 581 | skb_push(skb, sizeof(struct guehdr) + optlen); |
549 | 582 | ||
550 | guehdr = (struct guehdr *)skb->data; | 583 | guehdr = (struct guehdr *)skb->data; |
551 | 584 | ||
585 | guehdr->control = 0; | ||
552 | guehdr->version = 0; | 586 | guehdr->version = 0; |
553 | guehdr->hlen = 0; | 587 | guehdr->hlen = optlen >> 2; |
554 | guehdr->flags = 0; | 588 | guehdr->flags = 0; |
555 | guehdr->next_hdr = *protocol; | 589 | guehdr->proto_ctype = *protocol; |
590 | |||
591 | data = &guehdr[1]; | ||
592 | |||
593 | if (need_priv) { | ||
594 | __be32 *flags = data; | ||
595 | |||
596 | guehdr->flags |= GUE_FLAG_PRIV; | ||
597 | *flags = 0; | ||
598 | data += GUE_LEN_PRIV; | ||
599 | |||
600 | /* Add private flags */ | ||
601 | } | ||
556 | 602 | ||
557 | fou_build_udp(skb, e, fl4, protocol, sport); | 603 | fou_build_udp(skb, e, fl4, protocol, sport); |
558 | 604 | ||