summaryrefslogtreecommitdiffstats
path: root/net/dccp/options.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/dccp/options.c')
-rw-r--r--net/dccp/options.c139
1 files changed, 91 insertions, 48 deletions
diff --git a/net/dccp/options.c b/net/dccp/options.c
index d286cffe2c49..d2a84a2fecee 100644
--- a/net/dccp/options.c
+++ b/net/dccp/options.c
@@ -46,7 +46,13 @@ static u32 dccp_decode_value_var(const unsigned char *bf, const u8 len)
return value;
}
-int dccp_parse_options(struct sock *sk, struct sk_buff *skb)
+/**
+ * dccp_parse_options - Parse DCCP options present in @skb
+ * @sk: client|server|listening dccp socket (when @dreq != NULL)
+ * @dreq: request socket to use during connection setup, or NULL
+ */
+int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq,
+ struct sk_buff *skb)
{
struct dccp_sock *dp = dccp_sk(sk);
const struct dccp_hdr *dh = dccp_hdr(skb);
@@ -92,6 +98,20 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb)
goto out_invalid_option;
}
+ /*
+ * CCID-Specific Options (from RFC 4340, sec. 10.3):
+ *
+ * Option numbers 128 through 191 are for options sent from the
+ * HC-Sender to the HC-Receiver; option numbers 192 through 255
+ * are for options sent from the HC-Receiver to the HC-Sender.
+ *
+ * CCID-specific options are ignored during connection setup, as
+ * negotiation may still be in progress (see RFC 4340, 10.3).
+ *
+ */
+ if (dreq != NULL && opt >= 128)
+ goto ignore_option;
+
switch (opt) {
case DCCPO_PADDING:
break;
@@ -112,6 +132,8 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb)
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,
@@ -128,7 +150,9 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb)
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))
@@ -136,7 +160,7 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb)
break;
case DCCPO_ACK_VECTOR_0:
case DCCPO_ACK_VECTOR_1:
- if (pkt_type == DCCP_PKT_DATA)
+ if (dccp_packet_without_ack(skb)) /* RFC 4340, 11.4 */
break;
if (dccp_msk(sk)->dccpms_send_ack_vector &&
@@ -146,15 +170,27 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb)
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);
-
- 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;
@@ -194,18 +230,17 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb)
opt_recv->dccpor_elapsed_time = elapsed_time;
break;
case DCCPO_ELAPSED_TIME:
- if (len != 2 && len != 4)
- goto out_invalid_option;
-
- if (pkt_type == DCCP_PKT_DATA)
- continue;
+ if (dccp_packet_without_ack(skb)) /* RFC 4340, 13.2 */
+ break;
if (len == 2) {
__be16 opt_val2 = get_unaligned((__be16 *)value);
elapsed_time = ntohs(opt_val2);
- } else {
+ } else if (len == 4) {
opt_val = get_unaligned((__be32 *)value);
elapsed_time = ntohl(opt_val);
+ } else {
+ goto out_invalid_option;
}
if (elapsed_time > opt_recv->dccpor_elapsed_time)
@@ -214,15 +249,6 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb)
dccp_pr_debug("%s rx opt: ELAPSED_TIME=%d\n",
dccp_role(sk), elapsed_time);
break;
- /*
- * From RFC 4340, sec. 10.3:
- *
- * Option numbers 128 through 191 are for
- * options sent from the HC-Sender to the
- * HC-Receiver; option numbers 192 through 255
- * are for options sent from the HC-Receiver to
- * the HC-Sender.
- */
case 128 ... 191: {
const u16 idx = value - options;
@@ -246,7 +272,7 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb)
"implemented, ignoring", sk, opt, len);
break;
}
-
+ignore_option:
if (opt != DCCPO_MANDATORY)
mandatory = 0;
}
@@ -382,16 +408,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;
@@ -404,7 +438,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;
@@ -416,8 +449,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;
}
@@ -510,6 +541,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);
@@ -526,10 +569,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) {
@@ -553,18 +592,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;
}