Commit fcb6aa86 authored by Toshiaki Makita's avatar Toshiaki Makita Committed by Pablo Neira Ayuso
Browse files

act_ct: Support GRE offload



Support GREv0 without NAT.

Signed-off-by: default avatarToshiaki Makita <toshiaki.makita1@gmail.com>
Acked-by: default avatarPaul Blakey <paulb@nvidia.com>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 4e8d9584
Loading
Loading
Loading
Loading
+91 −24
Original line number Diff line number Diff line
@@ -415,6 +415,19 @@ static void tcf_ct_flow_table_process_conn(struct tcf_ct_flow_table *ct_ft,
		break;
	case IPPROTO_UDP:
		break;
#ifdef CONFIG_NF_CT_PROTO_GRE
	case IPPROTO_GRE: {
		struct nf_conntrack_tuple *tuple;

		if (ct->status & IPS_NAT_MASK)
			return;
		tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
		/* No support for GRE v1 */
		if (tuple->src.u.gre.key || tuple->dst.u.gre.key)
			return;
		break;
	}
#endif
	default:
		return;
	}
@@ -434,6 +447,8 @@ tcf_ct_flow_table_fill_tuple_ipv4(struct sk_buff *skb,
	struct flow_ports *ports;
	unsigned int thoff;
	struct iphdr *iph;
	size_t hdrsize;
	u8 ipproto;

	if (!pskb_network_may_pull(skb, sizeof(*iph)))
		return false;
@@ -445,29 +460,54 @@ tcf_ct_flow_table_fill_tuple_ipv4(struct sk_buff *skb,
	    unlikely(thoff != sizeof(struct iphdr)))
		return false;

	if (iph->protocol != IPPROTO_TCP &&
	    iph->protocol != IPPROTO_UDP)
	ipproto = iph->protocol;
	switch (ipproto) {
	case IPPROTO_TCP:
		hdrsize = sizeof(struct tcphdr);
		break;
	case IPPROTO_UDP:
		hdrsize = sizeof(*ports);
		break;
#ifdef CONFIG_NF_CT_PROTO_GRE
	case IPPROTO_GRE:
		hdrsize = sizeof(struct gre_base_hdr);
		break;
#endif
	default:
		return false;
	}

	if (iph->ttl <= 1)
		return false;

	if (!pskb_network_may_pull(skb, iph->protocol == IPPROTO_TCP ?
					thoff + sizeof(struct tcphdr) :
					thoff + sizeof(*ports)))
	if (!pskb_network_may_pull(skb, thoff + hdrsize))
		return false;

	iph = ip_hdr(skb);
	if (iph->protocol == IPPROTO_TCP)
	switch (ipproto) {
	case IPPROTO_TCP:
		*tcph = (void *)(skb_network_header(skb) + thoff);

		fallthrough;
	case IPPROTO_UDP:
		ports = (struct flow_ports *)(skb_network_header(skb) + thoff);
	tuple->src_v4.s_addr = iph->saddr;
	tuple->dst_v4.s_addr = iph->daddr;
		tuple->src_port = ports->source;
		tuple->dst_port = ports->dest;
		break;
	case IPPROTO_GRE: {
		struct gre_base_hdr *greh;

		greh = (struct gre_base_hdr *)(skb_network_header(skb) + thoff);
		if ((greh->flags & GRE_VERSION) != GRE_VERSION_0)
			return false;
		break;
	}
	}

	iph = ip_hdr(skb);

	tuple->src_v4.s_addr = iph->saddr;
	tuple->dst_v4.s_addr = iph->daddr;
	tuple->l3proto = AF_INET;
	tuple->l4proto = iph->protocol;
	tuple->l4proto = ipproto;

	return true;
}
@@ -480,36 +520,63 @@ tcf_ct_flow_table_fill_tuple_ipv6(struct sk_buff *skb,
	struct flow_ports *ports;
	struct ipv6hdr *ip6h;
	unsigned int thoff;
	size_t hdrsize;
	u8 nexthdr;

	if (!pskb_network_may_pull(skb, sizeof(*ip6h)))
		return false;

	ip6h = ipv6_hdr(skb);
	thoff = sizeof(*ip6h);

	if (ip6h->nexthdr != IPPROTO_TCP &&
	    ip6h->nexthdr != IPPROTO_UDP)
		return false;
	nexthdr = ip6h->nexthdr;
	switch (nexthdr) {
	case IPPROTO_TCP:
		hdrsize = sizeof(struct tcphdr);
		break;
	case IPPROTO_UDP:
		hdrsize = sizeof(*ports);
		break;
#ifdef CONFIG_NF_CT_PROTO_GRE
	case IPPROTO_GRE:
		hdrsize = sizeof(struct gre_base_hdr);
		break;
#endif
	default:
		return -1;
	}

	if (ip6h->hop_limit <= 1)
		return false;

	thoff = sizeof(*ip6h);
	if (!pskb_network_may_pull(skb, ip6h->nexthdr == IPPROTO_TCP ?
					thoff + sizeof(struct tcphdr) :
					thoff + sizeof(*ports)))
	if (!pskb_network_may_pull(skb, thoff + hdrsize))
		return false;

	ip6h = ipv6_hdr(skb);
	if (ip6h->nexthdr == IPPROTO_TCP)
	switch (nexthdr) {
	case IPPROTO_TCP:
		*tcph = (void *)(skb_network_header(skb) + thoff);

		fallthrough;
	case IPPROTO_UDP:
		ports = (struct flow_ports *)(skb_network_header(skb) + thoff);
	tuple->src_v6 = ip6h->saddr;
	tuple->dst_v6 = ip6h->daddr;
		tuple->src_port = ports->source;
		tuple->dst_port = ports->dest;
		break;
	case IPPROTO_GRE: {
		struct gre_base_hdr *greh;

		greh = (struct gre_base_hdr *)(skb_network_header(skb) + thoff);
		if ((greh->flags & GRE_VERSION) != GRE_VERSION_0)
			return false;
		break;
	}
	}

	ip6h = ipv6_hdr(skb);

	tuple->src_v6 = ip6h->saddr;
	tuple->dst_v6 = ip6h->daddr;
	tuple->l3proto = AF_INET6;
	tuple->l4proto = ip6h->nexthdr;
	tuple->l4proto = nexthdr;

	return true;
}