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: Vince Busam <vbusam@google.com>
Signed-off-by: Simon Horman <horms@verge.net.au>
This commit is contained in:
Vince Busam 2008-09-02 15:55:52 +02:00 committed by Simon Horman
parent a0eb662f9e
commit 09571c7ae3

View file

@ -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)
} }
#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,10 +775,18 @@ __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 */
if (inet_addr_type(&init_net, udest->addr.ip) == RTN_LOCAL) { #ifdef CONFIG_IP_VS_IPV6
conn_flags = (conn_flags & ~IP_VS_CONN_F_FWD_MASK) if (svc->af == AF_INET6) {
| IP_VS_CONN_F_LOCALNODE; 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) {
conn_flags = (conn_flags & ~IP_VS_CONN_F_FWD_MASK)
| IP_VS_CONN_F_LOCALNODE;
}
/* set the IP_VS_CONN_F_NOOUTPUT flag if not masquerading/NAT */ /* set the IP_VS_CONN_F_NOOUTPUT flag if not masquerading/NAT */
if ((conn_flags & IP_VS_CONN_F_FWD_MASK) != 0) { if ((conn_flags & IP_VS_CONN_F_FWD_MASK) != 0) {
@ -803,9 +835,19 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest,
EnterFunction(2); EnterFunction(2);
atype = inet_addr_type(&init_net, udest->addr.ip); #ifdef CONFIG_IP_VS_IPV6
if (atype != RTN_LOCAL && atype != RTN_UNICAST) if (svc->af == AF_INET6) {
return -EINVAL; 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);
if (atype != RTN_LOCAL && atype != RTN_UNICAST)
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) {