summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/linux/ipv6.h4
-rw-r--r--include/uapi/linux/ipv6.h1
-rw-r--r--net/ipv6/addrconf.c68
3 files changed, 73 insertions, 0 deletions
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 4d5169f5d7d1..82806c60aa42 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -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;
};
diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h
index 437a6a4b125a..5efa54ae567c 100644
--- a/include/uapi/linux/ipv6.h
+++ b/include/uapi/linux/ipv6.h
@@ -170,6 +170,7 @@ enum {
DEVCONF_ACCEPT_RA_FROM_LOCAL,
DEVCONF_USE_OPTIMISTIC,
DEVCONF_ACCEPT_RA_MTU,
+ DEVCONF_STABLE_SECRET,
DEVCONF_MAX
};
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 158378e73f0a..5b967c8a617a 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -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;
@@ -202,6 +206,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 +247,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 */
@@ -4430,6 +4440,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)
@@ -5074,6 +5085,53 @@ 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 ipv6_stable_secret *secret = ctl->data;
+
+ 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;
+
+out:
+ rtnl_unlock();
+
+ return err;
+}
static struct addrconf_sysctl_table
{
@@ -5347,6 +5405,13 @@ static struct addrconf_sysctl_table
.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 +5507,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;