17c657876SArnaldo Carvalho de Melo /* 27c657876SArnaldo Carvalho de Melo * net/dccp/options.c 37c657876SArnaldo Carvalho de Melo * 47c657876SArnaldo Carvalho de Melo * An implementation of the DCCP protocol 51bc09869SIan McDonald * Copyright (c) 2005 Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org> 61bc09869SIan McDonald * Copyright (c) 2005 Arnaldo Carvalho de Melo <acme@ghostprotocols.net> 7e6bccd35SIan McDonald * Copyright (c) 2005 Ian McDonald <ian.mcdonald@jandi.co.nz> 87c657876SArnaldo Carvalho de Melo * 97c657876SArnaldo Carvalho de Melo * This program is free software; you can redistribute it and/or 107c657876SArnaldo Carvalho de Melo * modify it under the terms of the GNU General Public License 117c657876SArnaldo Carvalho de Melo * as published by the Free Software Foundation; either version 127c657876SArnaldo Carvalho de Melo * 2 of the License, or (at your option) any later version. 137c657876SArnaldo Carvalho de Melo */ 147c657876SArnaldo Carvalho de Melo #include <linux/dccp.h> 157c657876SArnaldo Carvalho de Melo #include <linux/module.h> 167c657876SArnaldo Carvalho de Melo #include <linux/types.h> 1776fd1e87SGerrit Renker #include <asm/unaligned.h> 187c657876SArnaldo Carvalho de Melo #include <linux/kernel.h> 197c657876SArnaldo Carvalho de Melo #include <linux/skbuff.h> 207c657876SArnaldo Carvalho de Melo 21ae31c339SArnaldo Carvalho de Melo #include "ackvec.h" 227c657876SArnaldo Carvalho de Melo #include "ccid.h" 237c657876SArnaldo Carvalho de Melo #include "dccp.h" 24afe00251SAndrea Bittau #include "feat.h" 257c657876SArnaldo Carvalho de Melo 26410e27a4SGerrit Renker int sysctl_dccp_feat_sequence_window = DCCPF_INITIAL_SEQUENCE_WINDOW; 27410e27a4SGerrit Renker int sysctl_dccp_feat_rx_ccid = DCCPF_INITIAL_CCID; 28410e27a4SGerrit Renker int sysctl_dccp_feat_tx_ccid = DCCPF_INITIAL_CCID; 29410e27a4SGerrit Renker int sysctl_dccp_feat_send_ack_vector = DCCPF_INITIAL_SEND_ACK_VECTOR; 30410e27a4SGerrit Renker int sysctl_dccp_feat_send_ndp_count = DCCPF_INITIAL_SEND_NDP_COUNT; 317c657876SArnaldo Carvalho de Melo 32*02fa460eSGerrit Renker u64 dccp_decode_value_var(const u8 *bf, const u8 len) 33410e27a4SGerrit Renker { 34*02fa460eSGerrit Renker u64 value = 0; 35410e27a4SGerrit Renker 36*02fa460eSGerrit Renker if (len >= DCCP_OPTVAL_MAXLEN) 37*02fa460eSGerrit Renker value += ((u64)*bf++) << 40; 38*02fa460eSGerrit Renker if (len > 4) 39*02fa460eSGerrit Renker value += ((u64)*bf++) << 32; 407c657876SArnaldo Carvalho de Melo if (len > 3) 41*02fa460eSGerrit Renker value += ((u64)*bf++) << 24; 427c657876SArnaldo Carvalho de Melo if (len > 2) 43*02fa460eSGerrit Renker value += ((u64)*bf++) << 16; 447c657876SArnaldo Carvalho de Melo if (len > 1) 45*02fa460eSGerrit Renker value += ((u64)*bf++) << 8; 467c657876SArnaldo Carvalho de Melo if (len > 0) 477c657876SArnaldo Carvalho de Melo value += *bf; 487c657876SArnaldo Carvalho de Melo 497c657876SArnaldo Carvalho de Melo return value; 507c657876SArnaldo Carvalho de Melo } 517c657876SArnaldo Carvalho de Melo 528b819412SGerrit Renker /** 538b819412SGerrit Renker * dccp_parse_options - Parse DCCP options present in @skb 548b819412SGerrit Renker * @sk: client|server|listening dccp socket (when @dreq != NULL) 558b819412SGerrit Renker * @dreq: request socket to use during connection setup, or NULL 568b819412SGerrit Renker */ 578b819412SGerrit Renker int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq, 588b819412SGerrit Renker struct sk_buff *skb) 597c657876SArnaldo Carvalho de Melo { 607c657876SArnaldo Carvalho de Melo struct dccp_sock *dp = dccp_sk(sk); 617c657876SArnaldo Carvalho de Melo const struct dccp_hdr *dh = dccp_hdr(skb); 627c657876SArnaldo Carvalho de Melo const u8 pkt_type = DCCP_SKB_CB(skb)->dccpd_type; 63410e27a4SGerrit Renker u64 ackno = DCCP_SKB_CB(skb)->dccpd_ack_seq; 647c657876SArnaldo Carvalho de Melo unsigned char *options = (unsigned char *)dh + dccp_hdr_len(skb); 657c657876SArnaldo Carvalho de Melo unsigned char *opt_ptr = options; 667690af3fSArnaldo Carvalho de Melo const unsigned char *opt_end = (unsigned char *)dh + 677690af3fSArnaldo Carvalho de Melo (dh->dccph_doff * 4); 687c657876SArnaldo Carvalho de Melo struct dccp_options_received *opt_recv = &dp->dccps_options_received; 697c657876SArnaldo Carvalho de Melo unsigned char opt, len; 707c657876SArnaldo Carvalho de Melo unsigned char *value; 711c14ac0aSArnaldo Carvalho de Melo u32 elapsed_time; 7276fd1e87SGerrit Renker __be32 opt_val; 73afe00251SAndrea Bittau int rc; 74afe00251SAndrea Bittau int mandatory = 0; 757c657876SArnaldo Carvalho de Melo 767c657876SArnaldo Carvalho de Melo memset(opt_recv, 0, sizeof(*opt_recv)); 777c657876SArnaldo Carvalho de Melo 78fb950496SDavid S. Miller opt = len = 0; 797c657876SArnaldo Carvalho de Melo while (opt_ptr != opt_end) { 807c657876SArnaldo Carvalho de Melo opt = *opt_ptr++; 817c657876SArnaldo Carvalho de Melo len = 0; 827c657876SArnaldo Carvalho de Melo value = NULL; 837c657876SArnaldo Carvalho de Melo 847c657876SArnaldo Carvalho de Melo /* Check if this isn't a single byte option */ 857c657876SArnaldo Carvalho de Melo if (opt > DCCPO_MAX_RESERVED) { 867c657876SArnaldo Carvalho de Melo if (opt_ptr == opt_end) 87faf61c33SGerrit Renker goto out_nonsensical_length; 887c657876SArnaldo Carvalho de Melo 897c657876SArnaldo Carvalho de Melo len = *opt_ptr++; 90faf61c33SGerrit Renker if (len < 2) 91faf61c33SGerrit Renker goto out_nonsensical_length; 927c657876SArnaldo Carvalho de Melo /* 937c657876SArnaldo Carvalho de Melo * Remove the type and len fields, leaving 947c657876SArnaldo Carvalho de Melo * just the value size 957c657876SArnaldo Carvalho de Melo */ 967c657876SArnaldo Carvalho de Melo len -= 2; 977c657876SArnaldo Carvalho de Melo value = opt_ptr; 987c657876SArnaldo Carvalho de Melo opt_ptr += len; 997c657876SArnaldo Carvalho de Melo 1007c657876SArnaldo Carvalho de Melo if (opt_ptr > opt_end) 101faf61c33SGerrit Renker goto out_nonsensical_length; 1027c657876SArnaldo Carvalho de Melo } 1037c657876SArnaldo Carvalho de Melo 1048b819412SGerrit Renker /* 105410e27a4SGerrit Renker * CCID-Specific Options (from RFC 4340, sec. 10.3): 106410e27a4SGerrit Renker * 107410e27a4SGerrit Renker * Option numbers 128 through 191 are for options sent from the 108410e27a4SGerrit Renker * HC-Sender to the HC-Receiver; option numbers 192 through 255 109410e27a4SGerrit Renker * are for options sent from the HC-Receiver to the HC-Sender. 110410e27a4SGerrit Renker * 1118b819412SGerrit Renker * CCID-specific options are ignored during connection setup, as 1128b819412SGerrit Renker * negotiation may still be in progress (see RFC 4340, 10.3). 11365907a43SGerrit Renker * The same applies to Ack Vectors, as these depend on the CCID. 114410e27a4SGerrit Renker * 1158b819412SGerrit Renker */ 116410e27a4SGerrit Renker if (dreq != NULL && (opt >= 128 || 11765907a43SGerrit Renker opt == DCCPO_ACK_VECTOR_0 || opt == DCCPO_ACK_VECTOR_1)) 1188b819412SGerrit Renker goto ignore_option; 1198b819412SGerrit Renker 1207c657876SArnaldo Carvalho de Melo switch (opt) { 1217c657876SArnaldo Carvalho de Melo case DCCPO_PADDING: 1227c657876SArnaldo Carvalho de Melo break; 123afe00251SAndrea Bittau case DCCPO_MANDATORY: 124afe00251SAndrea Bittau if (mandatory) 125afe00251SAndrea Bittau goto out_invalid_option; 1266df9424aSArnaldo Carvalho de Melo if (pkt_type != DCCP_PKT_DATA) 127afe00251SAndrea Bittau mandatory = 1; 128afe00251SAndrea Bittau break; 1297c657876SArnaldo Carvalho de Melo case DCCPO_NDP_COUNT: 1305b5d0e70SGerrit Renker if (len > 6) 1317c657876SArnaldo Carvalho de Melo goto out_invalid_option; 1327c657876SArnaldo Carvalho de Melo 1337c657876SArnaldo Carvalho de Melo opt_recv->dccpor_ndp = dccp_decode_value_var(value, len); 1345b5d0e70SGerrit Renker dccp_pr_debug("%s opt: NDP count=%llu\n", dccp_role(sk), 1355b5d0e70SGerrit Renker (unsigned long long)opt_recv->dccpor_ndp); 1367c657876SArnaldo Carvalho de Melo break; 137410e27a4SGerrit Renker case DCCPO_CHANGE_L: 138410e27a4SGerrit Renker /* fall through */ 139410e27a4SGerrit Renker case DCCPO_CHANGE_R: 140410e27a4SGerrit Renker if (pkt_type == DCCP_PKT_DATA) 141cf86314cSGerrit Renker break; 142410e27a4SGerrit Renker if (len < 2) 143410e27a4SGerrit Renker goto out_invalid_option; 144410e27a4SGerrit Renker rc = dccp_feat_change_recv(sk, opt, *value, value + 1, 145410e27a4SGerrit Renker len - 1); 146410e27a4SGerrit Renker /* 147410e27a4SGerrit Renker * When there is a change error, change_recv is 148410e27a4SGerrit Renker * responsible for dealing with it. i.e. reply with an 149410e27a4SGerrit Renker * empty confirm. 150410e27a4SGerrit Renker * If the change was mandatory, then we need to die. 151410e27a4SGerrit Renker */ 152410e27a4SGerrit Renker if (rc && mandatory) 153410e27a4SGerrit Renker goto out_invalid_option; 154410e27a4SGerrit Renker break; 155410e27a4SGerrit Renker case DCCPO_CONFIRM_L: 156410e27a4SGerrit Renker /* fall through */ 157410e27a4SGerrit Renker case DCCPO_CONFIRM_R: 158410e27a4SGerrit Renker if (pkt_type == DCCP_PKT_DATA) 159410e27a4SGerrit Renker break; 160410e27a4SGerrit Renker if (len < 2) /* FIXME this disallows empty confirm */ 161410e27a4SGerrit Renker goto out_invalid_option; 162410e27a4SGerrit Renker if (dccp_feat_confirm_recv(sk, opt, *value, 163410e27a4SGerrit Renker value + 1, len - 1)) 164410e27a4SGerrit Renker goto out_invalid_option; 165410e27a4SGerrit Renker break; 166410e27a4SGerrit Renker case DCCPO_ACK_VECTOR_0: 167410e27a4SGerrit Renker case DCCPO_ACK_VECTOR_1: 168410e27a4SGerrit Renker if (dccp_packet_without_ack(skb)) /* RFC 4340, 11.4 */ 169410e27a4SGerrit Renker break; 170410e27a4SGerrit Renker 171410e27a4SGerrit Renker if (dccp_msk(sk)->dccpms_send_ack_vector && 172410e27a4SGerrit Renker dccp_ackvec_parse(sk, skb, &ackno, opt, value, len)) 173410e27a4SGerrit Renker goto out_invalid_option; 174afe00251SAndrea Bittau break; 1757c657876SArnaldo Carvalho de Melo case DCCPO_TIMESTAMP: 1767c657876SArnaldo Carvalho de Melo if (len != 4) 1777c657876SArnaldo Carvalho de Melo goto out_invalid_option; 178b4d4f7c7SGerrit Renker /* 179b4d4f7c7SGerrit Renker * RFC 4340 13.1: "The precise time corresponding to 180b4d4f7c7SGerrit Renker * Timestamp Value zero is not specified". We use 181b4d4f7c7SGerrit Renker * zero to indicate absence of a meaningful timestamp. 182b4d4f7c7SGerrit Renker */ 18376fd1e87SGerrit Renker opt_val = get_unaligned((__be32 *)value); 184b4d4f7c7SGerrit Renker if (unlikely(opt_val == 0)) { 185b4d4f7c7SGerrit Renker DCCP_WARN("Timestamp with zero value\n"); 186b4d4f7c7SGerrit Renker break; 187b4d4f7c7SGerrit Renker } 1887c657876SArnaldo Carvalho de Melo 189b4d4f7c7SGerrit Renker if (dreq != NULL) { 190b4d4f7c7SGerrit Renker dreq->dreq_timestamp_echo = ntohl(opt_val); 191b4d4f7c7SGerrit Renker dreq->dreq_timestamp_time = dccp_timestamp(); 192b4d4f7c7SGerrit Renker } else { 193b4d4f7c7SGerrit Renker opt_recv->dccpor_timestamp = 194b4d4f7c7SGerrit Renker dp->dccps_timestamp_echo = ntohl(opt_val); 195b4d4f7c7SGerrit Renker dp->dccps_timestamp_time = dccp_timestamp(); 196b4d4f7c7SGerrit Renker } 19709dbc389SGerrit Renker dccp_pr_debug("%s rx opt: TIMESTAMP=%u, ackno=%llu\n", 198b4d4f7c7SGerrit Renker dccp_role(sk), ntohl(opt_val), 199f6ccf554SDavid S. Miller (unsigned long long) 2007c657876SArnaldo Carvalho de Melo DCCP_SKB_CB(skb)->dccpd_ack_seq); 2017c657876SArnaldo Carvalho de Melo break; 2027c657876SArnaldo Carvalho de Melo case DCCPO_TIMESTAMP_ECHO: 2031bc09869SIan McDonald if (len != 4 && len != 6 && len != 8) 2047c657876SArnaldo Carvalho de Melo goto out_invalid_option; 2057c657876SArnaldo Carvalho de Melo 20676fd1e87SGerrit Renker opt_val = get_unaligned((__be32 *)value); 20776fd1e87SGerrit Renker opt_recv->dccpor_timestamp_echo = ntohl(opt_val); 2087c657876SArnaldo Carvalho de Melo 20909dbc389SGerrit Renker dccp_pr_debug("%s rx opt: TIMESTAMP_ECHO=%u, len=%d, " 210f73f7097SGerrit Renker "ackno=%llu", dccp_role(sk), 2117690af3fSArnaldo Carvalho de Melo opt_recv->dccpor_timestamp_echo, 212f6ccf554SDavid S. Miller len + 2, 213f6ccf554SDavid S. Miller (unsigned long long) 2141bc09869SIan McDonald DCCP_SKB_CB(skb)->dccpd_ack_seq); 2157c657876SArnaldo Carvalho de Melo 21676fd1e87SGerrit Renker value += 4; 2171bc09869SIan McDonald 21876fd1e87SGerrit Renker if (len == 4) { /* no elapsed time included */ 219f73f7097SGerrit Renker dccp_pr_debug_cat("\n"); 2201c14ac0aSArnaldo Carvalho de Melo break; 221f73f7097SGerrit Renker } 2221c14ac0aSArnaldo Carvalho de Melo 22376fd1e87SGerrit Renker if (len == 6) { /* 2-byte elapsed time */ 22476fd1e87SGerrit Renker __be16 opt_val2 = get_unaligned((__be16 *)value); 22576fd1e87SGerrit Renker elapsed_time = ntohs(opt_val2); 22676fd1e87SGerrit Renker } else { /* 4-byte elapsed time */ 22776fd1e87SGerrit Renker opt_val = get_unaligned((__be32 *)value); 22876fd1e87SGerrit Renker elapsed_time = ntohl(opt_val); 22976fd1e87SGerrit Renker } 2301c14ac0aSArnaldo Carvalho de Melo 231dcad856fSGerrit Renker dccp_pr_debug_cat(", ELAPSED_TIME=%u\n", elapsed_time); 232f73f7097SGerrit Renker 2331c14ac0aSArnaldo Carvalho de Melo /* Give precedence to the biggest ELAPSED_TIME */ 2341c14ac0aSArnaldo Carvalho de Melo if (elapsed_time > opt_recv->dccpor_elapsed_time) 2351c14ac0aSArnaldo Carvalho de Melo opt_recv->dccpor_elapsed_time = elapsed_time; 2367c657876SArnaldo Carvalho de Melo break; 2377c657876SArnaldo Carvalho de Melo case DCCPO_ELAPSED_TIME: 238c86ab2b6SGerrit Renker if (dccp_packet_without_ack(skb)) /* RFC 4340, 13.2 */ 239c86ab2b6SGerrit Renker break; 2401bc09869SIan McDonald 24176fd1e87SGerrit Renker if (len == 2) { 24276fd1e87SGerrit Renker __be16 opt_val2 = get_unaligned((__be16 *)value); 24376fd1e87SGerrit Renker elapsed_time = ntohs(opt_val2); 244c86ab2b6SGerrit Renker } else if (len == 4) { 24576fd1e87SGerrit Renker opt_val = get_unaligned((__be32 *)value); 24676fd1e87SGerrit Renker elapsed_time = ntohl(opt_val); 247c86ab2b6SGerrit Renker } else { 248c86ab2b6SGerrit Renker goto out_invalid_option; 24976fd1e87SGerrit Renker } 2501c14ac0aSArnaldo Carvalho de Melo 2511c14ac0aSArnaldo Carvalho de Melo if (elapsed_time > opt_recv->dccpor_elapsed_time) 2521c14ac0aSArnaldo Carvalho de Melo opt_recv->dccpor_elapsed_time = elapsed_time; 2531bc09869SIan McDonald 25409dbc389SGerrit Renker dccp_pr_debug("%s rx opt: ELAPSED_TIME=%d\n", 25509dbc389SGerrit Renker dccp_role(sk), elapsed_time); 2567c657876SArnaldo Carvalho de Melo break; 257410e27a4SGerrit Renker case 128 ... 191: { 258410e27a4SGerrit Renker const u16 idx = value - options; 259410e27a4SGerrit Renker 2607690af3fSArnaldo Carvalho de Melo if (ccid_hc_rx_parse_options(dp->dccps_hc_rx_ccid, sk, 261410e27a4SGerrit Renker opt, len, idx, 262410e27a4SGerrit Renker value) != 0) 2637c657876SArnaldo Carvalho de Melo goto out_invalid_option; 264410e27a4SGerrit Renker } 2657c657876SArnaldo Carvalho de Melo break; 266410e27a4SGerrit Renker case 192 ... 255: { 267410e27a4SGerrit Renker const u16 idx = value - options; 268410e27a4SGerrit Renker 2697690af3fSArnaldo Carvalho de Melo if (ccid_hc_tx_parse_options(dp->dccps_hc_tx_ccid, sk, 270410e27a4SGerrit Renker opt, len, idx, 271410e27a4SGerrit Renker value) != 0) 2727c657876SArnaldo Carvalho de Melo goto out_invalid_option; 273410e27a4SGerrit Renker } 2747c657876SArnaldo Carvalho de Melo break; 2757c657876SArnaldo Carvalho de Melo default: 27659348b19SGerrit Renker DCCP_CRIT("DCCP(%p): option %d(len=%d) not " 27759348b19SGerrit Renker "implemented, ignoring", sk, opt, len); 2787c657876SArnaldo Carvalho de Melo break; 2797c657876SArnaldo Carvalho de Melo } 2808b819412SGerrit Renker ignore_option: 281afe00251SAndrea Bittau if (opt != DCCPO_MANDATORY) 282afe00251SAndrea Bittau mandatory = 0; 2837c657876SArnaldo Carvalho de Melo } 2847c657876SArnaldo Carvalho de Melo 2856df9424aSArnaldo Carvalho de Melo /* mandatory was the last byte in option list -> reset connection */ 2866df9424aSArnaldo Carvalho de Melo if (mandatory) 2876df9424aSArnaldo Carvalho de Melo goto out_invalid_option; 2886df9424aSArnaldo Carvalho de Melo 289faf61c33SGerrit Renker out_nonsensical_length: 290faf61c33SGerrit Renker /* RFC 4340, 5.8: ignore option and all remaining option space */ 2917c657876SArnaldo Carvalho de Melo return 0; 2927c657876SArnaldo Carvalho de Melo 2937c657876SArnaldo Carvalho de Melo out_invalid_option: 2947c657876SArnaldo Carvalho de Melo DCCP_INC_STATS_BH(DCCP_MIB_INVALIDOPT); 295410e27a4SGerrit Renker DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_OPTION_ERROR; 296410e27a4SGerrit Renker DCCP_WARN("DCCP(%p): invalid option %d, len=%d", sk, opt, len); 297eac7726bSGerrit Renker DCCP_SKB_CB(skb)->dccpd_reset_data[0] = opt; 298eac7726bSGerrit Renker DCCP_SKB_CB(skb)->dccpd_reset_data[1] = len > 0 ? value[0] : 0; 299eac7726bSGerrit Renker DCCP_SKB_CB(skb)->dccpd_reset_data[2] = len > 1 ? value[1] : 0; 3007c657876SArnaldo Carvalho de Melo return -1; 3017c657876SArnaldo Carvalho de Melo } 3027c657876SArnaldo Carvalho de Melo 303b61fafc4SArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_parse_options); 304b61fafc4SArnaldo Carvalho de Melo 305*02fa460eSGerrit Renker void dccp_encode_value_var(const u64 value, u8 *to, const u8 len) 3067c657876SArnaldo Carvalho de Melo { 307*02fa460eSGerrit Renker if (len >= DCCP_OPTVAL_MAXLEN) 308*02fa460eSGerrit Renker *to++ = (value & 0xFF0000000000ull) >> 40; 309*02fa460eSGerrit Renker if (len > 4) 310*02fa460eSGerrit Renker *to++ = (value & 0xFF00000000ull) >> 32; 3117c657876SArnaldo Carvalho de Melo if (len > 3) 3127c657876SArnaldo Carvalho de Melo *to++ = (value & 0xFF000000) >> 24; 3137c657876SArnaldo Carvalho de Melo if (len > 2) 3147c657876SArnaldo Carvalho de Melo *to++ = (value & 0xFF0000) >> 16; 3157c657876SArnaldo Carvalho de Melo if (len > 1) 3167c657876SArnaldo Carvalho de Melo *to++ = (value & 0xFF00) >> 8; 3177c657876SArnaldo Carvalho de Melo if (len > 0) 3187c657876SArnaldo Carvalho de Melo *to++ = (value & 0xFF); 3197c657876SArnaldo Carvalho de Melo } 3207c657876SArnaldo Carvalho de Melo 3215b5d0e70SGerrit Renker static inline u8 dccp_ndp_len(const u64 ndp) 3227c657876SArnaldo Carvalho de Melo { 3235b5d0e70SGerrit Renker if (likely(ndp <= 0xFF)) 3245b5d0e70SGerrit Renker return 1; 3255b5d0e70SGerrit Renker return likely(ndp <= USHORT_MAX) ? 2 : (ndp <= UINT_MAX ? 4 : 6); 3267c657876SArnaldo Carvalho de Melo } 3277c657876SArnaldo Carvalho de Melo 3282d0817d1SArnaldo Carvalho de Melo int dccp_insert_option(struct sock *sk, struct sk_buff *skb, 3297c657876SArnaldo Carvalho de Melo const unsigned char option, 3307c657876SArnaldo Carvalho de Melo const void *value, const unsigned char len) 3317c657876SArnaldo Carvalho de Melo { 3327c657876SArnaldo Carvalho de Melo unsigned char *to; 3337c657876SArnaldo Carvalho de Melo 3342d0817d1SArnaldo Carvalho de Melo if (DCCP_SKB_CB(skb)->dccpd_opt_len + len + 2 > DCCP_MAX_OPT_LEN) 3352d0817d1SArnaldo Carvalho de Melo return -1; 3367c657876SArnaldo Carvalho de Melo 3377c657876SArnaldo Carvalho de Melo DCCP_SKB_CB(skb)->dccpd_opt_len += len + 2; 3387c657876SArnaldo Carvalho de Melo 3397c657876SArnaldo Carvalho de Melo to = skb_push(skb, len + 2); 3407c657876SArnaldo Carvalho de Melo *to++ = option; 3417c657876SArnaldo Carvalho de Melo *to++ = len + 2; 3427c657876SArnaldo Carvalho de Melo 3437c657876SArnaldo Carvalho de Melo memcpy(to, value, len); 3442d0817d1SArnaldo Carvalho de Melo return 0; 3457c657876SArnaldo Carvalho de Melo } 3467c657876SArnaldo Carvalho de Melo 3477c657876SArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_insert_option); 3487c657876SArnaldo Carvalho de Melo 3492d0817d1SArnaldo Carvalho de Melo static int dccp_insert_option_ndp(struct sock *sk, struct sk_buff *skb) 3507c657876SArnaldo Carvalho de Melo { 3517c657876SArnaldo Carvalho de Melo struct dccp_sock *dp = dccp_sk(sk); 3525b5d0e70SGerrit Renker u64 ndp = dp->dccps_ndp_count; 3537c657876SArnaldo Carvalho de Melo 3547c657876SArnaldo Carvalho de Melo if (dccp_non_data_packet(skb)) 3557c657876SArnaldo Carvalho de Melo ++dp->dccps_ndp_count; 3567c657876SArnaldo Carvalho de Melo else 3577c657876SArnaldo Carvalho de Melo dp->dccps_ndp_count = 0; 3587c657876SArnaldo Carvalho de Melo 3597c657876SArnaldo Carvalho de Melo if (ndp > 0) { 3607c657876SArnaldo Carvalho de Melo unsigned char *ptr; 3617c657876SArnaldo Carvalho de Melo const int ndp_len = dccp_ndp_len(ndp); 3627c657876SArnaldo Carvalho de Melo const int len = ndp_len + 2; 3637c657876SArnaldo Carvalho de Melo 3647c657876SArnaldo Carvalho de Melo if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN) 3652d0817d1SArnaldo Carvalho de Melo return -1; 3667c657876SArnaldo Carvalho de Melo 3677c657876SArnaldo Carvalho de Melo DCCP_SKB_CB(skb)->dccpd_opt_len += len; 3687c657876SArnaldo Carvalho de Melo 3697c657876SArnaldo Carvalho de Melo ptr = skb_push(skb, len); 3707c657876SArnaldo Carvalho de Melo *ptr++ = DCCPO_NDP_COUNT; 3717c657876SArnaldo Carvalho de Melo *ptr++ = len; 3727c657876SArnaldo Carvalho de Melo dccp_encode_value_var(ndp, ptr, ndp_len); 3737c657876SArnaldo Carvalho de Melo } 3742d0817d1SArnaldo Carvalho de Melo 3752d0817d1SArnaldo Carvalho de Melo return 0; 3767c657876SArnaldo Carvalho de Melo } 3777c657876SArnaldo Carvalho de Melo 3787c657876SArnaldo Carvalho de Melo static inline int dccp_elapsed_time_len(const u32 elapsed_time) 3797c657876SArnaldo Carvalho de Melo { 380b1c9fe7bSIan McDonald return elapsed_time == 0 ? 0 : elapsed_time <= 0xFFFF ? 2 : 4; 3817c657876SArnaldo Carvalho de Melo } 3827c657876SArnaldo Carvalho de Melo 3832d0817d1SArnaldo Carvalho de Melo int dccp_insert_option_elapsed_time(struct sock *sk, struct sk_buff *skb, 3847c657876SArnaldo Carvalho de Melo u32 elapsed_time) 3857c657876SArnaldo Carvalho de Melo { 3867c657876SArnaldo Carvalho de Melo const int elapsed_time_len = dccp_elapsed_time_len(elapsed_time); 3877c657876SArnaldo Carvalho de Melo const int len = 2 + elapsed_time_len; 3887c657876SArnaldo Carvalho de Melo unsigned char *to; 3897c657876SArnaldo Carvalho de Melo 3901bc09869SIan McDonald if (elapsed_time_len == 0) 3912d0817d1SArnaldo Carvalho de Melo return 0; 3927c657876SArnaldo Carvalho de Melo 3932d0817d1SArnaldo Carvalho de Melo if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN) 3942d0817d1SArnaldo Carvalho de Melo return -1; 3957c657876SArnaldo Carvalho de Melo 3967c657876SArnaldo Carvalho de Melo DCCP_SKB_CB(skb)->dccpd_opt_len += len; 3977c657876SArnaldo Carvalho de Melo 3987c657876SArnaldo Carvalho de Melo to = skb_push(skb, len); 3997c657876SArnaldo Carvalho de Melo *to++ = DCCPO_ELAPSED_TIME; 4007c657876SArnaldo Carvalho de Melo *to++ = len; 4017c657876SArnaldo Carvalho de Melo 4021bc09869SIan McDonald if (elapsed_time_len == 2) { 40360fe62e7SAndrea Bittau const __be16 var16 = htons((u16)elapsed_time); 4041bc09869SIan McDonald memcpy(to, &var16, 2); 4051bc09869SIan McDonald } else { 40660fe62e7SAndrea Bittau const __be32 var32 = htonl(elapsed_time); 4071bc09869SIan McDonald memcpy(to, &var32, 4); 4081bc09869SIan McDonald } 4097c657876SArnaldo Carvalho de Melo 4102d0817d1SArnaldo Carvalho de Melo return 0; 4117c657876SArnaldo Carvalho de Melo } 4127c657876SArnaldo Carvalho de Melo 413d4b81ff7SArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_insert_option_elapsed_time); 4147c657876SArnaldo Carvalho de Melo 4152d0817d1SArnaldo Carvalho de Melo int dccp_insert_option_timestamp(struct sock *sk, struct sk_buff *skb) 4167c657876SArnaldo Carvalho de Melo { 4174c70f383SGerrit Renker __be32 now = htonl(dccp_timestamp()); 4181bc09869SIan McDonald /* yes this will overflow but that is the point as we want a 4191bc09869SIan McDonald * 10 usec 32 bit timer which mean it wraps every 11.9 hours */ 4201bc09869SIan McDonald 4212d0817d1SArnaldo Carvalho de Melo return dccp_insert_option(sk, skb, DCCPO_TIMESTAMP, &now, sizeof(now)); 4227c657876SArnaldo Carvalho de Melo } 4237c657876SArnaldo Carvalho de Melo 424d4b81ff7SArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_insert_option_timestamp); 425d4b81ff7SArnaldo Carvalho de Melo 426b4d4f7c7SGerrit Renker static int dccp_insert_option_timestamp_echo(struct dccp_sock *dp, 427b4d4f7c7SGerrit Renker struct dccp_request_sock *dreq, 4287690af3fSArnaldo Carvalho de Melo struct sk_buff *skb) 4297c657876SArnaldo Carvalho de Melo { 43060fe62e7SAndrea Bittau __be32 tstamp_echo; 4317c657876SArnaldo Carvalho de Melo unsigned char *to; 432b4d4f7c7SGerrit Renker u32 elapsed_time, elapsed_time_len, len; 433b4d4f7c7SGerrit Renker 434b4d4f7c7SGerrit Renker if (dreq != NULL) { 435b4d4f7c7SGerrit Renker elapsed_time = dccp_timestamp() - dreq->dreq_timestamp_time; 436b4d4f7c7SGerrit Renker tstamp_echo = htonl(dreq->dreq_timestamp_echo); 437b4d4f7c7SGerrit Renker dreq->dreq_timestamp_echo = 0; 438b4d4f7c7SGerrit Renker } else { 439b4d4f7c7SGerrit Renker elapsed_time = dccp_timestamp() - dp->dccps_timestamp_time; 440b4d4f7c7SGerrit Renker tstamp_echo = htonl(dp->dccps_timestamp_echo); 441b4d4f7c7SGerrit Renker dp->dccps_timestamp_echo = 0; 442b4d4f7c7SGerrit Renker } 443b4d4f7c7SGerrit Renker 444b0e56780SArnaldo Carvalho de Melo elapsed_time_len = dccp_elapsed_time_len(elapsed_time); 445b0e56780SArnaldo Carvalho de Melo len = 6 + elapsed_time_len; 446b0e56780SArnaldo Carvalho de Melo 4472d0817d1SArnaldo Carvalho de Melo if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN) 4482d0817d1SArnaldo Carvalho de Melo return -1; 4497c657876SArnaldo Carvalho de Melo 4507c657876SArnaldo Carvalho de Melo DCCP_SKB_CB(skb)->dccpd_opt_len += len; 4517c657876SArnaldo Carvalho de Melo 4527c657876SArnaldo Carvalho de Melo to = skb_push(skb, len); 4537c657876SArnaldo Carvalho de Melo *to++ = DCCPO_TIMESTAMP_ECHO; 4547c657876SArnaldo Carvalho de Melo *to++ = len; 4557c657876SArnaldo Carvalho de Melo 4567c657876SArnaldo Carvalho de Melo memcpy(to, &tstamp_echo, 4); 4577c657876SArnaldo Carvalho de Melo to += 4; 4581bc09869SIan McDonald 4591bc09869SIan McDonald if (elapsed_time_len == 2) { 46060fe62e7SAndrea Bittau const __be16 var16 = htons((u16)elapsed_time); 4611bc09869SIan McDonald memcpy(to, &var16, 2); 4621bc09869SIan McDonald } else if (elapsed_time_len == 4) { 46360fe62e7SAndrea Bittau const __be32 var32 = htonl(elapsed_time); 4641bc09869SIan McDonald memcpy(to, &var32, 4); 4651bc09869SIan McDonald } 4667c657876SArnaldo Carvalho de Melo 4672d0817d1SArnaldo Carvalho de Melo return 0; 4687c657876SArnaldo Carvalho de Melo } 4697c657876SArnaldo Carvalho de Melo 470410e27a4SGerrit Renker static int dccp_insert_feat_opt(struct sk_buff *skb, u8 type, u8 feat, 471410e27a4SGerrit Renker u8 *val, u8 len) 4724829007cSGerrit Renker { 473410e27a4SGerrit Renker u8 *to; 4744829007cSGerrit Renker 475410e27a4SGerrit Renker if (DCCP_SKB_CB(skb)->dccpd_opt_len + len + 3 > DCCP_MAX_OPT_LEN) { 476cf9ddf73SGerrit Renker DCCP_WARN("packet too small for feature %d option!\n", feat); 477cf9ddf73SGerrit Renker return -1; 478cf9ddf73SGerrit Renker } 479cf9ddf73SGerrit Renker 480410e27a4SGerrit Renker DCCP_SKB_CB(skb)->dccpd_opt_len += len + 3; 481410e27a4SGerrit Renker 482410e27a4SGerrit Renker to = skb_push(skb, len + 3); 483afe00251SAndrea Bittau *to++ = type; 484410e27a4SGerrit Renker *to++ = len + 3; 485afe00251SAndrea Bittau *to++ = feat; 486afe00251SAndrea Bittau 487afe00251SAndrea Bittau if (len) 488afe00251SAndrea Bittau memcpy(to, val, len); 489410e27a4SGerrit Renker 490410e27a4SGerrit Renker dccp_pr_debug("%s(%s (%d), ...), length %d\n", 491410e27a4SGerrit Renker dccp_feat_typename(type), 492410e27a4SGerrit Renker dccp_feat_name(feat), feat, len); 493410e27a4SGerrit Renker return 0; 494410e27a4SGerrit Renker } 495410e27a4SGerrit Renker 496410e27a4SGerrit Renker static int dccp_insert_options_feat(struct sock *sk, struct sk_buff *skb) 497410e27a4SGerrit Renker { 498410e27a4SGerrit Renker struct dccp_minisock *dmsk = dccp_msk(sk); 499410e27a4SGerrit Renker struct dccp_opt_pend *opt, *next; 500410e27a4SGerrit Renker int change = 0; 501410e27a4SGerrit Renker 502410e27a4SGerrit Renker /* confirm any options [NN opts] */ 503410e27a4SGerrit Renker list_for_each_entry_safe(opt, next, &dmsk->dccpms_conf, dccpop_node) { 504410e27a4SGerrit Renker dccp_insert_feat_opt(skb, opt->dccpop_type, 505410e27a4SGerrit Renker opt->dccpop_feat, opt->dccpop_val, 506410e27a4SGerrit Renker opt->dccpop_len); 507410e27a4SGerrit Renker /* fear empty confirms */ 508410e27a4SGerrit Renker if (opt->dccpop_val) 509410e27a4SGerrit Renker kfree(opt->dccpop_val); 510410e27a4SGerrit Renker kfree(opt); 511410e27a4SGerrit Renker } 512410e27a4SGerrit Renker INIT_LIST_HEAD(&dmsk->dccpms_conf); 513410e27a4SGerrit Renker 514410e27a4SGerrit Renker /* see which features we need to send */ 515410e27a4SGerrit Renker list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) { 516410e27a4SGerrit Renker /* see if we need to send any confirm */ 517410e27a4SGerrit Renker if (opt->dccpop_sc) { 518410e27a4SGerrit Renker dccp_insert_feat_opt(skb, opt->dccpop_type + 1, 519410e27a4SGerrit Renker opt->dccpop_feat, 520410e27a4SGerrit Renker opt->dccpop_sc->dccpoc_val, 521410e27a4SGerrit Renker opt->dccpop_sc->dccpoc_len); 522410e27a4SGerrit Renker 523410e27a4SGerrit Renker BUG_ON(!opt->dccpop_sc->dccpoc_val); 524410e27a4SGerrit Renker kfree(opt->dccpop_sc->dccpoc_val); 525410e27a4SGerrit Renker kfree(opt->dccpop_sc); 526410e27a4SGerrit Renker opt->dccpop_sc = NULL; 527410e27a4SGerrit Renker } 528410e27a4SGerrit Renker 529410e27a4SGerrit Renker /* any option not confirmed, re-send it */ 530410e27a4SGerrit Renker if (!opt->dccpop_conf) { 531410e27a4SGerrit Renker dccp_insert_feat_opt(skb, opt->dccpop_type, 532410e27a4SGerrit Renker opt->dccpop_feat, opt->dccpop_val, 533410e27a4SGerrit Renker opt->dccpop_len); 534410e27a4SGerrit Renker change++; 535410e27a4SGerrit Renker } 536410e27a4SGerrit Renker } 537410e27a4SGerrit Renker 538afe00251SAndrea Bittau return 0; 539afe00251SAndrea Bittau } 540afe00251SAndrea Bittau 541af3b867eSGerrit Renker /* The length of all options needs to be a multiple of 4 (5.8) */ 542af3b867eSGerrit Renker static void dccp_insert_option_padding(struct sk_buff *skb) 543af3b867eSGerrit Renker { 544af3b867eSGerrit Renker int padding = DCCP_SKB_CB(skb)->dccpd_opt_len % 4; 545af3b867eSGerrit Renker 546af3b867eSGerrit Renker if (padding != 0) { 547af3b867eSGerrit Renker padding = 4 - padding; 548af3b867eSGerrit Renker memset(skb_push(skb, padding), 0, padding); 549af3b867eSGerrit Renker DCCP_SKB_CB(skb)->dccpd_opt_len += padding; 550af3b867eSGerrit Renker } 551af3b867eSGerrit Renker } 552af3b867eSGerrit Renker 5532d0817d1SArnaldo Carvalho de Melo int dccp_insert_options(struct sock *sk, struct sk_buff *skb) 5547c657876SArnaldo Carvalho de Melo { 5557c657876SArnaldo Carvalho de Melo struct dccp_sock *dp = dccp_sk(sk); 556410e27a4SGerrit Renker struct dccp_minisock *dmsk = dccp_msk(sk); 5577c657876SArnaldo Carvalho de Melo 5587c657876SArnaldo Carvalho de Melo DCCP_SKB_CB(skb)->dccpd_opt_len = 0; 5597c657876SArnaldo Carvalho de Melo 560410e27a4SGerrit Renker if (dmsk->dccpms_send_ndp_count && 561410e27a4SGerrit Renker dccp_insert_option_ndp(sk, skb)) 5622d0817d1SArnaldo Carvalho de Melo return -1; 5637c657876SArnaldo Carvalho de Melo 564410e27a4SGerrit Renker if (!dccp_packet_without_ack(skb)) { 565410e27a4SGerrit Renker if (dmsk->dccpms_send_ack_vector && 566410e27a4SGerrit Renker dccp_ackvec_pending(dp->dccps_hc_rx_ackvec) && 567410e27a4SGerrit Renker dccp_insert_option_ackvec(sk, skb)) 5682d0817d1SArnaldo Carvalho de Melo return -1; 5697c657876SArnaldo Carvalho de Melo } 5707c657876SArnaldo Carvalho de Melo 571507d37cfSArnaldo Carvalho de Melo if (dp->dccps_hc_rx_insert_options) { 5722d0817d1SArnaldo Carvalho de Melo if (ccid_hc_rx_insert_options(dp->dccps_hc_rx_ccid, sk, skb)) 5732d0817d1SArnaldo Carvalho de Melo return -1; 574507d37cfSArnaldo Carvalho de Melo dp->dccps_hc_rx_insert_options = 0; 575507d37cfSArnaldo Carvalho de Melo } 5767c657876SArnaldo Carvalho de Melo 577410e27a4SGerrit Renker /* Feature negotiation */ 578410e27a4SGerrit Renker /* Data packets can't do feat negotiation */ 579410e27a4SGerrit Renker if (DCCP_SKB_CB(skb)->dccpd_type != DCCP_PKT_DATA && 580410e27a4SGerrit Renker DCCP_SKB_CB(skb)->dccpd_type != DCCP_PKT_DATAACK && 581410e27a4SGerrit Renker dccp_insert_options_feat(sk, skb)) 582410e27a4SGerrit Renker return -1; 583410e27a4SGerrit Renker 584410e27a4SGerrit Renker /* 585410e27a4SGerrit Renker * Obtain RTT sample from Request/Response exchange. 586410e27a4SGerrit Renker * This is currently used in CCID 3 initialisation. 587410e27a4SGerrit Renker */ 588410e27a4SGerrit Renker if (DCCP_SKB_CB(skb)->dccpd_type == DCCP_PKT_REQUEST && 589410e27a4SGerrit Renker dccp_insert_option_timestamp(sk, skb)) 590410e27a4SGerrit Renker return -1; 591410e27a4SGerrit Renker 592b4d4f7c7SGerrit Renker if (dp->dccps_timestamp_echo != 0 && 593b4d4f7c7SGerrit Renker dccp_insert_option_timestamp_echo(dp, NULL, skb)) 594b4d4f7c7SGerrit Renker return -1; 595b4d4f7c7SGerrit Renker 596af3b867eSGerrit Renker dccp_insert_option_padding(skb); 597af3b867eSGerrit Renker return 0; 5987c657876SArnaldo Carvalho de Melo } 5992d0817d1SArnaldo Carvalho de Melo 600af3b867eSGerrit Renker int dccp_insert_options_rsk(struct dccp_request_sock *dreq, struct sk_buff *skb) 601af3b867eSGerrit Renker { 602af3b867eSGerrit Renker DCCP_SKB_CB(skb)->dccpd_opt_len = 0; 603af3b867eSGerrit Renker 604af3b867eSGerrit Renker if (dreq->dreq_timestamp_echo != 0 && 605af3b867eSGerrit Renker dccp_insert_option_timestamp_echo(NULL, dreq, skb)) 606af3b867eSGerrit Renker return -1; 607af3b867eSGerrit Renker 608af3b867eSGerrit Renker dccp_insert_option_padding(skb); 6092d0817d1SArnaldo Carvalho de Melo return 0; 6107c657876SArnaldo Carvalho de Melo } 611