net: Compute protocol sequence numbers and fragment IDs using MD5, CVE-2011-3188
[linux-flexiantxendom0-natty.git] / net / dccp / ipv4.c
index baa268d..6006dff 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <linux/dccp.h>
 #include <linux/icmp.h>
+#include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/skbuff.h>
 #include <linux/random.h>
@@ -25,6 +26,7 @@
 #include <net/timewait_sock.h>
 #include <net/tcp_states.h>
 #include <net/xfrm.h>
+#include <net/secure_seq.h>
 
 #include "ackvec.h"
 #include "ccid.h"
@@ -32,7 +34,7 @@
 #include "feat.h"
 
 /*
- * The dccp_ctl_sk is the global socket data structure used for responding to
+ * The per-net dccp.v4_ctl_sk socket is used for responding to
  * the Out-of-the-blue (OOTB) packets. A control sock will be created
  * for this socket at the initialization time.
  */
@@ -62,10 +64,10 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
                nexthop = inet->opt->faddr;
        }
 
-       tmp = ip_route_connect(&rt, nexthop, inet->saddr,
+       tmp = ip_route_connect(&rt, nexthop, inet->inet_saddr,
                               RT_CONN_FLAGS(sk), sk->sk_bound_dev_if,
                               IPPROTO_DCCP,
-                              inet->sport, usin->sin_port, sk, 1);
+                              inet->inet_sport, usin->sin_port, sk, 1);
        if (tmp < 0)
                return tmp;
 
@@ -77,12 +79,12 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
        if (inet->opt == NULL || !inet->opt->srr)
                daddr = rt->rt_dst;
 
-       if (inet->saddr == 0)
-               inet->saddr = rt->rt_src;
-       inet->rcv_saddr = inet->saddr;
+       if (inet->inet_saddr == 0)
+               inet->inet_saddr = rt->rt_src;
+       inet->inet_rcv_saddr = inet->inet_saddr;
 
-       inet->dport = usin->sin_port;
-       inet->daddr = daddr;
+       inet->inet_dport = usin->sin_port;
+       inet->inet_daddr = daddr;
 
        inet_csk(sk)->icsk_ext_hdr_len = 0;
        if (inet->opt != NULL)
@@ -98,17 +100,19 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
        if (err != 0)
                goto failure;
 
-       err = ip_route_newports(&rt, IPPROTO_DCCP, inet->sport, inet->dport,
-                               sk);
+       err = ip_route_newports(&rt, IPPROTO_DCCP, inet->inet_sport,
+                               inet->inet_dport, sk);
        if (err != 0)
                goto failure;
 
        /* OK, now commit destination to socket.  */
-       sk_setup_caps(sk, &rt->u.dst);
+       sk_setup_caps(sk, &rt->dst);
 
-       dp->dccps_iss = secure_dccp_sequence_number(inet->saddr, inet->daddr,
-                                                   inet->sport, inet->dport);
-       inet->id = dp->dccps_iss ^ jiffies;
+       dp->dccps_iss = secure_dccp_sequence_number(inet->inet_saddr,
+                                                   inet->inet_daddr,
+                                                   inet->inet_sport,
+                                                   inet->inet_dport);
+       inet->inet_id = dp->dccps_iss ^ jiffies;
 
        err = dccp_connect(sk);
        rt = NULL;
@@ -123,7 +127,7 @@ failure:
        dccp_set_state(sk, DCCP_CLOSED);
        ip_rt_put(rt);
        sk->sk_route_caps = 0;
-       inet->dport = 0;
+       inet->inet_dport = 0;
        goto out;
 }
 
@@ -196,8 +200,8 @@ static inline void dccp_do_pmtu_discovery(struct sock *sk,
 static void dccp_v4_err(struct sk_buff *skb, u32 info)
 {
        const struct iphdr *iph = (struct iphdr *)skb->data;
-       const struct dccp_hdr *dh = (struct dccp_hdr *)(skb->data +
-                                                       (iph->ihl << 2));
+       const u8 offset = iph->ihl << 2;
+       const struct dccp_hdr *dh = (struct dccp_hdr *)(skb->data + offset);
        struct dccp_sock *dp;
        struct inet_sock *inet;
        const int type = icmp_hdr(skb)->type;
@@ -205,17 +209,19 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info)
        struct sock *sk;
        __u64 seq;
        int err;
+       struct net *net = dev_net(skb->dev);
 
-       if (skb->len < (iph->ihl << 2) + 8) {
-               ICMP_INC_STATS_BH(ICMP_MIB_INERRORS);
+       if (skb->len < offset + sizeof(*dh) ||
+           skb->len < offset + __dccp_basic_hdr_len(dh)) {
+               ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS);
                return;
        }
 
-       sk = inet_lookup(dev_net(skb->dev), &dccp_hashinfo,
+       sk = inet_lookup(net, &dccp_hashinfo,
                        iph->daddr, dh->dccph_dport,
                        iph->saddr, dh->dccph_sport, inet_iif(skb));
        if (sk == NULL) {
-               ICMP_INC_STATS_BH(ICMP_MIB_INERRORS);
+               ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS);
                return;
        }
 
@@ -229,7 +235,7 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info)
         * servers this needs to be solved differently.
         */
        if (sock_owned_by_user(sk))
-               NET_INC_STATS_BH(LINUX_MIB_LOCKDROPPEDICMPS);
+               NET_INC_STATS_BH(net, LINUX_MIB_LOCKDROPPEDICMPS);
 
        if (sk->sk_state == DCCP_CLOSED)
                goto out;
@@ -237,8 +243,8 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info)
        dp = dccp_sk(sk);
        seq = dccp_hdr_seq(dh);
        if ((1 << sk->sk_state) & ~(DCCPF_REQUESTING | DCCPF_LISTEN) &&
-           !between48(seq, dp->dccps_swl, dp->dccps_swh)) {
-               NET_INC_STATS_BH(LINUX_MIB_OUTOFWINDOWICMPS);
+           !between48(seq, dp->dccps_awl, dp->dccps_awh)) {
+               NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
                goto out;
        }
 
@@ -282,10 +288,10 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info)
                 * ICMPs are not backlogged, hence we cannot get an established
                 * socket here.
                 */
-               BUG_TRAP(!req->sk);
+               WARN_ON(req->sk);
 
                if (seq != dccp_rsk(req)->dreq_iss) {
-                       NET_INC_STATS_BH(LINUX_MIB_OUTOFWINDOWICMPS);
+                       NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
                        goto out;
                }
                /*
@@ -344,13 +350,15 @@ static inline __sum16 dccp_v4_csum_finish(struct sk_buff *skb,
        return csum_tcpudp_magic(src, dst, skb->len, IPPROTO_DCCP, skb->csum);
 }
 
-void dccp_v4_send_check(struct sock *sk, int unused, struct sk_buff *skb)
+void dccp_v4_send_check(struct sock *sk, struct sk_buff *skb)
 {
        const struct inet_sock *inet = inet_sk(sk);
        struct dccp_hdr *dh = dccp_hdr(skb);
 
        dccp_csum_outgoing(skb);
-       dh->dccph_checksum = dccp_v4_csum_finish(skb, inet->saddr, inet->daddr);
+       dh->dccph_checksum = dccp_v4_csum_finish(skb,
+                                                inet->inet_saddr,
+                                                inet->inet_daddr);
 }
 
 EXPORT_SYMBOL_GPL(dccp_v4_send_check);
@@ -385,33 +393,37 @@ struct sock *dccp_v4_request_recv_sock(struct sock *sk, struct sk_buff *skb,
 
        newsk = dccp_create_openreq_child(sk, req, skb);
        if (newsk == NULL)
-               goto exit;
+               goto exit_nonewsk;
 
        sk_setup_caps(newsk, dst);
 
        newinet            = inet_sk(newsk);
        ireq               = inet_rsk(req);
-       newinet->daddr     = ireq->rmt_addr;
-       newinet->rcv_saddr = ireq->loc_addr;
-       newinet->saddr     = ireq->loc_addr;
+       newinet->inet_daddr     = ireq->rmt_addr;
+       newinet->inet_rcv_saddr = ireq->loc_addr;
+       newinet->inet_saddr     = ireq->loc_addr;
        newinet->opt       = ireq->opt;
        ireq->opt          = NULL;
        newinet->mc_index  = inet_iif(skb);
        newinet->mc_ttl    = ip_hdr(skb)->ttl;
-       newinet->id        = jiffies;
+       newinet->inet_id   = jiffies;
 
        dccp_sync_mss(newsk, dst_mtu(dst));
 
-       __inet_hash_nolisten(newsk);
-       __inet_inherit_port(sk, newsk);
+       if (__inet_inherit_port(sk, newsk) < 0) {
+               sock_put(newsk);
+               goto exit;
+       }
+       __inet_hash_nolisten(newsk, NULL);
 
        return newsk;
 
 exit_overflow:
-       NET_INC_STATS_BH(LINUX_MIB_LISTENOVERFLOWS);
-exit:
-       NET_INC_STATS_BH(LINUX_MIB_LISTENDROPS);
+       NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
+exit_nonewsk:
        dst_release(dst);
+exit:
+       NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS);
        return NULL;
 }
 
@@ -450,28 +462,26 @@ static struct dst_entry* dccp_v4_route_skb(struct net *net, struct sock *sk,
                                           struct sk_buff *skb)
 {
        struct rtable *rt;
-       struct flowi fl = { .oif = skb->rtable->rt_iif,
-                           .nl_u = { .ip4_u =
-                                     { .daddr = ip_hdr(skb)->saddr,
-                                       .saddr = ip_hdr(skb)->daddr,
-                                       .tos = RT_CONN_FLAGS(sk) } },
+       struct flowi fl = { .oif = skb_rtable(skb)->rt_iif,
+                           .fl4_dst = ip_hdr(skb)->saddr,
+                           .fl4_src = ip_hdr(skb)->daddr,
+                           .fl4_tos = RT_CONN_FLAGS(sk),
                            .proto = sk->sk_protocol,
-                           .uli_u = { .ports =
-                                      { .sport = dccp_hdr(skb)->dccph_dport,
-                                        .dport = dccp_hdr(skb)->dccph_sport }
-                                    }
+                           .fl_ip_sport = dccp_hdr(skb)->dccph_dport,
+                           .fl_ip_dport = dccp_hdr(skb)->dccph_sport
                          };
 
        security_skb_classify_flow(skb, &fl);
        if (ip_route_output_flow(net, &rt, &fl, sk, 0)) {
-               IP_INC_STATS_BH(IPSTATS_MIB_OUTNOROUTES);
+               IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES);
                return NULL;
        }
 
-       return &rt->u.dst;
+       return &rt->dst;
 }
 
-static int dccp_v4_send_response(struct sock *sk, struct request_sock *req)
+static int dccp_v4_send_response(struct sock *sk, struct request_sock *req,
+                                struct request_values *rv_unused)
 {
        int err = -1;
        struct sk_buff *skb;
@@ -505,14 +515,14 @@ static void dccp_v4_ctl_send_reset(struct sock *sk, struct sk_buff *rxskb)
        const struct iphdr *rxiph;
        struct sk_buff *skb;
        struct dst_entry *dst;
-       struct net *net = dev_net(rxskb->dst->dev);
+       struct net *net = dev_net(skb_dst(rxskb)->dev);
        struct sock *ctl_sk = net->dccp.v4_ctl_sk;
 
        /* Never send a reset in response to a reset. */
        if (dccp_hdr(rxskb)->dccph_type == DCCP_PKT_RESET)
                return;
 
-       if (rxskb->rtable->rt_type != RTN_LOCAL)
+       if (skb_rtable(rxskb)->rt_type != RTN_LOCAL)
                return;
 
        dst = dccp_v4_route_skb(net, ctl_sk, rxskb);
@@ -526,7 +536,7 @@ static void dccp_v4_ctl_send_reset(struct sock *sk, struct sk_buff *rxskb)
        rxiph = ip_hdr(rxskb);
        dccp_hdr(skb)->dccph_checksum = dccp_v4_csum_finish(skb, rxiph->saddr,
                                                                 rxiph->daddr);
-       skb->dst = dst_clone(dst);
+       skb_dst_set(skb, dst_clone(dst));
 
        bh_lock_sock(ctl_sk);
        err = ip_build_and_send_pkt(skb, ctl_sk,
@@ -543,6 +553,7 @@ out:
 
 static void dccp_v4_reqsk_destructor(struct request_sock *req)
 {
+       dccp_feat_list_purge(&dccp_rsk(req)->dreq_featneg);
        kfree(inet_rsk(req)->opt);
 }
 
@@ -564,7 +575,7 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
        struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb);
 
        /* Never answer to DCCP_PKT_REQUESTs send to broadcast or multicast */
-       if (skb->rtable->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
+       if (skb_rtable(skb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
                return 0;       /* discard, don't send a reset here */
 
        if (dccp_bad_service_code(sk, service)) {
@@ -589,11 +600,12 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
        if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
                goto drop;
 
-       req = reqsk_alloc(&dccp_request_sock_ops);
+       req = inet_reqsk_alloc(&dccp_request_sock_ops);
        if (req == NULL)
                goto drop;
 
-       dccp_reqsk_init(req, skb);
+       if (dccp_reqsk_init(req, dccp_sk(sk), skb))
+               goto drop_and_free;
 
        dreq = dccp_rsk(req);
        if (dccp_parse_options(sk, dreq, skb))
@@ -605,7 +617,6 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
        ireq = inet_rsk(req);
        ireq->loc_addr = ip_hdr(skb)->daddr;
        ireq->rmt_addr = ip_hdr(skb)->saddr;
-       ireq->opt       = NULL;
 
        /*
         * Step 3: Process LISTEN state
@@ -619,7 +630,7 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
        dreq->dreq_iss     = dccp_v4_init_sequence(skb);
        dreq->dreq_service = service;
 
-       if (dccp_v4_send_response(sk, req))
+       if (dccp_v4_send_response(sk, req, NULL))
                goto drop_and_free;
 
        inet_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT);
@@ -739,8 +750,8 @@ int dccp_invalid_packet(struct sk_buff *skb)
         * If P.type is not Data, Ack, or DataAck and P.X == 0 (the packet
         * has short sequence numbers), drop packet and return
         */
-       if (dh->dccph_type >= DCCP_PKT_DATA    &&
-           dh->dccph_type <= DCCP_PKT_DATAACK && dh->dccph_x == 0)  {
+       if ((dh->dccph_type < DCCP_PKT_DATA    ||
+           dh->dccph_type > DCCP_PKT_DATAACK) && dh->dccph_x == 0)  {
                DCCP_WARN("P.type (%s) not Data || [Data]Ack, while P.X == 0\n",
                          dccp_packet_name(dh->dccph_type));
                return 1;
@@ -791,12 +802,10 @@ static int dccp_v4_rcv(struct sk_buff *skb)
        DCCP_SKB_CB(skb)->dccpd_seq  = dccp_hdr_seq(dh);
        DCCP_SKB_CB(skb)->dccpd_type = dh->dccph_type;
 
-       dccp_pr_debug("%8.8s "
-                     "src=%u.%u.%u.%u@%-5d "
-                     "dst=%u.%u.%u.%u@%-5d seq=%llu",
+       dccp_pr_debug("%8.8s src=%pI4@%-5d dst=%pI4@%-5d seq=%llu",
                      dccp_packet_name(dh->dccph_type),
-                     NIPQUAD(iph->saddr), ntohs(dh->dccph_sport),
-                     NIPQUAD(iph->daddr), ntohs(dh->dccph_dport),
+                     &iph->saddr, ntohs(dh->dccph_sport),
+                     &iph->daddr, ntohs(dh->dccph_dport),
                      (unsigned long long) DCCP_SKB_CB(skb)->dccpd_seq);
 
        if (dccp_packet_without_ack(skb)) {
@@ -810,9 +819,8 @@ static int dccp_v4_rcv(struct sk_buff *skb)
 
        /* Step 2:
         *      Look up flow ID in table and get corresponding socket */
-       sk = __inet_lookup(dev_net(skb->dst->dev), &dccp_hashinfo,
-                          iph->saddr, dh->dccph_sport,
-                          iph->daddr, dh->dccph_dport, inet_iif(skb));
+       sk = __inet_lookup_skb(&dccp_hashinfo, skb,
+                              dh->dccph_sport, dh->dccph_dport);
        /*
         * Step 2:
         *      If no socket ...
@@ -880,7 +888,7 @@ discard_and_relse:
        goto discard_it;
 }
 
-static struct inet_connection_sock_af_ops dccp_ipv4_af_ops = {
+static const struct inet_connection_sock_af_ops dccp_ipv4_af_ops = {
        .queue_xmit        = ip_queue_xmit,
        .send_check        = dccp_v4_send_check,
        .rebuild_header    = inet_sk_rebuild_header,
@@ -938,6 +946,7 @@ static struct proto dccp_v4_prot = {
        .orphan_count           = &dccp_orphan_count,
        .max_header             = MAX_DCCP_HEADER,
        .obj_size               = sizeof(struct dccp_sock),
+       .slab_flags             = SLAB_DESTROY_BY_RCU,
        .rsk_prot               = &dccp_request_sock_ops,
        .twsk_prot              = &dccp_timewait_sock_ops,
        .h.hashinfo             = &dccp_hashinfo,
@@ -947,7 +956,7 @@ static struct proto dccp_v4_prot = {
 #endif
 };
 
-static struct net_protocol dccp_v4_protocol = {
+static const struct net_protocol dccp_v4_protocol = {
        .handler        = dccp_v4_rcv,
        .err_handler    = dccp_v4_err,
        .no_policy      = 1,
@@ -986,21 +995,20 @@ static struct inet_protosw dccp_v4_protosw = {
        .protocol       = IPPROTO_DCCP,
        .prot           = &dccp_v4_prot,
        .ops            = &inet_dccp_ops,
-       .capability     = -1,
        .no_check       = 0,
        .flags          = INET_PROTOSW_ICSK,
 };
 
-static int dccp_v4_init_net(struct net *net)
+static int __net_init dccp_v4_init_net(struct net *net)
 {
-       int err;
+       if (dccp_hashinfo.bhash == NULL)
+               return -ESOCKTNOSUPPORT;
 
-       err = inet_ctl_sock_create(&net->dccp.v4_ctl_sk, PF_INET,
-                                  SOCK_DCCP, IPPROTO_DCCP, net);
-       return err;
+       return inet_ctl_sock_create(&net->dccp.v4_ctl_sk, PF_INET,
+                                   SOCK_DCCP, IPPROTO_DCCP, net);
 }
 
-static void dccp_v4_exit_net(struct net *net)
+static void __net_exit dccp_v4_exit_net(struct net *net)
 {
        inet_ctl_sock_destroy(net->dccp.v4_ctl_sk);
 }