Skip to contentSkip to conversation

ipvs ipv6 fragment handling

RFC In review Hans Schillstrom 2 participants 2 messages Feb 9, 2012 – Feb 13, 2012

Subsystems (5)
lore
Share & tools
lore
#0 lore raw

RFC 1/1 ipvs ipv6 fragment handling

Hello,
Long time since the last ipvs patches from me...

IPVS do not have any kind of fragmentation handling for IPv6, neither can it handle extension headers in front of upper layer headers.

The last issue is easy to fix with ipv6_find_hdr() which also is a part of this RFC. This implementation relies on how nf_defrag_ipv6 works today.

Netfilter-team (Patrick) pls let me know if ipv6-nat makes any big changes to the defrag procedure.

IPVS do like conntrack i.e. use the skb->nfct_reasm;
(i.e. when all fragments is collected, nf_ct_frag6_output() starts a "re-play" of all fragments into the interrupted PREROUTING chain att prio -399 with nfct_reasm pointing to the assembled packet.)

IPVS adds a new hook into prerouting chain at prio -100 to catch fragments, and copy fw-mark and routing info from the first packet with an upper layer header.

For the first fragment ip_vs_in() and ip_vs_out() uses the skb->nfct_reasm to "store" fw-mark" and skb_dst().
Which will be copied into the following fragments.

This patch needs "NETFILTER added flags to ipv6_find_hdr()" patch

The patch is far from ready since this is only a RFC,
ex ip_vs_nat_xmit_v6() doesn't work since it writes right into the fragment header.. tunnel is not tested at all.
Only Direct routing is tested. (not to localhost)

Plenty of formatting rules is violated and there is also debug printouts to remove... (and kmail seems to corrupt tabs)

IPVS will depend on ip6tables and its defragmentation but NOT on conntrack. Is the design idea OK ?
Will IPv6-NAT break everything ?
Is there a reason not to use the skb->nfct_reasm as "storage" for mark and dst_entry ?

This doesn't violate the RFC 2460 "fragmentation in IPv6 is performed only by source nodes"

include/net/ip_vs.h +45 -4
index ebe517f..2d3aeab 100644
@@ -22,6 +22,9 @@
 #include <linux/ip.h>
 #include <linux/ipv6.h>			/* for struct ipv6hdr */
 #include <net/ipv6.h>
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#endif
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
 #include <net/netfilter/nf_conntrack.h>
 #endif
@@ -105,8 +108,10 @@ extern int ip_vs_conn_tab_size;
 
 
 struct ip_vs_iphdr {
-	int len;
-	__u8 protocol;
+	__s32 len; 	/* offset where to find next header */
+	__s32 flags;
+	__u16 fragoffs;
+	__s16 protocol;
 	union nf_inet_addr saddr;
 	union nf_inet_addr daddr;
 };
@@ -132,6 +137,42 @@ ip_vs_fill_iphdr(int af, const void *nh, struct ip_vs_iphdr *iphdr)
 	}
 }
 
+static inline void
+ip_vs_fill_iph_skb(int af, const struct sk_buff *skb, struct ip_vs_iphdr *iphdr)
+{
+	iphdr->len = 0;
+	iphdr->flags = 0;
+#ifdef CONFIG_IP_VS_IPV6
+	if (af == AF_INET6) {
+		const struct ipv6hdr *iph = (struct ipv6hdr *)skb_network_header(skb);
+
+		iphdr->protocol = ipv6_find_hdr(skb, &iphdr->len, -1,
+		                                &iphdr->fragoffs,
+		                                &iphdr->flags);
+		iphdr->saddr.in6 = iph->saddr;
+		iphdr->daddr.in6 = iph->daddr;
+	} else
+#endif
+	{
+		const struct iphdr *iph = (struct iphdr *)skb_network_header(skb);
+		iphdr->len = iph->ihl * 4;
+		iphdr->protocol = iph->protocol;
+		iphdr->saddr.ip = iph->saddr;
+		iphdr->daddr.ip = iph->daddr;
+	}
+}
+#if defined(CONFIG_NF_DEFRAG_IPV6) || defined(CONFIG_NF_DEFRAG_IPV6_MODULE)
+static inline struct sk_buff *skb_nfct_reasm(const struct sk_buff *skb)
+{
+	return skb->nfct_reasm;
+}
+#else
+static inline struct sk_buff *skb_reasm(const struct sk_buff *skb)
+{
+	return NULL;
+}
+#endif
+
 static inline void ip_vs_addr_copy(int af, union nf_inet_addr *dst,
 				   const union nf_inet_addr *src)
 {
@@ -412,7 +453,7 @@ struct ip_vs_protocol {
 			const struct sk_buff *skb,
 			const struct ip_vs_iphdr *iph,
 			unsigned int proto_off,
-			int inverse);
+			int inverse, unsigned short foffs);
 
 	int (*snat_handler)(struct sk_buff *skb,
 			    struct ip_vs_protocol *pp, struct ip_vs_conn *cp);
@@ -1001,7 +1042,7 @@ struct ip_vs_conn *ip_vs_conn_out_get(const struct ip_vs_conn_param *p);
 struct ip_vs_conn * ip_vs_conn_out_get_proto(int af, const struct sk_buff *skb,
 					     const struct ip_vs_iphdr *iph,
 					     unsigned int proto_off,
-					     int inverse);
+					     int inverse, unsigned short foffs);
 
 /* put back the conn without restarting its timer */
 static inline void __ip_vs_conn_put(struct ip_vs_conn *cp)
net/netfilter/ipvs/ip_vs_conn.c +10 -4
index 29fa5ba..36e65ca 100644
@@ -432,13 +432,19 @@ struct ip_vs_conn *ip_vs_conn_out_get(const struct ip_vs_conn_param *p)
 
 struct ip_vs_conn *
 ip_vs_conn_out_get_proto(int af, const struct sk_buff *skb,
-			 const struct ip_vs_iphdr *iph,
-			 unsigned int proto_off, int inverse)
+			 const struct ip_vs_iphdr *iph, unsigned int proto_off,
+			 int inverse, unsigned short foffs)
 {
 	struct ip_vs_conn_param p;
 
-	if (ip_vs_conn_fill_param_proto(af, skb, iph, proto_off, inverse, &p))
-		return NULL;
+	if (unlikely(foffs)) {
+		if (ip_vs_conn_fill_param_proto(af, skb_nfct_reasm(skb), iph,
+						proto_off, inverse, &p))
+			return NULL;
+	} else {
+		if (ip_vs_conn_fill_param_proto(af, skb, iph, proto_off, inverse, &p))
+			return NULL;
+	}
 
 	return ip_vs_conn_out_get(&p);
 }
net/netfilter/ipvs/ip_vs_core.c +217 -125

581 lines not on this page · raw message

net/netfilter/ipvs/ip_vs_proto_ah_esp.c +1 -1
index 5b8eb8b..bda8560 100644
@@ -87,7 +87,7 @@ static struct ip_vs_conn *
 ah_esp_conn_out_get(int af, const struct sk_buff *skb,
 		    const struct ip_vs_iphdr *iph,
 		    unsigned int proto_off,
-		    int inverse)
+		    int inverse, unsigned short foffs)
 {
 	struct ip_vs_conn *cp;
 	struct ip_vs_conn_param p;
net/netfilter/ipvs/ip_vs_proto_tcp.c +1 -1
index ef8641f..0199bb2 100644
@@ -40,7 +40,7 @@ tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
 	struct tcphdr _tcph, *th;
 	struct ip_vs_iphdr iph;
 
-	ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
+	ip_vs_fill_iph_skb(af, skb, &iph);
 
 	th = skb_header_pointer(skb, iph.len, sizeof(_tcph), &_tcph);
 	if (th == NULL) {
net/netfilter/ipvs/ip_vs_proto_udp.c +1 -1
index f4b7262..6064f6e 100644
@@ -37,7 +37,7 @@ udp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
 	struct udphdr _udph, *uh;
 	struct ip_vs_iphdr iph;
 
-	ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
+	ip_vs_fill_iph_skb(af, skb, &iph);
 
 	uh = skb_header_pointer(skb, iph.len, sizeof(_udph), &_udph);
 	if (uh == NULL) {
net/netfilter/ipvs/ip_vs_xmit.c +5 -0
index 7fd66de..1d1b5ad 100644
@@ -1080,6 +1080,11 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
 		IP_VS_XMIT(NFPROTO_IPV6, skb, cp, 1);
 	}
 
+	IP_VS_DBG(1, "DR XMIT(%p): %pI6c %pI6c via %pI6c dp:%d fw:%d hdr:%d proto:%d len:%d\n",
+	          skb, &cp->caddr, &cp->vaddr, &cp->daddr,
+	          ntohs(cp->dport), cp->fwmark,
+	          ipv6_hdr(skb)->nexthdr, cp->protocol, skb->len);
+
 	/* MTU checking */
 	mtu = dst_mtu(&rt->dst);
 	if (skb->len > mtu) {
net/netfilter/xt_ipvs.c +1 -1
index bb10b07..d475f01 100644
@@ -85,7 +85,7 @@ ipvs_mt(const struct sk_buff *skb, struct xt_action_param *par)
 	/*
 	 * Check if the packet belongs to an existing entry
 	 */
-	cp = pp->conn_out_get(family, skb, &iph, iph.len, 1 /* inverse */);
+	cp = pp->conn_out_get(family, skb, &iph, iph.len, 1 /* inverse */, 0);
 	if (unlikely(cp == NULL)) {
 		match = false;
 		goto out;

---
Regards
Hans Schillstrom <[email protected]>

maintainer
#1 lore raw
Quoted 2 lines · Hans Schillstrom

Hello,
Long time since the last ipvs patches from me...

Hi Hans,

I apologise that I have not reviewed this patch yet. I will find time to do so soon.