Merge tag 'v3.10.108' into update
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / net / ipv4 / tcp.c
index b80b399f237724fc71f0d1191c67e0658aeb7620..6c8931b6bba9bf05ea21cb8e6ded7e9df9f3fe72 100644 (file)
 #include <linux/crypto.h>
 #include <linux/time.h>
 #include <linux/slab.h>
+#include <linux/uid_stat.h>
 
 #include <net/icmp.h>
 #include <net/inet_common.h>
 #include <net/tcp.h>
 #include <net/xfrm.h>
 #include <net/ip.h>
+#include <net/ip6_route.h>
+#include <net/ipv6.h>
+#include <net/transp_v6.h>
 #include <net/netdma.h>
 #include <net/sock.h>
 
@@ -576,6 +580,26 @@ int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg)
                else
                        answ = tp->write_seq - tp->snd_nxt;
                break;
+               /* MTK_NET_CHANGES */
+        case SIOCKILLSOCK:
+         {
+             struct uid_err uid_e;
+             if (copy_from_user(&uid_e, (char __user *)arg, sizeof(uid_e)))
+                 return -EFAULT;
+             printk(KERN_WARNING "SIOCKILLSOCK uid = %d , err = %d",
+                                        uid_e.appuid, uid_e.errNum);
+             if (uid_e.errNum == 0)
+             {
+                 // handle BR release problem
+                 tcp_v4_handle_retrans_time_by_uid(uid_e);
+             }
+             else
+             {
+             tcp_v4_reset_connections_by_uid(uid_e);
+             }                                  
+
+                return 0;
+         }
        default:
                return -ENOIOCTLCMD;
        }
@@ -1246,6 +1270,9 @@ out:
                tcp_push(sk, flags, mss_now, tp->nonagle);
 out_nopush:
        release_sock(sk);
+
+       if (copied + copied_syn)
+               uid_stat_tcp_snd(current_uid(), copied + copied_syn);
        return copied + copied_syn;
 
 do_fault:
@@ -1550,6 +1577,7 @@ int tcp_read_sock(struct sock *sk, read_descriptor_t *desc,
        if (copied > 0) {
                tcp_recv_skb(sk, seq, &offset);
                tcp_cleanup_rbuf(sk, copied);
+               uid_stat_tcp_rcv(current_uid(), copied);
        }
        return copied;
 }
@@ -1954,6 +1982,9 @@ skip_copy:
        tcp_cleanup_rbuf(sk, copied);
 
        release_sock(sk);
+
+       if (copied > 0)
+               uid_stat_tcp_rcv(current_uid(), copied);
        return copied;
 
 out:
@@ -1962,6 +1993,8 @@ out:
 
 recv_urg:
        err = tcp_recv_urg(sk, msg, len, flags);
+       if (err > 0)
+               uid_stat_tcp_rcv(current_uid(), err);
        goto out;
 
 recv_sndq:
@@ -2599,7 +2632,7 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
                /* Translate value in seconds to number of retransmits */
                icsk->icsk_accept_queue.rskq_defer_accept =
                        secs_to_retrans(val, TCP_TIMEOUT_INIT / HZ,
-                                       TCP_RTO_MAX / HZ);
+                                       sysctl_tcp_rto_max / HZ);
                break;
 
        case TCP_WINDOW_CLAMP:
@@ -2808,7 +2841,7 @@ static int do_tcp_getsockopt(struct sock *sk, int level,
                break;
        case TCP_DEFER_ACCEPT:
                val = retrans_to_secs(icsk->icsk_accept_queue.rskq_defer_accept,
-                                     TCP_TIMEOUT_INIT / HZ, TCP_RTO_MAX / HZ);
+                                     TCP_TIMEOUT_INIT / HZ, sysctl_tcp_rto_max / HZ);
                break;
        case TCP_WINDOW_CLAMP:
                val = tp->window_clamp;
@@ -3480,3 +3513,107 @@ void __init tcp_init(void)
 
        tcp_tasklet_init();
 }
+
+static int tcp_is_local(struct net *net, __be32 addr) {
+       struct rtable *rt;
+       struct flowi4 fl4 = { .daddr = addr };
+       rt = ip_route_output_key(net, &fl4);
+       if (IS_ERR_OR_NULL(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->dst.dev && (rt6->dst.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 = NULL ;
+#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 (sysctl_ip_dynaddr && sk->sk_state == TCP_SYN_SENT)
+                               continue;
+                       if (sock_flag(sk, SOCK_DEAD))
+                               continue;
+
+                       if (family == AF_INET) {
+                               __be32 s4 = inet->inet_rcv_saddr;
+                               if (s4 == LOOPBACK4_IPV6)
+                                       continue;
+
+                               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_type(s6) == IPV6_ADDR_MAPPED)
+                                       continue;
+
+                               if (!ipv6_addr_equal(in6, s6) &&
+                                   !(ipv6_addr_equal(in6, &in6addr_any) &&
+                                     !tcp_is_local6(net, s6)))
+                               continue;
+                       }
+#endif
+
+                       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;
+}