]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - net/ipv4/af_inet.c
TPROXY: supply a struct flowi->flags argument in inet_sk_rebuild_header()
[linux-2.6.git] / net / ipv4 / af_inet.c
index 16aae8ef5555b65855f5437a729c13134cf9739b..1aa2dc9e380ec237c3a4abc8d1e29911bc09dacb 100644 (file)
@@ -5,8 +5,6 @@
  *
  *             PF_INET protocol family socket handler.
  *
- * Version:    $Id: af_inet.c,v 1.137 2002/02/01 22:01:03 davem Exp $
- *
  * Authors:    Ross Biro
  *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *             Florian La Roche, <flla@stud.uni-sb.de>
@@ -92,7 +90,6 @@
 #include <asm/uaccess.h>
 #include <asm/system.h>
 
-#include <linux/smp_lock.h>
 #include <linux/inet.h>
 #include <linux/igmp.h>
 #include <linux/inetdevice.h>
 #include <net/ipip.h>
 #include <net/inet_common.h>
 #include <net/xfrm.h>
+#include <net/net_namespace.h>
 #ifdef CONFIG_IP_MROUTE
 #include <linux/mroute.h>
 #endif
 
-DEFINE_SNMP_STAT(struct linux_mib, net_statistics) __read_mostly;
-
 extern void ip_mc_drop_socket(struct sock *sk);
 
 /* The inetsw table contains everything that inet_create needs to
@@ -127,6 +123,10 @@ extern void ip_mc_drop_socket(struct sock *sk);
 static struct list_head inetsw[SOCK_MAX];
 static DEFINE_SPINLOCK(inetsw_lock);
 
+struct ipv4_config ipv4_config;
+
+EXPORT_SYMBOL(ipv4_config);
+
 /* New destruction routine */
 
 void inet_sock_destruct(struct sock *sk)
@@ -136,6 +136,8 @@ void inet_sock_destruct(struct sock *sk)
        __skb_queue_purge(&sk->sk_receive_queue);
        __skb_queue_purge(&sk->sk_error_queue);
 
+       sk_mem_reclaim(sk);
+
        if (sk->sk_type == SOCK_STREAM && sk->sk_state != TCP_CLOSE) {
                printk("Attempt to release TCP socket in state %d %p\n",
                       sk->sk_state, sk);
@@ -146,10 +148,10 @@ void inet_sock_destruct(struct sock *sk)
                return;
        }
 
-       BUG_TRAP(!atomic_read(&sk->sk_rmem_alloc));
-       BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));
-       BUG_TRAP(!sk->sk_wmem_queued);
-       BUG_TRAP(!sk->sk_forward_alloc);
+       WARN_ON(atomic_read(&sk->sk_rmem_alloc));
+       WARN_ON(atomic_read(&sk->sk_wmem_alloc));
+       WARN_ON(sk->sk_wmem_queued);
+       WARN_ON(sk->sk_forward_alloc);
 
        kfree(inet->opt);
        dst_release(sk->sk_dst_cache);
@@ -238,14 +240,30 @@ void build_ehash_secret(void)
 }
 EXPORT_SYMBOL(build_ehash_secret);
 
+static inline int inet_netns_ok(struct net *net, int protocol)
+{
+       int hash;
+       struct net_protocol *ipprot;
+
+       if (net == &init_net)
+               return 1;
+
+       hash = protocol & (MAX_INET_PROTOS - 1);
+       ipprot = rcu_dereference(inet_protos[hash]);
+
+       if (ipprot == NULL)
+               /* raw IP is OK */
+               return 1;
+       return ipprot->netns_ok;
+}
+
 /*
  *     Create an inet socket.
  */
 
-static int inet_create(struct socket *sock, int protocol)
+static int inet_create(struct net *net, struct socket *sock, int protocol)
 {
        struct sock *sk;
-       struct list_head *p;
        struct inet_protosw *answer;
        struct inet_sock *inet;
        struct proto *answer_prot;
@@ -262,13 +280,12 @@ static int inet_create(struct socket *sock, int protocol)
        sock->state = SS_UNCONNECTED;
 
        /* Look for the requested type/protocol pair. */
-       answer = NULL;
 lookup_protocol:
        err = -ESOCKTNOSUPPORT;
        rcu_read_lock();
-       list_for_each_rcu(p, &inetsw[sock->type]) {
-               answer = list_entry(p, struct inet_protosw, list);
+       list_for_each_entry_rcu(answer, &inetsw[sock->type], list) {
 
+               err = 0;
                /* Check the non-wild match. */
                if (protocol == answer->protocol) {
                        if (protocol != IPPROTO_IP)
@@ -283,10 +300,9 @@ lookup_protocol:
                                break;
                }
                err = -EPROTONOSUPPORT;
-               answer = NULL;
        }
 
-       if (unlikely(answer == NULL)) {
+       if (unlikely(err)) {
                if (try_loading_module < 2) {
                        rcu_read_unlock();
                        /*
@@ -312,16 +328,20 @@ lookup_protocol:
        if (answer->capability > 0 && !capable(answer->capability))
                goto out_rcu_unlock;
 
+       err = -EAFNOSUPPORT;
+       if (!inet_netns_ok(net, protocol))
+               goto out_rcu_unlock;
+
        sock->ops = answer->ops;
        answer_prot = answer->prot;
        answer_no_check = answer->no_check;
        answer_flags = answer->flags;
        rcu_read_unlock();
 
-       BUG_TRAP(answer_prot->slab != NULL);
+       WARN_ON(answer_prot->slab == NULL);
 
        err = -ENOBUFS;
-       sk = sk_alloc(PF_INET, GFP_KERNEL, answer_prot, 1);
+       sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot);
        if (sk == NULL)
                goto out;
 
@@ -438,7 +458,7 @@ int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
        if (addr_len < sizeof(struct sockaddr_in))
                goto out;
 
-       chk_addr_ret = inet_addr_type(addr->sin_addr.s_addr);
+       chk_addr_ret = inet_addr_type(sock_net(sk), addr->sin_addr.s_addr);
 
        /* Not specified by any standard per-se, however it breaks too
         * many applications when removed.  It is unfortunate since
@@ -449,8 +469,8 @@ int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
         */
        err = -EADDRNOTAVAIL;
        if (!sysctl_ip_nonlocal_bind &&
-           !inet->freebind &&
-           addr->sin_addr.s_addr != INADDR_ANY &&
+           !(inet->freebind || inet->transparent) &&
+           addr->sin_addr.s_addr != htonl(INADDR_ANY) &&
            chk_addr_ret != RTN_LOCAL &&
            chk_addr_ret != RTN_MULTICAST &&
            chk_addr_ret != RTN_BROADCAST)
@@ -638,8 +658,8 @@ int inet_accept(struct socket *sock, struct socket *newsock, int flags)
 
        lock_sock(sk2);
 
-       BUG_TRAP((1 << sk2->sk_state) &
-                (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT | TCPF_CLOSE));
+       WARN_ON(!((1 << sk2->sk_state) &
+                 (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT | TCPF_CLOSE)));
 
        sock_graft(sk2, newsock);
 
@@ -776,6 +796,7 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
        struct sock *sk = sock->sk;
        int err = 0;
+       struct net *net = sock_net(sk);
 
        switch (cmd) {
                case SIOCGSTAMP:
@@ -787,12 +808,12 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                case SIOCADDRT:
                case SIOCDELRT:
                case SIOCRTMSG:
-                       err = ip_rt_ioctl(cmd, (void __user *)arg);
+                       err = ip_rt_ioctl(net, cmd, (void __user *)arg);
                        break;
                case SIOCDARP:
                case SIOCGARP:
                case SIOCSARP:
-                       err = arp_ioctl(cmd, (void __user *)arg);
+                       err = arp_ioctl(net, cmd, (void __user *)arg);
                        break;
                case SIOCGIFADDR:
                case SIOCSIFADDR:
@@ -805,7 +826,7 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                case SIOCSIFPFLAGS:
                case SIOCGIFPFLAGS:
                case SIOCSIFFLAGS:
-                       err = devinet_ioctl(cmd, (void __user *)arg);
+                       err = devinet_ioctl(net, cmd, (void __user *)arg);
                        break;
                default:
                        if (sk->sk_prot->ioctl)
@@ -832,10 +853,11 @@ const struct proto_ops inet_stream_ops = {
        .shutdown          = inet_shutdown,
        .setsockopt        = sock_common_setsockopt,
        .getsockopt        = sock_common_getsockopt,
-       .sendmsg           = inet_sendmsg,
+       .sendmsg           = tcp_sendmsg,
        .recvmsg           = sock_common_recvmsg,
        .mmap              = sock_no_mmap,
        .sendpage          = tcp_sendpage,
+       .splice_read       = tcp_splice_read,
 #ifdef CONFIG_COMPAT
        .compat_setsockopt = compat_sock_common_setsockopt,
        .compat_getsockopt = compat_sock_common_getsockopt,
@@ -940,7 +962,7 @@ static struct inet_protosw inetsw_array[] =
        }
 };
 
-#define INETSW_ARRAY_LEN (sizeof(inetsw_array) / sizeof(struct inet_protosw))
+#define INETSW_ARRAY_LEN ARRAY_SIZE(inetsw_array)
 
 void inet_register_protosw(struct inet_protosw *p)
 {
@@ -1049,8 +1071,8 @@ static int inet_sk_reselect_saddr(struct sock *sk)
 
        if (sysctl_ip_dynaddr > 1) {
                printk(KERN_INFO "%s(): shifting inet->"
-                                "saddr from %d.%d.%d.%d to %d.%d.%d.%d\n",
-                      __FUNCTION__,
+                                "saddr from " NIPQUAD_FMT " to " NIPQUAD_FMT "\n",
+                      __func__,
                       NIPQUAD(old_saddr),
                       NIPQUAD(new_saddr));
        }
@@ -1095,6 +1117,7 @@ int inet_sk_rebuild_header(struct sock *sk)
                        },
                },
                .proto = sk->sk_protocol,
+               .flags = inet_sk_flowi_flags(sk),
                .uli_u = {
                        .ports = {
                                .sport = inet->sport,
@@ -1104,7 +1127,7 @@ int inet_sk_rebuild_header(struct sock *sk)
        };
 
        security_sk_classify_flow(sk, &fl);
-       err = ip_route_output_flow(&rt, &fl, sk, 0);
+       err = ip_route_output_flow(sock_net(sk), &rt, &fl, sk, 0);
 }
        if (!err)
                sk_setup_caps(sk, &rt->u.dst);
@@ -1171,6 +1194,9 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features)
        int ihl;
        int id;
 
+       if (!(features & NETIF_F_V4_CSUM))
+               features &= ~NETIF_F_SG;
+
        if (unlikely(skb_shinfo(skb)->gso_type &
                     ~(SKB_GSO_TCPV4 |
                       SKB_GSO_UDP |
@@ -1203,7 +1229,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features)
                segs = ops->gso_segment(skb, features);
        rcu_read_unlock();
 
-       if (!segs || unlikely(IS_ERR(segs)))
+       if (!segs || IS_ERR(segs))
                goto out;
 
        skb = segs;
@@ -1219,6 +1245,29 @@ out:
        return segs;
 }
 
+int inet_ctl_sock_create(struct sock **sk, unsigned short family,
+                        unsigned short type, unsigned char protocol,
+                        struct net *net)
+{
+       struct socket *sock;
+       int rc = sock_create_kern(family, type, protocol, &sock);
+
+       if (rc == 0) {
+               *sk = sock->sk;
+               (*sk)->sk_allocation = GFP_ATOMIC;
+               /*
+                * Unhash it so that IP input processing does not even see it,
+                * we do not wish this socket to see incoming packets.
+                */
+               (*sk)->sk_prot->unhash(*sk);
+
+               sk_change_net(*sk, net);
+       }
+       return rc;
+}
+
+EXPORT_SYMBOL_GPL(inet_ctl_sock_create);
+
 unsigned long snmp_fold_field(void *mib[], int offt)
 {
        unsigned long res = 0;
@@ -1232,7 +1281,7 @@ unsigned long snmp_fold_field(void *mib[], int offt)
 }
 EXPORT_SYMBOL_GPL(snmp_fold_field);
 
-int snmp_mib_init(void *ptr[2], size_t mibsize, size_t mibalign)
+int snmp_mib_init(void *ptr[2], size_t mibsize)
 {
        BUG_ON(ptr == NULL);
        ptr[0] = __alloc_percpu(mibsize);
@@ -1271,63 +1320,86 @@ static struct net_protocol tcp_protocol = {
        .gso_send_check = tcp_v4_gso_send_check,
        .gso_segment =  tcp_tso_segment,
        .no_policy =    1,
+       .netns_ok =     1,
 };
 
 static struct net_protocol udp_protocol = {
        .handler =      udp_rcv,
        .err_handler =  udp_err,
        .no_policy =    1,
+       .netns_ok =     1,
 };
 
 static struct net_protocol icmp_protocol = {
        .handler =      icmp_rcv,
+       .no_policy =    1,
+       .netns_ok =     1,
 };
 
-static int __init init_ipv4_mibs(void)
+static __net_init int ipv4_mib_init_net(struct net *net)
 {
-       if (snmp_mib_init((void **)net_statistics,
-                         sizeof(struct linux_mib),
-                         __alignof__(struct linux_mib)) < 0)
-               goto err_net_mib;
-       if (snmp_mib_init((void **)ip_statistics,
-                         sizeof(struct ipstats_mib),
-                         __alignof__(struct ipstats_mib)) < 0)
-               goto err_ip_mib;
-       if (snmp_mib_init((void **)icmp_statistics,
-                         sizeof(struct icmp_mib),
-                         __alignof__(struct icmp_mib)) < 0)
-               goto err_icmp_mib;
-       if (snmp_mib_init((void **)tcp_statistics,
-                         sizeof(struct tcp_mib),
-                         __alignof__(struct tcp_mib)) < 0)
+       if (snmp_mib_init((void **)net->mib.tcp_statistics,
+                         sizeof(struct tcp_mib)) < 0)
                goto err_tcp_mib;
-       if (snmp_mib_init((void **)udp_statistics,
-                         sizeof(struct udp_mib),
-                         __alignof__(struct udp_mib)) < 0)
+       if (snmp_mib_init((void **)net->mib.ip_statistics,
+                         sizeof(struct ipstats_mib)) < 0)
+               goto err_ip_mib;
+       if (snmp_mib_init((void **)net->mib.net_statistics,
+                         sizeof(struct linux_mib)) < 0)
+               goto err_net_mib;
+       if (snmp_mib_init((void **)net->mib.udp_statistics,
+                         sizeof(struct udp_mib)) < 0)
                goto err_udp_mib;
-       if (snmp_mib_init((void **)udplite_statistics,
-                         sizeof(struct udp_mib),
-                         __alignof__(struct udp_mib)) < 0)
+       if (snmp_mib_init((void **)net->mib.udplite_statistics,
+                         sizeof(struct udp_mib)) < 0)
                goto err_udplite_mib;
+       if (snmp_mib_init((void **)net->mib.icmp_statistics,
+                         sizeof(struct icmp_mib)) < 0)
+               goto err_icmp_mib;
+       if (snmp_mib_init((void **)net->mib.icmpmsg_statistics,
+                         sizeof(struct icmpmsg_mib)) < 0)
+               goto err_icmpmsg_mib;
 
-       tcp_mib_init();
-
+       tcp_mib_init(net);
        return 0;
 
+err_icmpmsg_mib:
+       snmp_mib_free((void **)net->mib.icmp_statistics);
+err_icmp_mib:
+       snmp_mib_free((void **)net->mib.udplite_statistics);
 err_udplite_mib:
-       snmp_mib_free((void **)udp_statistics);
+       snmp_mib_free((void **)net->mib.udp_statistics);
 err_udp_mib:
-       snmp_mib_free((void **)tcp_statistics);
-err_tcp_mib:
-       snmp_mib_free((void **)icmp_statistics);
-err_icmp_mib:
-       snmp_mib_free((void **)ip_statistics);
-err_ip_mib:
-       snmp_mib_free((void **)net_statistics);
+       snmp_mib_free((void **)net->mib.net_statistics);
 err_net_mib:
+       snmp_mib_free((void **)net->mib.ip_statistics);
+err_ip_mib:
+       snmp_mib_free((void **)net->mib.tcp_statistics);
+err_tcp_mib:
        return -ENOMEM;
 }
 
+static __net_exit void ipv4_mib_exit_net(struct net *net)
+{
+       snmp_mib_free((void **)net->mib.icmpmsg_statistics);
+       snmp_mib_free((void **)net->mib.icmp_statistics);
+       snmp_mib_free((void **)net->mib.udplite_statistics);
+       snmp_mib_free((void **)net->mib.udp_statistics);
+       snmp_mib_free((void **)net->mib.net_statistics);
+       snmp_mib_free((void **)net->mib.ip_statistics);
+       snmp_mib_free((void **)net->mib.tcp_statistics);
+}
+
+static __net_initdata struct pernet_operations ipv4_mib_ops = {
+       .init = ipv4_mib_init_net,
+       .exit = ipv4_mib_exit_net,
+};
+
+static int __init init_ipv4_mibs(void)
+{
+       return register_pernet_subsys(&ipv4_mib_ops);
+}
+
 static int ipv4_proc_init(void);
 
 /*
@@ -1368,6 +1440,10 @@ static int __init inet_init(void)
 
        (void)sock_register(&inet_family_ops);
 
+#ifdef CONFIG_SYSCTL
+       ip_static_sysctl_init();
+#endif
+
        /*
         *      Add all the base protocols.
         */
@@ -1402,11 +1478,14 @@ static int __init inet_init(void)
 
        ip_init();
 
-       tcp_v4_init(&inet_family_ops);
+       tcp_v4_init();
 
        /* Setup TCP slab cache for open requests. */
        tcp_init();
 
+       /* Setup UDP memory threshold */
+       udp_init();
+
        /* Add UDP-Lite (RFC 3828) */
        udplite4_register();
 
@@ -1414,20 +1493,22 @@ static int __init inet_init(void)
         *      Set the ICMP layer up
         */
 
-       icmp_init(&inet_family_ops);
+       if (icmp_init() < 0)
+               panic("Failed to create the ICMP control socket.\n");
 
        /*
         *      Initialise the multicast router
         */
 #if defined(CONFIG_IP_MROUTE)
-       ip_mr_init();
+       if (ip_mr_init())
+               printk(KERN_CRIT "inet_init: Cannot init ipv4 mroute\n");
 #endif
        /*
         *      Initialise per-cpu ipv4 mibs
         */
 
        if (init_ipv4_mibs())
-               printk(KERN_CRIT "inet_init: Cannot init ipv4 mibs\n"); ;
+               printk(KERN_CRIT "inet_init: Cannot init ipv4 mibs\n");
 
        ipv4_proc_init();
 
@@ -1460,15 +1541,11 @@ static int __init ipv4_proc_init(void)
                goto out_tcp;
        if (udp4_proc_init())
                goto out_udp;
-       if (fib_proc_init())
-               goto out_fib;
        if (ip_misc_proc_init())
                goto out_misc;
 out:
        return rc;
 out_misc:
-       fib_proc_exit();
-out_fib:
        udp4_proc_exit();
 out_udp:
        tcp4_proc_exit();
@@ -1503,5 +1580,4 @@ EXPORT_SYMBOL(inet_sock_destruct);
 EXPORT_SYMBOL(inet_stream_connect);
 EXPORT_SYMBOL(inet_stream_ops);
 EXPORT_SYMBOL(inet_unregister_protosw);
-EXPORT_SYMBOL(net_statistics);
 EXPORT_SYMBOL(sysctl_ip_nonlocal_bind);