aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/fou.c
diff options
context:
space:
mode:
authorTom Herbert <therbert@google.com>2014-10-03 18:48:09 -0400
committerDavid S. Miller <davem@davemloft.net>2014-10-03 19:53:33 -0400
commit37dd0247797b168ad1cc7f5dbec825a1ee66535b (patch)
tree93d4136738152f6e02ef98d23cbf537ca531fa9e /net/ipv4/fou.c
parentefc98d08e1ec4fd131f794370b274dceaf32c958 (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.c196
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
29struct fou_cfg { 30struct 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
69static 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);
99drop:
100 kfree_skb(skb);
101 return 0;
102}
103
67static struct sk_buff **fou_gro_receive(struct sk_buff **head, 104static 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
147static 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
219out_unlock:
220 rcu_read_unlock();
221out:
222 NAPI_GRO_CB(skb)->flush |= flush;
223
224 return pp;
225}
226
227static 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
248out_unlock:
249 rcu_read_unlock();
250 return err;
251}
252
110static int fou_add_to_port_list(struct fou *fou) 253static 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
288static 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
300static 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
145static int fou_create(struct net *net, struct fou_cfg *cfg, 310static 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
245static int parse_nl_config(struct genl_info *info, 420static 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