Skip to content

Commit 5024c33

Browse files
Tom Herbertdavem330
authored andcommitted
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 <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 4bcb877 commit 5024c33

File tree

2 files changed

+189
-53
lines changed

2 files changed

+189
-53
lines changed

include/net/gue.h

Lines changed: 95 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,113 @@
11
#ifndef __NET_GUE_H
22
#define __NET_GUE_H
33

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+
432
struct guehdr {
533
union {
634
struct {
735
#if defined(__LITTLE_ENDIAN_BITFIELD)
8-
__u8 hlen:4,
9-
version:4;
36+
__u8 hlen:5,
37+
control:1,
38+
version:2;
1039
#elif defined (__BIG_ENDIAN_BITFIELD)
11-
__u8 version:4,
12-
hlen:4;
40+
__u8 version:2,
41+
control:1,
42+
hlen:5;
1343
#else
1444
#error "Please fix <asm/byteorder.h>"
1545
#endif
16-
__u8 next_hdr;
46+
__u8 proto_ctype;
1747
__u16 flags;
1848
};
1949
__u32 word;
2050
};
2151
};
2252

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+
*/
69+
static inline size_t guehdr_flags_len(__be16 flags)
70+
{
71+
return ((flags & GUE_FLAG_PRIV) ? GUE_LEN_PRIV : 0);
72+
}
73+
74+
static 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+
*/
83+
static 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+
23113
#endif

net/ipv4/fou.c

Lines changed: 94 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,17 @@ static inline struct fou *fou_from_sock(struct sock *sk)
3838
return sk->sk_user_data;
3939
}
4040

41-
static int fou_udp_encap_recv_deliver(struct sk_buff *skb,
42-
u8 protocol, size_t len)
41+
static void fou_recv_pull(struct sk_buff *skb, size_t len)
4342
{
4443
struct iphdr *iph = ip_hdr(skb);
4544

4645
/* Remove 'len' bytes from the packet (UDP header and
47-
* FOU header if present), modify the protocol to the one
48-
* we found, and then call rcv_encap.
46+
* FOU header if present).
4947
*/
5048
iph->tot_len = htons(ntohs(iph->tot_len) - len);
5149
__skb_pull(skb, len);
5250
skb_postpull_rcsum(skb, udp_hdr(skb), len);
5351
skb_reset_transport_header(skb);
54-
55-
return -protocol;
5652
}
5753

5854
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)
6258
if (!fou)
6359
return 1;
6460

65-
return fou_udp_encap_recv_deliver(skb, fou->protocol,
66-
sizeof(struct udphdr));
61+
fou_recv_pull(skb, 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;
6771
}
6872

6973
static int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
7074
{
7175
struct fou *fou = fou_from_sock(sk);
72-
size_t len;
76+
size_t len, optlen, hdrlen;
7377
struct guehdr *guehdr;
74-
struct udphdr *uh;
78+
void *data;
7579

7680
if (!fou)
7781
return 1;
@@ -80,25 +84,38 @@ static int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
8084
if (!pskb_may_pull(skb, len))
8185
goto drop;
8286

83-
uh = udp_hdr(skb);
84-
guehdr = (struct guehdr *)&uh[1];
87+
guehdr = (struct guehdr *)&udp_hdr(skb)[1];
88+
89+
optlen = guehdr->hlen << 2;
90+
len += optlen;
8591

86-
len += guehdr->hlen << 2;
8792
if (!pskb_may_pull(skb, len))
8893
goto drop;
8994

90-
uh = udp_hdr(skb);
91-
guehdr = (struct guehdr *)&uh[1];
95+
/* guehdr may change after pull */
96+
guehdr = (struct guehdr *)&udp_hdr(skb)[1];
9297

93-
if (guehdr->version != 0)
94-
goto drop;
98+
hdrlen = sizeof(struct guehdr) + optlen;
9599

96-
if (guehdr->flags) {
97-
/* No support yet */
100+
if (guehdr->version != 0 || validate_gue_flags(guehdr, optlen))
98101
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 */
99112
}
100113

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+
102119
drop:
103120
kfree_skb(skb);
104121
return 0;
@@ -154,36 +171,47 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head,
154171
const struct net_offload *ops;
155172
struct sk_buff **pp = NULL;
156173
struct sk_buff *p;
157-
u8 proto;
158174
struct guehdr *guehdr;
159-
unsigned int hlen, guehlen;
160-
unsigned int off;
175+
size_t len, optlen, hdrlen, off;
176+
void *data;
161177
int flush = 1;
162178

163179
off = skb_gro_offset(skb);
164-
hlen = off + sizeof(*guehdr);
180+
len = off + sizeof(*guehdr);
181+
165182
guehdr = skb_gro_header_fast(skb, off);
166-
if (skb_gro_header_hard(skb, hlen)) {
167-
guehdr = skb_gro_header_slow(skb, hlen, off);
183+
if (skb_gro_header_hard(skb, len)) {
184+
guehdr = skb_gro_header_slow(skb, len, off);
168185
if (unlikely(!guehdr))
169186
goto out;
170187
}
171188

172-
proto = guehdr->next_hdr;
189+
optlen = guehdr->hlen << 2;
190+
len += optlen;
173191

174-
rcu_read_lock();
175-
offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
176-
ops = rcu_dereference(offloads[proto]);
177-
if (WARN_ON(!ops || !ops->callbacks.gro_receive))
178-
goto out_unlock;
192+
if (skb_gro_header_hard(skb, len)) {
193+
guehdr = skb_gro_header_slow(skb, len, off);
194+
if (unlikely(!guehdr))
195+
goto out;
196+
}
179197

180-
guehlen = sizeof(*guehdr) + (guehdr->hlen << 2);
198+
if (unlikely(guehdr->control) || guehdr->version != 0 ||
199+
validate_gue_flags(guehdr, optlen))
200+
goto out;
181201

182-
hlen = off + guehlen;
183-
if (skb_gro_header_hard(skb, hlen)) {
184-
guehdr = skb_gro_header_slow(skb, hlen, off);
185-
if (unlikely(!guehdr))
186-
goto out_unlock;
202+
hdrlen = sizeof(*guehdr) + optlen;
203+
204+
skb_gro_pull(skb, hdrlen);
205+
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 */
187215
}
188216

189217
flush = 0;
@@ -197,7 +225,7 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head,
197225
guehdr2 = (struct guehdr *)(p->data + off);
198226

199227
/* Compare base GUE header to be equal (covers
200-
* hlen, version, next_hdr, and flags.
228+
* hlen, version, proto_ctype, and flags.
201229
*/
202230
if (guehdr->word != guehdr2->word) {
203231
NAPI_GRO_CB(p)->same_flow = 0;
@@ -212,10 +240,11 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head,
212240
}
213241
}
214242

215-
skb_gro_pull(skb, guehlen);
216-
217-
/* Adjusted NAPI_GRO_CB(skb)->csum after skb_gro_pull()*/
218-
skb_gro_postpull_rcsum(skb, guehdr, guehlen);
243+
rcu_read_lock();
244+
offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
245+
ops = rcu_dereference(offloads[guehdr->proto_ctype]);
246+
if (WARN_ON(!ops || !ops->callbacks.gro_receive))
247+
goto out_unlock;
219248

220249
pp = ops->callbacks.gro_receive(head, skb);
221250

@@ -236,7 +265,7 @@ static int gue_gro_complete(struct sk_buff *skb, int nhoff)
236265
u8 proto;
237266
int err = -ENOENT;
238267

239-
proto = guehdr->next_hdr;
268+
proto = guehdr->proto_ctype;
240269

241270
guehlen = sizeof(*guehdr) + (guehdr->hlen << 2);
242271

@@ -533,8 +562,12 @@ int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
533562
bool csum = !!(e->flags & TUNNEL_ENCAP_FLAG_CSUM);
534563
int type = csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL;
535564
struct guehdr *guehdr;
536-
size_t hdr_len = sizeof(struct guehdr);
565+
size_t optlen = 0;
537566
__be16 sport;
567+
void *data;
568+
bool need_priv = false;
569+
570+
optlen += need_priv ? GUE_LEN_PRIV : 0;
538571

539572
skb = iptunnel_handle_offloads(skb, csum, type);
540573

@@ -545,14 +578,27 @@ int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
545578
sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev),
546579
skb, 0, 0, false);
547580

548-
skb_push(skb, hdr_len);
581+
skb_push(skb, sizeof(struct guehdr) + optlen);
549582

550583
guehdr = (struct guehdr *)skb->data;
551584

585+
guehdr->control = 0;
552586
guehdr->version = 0;
553-
guehdr->hlen = 0;
587+
guehdr->hlen = optlen >> 2;
554588
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+
}
556602

557603
fou_build_udp(skb, e, fl4, protocol, sport);
558604

0 commit comments

Comments
 (0)