| 1 | From 2df5996350da51bcdee798c00ae1f2415fd9ad20 Mon Sep 17 00:00:00 2001 |
| 2 | From: Robert Love <rlove@google.com> |
| 3 | Date: Mon, 12 May 2008 17:08:29 -0400 |
| 4 | Subject: [PATCH 081/134] net: socket ioctl to reset connections matching local address |
| 5 | |
| 6 | Introduce a new socket ioctl, SIOCKILLADDR, that nukes all sockets |
| 7 | bound to the same local address. This is useful in situations with |
| 8 | dynamic IPs, to kill stuck connections. |
| 9 | |
| 10 | Signed-off-by: Brian Swetland <swetland@google.com> |
| 11 | |
| 12 | net: fix tcp_v4_nuke_addr |
| 13 | |
| 14 | Signed-off-by: Dima Zavin <dima@android.com> |
| 15 | --- |
| 16 | include/linux/sockios.h | 1 + |
| 17 | include/net/tcp.h | 2 ++ |
| 18 | net/ipv4/af_inet.c | 1 + |
| 19 | net/ipv4/devinet.c | 9 ++++++++- |
| 20 | net/ipv4/tcp_ipv4.c | 31 +++++++++++++++++++++++++++++++ |
| 21 | 5 files changed, 43 insertions(+), 1 deletions(-) |
| 22 | |
| 23 | --- a/include/linux/sockios.h |
| 24 | +++ b/include/linux/sockios.h |
| 25 | @@ -65,6 +65,7 @@ |
| 26 | #define SIOCDIFADDR 0x8936 /* delete PA address */ |
| 27 | #define SIOCSIFHWBROADCAST 0x8937 /* set hardware broadcast addr */ |
| 28 | #define SIOCGIFCOUNT 0x8938 /* get number of devices */ |
| 29 | +#define SIOCKILLADDR 0x8939 /* kill sockets with this local addr */ |
| 30 | |
| 31 | #define SIOCGIFBR 0x8940 /* Bridging support */ |
| 32 | #define SIOCSIFBR 0x8941 /* Set bridging options */ |
| 33 | --- a/include/net/tcp.h |
| 34 | +++ b/include/net/tcp.h |
| 35 | @@ -1381,6 +1381,8 @@ extern struct sk_buff **tcp4_gro_receive |
| 36 | extern int tcp_gro_complete(struct sk_buff *skb); |
| 37 | extern int tcp4_gro_complete(struct sk_buff *skb); |
| 38 | |
| 39 | +extern void tcp_v4_nuke_addr(__u32 saddr); |
| 40 | + |
| 41 | #ifdef CONFIG_PROC_FS |
| 42 | extern int tcp4_proc_init(void); |
| 43 | extern void tcp4_proc_exit(void); |
| 44 | --- a/net/ipv4/af_inet.c |
| 45 | +++ b/net/ipv4/af_inet.c |
| 46 | @@ -825,6 +825,7 @@ int inet_ioctl(struct socket *sock, unsi |
| 47 | case SIOCSIFPFLAGS: |
| 48 | case SIOCGIFPFLAGS: |
| 49 | case SIOCSIFFLAGS: |
| 50 | + case SIOCKILLADDR: |
| 51 | err = devinet_ioctl(net, cmd, (void __user *)arg); |
| 52 | break; |
| 53 | default: |
| 54 | --- a/net/ipv4/devinet.c |
| 55 | +++ b/net/ipv4/devinet.c |
| 56 | @@ -57,6 +57,7 @@ |
| 57 | |
| 58 | #include <net/arp.h> |
| 59 | #include <net/ip.h> |
| 60 | +#include <net/tcp.h> |
| 61 | #include <net/route.h> |
| 62 | #include <net/ip_fib.h> |
| 63 | #include <net/rtnetlink.h> |
| 64 | @@ -631,6 +632,7 @@ int devinet_ioctl(struct net *net, unsig |
| 65 | case SIOCSIFBRDADDR: /* Set the broadcast address */ |
| 66 | case SIOCSIFDSTADDR: /* Set the destination address */ |
| 67 | case SIOCSIFNETMASK: /* Set the netmask for the interface */ |
| 68 | + case SIOCKILLADDR: /* Nuke all sockets on this address */ |
| 69 | ret = -EACCES; |
| 70 | if (!capable(CAP_NET_ADMIN)) |
| 71 | goto out; |
| 72 | @@ -680,7 +682,8 @@ int devinet_ioctl(struct net *net, unsig |
| 73 | } |
| 74 | |
| 75 | ret = -EADDRNOTAVAIL; |
| 76 | - if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) |
| 77 | + if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS |
| 78 | + && cmd != SIOCKILLADDR) |
| 79 | goto done; |
| 80 | |
| 81 | switch (cmd) { |
| 82 | @@ -804,6 +807,10 @@ int devinet_ioctl(struct net *net, unsig |
| 83 | inet_insert_ifa(ifa); |
| 84 | } |
| 85 | break; |
| 86 | + case SIOCKILLADDR: /* Nuke all connections on this address */ |
| 87 | + ret = 0; |
| 88 | + tcp_v4_nuke_addr(sin->sin_addr.s_addr); |
| 89 | + break; |
| 90 | } |
| 91 | done: |
| 92 | rtnl_unlock(); |
| 93 | --- a/net/ipv4/tcp_ipv4.c |
| 94 | +++ b/net/ipv4/tcp_ipv4.c |
| 95 | @@ -1845,6 +1845,37 @@ void tcp_v4_destroy_sock(struct sock *sk |
| 96 | |
| 97 | EXPORT_SYMBOL(tcp_v4_destroy_sock); |
| 98 | |
| 99 | +/* |
| 100 | + * tcp_v4_nuke_addr - destroy all sockets on the given local address |
| 101 | + */ |
| 102 | +void tcp_v4_nuke_addr(__u32 saddr) |
| 103 | +{ |
| 104 | + unsigned int bucket; |
| 105 | + |
| 106 | + for (bucket = 0; bucket < tcp_hashinfo.ehash_size; bucket++) { |
| 107 | + struct hlist_nulls_node *node; |
| 108 | + struct sock *sk; |
| 109 | + spinlock_t *lock = inet_ehash_lockp(&tcp_hashinfo, bucket); |
| 110 | + |
| 111 | + spin_lock_bh(lock); |
| 112 | + sk_nulls_for_each(sk, node, &tcp_hashinfo.ehash[bucket].chain) { |
| 113 | + struct inet_sock *inet = inet_sk(sk); |
| 114 | + |
| 115 | + if (inet->rcv_saddr != saddr) |
| 116 | + continue; |
| 117 | + if (sysctl_ip_dynaddr && sk->sk_state == TCP_SYN_SENT) |
| 118 | + continue; |
| 119 | + if (sock_flag(sk, SOCK_DEAD)) |
| 120 | + continue; |
| 121 | + |
| 122 | + sk->sk_err = ETIMEDOUT; |
| 123 | + sk->sk_error_report(sk); |
| 124 | + tcp_done(sk); |
| 125 | + } |
| 126 | + spin_unlock_bh(lock); |
| 127 | + } |
| 128 | +} |
| 129 | + |
| 130 | #ifdef CONFIG_PROC_FS |
| 131 | /* Proc filesystem TCP sock list dumping. */ |
| 132 | |
| 133 | |