[PATCH 10/26] IPVS: Add IPv6 handler functions to UDP protocol handler.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Define new IPv6-specific handler functions in UDP protocol handler. Set new
function pointers in ip_vs_protocol struct to point to these functions.

Signed-off-by: Julius R. Volz <juliusv@xxxxxxxxxx>

 1 files changed, 264 insertions(+), 1 deletions(-)

diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c
index 76e97ef..ef0d921 100644
--- a/net/netfilter/ipvs/ip_vs_proto_udp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_udp.c
@@ -49,6 +49,31 @@ udp_conn_in_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,
 	return cp;
 }
 
+#ifdef CONFIG_IP_VS_IPV6
+static struct ip_vs_conn *
+udp_conn_in_get_v6(const struct sk_buff *skb, struct ip_vs_protocol *pp,
+		   const struct ipv6hdr *iph, unsigned int proto_off, int inverse)
+{
+	struct ip_vs_conn *cp;
+	__be16 _ports[2], *pptr;
+
+	pptr = skb_header_pointer(skb, proto_off, sizeof(_ports), _ports);
+	if (pptr == NULL)
+		return NULL;
+
+	if (likely(!inverse)) {
+		cp = ip_vs_conn_in_get_v6(iph->nexthdr,
+				          &iph->saddr, pptr[0],
+				          &iph->daddr, pptr[1]);
+	} else {
+		cp = ip_vs_conn_in_get_v6(iph->nexthdr,
+				          &iph->daddr, pptr[1],
+				          &iph->saddr, pptr[0]);
+	}
+
+	return cp;
+}
+#endif
 
 static struct ip_vs_conn *
 udp_conn_out_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,
@@ -75,6 +100,33 @@ udp_conn_out_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,
 	return cp;
 }
 
+#ifdef CONFIG_IP_VS_IPV6
+static struct ip_vs_conn *
+udp_conn_out_get_v6(const struct sk_buff *skb, struct ip_vs_protocol *pp,
+		    const struct ipv6hdr *iph, unsigned int proto_off, int inverse)
+{
+	struct ip_vs_conn *cp;
+	__be16 _ports[2], *pptr;
+
+	pptr = skb_header_pointer(skb, sizeof(struct ipv6hdr),
+				  sizeof(_ports), _ports);
+	if (pptr == NULL)
+		return NULL;
+
+	if (likely(!inverse)) {
+		cp = ip_vs_conn_out_get_v6(iph->nexthdr,
+					   &iph->saddr, pptr[0],
+					   &iph->daddr, pptr[1]);
+	} else {
+		cp = ip_vs_conn_out_get_v6(iph->nexthdr,
+					   &iph->daddr, pptr[1],
+					   &iph->saddr, pptr[0]);
+	}
+
+	return cp;
+}
+#endif
+
 
 static int
 udp_conn_schedule(struct sk_buff *skb, struct ip_vs_protocol *pp,
@@ -116,6 +168,48 @@ udp_conn_schedule(struct sk_buff *skb, struct ip_vs_protocol *pp,
 	return 1;
 }
 
+#ifdef CONFIG_IP_VS_IPV6
+static int
+udp_conn_schedule_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
+		     int *verdict, struct ip_vs_conn **cpp)
+{
+	struct ip_vs_service *svc;
+	struct udphdr _udph, *uh;
+
+	uh = skb_header_pointer(skb, sizeof(struct ipv6hdr),
+				sizeof(_udph), &_udph);
+	if (uh == NULL) {
+		*verdict = NF_DROP;
+		return 0;
+	}
+
+	if ((svc = ip_vs_service_get_v6(skb->mark, ipv6_hdr(skb)->nexthdr,
+					&ipv6_hdr(skb)->daddr, uh->dest))) {
+		if (ip_vs_todrop()) {
+			/*
+			 * It seems that we are very loaded.
+			 * We have to drop this packet :(
+			 */
+			ip_vs_service_put(svc);
+			*verdict = NF_DROP;
+			return 0;
+		}
+
+		/*
+		 * Let the virtual server select a real server for the
+		 * incoming connection, and create a connection entry.
+		 */
+		*cpp = ip_vs_schedule_v6(svc, skb);
+		if (!*cpp) {
+			*verdict = ip_vs_leave_v6(svc, skb, pp);
+			return 0;
+		}
+		ip_vs_service_put(svc);
+	}
+	return 1;
+}
+#endif
+
 
 static inline void
 udp_fast_csum_update(struct udphdr *uhdr, __be32 oldip, __be32 newip,
@@ -129,6 +223,21 @@ udp_fast_csum_update(struct udphdr *uhdr, __be32 oldip, __be32 newip,
 		uhdr->check = CSUM_MANGLED_0;
 }
 
+#ifdef CONFIG_IP_VS_IPV6
+static inline void
+udp_fast_csum_update_v6(struct udphdr *uhdr, const struct in6_addr *oldip,
+			const struct in6_addr *newip, __be16 oldport,
+			__be16 newport)
+{
+	uhdr->check =
+		csum_fold(ip_vs_check_diff16(oldip->s6_addr32, newip->s6_addr32,
+				 ip_vs_check_diff2(oldport, newport,
+					~csum_unfold(uhdr->check))));
+	if (!uhdr->check)
+		uhdr->check = CSUM_MANGLED_0;
+}
+#endif
+
 static int
 udp_snat_handler(struct sk_buff *skb,
 		 struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
@@ -180,6 +289,59 @@ udp_snat_handler(struct sk_buff *skb,
 	return 1;
 }
 
+#ifdef CONFIG_IP_VS_IPV6
+static int
+udp_snat_handler_v6(struct sk_buff *skb,
+		    struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
+{
+	struct udphdr *udph;
+	const unsigned int udphoff = sizeof(struct ipv6hdr);
+
+	/* csum_check requires unshared skb */
+	if (!skb_make_writable(skb, udphoff+sizeof(*udph)))
+		return 0;
+
+	if (unlikely(cp->app != NULL)) {
+		/* Some checks before mangling */
+		if (pp->csum_check_v6 && !pp->csum_check_v6(skb, pp))
+			return 0;
+
+		/*
+		 *	Call application helper if needed
+		 */
+		if (!ip_vs_app_pkt_out(cp, skb))
+			return 0;
+	}
+
+	udph = (void *)ipv6_hdr(skb) + udphoff;
+	udph->source = cp->vport;
+
+	/*
+	 *	Adjust UDP checksums
+	 */
+	if (!cp->app && (udph->check != 0)) {
+		/* Only port and addr are changed, do fast csum update */
+		udp_fast_csum_update_v6(udph, &cp->daddr.v6, &cp->vaddr.v6,
+					cp->dport, cp->vport);
+		if (skb->ip_summed == CHECKSUM_COMPLETE)
+			skb->ip_summed = CHECKSUM_NONE;
+	} else {
+		/* full checksum calculation */
+		udph->check = 0;
+		skb->csum = skb_checksum(skb, udphoff, skb->len - udphoff, 0);
+		udph->check = csum_ipv6_magic(&cp->vaddr.v6, &cp->caddr.v6,
+					      skb->len - udphoff,
+					      cp->protocol, skb->csum);
+		if (udph->check == 0)
+			udph->check = CSUM_MANGLED_0;
+		IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%zd)\n",
+			  pp->name, udph->check,
+			  (char*)&(udph->check) - (char*)udph);
+	}
+	return 1;
+}
+#endif
+
 
 static int
 udp_dnat_handler(struct sk_buff *skb,
@@ -231,6 +393,58 @@ udp_dnat_handler(struct sk_buff *skb,
 	return 1;
 }
 
+#ifdef CONFIG_IP_VS_IPV6
+static int
+udp_dnat_handler_v6(struct sk_buff *skb,
+		    struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
+{
+	struct udphdr *udph;
+	unsigned int udphoff = sizeof(struct ipv6hdr);
+
+	/* csum_check requires unshared skb */
+	if (!skb_make_writable(skb, udphoff+sizeof(*udph)))
+		return 0;
+
+	if (unlikely(cp->app != NULL)) {
+		/* Some checks before mangling */
+		if (pp->csum_check_v6 && !pp->csum_check_v6(skb, pp))
+			return 0;
+
+		/*
+		 *	Attempt ip_vs_app call.
+		 *	It will fix ip_vs_conn
+		 */
+		if (!ip_vs_app_pkt_in(cp, skb))
+			return 0;
+	}
+
+	udph = (void *)ipv6_hdr(skb) + udphoff;
+	udph->dest = cp->dport;
+
+	/*
+	 *	Adjust UDP checksums
+	 */
+	if (!cp->app && (udph->check != 0)) {
+		/* Only port and addr are changed, do fast csum update */
+		udp_fast_csum_update_v6(udph, &cp->vaddr.v6, &cp->daddr.v6,
+					cp->vport, cp->dport);
+		if (skb->ip_summed == CHECKSUM_COMPLETE)
+			skb->ip_summed = CHECKSUM_NONE;
+	} else {
+		/* full checksum calculation */
+		udph->check = 0;
+		skb->csum = skb_checksum(skb, udphoff, skb->len - udphoff, 0);
+		udph->check = csum_ipv6_magic(&cp->caddr.v6, &cp->daddr.v6,
+					      skb->len - udphoff,
+					      cp->protocol, skb->csum);
+		if (udph->check == 0)
+			udph->check = CSUM_MANGLED_0;
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+	}
+	return 1;
+}
+#endif
+
 
 static int
 udp_csum_check(struct sk_buff *skb, struct ip_vs_protocol *pp)
@@ -266,6 +480,42 @@ udp_csum_check(struct sk_buff *skb, struct ip_vs_protocol *pp)
 	return 1;
 }
 
+#ifdef CONFIG_IP_VS_IPV6
+static int
+udp_csum_check_v6(struct sk_buff *skb, struct ip_vs_protocol *pp)
+{
+	struct udphdr _udph, *uh;
+	const unsigned int udphoff = sizeof(struct ipv6hdr);
+
+	uh = skb_header_pointer(skb, udphoff, sizeof(_udph), &_udph);
+	if (uh == NULL)
+		return 0;
+
+	if (uh->check != 0) {
+		switch (skb->ip_summed) {
+		case CHECKSUM_NONE:
+			skb->csum = skb_checksum(skb, udphoff,
+						 skb->len - udphoff, 0);
+		case CHECKSUM_COMPLETE:
+			if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+					    &ipv6_hdr(skb)->daddr,
+					    skb->len - udphoff,
+					    ipv6_hdr(skb)->nexthdr,
+					    skb->csum)) {
+				IP_VS_DBG_RL_PKT(0, pp, skb, 0,
+						 "Failed checksum for");
+				return 0;
+			}
+			break;
+		default:
+			/* No need to checksum. */
+			break;
+		}
+	}
+	return 1;
+}
+#endif
+
 
 /*
  *	Note: the caller guarantees that only one of register_app,
@@ -413,7 +663,6 @@ static void udp_exit(struct ip_vs_protocol *pp)
 {
 }
 
-
 struct ip_vs_protocol ip_vs_protocol_udp = {
 	.name =			"UDP",
 	.protocol =		IPPROTO_UDP,
@@ -422,11 +671,25 @@ struct ip_vs_protocol ip_vs_protocol_udp = {
 	.init =			udp_init,
 	.exit =			udp_exit,
 	.conn_schedule =	udp_conn_schedule,
+#ifdef CONFIG_IP_VS_IPV6
+	.conn_schedule_v6 =	udp_conn_schedule_v6,
+#endif
 	.conn_in_get =		udp_conn_in_get,
 	.conn_out_get =		udp_conn_out_get,
+#ifdef CONFIG_IP_VS_IPV6
+	.conn_in_get_v6 =	udp_conn_in_get_v6,
+	.conn_out_get_v6 =	udp_conn_out_get_v6,
+#endif
 	.snat_handler =		udp_snat_handler,
 	.dnat_handler =		udp_dnat_handler,
+#ifdef CONFIG_IP_VS_IPV6
+	.snat_handler_v6 =	udp_snat_handler_v6,
+	.dnat_handler_v6 =	udp_dnat_handler_v6,
+#endif
 	.csum_check =		udp_csum_check,
+#ifdef CONFIG_IP_VS_IPV6
+	.csum_check_v6 =	udp_csum_check_v6,
+#endif
 	.state_transition =	udp_state_transition,
 	.state_name =		udp_state_name,
 	.register_app =		udp_register_app,
-- 
1.5.3.6

--
To unsubscribe from this list: send the line "unsubscribe lvs-devel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Filesystem Devel]     [Linux NFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux SCSI]     [X.Org]

  Powered by Linux