|
|
|
@ -46,6 +46,7 @@
|
|
|
|
|
#include <linux/socket.h>
|
|
|
|
|
#include <linux/sockios.h>
|
|
|
|
|
#include <linux/net.h>
|
|
|
|
|
#include <linux/inet.h>
|
|
|
|
|
#include <linux/in6.h>
|
|
|
|
|
#include <linux/netdevice.h>
|
|
|
|
|
#include <linux/if_addr.h>
|
|
|
|
@ -102,6 +103,9 @@
|
|
|
|
|
|
|
|
|
|
#define INFINITY_LIFE_TIME 0xFFFFFFFF
|
|
|
|
|
|
|
|
|
|
#define IPV6_MAX_STRLEN \
|
|
|
|
|
sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")
|
|
|
|
|
|
|
|
|
|
static inline u32 cstamp_delta(unsigned long cstamp)
|
|
|
|
|
{
|
|
|
|
|
return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
|
|
|
|
@ -127,6 +131,9 @@ static void ipv6_regen_rndid(unsigned long data);
|
|
|
|
|
|
|
|
|
|
static int ipv6_generate_eui64(u8 *eui, struct net_device *dev);
|
|
|
|
|
static int ipv6_count_addresses(struct inet6_dev *idev);
|
|
|
|
|
static int ipv6_generate_stable_address(struct in6_addr *addr,
|
|
|
|
|
u8 dad_count,
|
|
|
|
|
const struct inet6_dev *idev);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Configured unicast address hash table
|
|
|
|
@ -202,6 +209,9 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
|
|
|
|
|
.accept_dad = 1,
|
|
|
|
|
.suppress_frag_ndisc = 1,
|
|
|
|
|
.accept_ra_mtu = 1,
|
|
|
|
|
.stable_secret = {
|
|
|
|
|
.initialized = false,
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
|
|
|
|
@ -240,6 +250,9 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
|
|
|
|
|
.accept_dad = 1,
|
|
|
|
|
.suppress_frag_ndisc = 1,
|
|
|
|
|
.accept_ra_mtu = 1,
|
|
|
|
|
.stable_secret = {
|
|
|
|
|
.initialized = false,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Check if a valid qdisc is available */
|
|
|
|
@ -860,7 +873,6 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
|
|
|
|
|
ifa->peer_addr = *peer_addr;
|
|
|
|
|
|
|
|
|
|
spin_lock_init(&ifa->lock);
|
|
|
|
|
spin_lock_init(&ifa->state_lock);
|
|
|
|
|
INIT_DELAYED_WORK(&ifa->dad_work, addrconf_dad_work);
|
|
|
|
|
INIT_HLIST_NODE(&ifa->addr_lst);
|
|
|
|
|
ifa->scope = scope;
|
|
|
|
@ -1003,10 +1015,10 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
|
|
|
|
|
|
|
|
|
|
ASSERT_RTNL();
|
|
|
|
|
|
|
|
|
|
spin_lock_bh(&ifp->state_lock);
|
|
|
|
|
spin_lock_bh(&ifp->lock);
|
|
|
|
|
state = ifp->state;
|
|
|
|
|
ifp->state = INET6_IFADDR_STATE_DEAD;
|
|
|
|
|
spin_unlock_bh(&ifp->state_lock);
|
|
|
|
|
spin_unlock_bh(&ifp->lock);
|
|
|
|
|
|
|
|
|
|
if (state == INET6_IFADDR_STATE_DEAD)
|
|
|
|
|
goto out;
|
|
|
|
@ -1686,19 +1698,21 @@ static int addrconf_dad_end(struct inet6_ifaddr *ifp)
|
|
|
|
|
{
|
|
|
|
|
int err = -ENOENT;
|
|
|
|
|
|
|
|
|
|
spin_lock_bh(&ifp->state_lock);
|
|
|
|
|
spin_lock_bh(&ifp->lock);
|
|
|
|
|
if (ifp->state == INET6_IFADDR_STATE_DAD) {
|
|
|
|
|
ifp->state = INET6_IFADDR_STATE_POSTDAD;
|
|
|
|
|
err = 0;
|
|
|
|
|
}
|
|
|
|
|
spin_unlock_bh(&ifp->state_lock);
|
|
|
|
|
spin_unlock_bh(&ifp->lock);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void addrconf_dad_failure(struct inet6_ifaddr *ifp)
|
|
|
|
|
{
|
|
|
|
|
struct in6_addr addr;
|
|
|
|
|
struct inet6_dev *idev = ifp->idev;
|
|
|
|
|
struct net *net = dev_net(ifp->idev->dev);
|
|
|
|
|
|
|
|
|
|
if (addrconf_dad_end(ifp)) {
|
|
|
|
|
in6_ifa_put(ifp);
|
|
|
|
@ -1708,9 +1722,57 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp)
|
|
|
|
|
net_info_ratelimited("%s: IPv6 duplicate address %pI6c detected!\n",
|
|
|
|
|
ifp->idev->dev->name, &ifp->addr);
|
|
|
|
|
|
|
|
|
|
if (idev->cnf.accept_dad > 1 && !idev->cnf.disable_ipv6) {
|
|
|
|
|
struct in6_addr addr;
|
|
|
|
|
spin_lock_bh(&ifp->lock);
|
|
|
|
|
|
|
|
|
|
if (ifp->flags & IFA_F_STABLE_PRIVACY) {
|
|
|
|
|
int scope = ifp->scope;
|
|
|
|
|
u32 flags = ifp->flags;
|
|
|
|
|
struct in6_addr new_addr;
|
|
|
|
|
struct inet6_ifaddr *ifp2;
|
|
|
|
|
u32 valid_lft, preferred_lft;
|
|
|
|
|
int pfxlen = ifp->prefix_len;
|
|
|
|
|
int retries = ifp->stable_privacy_retry + 1;
|
|
|
|
|
|
|
|
|
|
if (retries > net->ipv6.sysctl.idgen_retries) {
|
|
|
|
|
net_info_ratelimited("%s: privacy stable address generation failed because of DAD conflicts!\n",
|
|
|
|
|
ifp->idev->dev->name);
|
|
|
|
|
goto errdad;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
new_addr = ifp->addr;
|
|
|
|
|
if (ipv6_generate_stable_address(&new_addr, retries,
|
|
|
|
|
idev))
|
|
|
|
|
goto errdad;
|
|
|
|
|
|
|
|
|
|
valid_lft = ifp->valid_lft;
|
|
|
|
|
preferred_lft = ifp->prefered_lft;
|
|
|
|
|
|
|
|
|
|
spin_unlock_bh(&ifp->lock);
|
|
|
|
|
|
|
|
|
|
if (idev->cnf.max_addresses &&
|
|
|
|
|
ipv6_count_addresses(idev) >=
|
|
|
|
|
idev->cnf.max_addresses)
|
|
|
|
|
goto lock_errdad;
|
|
|
|
|
|
|
|
|
|
net_info_ratelimited("%s: generating new stable privacy address because of DAD conflict\n",
|
|
|
|
|
ifp->idev->dev->name);
|
|
|
|
|
|
|
|
|
|
ifp2 = ipv6_add_addr(idev, &new_addr, NULL, pfxlen,
|
|
|
|
|
scope, flags, valid_lft,
|
|
|
|
|
preferred_lft);
|
|
|
|
|
if (IS_ERR(ifp2))
|
|
|
|
|
goto lock_errdad;
|
|
|
|
|
|
|
|
|
|
spin_lock_bh(&ifp2->lock);
|
|
|
|
|
ifp2->stable_privacy_retry = retries;
|
|
|
|
|
ifp2->state = INET6_IFADDR_STATE_PREDAD;
|
|
|
|
|
spin_unlock_bh(&ifp2->lock);
|
|
|
|
|
|
|
|
|
|
addrconf_mod_dad_work(ifp2, net->ipv6.sysctl.idgen_delay);
|
|
|
|
|
in6_ifa_put(ifp2);
|
|
|
|
|
lock_errdad:
|
|
|
|
|
spin_lock_bh(&ifp->lock);
|
|
|
|
|
} else if (idev->cnf.accept_dad > 1 && !idev->cnf.disable_ipv6) {
|
|
|
|
|
addr.s6_addr32[0] = htonl(0xfe800000);
|
|
|
|
|
addr.s6_addr32[1] = 0;
|
|
|
|
|
|
|
|
|
@ -1724,10 +1786,10 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
spin_lock_bh(&ifp->state_lock);
|
|
|
|
|
errdad:
|
|
|
|
|
/* transition from _POSTDAD to _ERRDAD */
|
|
|
|
|
ifp->state = INET6_IFADDR_STATE_ERRDAD;
|
|
|
|
|
spin_unlock_bh(&ifp->state_lock);
|
|
|
|
|
spin_unlock_bh(&ifp->lock);
|
|
|
|
|
|
|
|
|
|
addrconf_mod_dad_work(ifp, 0);
|
|
|
|
|
}
|
|
|
|
@ -2186,6 +2248,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
|
|
|
|
|
__u32 valid_lft;
|
|
|
|
|
__u32 prefered_lft;
|
|
|
|
|
int addr_type;
|
|
|
|
|
u32 addr_flags = 0;
|
|
|
|
|
struct inet6_dev *in6_dev;
|
|
|
|
|
struct net *net = dev_net(dev);
|
|
|
|
|
|
|
|
|
@ -2292,6 +2355,12 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
|
|
|
|
|
in6_dev->token.s6_addr + 8, 8);
|
|
|
|
|
read_unlock_bh(&in6_dev->lock);
|
|
|
|
|
tokenized = true;
|
|
|
|
|
} else if (in6_dev->addr_gen_mode ==
|
|
|
|
|
IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
|
|
|
|
|
!ipv6_generate_stable_address(&addr, 0,
|
|
|
|
|
in6_dev)) {
|
|
|
|
|
addr_flags |= IFA_F_STABLE_PRIVACY;
|
|
|
|
|
goto ok;
|
|
|
|
|
} else if (ipv6_generate_eui64(addr.s6_addr + 8, dev) &&
|
|
|
|
|
ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) {
|
|
|
|
|
in6_dev_put(in6_dev);
|
|
|
|
@ -2310,7 +2379,6 @@ ok:
|
|
|
|
|
|
|
|
|
|
if (ifp == NULL && valid_lft) {
|
|
|
|
|
int max_addresses = in6_dev->cnf.max_addresses;
|
|
|
|
|
u32 addr_flags = 0;
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
|
|
|
|
|
if (in6_dev->cnf.optimistic_dad &&
|
|
|
|
@ -2350,7 +2418,7 @@ ok:
|
|
|
|
|
u32 stored_lft;
|
|
|
|
|
|
|
|
|
|
/* update lifetime (RFC2462 5.5.3 e) */
|
|
|
|
|
spin_lock(&ifp->lock);
|
|
|
|
|
spin_lock_bh(&ifp->lock);
|
|
|
|
|
now = jiffies;
|
|
|
|
|
if (ifp->valid_lft > (now - ifp->tstamp) / HZ)
|
|
|
|
|
stored_lft = ifp->valid_lft - (now - ifp->tstamp) / HZ;
|
|
|
|
@ -2380,12 +2448,12 @@ ok:
|
|
|
|
|
ifp->tstamp = now;
|
|
|
|
|
flags = ifp->flags;
|
|
|
|
|
ifp->flags &= ~IFA_F_DEPRECATED;
|
|
|
|
|
spin_unlock(&ifp->lock);
|
|
|
|
|
spin_unlock_bh(&ifp->lock);
|
|
|
|
|
|
|
|
|
|
if (!(flags&IFA_F_TENTATIVE))
|
|
|
|
|
ipv6_ifa_notify(0, ifp);
|
|
|
|
|
} else
|
|
|
|
|
spin_unlock(&ifp->lock);
|
|
|
|
|
spin_unlock_bh(&ifp->lock);
|
|
|
|
|
|
|
|
|
|
manage_tempaddrs(in6_dev, ifp, valid_lft, prefered_lft,
|
|
|
|
|
create, now);
|
|
|
|
@ -2789,10 +2857,11 @@ static void init_loopback(struct net_device *dev)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void addrconf_add_linklocal(struct inet6_dev *idev, const struct in6_addr *addr)
|
|
|
|
|
static void addrconf_add_linklocal(struct inet6_dev *idev,
|
|
|
|
|
const struct in6_addr *addr, u32 flags)
|
|
|
|
|
{
|
|
|
|
|
struct inet6_ifaddr *ifp;
|
|
|
|
|
u32 addr_flags = IFA_F_PERMANENT;
|
|
|
|
|
u32 addr_flags = flags | IFA_F_PERMANENT;
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
|
|
|
|
|
if (idev->cnf.optimistic_dad &&
|
|
|
|
@ -2800,7 +2869,6 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, const struct in6_addr
|
|
|
|
|
addr_flags |= IFA_F_OPTIMISTIC;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ifp = ipv6_add_addr(idev, addr, NULL, 64, IFA_LINK, addr_flags,
|
|
|
|
|
INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
|
|
|
|
|
if (!IS_ERR(ifp)) {
|
|
|
|
@ -2810,18 +2878,103 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, const struct in6_addr
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool ipv6_reserved_interfaceid(struct in6_addr address)
|
|
|
|
|
{
|
|
|
|
|
if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (address.s6_addr32[2] == htonl(0x02005eff) &&
|
|
|
|
|
((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000)))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (address.s6_addr32[2] == htonl(0xfdffffff) &&
|
|
|
|
|
((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80)))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ipv6_generate_stable_address(struct in6_addr *address,
|
|
|
|
|
u8 dad_count,
|
|
|
|
|
const struct inet6_dev *idev)
|
|
|
|
|
{
|
|
|
|
|
static DEFINE_SPINLOCK(lock);
|
|
|
|
|
static __u32 digest[SHA_DIGEST_WORDS];
|
|
|
|
|
static __u32 workspace[SHA_WORKSPACE_WORDS];
|
|
|
|
|
|
|
|
|
|
static union {
|
|
|
|
|
char __data[SHA_MESSAGE_BYTES];
|
|
|
|
|
struct {
|
|
|
|
|
struct in6_addr secret;
|
|
|
|
|
__be64 prefix;
|
|
|
|
|
unsigned char hwaddr[MAX_ADDR_LEN];
|
|
|
|
|
u8 dad_count;
|
|
|
|
|
} __packed;
|
|
|
|
|
} data;
|
|
|
|
|
|
|
|
|
|
struct in6_addr secret;
|
|
|
|
|
struct in6_addr temp;
|
|
|
|
|
struct net *net = dev_net(idev->dev);
|
|
|
|
|
|
|
|
|
|
BUILD_BUG_ON(sizeof(data.__data) != sizeof(data));
|
|
|
|
|
|
|
|
|
|
if (idev->cnf.stable_secret.initialized)
|
|
|
|
|
secret = idev->cnf.stable_secret.secret;
|
|
|
|
|
else if (net->ipv6.devconf_dflt->stable_secret.initialized)
|
|
|
|
|
secret = net->ipv6.devconf_dflt->stable_secret.secret;
|
|
|
|
|
else
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
retry:
|
|
|
|
|
spin_lock_bh(&lock);
|
|
|
|
|
|
|
|
|
|
sha_init(digest);
|
|
|
|
|
memset(&data, 0, sizeof(data));
|
|
|
|
|
memset(workspace, 0, sizeof(workspace));
|
|
|
|
|
memcpy(data.hwaddr, idev->dev->perm_addr, idev->dev->addr_len);
|
|
|
|
|
data.prefix = ((__be64)address->s6_addr32[0] << 32) |
|
|
|
|
|
(__be64)address->s6_addr32[1];
|
|
|
|
|
data.secret = secret;
|
|
|
|
|
data.dad_count = dad_count;
|
|
|
|
|
|
|
|
|
|
sha_transform(digest, data.__data, workspace);
|
|
|
|
|
|
|
|
|
|
temp = *address;
|
|
|
|
|
temp.s6_addr32[2] = digest[0];
|
|
|
|
|
temp.s6_addr32[3] = digest[1];
|
|
|
|
|
|
|
|
|
|
spin_unlock_bh(&lock);
|
|
|
|
|
|
|
|
|
|
if (ipv6_reserved_interfaceid(temp)) {
|
|
|
|
|
dad_count++;
|
|
|
|
|
if (dad_count > dev_net(idev->dev)->ipv6.sysctl.idgen_retries)
|
|
|
|
|
return -1;
|
|
|
|
|
goto retry;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*address = temp;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route)
|
|
|
|
|
{
|
|
|
|
|
if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64) {
|
|
|
|
|
struct in6_addr addr;
|
|
|
|
|
|
|
|
|
|
ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0);
|
|
|
|
|
|
|
|
|
|
if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
|
|
|
|
|
if (!ipv6_generate_stable_address(&addr, 0, idev))
|
|
|
|
|
addrconf_add_linklocal(idev, &addr,
|
|
|
|
|
IFA_F_STABLE_PRIVACY);
|
|
|
|
|
else if (prefix_route)
|
|
|
|
|
addrconf_prefix_route(&addr, 64, idev->dev, 0, 0);
|
|
|
|
|
} else if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64) {
|
|
|
|
|
/* addrconf_add_linklocal also adds a prefix_route and we
|
|
|
|
|
* only need to care about prefix routes if ipv6_generate_eui64
|
|
|
|
|
* couldn't generate one.
|
|
|
|
|
*/
|
|
|
|
|
if (ipv6_generate_eui64(addr.s6_addr + 8, idev->dev) == 0)
|
|
|
|
|
addrconf_add_linklocal(idev, &addr);
|
|
|
|
|
addrconf_add_linklocal(idev, &addr, 0);
|
|
|
|
|
else if (prefix_route)
|
|
|
|
|
addrconf_prefix_route(&addr, 64, idev->dev, 0, 0);
|
|
|
|
|
}
|
|
|
|
@ -3159,10 +3312,10 @@ restart:
|
|
|
|
|
|
|
|
|
|
write_unlock_bh(&idev->lock);
|
|
|
|
|
|
|
|
|
|
spin_lock_bh(&ifa->state_lock);
|
|
|
|
|
spin_lock_bh(&ifa->lock);
|
|
|
|
|
state = ifa->state;
|
|
|
|
|
ifa->state = INET6_IFADDR_STATE_DEAD;
|
|
|
|
|
spin_unlock_bh(&ifa->state_lock);
|
|
|
|
|
spin_unlock_bh(&ifa->lock);
|
|
|
|
|
|
|
|
|
|
if (state != INET6_IFADDR_STATE_DEAD) {
|
|
|
|
|
__ipv6_ifa_notify(RTM_DELADDR, ifa);
|
|
|
|
@ -3320,12 +3473,12 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp)
|
|
|
|
|
{
|
|
|
|
|
bool begin_dad = false;
|
|
|
|
|
|
|
|
|
|
spin_lock_bh(&ifp->state_lock);
|
|
|
|
|
spin_lock_bh(&ifp->lock);
|
|
|
|
|
if (ifp->state != INET6_IFADDR_STATE_DEAD) {
|
|
|
|
|
ifp->state = INET6_IFADDR_STATE_PREDAD;
|
|
|
|
|
begin_dad = true;
|
|
|
|
|
}
|
|
|
|
|
spin_unlock_bh(&ifp->state_lock);
|
|
|
|
|
spin_unlock_bh(&ifp->lock);
|
|
|
|
|
|
|
|
|
|
if (begin_dad)
|
|
|
|
|
addrconf_mod_dad_work(ifp, 0);
|
|
|
|
@ -3347,7 +3500,7 @@ static void addrconf_dad_work(struct work_struct *w)
|
|
|
|
|
|
|
|
|
|
rtnl_lock();
|
|
|
|
|
|
|
|
|
|
spin_lock_bh(&ifp->state_lock);
|
|
|
|
|
spin_lock_bh(&ifp->lock);
|
|
|
|
|
if (ifp->state == INET6_IFADDR_STATE_PREDAD) {
|
|
|
|
|
action = DAD_BEGIN;
|
|
|
|
|
ifp->state = INET6_IFADDR_STATE_DAD;
|
|
|
|
@ -3355,7 +3508,7 @@ static void addrconf_dad_work(struct work_struct *w)
|
|
|
|
|
action = DAD_ABORT;
|
|
|
|
|
ifp->state = INET6_IFADDR_STATE_POSTDAD;
|
|
|
|
|
}
|
|
|
|
|
spin_unlock_bh(&ifp->state_lock);
|
|
|
|
|
spin_unlock_bh(&ifp->lock);
|
|
|
|
|
|
|
|
|
|
if (action == DAD_BEGIN) {
|
|
|
|
|
addrconf_dad_begin(ifp);
|
|
|
|
@ -4430,6 +4583,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
|
|
|
|
|
array[DEVCONF_SUPPRESS_FRAG_NDISC] = cnf->suppress_frag_ndisc;
|
|
|
|
|
array[DEVCONF_ACCEPT_RA_FROM_LOCAL] = cnf->accept_ra_from_local;
|
|
|
|
|
array[DEVCONF_ACCEPT_RA_MTU] = cnf->accept_ra_mtu;
|
|
|
|
|
/* we omit DEVCONF_STABLE_SECRET for now */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline size_t inet6_ifla6_size(void)
|
|
|
|
@ -4664,8 +4818,15 @@ static int inet6_set_link_af(struct net_device *dev, const struct nlattr *nla)
|
|
|
|
|
u8 mode = nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE]);
|
|
|
|
|
|
|
|
|
|
if (mode != IN6_ADDR_GEN_MODE_EUI64 &&
|
|
|
|
|
mode != IN6_ADDR_GEN_MODE_NONE)
|
|
|
|
|
mode != IN6_ADDR_GEN_MODE_NONE &&
|
|
|
|
|
mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
if (mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
|
|
|
|
|
!idev->cnf.stable_secret.initialized &&
|
|
|
|
|
!dev_net(dev)->ipv6.devconf_dflt->stable_secret.initialized)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
idev->addr_gen_mode = mode;
|
|
|
|
|
err = 0;
|
|
|
|
|
}
|
|
|
|
@ -5074,6 +5235,74 @@ int addrconf_sysctl_proxy_ndp(struct ctl_table *ctl, int write,
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int addrconf_sysctl_stable_secret(struct ctl_table *ctl, int write,
|
|
|
|
|
void __user *buffer, size_t *lenp,
|
|
|
|
|
loff_t *ppos)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
struct in6_addr addr;
|
|
|
|
|
char str[IPV6_MAX_STRLEN];
|
|
|
|
|
struct ctl_table lctl = *ctl;
|
|
|
|
|
struct net *net = ctl->extra2;
|
|
|
|
|
struct ipv6_stable_secret *secret = ctl->data;
|
|
|
|
|
|
|
|
|
|
if (&net->ipv6.devconf_all->stable_secret == ctl->data)
|
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
|
|
lctl.maxlen = IPV6_MAX_STRLEN;
|
|
|
|
|
lctl.data = str;
|
|
|
|
|
|
|
|
|
|
if (!rtnl_trylock())
|
|
|
|
|
return restart_syscall();
|
|
|
|
|
|
|
|
|
|
if (!write && !secret->initialized) {
|
|
|
|
|
err = -EIO;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!write) {
|
|
|
|
|
err = snprintf(str, sizeof(str), "%pI6",
|
|
|
|
|
&secret->secret);
|
|
|
|
|
if (err >= sizeof(str)) {
|
|
|
|
|
err = -EIO;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = proc_dostring(&lctl, write, buffer, lenp, ppos);
|
|
|
|
|
if (err || !write)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if (in6_pton(str, -1, addr.in6_u.u6_addr8, -1, NULL) != 1) {
|
|
|
|
|
err = -EIO;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
secret->initialized = true;
|
|
|
|
|
secret->secret = addr;
|
|
|
|
|
|
|
|
|
|
if (&net->ipv6.devconf_dflt->stable_secret == ctl->data) {
|
|
|
|
|
struct net_device *dev;
|
|
|
|
|
|
|
|
|
|
for_each_netdev(net, dev) {
|
|
|
|
|
struct inet6_dev *idev = __in6_dev_get(dev);
|
|
|
|
|
|
|
|
|
|
if (idev) {
|
|
|
|
|
idev->addr_gen_mode =
|
|
|
|
|
IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
struct inet6_dev *idev = ctl->extra1;
|
|
|
|
|
|
|
|
|
|
idev->addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
rtnl_unlock();
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct addrconf_sysctl_table
|
|
|
|
|
{
|
|
|
|
@ -5346,6 +5575,13 @@ static struct addrconf_sysctl_table
|
|
|
|
|
.mode = 0644,
|
|
|
|
|
.proc_handler = proc_dointvec,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.procname = "stable_secret",
|
|
|
|
|
.data = &ipv6_devconf.stable_secret,
|
|
|
|
|
.maxlen = IPV6_MAX_STRLEN,
|
|
|
|
|
.mode = 0600,
|
|
|
|
|
.proc_handler = addrconf_sysctl_stable_secret,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
/* sentinel */
|
|
|
|
|
}
|
|
|
|
@ -5442,6 +5678,9 @@ static int __net_init addrconf_init_net(struct net *net)
|
|
|
|
|
dflt->autoconf = ipv6_defaults.autoconf;
|
|
|
|
|
dflt->disable_ipv6 = ipv6_defaults.disable_ipv6;
|
|
|
|
|
|
|
|
|
|
dflt->stable_secret.initialized = false;
|
|
|
|
|
all->stable_secret.initialized = false;
|
|
|
|
|
|
|
|
|
|
net->ipv6.devconf_all = all;
|
|
|
|
|
net->ipv6.devconf_dflt = dflt;
|
|
|
|
|
|
|
|
|
|