|
@@ -38,21 +38,17 @@ static inline struct fou *fou_from_sock(struct sock *sk)
|
|
|
return sk->sk_user_data;
|
|
|
}
|
|
|
|
|
|
-static int fou_udp_encap_recv_deliver(struct sk_buff *skb,
|
|
|
- u8 protocol, size_t len)
|
|
|
+static void fou_recv_pull(struct sk_buff *skb, size_t len)
|
|
|
{
|
|
|
struct iphdr *iph = ip_hdr(skb);
|
|
|
|
|
|
/* Remove 'len' bytes from the packet (UDP header and
|
|
|
- * FOU header if present), modify the protocol to the one
|
|
|
- * we found, and then call rcv_encap.
|
|
|
+ * FOU header if present).
|
|
|
*/
|
|
|
iph->tot_len = htons(ntohs(iph->tot_len) - len);
|
|
|
__skb_pull(skb, len);
|
|
|
skb_postpull_rcsum(skb, udp_hdr(skb), len);
|
|
|
skb_reset_transport_header(skb);
|
|
|
-
|
|
|
- return -protocol;
|
|
|
}
|
|
|
|
|
|
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)
|
|
|
if (!fou)
|
|
|
return 1;
|
|
|
|
|
|
- return fou_udp_encap_recv_deliver(skb, fou->protocol,
|
|
|
- sizeof(struct udphdr));
|
|
|
+ fou_recv_pull(skb, sizeof(struct udphdr));
|
|
|
+
|
|
|
+ return -fou->protocol;
|
|
|
+}
|
|
|
+
|
|
|
+static int gue_control_message(struct sk_buff *skb, struct guehdr *guehdr)
|
|
|
+{
|
|
|
+ /* No support yet */
|
|
|
+ kfree_skb(skb);
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
|
|
|
{
|
|
|
struct fou *fou = fou_from_sock(sk);
|
|
|
- size_t len;
|
|
|
+ size_t len, optlen, hdrlen;
|
|
|
struct guehdr *guehdr;
|
|
|
- struct udphdr *uh;
|
|
|
+ void *data;
|
|
|
|
|
|
if (!fou)
|
|
|
return 1;
|
|
@@ -80,25 +84,38 @@ static int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
|
|
|
if (!pskb_may_pull(skb, len))
|
|
|
goto drop;
|
|
|
|
|
|
- uh = udp_hdr(skb);
|
|
|
- guehdr = (struct guehdr *)&uh[1];
|
|
|
+ guehdr = (struct guehdr *)&udp_hdr(skb)[1];
|
|
|
+
|
|
|
+ optlen = guehdr->hlen << 2;
|
|
|
+ len += optlen;
|
|
|
|
|
|
- len += guehdr->hlen << 2;
|
|
|
if (!pskb_may_pull(skb, len))
|
|
|
goto drop;
|
|
|
|
|
|
- uh = udp_hdr(skb);
|
|
|
- guehdr = (struct guehdr *)&uh[1];
|
|
|
+ /* guehdr may change after pull */
|
|
|
+ guehdr = (struct guehdr *)&udp_hdr(skb)[1];
|
|
|
|
|
|
- if (guehdr->version != 0)
|
|
|
- goto drop;
|
|
|
+ hdrlen = sizeof(struct guehdr) + optlen;
|
|
|
|
|
|
- if (guehdr->flags) {
|
|
|
- /* No support yet */
|
|
|
+ if (guehdr->version != 0 || validate_gue_flags(guehdr, optlen))
|
|
|
goto drop;
|
|
|
+
|
|
|
+ /* Pull UDP and GUE headers */
|
|
|
+ fou_recv_pull(skb, len);
|
|
|
+
|
|
|
+ data = &guehdr[1];
|
|
|
+
|
|
|
+ if (guehdr->flags & GUE_FLAG_PRIV) {
|
|
|
+ data += GUE_LEN_PRIV;
|
|
|
+
|
|
|
+ /* Process private flags */
|
|
|
}
|
|
|
|
|
|
- return fou_udp_encap_recv_deliver(skb, guehdr->next_hdr, len);
|
|
|
+ if (unlikely(guehdr->control))
|
|
|
+ return gue_control_message(skb, guehdr);
|
|
|
+
|
|
|
+ return -guehdr->proto_ctype;
|
|
|
+
|
|
|
drop:
|
|
|
kfree_skb(skb);
|
|
|
return 0;
|
|
@@ -154,36 +171,47 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head,
|
|
|
const struct net_offload *ops;
|
|
|
struct sk_buff **pp = NULL;
|
|
|
struct sk_buff *p;
|
|
|
- u8 proto;
|
|
|
struct guehdr *guehdr;
|
|
|
- unsigned int hlen, guehlen;
|
|
|
- unsigned int off;
|
|
|
+ size_t len, optlen, hdrlen, off;
|
|
|
+ void *data;
|
|
|
int flush = 1;
|
|
|
|
|
|
off = skb_gro_offset(skb);
|
|
|
- hlen = off + sizeof(*guehdr);
|
|
|
+ len = off + sizeof(*guehdr);
|
|
|
+
|
|
|
guehdr = skb_gro_header_fast(skb, off);
|
|
|
- if (skb_gro_header_hard(skb, hlen)) {
|
|
|
- guehdr = skb_gro_header_slow(skb, hlen, off);
|
|
|
+ if (skb_gro_header_hard(skb, len)) {
|
|
|
+ guehdr = skb_gro_header_slow(skb, len, off);
|
|
|
if (unlikely(!guehdr))
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
- proto = guehdr->next_hdr;
|
|
|
+ optlen = guehdr->hlen << 2;
|
|
|
+ len += optlen;
|
|
|
|
|
|
- rcu_read_lock();
|
|
|
- offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
|
|
|
- ops = rcu_dereference(offloads[proto]);
|
|
|
- if (WARN_ON(!ops || !ops->callbacks.gro_receive))
|
|
|
- goto out_unlock;
|
|
|
+ if (skb_gro_header_hard(skb, len)) {
|
|
|
+ guehdr = skb_gro_header_slow(skb, len, off);
|
|
|
+ if (unlikely(!guehdr))
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
|
|
|
- guehlen = sizeof(*guehdr) + (guehdr->hlen << 2);
|
|
|
+ if (unlikely(guehdr->control) || guehdr->version != 0 ||
|
|
|
+ validate_gue_flags(guehdr, optlen))
|
|
|
+ goto out;
|
|
|
|
|
|
- hlen = off + guehlen;
|
|
|
- if (skb_gro_header_hard(skb, hlen)) {
|
|
|
- guehdr = skb_gro_header_slow(skb, hlen, off);
|
|
|
- if (unlikely(!guehdr))
|
|
|
- goto out_unlock;
|
|
|
+ hdrlen = sizeof(*guehdr) + optlen;
|
|
|
+
|
|
|
+ skb_gro_pull(skb, hdrlen);
|
|
|
+
|
|
|
+ /* Adjusted NAPI_GRO_CB(skb)->csum after skb_gro_pull()*/
|
|
|
+ skb_gro_postpull_rcsum(skb, guehdr, hdrlen);
|
|
|
+
|
|
|
+ data = &guehdr[1];
|
|
|
+
|
|
|
+ if (guehdr->flags & GUE_FLAG_PRIV) {
|
|
|
+ data += GUE_LEN_PRIV;
|
|
|
+
|
|
|
+ /* Process private flags */
|
|
|
}
|
|
|
|
|
|
flush = 0;
|
|
@@ -197,7 +225,7 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head,
|
|
|
guehdr2 = (struct guehdr *)(p->data + off);
|
|
|
|
|
|
/* Compare base GUE header to be equal (covers
|
|
|
- * hlen, version, next_hdr, and flags.
|
|
|
+ * hlen, version, proto_ctype, and flags.
|
|
|
*/
|
|
|
if (guehdr->word != guehdr2->word) {
|
|
|
NAPI_GRO_CB(p)->same_flow = 0;
|
|
@@ -212,10 +240,11 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- skb_gro_pull(skb, guehlen);
|
|
|
-
|
|
|
- /* Adjusted NAPI_GRO_CB(skb)->csum after skb_gro_pull()*/
|
|
|
- skb_gro_postpull_rcsum(skb, guehdr, guehlen);
|
|
|
+ rcu_read_lock();
|
|
|
+ offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
|
|
|
+ ops = rcu_dereference(offloads[guehdr->proto_ctype]);
|
|
|
+ if (WARN_ON(!ops || !ops->callbacks.gro_receive))
|
|
|
+ goto out_unlock;
|
|
|
|
|
|
pp = ops->callbacks.gro_receive(head, skb);
|
|
|
|
|
@@ -236,7 +265,7 @@ static int gue_gro_complete(struct sk_buff *skb, int nhoff)
|
|
|
u8 proto;
|
|
|
int err = -ENOENT;
|
|
|
|
|
|
- proto = guehdr->next_hdr;
|
|
|
+ proto = guehdr->proto_ctype;
|
|
|
|
|
|
guehlen = sizeof(*guehdr) + (guehdr->hlen << 2);
|
|
|
|
|
@@ -533,8 +562,12 @@ int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
|
|
|
bool csum = !!(e->flags & TUNNEL_ENCAP_FLAG_CSUM);
|
|
|
int type = csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL;
|
|
|
struct guehdr *guehdr;
|
|
|
- size_t hdr_len = sizeof(struct guehdr);
|
|
|
+ size_t optlen = 0;
|
|
|
__be16 sport;
|
|
|
+ void *data;
|
|
|
+ bool need_priv = false;
|
|
|
+
|
|
|
+ optlen += need_priv ? GUE_LEN_PRIV : 0;
|
|
|
|
|
|
skb = iptunnel_handle_offloads(skb, csum, type);
|
|
|
|
|
@@ -545,14 +578,27 @@ int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
|
|
|
sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev),
|
|
|
skb, 0, 0, false);
|
|
|
|
|
|
- skb_push(skb, hdr_len);
|
|
|
+ skb_push(skb, sizeof(struct guehdr) + optlen);
|
|
|
|
|
|
guehdr = (struct guehdr *)skb->data;
|
|
|
|
|
|
+ guehdr->control = 0;
|
|
|
guehdr->version = 0;
|
|
|
- guehdr->hlen = 0;
|
|
|
+ guehdr->hlen = optlen >> 2;
|
|
|
guehdr->flags = 0;
|
|
|
- guehdr->next_hdr = *protocol;
|
|
|
+ guehdr->proto_ctype = *protocol;
|
|
|
+
|
|
|
+ data = &guehdr[1];
|
|
|
+
|
|
|
+ if (need_priv) {
|
|
|
+ __be32 *flags = data;
|
|
|
+
|
|
|
+ guehdr->flags |= GUE_FLAG_PRIV;
|
|
|
+ *flags = 0;
|
|
|
+ data += GUE_LEN_PRIV;
|
|
|
+
|
|
|
+ /* Add private flags */
|
|
|
+ }
|
|
|
|
|
|
fou_build_udp(skb, e, fl4, protocol, sport);
|
|
|
|