summaryrefslogtreecommitdiff
path: root/net/ipv6
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/addrconf.c75
-rw-r--r--net/ipv6/ip6_tunnel.c26
-rw-r--r--net/ipv6/ip6mr.c22
-rw-r--r--net/ipv6/ndisc.c8
-rw-r--r--net/ipv6/netfilter/Kconfig12
-rw-r--r--net/ipv6/netfilter/Makefile1
-rw-r--r--net/ipv6/netfilter/ip6_queue.c3
-rw-r--r--net/ipv6/netfilter/ip6table_security.c172
-rw-r--r--net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c5
-rw-r--r--net/ipv6/reassembly.c61
-rw-r--r--net/ipv6/route.c12
-rw-r--r--net/ipv6/sit.c113
-rw-r--r--net/ipv6/sysctl_net_ipv6.c29
13 files changed, 344 insertions, 195 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index e591e09e5e4..3a835578fd1 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1764,14 +1764,16 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len)
* 2) Configure prefixes with the auto flag set
*/
- /* Avoid arithmetic overflow. Really, we could
- save rt_expires in seconds, likely valid_lft,
- but it would require division in fib gc, that it
- not good.
- */
- if (valid_lft >= 0x7FFFFFFF/HZ)
+ if (valid_lft == INFINITY_LIFE_TIME)
+ rt_expires = ~0UL;
+ else if (valid_lft >= 0x7FFFFFFF/HZ) {
+ /* Avoid arithmetic overflow. Really, we could
+ * save rt_expires in seconds, likely valid_lft,
+ * but it would require division in fib gc, that it
+ * not good.
+ */
rt_expires = 0x7FFFFFFF - (0x7FFFFFFF % HZ);
- else
+ } else
rt_expires = valid_lft * HZ;
/*
@@ -1779,7 +1781,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len)
* Avoid arithmetic overflow there as well.
* Overflow can happen only if HZ < USER_HZ.
*/
- if (HZ < USER_HZ && rt_expires > 0x7FFFFFFF / USER_HZ)
+ if (HZ < USER_HZ && ~rt_expires && rt_expires > 0x7FFFFFFF / USER_HZ)
rt_expires = 0x7FFFFFFF / USER_HZ;
if (pinfo->onlink) {
@@ -1788,17 +1790,28 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len)
dev->ifindex, 1);
if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) {
- if (rt->rt6i_flags&RTF_EXPIRES) {
- if (valid_lft == 0) {
- ip6_del_rt(rt);
- rt = NULL;
- } else {
- rt->rt6i_expires = jiffies + rt_expires;
- }
+ /* Autoconf prefix route */
+ if (valid_lft == 0) {
+ ip6_del_rt(rt);
+ rt = NULL;
+ } else if (~rt_expires) {
+ /* not infinity */
+ rt->rt6i_expires = jiffies + rt_expires;
+ rt->rt6i_flags |= RTF_EXPIRES;
+ } else {
+ rt->rt6i_flags &= ~RTF_EXPIRES;
+ rt->rt6i_expires = 0;
}
} else if (valid_lft) {
+ int flags = RTF_ADDRCONF | RTF_PREFIX_RT;
+ clock_t expires = 0;
+ if (~rt_expires) {
+ /* not infinity */
+ flags |= RTF_EXPIRES;
+ expires = jiffies_to_clock_t(rt_expires);
+ }
addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len,
- dev, jiffies_to_clock_t(rt_expires), RTF_ADDRCONF|RTF_EXPIRES|RTF_PREFIX_RT);
+ dev, expires, flags);
}
if (rt)
dst_release(&rt->u.dst);
@@ -2021,7 +2034,8 @@ static int inet6_addr_add(struct net *net, int ifindex, struct in6_addr *pfx,
struct inet6_dev *idev;
struct net_device *dev;
int scope;
- u32 flags = RTF_EXPIRES;
+ u32 flags;
+ clock_t expires;
ASSERT_RTNL();
@@ -2041,8 +2055,13 @@ static int inet6_addr_add(struct net *net, int ifindex, struct in6_addr *pfx,
if (valid_lft == INFINITY_LIFE_TIME) {
ifa_flags |= IFA_F_PERMANENT;
flags = 0;
- } else if (valid_lft >= 0x7FFFFFFF/HZ)
- valid_lft = 0x7FFFFFFF/HZ;
+ expires = 0;
+ } else {
+ if (valid_lft >= 0x7FFFFFFF/HZ)
+ valid_lft = 0x7FFFFFFF/HZ;
+ flags = RTF_EXPIRES;
+ expires = jiffies_to_clock_t(valid_lft * HZ);
+ }
if (prefered_lft == 0)
ifa_flags |= IFA_F_DEPRECATED;
@@ -2060,7 +2079,7 @@ static int inet6_addr_add(struct net *net, int ifindex, struct in6_addr *pfx,
spin_unlock_bh(&ifp->lock);
addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev,
- jiffies_to_clock_t(valid_lft * HZ), flags);
+ expires, flags);
/*
* Note that section 3.1 of RFC 4429 indicates
* that the Optimistic flag should not be set for
@@ -3148,7 +3167,8 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
static int inet6_addr_modify(struct inet6_ifaddr *ifp, u8 ifa_flags,
u32 prefered_lft, u32 valid_lft)
{
- u32 flags = RTF_EXPIRES;
+ u32 flags;
+ clock_t expires;
if (!valid_lft || (prefered_lft > valid_lft))
return -EINVAL;
@@ -3156,8 +3176,13 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u8 ifa_flags,
if (valid_lft == INFINITY_LIFE_TIME) {
ifa_flags |= IFA_F_PERMANENT;
flags = 0;
- } else if (valid_lft >= 0x7FFFFFFF/HZ)
- valid_lft = 0x7FFFFFFF/HZ;
+ expires = 0;
+ } else {
+ if (valid_lft >= 0x7FFFFFFF/HZ)
+ valid_lft = 0x7FFFFFFF/HZ;
+ flags = RTF_EXPIRES;
+ expires = jiffies_to_clock_t(valid_lft * HZ);
+ }
if (prefered_lft == 0)
ifa_flags |= IFA_F_DEPRECATED;
@@ -3176,7 +3201,7 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u8 ifa_flags,
ipv6_ifa_notify(0, ifp);
addrconf_prefix_route(&ifp->addr, ifp->prefix_len, ifp->idev->dev,
- jiffies_to_clock_t(valid_lft * HZ), flags);
+ expires, flags);
addrconf_verify(0);
return 0;
@@ -4242,7 +4267,7 @@ static void addrconf_sysctl_register(struct inet6_dev *idev)
neigh_sysctl_register(idev->dev, idev->nd_parms, NET_IPV6,
NET_IPV6_NEIGH, "ipv6",
&ndisc_ifinfo_sysctl_change,
- NULL);
+ ndisc_ifinfo_sysctl_strategy);
__addrconf_sysctl_register(dev_net(idev->dev), idev->dev->name,
idev->dev->ifindex, idev, &idev->cnf);
}
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 2bda3ba100b..37814810ac4 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -711,7 +711,7 @@ static int ip6_tnl_rcv(struct sk_buff *skb, __u16 protocol,
}
if (!ip6_tnl_rcv_ctl(t)) {
- t->stat.rx_dropped++;
+ t->dev->stats.rx_dropped++;
read_unlock(&ip6_tnl_lock);
goto discard;
}
@@ -728,8 +728,8 @@ static int ip6_tnl_rcv(struct sk_buff *skb, __u16 protocol,
dscp_ecn_decapsulate(t, ipv6h, skb);
- t->stat.rx_packets++;
- t->stat.rx_bytes += skb->len;
+ t->dev->stats.rx_packets++;
+ t->dev->stats.rx_bytes += skb->len;
netif_rx(skb);
read_unlock(&ip6_tnl_lock);
return 0;
@@ -849,7 +849,7 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,
__u32 *pmtu)
{
struct ip6_tnl *t = netdev_priv(dev);
- struct net_device_stats *stats = &t->stat;
+ struct net_device_stats *stats = &t->dev->stats;
struct ipv6hdr *ipv6h = ipv6_hdr(skb);
struct ipv6_tel_txoption opt;
struct dst_entry *dst;
@@ -1043,11 +1043,11 @@ static int
ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ip6_tnl *t = netdev_priv(dev);
- struct net_device_stats *stats = &t->stat;
+ struct net_device_stats *stats = &t->dev->stats;
int ret;
if (t->recursion++) {
- t->stat.collisions++;
+ stats->collisions++;
goto tx_err;
}
@@ -1289,19 +1289,6 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
}
/**
- * ip6_tnl_get_stats - return the stats for tunnel device
- * @dev: virtual device associated with tunnel
- *
- * Return: stats for device
- **/
-
-static struct net_device_stats *
-ip6_tnl_get_stats(struct net_device *dev)
-{
- return &(((struct ip6_tnl *)netdev_priv(dev))->stat);
-}
-
-/**
* ip6_tnl_change_mtu - change mtu manually for tunnel device
* @dev: virtual device associated with tunnel
* @new_mtu: the new mtu
@@ -1334,7 +1321,6 @@ static void ip6_tnl_dev_setup(struct net_device *dev)
dev->uninit = ip6_tnl_dev_uninit;
dev->destructor = free_netdev;
dev->hard_start_xmit = ip6_tnl_xmit;
- dev->get_stats = ip6_tnl_get_stats;
dev->do_ioctl = ip6_tnl_ioctl;
dev->change_mtu = ip6_tnl_change_mtu;
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index 2de3c464fe7..bf268b38696 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -388,8 +388,8 @@ static int pim6_rcv(struct sk_buff *skb)
skb->ip_summed = 0;
skb->pkt_type = PACKET_HOST;
dst_release(skb->dst);
- ((struct net_device_stats *)netdev_priv(reg_dev))->rx_bytes += skb->len;
- ((struct net_device_stats *)netdev_priv(reg_dev))->rx_packets++;
+ reg_dev->stats.rx_bytes += skb->len;
+ reg_dev->stats.rx_packets++;
skb->dst = NULL;
nf_reset(skb);
netif_rx(skb);
@@ -409,26 +409,20 @@ static struct inet6_protocol pim6_protocol = {
static int reg_vif_xmit(struct sk_buff *skb, struct net_device *dev)
{
read_lock(&mrt_lock);
- ((struct net_device_stats *)netdev_priv(dev))->tx_bytes += skb->len;
- ((struct net_device_stats *)netdev_priv(dev))->tx_packets++;
+ dev->stats.tx_bytes += skb->len;
+ dev->stats.tx_packets++;
ip6mr_cache_report(skb, reg_vif_num, MRT6MSG_WHOLEPKT);
read_unlock(&mrt_lock);
kfree_skb(skb);
return 0;
}
-static struct net_device_stats *reg_vif_get_stats(struct net_device *dev)
-{
- return (struct net_device_stats *)netdev_priv(dev);
-}
-
static void reg_vif_setup(struct net_device *dev)
{
dev->type = ARPHRD_PIMREG;
dev->mtu = 1500 - sizeof(struct ipv6hdr) - 8;
dev->flags = IFF_NOARP;
dev->hard_start_xmit = reg_vif_xmit;
- dev->get_stats = reg_vif_get_stats;
dev->destructor = free_netdev;
}
@@ -436,9 +430,7 @@ static struct net_device *ip6mr_reg_vif(void)
{
struct net_device *dev;
- dev = alloc_netdev(sizeof(struct net_device_stats), "pim6reg",
- reg_vif_setup);
-
+ dev = alloc_netdev(0, "pim6reg", reg_vif_setup);
if (dev == NULL)
return NULL;
@@ -1377,8 +1369,8 @@ static int ip6mr_forward2(struct sk_buff *skb, struct mfc6_cache *c, int vifi)
if (vif->flags & MIFF_REGISTER) {
vif->pkt_out++;
vif->bytes_out += skb->len;
- ((struct net_device_stats *)netdev_priv(vif->dev))->tx_bytes += skb->len;
- ((struct net_device_stats *)netdev_priv(vif->dev))->tx_packets++;
+ vif->dev->stats.tx_bytes += skb->len;
+ vif->dev->stats.tx_packets++;
ip6mr_cache_report(skb, vifi, MRT6MSG_WHOLEPKT);
kfree_skb(skb);
return 0;
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index a55fc05b812..282fdb31f8e 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -1727,10 +1727,10 @@ int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, struct file * f
return ret;
}
-static int ndisc_ifinfo_sysctl_strategy(ctl_table *ctl, int __user *name,
- int nlen, void __user *oldval,
- size_t __user *oldlenp,
- void __user *newval, size_t newlen)
+int ndisc_ifinfo_sysctl_strategy(ctl_table *ctl, int __user *name,
+ int nlen, void __user *oldval,
+ size_t __user *oldlenp,
+ void __user *newval, size_t newlen)
{
struct net_device *dev = ctl->extra1;
struct inet6_dev *idev;
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig
index 6cae5475737..689dec899c5 100644
--- a/net/ipv6/netfilter/Kconfig
+++ b/net/ipv6/netfilter/Kconfig
@@ -208,5 +208,17 @@ config IP6_NF_RAW
If you want to compile it as a module, say M here and read
<file:Documentation/kbuild/modules.txt>. If unsure, say `N'.
+# security table for MAC policy
+config IP6_NF_SECURITY
+ tristate "Security table"
+ depends on IP6_NF_IPTABLES
+ depends on SECURITY
+ default m if NETFILTER_ADVANCED=n
+ help
+ This option adds a `security' table to iptables, for use
+ with Mandatory Access Control (MAC) policy.
+
+ If unsure, say N.
+
endmenu
diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile
index fbf2c14ed88..3f17c948eef 100644
--- a/net/ipv6/netfilter/Makefile
+++ b/net/ipv6/netfilter/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_IP6_NF_FILTER) += ip6table_filter.o
obj-$(CONFIG_IP6_NF_MANGLE) += ip6table_mangle.o
obj-$(CONFIG_IP6_NF_QUEUE) += ip6_queue.o
obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o
+obj-$(CONFIG_IP6_NF_SECURITY) += ip6table_security.o
# objects for l3 independent conntrack
nf_conntrack_ipv6-objs := nf_conntrack_l3proto_ipv6.o nf_conntrack_proto_icmpv6.o nf_conntrack_reasm.o
diff --git a/net/ipv6/netfilter/ip6_queue.c b/net/ipv6/netfilter/ip6_queue.c
index 2eff3ae8977..1b8815f6153 100644
--- a/net/ipv6/netfilter/ip6_queue.c
+++ b/net/ipv6/netfilter/ip6_queue.c
@@ -159,7 +159,6 @@ ipq_build_packet_message(struct nf_queue_entry *entry, int *errp)
case IPQ_COPY_META:
case IPQ_COPY_NONE:
size = NLMSG_SPACE(sizeof(*pmsg));
- data_len = 0;
break;
case IPQ_COPY_PACKET:
@@ -226,8 +225,6 @@ ipq_build_packet_message(struct nf_queue_entry *entry, int *errp)
return skb;
nlmsg_failure:
- if (skb)
- kfree_skb(skb);
*errp = -EINVAL;
printk(KERN_ERR "ip6_queue: error creating packet message\n");
return NULL;
diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c
new file mode 100644
index 00000000000..063a3d9c3c6
--- /dev/null
+++ b/net/ipv6/netfilter/ip6table_security.c
@@ -0,0 +1,172 @@
+/*
+ * "security" table for IPv6
+ *
+ * This is for use by Mandatory Access Control (MAC) security models,
+ * which need to be able to manage security policy in separate context
+ * to DAC.
+ *
+ * Based on iptable_mangle.c
+ *
+ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ * Copyright (C) 2000-2004 Netfilter Core Team <coreteam <at> netfilter.org>
+ * Copyright (C) 2008 Red Hat, Inc., James Morris <jmorris <at> redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("James Morris <jmorris <at> redhat.com>");
+MODULE_DESCRIPTION("ip6tables security table, for MAC rules");
+
+#define SECURITY_VALID_HOOKS (1 << NF_INET_LOCAL_IN) | \
+ (1 << NF_INET_FORWARD) | \
+ (1 << NF_INET_LOCAL_OUT)
+
+static struct
+{
+ struct ip6t_replace repl;
+ struct ip6t_standard entries[3];
+ struct ip6t_error term;
+} initial_table __initdata = {
+ .repl = {
+ .name = "security",
+ .valid_hooks = SECURITY_VALID_HOOKS,
+ .num_entries = 4,
+ .size = sizeof(struct ip6t_standard) * 3 + sizeof(struct ip6t_error),
+ .hook_entry = {
+ [NF_INET_LOCAL_IN] = 0,
+ [NF_INET_FORWARD] = sizeof(struct ip6t_standard),
+ [NF_INET_LOCAL_OUT] = sizeof(struct ip6t_standard) * 2,
+ },
+ .underflow = {
+ [NF_INET_LOCAL_IN] = 0,
+ [NF_INET_FORWARD] = sizeof(struct ip6t_standard),
+ [NF_INET_LOCAL_OUT] = sizeof(struct ip6t_standard) * 2,
+ },
+ },
+ .entries = {
+ IP6T_STANDARD_INIT(NF_ACCEPT), /* LOCAL_IN */
+ IP6T_STANDARD_INIT(NF_ACCEPT), /* FORWARD */
+ IP6T_STANDARD_INIT(NF_ACCEPT), /* LOCAL_OUT */
+ },
+ .term = IP6T_ERROR_INIT, /* ERROR */
+};
+
+static struct xt_table security_table = {
+ .name = "security",
+ .valid_hooks = SECURITY_VALID_HOOKS,
+ .lock = __RW_LOCK_UNLOCKED(security_table.lock),
+ .me = THIS_MODULE,
+ .af = AF_INET6,
+};
+
+static unsigned int
+ip6t_local_in_hook(unsigned int hook,
+ struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ return ip6t_do_table(skb, hook, in, out,
+ init_net.ipv6.ip6table_security);
+}
+
+static unsigned int
+ip6t_forward_hook(unsigned int hook,
+ struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ return ip6t_do_table(skb, hook, in, out,
+ init_net.ipv6.ip6table_security);
+}
+
+static unsigned int
+ip6t_local_out_hook(unsigned int hook,
+ struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ /* TBD: handle short packets via raw socket */
+ return ip6t_do_table(skb, hook, in, out,
+ init_net.ipv6.ip6table_security);
+}
+
+static struct nf_hook_ops ip6t_ops[] __read_mostly = {
+ {
+ .hook = ip6t_local_in_hook,
+ .owner = THIS_MODULE,
+ .pf = PF_INET6,
+ .hooknum = NF_INET_LOCAL_IN,
+ .priority = NF_IP6_PRI_SECURITY,
+ },
+ {
+ .hook = ip6t_forward_hook,
+ .owner = THIS_MODULE,
+ .pf = PF_INET6,
+ .hooknum = NF_INET_FORWARD,
+ .priority = NF_IP6_PRI_SECURITY,
+ },
+ {
+ .hook = ip6t_local_out_hook,
+ .owner = THIS_MODULE,
+ .pf = PF_INET6,
+ .hooknum = NF_INET_LOCAL_OUT,
+ .priority = NF_IP6_PRI_SECURITY,
+ },
+};
+
+static int __net_init ip6table_security_net_init(struct net *net)
+{
+ net->ipv6.ip6table_security =
+ ip6t_register_table(net, &security_table, &initial_table.repl);
+
+ if (IS_ERR(net->ipv6.ip6table_security))
+ return PTR_ERR(net->ipv6.ip6table_security);
+
+ return 0;
+}
+
+static void __net_exit ip6table_security_net_exit(struct net *net)
+{
+ ip6t_unregister_table(net->ipv6.ip6table_security);
+}
+
+static struct pernet_operations ip6table_security_net_ops = {
+ .init = ip6table_security_net_init,
+ .exit = ip6table_security_net_exit,
+};
+
+static int __init ip6table_security_init(void)
+{
+ int ret;
+
+ ret = register_pernet_subsys(&ip6table_security_net_ops);
+ if (ret < 0)
+ return ret;
+
+ ret = nf_register_hooks(ip6t_ops, ARRAY_SIZE(ip6t_ops));
+ if (ret < 0)
+ goto cleanup_table;
+
+ return ret;
+
+cleanup_table:
+ unregister_pernet_subsys(&ip6table_security_net_ops);
+ return ret;
+}
+
+static void __exit ip6table_security_fini(void)
+{
+ nf_unregister_hooks(ip6t_ops, ARRAY_SIZE(ip6t_ops));
+ unregister_pernet_subsys(&ip6table_security_net_ops);
+}
+
+module_init(ip6table_security_init);
+module_exit(ip6table_security_fini);
diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
index ee713b03e9e..14d47d83354 100644
--- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
+++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
@@ -89,9 +89,8 @@ static int icmpv6_packet(struct nf_conn *ct,
means this will only run once even if count hits zero twice
(theoretically possible with SMP) */
if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) {
- if (atomic_dec_and_test(&ct->proto.icmp.count)
- && del_timer(&ct->timeout))
- ct->timeout.function((unsigned long)ct);
+ if (atomic_dec_and_test(&ct->proto.icmp.count))
+ nf_ct_kill_acct(ct, ctinfo, skb);
} else {
atomic_inc(&ct->proto.icmp.count);
nf_conntrack_event_cache(IPCT_PROTOINFO_VOLATILE, skb);
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
index 798cabc7535..9391a6949b9 100644
--- a/net/ipv6/reassembly.c
+++ b/net/ipv6/reassembly.c
@@ -632,7 +632,7 @@ static struct inet6_protocol frag_protocol =
};
#ifdef CONFIG_SYSCTL
-static struct ctl_table ip6_frags_ctl_table[] = {
+static struct ctl_table ip6_frags_ns_ctl_table[] = {
{
.ctl_name = NET_IPV6_IP6FRAG_HIGH_THRESH,
.procname = "ip6frag_high_thresh",
@@ -658,6 +658,10 @@ static struct ctl_table ip6_frags_ctl_table[] = {
.proc_handler = &proc_dointvec_jiffies,
.strategy = &sysctl_jiffies,
},
+ { }
+};
+
+static struct ctl_table ip6_frags_ctl_table[] = {
{
.ctl_name = NET_IPV6_IP6FRAG_SECRET_INTERVAL,
.procname = "ip6frag_secret_interval",
@@ -670,21 +674,20 @@ static struct ctl_table ip6_frags_ctl_table[] = {
{ }
};
-static int ip6_frags_sysctl_register(struct net *net)
+static int ip6_frags_ns_sysctl_register(struct net *net)
{
struct ctl_table *table;
struct ctl_table_header *hdr;
- table = ip6_frags_ctl_table;
+ table = ip6_frags_ns_ctl_table;
if (net != &init_net) {
- table = kmemdup(table, sizeof(ip6_frags_ctl_table), GFP_KERNEL);
+ table = kmemdup(table, sizeof(ip6_frags_ns_ctl_table), GFP_KERNEL);
if (table == NULL)
goto err_alloc;
table[0].data = &net->ipv6.frags.high_thresh;
table[1].data = &net->ipv6.frags.low_thresh;
table[2].data = &net->ipv6.frags.timeout;
- table[3].mode &= ~0222;
}
hdr = register_net_sysctl_table(net, net_ipv6_ctl_path, table);
@@ -701,7 +704,7 @@ err_alloc:
return -ENOMEM;
}
-static void ip6_frags_sysctl_unregister(struct net *net)
+static void ip6_frags_ns_sysctl_unregister(struct net *net)
{
struct ctl_table *table;
@@ -709,13 +712,36 @@ static void ip6_frags_sysctl_unregister(struct net *net)
unregister_net_sysctl_table(net->ipv6.sysctl.frags_hdr);
kfree(table);
}
+
+static struct ctl_table_header *ip6_ctl_header;
+
+static int ip6_frags_sysctl_register(void)
+{
+ ip6_ctl_header = register_net_sysctl_rotable(net_ipv6_ctl_path,
+ ip6_frags_ctl_table);
+ return ip6_ctl_header == NULL ? -ENOMEM : 0;
+}
+
+static void ip6_frags_sysctl_unregister(void)
+{
+ unregister_net_sysctl_table(ip6_ctl_header);
+}
#else
-static inline int ip6_frags_sysctl_register(struct net *net)
+static inline int ip6_frags_ns_sysctl_register(struct net *net)
{
return 0;
}
-static inline void ip6_frags_sysctl_unregister(struct net *net)
+static inline void ip6_frags_ns_sysctl_unregister(struct net *net)
+{
+}
+
+static inline int ip6_frags_sysctl_register(void)
+{
+ return 0;
+}
+
+static inline void ip6_frags_sysctl_unregister(void)
{
}
#endif
@@ -728,12 +754,12 @@ static int ipv6_frags_init_net(struct net *net)
inet_frags_init_net(&net->ipv6.frags);
- return ip6_frags_sysctl_register(net);
+ return ip6_frags_ns_sysctl_register(net);
}
static void ipv6_frags_exit_net(struct net *net)
{
- ip6_frags_sysctl_unregister(net);
+ ip6_frags_ns_sysctl_unregister(net);
inet_frags_exit_net(&net->ipv6.frags, &ip6_frags);
}
@@ -750,7 +776,13 @@ int __init ipv6_frag_init(void)
if (ret)
goto out;
- register_pernet_subsys(&ip6_frags_ops);
+ ret = ip6_frags_sysctl_register();
+ if (ret)
+ goto err_sysctl;
+
+ ret = register_pernet_subsys(&ip6_frags_ops);
+ if (ret)
+ goto err_pernet;
ip6_frags.hashfn = ip6_hashfn;
ip6_frags.constructor = ip6_frag_init;
@@ -763,11 +795,18 @@ int __init ipv6_frag_init(void)
inet_frags_init(&ip6_frags);
out:
return ret;
+
+err_pernet:
+ ip6_frags_sysctl_unregister();
+err_sysctl:
+ inet6_del_protocol(&frag_protocol, IPPROTO_FRAGMENT);
+ goto out;
}
void ipv6_frag_exit(void)
{
inet_frags_fini(&ip6_frags);
+ ip6_frags_sysctl_unregister();
unregister_pernet_subsys(&ip6_frags_ops);
inet6_del_protocol(&frag_protocol, IPPROTO_FRAGMENT);
}
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 12bba088034..48534c6c073 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -109,7 +109,7 @@ static struct dst_ops ip6_dst_ops_template = {
.negative_advice = ip6_negative_advice,
.link_failure = ip6_link_failure,
.update_pmtu = ip6_rt_update_pmtu,
- .local_out = ip6_local_out,
+ .local_out = __ip6_local_out,
.entry_size = sizeof(struct rt6_info),
.entries = ATOMIC_INIT(0),
};
@@ -475,7 +475,7 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
lifetime = ntohl(rinfo->lifetime);
if (lifetime == 0xffffffff) {
/* infinity */
- } else if (lifetime > 0x7fffffff/HZ) {
+ } else if (lifetime > 0x7fffffff/HZ - 1) {
/* Avoid arithmetic overflow */
lifetime = 0x7fffffff/HZ - 1;
}
@@ -1106,7 +1106,9 @@ int ip6_route_add(struct fib6_config *cfg)
}
rt->u.dst.obsolete = -1;
- rt->rt6i_expires = jiffies + clock_t_to_jiffies(cfg->fc_expires);
+ rt->rt6i_expires = (cfg->fc_flags & RTF_EXPIRES) ?
+ jiffies + clock_t_to_jiffies(cfg->fc_expires) :
+ 0;
if (cfg->fc_protocol == RTPROT_UNSPEC)
cfg->fc_protocol = RTPROT_BOOT;
@@ -2200,7 +2202,9 @@ static int rt6_fill_node(struct sk_buff *skb, struct rt6_info *rt,
NLA_PUT_U32(skb, RTA_PRIORITY, rt->rt6i_metric);
- expires = rt->rt6i_expires ? rt->rt6i_expires - jiffies : 0;
+ expires = (rt->rt6i_flags & RTF_EXPIRES) ?
+ rt->rt6i_expires - jiffies : 0;
+
if (rtnl_put_cacheinfo(skb, &rt->u.dst, 0, 0, 0,
expires, rt->u.dst.error) < 0)
goto nla_put_failure;
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index 5a6fab95569..6b8f0583b63 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -403,9 +403,8 @@ static void ipip6_tunnel_uninit(struct net_device *dev)
static int ipip6_err(struct sk_buff *skb, u32 info)
{
-#ifndef I_WISH_WORLD_WERE_PERFECT
-/* It is not :-( All the routers (except for Linux) return only
+/* All the routers (except for Linux) return only
8 bytes of packet payload. It means, that precise relaying of
ICMP in the real Internet is absolutely infeasible.
*/
@@ -462,92 +461,6 @@ static int ipip6_err(struct sk_buff *skb, u32 info)
out:
read_unlock(&ipip6_lock);
return err;
-#else
- struct iphdr *iph = (struct iphdr*)dp;
- int hlen = iph->ihl<<2;
- struct ipv6hdr *iph6;
- const int type = icmp_hdr(skb)->type;
- const int code = icmp_hdr(skb)->code;
- int rel_type = 0;
- int rel_code = 0;
- int rel_info = 0;
- struct sk_buff *skb2;
- struct rt6_info *rt6i;
-
- if (len < hlen + sizeof(struct ipv6hdr))
- return;
- iph6 = (struct ipv6hdr*)(dp + hlen);
-
- switch (type) {
- default:
- return;
- case ICMP_PARAMETERPROB:
- if (icmp_hdr(skb)->un.gateway < hlen)
- return;
-
- /* So... This guy found something strange INSIDE encapsulated
- packet. Well, he is fool, but what can we do ?
- */
- rel_type = ICMPV6_PARAMPROB;
- rel_info = icmp_hdr(skb)->un.gateway - hlen;
- break;
-
- case ICMP_DEST_UNREACH:
- switch (code) {
- case ICMP_SR_FAILED:
- case ICMP_PORT_UNREACH:
- /* Impossible event. */
- return;
- case ICMP_FRAG_NEEDED:
- /* Too complicated case ... */
- return;
- default:
- /* All others are translated to HOST_UNREACH.
- rfc2003 contains "deep thoughts" about NET_UNREACH,
- I believe, it is just ether pollution. --ANK
- */
- rel_type = ICMPV6_DEST_UNREACH;
- rel_code = ICMPV6_ADDR_UNREACH;
- break;
- }
- break;
- case ICMP_TIME_EXCEEDED:
- if (code != ICMP_EXC_TTL)
- return;
- rel_type = ICMPV6_TIME_EXCEED;
- rel_code = ICMPV6_EXC_HOPLIMIT;
- break;
- }
-
- /* Prepare fake skb to feed it to icmpv6_send */
- skb2 = skb_clone(skb, GFP_ATOMIC);
- if (skb2 == NULL)
- return 0;
- dst_release(skb2->dst);
- skb2->dst = NULL;
- skb_pull(skb2, skb->data - (u8*)iph6);
- skb_reset_network_header(skb2);
-
- /* Try to guess incoming interface */
- rt6i = rt6_lookup(dev_net(skb->dev), &iph6->saddr, NULL, NULL, 0);
- if (rt6i && rt6i->rt6i_dev) {
- skb2->dev = rt6i->rt6i_dev;
-
- rt6i = rt6_lookup(dev_net(skb->dev),
- &iph6->daddr, &iph6->saddr, NULL, 0);
-
- if (rt6i && rt6i->rt6i_dev && rt6i->rt6i_dev->type == ARPHRD_SIT) {
- struct ip_tunnel *t = netdev_priv(rt6i->rt6i_dev);
- if (rel_type == ICMPV6_TIME_EXCEED && t->parms.iph.ttl) {
- rel_type = ICMPV6_DEST_UNREACH;
- rel_code = ICMPV6_ADDR_UNREACH;
- }
- icmpv6_send(skb2, rel_type, rel_code, rel_info, skb2->dev);
- }
- }
- kfree_skb(skb2);
- return 0;
-#endif
}
static inline void ipip6_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb)
@@ -578,13 +491,13 @@ static int ipip6_rcv(struct sk_buff *skb)
if ((tunnel->dev->priv_flags & IFF_ISATAP) &&
!isatap_chksrc(skb, iph, tunnel)) {
- tunnel->stat.rx_errors++;
+ tunnel->dev->stats.rx_errors++;
read_unlock(&ipip6_lock);
kfree_skb(skb);
return 0;
}
- tunnel->stat.rx_packets++;
- tunnel->stat.rx_bytes += skb->len;
+ tunnel->dev->stats.rx_packets++;
+ tunnel->dev->stats.rx_bytes += skb->len;
skb->dev = tunnel->dev;
dst_release(skb->dst);
skb->dst = NULL;
@@ -624,7 +537,7 @@ static inline __be32 try_6to4(struct in6_addr *v6dst)
static int ipip6_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
- struct net_device_stats *stats = &tunnel->stat;
+ struct net_device_stats *stats = &tunnel->dev->stats;
struct iphdr *tiph = &tunnel->parms.iph;
struct ipv6hdr *iph6 = ipv6_hdr(skb);
u8 tos = tunnel->parms.iph.tos;
@@ -638,7 +551,7 @@ static int ipip6_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
int addr_type;
if (tunnel->recursion++) {
- tunnel->stat.collisions++;
+ stats->collisions++;
goto tx_error;
}
@@ -705,20 +618,20 @@ static int ipip6_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
.oif = tunnel->parms.link,
.proto = IPPROTO_IPV6 };
if (ip_route_output_key(dev_net(dev), &rt, &fl)) {
- tunnel->stat.tx_carrier_errors++;
+ stats->tx_carrier_errors++;
goto tx_error_icmp;
}
}
if (rt->rt_type != RTN_UNICAST) {
ip_rt_put(rt);
- tunnel->stat.tx_carrier_errors++;
+ stats->tx_carrier_errors++;
goto tx_error_icmp;
}
tdev = rt->u.dst.dev;
if (tdev == dev) {
ip_rt_put(rt);
- tunnel->stat.collisions++;
+ stats->collisions++;
goto tx_error;
}
@@ -728,7 +641,7 @@ static int ipip6_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
mtu = skb->dst ? dst_mtu(skb->dst) : dev->mtu;
if (mtu < 68) {
- tunnel->stat.collisions++;
+ stats->collisions++;
ip_rt_put(rt);
goto tx_error;
}
@@ -1003,11 +916,6 @@ done:
return err;
}
-static struct net_device_stats *ipip6_tunnel_get_stats(struct net_device *dev)
-{
- return &(((struct ip_tunnel*)netdev_priv(dev))->stat);
-}
-
static int ipip6_tunnel_change_mtu(struct net_device *dev, int new_mtu)
{
if (new_mtu < IPV6_MIN_MTU || new_mtu > 0xFFF8 - sizeof(struct iphdr))
@@ -1021,7 +929,6 @@ static void ipip6_tunnel_setup(struct net_device *dev)
dev->uninit = ipip6_tunnel_uninit;
dev->destructor = free_netdev;
dev->hard_start_xmit = ipip6_tunnel_xmit;
- dev->get_stats = ipip6_tunnel_get_stats;
dev->do_ioctl = ipip6_tunnel_ioctl;
dev->change_mtu = ipip6_tunnel_change_mtu;
diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c
index 3804dcbbfab..5c99274558b 100644
--- a/net/ipv6/sysctl_net_ipv6.c
+++ b/net/ipv6/sysctl_net_ipv6.c
@@ -37,6 +37,10 @@ static ctl_table ipv6_table_template[] = {
.mode = 0644,
.proc_handler = &proc_dointvec
},
+ { .ctl_name = 0 }
+};
+
+static ctl_table ipv6_table[] = {
{
.ctl_name = NET_IPV6_MLD_MAX_MSF,
.procname = "mld_max_msf",
@@ -80,12 +84,6 @@ static int ipv6_sysctl_net_init(struct net *net)
ipv6_table[2].data = &net->ipv6.sysctl.bindv6only;
- /* We don't want this value to be per namespace, it should be global
- to all namespaces, so make it read-only when we are not in the
- init network namespace */
- if (net != &init_net)
- ipv6_table[3].mode = 0444;
-
net->ipv6.sysctl.table = register_net_sysctl_table(net, net_ipv6_ctl_path,
ipv6_table);
if (!net->ipv6.sysctl.table)
@@ -126,12 +124,29 @@ static struct pernet_operations ipv6_sysctl_net_ops = {
.exit = ipv6_sysctl_net_exit,
};
+static struct ctl_table_header *ip6_header;
+
int ipv6_sysctl_register(void)
{
- return register_pernet_subsys(&ipv6_sysctl_net_ops);
+ int err = -ENOMEM;;
+
+ ip6_header = register_net_sysctl_rotable(net_ipv6_ctl_path, ipv6_table);
+ if (ip6_header == NULL)
+ goto out;
+
+ err = register_pernet_subsys(&ipv6_sysctl_net_ops);
+ if (err)
+ goto err_pernet;
+out:
+ return err;
+
+err_pernet:
+ unregister_net_sysctl_table(ip6_header);
+ goto out;
}
void ipv6_sysctl_unregister(void)
{
+ unregister_net_sysctl_table(ip6_header);
unregister_pernet_subsys(&ipv6_sysctl_net_ops);
}