aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/gue.h100
-rw-r--r--net/ipv4/fou.c142
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
4struct guehdr { 32struct 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 */
69static inline size_t guehdr_flags_len(__be16 flags)
70{
71 return ((flags & GUE_FLAG_PRIV) ? GUE_LEN_PRIV : 0);
72}
73
74static 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 */
83static 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
41static int fou_udp_encap_recv_deliver(struct sk_buff *skb, 41static 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
58static int fou_udp_recv(struct sock *sk, struct sk_buff *skb) 54static 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
66static 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
69static int gue_udp_recv(struct sock *sk, struct sk_buff *skb) 73static 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
102drop: 119drop:
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