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
|
||||
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
|
||||
Controls the MLD query robustness variable (see RFC3810 9.1).
|
||||
Default: 2 (as specified by RFC3810 9.1)
|
||||
|
@ -1540,6 +1551,20 @@ use_optimistic - BOOLEAN
|
|||
0: disabled (default)
|
||||
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/*:
|
||||
ratelimit - INTEGER
|
||||
Limit the maximal rates for sending ICMPv6 packets.
|
||||
|
|
|
@ -53,6 +53,10 @@ struct ipv6_devconf {
|
|||
__s32 ndisc_notify;
|
||||
__s32 suppress_frag_ndisc;
|
||||
__s32 accept_ra_mtu;
|
||||
struct ipv6_stable_secret {
|
||||
bool initialized;
|
||||
struct in6_addr secret;
|
||||
} stable_secret;
|
||||
void *sysctl;
|
||||
};
|
||||
|
||||
|
|
|
@ -41,18 +41,18 @@ enum {
|
|||
struct inet6_ifaddr {
|
||||
struct in6_addr addr;
|
||||
__u32 prefix_len;
|
||||
|
||||
|
||||
/* In seconds, relative to tstamp. Expiry is at tstamp + HZ * lft. */
|
||||
__u32 valid_lft;
|
||||
__u32 prefered_lft;
|
||||
atomic_t refcnt;
|
||||
spinlock_t lock;
|
||||
spinlock_t state_lock;
|
||||
|
||||
int state;
|
||||
|
||||
__u32 flags;
|
||||
__u8 dad_probes;
|
||||
__u8 stable_privacy_retry;
|
||||
|
||||
__u16 scope;
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ struct netns_sysctl_ipv6 {
|
|||
int icmpv6_time;
|
||||
int anycast_src_echo_reply;
|
||||
int fwmark_reflect;
|
||||
int idgen_retries;
|
||||
int idgen_delay;
|
||||
};
|
||||
|
||||
struct netns_ipv6 {
|
||||
|
|
|
@ -51,6 +51,7 @@ enum {
|
|||
#define IFA_F_MANAGETEMPADDR 0x100
|
||||
#define IFA_F_NOPREFIXROUTE 0x200
|
||||
#define IFA_F_MCAUTOJOIN 0x400
|
||||
#define IFA_F_STABLE_PRIVACY 0x800
|
||||
|
||||
struct ifa_cacheinfo {
|
||||
__u32 ifa_prefered;
|
||||
|
|
|
@ -216,6 +216,7 @@ enum {
|
|||
enum in6_addr_gen_mode {
|
||||
IN6_ADDR_GEN_MODE_EUI64,
|
||||
IN6_ADDR_GEN_MODE_NONE,
|
||||
IN6_ADDR_GEN_MODE_STABLE_PRIVACY,
|
||||
};
|
||||
|
||||
/* Bridge section */
|
||||
|
|
|
@ -170,6 +170,7 @@ enum {
|
|||
DEVCONF_ACCEPT_RA_FROM_LOCAL,
|
||||
DEVCONF_USE_OPTIMISTIC,
|
||||
DEVCONF_ACCEPT_RA_MTU,
|
||||
DEVCONF_STABLE_SECRET,
|
||||
DEVCONF_MAX
|
||||
};
|
||||
|
||||
|
|
|
@ -198,3 +198,4 @@ void sha_init(__u32 *buf)
|
|||
buf[3] = 0x10325476;
|
||||
buf[4] = 0xc3d2e1f0;
|
||||
}
|
||||
EXPORT_SYMBOL(sha_init);
|
||||
|
|
|
@ -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;
|
||||
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
|
||||
* 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;
|
||||
|
||||
|
|
|
@ -766,6 +766,8 @@ static int __net_init inet6_net_init(struct net *net)
|
|||
net->ipv6.sysctl.icmpv6_time = 1*HZ;
|
||||
net->ipv6.sysctl.flowlabel_consistency = 1;
|
||||
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);
|
||||
|
||||
err = ipv6_init_mibs(net);
|
||||
|
|
|
@ -54,6 +54,20 @@ static struct ctl_table ipv6_table_template[] = {
|
|||
.mode = 0644,
|
||||
.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[3].data = &net->ipv6.sysctl.auto_flowlabels;
|
||||
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);
|
||||
if (!ipv6_route_table)
|
||||
|
|
Loading…
Reference in New Issue