aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/fou.c
diff options
context:
space:
mode:
authorTom Herbert <therbert@google.com>2014-11-04 12:06:53 -0500
committerDavid S. Miller <davem@davemloft.net>2014-11-05 16:30:03 -0500
commit5024c33ac354577635c5671498891eb197f3ec4d (patch)
treebb59341e98a708195c3ff6fe3f704a6f0f2dc002 /net/ipv4/fou.c
parent4bcb877d257c87298aedead1ffeaba0d5df1991d (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.c142
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
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