diff options
author | Tom Herbert <therbert@google.com> | 2014-11-04 12:06:53 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-11-05 16:30:03 -0500 |
commit | 5024c33ac354577635c5671498891eb197f3ec4d (patch) | |
tree | bb59341e98a708195c3ff6fe3f704a6f0f2dc002 /net/ipv4/fou.c | |
parent | 4bcb877d257c87298aedead1ffeaba0d5df1991d (diff) |
gue: Add infrastructure for flags and options
Add functions and basic definitions for processing standard flags,
private flags, and control messages. This includes definitions
to compute length of optional fields corresponding to a set of flags.
Flag validation is in validate_gue_flags function. This checks for
unknown flags, and that length of optional fields is <= length
in guehdr hlen.
Signed-off-by: Tom Herbert <therbert@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/fou.c')
-rw-r--r-- | net/ipv4/fou.c | 142 |
1 files changed, 94 insertions, 48 deletions
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 | ||