aboutsummaryrefslogtreecommitdiff
path: root/net/ipv4
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv4')
-rw-r--r--net/ipv4/devinet.c15
-rw-r--r--net/ipv4/icmp.c22
-rw-r--r--net/ipv4/inet_timewait_sock.c35
-rw-r--r--net/ipv4/netfilter/ipt_addrtype.c2
-rw-r--r--net/ipv4/netfilter/nf_nat_proto_common.c8
-rw-r--r--net/ipv4/route.c100
-rw-r--r--net/ipv4/tcp_ipv4.c1
-rw-r--r--net/ipv4/tcp_output.c6
8 files changed, 163 insertions, 26 deletions
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 91d3d96805d..b12dae2b0b2 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -1029,6 +1029,11 @@ skip:
}
}
+static inline bool inetdev_valid_mtu(unsigned mtu)
+{
+ return mtu >= 68;
+}
+
/* Called only under RTNL semaphore */
static int inetdev_event(struct notifier_block *this, unsigned long event,
@@ -1048,6 +1053,10 @@ static int inetdev_event(struct notifier_block *this, unsigned long event,
IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
}
+ } else if (event == NETDEV_CHANGEMTU) {
+ /* Re-enabling IP */
+ if (inetdev_valid_mtu(dev->mtu))
+ in_dev = inetdev_init(dev);
}
goto out;
}
@@ -1058,7 +1067,7 @@ static int inetdev_event(struct notifier_block *this, unsigned long event,
dev->ip_ptr = NULL;
break;
case NETDEV_UP:
- if (dev->mtu < 68)
+ if (!inetdev_valid_mtu(dev->mtu))
break;
if (dev->flags & IFF_LOOPBACK) {
struct in_ifaddr *ifa;
@@ -1080,9 +1089,9 @@ static int inetdev_event(struct notifier_block *this, unsigned long event,
ip_mc_down(in_dev);
break;
case NETDEV_CHANGEMTU:
- if (dev->mtu >= 68)
+ if (inetdev_valid_mtu(dev->mtu))
break;
- /* MTU falled under 68, disable IP */
+ /* disable IP when MTU is not enough */
case NETDEV_UNREGISTER:
inetdev_destroy(in_dev);
break;
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 860558633b2..55c355e6323 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -204,18 +204,22 @@ static struct sock *icmp_sk(struct net *net)
return net->ipv4.icmp_sk[smp_processor_id()];
}
-static inline int icmp_xmit_lock(struct sock *sk)
+static inline struct sock *icmp_xmit_lock(struct net *net)
{
+ struct sock *sk;
+
local_bh_disable();
+ sk = icmp_sk(net);
+
if (unlikely(!spin_trylock(&sk->sk_lock.slock))) {
/* This can happen if the output path signals a
* dst_link_failure() for an outgoing ICMP packet.
*/
local_bh_enable();
- return 1;
+ return NULL;
}
- return 0;
+ return sk;
}
static inline void icmp_xmit_unlock(struct sock *sk)
@@ -354,15 +358,17 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
struct ipcm_cookie ipc;
struct rtable *rt = skb->rtable;
struct net *net = dev_net(rt->u.dst.dev);
- struct sock *sk = icmp_sk(net);
- struct inet_sock *inet = inet_sk(sk);
+ struct sock *sk;
+ struct inet_sock *inet;
__be32 daddr;
if (ip_options_echo(&icmp_param->replyopts, skb))
return;
- if (icmp_xmit_lock(sk))
+ sk = icmp_xmit_lock(net);
+ if (sk == NULL)
return;
+ inet = inet_sk(sk);
icmp_param->data.icmph.checksum = 0;
@@ -419,7 +425,6 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
if (!rt)
goto out;
net = dev_net(rt->u.dst.dev);
- sk = icmp_sk(net);
/*
* Find the original header. It is expected to be valid, of course.
@@ -483,7 +488,8 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
}
}
- if (icmp_xmit_lock(sk))
+ sk = icmp_xmit_lock(net);
+ if (sk == NULL)
return;
/*
diff --git a/net/ipv4/inet_timewait_sock.c b/net/ipv4/inet_timewait_sock.c
index d985bd613d2..743f011b9a8 100644
--- a/net/ipv4/inet_timewait_sock.c
+++ b/net/ipv4/inet_timewait_sock.c
@@ -409,3 +409,38 @@ out:
}
EXPORT_SYMBOL_GPL(inet_twdr_twcal_tick);
+
+void inet_twsk_purge(struct net *net, struct inet_hashinfo *hashinfo,
+ struct inet_timewait_death_row *twdr, int family)
+{
+ struct inet_timewait_sock *tw;
+ struct sock *sk;
+ struct hlist_node *node;
+ int h;
+
+ local_bh_disable();
+ for (h = 0; h < (hashinfo->ehash_size); h++) {
+ struct inet_ehash_bucket *head =
+ inet_ehash_bucket(hashinfo, h);
+ rwlock_t *lock = inet_ehash_lockp(hashinfo, h);
+restart:
+ write_lock(lock);
+ sk_for_each(sk, node, &head->twchain) {
+
+ tw = inet_twsk(sk);
+ if (!net_eq(twsk_net(tw), net) ||
+ tw->tw_family != family)
+ continue;
+
+ atomic_inc(&tw->tw_refcnt);
+ write_unlock(lock);
+ inet_twsk_deschedule(tw, twdr);
+ inet_twsk_put(tw);
+
+ goto restart;
+ }
+ write_unlock(lock);
+ }
+ local_bh_enable();
+}
+EXPORT_SYMBOL_GPL(inet_twsk_purge);
diff --git a/net/ipv4/netfilter/ipt_addrtype.c b/net/ipv4/netfilter/ipt_addrtype.c
index 49587a49722..462a22c9787 100644
--- a/net/ipv4/netfilter/ipt_addrtype.c
+++ b/net/ipv4/netfilter/ipt_addrtype.c
@@ -70,7 +70,7 @@ addrtype_mt_v1(const struct sk_buff *skb, const struct net_device *in,
(info->flags & IPT_ADDRTYPE_INVERT_SOURCE);
if (ret && info->dest)
ret &= match_type(dev, iph->daddr, info->dest) ^
- (info->flags & IPT_ADDRTYPE_INVERT_DEST);
+ !!(info->flags & IPT_ADDRTYPE_INVERT_DEST);
return ret;
}
diff --git a/net/ipv4/netfilter/nf_nat_proto_common.c b/net/ipv4/netfilter/nf_nat_proto_common.c
index 91537f11273..6c4f11f5144 100644
--- a/net/ipv4/netfilter/nf_nat_proto_common.c
+++ b/net/ipv4/netfilter/nf_nat_proto_common.c
@@ -73,9 +73,13 @@ bool nf_nat_proto_unique_tuple(struct nf_conntrack_tuple *tuple,
range_size = ntohs(range->max.all) - min + 1;
}
- off = *rover;
if (range->flags & IP_NAT_RANGE_PROTO_RANDOM)
- off = net_random();
+ off = secure_ipv4_port_ephemeral(tuple->src.u3.ip, tuple->dst.u3.ip,
+ maniptype == IP_NAT_MANIP_SRC
+ ? tuple->dst.u.all
+ : tuple->src.u.all);
+ else
+ off = *rover;
for (i = 0; i < range_size; i++, off++) {
*portptr = htons(min + off % range_size);
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 16fc6f454a3..6ee5354c9aa 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -2914,6 +2914,68 @@ static int ipv4_sysctl_rtcache_flush_strategy(ctl_table *table,
return 0;
}
+static void rt_secret_reschedule(int old)
+{
+ struct net *net;
+ int new = ip_rt_secret_interval;
+ int diff = new - old;
+
+ if (!diff)
+ return;
+
+ rtnl_lock();
+ for_each_net(net) {
+ int deleted = del_timer_sync(&net->ipv4.rt_secret_timer);
+
+ if (!new)
+ continue;
+
+ if (deleted) {
+ long time = net->ipv4.rt_secret_timer.expires - jiffies;
+
+ if (time <= 0 || (time += diff) <= 0)
+ time = 0;
+
+ net->ipv4.rt_secret_timer.expires = time;
+ } else
+ net->ipv4.rt_secret_timer.expires = new;
+
+ net->ipv4.rt_secret_timer.expires += jiffies;
+ add_timer(&net->ipv4.rt_secret_timer);
+ }
+ rtnl_unlock();
+}
+
+static int ipv4_sysctl_rt_secret_interval(ctl_table *ctl, int write,
+ struct file *filp,
+ void __user *buffer, size_t *lenp,
+ loff_t *ppos)
+{
+ int old = ip_rt_secret_interval;
+ int ret = proc_dointvec_jiffies(ctl, write, filp, buffer, lenp, ppos);
+
+ rt_secret_reschedule(old);
+
+ return ret;
+}
+
+static int ipv4_sysctl_rt_secret_interval_strategy(ctl_table *table,
+ int __user *name,
+ int nlen,
+ void __user *oldval,
+ size_t __user *oldlenp,
+ void __user *newval,
+ size_t newlen)
+{
+ int old = ip_rt_secret_interval;
+ int ret = sysctl_jiffies(table, name, nlen, oldval, oldlenp, newval,
+ newlen);
+
+ rt_secret_reschedule(old);
+
+ return ret;
+}
+
static ctl_table ipv4_route_table[] = {
{
.ctl_name = NET_IPV4_ROUTE_GC_THRESH,
@@ -3048,20 +3110,29 @@ static ctl_table ipv4_route_table[] = {
.data = &ip_rt_secret_interval,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = &ipv4_sysctl_rt_secret_interval,
+ .strategy = &ipv4_sysctl_rt_secret_interval_strategy,
},
{ .ctl_name = 0 }
};
-static __net_initdata struct ctl_path ipv4_route_path[] = {
+static struct ctl_table empty[1];
+
+static struct ctl_table ipv4_skeleton[] =
+{
+ { .procname = "route", .ctl_name = NET_IPV4_ROUTE,
+ .mode = 0555, .child = ipv4_route_table},
+ { .procname = "neigh", .ctl_name = NET_IPV4_NEIGH,
+ .mode = 0555, .child = empty},
+ { }
+};
+
+static __net_initdata struct ctl_path ipv4_path[] = {
{ .procname = "net", .ctl_name = CTL_NET, },
{ .procname = "ipv4", .ctl_name = NET_IPV4, },
- { .procname = "route", .ctl_name = NET_IPV4_ROUTE, },
{ },
};
-
static struct ctl_table ipv4_route_flush_table[] = {
{
.ctl_name = NET_IPV4_ROUTE_FLUSH,
@@ -3074,6 +3145,13 @@ static struct ctl_table ipv4_route_flush_table[] = {
{ .ctl_name = 0 },
};
+static __net_initdata struct ctl_path ipv4_route_path[] = {
+ { .procname = "net", .ctl_name = CTL_NET, },
+ { .procname = "ipv4", .ctl_name = NET_IPV4, },
+ { .procname = "route", .ctl_name = NET_IPV4_ROUTE, },
+ { },
+};
+
static __net_init int sysctl_route_net_init(struct net *net)
{
struct ctl_table *tbl;
@@ -3126,10 +3204,12 @@ static __net_init int rt_secret_timer_init(struct net *net)
net->ipv4.rt_secret_timer.data = (unsigned long)net;
init_timer_deferrable(&net->ipv4.rt_secret_timer);
- net->ipv4.rt_secret_timer.expires =
- jiffies + net_random() % ip_rt_secret_interval +
- ip_rt_secret_interval;
- add_timer(&net->ipv4.rt_secret_timer);
+ if (ip_rt_secret_interval) {
+ net->ipv4.rt_secret_timer.expires =
+ jiffies + net_random() % ip_rt_secret_interval +
+ ip_rt_secret_interval;
+ add_timer(&net->ipv4.rt_secret_timer);
+ }
return 0;
}
@@ -3223,7 +3303,7 @@ int __init ip_rt_init(void)
*/
void __init ip_static_sysctl_init(void)
{
- register_sysctl_paths(ipv4_route_path, ipv4_route_table);
+ register_sysctl_paths(ipv4_path, ipv4_skeleton);
}
#endif
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 44c1e934824..1b4fee20fc9 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -2376,6 +2376,7 @@ static int __net_init tcp_sk_init(struct net *net)
static void __net_exit tcp_sk_exit(struct net *net)
{
inet_ctl_sock_destroy(net->ipv4.tcp_sock);
+ inet_twsk_purge(net, &tcp_hashinfo, &tcp_death_row, AF_INET);
}
static struct pernet_operations __net_initdata tcp_sk_ops = {
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index a00532de2a8..8165f5aa8c7 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -468,7 +468,8 @@ static unsigned tcp_syn_options(struct sock *sk, struct sk_buff *skb,
}
if (likely(sysctl_tcp_window_scaling)) {
opts->ws = tp->rx_opt.rcv_wscale;
- size += TCPOLEN_WSCALE_ALIGNED;
+ if(likely(opts->ws))
+ size += TCPOLEN_WSCALE_ALIGNED;
}
if (likely(sysctl_tcp_sack)) {
opts->options |= OPTION_SACK_ADVERTISE;
@@ -509,7 +510,8 @@ static unsigned tcp_synack_options(struct sock *sk,
if (likely(ireq->wscale_ok)) {
opts->ws = ireq->rcv_wscale;
- size += TCPOLEN_WSCALE_ALIGNED;
+ if(likely(opts->ws))
+ size += TCPOLEN_WSCALE_ALIGNED;
}
if (likely(doing_ts)) {
opts->options |= OPTION_TS;