]> err.no Git - linux-2.6/blobdiff - net/dccp/options.c
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[linux-2.6] / net / dccp / options.c
index f496d4dc7efcf760fe1d67f210da4431939643e2..43bc24e761d0e707fca63a4404e061c270a071bc 100644 (file)
@@ -107,9 +107,11 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq,
                 *
                 * CCID-specific options are ignored during connection setup, as
                 * negotiation may still be in progress (see RFC 4340, 10.3).
+                * The same applies to Ack Vectors, as these depend on the CCID.
                 *
                 */
-               if (dreq != NULL && opt >= 128)
+               if (dreq != NULL && (opt >= 128 ||
+                   opt == DCCPO_ACK_VECTOR_0 || opt == DCCPO_ACK_VECTOR_1))
                        goto ignore_option;
 
                switch (opt) {
@@ -132,6 +134,8 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq,
                case DCCPO_CHANGE_L:
                        /* fall through */
                case DCCPO_CHANGE_R:
+                       if (pkt_type == DCCP_PKT_DATA)
+                               break;
                        if (len < 2)
                                goto out_invalid_option;
                        rc = dccp_feat_change_recv(sk, opt, *value, value + 1,
@@ -148,7 +152,9 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq,
                case DCCPO_CONFIRM_L:
                        /* fall through */
                case DCCPO_CONFIRM_R:
-                       if (len < 2)
+                       if (pkt_type == DCCP_PKT_DATA)
+                               break;
+                       if (len < 2)    /* FIXME this disallows empty confirm */
                                goto out_invalid_option;
                        if (dccp_feat_confirm_recv(sk, opt, *value,
                                                   value + 1, len - 1))
@@ -166,16 +172,27 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq,
                case DCCPO_TIMESTAMP:
                        if (len != 4)
                                goto out_invalid_option;
-
+                       /*
+                        * RFC 4340 13.1: "The precise time corresponding to
+                        * Timestamp Value zero is not specified". We use
+                        * zero to indicate absence of a meaningful timestamp.
+                        */
                        opt_val = get_unaligned((__be32 *)value);
-                       opt_recv->dccpor_timestamp = ntohl(opt_val);
-
-                       /* FIXME: if dreq != NULL, don't store this on listening socket */
-                       dp->dccps_timestamp_echo = opt_recv->dccpor_timestamp;
-                       dp->dccps_timestamp_time = ktime_get_real();
+                       if (unlikely(opt_val == 0)) {
+                               DCCP_WARN("Timestamp with zero value\n");
+                               break;
+                       }
 
+                       if (dreq != NULL) {
+                               dreq->dreq_timestamp_echo = ntohl(opt_val);
+                               dreq->dreq_timestamp_time = dccp_timestamp();
+                       } else {
+                               opt_recv->dccpor_timestamp =
+                                       dp->dccps_timestamp_echo = ntohl(opt_val);
+                               dp->dccps_timestamp_time = dccp_timestamp();
+                       }
                        dccp_pr_debug("%s rx opt: TIMESTAMP=%u, ackno=%llu\n",
-                                     dccp_role(sk), opt_recv->dccpor_timestamp,
+                                     dccp_role(sk), ntohl(opt_val),
                                      (unsigned long long)
                                      DCCP_SKB_CB(skb)->dccpd_ack_seq);
                        break;
@@ -393,16 +410,24 @@ int dccp_insert_option_timestamp(struct sock *sk, struct sk_buff *skb)
 
 EXPORT_SYMBOL_GPL(dccp_insert_option_timestamp);
 
-static int dccp_insert_option_timestamp_echo(struct sock *sk,
+static int dccp_insert_option_timestamp_echo(struct dccp_sock *dp,
+                                            struct dccp_request_sock *dreq,
                                             struct sk_buff *skb)
 {
-       struct dccp_sock *dp = dccp_sk(sk);
        __be32 tstamp_echo;
-       int len, elapsed_time_len;
        unsigned char *to;
-       const suseconds_t delta = ktime_us_delta(ktime_get_real(),
-                                                dp->dccps_timestamp_time);
-       u32 elapsed_time = delta / 10;
+       u32 elapsed_time, elapsed_time_len, len;
+
+       if (dreq != NULL) {
+               elapsed_time = dccp_timestamp() - dreq->dreq_timestamp_time;
+               tstamp_echo  = htonl(dreq->dreq_timestamp_echo);
+               dreq->dreq_timestamp_echo = 0;
+       } else {
+               elapsed_time = dccp_timestamp() - dp->dccps_timestamp_time;
+               tstamp_echo  = htonl(dp->dccps_timestamp_echo);
+               dp->dccps_timestamp_echo = 0;
+       }
+
        elapsed_time_len = dccp_elapsed_time_len(elapsed_time);
        len = 6 + elapsed_time_len;
 
@@ -415,7 +440,6 @@ static int dccp_insert_option_timestamp_echo(struct sock *sk,
        *to++ = DCCPO_TIMESTAMP_ECHO;
        *to++ = len;
 
-       tstamp_echo = htonl(dp->dccps_timestamp_echo);
        memcpy(to, &tstamp_echo, 4);
        to += 4;
 
@@ -427,8 +451,6 @@ static int dccp_insert_option_timestamp_echo(struct sock *sk,
                memcpy(to, &var32, 4);
        }
 
-       dp->dccps_timestamp_echo = 0;
-       dp->dccps_timestamp_time = ktime_set(0, 0);
        return 0;
 }
 
@@ -521,6 +543,18 @@ static int dccp_insert_options_feat(struct sock *sk, struct sk_buff *skb)
        return 0;
 }
 
+/* The length of all options needs to be a multiple of 4 (5.8) */
+static void dccp_insert_option_padding(struct sk_buff *skb)
+{
+       int padding = DCCP_SKB_CB(skb)->dccpd_opt_len % 4;
+
+       if (padding != 0) {
+               padding = 4 - padding;
+               memset(skb_push(skb, padding), 0, padding);
+               DCCP_SKB_CB(skb)->dccpd_opt_len += padding;
+       }
+}
+
 int dccp_insert_options(struct sock *sk, struct sk_buff *skb)
 {
        struct dccp_sock *dp = dccp_sk(sk);
@@ -537,10 +571,6 @@ int dccp_insert_options(struct sock *sk, struct sk_buff *skb)
                    dccp_ackvec_pending(dp->dccps_hc_rx_ackvec) &&
                    dccp_insert_option_ackvec(sk, skb))
                        return -1;
-
-               if (dp->dccps_timestamp_echo != 0 &&
-                   dccp_insert_option_timestamp_echo(sk, skb))
-                       return -1;
        }
 
        if (dp->dccps_hc_rx_insert_options) {
@@ -564,18 +594,22 @@ int dccp_insert_options(struct sock *sk, struct sk_buff *skb)
            dccp_insert_option_timestamp(sk, skb))
                return -1;
 
-       /* XXX: insert other options when appropriate */
+       if (dp->dccps_timestamp_echo != 0 &&
+           dccp_insert_option_timestamp_echo(dp, NULL, skb))
+               return -1;
+
+       dccp_insert_option_padding(skb);
+       return 0;
+}
 
-       if (DCCP_SKB_CB(skb)->dccpd_opt_len != 0) {
-               /* The length of all options has to be a multiple of 4 */
-               int padding = DCCP_SKB_CB(skb)->dccpd_opt_len % 4;
+int dccp_insert_options_rsk(struct dccp_request_sock *dreq, struct sk_buff *skb)
+{
+       DCCP_SKB_CB(skb)->dccpd_opt_len = 0;
 
-               if (padding != 0) {
-                       padding = 4 - padding;
-                       memset(skb_push(skb, padding), 0, padding);
-                       DCCP_SKB_CB(skb)->dccpd_opt_len += padding;
-               }
-       }
+       if (dreq->dreq_timestamp_echo != 0 &&
+           dccp_insert_option_timestamp_echo(NULL, dreq, skb))
+               return -1;
 
+       dccp_insert_option_padding(skb);
        return 0;
 }