Use sk_mark for IPv6 routing lookups
[safe/jmp/linux-2.6] / net / ipv6 / af_inet6.c
index bd91ead..da36497 100644 (file)
@@ -72,6 +72,22 @@ MODULE_LICENSE("GPL");
 static struct list_head inetsw6[SOCK_MAX];
 static DEFINE_SPINLOCK(inetsw6_lock);
 
+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)
 {
        const int offset = sk->sk_prot->obj_size - sizeof(struct ipv6_pinfo);
@@ -272,8 +288,25 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 
        /* Check if the address belongs to the host. */
        if (addr_type == IPV6_ADDR_MAPPED) {
+               int chk_addr_ret;
+
+               /* Binding to v4-mapped address on a v6-only socket
+                * makes no sense
+                */
+               if (np->ipv6only) {
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               /* Reproduce AF_INET checks to make the bindings consitant */
                v4addr = addr->sin6_addr.s6_addr32[3];
-               if (inet_addr_type(net, v4addr) != RTN_LOCAL) {
+               chk_addr_ret = inet_addr_type(net, v4addr);
+               if (!sysctl_ip_nonlocal_bind &&
+                   !(inet->freebind || inet->transparent) &&
+                   v4addr != htonl(INADDR_ANY) &&
+                   chk_addr_ret != RTN_LOCAL &&
+                   chk_addr_ret != RTN_MULTICAST &&
+                   chk_addr_ret != RTN_BROADCAST) {
                        err = -EADDRNOTAVAIL;
                        goto out;
                }
@@ -335,8 +368,11 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
                goto out;
        }
 
-       if (addr_type != IPV6_ADDR_ANY)
+       if (addr_type != IPV6_ADDR_ANY) {
                sk->sk_userlocks |= SOCK_BINDADDR_LOCK;
+               if (addr_type != IPV6_ADDR_MAPPED)
+                       np->ipv6only = 1;
+       }
        if (snum)
                sk->sk_userlocks |= SOCK_BINDPORT_LOCK;
        inet->sport = htons(inet->num);
@@ -618,6 +654,7 @@ 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.mark = sk->sk_mark;
                fl.fl_ip_dport = inet->dport;
                fl.fl_ip_sport = inet->sport;
                security_sk_classify_flow(sk, &fl);
@@ -674,7 +711,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;
@@ -709,7 +746,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))))
@@ -737,7 +774,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;
@@ -757,10 +799,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);
@@ -774,6 +815,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:
@@ -790,18 +841,25 @@ 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;
        unsigned int nlen;
+       unsigned int hlen;
+       unsigned int off;
        int flush = 1;
        int proto;
        __wsum csum;
 
-       iph = skb_gro_header(skb, sizeof(*iph));
-       if (unlikely(!iph))
-               goto out;
+       off = skb_gro_offset(skb);
+       hlen = off + sizeof(*iph);
+       iph = skb_gro_header_fast(skb, off);
+       if (skb_gro_header_hard(skb, hlen)) {
+               iph = skb_gro_header_slow(skb, hlen, off);
+               if (unlikely(!iph))
+                       goto out;
+       }
 
        skb_gro_pull(skb, sizeof(*iph));
        skb_set_transport_header(skb, skb_gro_offset(skb));
@@ -869,7 +927,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;
 
@@ -889,8 +947,8 @@ out_unlock:
        return err;
 }
 
-static struct packet_type ipv6_packet_type = {
-       .type = __constant_htons(ETH_P_IPV6),
+static struct packet_type ipv6_packet_type __read_mostly = {
+       .type = cpu_to_be16(ETH_P_IPV6),
        .func = ipv6_rcv,
        .gso_send_check = ipv6_gso_send_check,
        .gso_segment = ipv6_gso_segment,
@@ -1001,10 +1059,21 @@ static int __init inet6_init(void)
 {
        struct sk_buff *dummy_skb;
        struct list_head *r;
-       int err;
+       int err = 0;
 
        BUILD_BUG_ON(sizeof(struct inet6_skb_parm) > sizeof(dummy_skb->cb));
 
+       /* Register the socket-side information for inet6_create.  */
+       for(r = &inetsw6[0]; r < &inetsw6[SOCK_MAX]; ++r)
+               INIT_LIST_HEAD(r);
+
+       if (disable_ipv6_mod) {
+               printk(KERN_INFO
+                      "IPv6: Loaded, but administratively disabled, "
+                      "reboot required to enable\n");
+               goto out;
+       }
+
        err = proto_register(&tcpv6_prot, 1);
        if (err)
                goto out;
@@ -1022,10 +1091,6 @@ static int __init inet6_init(void)
                goto out_unregister_udplite_proto;
 
 
-       /* Register the socket-side information for inet6_create.  */
-       for(r = &inetsw6[0]; r < &inetsw6[SOCK_MAX]; ++r)
-               INIT_LIST_HEAD(r);
-
        /* We MUST register RAW sockets before we create the ICMP6,
         * IGMP6, or NDISC control sockets.
         */
@@ -1191,6 +1256,9 @@ module_init(inet6_init);
 
 static void __exit inet6_exit(void)
 {
+       if (disable_ipv6_mod)
+               return;
+
        /* First of all disallow new sockets creation. */
        sock_unregister(PF_INET6);
        /* Disallow any further netlink messages */
@@ -1233,6 +1301,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);