Commit 09571c7a authored by Vince Busam's avatar Vince Busam Committed by Simon Horman

IPVS: Add function to determine if IPv6 address is local

Add __ip_vs_addr_is_local_v6() to find out if an IPv6 address belongs to a
local interface. Use this function to decide whether to set the
IP_VS_CONN_F_LOCALNODE flag for IPv6 destinations.
Signed-off-by: default avatarVince Busam <vbusam@google.com>
Signed-off-by: default avatarSimon Horman <horms@verge.net.au>
parent a0eb662f
...@@ -35,6 +35,10 @@ ...@@ -35,6 +35,10 @@
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/ip.h> #include <net/ip.h>
#ifdef CONFIG_IP_VS_IPV6
#include <net/ipv6.h>
#include <net/ip6_route.h>
#endif
#include <net/route.h> #include <net/route.h>
#include <net/sock.h> #include <net/sock.h>
#include <net/genetlink.h> #include <net/genetlink.h>
...@@ -91,6 +95,26 @@ int ip_vs_get_debug_level(void) ...@@ -91,6 +95,26 @@ int ip_vs_get_debug_level(void)
} }
#endif #endif
#ifdef CONFIG_IP_VS_IPV6
/* Taken from rt6_fill_node() in net/ipv6/route.c, is there a better way? */
static int __ip_vs_addr_is_local_v6(const struct in6_addr *addr)
{
struct rt6_info *rt;
struct flowi fl = {
.oif = 0,
.nl_u = {
.ip6_u = {
.daddr = *addr,
.saddr = { .s6_addr32 = {0, 0, 0, 0} }, } },
};
rt = (struct rt6_info *)ip6_route_output(&init_net, NULL, &fl);
if (rt && rt->rt6i_dev && (rt->rt6i_dev->flags & IFF_LOOPBACK))
return 1;
return 0;
}
#endif
/* /*
* update_defense_level is called from keventd and from sysctl, * update_defense_level is called from keventd and from sysctl,
* so it needs to protect itself from softirqs * so it needs to protect itself from softirqs
...@@ -751,6 +775,14 @@ __ip_vs_update_dest(struct ip_vs_service *svc, ...@@ -751,6 +775,14 @@ __ip_vs_update_dest(struct ip_vs_service *svc,
conn_flags = udest->conn_flags | IP_VS_CONN_F_INACTIVE; conn_flags = udest->conn_flags | IP_VS_CONN_F_INACTIVE;
/* check if local node and update the flags */ /* check if local node and update the flags */
#ifdef CONFIG_IP_VS_IPV6
if (svc->af == AF_INET6) {
if (__ip_vs_addr_is_local_v6(&udest->addr.in6)) {
conn_flags = (conn_flags & ~IP_VS_CONN_F_FWD_MASK)
| IP_VS_CONN_F_LOCALNODE;
}
} else
#endif
if (inet_addr_type(&init_net, udest->addr.ip) == RTN_LOCAL) { if (inet_addr_type(&init_net, udest->addr.ip) == RTN_LOCAL) {
conn_flags = (conn_flags & ~IP_VS_CONN_F_FWD_MASK) conn_flags = (conn_flags & ~IP_VS_CONN_F_FWD_MASK)
| IP_VS_CONN_F_LOCALNODE; | IP_VS_CONN_F_LOCALNODE;
...@@ -803,9 +835,19 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest, ...@@ -803,9 +835,19 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest,
EnterFunction(2); EnterFunction(2);
#ifdef CONFIG_IP_VS_IPV6
if (svc->af == AF_INET6) {
atype = ipv6_addr_type(&udest->addr.in6);
if (!(atype & IPV6_ADDR_UNICAST) &&
!__ip_vs_addr_is_local_v6(&udest->addr.in6))
return -EINVAL;
} else
#endif
{
atype = inet_addr_type(&init_net, udest->addr.ip); atype = inet_addr_type(&init_net, udest->addr.ip);
if (atype != RTN_LOCAL && atype != RTN_UNICAST) if (atype != RTN_LOCAL && atype != RTN_UNICAST)
return -EINVAL; return -EINVAL;
}
dest = kzalloc(sizeof(struct ip_vs_dest), GFP_ATOMIC); dest = kzalloc(sizeof(struct ip_vs_dest), GFP_ATOMIC);
if (dest == NULL) { if (dest == NULL) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment