diff options
author | Rebecca Schultz Zavin <rebecca@android.com> | 2011-04-22 15:54:34 -0700 |
---|---|---|
committer | Rebecca Schultz Zavin <rebecca@android.com> | 2011-04-22 15:54:34 -0700 |
commit | 2e35e1d7b965893e68f2fb1af77129406be5ff05 (patch) | |
tree | 4133e079309e275c0aaf96461bdda21d44c59038 /net | |
parent | 882113dbcbe92bca8fe08f67791e9b18d38a3fd3 (diff) | |
parent | 438f97f7bd3127a95174ee8c36c6826bd9ba11ea (diff) |
Merge remote branch 'common/android-2.6.36' into android-tegra-2.6.36
Diffstat (limited to 'net')
-rw-r--r-- | net/bluetooth/hci_core.c | 2 | ||||
-rw-r--r-- | net/ipv4/devinet.c | 3 | ||||
-rw-r--r-- | net/ipv4/tcp.c | 97 | ||||
-rw-r--r-- | net/ipv4/tcp_ipv4.c | 43 | ||||
-rw-r--r-- | net/ipv6/af_inet6.c | 17 |
5 files changed, 116 insertions, 46 deletions
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index de9127d6b9d3..c2045b77bab9 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1609,7 +1609,7 @@ static inline void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb) if (conn) { register struct hci_proto *hp; - hci_conn_enter_active_mode(conn, 1); + hci_conn_enter_active_mode(conn, bt_cb(skb)->force_active); /* Send to upper protocol */ if ((hp = hci_proto[HCI_PROTO_L2CAP]) && hp->recv_acldata) { diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 8b7dfaf3b6ea..ac582d17bf40 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -815,8 +815,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) } break; case SIOCKILLADDR: /* Nuke all connections on this address */ - ret = 0; - tcp_v4_nuke_addr(sin->sin_addr.s_addr); + ret = tcp_nuke_addr(net, (struct sockaddr *) sin); break; } done: diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 1b171b5dcf3c..886721da4888 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -272,6 +272,7 @@ #include <net/tcp.h> #include <net/xfrm.h> #include <net/ip.h> +#include <net/ip6_route.h> #include <net/netdma.h> #include <net/sock.h> @@ -3320,3 +3321,99 @@ void __init tcp_init(void) tcp_secret_retiring = &tcp_secret_two; tcp_secret_secondary = &tcp_secret_two; } + +static int tcp_is_local(struct net *net, __be32 addr) { + struct rtable *rt; + struct flowi fl = { .nl_u = { .ip4_u = { .daddr = addr } } }; + if (ip_route_output_key(net, &rt, &fl) || !rt) + return 0; + return rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK); +} + +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +static int tcp_is_local6(struct net *net, struct in6_addr *addr) { + struct rt6_info *rt6 = rt6_lookup(net, addr, addr, 0, 0); + return rt6 && rt6->rt6i_dev && (rt6->rt6i_dev->flags & IFF_LOOPBACK); +} +#endif + +/* + * tcp_nuke_addr - destroy all sockets on the given local address + * if local address is the unspecified address (0.0.0.0 or ::), destroy all + * sockets with local addresses that are not configured. + */ +int tcp_nuke_addr(struct net *net, struct sockaddr *addr) +{ + int family = addr->sa_family; + unsigned int bucket; + + struct in_addr *in; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + struct in6_addr *in6; +#endif + if (family == AF_INET) { + in = &((struct sockaddr_in *)addr)->sin_addr; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + } else if (family == AF_INET6) { + in6 = &((struct sockaddr_in6 *)addr)->sin6_addr; +#endif + } else { + return -EAFNOSUPPORT; + } + + for (bucket = 0; bucket < tcp_hashinfo.ehash_mask; bucket++) { + struct hlist_nulls_node *node; + struct sock *sk; + spinlock_t *lock = inet_ehash_lockp(&tcp_hashinfo, bucket); + +restart: + spin_lock_bh(lock); + sk_nulls_for_each(sk, node, &tcp_hashinfo.ehash[bucket].chain) { + struct inet_sock *inet = inet_sk(sk); + + if (family == AF_INET) { + __be32 s4 = inet->inet_rcv_saddr; + if (in->s_addr != s4 && + !(in->s_addr == INADDR_ANY && + !tcp_is_local(net, s4))) + continue; + } + +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + if (family == AF_INET6) { + struct in6_addr *s6; + if (!inet->pinet6) + continue; + s6 = &inet->pinet6->rcv_saddr; + if (!ipv6_addr_equal(in6, s6) && + !(ipv6_addr_equal(in6, &in6addr_any) && + !tcp_is_local6(net, s6))) + continue; + } +#endif + + if (sysctl_ip_dynaddr && sk->sk_state == TCP_SYN_SENT) + continue; + if (sock_flag(sk, SOCK_DEAD)) + continue; + + sock_hold(sk); + spin_unlock_bh(lock); + + local_bh_disable(); + bh_lock_sock(sk); + sk->sk_err = ETIMEDOUT; + sk->sk_error_report(sk); + + tcp_done(sk); + bh_unlock_sock(sk); + local_bh_enable(); + sock_put(sk); + + goto restart; + } + spin_unlock_bh(lock); + } + + return 0; +} diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 459eb1d24629..cb8d305cb5b4 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1967,49 +1967,6 @@ 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_mask; bucket++) { - struct hlist_nulls_node *node; - struct sock *sk; - spinlock_t *lock = inet_ehash_lockp(&tcp_hashinfo, bucket); - -restart: - spin_lock_bh(lock); - sk_nulls_for_each(sk, node, &tcp_hashinfo.ehash[bucket].chain) { - struct inet_sock *inet = inet_sk(sk); - - if (inet->inet_rcv_saddr != saddr) - continue; - if (sysctl_ip_dynaddr && sk->sk_state == TCP_SYN_SENT) - continue; - if (sock_flag(sk, SOCK_DEAD)) - continue; - - sock_hold(sk); - spin_unlock_bh(lock); - - local_bh_disable(); - bh_lock_sock(sk); - sk->sk_err = ETIMEDOUT; - sk->sk_error_report(sk); - - tcp_done(sk); - bh_unlock_sock(sk); - local_bh_enable(); - sock_put(sk); - - goto restart; - } - spin_unlock_bh(lock); - } -} - #ifdef CONFIG_PROC_FS /* Proc filesystem TCP sock list dumping. */ diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 2f421a4beca7..fa743e0d3207 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -489,6 +489,21 @@ int inet6_getname(struct socket *sock, struct sockaddr *uaddr, EXPORT_SYMBOL(inet6_getname); +int inet6_killaddr_ioctl(struct net *net, void __user *arg) { + struct in6_ifreq ireq; + struct sockaddr_in6 sin6; + + if (!capable(CAP_NET_ADMIN)) + return -EACCES; + + if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq))) + return -EFAULT; + + sin6.sin6_family = AF_INET6; + ipv6_addr_copy(&sin6.sin6_addr, &ireq.ifr6_addr); + return tcp_nuke_addr(net, (struct sockaddr *) &sin6); +} + int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; @@ -513,6 +528,8 @@ int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) return addrconf_del_ifaddr(net, (void __user *) arg); case SIOCSIFDSTADDR: return addrconf_set_dstaddr(net, (void __user *) arg); + case SIOCKILLADDR: + return inet6_killaddr_ioctl(net, (void __user *) arg); default: if (!sk->sk_prot->ioctl) return -ENOIOCTLCMD; |