Commit 453c5e30 authored by David Stevens's avatar David Stevens Committed by David S. Miller

[IPV6]: Add anycast support.

parent 6d6ce30e
...@@ -65,6 +65,8 @@ struct ipv6_mreq { ...@@ -65,6 +65,8 @@ struct ipv6_mreq {
int ipv6mr_ifindex; int ipv6mr_ifindex;
}; };
#define ipv6mr_acaddr ipv6mr_multiaddr
struct in6_flowlabel_req struct in6_flowlabel_req
{ {
struct in6_addr flr_dst; struct in6_addr flr_dst;
...@@ -166,6 +168,8 @@ struct in6_flowlabel_req ...@@ -166,6 +168,8 @@ struct in6_flowlabel_req
#define IPV6_MTU 24 #define IPV6_MTU 24
#define IPV6_RECVERR 25 #define IPV6_RECVERR 25
#define IPV6_V6ONLY 26 #define IPV6_V6ONLY 26
#define IPV6_JOIN_ANYCAST 27
#define IPV6_LEAVE_ANYCAST 28
/* IPV6_MTU_DISCOVER values */ /* IPV6_MTU_DISCOVER values */
#define IPV6_PMTUDISC_DONT 0 #define IPV6_PMTUDISC_DONT 0
......
...@@ -172,6 +172,7 @@ struct ipv6_pinfo { ...@@ -172,6 +172,7 @@ struct ipv6_pinfo {
ipv6only:1; ipv6only:1;
struct ipv6_mc_socklist *ipv6_mc_list; struct ipv6_mc_socklist *ipv6_mc_list;
struct ipv6_ac_socklist *ipv6_ac_list;
struct ipv6_fl_socklist *ipv6_fl_list; struct ipv6_fl_socklist *ipv6_fl_list;
__u32 dst_cookie; __u32 dst_cookie;
......
...@@ -469,6 +469,10 @@ extern struct net_device *dev_getbyhwaddr(unsigned short type, char *hwaddr); ...@@ -469,6 +469,10 @@ extern struct net_device *dev_getbyhwaddr(unsigned short type, char *hwaddr);
extern void dev_add_pack(struct packet_type *pt); extern void dev_add_pack(struct packet_type *pt);
extern void dev_remove_pack(struct packet_type *pt); extern void dev_remove_pack(struct packet_type *pt);
extern int dev_get(const char *name); extern int dev_get(const char *name);
extern struct net_device *dev_get_by_flags(unsigned short flags,
unsigned short mask);
extern struct net_device *__dev_get_by_flags(unsigned short flags,
unsigned short mask);
extern struct net_device *dev_get_by_name(const char *name); extern struct net_device *dev_get_by_name(const char *name);
extern struct net_device *__dev_get_by_name(const char *name); extern struct net_device *__dev_get_by_name(const char *name);
extern struct net_device *dev_alloc(const char *name, int *err); extern struct net_device *dev_alloc(const char *name, int *err);
......
...@@ -63,7 +63,15 @@ extern struct inet6_ifaddr * ipv6_get_ifaddr(struct in6_addr *addr, ...@@ -63,7 +63,15 @@ extern struct inet6_ifaddr * ipv6_get_ifaddr(struct in6_addr *addr,
extern int ipv6_get_saddr(struct dst_entry *dst, extern int ipv6_get_saddr(struct dst_entry *dst,
struct in6_addr *daddr, struct in6_addr *daddr,
struct in6_addr *saddr); struct in6_addr *saddr);
extern int ipv6_dev_get_saddr(struct net_device *dev,
struct in6_addr *daddr,
struct in6_addr *saddr,
int onlink);
extern int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *); extern int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *);
extern void addrconf_join_solict(struct net_device *dev,
struct in6_addr *addr);
extern void addrconf_leave_solict(struct net_device *dev,
struct in6_addr *addr);
/* /*
* multicast prototypes (mcast.c) * multicast prototypes (mcast.c)
...@@ -93,6 +101,26 @@ extern int ipv6_chk_mcast_addr(struct net_device *dev, ...@@ -93,6 +101,26 @@ extern int ipv6_chk_mcast_addr(struct net_device *dev,
extern void addrconf_prefix_rcv(struct net_device *dev, extern void addrconf_prefix_rcv(struct net_device *dev,
u8 *opt, int len); u8 *opt, int len);
/*
* anycast prototypes (anycast.c)
*/
extern int ipv6_sock_ac_join(struct sock *sk,
int ifindex,
struct in6_addr *addr);
extern int ipv6_sock_ac_drop(struct sock *sk,
int ifindex,
struct in6_addr *addr);
extern void ipv6_sock_ac_close(struct sock *sk);
extern int inet6_ac_check(struct sock *sk, struct in6_addr *addr, int ifindex);
extern int ipv6_dev_ac_inc(struct net_device *dev,
struct in6_addr *addr);
extern int ipv6_dev_ac_dec(struct net_device *dev,
struct in6_addr *addr);
extern int ipv6_chk_acast_addr(struct net_device *dev,
struct in6_addr *addr);
/* Device notifier */ /* Device notifier */
extern int register_inet6addr_notifier(struct notifier_block *nb); extern int register_inet6addr_notifier(struct notifier_block *nb);
extern int unregister_inet6addr_notifier(struct notifier_block *nb); extern int unregister_inet6addr_notifier(struct notifier_block *nb);
......
...@@ -75,6 +75,25 @@ struct ifmcaddr6 ...@@ -75,6 +75,25 @@ struct ifmcaddr6
spinlock_t mca_lock; spinlock_t mca_lock;
}; };
/* Anycast stuff */
struct ipv6_ac_socklist
{
struct in6_addr acl_addr;
int acl_ifindex;
struct ipv6_ac_socklist *acl_next;
};
struct ifacaddr6
{
struct in6_addr aca_addr;
struct inet6_dev *aca_idev;
struct ifacaddr6 *aca_next;
int aca_users;
atomic_t aca_refcnt;
spinlock_t aca_lock;
};
#define IFA_HOST IPV6_ADDR_LOOPBACK #define IFA_HOST IPV6_ADDR_LOOPBACK
#define IFA_LINK IPV6_ADDR_LINKLOCAL #define IFA_LINK IPV6_ADDR_LINKLOCAL
#define IFA_SITE IPV6_ADDR_SITELOCAL #define IFA_SITE IPV6_ADDR_SITELOCAL
...@@ -108,6 +127,7 @@ struct inet6_dev ...@@ -108,6 +127,7 @@ struct inet6_dev
struct inet6_ifaddr *addr_list; struct inet6_ifaddr *addr_list;
struct ifmcaddr6 *mc_list; struct ifmcaddr6 *mc_list;
struct ifacaddr6 *ac_list;
rwlock_t lock; rwlock_t lock;
atomic_t refcnt; atomic_t refcnt;
__u32 if_flags; __u32 if_flags;
......
...@@ -546,6 +546,50 @@ struct net_device *dev_getbyhwaddr(unsigned short type, char *ha) ...@@ -546,6 +546,50 @@ struct net_device *dev_getbyhwaddr(unsigned short type, char *ha)
return dev; return dev;
} }
/**
* dev_get_by_flags - find any device with given flags
* @if_flags: IFF_* values
* @mask: bitmask of bits in if_flags to check
*
* Search for any interface with the given flags. Returns NULL if a device
* is not found or a pointer to the device. The device returned has
* had a reference added and the pointer is safe until the user calls
* dev_put to indicate they have finished with it.
*/
struct net_device * dev_get_by_flags(unsigned short if_flags, unsigned short mask)
{
struct net_device *dev;
read_lock(&dev_base_lock);
dev = __dev_get_by_flags(if_flags, mask);
if (dev)
dev_hold(dev);
read_unlock(&dev_base_lock);
return dev;
}
/**
* __dev_get_by_flags - find any device with given flags
* @if_flags: IFF_* values
* @mask: bitmask of bits in if_flags to check
*
* Search for any interface with the given flags. Returns NULL if a device
* is not found or a pointer to the device. The caller must hold either
* the RTNL semaphore or @dev_base_lock.
*/
struct net_device *__dev_get_by_flags(unsigned short if_flags, unsigned short mask)
{
struct net_device *dev;
for (dev = dev_base; dev != NULL; dev = dev->next) {
if (((dev->flags ^ if_flags) & mask) == 0)
return dev;
}
return NULL;
}
/** /**
* dev_alloc_name - allocate a name for a device * dev_alloc_name - allocate a name for a device
* @dev: device * @dev: device
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
obj-$(CONFIG_IPV6) += ipv6.o obj-$(CONFIG_IPV6) += ipv6.o
ipv6-objs := af_inet6.o ip6_output.o ip6_input.o addrconf.o sit.o \ ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o sit.o \
route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o raw.o \ route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o raw.o \
protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \ protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \ exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \
......
...@@ -174,21 +174,15 @@ const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; ...@@ -174,21 +174,15 @@ const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
int ipv6_addr_type(struct in6_addr *addr) int ipv6_addr_type(struct in6_addr *addr)
{ {
int type;
u32 st; u32 st;
st = addr->s6_addr32[0]; st = addr->s6_addr32[0];
/* Consider all addresses with the first three bits different of if ((st & __constant_htonl(0xFF000000)) == __constant_htonl(0xFF000000)) {
000 and 111 as unicasts. type = IPV6_ADDR_MULTICAST;
*/
if ((st & htonl(0xE0000000)) != htonl(0x00000000) &&
(st & htonl(0xE0000000)) != htonl(0xE0000000))
return IPV6_ADDR_UNICAST;
if ((st & htonl(0xFF000000)) == htonl(0xFF000000)) {
int type = IPV6_ADDR_MULTICAST;
switch((st & htonl(0x00FF0000))) { switch((st & __constant_htonl(0x00FF0000))) {
case __constant_htonl(0x00010000): case __constant_htonl(0x00010000):
type |= IPV6_ADDR_LOOPBACK; type |= IPV6_ADDR_LOOPBACK;
break; break;
...@@ -203,29 +197,53 @@ int ipv6_addr_type(struct in6_addr *addr) ...@@ -203,29 +197,53 @@ int ipv6_addr_type(struct in6_addr *addr)
}; };
return type; return type;
} }
/* check for reserved anycast addresses */
if ((st & __constant_htonl(0xE0000000)) &&
((addr->s6_addr32[2] == __constant_htonl(0xFDFFFFFF) &&
(addr->s6_addr32[3] | __constant_htonl(0x7F)) == (u32)~0) ||
(addr->s6_addr32[2] == 0 && addr->s6_addr32[3] == 0)))
type = IPV6_ADDR_ANYCAST;
else
type = IPV6_ADDR_UNICAST;
/* Consider all addresses with the first three bits different of
000 and 111 as finished.
*/
if ((st & __constant_htonl(0xE0000000)) != __constant_htonl(0x00000000) &&
(st & __constant_htonl(0xE0000000)) != __constant_htonl(0xE0000000))
return type;
if ((st & htonl(0xFFC00000)) == htonl(0xFE800000)) if ((st & __constant_htonl(0xFFC00000)) == __constant_htonl(0xFE800000))
return (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST); return (IPV6_ADDR_LINKLOCAL | type);
if ((st & htonl(0xFFC00000)) == htonl(0xFEC00000)) if ((st & __constant_htonl(0xFFC00000)) == __constant_htonl(0xFEC00000))
return (IPV6_ADDR_SITELOCAL | IPV6_ADDR_UNICAST); return (IPV6_ADDR_SITELOCAL | type);
if ((addr->s6_addr32[0] | addr->s6_addr32[1]) == 0) { if ((addr->s6_addr32[0] | addr->s6_addr32[1]) == 0) {
if (addr->s6_addr32[2] == 0) { if (addr->s6_addr32[2] == 0) {
if (addr->s6_addr32[3] == 0) if (addr->in6_u.u6_addr32[3] == 0)
return IPV6_ADDR_ANY; return IPV6_ADDR_ANY;
if (addr->s6_addr32[3] == htonl(0x00000001)) if (addr->s6_addr32[3] == __constant_htonl(0x00000001))
return (IPV6_ADDR_LOOPBACK | IPV6_ADDR_UNICAST); return (IPV6_ADDR_LOOPBACK | type);
return (IPV6_ADDR_COMPATv4 | IPV6_ADDR_UNICAST); return (IPV6_ADDR_COMPATv4 | type);
} }
if (addr->s6_addr32[2] == htonl(0x0000ffff)) if (addr->s6_addr32[2] == __constant_htonl(0x0000ffff))
return IPV6_ADDR_MAPPED; return IPV6_ADDR_MAPPED;
} }
return IPV6_ADDR_RESERVED; st &= __constant_htonl(0xFF000000);
if (st == 0)
return IPV6_ADDR_RESERVED;
st &= __constant_htonl(0xFE000000);
if (st == __constant_htonl(0x02000000))
return IPV6_ADDR_RESERVED; /* for NSAP */
if (st == __constant_htonl(0x04000000))
return IPV6_ADDR_RESERVED; /* for IPX */
return type;
} }
static void addrconf_del_timer(struct inet6_ifaddr *ifp) static void addrconf_del_timer(struct inet6_ifaddr *ifp)
...@@ -261,7 +279,6 @@ static void addrconf_mod_timer(struct inet6_ifaddr *ifp, ...@@ -261,7 +279,6 @@ static void addrconf_mod_timer(struct inet6_ifaddr *ifp,
add_timer(&ifp->timer); add_timer(&ifp->timer);
} }
/* Nobody refers to this device, we may destroy it. */ /* Nobody refers to this device, we may destroy it. */
void in6_dev_finish_destroy(struct inet6_dev *idev) void in6_dev_finish_destroy(struct inet6_dev *idev)
...@@ -358,24 +375,91 @@ static struct inet6_dev * ipv6_find_idev(struct net_device *dev) ...@@ -358,24 +375,91 @@ static struct inet6_dev * ipv6_find_idev(struct net_device *dev)
return idev; return idev;
} }
void ipv6_addr_prefix(struct in6_addr *prefix,
struct in6_addr *addr, int prefix_len)
{
unsigned long mask;
int ncopy, nbits;
memset(prefix, 0, sizeof(*prefix));
if (prefix_len <= 0)
return;
if (prefix_len > 128)
prefix_len = 128;
ncopy = prefix_len / 32;
switch (ncopy) {
case 4: prefix->s6_addr32[3] = addr->s6_addr32[3];
case 3: prefix->s6_addr32[2] = addr->s6_addr32[2];
case 2: prefix->s6_addr32[1] = addr->s6_addr32[1];
case 1: prefix->s6_addr32[0] = addr->s6_addr32[0];
case 0: break;
}
nbits = prefix_len % 32;
if (nbits == 0)
return;
mask = ~((1 << (32 - nbits)) - 1);
mask = htonl(mask);
prefix->s6_addr32[ncopy] = addr->s6_addr32[ncopy] & mask;
}
static void dev_forward_change(struct inet6_dev *idev)
{
struct net_device *dev;
struct inet6_ifaddr *ifa;
struct in6_addr addr;
if (!idev)
return;
dev = idev->dev;
if (dev && (dev->flags & IFF_MULTICAST)) {
ipv6_addr_all_routers(&addr);
if (idev->cnf.forwarding)
ipv6_dev_mc_inc(dev, &addr);
else
ipv6_dev_mc_dec(dev, &addr);
}
for (ifa=idev->addr_list; ifa; ifa=ifa->if_next) {
ipv6_addr_prefix(&addr, &ifa->addr, ifa->prefix_len);
if (addr.s6_addr32[0] == 0 && addr.s6_addr32[1] == 0 &&
addr.s6_addr32[2] == 0 && addr.s6_addr32[3] == 0)
continue;
if (idev->cnf.forwarding)
ipv6_dev_ac_inc(idev->dev, &addr);
else
ipv6_dev_ac_dec(idev->dev, &addr);
}
}
static void addrconf_forward_change(struct inet6_dev *idev) static void addrconf_forward_change(struct inet6_dev *idev)
{ {
struct net_device *dev; struct net_device *dev;
if (idev) if (idev) {
dev_forward_change(idev);
return; return;
}
read_lock(&dev_base_lock); read_lock(&dev_base_lock);
for (dev=dev_base; dev; dev=dev->next) { for (dev=dev_base; dev; dev=dev->next) {
read_lock(&addrconf_lock); read_lock(&addrconf_lock);
idev = __in6_dev_get(dev); idev = __in6_dev_get(dev);
if (idev) if (idev) {
idev->cnf.forwarding = ipv6_devconf.forwarding; idev->cnf.forwarding = ipv6_devconf.forwarding;
dev_forward_change(idev);
}
read_unlock(&addrconf_lock); read_unlock(&addrconf_lock);
} }
read_unlock(&dev_base_lock); read_unlock(&dev_base_lock);
} }
/* Nobody refers to this ifaddr, destroy it */ /* Nobody refers to this ifaddr, destroy it */
void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp) void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)
...@@ -658,30 +742,20 @@ static int inline ipv6_saddr_pref(const struct inet6_ifaddr *ifp, u8 invpref) ...@@ -658,30 +742,20 @@ static int inline ipv6_saddr_pref(const struct inet6_ifaddr *ifp, u8 invpref)
#define IPV6_GET_SADDR_MAXSCORE(score) (score) #define IPV6_GET_SADDR_MAXSCORE(score) (score)
#endif #endif
int ipv6_get_saddr(struct dst_entry *dst, int ipv6_dev_get_saddr(struct net_device *dev,
struct in6_addr *daddr, struct in6_addr *saddr) struct in6_addr *daddr, struct in6_addr *saddr, int onlink)
{ {
int scope;
struct inet6_ifaddr *ifp = NULL; struct inet6_ifaddr *ifp = NULL;
struct inet6_ifaddr *match = NULL; struct inet6_ifaddr *match = NULL;
struct net_device *dev = NULL;
struct inet6_dev *idev; struct inet6_dev *idev;
struct rt6_info *rt; int scope;
int err; int err;
int hiscore = -1, score; int hiscore = -1, score;
rt = (struct rt6_info *) dst; if (!onlink)
if (rt) scope = ipv6_addr_scope(daddr);
dev = rt->rt6i_dev; else
scope = ipv6_addr_scope(daddr);
if (rt && (rt->rt6i_flags & RTF_ALLONLINK)) {
/*
* route for the "all destinations on link" rule
* when no routers are present
*/
scope = IFA_LINK; scope = IFA_LINK;
}
/* /*
* known dev * known dev
...@@ -782,6 +856,24 @@ int ipv6_get_saddr(struct dst_entry *dst, ...@@ -782,6 +856,24 @@ int ipv6_get_saddr(struct dst_entry *dst,
return err; return err;
} }
int ipv6_get_saddr(struct dst_entry *dst,
struct in6_addr *daddr, struct in6_addr *saddr)
{
struct rt6_info *rt;
struct net_device *dev = NULL;
int onlink;
rt = (struct rt6_info *) dst;
if (rt)
dev = rt->rt6i_dev;
onlink = (rt && (rt->rt6i_flags & RTF_ALLONLINK));
return ipv6_dev_get_saddr(dev, daddr, saddr, onlink);
}
int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr) int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
{ {
struct inet6_dev *idev; struct inet6_dev *idev;
...@@ -889,7 +981,7 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp) ...@@ -889,7 +981,7 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp)
/* Join to solicited addr multicast group. */ /* Join to solicited addr multicast group. */
static void addrconf_join_solict(struct net_device *dev, struct in6_addr *addr) void addrconf_join_solict(struct net_device *dev, struct in6_addr *addr)
{ {
struct in6_addr maddr; struct in6_addr maddr;
...@@ -900,7 +992,7 @@ static void addrconf_join_solict(struct net_device *dev, struct in6_addr *addr) ...@@ -900,7 +992,7 @@ static void addrconf_join_solict(struct net_device *dev, struct in6_addr *addr)
ipv6_dev_mc_inc(dev, &maddr); ipv6_dev_mc_inc(dev, &maddr);
} }
static void addrconf_leave_solict(struct net_device *dev, struct in6_addr *addr) void addrconf_leave_solict(struct net_device *dev, struct in6_addr *addr)
{ {
struct in6_addr maddr; struct in6_addr maddr;
...@@ -1937,6 +2029,15 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp) ...@@ -1937,6 +2029,15 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
addrconf_mod_timer(ifp, AC_RS, ifp->idev->cnf.rtr_solicit_interval); addrconf_mod_timer(ifp, AC_RS, ifp->idev->cnf.rtr_solicit_interval);
spin_unlock_bh(&ifp->lock); spin_unlock_bh(&ifp->lock);
} }
if (ifp->idev->cnf.forwarding) {
struct in6_addr addr;
ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len);
if (addr.s6_addr32[0] || addr.s6_addr32[1] ||
addr.s6_addr32[2] || addr.s6_addr32[3])
ipv6_dev_ac_inc(ifp->idev->dev, &addr);
}
} }
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
...@@ -2267,6 +2368,14 @@ static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp) ...@@ -2267,6 +2368,14 @@ static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
break; break;
case RTM_DELADDR: case RTM_DELADDR:
addrconf_leave_solict(ifp->idev->dev, &ifp->addr); addrconf_leave_solict(ifp->idev->dev, &ifp->addr);
if (ifp->idev->cnf.forwarding) {
struct in6_addr addr;
ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len);
if (addr.s6_addr32[0] || addr.s6_addr32[1] ||
addr.s6_addr32[2] || addr.s6_addr32[3])
ipv6_dev_ac_dec(ifp->idev->dev, &addr);
}
if (!ipv6_chk_addr(&ifp->addr, NULL)) if (!ipv6_chk_addr(&ifp->addr, NULL))
ip6_rt_addr_del(&ifp->addr, ifp->idev->dev); ip6_rt_addr_del(&ifp->addr, ifp->idev->dev);
break; break;
...@@ -2289,11 +2398,7 @@ int addrconf_sysctl_forward(ctl_table *ctl, int write, struct file * filp, ...@@ -2289,11 +2398,7 @@ int addrconf_sysctl_forward(ctl_table *ctl, int write, struct file * filp,
struct inet6_dev *idev = NULL; struct inet6_dev *idev = NULL;
if (valp != &ipv6_devconf.forwarding) { if (valp != &ipv6_devconf.forwarding) {
struct net_device *dev = dev_get_by_index(ctl->ctl_name); idev = (struct inet6_dev *)ctl->extra1;
if (dev) {
idev = in6_dev_get(dev);
dev_put(dev);
}
if (idev == NULL) if (idev == NULL)
return ret; return ret;
} else } else
...@@ -2303,8 +2408,6 @@ int addrconf_sysctl_forward(ctl_table *ctl, int write, struct file * filp, ...@@ -2303,8 +2408,6 @@ int addrconf_sysctl_forward(ctl_table *ctl, int write, struct file * filp,
if (*valp) if (*valp)
rt6_purge_dflt_routers(0); rt6_purge_dflt_routers(0);
if (idev)
in6_dev_put(idev);
} }
return ret; return ret;
...@@ -2491,6 +2594,7 @@ static void addrconf_sysctl_register(struct inet6_dev *idev, struct ipv6_devconf ...@@ -2491,6 +2594,7 @@ static void addrconf_sysctl_register(struct inet6_dev *idev, struct ipv6_devconf
for (i=0; t->addrconf_vars[i].data; i++) { for (i=0; t->addrconf_vars[i].data; i++) {
t->addrconf_vars[i].data += (char*)p - (char*)&ipv6_devconf; t->addrconf_vars[i].data += (char*)p - (char*)&ipv6_devconf;
t->addrconf_vars[i].de = NULL; t->addrconf_vars[i].de = NULL;
t->addrconf_vars[i].extra1 = idev; /* embedded; no ref */
} }
if (dev) { if (dev) {
t->addrconf_dev[0].procname = dev->name; t->addrconf_dev[0].procname = dev->name;
......
...@@ -74,6 +74,7 @@ MODULE_PARM(unloadable, "i"); ...@@ -74,6 +74,7 @@ MODULE_PARM(unloadable, "i");
/* IPv6 procfs goodies... */ /* IPv6 procfs goodies... */
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
extern int anycast6_get_info(char *, char **, off_t, int);
extern int raw6_get_info(char *, char **, off_t, int); extern int raw6_get_info(char *, char **, off_t, int);
extern int tcp6_get_info(char *, char **, off_t, int); extern int tcp6_get_info(char *, char **, off_t, int);
extern int udp6_get_info(char *, char **, off_t, int); extern int udp6_get_info(char *, char **, off_t, int);
...@@ -381,6 +382,9 @@ int inet6_release(struct socket *sock) ...@@ -381,6 +382,9 @@ int inet6_release(struct socket *sock)
/* Free mc lists */ /* Free mc lists */
ipv6_sock_mc_close(sk); ipv6_sock_mc_close(sk);
/* Free ac lists */
ipv6_sock_ac_close(sk);
return inet_release(sock); return inet_release(sock);
} }
...@@ -785,6 +789,8 @@ static int __init inet6_init(void) ...@@ -785,6 +789,8 @@ static int __init inet6_init(void)
goto proc_sockstat6_fail; goto proc_sockstat6_fail;
if (!proc_net_create("snmp6", 0, afinet6_get_snmp)) if (!proc_net_create("snmp6", 0, afinet6_get_snmp))
goto proc_snmp6_fail; goto proc_snmp6_fail;
if (!proc_net_create("anycast6", 0, anycast6_get_info))
goto proc_anycast6_fail;
#endif #endif
ipv6_netdev_notif_init(); ipv6_netdev_notif_init();
ipv6_packet_init(); ipv6_packet_init();
...@@ -800,6 +806,8 @@ static int __init inet6_init(void) ...@@ -800,6 +806,8 @@ static int __init inet6_init(void)
return 0; return 0;
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
proc_anycast6_fail:
proc_net_remove("anycast6");
proc_snmp6_fail: proc_snmp6_fail:
proc_net_remove("sockstat6"); proc_net_remove("sockstat6");
proc_sockstat6_fail: proc_sockstat6_fail:
...@@ -837,6 +845,7 @@ static void inet6_exit(void) ...@@ -837,6 +845,7 @@ static void inet6_exit(void)
proc_net_remove("udp6"); proc_net_remove("udp6");
proc_net_remove("sockstat6"); proc_net_remove("sockstat6");
proc_net_remove("snmp6"); proc_net_remove("snmp6");
proc_net_remove("anycast6");
#endif #endif
/* Cleanup code parts. */ /* Cleanup code parts. */
sit_cleanup(); sit_cleanup();
......
This diff is collapsed.
...@@ -369,7 +369,8 @@ static void icmpv6_echo_reply(struct sk_buff *skb) ...@@ -369,7 +369,8 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
saddr = &skb->nh.ipv6h->daddr; saddr = &skb->nh.ipv6h->daddr;
if (ipv6_addr_type(saddr) & IPV6_ADDR_MULTICAST) if (ipv6_addr_type(saddr) & IPV6_ADDR_MULTICAST ||
ipv6_chk_acast_addr(0, saddr))
saddr = NULL; saddr = NULL;
msg.icmph.icmp6_type = ICMPV6_ECHO_REPLY; msg.icmph.icmp6_type = ICMPV6_ECHO_REPLY;
......
...@@ -358,6 +358,24 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval, ...@@ -358,6 +358,24 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
retv = ipv6_sock_mc_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr); retv = ipv6_sock_mc_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
break; break;
} }
case IPV6_JOIN_ANYCAST:
case IPV6_LEAVE_ANYCAST:
{
struct ipv6_mreq mreq;
if (optlen != sizeof(struct ipv6_mreq))
goto e_inval;
retv = -EFAULT;
if (copy_from_user(&mreq, optval, sizeof(struct ipv6_mreq)))
break;
if (optname == IPV6_JOIN_ANYCAST)
retv = ipv6_sock_ac_join(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_acaddr);
else
retv = ipv6_sock_ac_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_acaddr);
break;
}
case IPV6_ROUTER_ALERT: case IPV6_ROUTER_ALERT:
retv = ip6_ra_control(sk, val, NULL); retv = ip6_ra_control(sk, val, NULL);
break; break;
......
...@@ -413,10 +413,13 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, ...@@ -413,10 +413,13 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
struct in6_addr *daddr, struct in6_addr *solicited_addr, struct in6_addr *daddr, struct in6_addr *solicited_addr,
int router, int solicited, int override, int inc_opt) int router, int solicited, int override, int inc_opt)
{ {
static struct in6_addr tmpaddr;
struct inet6_ifaddr *ifp;
struct flowi fl; struct flowi fl;
struct rt6_info *rt = NULL; struct rt6_info *rt = NULL;
struct dst_entry* dst; struct dst_entry* dst;
struct sock *sk = ndisc_socket->sk; struct sock *sk = ndisc_socket->sk;
struct in6_addr *src_addr;
struct nd_msg *msg; struct nd_msg *msg;
int len; int len;
struct sk_buff *skb; struct sk_buff *skb;
...@@ -428,7 +431,18 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, ...@@ -428,7 +431,18 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
if (!rt) if (!rt)
return; return;
ndisc_flow_init(&fl, NDISC_NEIGHBOUR_ADVERTISEMENT, solicited_addr, daddr); /* for anycast or proxy, solicited_addr != src_addr */
ifp = ipv6_get_ifaddr(solicited_addr, dev);
if (ifp) {
src_addr = solicited_addr;
in6_ifa_put(ifp);
} else {
if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr, 0))
return;
src_addr = &tmpaddr;
}
ndisc_flow_init(&fl, NDISC_NEIGHBOUR_ADVERTISEMENT, src_addr, daddr);
ndisc_rt_init(rt, dev, neigh); ndisc_rt_init(rt, dev, neigh);
dst = (struct dst_entry*)rt; dst = (struct dst_entry*)rt;
...@@ -456,7 +470,7 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, ...@@ -456,7 +470,7 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
} }
skb_reserve(skb, (dev->hard_header_len + 15) & ~15); skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
ip6_nd_hdr(sk, skb, dev, solicited_addr, daddr, IPPROTO_ICMPV6, len); ip6_nd_hdr(sk, skb, dev, src_addr, daddr, IPPROTO_ICMPV6, len);
skb->h.raw = (unsigned char*) msg = (struct nd_msg *) skb_put(skb, len); skb->h.raw = (unsigned char*) msg = (struct nd_msg *) skb_put(skb, len);
...@@ -470,13 +484,13 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, ...@@ -470,13 +484,13 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
msg->icmph.icmp6_override = !!override; msg->icmph.icmp6_override = !!override;
/* Set the target address. */ /* Set the target address. */
ipv6_addr_copy(&msg->target, solicited_addr); ipv6_addr_copy(&msg->target, src_addr);
if (inc_opt) if (inc_opt)
ndisc_fill_option(msg->opt, ND_OPT_TARGET_LL_ADDR, dev->dev_addr, dev->addr_len); ndisc_fill_option(msg->opt, ND_OPT_TARGET_LL_ADDR, dev->dev_addr, dev->addr_len);
/* checksum */ /* checksum */
msg->icmph.icmp6_cksum = csum_ipv6_magic(solicited_addr, daddr, len, msg->icmph.icmp6_cksum = csum_ipv6_magic(src_addr, daddr, len,
IPPROTO_ICMPV6, IPPROTO_ICMPV6,
csum_partial((__u8 *) msg, csum_partial((__u8 *) msg,
len, 0)); len, 0));
...@@ -793,6 +807,50 @@ void ndisc_recv_ns(struct sk_buff *skb) ...@@ -793,6 +807,50 @@ void ndisc_recv_ns(struct sk_buff *skb)
} }
} }
in6_ifa_put(ifp); in6_ifa_put(ifp);
} else if (ipv6_chk_acast_addr(dev, &msg->target)) {
struct inet6_dev *idev = in6_dev_get(dev);
int addr_type = ipv6_addr_type(saddr);
/* anycast */
if (!idev) {
/* XXX: count this drop? */
return 0;
}
if (addr_type == IPV6_ADDR_ANY) {
struct in6_addr maddr;
ipv6_addr_all_nodes(&maddr);
ndisc_send_na(dev, NULL, &maddr, &msg->target,
idev->cnf.forwarding, 0, 0, 1);
in6_dev_put(idev);
return 0;
}
if (addr_type & IPV6_ADDR_UNICAST) {
int inc = ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST;
if (inc)
nd_tbl.stats.rcv_probes_mcast++;
else
nd_tbl.stats.rcv_probes_ucast++;
/*
* update / create cache entry
* for the source adddress
*/
neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, skb->dev);
if (neigh || !dev->hard_header) {
ndisc_send_na(dev, neigh, saddr,
&msg->target,
idev->cnf.forwarding, 1, 0, inc);
if (neigh)
neigh_release(neigh);
}
}
in6_dev_put(idev);
} else { } else {
struct inet6_dev *in6_dev = in6_dev_get(dev); struct inet6_dev *in6_dev = in6_dev_get(dev);
int addr_type = ipv6_addr_type(saddr); int addr_type = ipv6_addr_type(saddr);
......
...@@ -547,6 +547,8 @@ EXPORT_SYMBOL(register_netdevice); ...@@ -547,6 +547,8 @@ EXPORT_SYMBOL(register_netdevice);
EXPORT_SYMBOL(unregister_netdevice); EXPORT_SYMBOL(unregister_netdevice);
EXPORT_SYMBOL(netdev_state_change); EXPORT_SYMBOL(netdev_state_change);
EXPORT_SYMBOL(dev_new_index); EXPORT_SYMBOL(dev_new_index);
EXPORT_SYMBOL(dev_get_by_flags);
EXPORT_SYMBOL(__dev_get_by_flags);
EXPORT_SYMBOL(dev_get_by_index); EXPORT_SYMBOL(dev_get_by_index);
EXPORT_SYMBOL(__dev_get_by_index); EXPORT_SYMBOL(__dev_get_by_index);
EXPORT_SYMBOL(dev_get_by_name); EXPORT_SYMBOL(dev_get_by_name);
......
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