diff mbox

ipv6: allow nonlocal_bind for ipv6 addresses

Message ID bded64b7c4efc6584d1e5808af2fde8d.squirrel@webmail.shiwebs.net
State New
Headers show

Commit Message

Timh B June 26, 2013, 8:47 a.m. UTC
Hi,

We've encountered a problem when migrating services that uses vrrpd (for
example) to IPv6 (dualstack), a nonlocal_bind problem to be exact, for
IPv4 there's a sysctl-setting that allows us to bind to addresses that are
not configured on the host directly.

I'm aware that there's freebind and ip_transparent options to setsockopt()
but changing all the services's code would have been a larger task than
implementing the nonlocal_bind option for IPv6 in the kenrel.

The patch is done for kernel 3.5, so for 3.2 (Ubuntu 12.04 LTS) the patch
from Maciej Żenczykowski has to be applied first;

https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/commit/net/ipv6/af_inet6.c?id=f74024d9f05caa570dcf7582b498bbf011943491

My patch is attached.

Usage: sysctl -w net.ipv6.ip6_nonlocal_bind=1

-- Timh

Comments

Luis Henriques June 26, 2013, 4:53 p.m. UTC | #1
Hi,

"Timh B" <timh@shiwebs.net> writes:

> Hi,
>
> We've encountered a problem when migrating services that uses vrrpd (for
> example) to IPv6 (dualstack), a nonlocal_bind problem to be exact, for
> IPv4 there's a sysctl-setting that allows us to bind to addresses that are
> not configured on the host directly.
>
> I'm aware that there's freebind and ip_transparent options to setsockopt()
> but changing all the services's code would have been a larger task than
> implementing the nonlocal_bind option for IPv6 in the kenrel.
>
> The patch is done for kernel 3.5, so for 3.2 (Ubuntu 12.04 LTS) the patch
> from Maciej Żenczykowski has to be applied first;
>
> https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/commit/net/ipv6/af_inet6.c?id=f74024d9f05caa570dcf7582b498bbf011943491
>
> My patch is attached.

Although I can't assess the correctness of your patch, this is the
kind of changes I don't feel comfortable to see in the Ubuntu kernels
without being in mainline kernel.

From your description, this is an upstream problem so I think you
should try to submit it to lkml (and netdev, I guess), possibly
tagging it for inclusion in stable kernels as well.

Cheers,
diff mbox

Patch

From 44934986d09b6bd55023123b236e09db7a03778a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timh=20Bergstr=C3=B6m?= <timh@shiwebs.net>
Date: Tue, 25 Jun 2013 12:43:00 +0200
Subject: [PATCH] UBUNTU: SAUCE: Added ip_nonlocal_bind functionality for IPv6

Signed-off-by: timh <timh@shiwebs.net>

* Added sysctl for ipv6 equalivient of net.ipv4.ip_nonlocal_bind.
* Added check in inet6_bind() for ip6_nonlocal_bind so we allow
  binding to arbitrary IPv6 addresses on a host.

Same weaknesses and strengths that exists for ip_nonlocal_bind apply
on ip6_nonlocal_bind.
---
 include/linux/sysctl.h     |    1 +
 include/net/ipv6.h         |    2 ++
 include/net/netns/ipv6.h   |    1 +
 kernel/sysctl_binary.c     |    1 +
 net/ipv6/af_inet6.c        |    9 +++++++--
 net/ipv6/sysctl_net_ipv6.c |    7 +++++++
 6 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index c34b4c8..7af300f 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -528,6 +528,7 @@  enum {
 	NET_IPV6_IP6FRAG_TIME=23,
 	NET_IPV6_IP6FRAG_SECRET_INTERVAL=24,
 	NET_IPV6_MLD_MAX_MSF=25,
+        NET_IPV6_NONLOCAL_BIND=30,
 };
 
 enum {
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index c11fa5d..44fdad6 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -583,6 +583,8 @@  extern struct in6_addr *fl6_update_dst(struct flowi6 *fl6,
  *	socket options (ipv6_sockglue.c)
  */
 
+extern int sysctl_ip6_nonlocal_bind;
+
 extern int			ipv6_setsockopt(struct sock *sk, int level, 
 						int optname,
 						char __user *optval, 
diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h
index b42be53..cfc9486 100644
--- a/include/net/netns/ipv6.h
+++ b/include/net/netns/ipv6.h
@@ -18,6 +18,7 @@  struct netns_sysctl_ipv6 {
 	struct ctl_table_header *frags_hdr;
 #endif
 	int bindv6only;
+        int ip6_nonlocal_bind;
 	int flush_delay;
 	int ip6_rt_max_size;
 	int ip6_rt_gc_min_interval;
diff --git a/kernel/sysctl_binary.c b/kernel/sysctl_binary.c
index 9f9aa32..b611831 100644
--- a/kernel/sysctl_binary.c
+++ b/kernel/sysctl_binary.c
@@ -558,6 +558,7 @@  static const struct bin_table bin_net_ipv6_table[] = {
 	{ CTL_DIR,	NET_IPV6_ROUTE,		"route",	bin_net_ipv6_route_table },
 	{ CTL_DIR,	NET_IPV6_ICMP,		"icmp",		bin_net_ipv6_icmp_table },
 	{ CTL_INT,	NET_IPV6_BINDV6ONLY,		"bindv6only" },
+        { CTL_INT,      NET_IPV6_NONLOCAL_BIND,         "ip6_nonlocal_bind" },
 	{ CTL_INT,	NET_IPV6_IP6FRAG_HIGH_THRESH,	"ip6frag_high_thresh" },
 	{ CTL_INT,	NET_IPV6_IP6FRAG_LOW_THRESH,	"ip6frag_low_thresh" },
 	{ CTL_INT,	NET_IPV6_IP6FRAG_TIME,		"ip6frag_time" },
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index e22e6d8..55dd5be 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -255,6 +255,9 @@  out_rcu_unlock:
 
 
 /* bind for INET6 API */
+int sysctl_ip6_nonlocal_bind __read_mostly;
+EXPORT_SYMBOL(sysctl_ip6_nonlocal_bind);
+
 int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 {
 	struct sockaddr_in6 *addr = (struct sockaddr_in6 *)uaddr;
@@ -308,7 +311,7 @@  int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 		/* Reproduce AF_INET checks to make the bindings consistent */
 		v4addr = addr->sin6_addr.s6_addr32[3];
 		chk_addr_ret = inet_addr_type(net, v4addr);
-		if (!sysctl_ip_nonlocal_bind &&
+		if (!sysctl_ip6_nonlocal_bind &&
 		    !(inet->freebind || inet->transparent) &&
 		    v4addr != htonl(INADDR_ANY) &&
 		    chk_addr_ret != RTN_LOCAL &&
@@ -348,7 +351,8 @@  int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 			 */
 			v4addr = LOOPBACK4_IPV6;
 			if (!(addr_type & IPV6_ADDR_MULTICAST))	{
-				if (!(inet->freebind || inet->transparent) &&
+                            if (!sysctl_ip6_nonlocal_bind &&
+                                !(inet->freebind || inet->transparent) &&
 				    !ipv6_chk_addr(net, &addr->sin6_addr,
 						   dev, 0)) {
 					err = -EADDRNOTAVAIL;
@@ -1008,6 +1012,7 @@  static int __net_init inet6_net_init(struct net *net)
 	int err = 0;
 
 	net->ipv6.sysctl.bindv6only = 0;
+        net->ipv6.sysctl.ip6_nonlocal_bind = 0;
 	net->ipv6.sysctl.icmpv6_time = 1*HZ;
 
 	err = ipv6_init_mibs(net);
diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c
index e85c48b..87699ca 100644
--- a/net/ipv6/sysctl_net_ipv6.c
+++ b/net/ipv6/sysctl_net_ipv6.c
@@ -24,6 +24,13 @@  static ctl_table ipv6_table_template[] = {
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec
 	},
+        {
+                .procname       = "ip6_nonlocal_bind",
+                .data           = &sysctl_ip6_nonlocal_bind,
+                .maxlen         = sizeof(int),
+                .mode           = 0644,
+                .proc_handler   = proc_dointvec
+        },
 	{ }
 };
 
-- 
1.7.9.5