aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/goldfish/patches-2.6.30/0081-net-socket-ioctl-to-reset-connections-matching-loca.patch
blob: fdffc507a078fb32783716a5314cd6b4c0975322 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
From 2df5996350da51bcdee798c00ae1f2415fd9ad20 Mon Sep 17 00:00:00 2001
From: Robert Love <rlove@google.com>
Date: Mon, 12 May 2008 17:08:29 -0400
Subject: [PATCH 081/134] net: socket ioctl to reset connections matching local address

Introduce a new socket ioctl, SIOCKILLADDR, that nukes all sockets
bound to the same local address. This is useful in situations with
dynamic IPs, to kill stuck connections.

Signed-off-by: Brian Swetland <swetland@google.com>

net: fix tcp_v4_nuke_addr

Signed-off-by: Dima Zavin <dima@android.com>
---
 include/linux/sockios.h |    1 +
 include/net/tcp.h       |    2 ++
 net/ipv4/af_inet.c      |    1 +
 net/ipv4/devinet.c      |    9 ++++++++-
 net/ipv4/tcp_ipv4.c     |   31 +++++++++++++++++++++++++++++++
 5 files changed, 43 insertions(+), 1 deletions(-)

--- a/include/linux/sockios.h
+++ b/include/linux/sockios.h
@@ -65,6 +65,7 @@
 #define SIOCDIFADDR	0x8936		/* delete PA address		*/
 #define	SIOCSIFHWBROADCAST	0x8937	/* set hardware broadcast addr	*/
 #define SIOCGIFCOUNT	0x8938		/* get number of devices */
+#define SIOCKILLADDR	0x8939		/* kill sockets with this local addr */
 
 #define SIOCGIFBR	0x8940		/* Bridging support		*/
 #define SIOCSIFBR	0x8941		/* Set bridging options 	*/
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1381,6 +1381,8 @@ extern struct sk_buff **tcp4_gro_receive
 extern int tcp_gro_complete(struct sk_buff *skb);
 extern int tcp4_gro_complete(struct sk_buff *skb);
 
+extern void tcp_v4_nuke_addr(__u32 saddr);
+
 #ifdef CONFIG_PROC_FS
 extern int  tcp4_proc_init(void);
 extern void tcp4_proc_exit(void);
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -825,6 +825,7 @@ int inet_ioctl(struct socket *sock, unsi
 		case SIOCSIFPFLAGS:
 		case SIOCGIFPFLAGS:
 		case SIOCSIFFLAGS:
+		case SIOCKILLADDR:
 			err = devinet_ioctl(net, cmd, (void __user *)arg);
 			break;
 		default:
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -57,6 +57,7 @@
 
 #include <net/arp.h>
 #include <net/ip.h>
+#include <net/tcp.h>
 #include <net/route.h>
 #include <net/ip_fib.h>
 #include <net/rtnetlink.h>
@@ -631,6 +632,7 @@ int devinet_ioctl(struct net *net, unsig
 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
 	case SIOCSIFDSTADDR:	/* Set the destination address */
 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
+	case SIOCKILLADDR:	/* Nuke all sockets on this address */
 		ret = -EACCES;
 		if (!capable(CAP_NET_ADMIN))
 			goto out;
@@ -680,7 +682,8 @@ int devinet_ioctl(struct net *net, unsig
 	}
 
 	ret = -EADDRNOTAVAIL;
-	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
+	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS
+	    && cmd != SIOCKILLADDR)
 		goto done;
 
 	switch (cmd) {
@@ -804,6 +807,10 @@ int devinet_ioctl(struct net *net, unsig
 			inet_insert_ifa(ifa);
 		}
 		break;
+	case SIOCKILLADDR:	/* Nuke all connections on this address */
+		ret = 0;
+		tcp_v4_nuke_addr(sin->sin_addr.s_addr);
+		break;
 	}
 done:
 	rtnl_unlock();
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1845,6 +1845,37 @@ void tcp_v4_destroy_sock(struct sock *sk
 
 EXPORT_SYMBOL(tcp_v4_destroy_sock);
 
+/*
+ * tcp_v4_nuke_addr - destroy all sockets on the given local address
+ */
+void tcp_v4_nuke_addr(__u32 saddr)
+{
+	unsigned int bucket;
+
+	for (bucket = 0; bucket < tcp_hashinfo.ehash_size; bucket++) {
+		struct hlist_nulls_node *node;
+		struct sock *sk;
+		spinlock_t *lock = inet_ehash_lockp(&tcp_hashinfo, bucket);
+
+		spin_lock_bh(lock);
+		sk_nulls_for_each(sk, node, &tcp_hashinfo.ehash[bucket].chain) {
+			struct inet_sock *inet = inet_sk(sk);
+
+			if (inet->rcv_saddr != saddr)
+				continue;
+			if (sysctl_ip_dynaddr && sk->sk_state == TCP_SYN_SENT)
+				continue;
+			if (sock_flag(sk, SOCK_DEAD))
+				continue;
+
+			sk->sk_err = ETIMEDOUT;
+			sk->sk_error_report(sk);
+			tcp_done(sk);
+		}
+		spin_unlock_bh(lock);
+	}
+}
+
 #ifdef CONFIG_PROC_FS
 /* Proc filesystem TCP sock list dumping. */