diff options
author | Tom Herbert <therbert@google.com> | 2014-10-03 18:48:09 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-10-03 19:53:33 -0400 |
commit | 37dd0247797b168ad1cc7f5dbec825a1ee66535b (patch) | |
tree | 93d4136738152f6e02ef98d23cbf537ca531fa9e /net/ipv4/fou.c | |
parent | efc98d08e1ec4fd131f794370b274dceaf32c958 (diff) |
gue: Receive side for Generic UDP Encapsulation
This patch adds support receiving for GUE packets in the fou module. The
fou module now supports direct foo-over-udp (no encapsulation header)
and GUE. To support this a type parameter is added to the fou netlink
parameters.
For a GUE socket we define gue_udp_recv, gue_gro_receive, and
gue_gro_complete to handle the specifics of the GUE protocol. Most
of the code to manage and configure sockets is common with the fou.
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 | 196 |
1 files changed, 187 insertions, 9 deletions
diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c index 7e2126a31f2e..efa70ad44906 100644 --- a/net/ipv4/fou.c +++ b/net/ipv4/fou.c | |||
@@ -7,6 +7,7 @@ | |||
7 | #include <linux/types.h> | 7 | #include <linux/types.h> |
8 | #include <linux/kernel.h> | 8 | #include <linux/kernel.h> |
9 | #include <net/genetlink.h> | 9 | #include <net/genetlink.h> |
10 | #include <net/gue.h> | ||
10 | #include <net/ip.h> | 11 | #include <net/ip.h> |
11 | #include <net/protocol.h> | 12 | #include <net/protocol.h> |
12 | #include <net/udp.h> | 13 | #include <net/udp.h> |
@@ -27,6 +28,7 @@ struct fou { | |||
27 | }; | 28 | }; |
28 | 29 | ||
29 | struct fou_cfg { | 30 | struct fou_cfg { |
31 | u16 type; | ||
30 | u8 protocol; | 32 | u8 protocol; |
31 | struct udp_port_cfg udp_config; | 33 | struct udp_port_cfg udp_config; |
32 | }; | 34 | }; |
@@ -64,6 +66,41 @@ static int fou_udp_recv(struct sock *sk, struct sk_buff *skb) | |||
64 | sizeof(struct udphdr)); | 66 | sizeof(struct udphdr)); |
65 | } | 67 | } |
66 | 68 | ||
69 | static int gue_udp_recv(struct sock *sk, struct sk_buff *skb) | ||
70 | { | ||
71 | struct fou *fou = fou_from_sock(sk); | ||
72 | size_t len; | ||
73 | struct guehdr *guehdr; | ||
74 | struct udphdr *uh; | ||
75 | |||
76 | if (!fou) | ||
77 | return 1; | ||
78 | |||
79 | len = sizeof(struct udphdr) + sizeof(struct guehdr); | ||
80 | if (!pskb_may_pull(skb, len)) | ||
81 | goto drop; | ||
82 | |||
83 | uh = udp_hdr(skb); | ||
84 | guehdr = (struct guehdr *)&uh[1]; | ||
85 | |||
86 | len += guehdr->hlen << 2; | ||
87 | if (!pskb_may_pull(skb, len)) | ||
88 | goto drop; | ||
89 | |||
90 | if (guehdr->version != 0) | ||
91 | goto drop; | ||
92 | |||
93 | if (guehdr->flags) { | ||
94 | /* No support yet */ | ||
95 | goto drop; | ||
96 | } | ||
97 | |||
98 | return fou_udp_encap_recv_deliver(skb, guehdr->next_hdr, len); | ||
99 | drop: | ||
100 | kfree_skb(skb); | ||
101 | return 0; | ||
102 | } | ||
103 | |||
67 | static struct sk_buff **fou_gro_receive(struct sk_buff **head, | 104 | static struct sk_buff **fou_gro_receive(struct sk_buff **head, |
68 | struct sk_buff *skb) | 105 | struct sk_buff *skb) |
69 | { | 106 | { |
@@ -107,6 +144,112 @@ out_unlock: | |||
107 | return err; | 144 | return err; |
108 | } | 145 | } |
109 | 146 | ||
147 | static struct sk_buff **gue_gro_receive(struct sk_buff **head, | ||
148 | struct sk_buff *skb) | ||
149 | { | ||
150 | const struct net_offload **offloads; | ||
151 | const struct net_offload *ops; | ||
152 | struct sk_buff **pp = NULL; | ||
153 | struct sk_buff *p; | ||
154 | u8 proto; | ||
155 | struct guehdr *guehdr; | ||
156 | unsigned int hlen, guehlen; | ||
157 | unsigned int off; | ||
158 | int flush = 1; | ||
159 | |||
160 | off = skb_gro_offset(skb); | ||
161 | hlen = off + sizeof(*guehdr); | ||
162 | guehdr = skb_gro_header_fast(skb, off); | ||
163 | if (skb_gro_header_hard(skb, hlen)) { | ||
164 | guehdr = skb_gro_header_slow(skb, hlen, off); | ||
165 | if (unlikely(!guehdr)) | ||
166 | goto out; | ||
167 | } | ||
168 | |||
169 | proto = guehdr->next_hdr; | ||
170 | |||
171 | rcu_read_lock(); | ||
172 | offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads; | ||
173 | ops = rcu_dereference(offloads[proto]); | ||
174 | if (WARN_ON(!ops || !ops->callbacks.gro_receive)) | ||
175 | goto out_unlock; | ||
176 | |||
177 | guehlen = sizeof(*guehdr) + (guehdr->hlen << 2); | ||
178 | |||
179 | hlen = off + guehlen; | ||
180 | if (skb_gro_header_hard(skb, hlen)) { | ||
181 | guehdr = skb_gro_header_slow(skb, hlen, off); | ||
182 | if (unlikely(!guehdr)) | ||
183 | goto out_unlock; | ||
184 | } | ||
185 | |||
186 | flush = 0; | ||
187 | |||
188 | for (p = *head; p; p = p->next) { | ||
189 | const struct guehdr *guehdr2; | ||
190 | |||
191 | if (!NAPI_GRO_CB(p)->same_flow) | ||
192 | continue; | ||
193 | |||
194 | guehdr2 = (struct guehdr *)(p->data + off); | ||
195 | |||
196 | /* Compare base GUE header to be equal (covers | ||
197 | * hlen, version, next_hdr, and flags. | ||
198 | */ | ||
199 | if (guehdr->word != guehdr2->word) { | ||
200 | NAPI_GRO_CB(p)->same_flow = 0; | ||
201 | continue; | ||
202 | } | ||
203 | |||
204 | /* Compare optional fields are the same. */ | ||
205 | if (guehdr->hlen && memcmp(&guehdr[1], &guehdr2[1], | ||
206 | guehdr->hlen << 2)) { | ||
207 | NAPI_GRO_CB(p)->same_flow = 0; | ||
208 | continue; | ||
209 | } | ||
210 | } | ||
211 | |||
212 | skb_gro_pull(skb, guehlen); | ||
213 | |||
214 | /* Adjusted NAPI_GRO_CB(skb)->csum after skb_gro_pull()*/ | ||
215 | skb_gro_postpull_rcsum(skb, guehdr, guehlen); | ||
216 | |||
217 | pp = ops->callbacks.gro_receive(head, skb); | ||
218 | |||
219 | out_unlock: | ||
220 | rcu_read_unlock(); | ||
221 | out: | ||
222 | NAPI_GRO_CB(skb)->flush |= flush; | ||
223 | |||
224 | return pp; | ||
225 | } | ||
226 | |||
227 | static int gue_gro_complete(struct sk_buff *skb, int nhoff) | ||
228 | { | ||
229 | const struct net_offload **offloads; | ||
230 | struct guehdr *guehdr = (struct guehdr *)(skb->data + nhoff); | ||
231 | const struct net_offload *ops; | ||
232 | unsigned int guehlen; | ||
233 | u8 proto; | ||
234 | int err = -ENOENT; | ||
235 | |||
236 | proto = guehdr->next_hdr; | ||
237 | |||
238 | guehlen = sizeof(*guehdr) + (guehdr->hlen << 2); | ||
239 | |||
240 | rcu_read_lock(); | ||
241 | offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads; | ||
242 | ops = rcu_dereference(offloads[proto]); | ||
243 | if (WARN_ON(!ops || !ops->callbacks.gro_complete)) | ||
244 | goto out_unlock; | ||
245 | |||
246 | err = ops->callbacks.gro_complete(skb, nhoff + guehlen); | ||
247 | |||
248 | out_unlock: | ||
249 | rcu_read_unlock(); | ||
250 | return err; | ||
251 | } | ||
252 | |||
110 | static int fou_add_to_port_list(struct fou *fou) | 253 | static int fou_add_to_port_list(struct fou *fou) |
111 | { | 254 | { |
112 | struct fou *fout; | 255 | struct fou *fout; |
@@ -142,6 +285,28 @@ static void fou_release(struct fou *fou) | |||
142 | kfree(fou); | 285 | kfree(fou); |
143 | } | 286 | } |
144 | 287 | ||
288 | static int fou_encap_init(struct sock *sk, struct fou *fou, struct fou_cfg *cfg) | ||
289 | { | ||
290 | udp_sk(sk)->encap_rcv = fou_udp_recv; | ||
291 | fou->protocol = cfg->protocol; | ||
292 | fou->udp_offloads.callbacks.gro_receive = fou_gro_receive; | ||
293 | fou->udp_offloads.callbacks.gro_complete = fou_gro_complete; | ||
294 | fou->udp_offloads.port = cfg->udp_config.local_udp_port; | ||
295 | fou->udp_offloads.ipproto = cfg->protocol; | ||
296 | |||
297 | return 0; | ||
298 | } | ||
299 | |||
300 | static int gue_encap_init(struct sock *sk, struct fou *fou, struct fou_cfg *cfg) | ||
301 | { | ||
302 | udp_sk(sk)->encap_rcv = gue_udp_recv; | ||
303 | fou->udp_offloads.callbacks.gro_receive = gue_gro_receive; | ||
304 | fou->udp_offloads.callbacks.gro_complete = gue_gro_complete; | ||
305 | fou->udp_offloads.port = cfg->udp_config.local_udp_port; | ||
306 | |||
307 | return 0; | ||
308 | } | ||
309 | |||
145 | static int fou_create(struct net *net, struct fou_cfg *cfg, | 310 | static int fou_create(struct net *net, struct fou_cfg *cfg, |
146 | struct socket **sockp) | 311 | struct socket **sockp) |
147 | { | 312 | { |
@@ -164,10 +329,24 @@ static int fou_create(struct net *net, struct fou_cfg *cfg, | |||
164 | 329 | ||
165 | sk = sock->sk; | 330 | sk = sock->sk; |
166 | 331 | ||
167 | /* Mark socket as an encapsulation socket. See net/ipv4/udp.c */ | 332 | fou->port = cfg->udp_config.local_udp_port; |
168 | fou->protocol = cfg->protocol; | 333 | |
169 | fou->port = cfg->udp_config.local_udp_port; | 334 | /* Initial for fou type */ |
170 | udp_sk(sk)->encap_rcv = fou_udp_recv; | 335 | switch (cfg->type) { |
336 | case FOU_ENCAP_DIRECT: | ||
337 | err = fou_encap_init(sk, fou, cfg); | ||
338 | if (err) | ||
339 | goto error; | ||
340 | break; | ||
341 | case FOU_ENCAP_GUE: | ||
342 | err = gue_encap_init(sk, fou, cfg); | ||
343 | if (err) | ||
344 | goto error; | ||
345 | break; | ||
346 | default: | ||
347 | err = -EINVAL; | ||
348 | goto error; | ||
349 | } | ||
171 | 350 | ||
172 | udp_sk(sk)->encap_type = 1; | 351 | udp_sk(sk)->encap_type = 1; |
173 | udp_encap_enable(); | 352 | udp_encap_enable(); |
@@ -179,11 +358,6 @@ static int fou_create(struct net *net, struct fou_cfg *cfg, | |||
179 | 358 | ||
180 | sk->sk_allocation = GFP_ATOMIC; | 359 | sk->sk_allocation = GFP_ATOMIC; |
181 | 360 | ||
182 | fou->udp_offloads.callbacks.gro_receive = fou_gro_receive; | ||
183 | fou->udp_offloads.callbacks.gro_complete = fou_gro_complete; | ||
184 | fou->udp_offloads.port = cfg->udp_config.local_udp_port; | ||
185 | fou->udp_offloads.ipproto = cfg->protocol; | ||
186 | |||
187 | if (cfg->udp_config.family == AF_INET) { | 361 | if (cfg->udp_config.family == AF_INET) { |
188 | err = udp_add_offload(&fou->udp_offloads); | 362 | err = udp_add_offload(&fou->udp_offloads); |
189 | if (err) | 363 | if (err) |
@@ -240,6 +414,7 @@ static struct nla_policy fou_nl_policy[FOU_ATTR_MAX + 1] = { | |||
240 | [FOU_ATTR_PORT] = { .type = NLA_U16, }, | 414 | [FOU_ATTR_PORT] = { .type = NLA_U16, }, |
241 | [FOU_ATTR_AF] = { .type = NLA_U8, }, | 415 | [FOU_ATTR_AF] = { .type = NLA_U8, }, |
242 | [FOU_ATTR_IPPROTO] = { .type = NLA_U8, }, | 416 | [FOU_ATTR_IPPROTO] = { .type = NLA_U8, }, |
417 | [FOU_ATTR_TYPE] = { .type = NLA_U8, }, | ||
243 | }; | 418 | }; |
244 | 419 | ||
245 | static int parse_nl_config(struct genl_info *info, | 420 | static int parse_nl_config(struct genl_info *info, |
@@ -267,6 +442,9 @@ static int parse_nl_config(struct genl_info *info, | |||
267 | if (info->attrs[FOU_ATTR_IPPROTO]) | 442 | if (info->attrs[FOU_ATTR_IPPROTO]) |
268 | cfg->protocol = nla_get_u8(info->attrs[FOU_ATTR_IPPROTO]); | 443 | cfg->protocol = nla_get_u8(info->attrs[FOU_ATTR_IPPROTO]); |
269 | 444 | ||
445 | if (info->attrs[FOU_ATTR_TYPE]) | ||
446 | cfg->type = nla_get_u8(info->attrs[FOU_ATTR_TYPE]); | ||
447 | |||
270 | return 0; | 448 | return 0; |
271 | } | 449 | } |
272 | 450 | ||