Signed-off-by: Hannes Frederic Sowa <han...@stressinduktion.org> --- include/net/inet_common.h | 1 + net/ipv4/af_inet.c | 51 ++++++++++++++++++++++++++++++----------------- 2 files changed, 34 insertions(+), 18 deletions(-)
diff --git a/include/net/inet_common.h b/include/net/inet_common.h index b7952d55b9c000..4ac8229dca6af4 100644 --- a/include/net/inet_common.h +++ b/include/net/inet_common.h @@ -30,6 +30,7 @@ int inet_shutdown(struct socket *sock, int how); int inet_listen(struct socket *sock, int backlog); void inet_sock_destruct(struct sock *sk); int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len); +int inet_allow_bind(struct sock *sk, __be32 addr); int inet_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer); int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 602d40f43687c9..aee599e23137e7 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -428,6 +428,35 @@ int inet_release(struct socket *sock) } EXPORT_SYMBOL(inet_release); +int inet_allow_bind(struct sock *sk, __be32 addr) +{ + struct inet_sock *inet = inet_sk(sk); + struct net *net = sock_net(sk); + u32 tb_id = RT_TABLE_LOCAL; + int chk_addr_ret; + + tb_id = l3mdev_fib_table_by_index(net, sk->sk_bound_dev_if) ? : tb_id; + chk_addr_ret = inet_addr_type_table(net, addr, tb_id); + + /* Not specified by any standard per-se, however it breaks too + * many applications when removed. It is unfortunate since + * allowing applications to make a non-local bind solves + * several problems with systems using dynamic addressing. + * (ie. your servers still start up even if your ISDN link + * is temporarily down) + */ + if (!net->ipv4.sysctl_ip_nonlocal_bind && + !(inet->freebind || inet->transparent) && + addr != htonl(INADDR_ANY) && + chk_addr_ret != RTN_LOCAL && + chk_addr_ret != RTN_MULTICAST && + chk_addr_ret != RTN_BROADCAST) + return -EADDRNOTAVAIL; + + return chk_addr_ret; +} +EXPORT_SYMBOL(inet_allow_bind); + int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sockaddr_in *addr = (struct sockaddr_in *)uaddr; @@ -436,7 +465,6 @@ int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) struct net *net = sock_net(sk); unsigned short snum; int chk_addr_ret; - u32 tb_id = RT_TABLE_LOCAL; int err; /* If the socket has its own bind function then use it. (RAW) */ @@ -458,24 +486,11 @@ int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) goto out; } - tb_id = l3mdev_fib_table_by_index(net, sk->sk_bound_dev_if) ? : tb_id; - chk_addr_ret = inet_addr_type_table(net, addr->sin_addr.s_addr, tb_id); - - /* Not specified by any standard per-se, however it breaks too - * many applications when removed. It is unfortunate since - * allowing applications to make a non-local bind solves - * several problems with systems using dynamic addressing. - * (ie. your servers still start up even if your ISDN link - * is temporarily down) - */ - err = -EADDRNOTAVAIL; - if (!net->ipv4.sysctl_ip_nonlocal_bind && - !(inet->freebind || inet->transparent) && - addr->sin_addr.s_addr != htonl(INADDR_ANY) && - chk_addr_ret != RTN_LOCAL && - chk_addr_ret != RTN_MULTICAST && - chk_addr_ret != RTN_BROADCAST) + chk_addr_ret = inet_allow_bind(sk, addr->sin_addr.s_addr); + if (chk_addr_ret < 0) { + err = chk_addr_ret; goto out; + } snum = ntohs(addr->sin_port); err = -EACCES; -- 2.9.3