From 2df5996350da51bcdee798c00ae1f2415fd9ad20 Mon Sep 17 00:00:00 2001 From: Robert Love 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 net: fix tcp_v4_nuke_addr Signed-off-by: Dima Zavin --- 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 #include +#include #include #include #include @@ -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. */