[PATCH v2 net-next] ip6_tunnel: Adding support of mapping rules for MAP-E tunnel

2018-12-02 Thread Felix Jia
From: Blair Steven 

Mapping of Addresses and Ports with Encapsulation (MAP-E) is defined in
RFC7597, and is an IPv6 transition technology providing interoperability
between IPv4 and IPv6 networks.

MAP-E uses the encapsulation mode described in RFC2473 (IPv6 Tunneling)
to transport IPv4 and IPv6 packets over an IPv6 network. It requires a
list rules for mapping between IPv4 prefix/shared addresses and IPv6
addresses.

This patch also supports the mapping rules defined in the draft3 version
of the RFC.

Co-developed-by: Felix Jia 
Co-developed-by: Sheena Mira-ato 
Co-developed-by: Masakazu Asama 
Signed-off-by: Blair Steven 
Signed-off-by: Felix Jia 
Signed-off-by: Sheena Mira-ato 
Signed-off-by: Masakazu Asama 
---
 include/net/ip6_tunnel.h   |  18 ++
 include/uapi/linux/if_tunnel.h |  18 ++
 net/ipv6/ip6_tunnel.c  | 490 -
 3 files changed, 524 insertions(+), 2 deletions(-)

diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h
index 69b4bcf880c9..ed715ee8d87c 100644
--- a/include/net/ip6_tunnel.h
+++ b/include/net/ip6_tunnel.h
@@ -18,6 +18,16 @@
 /* determine capability on a per-packet basis */
 #define IP6_TNL_F_CAP_PER_PACKET 0x4
 
+struct ip6_tnl_rule {
+   struct in_addr ipv4_subnet;
+   struct in6_addr ipv6_subnet;
+   u8 version;
+   u8 ea_length;
+   u8 psid_offset;
+   u8 ipv4_prefixlen;
+   u8 ipv6_prefixlen;
+};
+
 struct __ip6_tnl_parm {
char name[IFNAMSIZ];/* name of tunnel device */
int link;   /* ifindex of underlying L2 interface */
@@ -40,6 +50,13 @@ struct __ip6_tnl_parm {
__u8erspan_ver; /* ERSPAN version */
__u8dir;/* direction */
__u16   hwid;   /* hwid */
+   __u8rule_action;
+   struct ip6_tnl_rule rule;
+};
+
+struct ip6_rule_list {
+   struct list_head list;
+   struct ip6_tnl_rule data;
 };
 
 /* IPv6 tunnel */
@@ -63,6 +80,7 @@ struct ip6_tnl {
int encap_hlen; /* Encap header length (FOU,GUE) */
struct ip_tunnel_encap encap;
int mlink;
+   struct ip6_rule_list rules;
 };
 
 struct ip6_tnl_encap_ops {
diff --git a/include/uapi/linux/if_tunnel.h b/include/uapi/linux/if_tunnel.h
index 1b3d148c4560..7cb09c8c4d8a 100644
--- a/include/uapi/linux/if_tunnel.h
+++ b/include/uapi/linux/if_tunnel.h
@@ -77,10 +77,28 @@ enum {
IFLA_IPTUN_ENCAP_DPORT,
IFLA_IPTUN_COLLECT_METADATA,
IFLA_IPTUN_FWMARK,
+   IFLA_IPTUN_RULE_VERSION,
+   IFLA_IPTUN_RULE_ACTION,
+   IFLA_IPTUN_RULE_IPV6_PREFIX,
+   IFLA_IPTUN_RULE_IPV6_PREFIXLEN,
+   IFLA_IPTUN_RULE_IPV4_PREFIX,
+   IFLA_IPTUN_RULE_IPV4_PREFIXLEN,
+   IFLA_IPTUN_RULE_EA_LENGTH,
+   IFLA_IPTUN_RULE_PSID_OFFSET,
__IFLA_IPTUN_MAX,
 };
 #define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1)
 
+enum map_rule_versions {
+   MAP_VERSION_RFC,
+   MAP_VERSION_DRAFT3,
+};
+
+enum tunnel_rule_actions {
+   TUNNEL_RULE_ACTION_ADD = 1,
+   TUNNEL_RULE_ACTION_DELETE = 2,
+};
+
 enum tunnel_encap_types {
TUNNEL_ENCAP_NONE,
TUNNEL_ENCAP_FOU,
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index a9d06d4dd057..3bd7a5045f28 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -20,6 +20,8 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+#include 
+#include 
 #include 
 #include 
 #include 
@@ -32,6 +34,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -124,6 +127,226 @@ static struct net_device_stats *ip6_get_stats(struct 
net_device *dev)
return >stats;
 }
 
+int
+ip6_get_addrport(struct iphdr *iph, __be32 *saddr4, __be32 *daddr4,
+__be16 *sport4, __be16 *dport4, __u8 *proto, int *icmperr)
+{
+   u8 *ptr;
+   struct iphdr *icmpiph = NULL;
+   struct tcphdr *tcph, *icmptcph;
+   struct udphdr *udph, *icmpudph;
+   struct icmphdr *icmph, *icmpicmph;
+
+   *icmperr = 0;
+   *saddr4 = iph->saddr;
+   *daddr4 = iph->daddr;
+   ptr = (u8 *)iph;
+   ptr += iph->ihl * 4;
+   switch (iph->protocol) {
+   case IPPROTO_TCP:
+   *proto = IPPROTO_TCP;
+   tcph = (struct tcphdr *)ptr;
+   *sport4 = tcph->source;
+   *dport4 = tcph->dest;
+   break;
+   case IPPROTO_UDP:
+   *proto = IPPROTO_UDP;
+   udph = (struct udphdr *)ptr;
+   *sport4 = udph->source;
+   *dport4 = udph->dest;
+   break;
+   case IPPROTO_ICMP:
+   *proto = IPPROTO_ICMP;
+   icmph = (struct icmphdr *)ptr;
+   switch (icmph->type) {
+   case ICMP_DEST_UNREACH:
+   case ICMP_SOURCE_QUENCH:
+   case ICMP_TIME_EXCEEDED:
+   case ICMP_PARAMETERPROB:
+   *icmper

[PATCH v1 net-next] ip6_tunnel: Adding support of mapping rules for MAP-E tunnel

2018-11-19 Thread Felix Jia
From: Blair Steven 

Mapping of Addresses and Ports with Encapsulation (MAP-E) is defined in
RFC7597, and is an IPv6 transition technology providing interoperability
between IPv4 and IPv6 networks.

MAP-E uses the encapsulation mode described in RFC2473 (IPv6 Tunneling)
to transport IPv4 and IPv6 packets over an IPv6 network. It requires a
list rules for mapping between IPv4 prefix/shared addresses and IPv6
addresses.

This patch also support the mapping rules define in the draft3 version of
the RFC.

Co-developed-by: Felix Jia 
Co-developed-by: Sheena Mira-ato 
Co-developed-by: Masakazu Asama 
Signed-off-by: Blair Steven 
Signed-off-by: Felix Jia 
Signed-off-by: Sheena Mira-ato 
Signed-off-by: Masakazu Asama 
---
 include/net/ip6_tunnel.h   |  18 ++
 include/uapi/linux/if_tunnel.h |  18 ++
 net/ipv6/ip6_tunnel.c  | 491 -
 3 files changed, 525 insertions(+), 2 deletions(-)

diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h
index 69b4bcf880c9..daa159185228 100644
--- a/include/net/ip6_tunnel.h
+++ b/include/net/ip6_tunnel.h
@@ -18,6 +18,16 @@
 /* determine capability on a per-packet basis */
 #define IP6_TNL_F_CAP_PER_PACKET 0x4
 
+struct ip6_tnl_rule {
+   u8 version;
+   struct in6_addr ipv6_subnet;
+   u8 ipv6_prefixlen;
+   struct in_addr ipv4_subnet;
+   u8 ipv4_prefixlen;
+   u8 ea_length;
+   u8 psid_offset;
+};
+
 struct __ip6_tnl_parm {
char name[IFNAMSIZ];/* name of tunnel device */
int link;   /* ifindex of underlying L2 interface */
@@ -40,6 +50,13 @@ struct __ip6_tnl_parm {
__u8erspan_ver; /* ERSPAN version */
__u8dir;/* direction */
__u16   hwid;   /* hwid */
+   __u8rule_action;
+   struct ip6_tnl_rule rule;
+};
+
+struct ip6_rule_list {
+   struct list_head list;
+   struct ip6_tnl_rule data;
 };
 
 /* IPv6 tunnel */
@@ -63,6 +80,7 @@ struct ip6_tnl {
int encap_hlen; /* Encap header length (FOU,GUE) */
struct ip_tunnel_encap encap;
int mlink;
+   struct ip6_rule_list rules;
 };
 
 struct ip6_tnl_encap_ops {
diff --git a/include/uapi/linux/if_tunnel.h b/include/uapi/linux/if_tunnel.h
index 1b3d148c4560..7cb09c8c4d8a 100644
--- a/include/uapi/linux/if_tunnel.h
+++ b/include/uapi/linux/if_tunnel.h
@@ -77,10 +77,28 @@ enum {
IFLA_IPTUN_ENCAP_DPORT,
IFLA_IPTUN_COLLECT_METADATA,
IFLA_IPTUN_FWMARK,
+   IFLA_IPTUN_RULE_VERSION,
+   IFLA_IPTUN_RULE_ACTION,
+   IFLA_IPTUN_RULE_IPV6_PREFIX,
+   IFLA_IPTUN_RULE_IPV6_PREFIXLEN,
+   IFLA_IPTUN_RULE_IPV4_PREFIX,
+   IFLA_IPTUN_RULE_IPV4_PREFIXLEN,
+   IFLA_IPTUN_RULE_EA_LENGTH,
+   IFLA_IPTUN_RULE_PSID_OFFSET,
__IFLA_IPTUN_MAX,
 };
 #define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1)
 
+enum map_rule_versions {
+   MAP_VERSION_RFC,
+   MAP_VERSION_DRAFT3,
+};
+
+enum tunnel_rule_actions {
+   TUNNEL_RULE_ACTION_ADD = 1,
+   TUNNEL_RULE_ACTION_DELETE = 2,
+};
+
 enum tunnel_encap_types {
TUNNEL_ENCAP_NONE,
TUNNEL_ENCAP_FOU,
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index a9d06d4dd057..e13dc5bd09b5 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -20,6 +20,8 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+#include 
+#include 
 #include 
 #include 
 #include 
@@ -32,6 +34,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -124,6 +127,228 @@ static struct net_device_stats *ip6_get_stats(struct 
net_device *dev)
return >stats;
 }
 
+int
+ip6_get_addrport(struct iphdr *iph, __be32 *saddr4, __be32 *daddr4,
+__be16 *sport4, __be16 *dport4, __u8 *proto, int *icmperr)
+{
+   u8 *ptr;
+   struct iphdr *icmpiph = NULL;
+   struct tcphdr *tcph, *icmptcph;
+   struct udphdr *udph, *icmpudph;
+   struct icmphdr *icmph, *icmpicmph;
+
+   *icmperr = 0;
+   *saddr4 = iph->saddr;
+   *daddr4 = iph->daddr;
+   ptr = (u8 *)iph;
+   ptr += iph->ihl * 4;
+   switch (iph->protocol) {
+   case IPPROTO_TCP:
+   *proto = IPPROTO_TCP;
+   tcph = (struct tcphdr *)ptr;
+   *sport4 = tcph->source;
+   *dport4 = tcph->dest;
+   break;
+   case IPPROTO_UDP:
+   *proto = IPPROTO_UDP;
+   udph = (struct udphdr *)ptr;
+   *sport4 = udph->source;
+   *dport4 = udph->dest;
+   break;
+   case IPPROTO_ICMP:
+   *proto = IPPROTO_ICMP;
+   icmph = (struct icmphdr *)ptr;
+   switch (icmph->type) {
+   case ICMP_DEST_UNREACH:
+   case ICMP_SOURCE_QUENCH:
+   case ICMP_TIME_EXCEEDED:
+   case ICMP_PARAMETERPROB:
+   *icmper

[PATCH v1 1/2] ip6_tunnel: Adding support of mapping rules for MAP-E tunnel

2018-11-19 Thread Felix Jia
From: Blair Steven 

Mapping of Addresses and Ports with Encapsulation (MAP-E) is defined in
RFC7597, and is an IPv6 transition technology providing interoperability
between IPv4 and IPv6 networks.

MAP-E uses the encapsulation mode described in RFC2473 (IPv6 Tunneling)
to transport IPv4 and IPv6 packets over an IPv6 network. It requires a
list rules for mapping between IPv4 prefix/shared addresses and IPv6
addresses.

This patch also support the mapping rules define in the draft3 version of
the RFC.

Co-developed-by: Felix Jia 
Co-developed-by: Sheena Mira-ato 
Co-developed-by: Masakazu Asama 
Signed-off-by: Blair Steven 
Signed-off-by: Felix Jia 
Signed-off-by: Sheena Mira-ato 
Signed-off-by: Masakazu Asama 
---
 include/net/ip6_tunnel.h   |  18 ++
 include/uapi/linux/if_tunnel.h |  18 ++
 net/ipv6/ip6_tunnel.c  | 491 -
 3 files changed, 525 insertions(+), 2 deletions(-)

diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h
index 69b4bcf880c9..daa159185228 100644
--- a/include/net/ip6_tunnel.h
+++ b/include/net/ip6_tunnel.h
@@ -18,6 +18,16 @@
 /* determine capability on a per-packet basis */
 #define IP6_TNL_F_CAP_PER_PACKET 0x4
 
+struct ip6_tnl_rule {
+   u8 version;
+   struct in6_addr ipv6_subnet;
+   u8 ipv6_prefixlen;
+   struct in_addr ipv4_subnet;
+   u8 ipv4_prefixlen;
+   u8 ea_length;
+   u8 psid_offset;
+};
+
 struct __ip6_tnl_parm {
char name[IFNAMSIZ];/* name of tunnel device */
int link;   /* ifindex of underlying L2 interface */
@@ -40,6 +50,13 @@ struct __ip6_tnl_parm {
__u8erspan_ver; /* ERSPAN version */
__u8dir;/* direction */
__u16   hwid;   /* hwid */
+   __u8rule_action;
+   struct ip6_tnl_rule rule;
+};
+
+struct ip6_rule_list {
+   struct list_head list;
+   struct ip6_tnl_rule data;
 };
 
 /* IPv6 tunnel */
@@ -63,6 +80,7 @@ struct ip6_tnl {
int encap_hlen; /* Encap header length (FOU,GUE) */
struct ip_tunnel_encap encap;
int mlink;
+   struct ip6_rule_list rules;
 };
 
 struct ip6_tnl_encap_ops {
diff --git a/include/uapi/linux/if_tunnel.h b/include/uapi/linux/if_tunnel.h
index 1b3d148c4560..7cb09c8c4d8a 100644
--- a/include/uapi/linux/if_tunnel.h
+++ b/include/uapi/linux/if_tunnel.h
@@ -77,10 +77,28 @@ enum {
IFLA_IPTUN_ENCAP_DPORT,
IFLA_IPTUN_COLLECT_METADATA,
IFLA_IPTUN_FWMARK,
+   IFLA_IPTUN_RULE_VERSION,
+   IFLA_IPTUN_RULE_ACTION,
+   IFLA_IPTUN_RULE_IPV6_PREFIX,
+   IFLA_IPTUN_RULE_IPV6_PREFIXLEN,
+   IFLA_IPTUN_RULE_IPV4_PREFIX,
+   IFLA_IPTUN_RULE_IPV4_PREFIXLEN,
+   IFLA_IPTUN_RULE_EA_LENGTH,
+   IFLA_IPTUN_RULE_PSID_OFFSET,
__IFLA_IPTUN_MAX,
 };
 #define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1)
 
+enum map_rule_versions {
+   MAP_VERSION_RFC,
+   MAP_VERSION_DRAFT3,
+};
+
+enum tunnel_rule_actions {
+   TUNNEL_RULE_ACTION_ADD = 1,
+   TUNNEL_RULE_ACTION_DELETE = 2,
+};
+
 enum tunnel_encap_types {
TUNNEL_ENCAP_NONE,
TUNNEL_ENCAP_FOU,
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index a9d06d4dd057..e13dc5bd09b5 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -20,6 +20,8 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+#include 
+#include 
 #include 
 #include 
 #include 
@@ -32,6 +34,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -124,6 +127,228 @@ static struct net_device_stats *ip6_get_stats(struct 
net_device *dev)
return >stats;
 }
 
+int
+ip6_get_addrport(struct iphdr *iph, __be32 *saddr4, __be32 *daddr4,
+__be16 *sport4, __be16 *dport4, __u8 *proto, int *icmperr)
+{
+   u8 *ptr;
+   struct iphdr *icmpiph = NULL;
+   struct tcphdr *tcph, *icmptcph;
+   struct udphdr *udph, *icmpudph;
+   struct icmphdr *icmph, *icmpicmph;
+
+   *icmperr = 0;
+   *saddr4 = iph->saddr;
+   *daddr4 = iph->daddr;
+   ptr = (u8 *)iph;
+   ptr += iph->ihl * 4;
+   switch (iph->protocol) {
+   case IPPROTO_TCP:
+   *proto = IPPROTO_TCP;
+   tcph = (struct tcphdr *)ptr;
+   *sport4 = tcph->source;
+   *dport4 = tcph->dest;
+   break;
+   case IPPROTO_UDP:
+   *proto = IPPROTO_UDP;
+   udph = (struct udphdr *)ptr;
+   *sport4 = udph->source;
+   *dport4 = udph->dest;
+   break;
+   case IPPROTO_ICMP:
+   *proto = IPPROTO_ICMP;
+   icmph = (struct icmphdr *)ptr;
+   switch (icmph->type) {
+   case ICMP_DEST_UNREACH:
+   case ICMP_SOURCE_QUENCH:
+   case ICMP_TIME_EXCEEDED:
+   case ICMP_PARAMETERPROB:
+   *icmper

[PATCH v1 2/2] netfilter: Add PSID mode to MASQUERADE

2018-11-19 Thread Felix Jia
From: Blair Steven 

This adds support for masquerading into a smaller subset of ports -
defined by the PSID values from RFC-7597 Section 5.1. This is part of
the support for MAP-E, which allows multiple devices to share an IPv4
address by splitting the L4 port / id into ranges by both masquerading
and encapsulating IPv4 packets inside an IPv6 carrier.

Co-developed-by: Anthony Lineham 
Co-developed-by: Scott Parlane 
Signed-off-by: Blair Steven 
Signed-off-by: Anthony Lineham 
Signed-off-by: Scott Parlane 
Signed-off-by: Felix Jia 
---
 include/net/netfilter/nf_nat_l4proto.h|  6 +--
 .../netfilter/nf_conntrack_tuple_common.h |  5 ++
 include/uapi/linux/netfilter/nf_nat.h |  4 +-
 net/ipv4/netfilter/nf_nat_proto_icmp.c| 51 ++-
 net/netfilter/nf_nat_core.c   | 25 ++---
 net/netfilter/nf_nat_proto_common.c   | 51 +--
 net/netfilter/nf_nat_proto_unknown.c  |  3 +-
 7 files changed, 126 insertions(+), 19 deletions(-)

diff --git a/include/net/netfilter/nf_nat_l4proto.h 
b/include/net/netfilter/nf_nat_l4proto.h
index b4d6b29bca62..d3fb8f138d0a 100644
--- a/include/net/netfilter/nf_nat_l4proto.h
+++ b/include/net/netfilter/nf_nat_l4proto.h
@@ -24,8 +24,7 @@ struct nf_nat_l4proto {
/* Is the manipable part of the tuple between min and max incl? */
bool (*in_range)(const struct nf_conntrack_tuple *tuple,
 enum nf_nat_manip_type maniptype,
-const union nf_conntrack_man_proto *min,
-const union nf_conntrack_man_proto *max);
+const struct nf_nat_range2 *range);
 
/* Alter the per-proto part of the tuple (depending on
 * maniptype), to give a unique tuple in the given range if
@@ -67,8 +66,7 @@ extern const struct nf_nat_l4proto nf_nat_l4proto_udplite;
 
 bool nf_nat_l4proto_in_range(const struct nf_conntrack_tuple *tuple,
 enum nf_nat_manip_type maniptype,
-const union nf_conntrack_man_proto *min,
-const union nf_conntrack_man_proto *max);
+const struct nf_nat_range2 *range);
 
 void nf_nat_l4proto_unique_tuple(const struct nf_nat_l3proto *l3proto,
 struct nf_conntrack_tuple *tuple,
diff --git a/include/uapi/linux/netfilter/nf_conntrack_tuple_common.h 
b/include/uapi/linux/netfilter/nf_conntrack_tuple_common.h
index 64390fac6f7e..36d16d47c2b0 100644
--- a/include/uapi/linux/netfilter/nf_conntrack_tuple_common.h
+++ b/include/uapi/linux/netfilter/nf_conntrack_tuple_common.h
@@ -39,6 +39,11 @@ union nf_conntrack_man_proto {
struct {
__be16 key; /* GRE key is 32bit, PPtP only uses 16bit */
} gre;
+   struct {
+   unsigned char psid_length;
+   unsigned char offset;
+   __be16 psid;
+   } psid;
 };
 
 #define CTINFO2DIR(ctinfo) ((ctinfo) >= IP_CT_IS_REPLY ? IP_CT_DIR_REPLY : 
IP_CT_DIR_ORIGINAL)
diff --git a/include/uapi/linux/netfilter/nf_nat.h 
b/include/uapi/linux/netfilter/nf_nat.h
index 4a95c0db14d4..d145d3eca25f 100644
--- a/include/uapi/linux/netfilter/nf_nat.h
+++ b/include/uapi/linux/netfilter/nf_nat.h
@@ -11,6 +11,7 @@
 #define NF_NAT_RANGE_PERSISTENT(1 << 3)
 #define NF_NAT_RANGE_PROTO_RANDOM_FULLY(1 << 4)
 #define NF_NAT_RANGE_PROTO_OFFSET  (1 << 5)
+#define NF_NAT_RANGE_PSID  (1 << 6)
 
 #define NF_NAT_RANGE_PROTO_RANDOM_ALL  \
(NF_NAT_RANGE_PROTO_RANDOM | NF_NAT_RANGE_PROTO_RANDOM_FULLY)
@@ -18,7 +19,8 @@
 #define NF_NAT_RANGE_MASK  \
(NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED |  \
 NF_NAT_RANGE_PROTO_RANDOM | NF_NAT_RANGE_PERSISTENT |  \
-NF_NAT_RANGE_PROTO_RANDOM_FULLY | NF_NAT_RANGE_PROTO_OFFSET)
+NF_NAT_RANGE_PROTO_RANDOM_FULLY | NF_NAT_RANGE_PROTO_OFFSET |  \
+NF_NAT_RANGE_PSID)
 
 struct nf_nat_ipv4_range {
unsigned intflags;
diff --git a/net/ipv4/netfilter/nf_nat_proto_icmp.c 
b/net/ipv4/netfilter/nf_nat_proto_icmp.c
index 6d7cf1d79baf..39efac4930b6 100644
--- a/net/ipv4/netfilter/nf_nat_proto_icmp.c
+++ b/net/ipv4/netfilter/nf_nat_proto_icmp.c
@@ -20,9 +20,23 @@
 static bool
 icmp_in_range(const struct nf_conntrack_tuple *tuple,
  enum nf_nat_manip_type maniptype,
- const union nf_conntrack_man_proto *min,
- const union nf_conntrack_man_proto *max)
+ const struct nf_nat_range2 *range)
 {
+   const union nf_conntrack_man_proto *min = >min_proto;
+   const union nf_conntrack_man_proto *max = >max_proto;
+
+   /* In this case we are in PSID mode and the rules are all different */
+   if (range->flags & NF_NAT_RANGE_PSID) {
+   u16 of

[PATCH net/ipv6] net/ipv6: avoid possible dead locking on addr_gen_mode sysctl

2017-02-26 Thread Felix Jia
The addr_gen_mode variable can be accessed by both sysctl and netlink.
Repleacd rtnl_lock() with rtnl_trylock() protect the sysctl operation to
avoid the possbile dead lock.`

Signed-off-by: Felix Jia <felix@alliedtelesis.co.nz>
---
 net/ipv6/addrconf.c | 22 +++---
 1 file changed, 15 insertions(+), 7 deletions(-)

diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 3a2025f5bf2c..cfc485a8e1c0 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -5692,13 +5692,18 @@ static int addrconf_sysctl_addr_gen_mode(struct 
ctl_table *ctl, int write,
struct inet6_dev *idev = (struct inet6_dev *)ctl->extra1;
struct net *net = (struct net *)ctl->extra2;
 
+   if (!rtnl_trylock())
+   return restart_syscall();
+
ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
 
if (write) {
new_val = *((int *)ctl->data);
 
-   if (check_addr_gen_mode(new_val) < 0)
-   return -EINVAL;
+   if (check_addr_gen_mode(new_val) < 0) {
+   ret = -EINVAL;
+   goto out;
+   }
 
/* request for default */
if (>ipv6.devconf_dflt->addr_gen_mode == ctl->data) {
@@ -5707,20 +5712,23 @@ static int addrconf_sysctl_addr_gen_mode(struct 
ctl_table *ctl, int write,
/* request for individual net device */
} else {
if (!idev)
-   return ret;
+   goto out;
 
-   if (check_stable_privacy(idev, net, new_val) < 0)
-   return -EINVAL;
+   if (check_stable_privacy(idev, net, new_val) < 0) {
+   ret = -EINVAL;
+   goto out;
+   }
 
if (idev->cnf.addr_gen_mode != new_val) {
idev->cnf.addr_gen_mode = new_val;
-   rtnl_lock();
addrconf_dev_config(idev->dev);
-   rtnl_unlock();
}
}
}
 
+out:
+   rtnl_unlock();
+
return ret;
 }
 
-- 
2.11.0



[PATCH v4 net-next 1/2] net/ipv6: allow sysctl to change link-local address generation mode

2017-01-25 Thread Felix Jia
The address generation mode for IPv6 link-local can only be configured
by netlink messages. This patch adds the ability to change the address
generation mode via sysctl.

v1 -> v2
Removed the rtnl lock and switch to use RCU lock to iterate through
the netdev list.

v2 -> v3
Removed the addrgenmode variable from the idev structure and use the 
systcl storage for the flag.

Simplifed the logic for sysctl handling by removing the supported 
for all operation.

Added support for more types of tunnel interfaces for link-local 
address generation.

Based the patches from net-next.

v3 -> v4
Removed unnecessary whitespace changes.

Signed-off-by: Felix Jia <felix@alliedtelesis.co.nz>
---
 include/linux/ipv6.h  |   1 +
 include/net/if_inet6.h|   1 -
 include/uapi/linux/ipv6.h |   1 +
 net/ipv6/addrconf.c   | 104 +-
 4 files changed, 86 insertions(+), 21 deletions(-)

diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 671d014e6429..71be5b330d21 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -69,6 +69,7 @@ struct ipv6_devconf {
__s32   seg6_require_hmac;
 #endif
__u32   enhanced_dad;
+   __u32   addr_gen_mode;
 
struct ctl_table_header *sysctl_header;
 };
diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h
index 0fa4c324b713..f656f9051aca 100644
--- a/include/net/if_inet6.h
+++ b/include/net/if_inet6.h
@@ -205,7 +205,6 @@ struct inet6_dev {
__s32   rs_interval;/* in jiffies */
__u8rs_probes;
 
-   __u8addr_gen_mode;
unsigned long   tstamp; /* ipv6InterfaceTable update timestamp 
*/
struct rcu_head rcu;
 };
diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h
index eaf65dc82e22..8ef9e75e004e 100644
--- a/include/uapi/linux/ipv6.h
+++ b/include/uapi/linux/ipv6.h
@@ -182,6 +182,7 @@ enum {
DEVCONF_SEG6_ENABLED,
DEVCONF_SEG6_REQUIRE_HMAC,
DEVCONF_ENHANCED_DAD,
+   DEVCONF_ADDR_GEN_MODE,
DEVCONF_MAX
 };
 
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index ac9bd5620f81..e35259dd17ba 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -243,6 +243,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
.seg6_require_hmac  = 0,
 #endif
.enhanced_dad   = 1,
+   .addr_gen_mode  = IN6_ADDR_GEN_MODE_EUI64,
 };
 
 static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
@@ -294,6 +295,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly 
= {
.seg6_require_hmac  = 0,
 #endif
.enhanced_dad   = 1,
+   .addr_gen_mode  = IN6_ADDR_GEN_MODE_EUI64,
 };
 
 /* Check if a valid qdisc is available */
@@ -386,9 +388,9 @@ static struct inet6_dev *ipv6_add_dev(struct net_device 
*dev)
memcpy(>cnf, dev_net(dev)->ipv6.devconf_dflt, sizeof(ndev->cnf));
 
if (ndev->cnf.stable_secret.initialized)
-   ndev->addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
+   ndev->cnf.addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
else
-   ndev->addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64;
+   ndev->cnf.addr_gen_mode = ipv6_devconf_dflt.addr_gen_mode;
 
ndev->cnf.mtu6 = dev->mtu;
ndev->nd_parms = neigh_parms_alloc(dev, _tbl);
@@ -2387,8 +2389,8 @@ static void manage_tempaddrs(struct inet6_dev *idev,
 
 static bool is_addr_mode_generate_stable(struct inet6_dev *idev)
 {
-   return idev->addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY ||
-  idev->addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
+   return idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY ||
+  idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
 }
 
 int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
@@ -3152,7 +3154,7 @@ static void addrconf_addr_gen(struct inet6_dev *idev, 
bool prefix_route)
 
ipv6_addr_set(, htonl(0xFE80), 0, 0, 0);
 
-   switch (idev->addr_gen_mode) {
+   switch (idev->cnf.addr_gen_mode) {
case IN6_ADDR_GEN_MODE_RANDOM:
ipv6_gen_mode_random_init(idev);
/* fallthrough */
@@ -3204,8 +3206,8 @@ static void addrconf_dev_config(struct net_device *dev)
 
/* this device type has no EUI support */
if (dev->type == ARPHRD_NONE &&
-   idev->addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64)
-   idev->addr_gen_mode = IN6_ADDR_GEN_MODE_RANDOM;
+   idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64)
+   idev->cnf.addr_gen_mode = IN6_ADDR_GEN_MODE_RANDOM;
 
addrconf_addr_gen(idev, false);
 }
@@ -4982,6 +4984,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf 
*cnf,
array[DEVCONF_SEG6_REQUIRE_HMAC] 

[PATCH v4 net-next 2/2] net/ipv6: support more tunnel interfaces for EUI64 link-local generation

2017-01-25 Thread Felix Jia
Signed-off-by: Felix Jia <felix@alliedtelesis.co.nz>
---
 net/ipv6/addrconf.c | 5 +
 net/ipv6/ip6_gre.c  | 3 +++
 net/ipv6/ip6_vti.c  | 4 
 3 files changed, 12 insertions(+)

diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index e35259dd17ba..4c47656b9f09 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -2146,12 +2146,14 @@ static int ipv6_generate_eui64(u8 *eui, struct 
net_device *dev)
case ARPHRD_SIT:
return addrconf_ifid_sit(eui, dev);
case ARPHRD_IPGRE:
+   case ARPHRD_TUNNEL:
return addrconf_ifid_gre(eui, dev);
case ARPHRD_6LOWPAN:
return addrconf_ifid_eui64(eui, dev);
case ARPHRD_IEEE1394:
return addrconf_ifid_ieee1394(eui, dev);
case ARPHRD_TUNNEL6:
+   case ARPHRD_IP6GRE:
return addrconf_ifid_ip6tnl(eui, dev);
}
return -1;
@@ -3195,6 +3197,9 @@ static void addrconf_dev_config(struct net_device *dev)
(dev->type != ARPHRD_IEEE1394) &&
(dev->type != ARPHRD_TUNNEL6) &&
(dev->type != ARPHRD_6LOWPAN) &&
+   (dev->type != ARPHRD_IP6GRE) &&
+   (dev->type != ARPHRD_IPGRE) &&
+   (dev->type != ARPHRD_TUNNEL) &&
(dev->type != ARPHRD_NONE)) {
/* Alas, we support only Ethernet autoconfiguration. */
return;
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index 65bdfd1cca80..1ba7567b4d8f 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -993,6 +993,9 @@ static void ip6gre_tunnel_setup(struct net_device *dev)
dev->flags |= IFF_NOARP;
dev->addr_len = sizeof(struct in6_addr);
netif_keep_dst(dev);
+   /* This perm addr will be used as interface identifier by IPv6 */
+   dev->addr_assign_type = NET_ADDR_RANDOM;
+   eth_random_addr(dev->perm_addr);
 }
 
 static int ip6gre_tunnel_init_common(struct net_device *dev)
diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c
index d82042c8d8fd..c795fee372c4 100644
--- a/net/ipv6/ip6_vti.c
+++ b/net/ipv6/ip6_vti.c
@@ -49,6 +49,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #define IP6_VTI_HASH_SIZE_SHIFT  5
 #define IP6_VTI_HASH_SIZE (1 << IP6_VTI_HASH_SIZE_SHIFT)
@@ -842,6 +843,9 @@ static void vti6_dev_setup(struct net_device *dev)
dev->flags |= IFF_NOARP;
dev->addr_len = sizeof(struct in6_addr);
netif_keep_dst(dev);
+   /* This perm addr will be used as interface identifier by IPv6 */
+   dev->addr_assign_type = NET_ADDR_RANDOM;
+   eth_random_addr(dev->perm_addr);
 }
 
 /**
-- 
2.11.0



[PATCH v3 net-next 2/2] net/ipv6: support more tunnel interfaces for EUI64 link-local generation

2017-01-19 Thread Felix Jia
Signed-off-by: Felix Jia <felix@alliedtelesis.co.nz>
---
 net/ipv6/addrconf.c | 5 +
 net/ipv6/ip6_gre.c  | 3 +++
 net/ipv6/ip6_vti.c  | 4 
 3 files changed, 12 insertions(+)

diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index e35259dd17ba..4c47656b9f09 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -2146,12 +2146,14 @@ static int ipv6_generate_eui64(u8 *eui, struct 
net_device *dev)
case ARPHRD_SIT:
return addrconf_ifid_sit(eui, dev);
case ARPHRD_IPGRE:
+   case ARPHRD_TUNNEL:
return addrconf_ifid_gre(eui, dev);
case ARPHRD_6LOWPAN:
return addrconf_ifid_eui64(eui, dev);
case ARPHRD_IEEE1394:
return addrconf_ifid_ieee1394(eui, dev);
case ARPHRD_TUNNEL6:
+   case ARPHRD_IP6GRE:
return addrconf_ifid_ip6tnl(eui, dev);
}
return -1;
@@ -3195,6 +3197,9 @@ static void addrconf_dev_config(struct net_device *dev)
(dev->type != ARPHRD_IEEE1394) &&
(dev->type != ARPHRD_TUNNEL6) &&
(dev->type != ARPHRD_6LOWPAN) &&
+   (dev->type != ARPHRD_IP6GRE) &&
+   (dev->type != ARPHRD_IPGRE) &&
+   (dev->type != ARPHRD_TUNNEL) &&
(dev->type != ARPHRD_NONE)) {
/* Alas, we support only Ethernet autoconfiguration. */
return;
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index 75b6108234dd..7f22e1510082 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -998,6 +998,9 @@ static void ip6gre_tunnel_setup(struct net_device *dev)
dev->flags |= IFF_NOARP;
dev->addr_len = sizeof(struct in6_addr);
netif_keep_dst(dev);
+   /* This perm addr will be used as interface identifier by IPv6 */
+   dev->addr_assign_type = NET_ADDR_RANDOM;
+   eth_random_addr(dev->perm_addr);
 }
 
 static int ip6gre_tunnel_init_common(struct net_device *dev)
diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c
index d82042c8d8fd..c795fee372c4 100644
--- a/net/ipv6/ip6_vti.c
+++ b/net/ipv6/ip6_vti.c
@@ -49,6 +49,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #define IP6_VTI_HASH_SIZE_SHIFT  5
 #define IP6_VTI_HASH_SIZE (1 << IP6_VTI_HASH_SIZE_SHIFT)
@@ -842,6 +843,9 @@ static void vti6_dev_setup(struct net_device *dev)
dev->flags |= IFF_NOARP;
dev->addr_len = sizeof(struct in6_addr);
netif_keep_dst(dev);
+   /* This perm addr will be used as interface identifier by IPv6 */
+   dev->addr_assign_type = NET_ADDR_RANDOM;
+   eth_random_addr(dev->perm_addr);
 }
 
 /**
-- 
2.11.0



[PATCH v3 net-next 1/2] net/ipv6: allow sysctl to change link-local address generation mode

2017-01-19 Thread Felix Jia
The address generation mode for IPv6 link-local can only be configured
by netlink messages. This patch adds the ability to change the address
generation mode via sysctl.

v1 -> v2
Removed the rtnl lock and switch to use RCU lock to iterate through
the netdev list.

v2 -> v3
Removed the addrgenmode variable from the idev structure and use the 
systcl storage for the flag.

Simplifed the logic for sysctl handling by removing the supported 
for all operation.

Added support for more types of tunnel interfaces for link-local 
address generation.

Based the patches from net-next.


Signed-off-by: Felix Jia <felix@alliedtelesis.co.nz>
---
 include/linux/ipv6.h  |   3 +-
 include/net/if_inet6.h|   5 +--
 include/uapi/linux/ipv6.h |   5 ++-
 net/ipv6/addrconf.c   | 104 +-
 4 files changed, 91 insertions(+), 26 deletions(-)

diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 671d014e6429..74d0e0913211 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -69,6 +69,7 @@ struct ipv6_devconf {
__s32   seg6_require_hmac;
 #endif
__u32   enhanced_dad;
+   __u32   addr_gen_mode;
 
struct ctl_table_header *sysctl_header;
 };
@@ -99,7 +100,7 @@ static inline struct ipv6hdr *ipipv6_hdr(const struct 
sk_buff *skb)
return (struct ipv6hdr *)skb_transport_header(skb);
 }
 
-/* 
+/*
This structure contains results of exthdrs parsing
as offsets from skb->nh.
  */
diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h
index 0fa4c324b713..48e2a34a6ed3 100644
--- a/include/net/if_inet6.h
+++ b/include/net/if_inet6.h
@@ -1,9 +1,9 @@
 /*
  * inet6 interface/address list definitions
- * Linux INET6 implementation 
+ * Linux INET6 implementation
  *
  * Authors:
- * Pedro Roque <ro...@di.fc.ul.pt> 
+ * Pedro Roque <ro...@di.fc.ul.pt>
  *
  *
  * This program is free software; you can redistribute it and/or
@@ -205,7 +205,6 @@ struct inet6_dev {
__s32   rs_interval;/* in jiffies */
__u8rs_probes;
 
-   __u8addr_gen_mode;
unsigned long   tstamp; /* ipv6InterfaceTable update timestamp 
*/
struct rcu_head rcu;
 };
diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h
index eaf65dc82e22..7207b9275c28 100644
--- a/include/uapi/linux/ipv6.h
+++ b/include/uapi/linux/ipv6.h
@@ -33,7 +33,7 @@ struct ip6_mtuinfo {
 struct in6_ifreq {
struct in6_addr ifr6_addr;
__u32   ifr6_prefixlen;
-   int ifr6_ifindex; 
+   int ifr6_ifindex;
 };
 
 #define IPV6_SRCRT_STRICT  0x01/* Deprecated; will be removed */
@@ -60,7 +60,7 @@ struct ipv6_rt_hdr {
 struct ipv6_opt_hdr {
__u8nexthdr;
__u8hdrlen;
-   /* 
+   /*
 * TLV encoded option data follows.
 */
 } __attribute__((packed)); /* required for some archs */
@@ -182,6 +182,7 @@ enum {
DEVCONF_SEG6_ENABLED,
DEVCONF_SEG6_REQUIRE_HMAC,
DEVCONF_ENHANCED_DAD,
+   DEVCONF_ADDR_GEN_MODE,
DEVCONF_MAX
 };
 
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index ac9bd5620f81..e35259dd17ba 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -243,6 +243,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
.seg6_require_hmac  = 0,
 #endif
.enhanced_dad   = 1,
+   .addr_gen_mode  = IN6_ADDR_GEN_MODE_EUI64,
 };
 
 static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
@@ -294,6 +295,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly 
= {
.seg6_require_hmac  = 0,
 #endif
.enhanced_dad   = 1,
+   .addr_gen_mode  = IN6_ADDR_GEN_MODE_EUI64,
 };
 
 /* Check if a valid qdisc is available */
@@ -386,9 +388,9 @@ static struct inet6_dev *ipv6_add_dev(struct net_device 
*dev)
memcpy(>cnf, dev_net(dev)->ipv6.devconf_dflt, sizeof(ndev->cnf));
 
if (ndev->cnf.stable_secret.initialized)
-   ndev->addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
+   ndev->cnf.addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
else
-   ndev->addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64;
+   ndev->cnf.addr_gen_mode = ipv6_devconf_dflt.addr_gen_mode;
 
ndev->cnf.mtu6 = dev->mtu;
ndev->nd_parms = neigh_parms_alloc(dev, _tbl);
@@ -2387,8 +2389,8 @@ static void manage_tempaddrs(struct inet6_dev *idev,
 
 static bool is_addr_mode_generate_stable(struct inet6_dev *idev)
 {
-   return idev->addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY ||
-  idev->addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
+   return idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY ||
+   

[PATCH v2 main-v4.9-rc7] net/ipv6: allow sysctl to change link-local address generation mode

2016-12-04 Thread Felix Jia
Removed the rtnl lock and switch to use RCU lock to iterate through
the netdev list.

The address generation mode for IPv6 link-local can only be configured
by netlink messages. This patch adds the ability to change the address
generation mode via sysctl.

An possible improvement is to remove the addrgenmode variable from the
idev structure and use the systcl storage for the flag.

The patch is based from v4.9-rc7 in mainline.

Signed-off-by: Felix Jia <felix@alliedtelesis.co.nz>
Cc: Carl Smith <carl.sm...@alliedtelesis.co.nz>
---
 include/linux/ipv6.h  |  1 +
 include/uapi/linux/ipv6.h |  1 +
 net/ipv6/addrconf.c   | 73 ++-
 3 files changed, 74 insertions(+), 1 deletion(-)

diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index a064997..0d9e5d4 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -64,6 +64,7 @@ struct ipv6_devconf {
} stable_secret;
__s32   use_oif_addrs_only;
__s32   keep_addr_on_down;
+   __s32   addrgenmode;
 
struct ctl_table_header *sysctl_header;
 };
diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h
index 8c27723..0524e2c 100644
--- a/include/uapi/linux/ipv6.h
+++ b/include/uapi/linux/ipv6.h
@@ -178,6 +178,7 @@ enum {
DEVCONF_DROP_UNSOLICITED_NA,
DEVCONF_KEEP_ADDR_ON_DOWN,
DEVCONF_RTR_SOLICIT_MAX_INTERVAL,
+   DEVCONF_ADDRGENMODE,
DEVCONF_MAX
 };
 
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 4bc5ba3..2b83cc7 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -238,6 +238,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
.use_oif_addrs_only = 0,
.ignore_routes_with_linkdown = 0,
.keep_addr_on_down  = 0,
+   .addrgenmode = IN6_ADDR_GEN_MODE_EUI64,
 };
 
 static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
@@ -284,6 +285,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly 
= {
.use_oif_addrs_only = 0,
.ignore_routes_with_linkdown = 0,
.keep_addr_on_down  = 0,
+   .addrgenmode = IN6_ADDR_GEN_MODE_EUI64,
 };
 
 /* Check if a valid qdisc is available */
@@ -378,7 +380,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device 
*dev)
if (ndev->cnf.stable_secret.initialized)
ndev->addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
else
-   ndev->addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64;
+   ndev->addr_gen_mode = ipv6_devconf_dflt.addrgenmode;
 
ndev->cnf.mtu6 = dev->mtu;
ndev->nd_parms = neigh_parms_alloc(dev, _tbl);
@@ -4950,6 +4952,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf 
*cnf,
array[DEVCONF_DROP_UNICAST_IN_L2_MULTICAST] = 
cnf->drop_unicast_in_l2_multicast;
array[DEVCONF_DROP_UNSOLICITED_NA] = cnf->drop_unsolicited_na;
array[DEVCONF_KEEP_ADDR_ON_DOWN] = cnf->keep_addr_on_down;
+   array[DEVCONF_ADDRGENMODE] = cnf->addrgenmode;
 }
 
 static inline size_t inet6_ifla6_size(void)
@@ -5496,6 +5499,67 @@ int addrconf_sysctl_mtu(struct ctl_table *ctl, int write,
return proc_dointvec_minmax(, write, buffer, lenp, ppos);
 }
 
+static void addrconf_addrgenmode_change(struct net *net)
+{
+   struct net_device *dev;
+   struct inet6_dev *idev;
+
+   rcu_read_lock();
+   for_each_netdev_rcu(net, dev) {
+   idev = __in6_dev_get(dev);
+   if (idev) {
+   idev->cnf.addrgenmode = ipv6_devconf_dflt.addrgenmode;
+   idev->addr_gen_mode = ipv6_devconf_dflt.addrgenmode;
+   addrconf_dev_config(idev->dev);
+   }
+   }
+   rcu_read_unlock();
+}
+
+static int addrconf_sysctl_addrgenmode(struct ctl_table *ctl, int write,
+   void __user 
*buffer, size_t *lenp, loff_t *ppos)
+{
+   int ret;
+   int new_val;
+   struct inet6_dev *idev = (struct inet6_dev *)ctl->extra1;
+   struct net *net = (struct net *)ctl->extra2;
+
+   if (write) { /* sysctl write request */
+   ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
+   new_val = *((int *)ctl->data);
+
+   /* request for the all */
+   if (>ipv6.devconf_all->addrgenmode == ctl->data) {
+   ipv6_devconf_dflt.addrgenmode = new_val;
+   addrconf_addrgenmode_change(net);
+
+   /* request for default */
+   } else if (>ipv6.devconf_dflt->addrgenmode == ctl->data) {
+   ipv6_devconf_dflt.addrgenmode = new_val;
+
+   /* request for individual inet device */
+   } else {
+   if (!idev) {
+   return ret;
+   }
+

[PATCH main-v4.9-rc7] net/ipv6: allow sysctl to change link-local address generation mode

2016-12-01 Thread Felix Jia
The address generation mode for IPv6 link-local can only be configured
by netlink messages. This patch adds the ability to change the address
generation mode via sysctl.

An possible improvement is to remove the addrgenmode variable from the
idev structure and use the systcl storage for the flag.

The patch is based from v4.9-rc7 in mainline.

Signed-off-by: Felix Jia <felix@alliedtelesis.co.nz>
Cc: Carl Smith <carl.sm...@alliedtelesis.co.nz>
---
 include/linux/ipv6.h  |  1 +
 include/uapi/linux/ipv6.h |  1 +
 net/ipv6/addrconf.c   | 77 ++-
 3 files changed, 78 insertions(+), 1 deletion(-)

diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index a064997..0d9e5d4 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -64,6 +64,7 @@ struct ipv6_devconf {
} stable_secret;
__s32   use_oif_addrs_only;
__s32   keep_addr_on_down;
+   __s32   addrgenmode;
 
struct ctl_table_header *sysctl_header;
 };
diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h
index 8c27723..0524e2c 100644
--- a/include/uapi/linux/ipv6.h
+++ b/include/uapi/linux/ipv6.h
@@ -178,6 +178,7 @@ enum {
DEVCONF_DROP_UNSOLICITED_NA,
DEVCONF_KEEP_ADDR_ON_DOWN,
DEVCONF_RTR_SOLICIT_MAX_INTERVAL,
+   DEVCONF_ADDRGENMODE,
DEVCONF_MAX
 };
 
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 4bc5ba3..8d1f6e6 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -238,6 +238,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
.use_oif_addrs_only = 0,
.ignore_routes_with_linkdown = 0,
.keep_addr_on_down  = 0,
+   .addrgenmode = IN6_ADDR_GEN_MODE_EUI64,
 };
 
 static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
@@ -284,6 +285,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly 
= {
.use_oif_addrs_only = 0,
.ignore_routes_with_linkdown = 0,
.keep_addr_on_down  = 0,
+   .addrgenmode = IN6_ADDR_GEN_MODE_EUI64,
 };
 
 /* Check if a valid qdisc is available */
@@ -378,7 +380,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device 
*dev)
if (ndev->cnf.stable_secret.initialized)
ndev->addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
else
-   ndev->addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64;
+   ndev->addr_gen_mode = ipv6_devconf_dflt.addrgenmode;
 
ndev->cnf.mtu6 = dev->mtu;
ndev->nd_parms = neigh_parms_alloc(dev, _tbl);
@@ -4950,6 +4952,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf 
*cnf,
array[DEVCONF_DROP_UNICAST_IN_L2_MULTICAST] = 
cnf->drop_unicast_in_l2_multicast;
array[DEVCONF_DROP_UNSOLICITED_NA] = cnf->drop_unsolicited_na;
array[DEVCONF_KEEP_ADDR_ON_DOWN] = cnf->keep_addr_on_down;
+   array[DEVCONF_ADDRGENMODE] = cnf->addrgenmode;
 }
 
 static inline size_t inet6_ifla6_size(void)
@@ -5496,6 +5499,71 @@ int addrconf_sysctl_mtu(struct ctl_table *ctl, int write,
return proc_dointvec_minmax(, write, buffer, lenp, ppos);
 }
 
+static void addrconf_addrgenmode_change(struct net *net)
+{
+   struct net_device *dev;
+   struct inet6_dev *idev;
+
+   read_lock(_base_lock);
+   for_each_netdev(net, dev) {
+   rcu_read_lock();
+   idev = __in6_dev_get(dev);
+   if (idev) {
+   idev->cnf.addrgenmode = ipv6_devconf_dflt.addrgenmode;
+   idev->addr_gen_mode = ipv6_devconf_dflt.addrgenmode;
+   rtnl_lock();
+   addrconf_dev_config(idev->dev);
+   rtnl_unlock();
+   }
+   rcu_read_unlock();
+   }
+   read_unlock(_base_lock);
+}
+
+static int addrconf_sysctl_addrgenmode(struct ctl_table *ctl, int write,
+   void __user 
*buffer, size_t *lenp, loff_t *ppos)
+{
+   int ret;
+   int new_val;
+   struct inet6_dev *idev = (struct inet6_dev *)ctl->extra1;
+   struct net *net = (struct net *)ctl->extra2;
+
+   if (write) { /* sysctl write request */
+   ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
+   new_val = *((int *)ctl->data);
+
+   /* request for the all */
+   if (>ipv6.devconf_all->addrgenmode == ctl->data) {
+   ipv6_devconf_dflt.addrgenmode = new_val;
+   addrconf_addrgenmode_change(net);
+
+   /* request for default */
+   } else if (>ipv6.devconf_dflt->addrgenmode == ctl->data) {
+   ipv6_devconf_dflt.addrgenmode = new_val;
+
+   /* request for individual inet device */
+   } else {
+   if (!idev) {
+