ALSA: sound/usb: add preliminary support for UAC2 interrupts
[safe/jmp/linux-2.6] / net / ipv6 / af_inet6.c
index b6215be..37d14e7 100644 (file)
@@ -72,9 +72,21 @@ MODULE_LICENSE("GPL");
 static struct list_head inetsw6[SOCK_MAX];
 static DEFINE_SPINLOCK(inetsw6_lock);
 
-static int disable_ipv6 = 0;
-module_param_named(disable, disable_ipv6, int, 0);
-MODULE_PARM_DESC(disable, "Disable IPv6 such that it is non-functional");
+struct ipv6_params ipv6_defaults = {
+       .disable_ipv6 = 0,
+       .autoconf = 1,
+};
+
+static int disable_ipv6_mod = 0;
+
+module_param_named(disable, disable_ipv6_mod, int, 0444);
+MODULE_PARM_DESC(disable, "Disable IPv6 module such that it is non-functional");
+
+module_param_named(disable_ipv6, ipv6_defaults.disable_ipv6, int, 0444);
+MODULE_PARM_DESC(disable_ipv6, "Disable IPv6 on all interfaces");
+
+module_param_named(autoconf, ipv6_defaults.autoconf, int, 0444);
+MODULE_PARM_DESC(autoconf, "Enable IPv6 address autoconfiguration on all interfaces");
 
 static __inline__ struct ipv6_pinfo *inet6_sk_generic(struct sock *sk)
 {
@@ -83,7 +95,8 @@ static __inline__ struct ipv6_pinfo *inet6_sk_generic(struct sock *sk)
        return (struct ipv6_pinfo *)(((u8 *)sk) + offset);
 }
 
-static int inet6_create(struct net *net, struct socket *sock, int protocol)
+static int inet6_create(struct net *net, struct socket *sock, int protocol,
+                       int kern)
 {
        struct inet_sock *inet;
        struct ipv6_pinfo *np;
@@ -146,7 +159,7 @@ lookup_protocol:
        }
 
        err = -EPERM;
-       if (answer->capability > 0 && !capable(answer->capability))
+       if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW))
                goto out_rcu_unlock;
 
        sock->ops = answer->ops;
@@ -173,7 +186,7 @@ lookup_protocol:
        inet->is_icsk = (INET_PROTOSW_ICSK & answer_flags) != 0;
 
        if (SOCK_RAW == sock->type) {
-               inet->num = protocol;
+               inet->inet_num = protocol;
                if (IPPROTO_RAW == protocol)
                        inet->hdrincl = 1;
        }
@@ -216,12 +229,12 @@ lookup_protocol:
         */
        sk_refcnt_debug_inc(sk);
 
-       if (inet->num) {
+       if (inet->inet_num) {
                /* It assumes that any protocol which allows
                 * the user to assign a number at socket
                 * creation time automatically shares.
                 */
-               inet->sport = htons(inet->num);
+               inet->inet_sport = htons(inet->inet_num);
                sk->sk_prot->hash(sk);
        }
        if (sk->sk_prot->init) {
@@ -269,7 +282,7 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
        lock_sock(sk);
 
        /* Check these errors (active socket, double bind). */
-       if (sk->sk_state != TCP_CLOSE || inet->num) {
+       if (sk->sk_state != TCP_CLOSE || inet->inet_num) {
                err = -EINVAL;
                goto out;
        }
@@ -294,12 +307,15 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
                    v4addr != htonl(INADDR_ANY) &&
                    chk_addr_ret != RTN_LOCAL &&
                    chk_addr_ret != RTN_MULTICAST &&
-                   chk_addr_ret != RTN_BROADCAST)
+                   chk_addr_ret != RTN_BROADCAST) {
+                       err = -EADDRNOTAVAIL;
                        goto out;
+               }
        } else {
                if (addr_type != IPV6_ADDR_ANY) {
                        struct net_device *dev = NULL;
 
+                       rcu_read_lock();
                        if (addr_type & IPV6_ADDR_LINKLOCAL) {
                                if (addr_len >= sizeof(struct sockaddr_in6) &&
                                    addr->sin6_scope_id) {
@@ -312,12 +328,12 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
                                /* Binding to link-local address requires an interface */
                                if (!sk->sk_bound_dev_if) {
                                        err = -EINVAL;
-                                       goto out;
+                                       goto out_unlock;
                                }
-                               dev = dev_get_by_index(net, sk->sk_bound_dev_if);
+                               dev = dev_get_by_index_rcu(net, sk->sk_bound_dev_if);
                                if (!dev) {
                                        err = -ENODEV;
-                                       goto out;
+                                       goto out_unlock;
                                }
                        }
 
@@ -328,19 +344,16 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
                        if (!(addr_type & IPV6_ADDR_MULTICAST)) {
                                if (!ipv6_chk_addr(net, &addr->sin6_addr,
                                                   dev, 0)) {
-                                       if (dev)
-                                               dev_put(dev);
                                        err = -EADDRNOTAVAIL;
-                                       goto out;
+                                       goto out_unlock;
                                }
                        }
-                       if (dev)
-                               dev_put(dev);
+                       rcu_read_unlock();
                }
        }
 
-       inet->rcv_saddr = v4addr;
-       inet->saddr = v4addr;
+       inet->inet_rcv_saddr = v4addr;
+       inet->inet_saddr = v4addr;
 
        ipv6_addr_copy(&np->rcv_saddr, &addr->sin6_addr);
 
@@ -361,12 +374,15 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
        }
        if (snum)
                sk->sk_userlocks |= SOCK_BINDPORT_LOCK;
-       inet->sport = htons(inet->num);
-       inet->dport = 0;
-       inet->daddr = 0;
+       inet->inet_sport = htons(inet->inet_num);
+       inet->inet_dport = 0;
+       inet->inet_daddr = 0;
 out:
        release_sock(sk);
        return err;
+out_unlock:
+       rcu_read_unlock();
+       goto out;
 }
 
 EXPORT_SYMBOL(inet6_bind);
@@ -427,12 +443,12 @@ int inet6_getname(struct socket *sock, struct sockaddr *uaddr,
        sin->sin6_flowinfo = 0;
        sin->sin6_scope_id = 0;
        if (peer) {
-               if (!inet->dport)
+               if (!inet->inet_dport)
                        return -ENOTCONN;
                if (((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_SYN_SENT)) &&
                    peer == 1)
                        return -ENOTCONN;
-               sin->sin6_port = inet->dport;
+               sin->sin6_port = inet->inet_dport;
                ipv6_addr_copy(&sin->sin6_addr, &np->daddr);
                if (np->sndflow)
                        sin->sin6_flowinfo = np->flow_label;
@@ -442,7 +458,7 @@ int inet6_getname(struct socket *sock, struct sockaddr *uaddr,
                else
                        ipv6_addr_copy(&sin->sin6_addr, &np->rcv_saddr);
 
-               sin->sin6_port = inet->sport;
+               sin->sin6_port = inet->inet_sport;
        }
        if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL)
                sin->sin6_scope_id = sk->sk_bound_dev_if;
@@ -538,7 +554,7 @@ const struct proto_ops inet6_dgram_ops = {
 #endif
 };
 
-static struct net_proto_family inet6_family_ops = {
+static const struct net_proto_family inet6_family_ops = {
        .family = PF_INET6,
        .create = inet6_create,
        .owner  = THIS_MODULE,
@@ -640,8 +656,9 @@ int inet6_sk_rebuild_header(struct sock *sk)
                ipv6_addr_copy(&fl.fl6_src, &np->saddr);
                fl.fl6_flowlabel = np->flow_label;
                fl.oif = sk->sk_bound_dev_if;
-               fl.fl_ip_dport = inet->dport;
-               fl.fl_ip_sport = inet->sport;
+               fl.mark = sk->sk_mark;
+               fl.fl_ip_dport = inet->inet_dport;
+               fl.fl_ip_sport = inet->inet_sport;
                security_sk_classify_flow(sk, &fl);
 
                if (np->opt && np->opt->srcrt) {
@@ -696,7 +713,7 @@ EXPORT_SYMBOL_GPL(ipv6_opt_accepted);
 
 static int ipv6_gso_pull_exthdrs(struct sk_buff *skb, int proto)
 {
-       struct inet6_protocol *ops = NULL;
+       const struct inet6_protocol *ops = NULL;
 
        for (;;) {
                struct ipv6_opt_hdr *opth;
@@ -731,7 +748,7 @@ static int ipv6_gso_pull_exthdrs(struct sk_buff *skb, int proto)
 static int ipv6_gso_send_check(struct sk_buff *skb)
 {
        struct ipv6hdr *ipv6h;
-       struct inet6_protocol *ops;
+       const struct inet6_protocol *ops;
        int err = -EINVAL;
 
        if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
@@ -759,7 +776,12 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features)
 {
        struct sk_buff *segs = ERR_PTR(-EINVAL);
        struct ipv6hdr *ipv6h;
-       struct inet6_protocol *ops;
+       const struct inet6_protocol *ops;
+       int proto;
+       struct frag_hdr *fptr;
+       unsigned int unfrag_ip6hlen;
+       u8 *prevhdr;
+       int offset = 0;
 
        if (!(features & NETIF_F_V6_CSUM))
                features &= ~NETIF_F_SG;
@@ -779,10 +801,9 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features)
        __skb_pull(skb, sizeof(*ipv6h));
        segs = ERR_PTR(-EPROTONOSUPPORT);
 
+       proto = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr);
        rcu_read_lock();
-       ops = rcu_dereference(inet6_protos[
-               ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr)]);
-
+       ops = rcu_dereference(inet6_protos[proto]);
        if (likely(ops && ops->gso_segment)) {
                skb_reset_transport_header(skb);
                segs = ops->gso_segment(skb, features);
@@ -796,6 +817,16 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features)
                ipv6h = ipv6_hdr(skb);
                ipv6h->payload_len = htons(skb->len - skb->mac_len -
                                           sizeof(*ipv6h));
+               if (proto == IPPROTO_UDP) {
+                       unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
+                       fptr = (struct frag_hdr *)(skb_network_header(skb) +
+                               unfrag_ip6hlen);
+                       fptr->frag_off = htons(offset);
+                       if (skb->next != NULL)
+                               fptr->frag_off |= htons(IP6_MF);
+                       offset += (ntohs(ipv6h->payload_len) -
+                                  sizeof(struct frag_hdr));
+               }
        }
 
 out:
@@ -812,7 +843,7 @@ struct ipv6_gro_cb {
 static struct sk_buff **ipv6_gro_receive(struct sk_buff **head,
                                         struct sk_buff *skb)
 {
-       struct inet6_protocol *ops;
+       const struct inet6_protocol *ops;
        struct sk_buff **pp = NULL;
        struct sk_buff *p;
        struct ipv6hdr *iph;
@@ -898,7 +929,7 @@ out:
 
 static int ipv6_gro_complete(struct sk_buff *skb)
 {
-       struct inet6_protocol *ops;
+       const struct inet6_protocol *ops;
        struct ipv6hdr *iph = ipv6_hdr(skb);
        int err = -ENOSYS;
 
@@ -940,41 +971,41 @@ static void ipv6_packet_cleanup(void)
 
 static int __net_init ipv6_init_mibs(struct net *net)
 {
-       if (snmp_mib_init((void **)net->mib.udp_stats_in6,
+       if (snmp_mib_init((void __percpu **)net->mib.udp_stats_in6,
                          sizeof (struct udp_mib)) < 0)
                return -ENOMEM;
-       if (snmp_mib_init((void **)net->mib.udplite_stats_in6,
+       if (snmp_mib_init((void __percpu **)net->mib.udplite_stats_in6,
                          sizeof (struct udp_mib)) < 0)
                goto err_udplite_mib;
-       if (snmp_mib_init((void **)net->mib.ipv6_statistics,
+       if (snmp_mib_init((void __percpu **)net->mib.ipv6_statistics,
                          sizeof(struct ipstats_mib)) < 0)
                goto err_ip_mib;
-       if (snmp_mib_init((void **)net->mib.icmpv6_statistics,
+       if (snmp_mib_init((void __percpu **)net->mib.icmpv6_statistics,
                          sizeof(struct icmpv6_mib)) < 0)
                goto err_icmp_mib;
-       if (snmp_mib_init((void **)net->mib.icmpv6msg_statistics,
+       if (snmp_mib_init((void __percpu **)net->mib.icmpv6msg_statistics,
                          sizeof(struct icmpv6msg_mib)) < 0)
                goto err_icmpmsg_mib;
        return 0;
 
 err_icmpmsg_mib:
-       snmp_mib_free((void **)net->mib.icmpv6_statistics);
+       snmp_mib_free((void __percpu **)net->mib.icmpv6_statistics);
 err_icmp_mib:
-       snmp_mib_free((void **)net->mib.ipv6_statistics);
+       snmp_mib_free((void __percpu **)net->mib.ipv6_statistics);
 err_ip_mib:
-       snmp_mib_free((void **)net->mib.udplite_stats_in6);
+       snmp_mib_free((void __percpu **)net->mib.udplite_stats_in6);
 err_udplite_mib:
-       snmp_mib_free((void **)net->mib.udp_stats_in6);
+       snmp_mib_free((void __percpu **)net->mib.udp_stats_in6);
        return -ENOMEM;
 }
 
-static void __net_exit ipv6_cleanup_mibs(struct net *net)
+static void ipv6_cleanup_mibs(struct net *net)
 {
-       snmp_mib_free((void **)net->mib.udp_stats_in6);
-       snmp_mib_free((void **)net->mib.udplite_stats_in6);
-       snmp_mib_free((void **)net->mib.ipv6_statistics);
-       snmp_mib_free((void **)net->mib.icmpv6_statistics);
-       snmp_mib_free((void **)net->mib.icmpv6msg_statistics);
+       snmp_mib_free((void __percpu **)net->mib.udp_stats_in6);
+       snmp_mib_free((void __percpu **)net->mib.udplite_stats_in6);
+       snmp_mib_free((void __percpu **)net->mib.ipv6_statistics);
+       snmp_mib_free((void __percpu **)net->mib.icmpv6_statistics);
+       snmp_mib_free((void __percpu **)net->mib.icmpv6msg_statistics);
 }
 
 static int __net_init inet6_net_init(struct net *net)
@@ -1011,7 +1042,7 @@ out:
 #endif
 }
 
-static void inet6_net_exit(struct net *net)
+static void __net_exit inet6_net_exit(struct net *net)
 {
 #ifdef CONFIG_PROC_FS
        udp6_proc_exit(net);
@@ -1038,7 +1069,7 @@ static int __init inet6_init(void)
        for(r = &inetsw6[0]; r < &inetsw6[SOCK_MAX]; ++r)
                INIT_LIST_HEAD(r);
 
-       if (disable_ipv6) {
+       if (disable_ipv6_mod) {
                printk(KERN_INFO
                       "IPv6: Loaded, but administratively disabled, "
                       "reboot required to enable\n");
@@ -1227,7 +1258,7 @@ module_init(inet6_init);
 
 static void __exit inet6_exit(void)
 {
-       if (disable_ipv6)
+       if (disable_ipv6_mod)
                return;
 
        /* First of all disallow new sockets creation. */
@@ -1272,6 +1303,8 @@ static void __exit inet6_exit(void)
        proto_unregister(&udplitev6_prot);
        proto_unregister(&udpv6_prot);
        proto_unregister(&tcpv6_prot);
+
+       rcu_barrier(); /* Wait for completion of call_rcu()'s */
 }
 module_exit(inet6_exit);