Merge branch 'ipv6_stable_privacy_address'
Hannes Frederic Sowa says: ==================== ipv6: RFC7217 stable privacy addresses implementation this is an implementation of basic support for RFC7217 stable privacy addresses. Please review and consider for net-next. v2: * Correct references to RFC 7212 -> RFC 7217 in documentation patch (thanks, Eric!) ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
ce046c568c
|
@ -1220,6 +1220,17 @@ anycast_src_echo_reply - BOOLEAN
|
||||||
FALSE: disabled
|
FALSE: disabled
|
||||||
Default: FALSE
|
Default: FALSE
|
||||||
|
|
||||||
|
idgen_delay - INTEGER
|
||||||
|
Controls the delay in seconds after which time to retry
|
||||||
|
privacy stable address generation if a DAD conflict is
|
||||||
|
detected.
|
||||||
|
Default: 1 (as specified in RFC7217)
|
||||||
|
|
||||||
|
idgen_retries - INTEGER
|
||||||
|
Controls the number of retries to generate a stable privacy
|
||||||
|
address if a DAD conflict is detected.
|
||||||
|
Default: 3 (as specified in RFC7217)
|
||||||
|
|
||||||
mld_qrv - INTEGER
|
mld_qrv - INTEGER
|
||||||
Controls the MLD query robustness variable (see RFC3810 9.1).
|
Controls the MLD query robustness variable (see RFC3810 9.1).
|
||||||
Default: 2 (as specified by RFC3810 9.1)
|
Default: 2 (as specified by RFC3810 9.1)
|
||||||
|
@ -1540,6 +1551,20 @@ use_optimistic - BOOLEAN
|
||||||
0: disabled (default)
|
0: disabled (default)
|
||||||
1: enabled
|
1: enabled
|
||||||
|
|
||||||
|
stable_secret - IPv6 address
|
||||||
|
This IPv6 address will be used as a secret to generate IPv6
|
||||||
|
addresses for link-local addresses and autoconfigured
|
||||||
|
ones. All addresses generated after setting this secret will
|
||||||
|
be stable privacy ones by default. This can be changed via the
|
||||||
|
addrgenmode ip-link. conf/default/stable_secret is used as the
|
||||||
|
secret for the namespace, the interface specific ones can
|
||||||
|
overwrite that. Writes to conf/all/stable_secret are refused.
|
||||||
|
|
||||||
|
It is recommended to generate this secret during installation
|
||||||
|
of a system and keep it stable after that.
|
||||||
|
|
||||||
|
By default the stable secret is unset.
|
||||||
|
|
||||||
icmp/*:
|
icmp/*:
|
||||||
ratelimit - INTEGER
|
ratelimit - INTEGER
|
||||||
Limit the maximal rates for sending ICMPv6 packets.
|
Limit the maximal rates for sending ICMPv6 packets.
|
||||||
|
|
|
@ -53,6 +53,10 @@ struct ipv6_devconf {
|
||||||
__s32 ndisc_notify;
|
__s32 ndisc_notify;
|
||||||
__s32 suppress_frag_ndisc;
|
__s32 suppress_frag_ndisc;
|
||||||
__s32 accept_ra_mtu;
|
__s32 accept_ra_mtu;
|
||||||
|
struct ipv6_stable_secret {
|
||||||
|
bool initialized;
|
||||||
|
struct in6_addr secret;
|
||||||
|
} stable_secret;
|
||||||
void *sysctl;
|
void *sysctl;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -41,18 +41,18 @@ enum {
|
||||||
struct inet6_ifaddr {
|
struct inet6_ifaddr {
|
||||||
struct in6_addr addr;
|
struct in6_addr addr;
|
||||||
__u32 prefix_len;
|
__u32 prefix_len;
|
||||||
|
|
||||||
/* In seconds, relative to tstamp. Expiry is at tstamp + HZ * lft. */
|
/* In seconds, relative to tstamp. Expiry is at tstamp + HZ * lft. */
|
||||||
__u32 valid_lft;
|
__u32 valid_lft;
|
||||||
__u32 prefered_lft;
|
__u32 prefered_lft;
|
||||||
atomic_t refcnt;
|
atomic_t refcnt;
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
spinlock_t state_lock;
|
|
||||||
|
|
||||||
int state;
|
int state;
|
||||||
|
|
||||||
__u32 flags;
|
__u32 flags;
|
||||||
__u8 dad_probes;
|
__u8 dad_probes;
|
||||||
|
__u8 stable_privacy_retry;
|
||||||
|
|
||||||
__u16 scope;
|
__u16 scope;
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,8 @@ struct netns_sysctl_ipv6 {
|
||||||
int icmpv6_time;
|
int icmpv6_time;
|
||||||
int anycast_src_echo_reply;
|
int anycast_src_echo_reply;
|
||||||
int fwmark_reflect;
|
int fwmark_reflect;
|
||||||
|
int idgen_retries;
|
||||||
|
int idgen_delay;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct netns_ipv6 {
|
struct netns_ipv6 {
|
||||||
|
|
|
@ -51,6 +51,7 @@ enum {
|
||||||
#define IFA_F_MANAGETEMPADDR 0x100
|
#define IFA_F_MANAGETEMPADDR 0x100
|
||||||
#define IFA_F_NOPREFIXROUTE 0x200
|
#define IFA_F_NOPREFIXROUTE 0x200
|
||||||
#define IFA_F_MCAUTOJOIN 0x400
|
#define IFA_F_MCAUTOJOIN 0x400
|
||||||
|
#define IFA_F_STABLE_PRIVACY 0x800
|
||||||
|
|
||||||
struct ifa_cacheinfo {
|
struct ifa_cacheinfo {
|
||||||
__u32 ifa_prefered;
|
__u32 ifa_prefered;
|
||||||
|
|
|
@ -216,6 +216,7 @@ enum {
|
||||||
enum in6_addr_gen_mode {
|
enum in6_addr_gen_mode {
|
||||||
IN6_ADDR_GEN_MODE_EUI64,
|
IN6_ADDR_GEN_MODE_EUI64,
|
||||||
IN6_ADDR_GEN_MODE_NONE,
|
IN6_ADDR_GEN_MODE_NONE,
|
||||||
|
IN6_ADDR_GEN_MODE_STABLE_PRIVACY,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Bridge section */
|
/* Bridge section */
|
||||||
|
|
|
@ -170,6 +170,7 @@ enum {
|
||||||
DEVCONF_ACCEPT_RA_FROM_LOCAL,
|
DEVCONF_ACCEPT_RA_FROM_LOCAL,
|
||||||
DEVCONF_USE_OPTIMISTIC,
|
DEVCONF_USE_OPTIMISTIC,
|
||||||
DEVCONF_ACCEPT_RA_MTU,
|
DEVCONF_ACCEPT_RA_MTU,
|
||||||
|
DEVCONF_STABLE_SECRET,
|
||||||
DEVCONF_MAX
|
DEVCONF_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -198,3 +198,4 @@ void sha_init(__u32 *buf)
|
||||||
buf[3] = 0x10325476;
|
buf[3] = 0x10325476;
|
||||||
buf[4] = 0xc3d2e1f0;
|
buf[4] = 0xc3d2e1f0;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(sha_init);
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
#include <linux/socket.h>
|
#include <linux/socket.h>
|
||||||
#include <linux/sockios.h>
|
#include <linux/sockios.h>
|
||||||
#include <linux/net.h>
|
#include <linux/net.h>
|
||||||
|
#include <linux/inet.h>
|
||||||
#include <linux/in6.h>
|
#include <linux/in6.h>
|
||||||
#include <linux/netdevice.h>
|
#include <linux/netdevice.h>
|
||||||
#include <linux/if_addr.h>
|
#include <linux/if_addr.h>
|
||||||
|
@ -102,6 +103,9 @@
|
||||||
|
|
||||||
#define INFINITY_LIFE_TIME 0xFFFFFFFF
|
#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)
|
static inline u32 cstamp_delta(unsigned long cstamp)
|
||||||
{
|
{
|
||||||
return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
|
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_generate_eui64(u8 *eui, struct net_device *dev);
|
||||||
static int ipv6_count_addresses(struct inet6_dev *idev);
|
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
|
* Configured unicast address hash table
|
||||||
|
@ -202,6 +209,9 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
|
||||||
.accept_dad = 1,
|
.accept_dad = 1,
|
||||||
.suppress_frag_ndisc = 1,
|
.suppress_frag_ndisc = 1,
|
||||||
.accept_ra_mtu = 1,
|
.accept_ra_mtu = 1,
|
||||||
|
.stable_secret = {
|
||||||
|
.initialized = false,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
|
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,
|
.accept_dad = 1,
|
||||||
.suppress_frag_ndisc = 1,
|
.suppress_frag_ndisc = 1,
|
||||||
.accept_ra_mtu = 1,
|
.accept_ra_mtu = 1,
|
||||||
|
.stable_secret = {
|
||||||
|
.initialized = false,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Check if a valid qdisc is available */
|
/* 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;
|
ifa->peer_addr = *peer_addr;
|
||||||
|
|
||||||
spin_lock_init(&ifa->lock);
|
spin_lock_init(&ifa->lock);
|
||||||
spin_lock_init(&ifa->state_lock);
|
|
||||||
INIT_DELAYED_WORK(&ifa->dad_work, addrconf_dad_work);
|
INIT_DELAYED_WORK(&ifa->dad_work, addrconf_dad_work);
|
||||||
INIT_HLIST_NODE(&ifa->addr_lst);
|
INIT_HLIST_NODE(&ifa->addr_lst);
|
||||||
ifa->scope = scope;
|
ifa->scope = scope;
|
||||||
|
@ -1003,10 +1015,10 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
|
||||||
|
|
||||||
ASSERT_RTNL();
|
ASSERT_RTNL();
|
||||||
|
|
||||||
spin_lock_bh(&ifp->state_lock);
|
spin_lock_bh(&ifp->lock);
|
||||||
state = ifp->state;
|
state = ifp->state;
|
||||||
ifp->state = INET6_IFADDR_STATE_DEAD;
|
ifp->state = INET6_IFADDR_STATE_DEAD;
|
||||||
spin_unlock_bh(&ifp->state_lock);
|
spin_unlock_bh(&ifp->lock);
|
||||||
|
|
||||||
if (state == INET6_IFADDR_STATE_DEAD)
|
if (state == INET6_IFADDR_STATE_DEAD)
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -1686,19 +1698,21 @@ static int addrconf_dad_end(struct inet6_ifaddr *ifp)
|
||||||
{
|
{
|
||||||
int err = -ENOENT;
|
int err = -ENOENT;
|
||||||
|
|
||||||
spin_lock_bh(&ifp->state_lock);
|
spin_lock_bh(&ifp->lock);
|
||||||
if (ifp->state == INET6_IFADDR_STATE_DAD) {
|
if (ifp->state == INET6_IFADDR_STATE_DAD) {
|
||||||
ifp->state = INET6_IFADDR_STATE_POSTDAD;
|
ifp->state = INET6_IFADDR_STATE_POSTDAD;
|
||||||
err = 0;
|
err = 0;
|
||||||
}
|
}
|
||||||
spin_unlock_bh(&ifp->state_lock);
|
spin_unlock_bh(&ifp->lock);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addrconf_dad_failure(struct inet6_ifaddr *ifp)
|
void addrconf_dad_failure(struct inet6_ifaddr *ifp)
|
||||||
{
|
{
|
||||||
|
struct in6_addr addr;
|
||||||
struct inet6_dev *idev = ifp->idev;
|
struct inet6_dev *idev = ifp->idev;
|
||||||
|
struct net *net = dev_net(ifp->idev->dev);
|
||||||
|
|
||||||
if (addrconf_dad_end(ifp)) {
|
if (addrconf_dad_end(ifp)) {
|
||||||
in6_ifa_put(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",
|
net_info_ratelimited("%s: IPv6 duplicate address %pI6c detected!\n",
|
||||||
ifp->idev->dev->name, &ifp->addr);
|
ifp->idev->dev->name, &ifp->addr);
|
||||||
|
|
||||||
if (idev->cnf.accept_dad > 1 && !idev->cnf.disable_ipv6) {
|
spin_lock_bh(&ifp->lock);
|
||||||
struct in6_addr addr;
|
|
||||||
|
|
||||||
|
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[0] = htonl(0xfe800000);
|
||||||
addr.s6_addr32[1] = 0;
|
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 */
|
/* transition from _POSTDAD to _ERRDAD */
|
||||||
ifp->state = INET6_IFADDR_STATE_ERRDAD;
|
ifp->state = INET6_IFADDR_STATE_ERRDAD;
|
||||||
spin_unlock_bh(&ifp->state_lock);
|
spin_unlock_bh(&ifp->lock);
|
||||||
|
|
||||||
addrconf_mod_dad_work(ifp, 0);
|
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 valid_lft;
|
||||||
__u32 prefered_lft;
|
__u32 prefered_lft;
|
||||||
int addr_type;
|
int addr_type;
|
||||||
|
u32 addr_flags = 0;
|
||||||
struct inet6_dev *in6_dev;
|
struct inet6_dev *in6_dev;
|
||||||
struct net *net = dev_net(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);
|
in6_dev->token.s6_addr + 8, 8);
|
||||||
read_unlock_bh(&in6_dev->lock);
|
read_unlock_bh(&in6_dev->lock);
|
||||||
tokenized = true;
|
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) &&
|
} else if (ipv6_generate_eui64(addr.s6_addr + 8, dev) &&
|
||||||
ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) {
|
ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) {
|
||||||
in6_dev_put(in6_dev);
|
in6_dev_put(in6_dev);
|
||||||
|
@ -2310,7 +2379,6 @@ ok:
|
||||||
|
|
||||||
if (ifp == NULL && valid_lft) {
|
if (ifp == NULL && valid_lft) {
|
||||||
int max_addresses = in6_dev->cnf.max_addresses;
|
int max_addresses = in6_dev->cnf.max_addresses;
|
||||||
u32 addr_flags = 0;
|
|
||||||
|
|
||||||
#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
|
#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
|
||||||
if (in6_dev->cnf.optimistic_dad &&
|
if (in6_dev->cnf.optimistic_dad &&
|
||||||
|
@ -2350,7 +2418,7 @@ ok:
|
||||||
u32 stored_lft;
|
u32 stored_lft;
|
||||||
|
|
||||||
/* update lifetime (RFC2462 5.5.3 e) */
|
/* update lifetime (RFC2462 5.5.3 e) */
|
||||||
spin_lock(&ifp->lock);
|
spin_lock_bh(&ifp->lock);
|
||||||
now = jiffies;
|
now = jiffies;
|
||||||
if (ifp->valid_lft > (now - ifp->tstamp) / HZ)
|
if (ifp->valid_lft > (now - ifp->tstamp) / HZ)
|
||||||
stored_lft = ifp->valid_lft - (now - ifp->tstamp) / HZ;
|
stored_lft = ifp->valid_lft - (now - ifp->tstamp) / HZ;
|
||||||
|
@ -2380,12 +2448,12 @@ ok:
|
||||||
ifp->tstamp = now;
|
ifp->tstamp = now;
|
||||||
flags = ifp->flags;
|
flags = ifp->flags;
|
||||||
ifp->flags &= ~IFA_F_DEPRECATED;
|
ifp->flags &= ~IFA_F_DEPRECATED;
|
||||||
spin_unlock(&ifp->lock);
|
spin_unlock_bh(&ifp->lock);
|
||||||
|
|
||||||
if (!(flags&IFA_F_TENTATIVE))
|
if (!(flags&IFA_F_TENTATIVE))
|
||||||
ipv6_ifa_notify(0, ifp);
|
ipv6_ifa_notify(0, ifp);
|
||||||
} else
|
} else
|
||||||
spin_unlock(&ifp->lock);
|
spin_unlock_bh(&ifp->lock);
|
||||||
|
|
||||||
manage_tempaddrs(in6_dev, ifp, valid_lft, prefered_lft,
|
manage_tempaddrs(in6_dev, ifp, valid_lft, prefered_lft,
|
||||||
create, now);
|
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;
|
struct inet6_ifaddr *ifp;
|
||||||
u32 addr_flags = IFA_F_PERMANENT;
|
u32 addr_flags = flags | IFA_F_PERMANENT;
|
||||||
|
|
||||||
#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
|
#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
|
||||||
if (idev->cnf.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;
|
addr_flags |= IFA_F_OPTIMISTIC;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
ifp = ipv6_add_addr(idev, addr, NULL, 64, IFA_LINK, addr_flags,
|
ifp = ipv6_add_addr(idev, addr, NULL, 64, IFA_LINK, addr_flags,
|
||||||
INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
|
INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
|
||||||
if (!IS_ERR(ifp)) {
|
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)
|
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;
|
||||||
struct in6_addr addr;
|
|
||||||
|
|
||||||
ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0);
|
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
|
/* addrconf_add_linklocal also adds a prefix_route and we
|
||||||
* only need to care about prefix routes if ipv6_generate_eui64
|
* only need to care about prefix routes if ipv6_generate_eui64
|
||||||
* couldn't generate one.
|
* couldn't generate one.
|
||||||
*/
|
*/
|
||||||
if (ipv6_generate_eui64(addr.s6_addr + 8, idev->dev) == 0)
|
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)
|
else if (prefix_route)
|
||||||
addrconf_prefix_route(&addr, 64, idev->dev, 0, 0);
|
addrconf_prefix_route(&addr, 64, idev->dev, 0, 0);
|
||||||
}
|
}
|
||||||
|
@ -3159,10 +3312,10 @@ restart:
|
||||||
|
|
||||||
write_unlock_bh(&idev->lock);
|
write_unlock_bh(&idev->lock);
|
||||||
|
|
||||||
spin_lock_bh(&ifa->state_lock);
|
spin_lock_bh(&ifa->lock);
|
||||||
state = ifa->state;
|
state = ifa->state;
|
||||||
ifa->state = INET6_IFADDR_STATE_DEAD;
|
ifa->state = INET6_IFADDR_STATE_DEAD;
|
||||||
spin_unlock_bh(&ifa->state_lock);
|
spin_unlock_bh(&ifa->lock);
|
||||||
|
|
||||||
if (state != INET6_IFADDR_STATE_DEAD) {
|
if (state != INET6_IFADDR_STATE_DEAD) {
|
||||||
__ipv6_ifa_notify(RTM_DELADDR, ifa);
|
__ipv6_ifa_notify(RTM_DELADDR, ifa);
|
||||||
|
@ -3320,12 +3473,12 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp)
|
||||||
{
|
{
|
||||||
bool begin_dad = false;
|
bool begin_dad = false;
|
||||||
|
|
||||||
spin_lock_bh(&ifp->state_lock);
|
spin_lock_bh(&ifp->lock);
|
||||||
if (ifp->state != INET6_IFADDR_STATE_DEAD) {
|
if (ifp->state != INET6_IFADDR_STATE_DEAD) {
|
||||||
ifp->state = INET6_IFADDR_STATE_PREDAD;
|
ifp->state = INET6_IFADDR_STATE_PREDAD;
|
||||||
begin_dad = true;
|
begin_dad = true;
|
||||||
}
|
}
|
||||||
spin_unlock_bh(&ifp->state_lock);
|
spin_unlock_bh(&ifp->lock);
|
||||||
|
|
||||||
if (begin_dad)
|
if (begin_dad)
|
||||||
addrconf_mod_dad_work(ifp, 0);
|
addrconf_mod_dad_work(ifp, 0);
|
||||||
|
@ -3347,7 +3500,7 @@ static void addrconf_dad_work(struct work_struct *w)
|
||||||
|
|
||||||
rtnl_lock();
|
rtnl_lock();
|
||||||
|
|
||||||
spin_lock_bh(&ifp->state_lock);
|
spin_lock_bh(&ifp->lock);
|
||||||
if (ifp->state == INET6_IFADDR_STATE_PREDAD) {
|
if (ifp->state == INET6_IFADDR_STATE_PREDAD) {
|
||||||
action = DAD_BEGIN;
|
action = DAD_BEGIN;
|
||||||
ifp->state = INET6_IFADDR_STATE_DAD;
|
ifp->state = INET6_IFADDR_STATE_DAD;
|
||||||
|
@ -3355,7 +3508,7 @@ static void addrconf_dad_work(struct work_struct *w)
|
||||||
action = DAD_ABORT;
|
action = DAD_ABORT;
|
||||||
ifp->state = INET6_IFADDR_STATE_POSTDAD;
|
ifp->state = INET6_IFADDR_STATE_POSTDAD;
|
||||||
}
|
}
|
||||||
spin_unlock_bh(&ifp->state_lock);
|
spin_unlock_bh(&ifp->lock);
|
||||||
|
|
||||||
if (action == DAD_BEGIN) {
|
if (action == DAD_BEGIN) {
|
||||||
addrconf_dad_begin(ifp);
|
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_SUPPRESS_FRAG_NDISC] = cnf->suppress_frag_ndisc;
|
||||||
array[DEVCONF_ACCEPT_RA_FROM_LOCAL] = cnf->accept_ra_from_local;
|
array[DEVCONF_ACCEPT_RA_FROM_LOCAL] = cnf->accept_ra_from_local;
|
||||||
array[DEVCONF_ACCEPT_RA_MTU] = cnf->accept_ra_mtu;
|
array[DEVCONF_ACCEPT_RA_MTU] = cnf->accept_ra_mtu;
|
||||||
|
/* we omit DEVCONF_STABLE_SECRET for now */
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline size_t inet6_ifla6_size(void)
|
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]);
|
u8 mode = nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE]);
|
||||||
|
|
||||||
if (mode != IN6_ADDR_GEN_MODE_EUI64 &&
|
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;
|
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;
|
idev->addr_gen_mode = mode;
|
||||||
err = 0;
|
err = 0;
|
||||||
}
|
}
|
||||||
|
@ -5074,6 +5235,74 @@ int addrconf_sysctl_proxy_ndp(struct ctl_table *ctl, int write,
|
||||||
return ret;
|
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
|
static struct addrconf_sysctl_table
|
||||||
{
|
{
|
||||||
|
@ -5346,6 +5575,13 @@ static struct addrconf_sysctl_table
|
||||||
.mode = 0644,
|
.mode = 0644,
|
||||||
.proc_handler = proc_dointvec,
|
.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 */
|
/* sentinel */
|
||||||
}
|
}
|
||||||
|
@ -5442,6 +5678,9 @@ static int __net_init addrconf_init_net(struct net *net)
|
||||||
dflt->autoconf = ipv6_defaults.autoconf;
|
dflt->autoconf = ipv6_defaults.autoconf;
|
||||||
dflt->disable_ipv6 = ipv6_defaults.disable_ipv6;
|
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_all = all;
|
||||||
net->ipv6.devconf_dflt = dflt;
|
net->ipv6.devconf_dflt = dflt;
|
||||||
|
|
||||||
|
|
|
@ -766,6 +766,8 @@ static int __net_init inet6_net_init(struct net *net)
|
||||||
net->ipv6.sysctl.icmpv6_time = 1*HZ;
|
net->ipv6.sysctl.icmpv6_time = 1*HZ;
|
||||||
net->ipv6.sysctl.flowlabel_consistency = 1;
|
net->ipv6.sysctl.flowlabel_consistency = 1;
|
||||||
net->ipv6.sysctl.auto_flowlabels = 0;
|
net->ipv6.sysctl.auto_flowlabels = 0;
|
||||||
|
net->ipv6.sysctl.idgen_retries = 3;
|
||||||
|
net->ipv6.sysctl.idgen_delay = 1 * HZ;
|
||||||
atomic_set(&net->ipv6.fib6_sernum, 1);
|
atomic_set(&net->ipv6.fib6_sernum, 1);
|
||||||
|
|
||||||
err = ipv6_init_mibs(net);
|
err = ipv6_init_mibs(net);
|
||||||
|
|
|
@ -54,6 +54,20 @@ static struct ctl_table ipv6_table_template[] = {
|
||||||
.mode = 0644,
|
.mode = 0644,
|
||||||
.proc_handler = proc_dointvec
|
.proc_handler = proc_dointvec
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.procname = "idgen_retries",
|
||||||
|
.data = &init_net.ipv6.sysctl.idgen_retries,
|
||||||
|
.maxlen = sizeof(int),
|
||||||
|
.mode = 0644,
|
||||||
|
.proc_handler = proc_dointvec,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.procname = "idgen_delay",
|
||||||
|
.data = &init_net.ipv6.sysctl.idgen_delay,
|
||||||
|
.maxlen = sizeof(int),
|
||||||
|
.mode = 0644,
|
||||||
|
.proc_handler = proc_dointvec_jiffies,
|
||||||
|
},
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -93,6 +107,8 @@ static int __net_init ipv6_sysctl_net_init(struct net *net)
|
||||||
ipv6_table[2].data = &net->ipv6.sysctl.flowlabel_consistency;
|
ipv6_table[2].data = &net->ipv6.sysctl.flowlabel_consistency;
|
||||||
ipv6_table[3].data = &net->ipv6.sysctl.auto_flowlabels;
|
ipv6_table[3].data = &net->ipv6.sysctl.auto_flowlabels;
|
||||||
ipv6_table[4].data = &net->ipv6.sysctl.fwmark_reflect;
|
ipv6_table[4].data = &net->ipv6.sysctl.fwmark_reflect;
|
||||||
|
ipv6_table[5].data = &net->ipv6.sysctl.idgen_retries;
|
||||||
|
ipv6_table[6].data = &net->ipv6.sysctl.idgen_delay;
|
||||||
|
|
||||||
ipv6_route_table = ipv6_route_sysctl_init(net);
|
ipv6_route_table = ipv6_route_sysctl_init(net);
|
||||||
if (!ipv6_route_table)
|
if (!ipv6_route_table)
|
||||||
|
|
Loading…
Reference in New Issue