xref: /openbmc/linux/net/dccp/options.c (revision df561f66)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
27c657876SArnaldo Carvalho de Melo /*
37c657876SArnaldo Carvalho de Melo  *  net/dccp/options.c
47c657876SArnaldo Carvalho de Melo  *
57c657876SArnaldo Carvalho de Melo  *  An implementation of the DCCP protocol
61bc09869SIan McDonald  *  Copyright (c) 2005 Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
71bc09869SIan McDonald  *  Copyright (c) 2005 Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
8e6bccd35SIan McDonald  *  Copyright (c) 2005 Ian McDonald <ian.mcdonald@jandi.co.nz>
97c657876SArnaldo Carvalho de Melo  */
107c657876SArnaldo Carvalho de Melo #include <linux/dccp.h>
117c657876SArnaldo Carvalho de Melo #include <linux/module.h>
127c657876SArnaldo Carvalho de Melo #include <linux/types.h>
1376fd1e87SGerrit Renker #include <asm/unaligned.h>
147c657876SArnaldo Carvalho de Melo #include <linux/kernel.h>
157c657876SArnaldo Carvalho de Melo #include <linux/skbuff.h>
167c657876SArnaldo Carvalho de Melo 
17ae31c339SArnaldo Carvalho de Melo #include "ackvec.h"
187c657876SArnaldo Carvalho de Melo #include "ccid.h"
197c657876SArnaldo Carvalho de Melo #include "dccp.h"
20afe00251SAndrea Bittau #include "feat.h"
217c657876SArnaldo Carvalho de Melo 
dccp_decode_value_var(const u8 * bf,const u8 len)2202fa460eSGerrit Renker u64 dccp_decode_value_var(const u8 *bf, const u8 len)
23410e27a4SGerrit Renker {
2402fa460eSGerrit Renker 	u64 value = 0;
25410e27a4SGerrit Renker 
2602fa460eSGerrit Renker 	if (len >= DCCP_OPTVAL_MAXLEN)
2702fa460eSGerrit Renker 		value += ((u64)*bf++) << 40;
2802fa460eSGerrit Renker 	if (len > 4)
2902fa460eSGerrit Renker 		value += ((u64)*bf++) << 32;
307c657876SArnaldo Carvalho de Melo 	if (len > 3)
3102fa460eSGerrit Renker 		value += ((u64)*bf++) << 24;
327c657876SArnaldo Carvalho de Melo 	if (len > 2)
3302fa460eSGerrit Renker 		value += ((u64)*bf++) << 16;
347c657876SArnaldo Carvalho de Melo 	if (len > 1)
3502fa460eSGerrit Renker 		value += ((u64)*bf++) << 8;
367c657876SArnaldo Carvalho de Melo 	if (len > 0)
377c657876SArnaldo Carvalho de Melo 		value += *bf;
387c657876SArnaldo Carvalho de Melo 
397c657876SArnaldo Carvalho de Melo 	return value;
407c657876SArnaldo Carvalho de Melo }
417c657876SArnaldo Carvalho de Melo 
428b819412SGerrit Renker /**
438b819412SGerrit Renker  * dccp_parse_options  -  Parse DCCP options present in @skb
448b819412SGerrit Renker  * @sk: client|server|listening dccp socket (when @dreq != NULL)
458b819412SGerrit Renker  * @dreq: request socket to use during connection setup, or NULL
46d0b1101bSAndrew Lunn  * @skb: frame to parse
478b819412SGerrit Renker  */
dccp_parse_options(struct sock * sk,struct dccp_request_sock * dreq,struct sk_buff * skb)488b819412SGerrit Renker int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq,
498b819412SGerrit Renker 		       struct sk_buff *skb)
507c657876SArnaldo Carvalho de Melo {
517c657876SArnaldo Carvalho de Melo 	struct dccp_sock *dp = dccp_sk(sk);
527c657876SArnaldo Carvalho de Melo 	const struct dccp_hdr *dh = dccp_hdr(skb);
537c657876SArnaldo Carvalho de Melo 	const u8 pkt_type = DCCP_SKB_CB(skb)->dccpd_type;
547c657876SArnaldo Carvalho de Melo 	unsigned char *options = (unsigned char *)dh + dccp_hdr_len(skb);
557c657876SArnaldo Carvalho de Melo 	unsigned char *opt_ptr = options;
567690af3fSArnaldo Carvalho de Melo 	const unsigned char *opt_end = (unsigned char *)dh +
577690af3fSArnaldo Carvalho de Melo 					(dh->dccph_doff * 4);
587c657876SArnaldo Carvalho de Melo 	struct dccp_options_received *opt_recv = &dp->dccps_options_received;
597c657876SArnaldo Carvalho de Melo 	unsigned char opt, len;
603f649ab7SKees Cook 	unsigned char *value;
611c14ac0aSArnaldo Carvalho de Melo 	u32 elapsed_time;
6276fd1e87SGerrit Renker 	__be32 opt_val;
63afe00251SAndrea Bittau 	int rc;
64afe00251SAndrea Bittau 	int mandatory = 0;
657c657876SArnaldo Carvalho de Melo 
667c657876SArnaldo Carvalho de Melo 	memset(opt_recv, 0, sizeof(*opt_recv));
677c657876SArnaldo Carvalho de Melo 
68fb950496SDavid S. Miller 	opt = len = 0;
697c657876SArnaldo Carvalho de Melo 	while (opt_ptr != opt_end) {
707c657876SArnaldo Carvalho de Melo 		opt   = *opt_ptr++;
717c657876SArnaldo Carvalho de Melo 		len   = 0;
727c657876SArnaldo Carvalho de Melo 		value = NULL;
737c657876SArnaldo Carvalho de Melo 
747c657876SArnaldo Carvalho de Melo 		/* Check if this isn't a single byte option */
757c657876SArnaldo Carvalho de Melo 		if (opt > DCCPO_MAX_RESERVED) {
767c657876SArnaldo Carvalho de Melo 			if (opt_ptr == opt_end)
77faf61c33SGerrit Renker 				goto out_nonsensical_length;
787c657876SArnaldo Carvalho de Melo 
797c657876SArnaldo Carvalho de Melo 			len = *opt_ptr++;
80faf61c33SGerrit Renker 			if (len < 2)
81faf61c33SGerrit Renker 				goto out_nonsensical_length;
827c657876SArnaldo Carvalho de Melo 			/*
837c657876SArnaldo Carvalho de Melo 			 * Remove the type and len fields, leaving
847c657876SArnaldo Carvalho de Melo 			 * just the value size
857c657876SArnaldo Carvalho de Melo 			 */
867c657876SArnaldo Carvalho de Melo 			len	-= 2;
877c657876SArnaldo Carvalho de Melo 			value	= opt_ptr;
887c657876SArnaldo Carvalho de Melo 			opt_ptr += len;
897c657876SArnaldo Carvalho de Melo 
907c657876SArnaldo Carvalho de Melo 			if (opt_ptr > opt_end)
91faf61c33SGerrit Renker 				goto out_nonsensical_length;
927c657876SArnaldo Carvalho de Melo 		}
937c657876SArnaldo Carvalho de Melo 
948b819412SGerrit Renker 		/*
958b819412SGerrit Renker 		 * CCID-specific options are ignored during connection setup, as
968b819412SGerrit Renker 		 * negotiation may still be in progress (see RFC 4340, 10.3).
9765907a43SGerrit Renker 		 * The same applies to Ack Vectors, as these depend on the CCID.
988b819412SGerrit Renker 		 */
99a18213d1SGerrit Renker 		if (dreq != NULL && (opt >= DCCPO_MIN_RX_CCID_SPECIFIC ||
10065907a43SGerrit Renker 		    opt == DCCPO_ACK_VECTOR_0 || opt == DCCPO_ACK_VECTOR_1))
1018b819412SGerrit Renker 			goto ignore_option;
1028b819412SGerrit Renker 
1037c657876SArnaldo Carvalho de Melo 		switch (opt) {
1047c657876SArnaldo Carvalho de Melo 		case DCCPO_PADDING:
1057c657876SArnaldo Carvalho de Melo 			break;
106afe00251SAndrea Bittau 		case DCCPO_MANDATORY:
107afe00251SAndrea Bittau 			if (mandatory)
108afe00251SAndrea Bittau 				goto out_invalid_option;
1096df9424aSArnaldo Carvalho de Melo 			if (pkt_type != DCCP_PKT_DATA)
110afe00251SAndrea Bittau 				mandatory = 1;
111afe00251SAndrea Bittau 			break;
1127c657876SArnaldo Carvalho de Melo 		case DCCPO_NDP_COUNT:
1135b5d0e70SGerrit Renker 			if (len > 6)
1147c657876SArnaldo Carvalho de Melo 				goto out_invalid_option;
1157c657876SArnaldo Carvalho de Melo 
1167c657876SArnaldo Carvalho de Melo 			opt_recv->dccpor_ndp = dccp_decode_value_var(value, len);
1175b5d0e70SGerrit Renker 			dccp_pr_debug("%s opt: NDP count=%llu\n", dccp_role(sk),
1185b5d0e70SGerrit Renker 				      (unsigned long long)opt_recv->dccpor_ndp);
1197c657876SArnaldo Carvalho de Melo 			break;
120b1ad0042SGerrit Renker 		case DCCPO_CHANGE_L ... DCCPO_CONFIRM_R:
121b1ad0042SGerrit Renker 			if (pkt_type == DCCP_PKT_DATA)      /* RFC 4340, 6 */
122cf86314cSGerrit Renker 				break;
123a2948659SDan Rosenberg 			if (len == 0)
124a2948659SDan Rosenberg 				goto out_invalid_option;
125e77b8363SGerrit Renker 			rc = dccp_feat_parse_options(sk, dreq, mandatory, opt,
126e77b8363SGerrit Renker 						    *value, value + 1, len - 1);
127e77b8363SGerrit Renker 			if (rc)
128e77b8363SGerrit Renker 				goto out_featneg_failed;
129410e27a4SGerrit Renker 			break;
1307c657876SArnaldo Carvalho de Melo 		case DCCPO_TIMESTAMP:
1317c657876SArnaldo Carvalho de Melo 			if (len != 4)
1327c657876SArnaldo Carvalho de Melo 				goto out_invalid_option;
133b4d4f7c7SGerrit Renker 			/*
134b4d4f7c7SGerrit Renker 			 * RFC 4340 13.1: "The precise time corresponding to
135b4d4f7c7SGerrit Renker 			 * Timestamp Value zero is not specified". We use
136b4d4f7c7SGerrit Renker 			 * zero to indicate absence of a meaningful timestamp.
137b4d4f7c7SGerrit Renker 			 */
13876fd1e87SGerrit Renker 			opt_val = get_unaligned((__be32 *)value);
139b4d4f7c7SGerrit Renker 			if (unlikely(opt_val == 0)) {
140b4d4f7c7SGerrit Renker 				DCCP_WARN("Timestamp with zero value\n");
141b4d4f7c7SGerrit Renker 				break;
142b4d4f7c7SGerrit Renker 			}
1437c657876SArnaldo Carvalho de Melo 
144b4d4f7c7SGerrit Renker 			if (dreq != NULL) {
145b4d4f7c7SGerrit Renker 				dreq->dreq_timestamp_echo = ntohl(opt_val);
146b4d4f7c7SGerrit Renker 				dreq->dreq_timestamp_time = dccp_timestamp();
147b4d4f7c7SGerrit Renker 			} else {
148b4d4f7c7SGerrit Renker 				opt_recv->dccpor_timestamp =
149b4d4f7c7SGerrit Renker 					dp->dccps_timestamp_echo = ntohl(opt_val);
150b4d4f7c7SGerrit Renker 				dp->dccps_timestamp_time = dccp_timestamp();
151b4d4f7c7SGerrit Renker 			}
15209dbc389SGerrit Renker 			dccp_pr_debug("%s rx opt: TIMESTAMP=%u, ackno=%llu\n",
153b4d4f7c7SGerrit Renker 				      dccp_role(sk), ntohl(opt_val),
154f6ccf554SDavid S. Miller 				      (unsigned long long)
1557c657876SArnaldo Carvalho de Melo 				      DCCP_SKB_CB(skb)->dccpd_ack_seq);
156ecdfbdabSGerrit Renker 			/* schedule an Ack in case this sender is quiescent */
157ecdfbdabSGerrit Renker 			inet_csk_schedule_ack(sk);
1587c657876SArnaldo Carvalho de Melo 			break;
1597c657876SArnaldo Carvalho de Melo 		case DCCPO_TIMESTAMP_ECHO:
1601bc09869SIan McDonald 			if (len != 4 && len != 6 && len != 8)
1617c657876SArnaldo Carvalho de Melo 				goto out_invalid_option;
1627c657876SArnaldo Carvalho de Melo 
16376fd1e87SGerrit Renker 			opt_val = get_unaligned((__be32 *)value);
16476fd1e87SGerrit Renker 			opt_recv->dccpor_timestamp_echo = ntohl(opt_val);
1657c657876SArnaldo Carvalho de Melo 
16609dbc389SGerrit Renker 			dccp_pr_debug("%s rx opt: TIMESTAMP_ECHO=%u, len=%d, "
167f73f7097SGerrit Renker 				      "ackno=%llu", dccp_role(sk),
1687690af3fSArnaldo Carvalho de Melo 				      opt_recv->dccpor_timestamp_echo,
169f6ccf554SDavid S. Miller 				      len + 2,
170f6ccf554SDavid S. Miller 				      (unsigned long long)
1711bc09869SIan McDonald 				      DCCP_SKB_CB(skb)->dccpd_ack_seq);
1727c657876SArnaldo Carvalho de Melo 
17376fd1e87SGerrit Renker 			value += 4;
1741bc09869SIan McDonald 
17576fd1e87SGerrit Renker 			if (len == 4) {		/* no elapsed time included */
176f73f7097SGerrit Renker 				dccp_pr_debug_cat("\n");
1771c14ac0aSArnaldo Carvalho de Melo 				break;
178f73f7097SGerrit Renker 			}
1791c14ac0aSArnaldo Carvalho de Melo 
18076fd1e87SGerrit Renker 			if (len == 6) {		/* 2-byte elapsed time */
18176fd1e87SGerrit Renker 				__be16 opt_val2 = get_unaligned((__be16 *)value);
18276fd1e87SGerrit Renker 				elapsed_time = ntohs(opt_val2);
18376fd1e87SGerrit Renker 			} else {		/* 4-byte elapsed time */
18476fd1e87SGerrit Renker 				opt_val = get_unaligned((__be32 *)value);
18576fd1e87SGerrit Renker 				elapsed_time = ntohl(opt_val);
18676fd1e87SGerrit Renker 			}
1871c14ac0aSArnaldo Carvalho de Melo 
188dcad856fSGerrit Renker 			dccp_pr_debug_cat(", ELAPSED_TIME=%u\n", elapsed_time);
189f73f7097SGerrit Renker 
1901c14ac0aSArnaldo Carvalho de Melo 			/* Give precedence to the biggest ELAPSED_TIME */
1911c14ac0aSArnaldo Carvalho de Melo 			if (elapsed_time > opt_recv->dccpor_elapsed_time)
1921c14ac0aSArnaldo Carvalho de Melo 				opt_recv->dccpor_elapsed_time = elapsed_time;
1937c657876SArnaldo Carvalho de Melo 			break;
1947c657876SArnaldo Carvalho de Melo 		case DCCPO_ELAPSED_TIME:
195c86ab2b6SGerrit Renker 			if (dccp_packet_without_ack(skb))   /* RFC 4340, 13.2 */
196c86ab2b6SGerrit Renker 				break;
1971bc09869SIan McDonald 
19876fd1e87SGerrit Renker 			if (len == 2) {
19976fd1e87SGerrit Renker 				__be16 opt_val2 = get_unaligned((__be16 *)value);
20076fd1e87SGerrit Renker 				elapsed_time = ntohs(opt_val2);
201c86ab2b6SGerrit Renker 			} else if (len == 4) {
20276fd1e87SGerrit Renker 				opt_val = get_unaligned((__be32 *)value);
20376fd1e87SGerrit Renker 				elapsed_time = ntohl(opt_val);
204c86ab2b6SGerrit Renker 			} else {
205c86ab2b6SGerrit Renker 				goto out_invalid_option;
20676fd1e87SGerrit Renker 			}
2071c14ac0aSArnaldo Carvalho de Melo 
2081c14ac0aSArnaldo Carvalho de Melo 			if (elapsed_time > opt_recv->dccpor_elapsed_time)
2091c14ac0aSArnaldo Carvalho de Melo 				opt_recv->dccpor_elapsed_time = elapsed_time;
2101bc09869SIan McDonald 
21109dbc389SGerrit Renker 			dccp_pr_debug("%s rx opt: ELAPSED_TIME=%d\n",
21209dbc389SGerrit Renker 				      dccp_role(sk), elapsed_time);
2137c657876SArnaldo Carvalho de Melo 			break;
214a18213d1SGerrit Renker 		case DCCPO_MIN_RX_CCID_SPECIFIC ... DCCPO_MAX_RX_CCID_SPECIFIC:
2157690af3fSArnaldo Carvalho de Melo 			if (ccid_hc_rx_parse_options(dp->dccps_hc_rx_ccid, sk,
2164874c131SGerrit Renker 						     pkt_type, opt, value, len))
2177c657876SArnaldo Carvalho de Melo 				goto out_invalid_option;
2187c657876SArnaldo Carvalho de Melo 			break;
2197e87fe84SGerrit Renker 		case DCCPO_ACK_VECTOR_0:
2207e87fe84SGerrit Renker 		case DCCPO_ACK_VECTOR_1:
2217e87fe84SGerrit Renker 			if (dccp_packet_without_ack(skb))   /* RFC 4340, 11.4 */
2227e87fe84SGerrit Renker 				break;
2237e87fe84SGerrit Renker 			/*
2247e87fe84SGerrit Renker 			 * Ack vectors are processed by the TX CCID if it is
2257e87fe84SGerrit Renker 			 * interested. The RX CCID need not parse Ack Vectors,
2267e87fe84SGerrit Renker 			 * since it is only interested in clearing old state.
2277e87fe84SGerrit Renker 			 */
228df561f66SGustavo A. R. Silva 			fallthrough;
229a18213d1SGerrit Renker 		case DCCPO_MIN_TX_CCID_SPECIFIC ... DCCPO_MAX_TX_CCID_SPECIFIC:
2307690af3fSArnaldo Carvalho de Melo 			if (ccid_hc_tx_parse_options(dp->dccps_hc_tx_ccid, sk,
2314874c131SGerrit Renker 						     pkt_type, opt, value, len))
2327c657876SArnaldo Carvalho de Melo 				goto out_invalid_option;
2337c657876SArnaldo Carvalho de Melo 			break;
2347c657876SArnaldo Carvalho de Melo 		default:
23559348b19SGerrit Renker 			DCCP_CRIT("DCCP(%p): option %d(len=%d) not "
23659348b19SGerrit Renker 				  "implemented, ignoring", sk, opt, len);
2377c657876SArnaldo Carvalho de Melo 			break;
2387c657876SArnaldo Carvalho de Melo 		}
2398b819412SGerrit Renker ignore_option:
240afe00251SAndrea Bittau 		if (opt != DCCPO_MANDATORY)
241afe00251SAndrea Bittau 			mandatory = 0;
2427c657876SArnaldo Carvalho de Melo 	}
2437c657876SArnaldo Carvalho de Melo 
2446df9424aSArnaldo Carvalho de Melo 	/* mandatory was the last byte in option list -> reset connection */
2456df9424aSArnaldo Carvalho de Melo 	if (mandatory)
2466df9424aSArnaldo Carvalho de Melo 		goto out_invalid_option;
2476df9424aSArnaldo Carvalho de Melo 
248faf61c33SGerrit Renker out_nonsensical_length:
249faf61c33SGerrit Renker 	/* RFC 4340, 5.8: ignore option and all remaining option space */
2507c657876SArnaldo Carvalho de Melo 	return 0;
2517c657876SArnaldo Carvalho de Melo 
2527c657876SArnaldo Carvalho de Melo out_invalid_option:
2537309f882SEric Dumazet 	DCCP_INC_STATS(DCCP_MIB_INVALIDOPT);
254e77b8363SGerrit Renker 	rc = DCCP_RESET_CODE_OPTION_ERROR;
255e77b8363SGerrit Renker out_featneg_failed:
256e77b8363SGerrit Renker 	DCCP_WARN("DCCP(%p): Option %d (len=%d) error=%u\n", sk, opt, len, rc);
257e77b8363SGerrit Renker 	DCCP_SKB_CB(skb)->dccpd_reset_code = rc;
258eac7726bSGerrit Renker 	DCCP_SKB_CB(skb)->dccpd_reset_data[0] = opt;
259eac7726bSGerrit Renker 	DCCP_SKB_CB(skb)->dccpd_reset_data[1] = len > 0 ? value[0] : 0;
260eac7726bSGerrit Renker 	DCCP_SKB_CB(skb)->dccpd_reset_data[2] = len > 1 ? value[1] : 0;
2617c657876SArnaldo Carvalho de Melo 	return -1;
2627c657876SArnaldo Carvalho de Melo }
2637c657876SArnaldo Carvalho de Melo 
264b61fafc4SArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_parse_options);
265b61fafc4SArnaldo Carvalho de Melo 
dccp_encode_value_var(const u64 value,u8 * to,const u8 len)26602fa460eSGerrit Renker void dccp_encode_value_var(const u64 value, u8 *to, const u8 len)
2677c657876SArnaldo Carvalho de Melo {
26802fa460eSGerrit Renker 	if (len >= DCCP_OPTVAL_MAXLEN)
26902fa460eSGerrit Renker 		*to++ = (value & 0xFF0000000000ull) >> 40;
27002fa460eSGerrit Renker 	if (len > 4)
27102fa460eSGerrit Renker 		*to++ = (value & 0xFF00000000ull) >> 32;
2727c657876SArnaldo Carvalho de Melo 	if (len > 3)
2737c657876SArnaldo Carvalho de Melo 		*to++ = (value & 0xFF000000) >> 24;
2747c657876SArnaldo Carvalho de Melo 	if (len > 2)
2757c657876SArnaldo Carvalho de Melo 		*to++ = (value & 0xFF0000) >> 16;
2767c657876SArnaldo Carvalho de Melo 	if (len > 1)
2777c657876SArnaldo Carvalho de Melo 		*to++ = (value & 0xFF00) >> 8;
2787c657876SArnaldo Carvalho de Melo 	if (len > 0)
2797c657876SArnaldo Carvalho de Melo 		*to++ = (value & 0xFF);
2807c657876SArnaldo Carvalho de Melo }
2817c657876SArnaldo Carvalho de Melo 
dccp_ndp_len(const u64 ndp)2825b5d0e70SGerrit Renker static inline u8 dccp_ndp_len(const u64 ndp)
2837c657876SArnaldo Carvalho de Melo {
2845b5d0e70SGerrit Renker 	if (likely(ndp <= 0xFF))
2855b5d0e70SGerrit Renker 		return 1;
2864be929beSAlexey Dobriyan 	return likely(ndp <= USHRT_MAX) ? 2 : (ndp <= UINT_MAX ? 4 : 6);
2877c657876SArnaldo Carvalho de Melo }
2887c657876SArnaldo Carvalho de Melo 
dccp_insert_option(struct sk_buff * skb,const unsigned char option,const void * value,const unsigned char len)289a7d13fbfSGerrit Renker int dccp_insert_option(struct sk_buff *skb, const unsigned char option,
2907c657876SArnaldo Carvalho de Melo 		       const void *value, const unsigned char len)
2917c657876SArnaldo Carvalho de Melo {
2927c657876SArnaldo Carvalho de Melo 	unsigned char *to;
2937c657876SArnaldo Carvalho de Melo 
2942d0817d1SArnaldo Carvalho de Melo 	if (DCCP_SKB_CB(skb)->dccpd_opt_len + len + 2 > DCCP_MAX_OPT_LEN)
2952d0817d1SArnaldo Carvalho de Melo 		return -1;
2967c657876SArnaldo Carvalho de Melo 
2977c657876SArnaldo Carvalho de Melo 	DCCP_SKB_CB(skb)->dccpd_opt_len += len + 2;
2987c657876SArnaldo Carvalho de Melo 
2997c657876SArnaldo Carvalho de Melo 	to    = skb_push(skb, len + 2);
3007c657876SArnaldo Carvalho de Melo 	*to++ = option;
3017c657876SArnaldo Carvalho de Melo 	*to++ = len + 2;
3027c657876SArnaldo Carvalho de Melo 
3037c657876SArnaldo Carvalho de Melo 	memcpy(to, value, len);
3042d0817d1SArnaldo Carvalho de Melo 	return 0;
3057c657876SArnaldo Carvalho de Melo }
3067c657876SArnaldo Carvalho de Melo 
3077c657876SArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(dccp_insert_option);
3087c657876SArnaldo Carvalho de Melo 
dccp_insert_option_ndp(struct sock * sk,struct sk_buff * skb)3092d0817d1SArnaldo Carvalho de Melo static int dccp_insert_option_ndp(struct sock *sk, struct sk_buff *skb)
3107c657876SArnaldo Carvalho de Melo {
3117c657876SArnaldo Carvalho de Melo 	struct dccp_sock *dp = dccp_sk(sk);
3125b5d0e70SGerrit Renker 	u64 ndp = dp->dccps_ndp_count;
3137c657876SArnaldo Carvalho de Melo 
3147c657876SArnaldo Carvalho de Melo 	if (dccp_non_data_packet(skb))
3157c657876SArnaldo Carvalho de Melo 		++dp->dccps_ndp_count;
3167c657876SArnaldo Carvalho de Melo 	else
3177c657876SArnaldo Carvalho de Melo 		dp->dccps_ndp_count = 0;
3187c657876SArnaldo Carvalho de Melo 
3197c657876SArnaldo Carvalho de Melo 	if (ndp > 0) {
3207c657876SArnaldo Carvalho de Melo 		unsigned char *ptr;
3217c657876SArnaldo Carvalho de Melo 		const int ndp_len = dccp_ndp_len(ndp);
3227c657876SArnaldo Carvalho de Melo 		const int len = ndp_len + 2;
3237c657876SArnaldo Carvalho de Melo 
3247c657876SArnaldo Carvalho de Melo 		if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN)
3252d0817d1SArnaldo Carvalho de Melo 			return -1;
3267c657876SArnaldo Carvalho de Melo 
3277c657876SArnaldo Carvalho de Melo 		DCCP_SKB_CB(skb)->dccpd_opt_len += len;
3287c657876SArnaldo Carvalho de Melo 
3297c657876SArnaldo Carvalho de Melo 		ptr = skb_push(skb, len);
3307c657876SArnaldo Carvalho de Melo 		*ptr++ = DCCPO_NDP_COUNT;
3317c657876SArnaldo Carvalho de Melo 		*ptr++ = len;
3327c657876SArnaldo Carvalho de Melo 		dccp_encode_value_var(ndp, ptr, ndp_len);
3337c657876SArnaldo Carvalho de Melo 	}
3342d0817d1SArnaldo Carvalho de Melo 
3352d0817d1SArnaldo Carvalho de Melo 	return 0;
3367c657876SArnaldo Carvalho de Melo }
3377c657876SArnaldo Carvalho de Melo 
dccp_elapsed_time_len(const u32 elapsed_time)3387c657876SArnaldo Carvalho de Melo static inline int dccp_elapsed_time_len(const u32 elapsed_time)
3397c657876SArnaldo Carvalho de Melo {
340b1c9fe7bSIan McDonald 	return elapsed_time == 0 ? 0 : elapsed_time <= 0xFFFF ? 2 : 4;
3417c657876SArnaldo Carvalho de Melo }
3427c657876SArnaldo Carvalho de Melo 
dccp_insert_option_timestamp(struct sk_buff * skb)3431f4f0f64Sstephen hemminger static int dccp_insert_option_timestamp(struct sk_buff *skb)
3447c657876SArnaldo Carvalho de Melo {
3454c70f383SGerrit Renker 	__be32 now = htonl(dccp_timestamp());
3461bc09869SIan McDonald 	/* yes this will overflow but that is the point as we want a
3471bc09869SIan McDonald 	 * 10 usec 32 bit timer which mean it wraps every 11.9 hours */
3481bc09869SIan McDonald 
349a7d13fbfSGerrit Renker 	return dccp_insert_option(skb, DCCPO_TIMESTAMP, &now, sizeof(now));
3507c657876SArnaldo Carvalho de Melo }
3517c657876SArnaldo Carvalho de Melo 
dccp_insert_option_timestamp_echo(struct dccp_sock * dp,struct dccp_request_sock * dreq,struct sk_buff * skb)352b4d4f7c7SGerrit Renker static int dccp_insert_option_timestamp_echo(struct dccp_sock *dp,
353b4d4f7c7SGerrit Renker 					     struct dccp_request_sock *dreq,
3547690af3fSArnaldo Carvalho de Melo 					     struct sk_buff *skb)
3557c657876SArnaldo Carvalho de Melo {
35660fe62e7SAndrea Bittau 	__be32 tstamp_echo;
3577c657876SArnaldo Carvalho de Melo 	unsigned char *to;
358b4d4f7c7SGerrit Renker 	u32 elapsed_time, elapsed_time_len, len;
359b4d4f7c7SGerrit Renker 
360b4d4f7c7SGerrit Renker 	if (dreq != NULL) {
361b4d4f7c7SGerrit Renker 		elapsed_time = dccp_timestamp() - dreq->dreq_timestamp_time;
362b4d4f7c7SGerrit Renker 		tstamp_echo  = htonl(dreq->dreq_timestamp_echo);
363b4d4f7c7SGerrit Renker 		dreq->dreq_timestamp_echo = 0;
364b4d4f7c7SGerrit Renker 	} else {
365b4d4f7c7SGerrit Renker 		elapsed_time = dccp_timestamp() - dp->dccps_timestamp_time;
366b4d4f7c7SGerrit Renker 		tstamp_echo  = htonl(dp->dccps_timestamp_echo);
367b4d4f7c7SGerrit Renker 		dp->dccps_timestamp_echo = 0;
368b4d4f7c7SGerrit Renker 	}
369b4d4f7c7SGerrit Renker 
370b0e56780SArnaldo Carvalho de Melo 	elapsed_time_len = dccp_elapsed_time_len(elapsed_time);
371b0e56780SArnaldo Carvalho de Melo 	len = 6 + elapsed_time_len;
372b0e56780SArnaldo Carvalho de Melo 
3732d0817d1SArnaldo Carvalho de Melo 	if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN)
3742d0817d1SArnaldo Carvalho de Melo 		return -1;
3757c657876SArnaldo Carvalho de Melo 
3767c657876SArnaldo Carvalho de Melo 	DCCP_SKB_CB(skb)->dccpd_opt_len += len;
3777c657876SArnaldo Carvalho de Melo 
3787c657876SArnaldo Carvalho de Melo 	to    = skb_push(skb, len);
3797c657876SArnaldo Carvalho de Melo 	*to++ = DCCPO_TIMESTAMP_ECHO;
3807c657876SArnaldo Carvalho de Melo 	*to++ = len;
3817c657876SArnaldo Carvalho de Melo 
3827c657876SArnaldo Carvalho de Melo 	memcpy(to, &tstamp_echo, 4);
3837c657876SArnaldo Carvalho de Melo 	to += 4;
3841bc09869SIan McDonald 
3851bc09869SIan McDonald 	if (elapsed_time_len == 2) {
38660fe62e7SAndrea Bittau 		const __be16 var16 = htons((u16)elapsed_time);
3871bc09869SIan McDonald 		memcpy(to, &var16, 2);
3881bc09869SIan McDonald 	} else if (elapsed_time_len == 4) {
38960fe62e7SAndrea Bittau 		const __be32 var32 = htonl(elapsed_time);
3901bc09869SIan McDonald 		memcpy(to, &var32, 4);
3911bc09869SIan McDonald 	}
3927c657876SArnaldo Carvalho de Melo 
3932d0817d1SArnaldo Carvalho de Melo 	return 0;
3947c657876SArnaldo Carvalho de Melo }
3957c657876SArnaldo Carvalho de Melo 
dccp_insert_option_ackvec(struct sock * sk,struct sk_buff * skb)3967d870936SGerrit Renker static int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb)
3977d870936SGerrit Renker {
3987d870936SGerrit Renker 	struct dccp_sock *dp = dccp_sk(sk);
3997d870936SGerrit Renker 	struct dccp_ackvec *av = dp->dccps_hc_rx_ackvec;
400d83447f0SGerrit Renker 	struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb);
401b3d14bffSGerrit Renker 	const u16 buflen = dccp_ackvec_buflen(av);
4027d870936SGerrit Renker 	/* Figure out how many options do we need to represent the ackvec */
403b3d14bffSGerrit Renker 	const u8 nr_opts = DIV_ROUND_UP(buflen, DCCP_SINGLE_OPT_MAXLEN);
404b3d14bffSGerrit Renker 	u16 len = buflen + 2 * nr_opts;
4057d870936SGerrit Renker 	u8 i, nonce = 0;
4067d870936SGerrit Renker 	const unsigned char *tail, *from;
4077d870936SGerrit Renker 	unsigned char *to;
4087d870936SGerrit Renker 
409d83447f0SGerrit Renker 	if (dcb->dccpd_opt_len + len > DCCP_MAX_OPT_LEN) {
410d83447f0SGerrit Renker 		DCCP_WARN("Lacking space for %u bytes on %s packet\n", len,
411d83447f0SGerrit Renker 			  dccp_packet_name(dcb->dccpd_type));
4127d870936SGerrit Renker 		return -1;
413d83447f0SGerrit Renker 	}
414d83447f0SGerrit Renker 	/*
415d83447f0SGerrit Renker 	 * Since Ack Vectors are variable-length, we can not always predict
416d83447f0SGerrit Renker 	 * their size. To catch exception cases where the space is running out
417d83447f0SGerrit Renker 	 * on the skb, a separate Sync is scheduled to carry the Ack Vector.
418d83447f0SGerrit Renker 	 */
419d83447f0SGerrit Renker 	if (len > DCCPAV_MIN_OPTLEN &&
420d83447f0SGerrit Renker 	    len + dcb->dccpd_opt_len + skb->len > dp->dccps_mss_cache) {
421d83447f0SGerrit Renker 		DCCP_WARN("No space left for Ack Vector (%u) on skb (%u+%u), "
422d83447f0SGerrit Renker 			  "MPS=%u ==> reduce payload size?\n", len, skb->len,
423d83447f0SGerrit Renker 			  dcb->dccpd_opt_len, dp->dccps_mss_cache);
424d83447f0SGerrit Renker 		dp->dccps_sync_scheduled = 1;
425d83447f0SGerrit Renker 		return 0;
426d83447f0SGerrit Renker 	}
427d83447f0SGerrit Renker 	dcb->dccpd_opt_len += len;
4287d870936SGerrit Renker 
4297d870936SGerrit Renker 	to   = skb_push(skb, len);
430b3d14bffSGerrit Renker 	len  = buflen;
4317d870936SGerrit Renker 	from = av->av_buf + av->av_buf_head;
4327d870936SGerrit Renker 	tail = av->av_buf + DCCPAV_MAX_ACKVEC_LEN;
4337d870936SGerrit Renker 
4347d870936SGerrit Renker 	for (i = 0; i < nr_opts; ++i) {
4357d870936SGerrit Renker 		int copylen = len;
4367d870936SGerrit Renker 
4377d870936SGerrit Renker 		if (len > DCCP_SINGLE_OPT_MAXLEN)
4387d870936SGerrit Renker 			copylen = DCCP_SINGLE_OPT_MAXLEN;
4397d870936SGerrit Renker 
4407d870936SGerrit Renker 		/*
4417d870936SGerrit Renker 		 * RFC 4340, 12.2: Encode the Nonce Echo for this Ack Vector via
4427d870936SGerrit Renker 		 * its type; ack_nonce is the sum of all individual buf_nonce's.
4437d870936SGerrit Renker 		 */
4447d870936SGerrit Renker 		nonce ^= av->av_buf_nonce[i];
4457d870936SGerrit Renker 
4467d870936SGerrit Renker 		*to++ = DCCPO_ACK_VECTOR_0 + av->av_buf_nonce[i];
4477d870936SGerrit Renker 		*to++ = copylen + 2;
4487d870936SGerrit Renker 
4497d870936SGerrit Renker 		/* Check if buf_head wraps */
4507d870936SGerrit Renker 		if (from + copylen > tail) {
4517d870936SGerrit Renker 			const u16 tailsize = tail - from;
4527d870936SGerrit Renker 
4537d870936SGerrit Renker 			memcpy(to, from, tailsize);
4547d870936SGerrit Renker 			to	+= tailsize;
4557d870936SGerrit Renker 			len	-= tailsize;
4567d870936SGerrit Renker 			copylen	-= tailsize;
4577d870936SGerrit Renker 			from	= av->av_buf;
4587d870936SGerrit Renker 		}
4597d870936SGerrit Renker 
4607d870936SGerrit Renker 		memcpy(to, from, copylen);
4617d870936SGerrit Renker 		from += copylen;
4627d870936SGerrit Renker 		to   += copylen;
4637d870936SGerrit Renker 		len  -= copylen;
4647d870936SGerrit Renker 	}
4657d870936SGerrit Renker 	/*
4667d870936SGerrit Renker 	 * Each sent Ack Vector is recorded in the list, as per A.2 of RFC 4340.
4677d870936SGerrit Renker 	 */
468d83447f0SGerrit Renker 	if (dccp_ackvec_update_records(av, dcb->dccpd_seq, nonce))
4697d870936SGerrit Renker 		return -ENOBUFS;
4707d870936SGerrit Renker 	return 0;
4717d870936SGerrit Renker }
4727d870936SGerrit Renker 
473d3710566SGerrit Renker /**
474d3710566SGerrit Renker  * dccp_insert_option_mandatory  -  Mandatory option (5.8.2)
475d0b1101bSAndrew Lunn  * @skb: frame into which to insert option
476d0b1101bSAndrew Lunn  *
477d3710566SGerrit Renker  * Note that since we are using skb_push, this function needs to be called
478d3710566SGerrit Renker  * _after_ inserting the option it is supposed to influence (stack order).
479d3710566SGerrit Renker  */
dccp_insert_option_mandatory(struct sk_buff * skb)480d3710566SGerrit Renker int dccp_insert_option_mandatory(struct sk_buff *skb)
481d3710566SGerrit Renker {
482d3710566SGerrit Renker 	if (DCCP_SKB_CB(skb)->dccpd_opt_len >= DCCP_MAX_OPT_LEN)
483d3710566SGerrit Renker 		return -1;
484d3710566SGerrit Renker 
485d3710566SGerrit Renker 	DCCP_SKB_CB(skb)->dccpd_opt_len++;
486d58ff351SJohannes Berg 	*(u8 *)skb_push(skb, 1) = DCCPO_MANDATORY;
487d3710566SGerrit Renker 	return 0;
488d3710566SGerrit Renker }
489d3710566SGerrit Renker 
4908c862c23SGerrit Renker /**
4918c862c23SGerrit Renker  * dccp_insert_fn_opt  -  Insert single Feature-Negotiation option into @skb
492d0b1101bSAndrew Lunn  * @skb: frame to insert feature negotiation option into
4938c862c23SGerrit Renker  * @type: %DCCPO_CHANGE_L, %DCCPO_CHANGE_R, %DCCPO_CONFIRM_L, %DCCPO_CONFIRM_R
4948c862c23SGerrit Renker  * @feat: one out of %dccp_feature_numbers
4958c862c23SGerrit Renker  * @val: NN value or SP array (preferred element first) to copy
4968c862c23SGerrit Renker  * @len: true length of @val in bytes (excluding first element repetition)
4978c862c23SGerrit Renker  * @repeat_first: whether to copy the first element of @val twice
4982c53040fSBen Hutchings  *
4998c862c23SGerrit Renker  * The last argument is used to construct Confirm options, where the preferred
5008c862c23SGerrit Renker  * value and the preference list appear separately (RFC 4340, 6.3.1). Preference
5018c862c23SGerrit Renker  * lists are kept such that the preferred entry is always first, so we only need
5028c862c23SGerrit Renker  * to copy twice, and avoid the overhead of cloning into a bigger array.
5038c862c23SGerrit Renker  */
dccp_insert_fn_opt(struct sk_buff * skb,u8 type,u8 feat,u8 * val,u8 len,bool repeat_first)5048c862c23SGerrit Renker int dccp_insert_fn_opt(struct sk_buff *skb, u8 type, u8 feat,
5058c862c23SGerrit Renker 		       u8 *val, u8 len, bool repeat_first)
5064829007cSGerrit Renker {
5078c862c23SGerrit Renker 	u8 tot_len, *to;
5084829007cSGerrit Renker 
5098c862c23SGerrit Renker 	/* take the `Feature' field and possible repetition into account */
5108c862c23SGerrit Renker 	if (len > (DCCP_SINGLE_OPT_MAXLEN - 2)) {
5118c862c23SGerrit Renker 		DCCP_WARN("length %u for feature %u too large\n", len, feat);
512cf9ddf73SGerrit Renker 		return -1;
513cf9ddf73SGerrit Renker 	}
514cf9ddf73SGerrit Renker 
5158c862c23SGerrit Renker 	if (unlikely(val == NULL || len == 0))
5163db1cd5cSRusty Russell 		len = repeat_first = false;
5178c862c23SGerrit Renker 	tot_len = 3 + repeat_first + len;
518410e27a4SGerrit Renker 
5198c862c23SGerrit Renker 	if (DCCP_SKB_CB(skb)->dccpd_opt_len + tot_len > DCCP_MAX_OPT_LEN) {
5208c862c23SGerrit Renker 		DCCP_WARN("packet too small for feature %d option!\n", feat);
5218c862c23SGerrit Renker 		return -1;
5228c862c23SGerrit Renker 	}
5238c862c23SGerrit Renker 	DCCP_SKB_CB(skb)->dccpd_opt_len += tot_len;
5248c862c23SGerrit Renker 
5258c862c23SGerrit Renker 	to    = skb_push(skb, tot_len);
526afe00251SAndrea Bittau 	*to++ = type;
5278c862c23SGerrit Renker 	*to++ = tot_len;
528afe00251SAndrea Bittau 	*to++ = feat;
529afe00251SAndrea Bittau 
5308c862c23SGerrit Renker 	if (repeat_first)
5318c862c23SGerrit Renker 		*to++ = *val;
532afe00251SAndrea Bittau 	if (len)
533afe00251SAndrea Bittau 		memcpy(to, val, len);
534410e27a4SGerrit Renker 	return 0;
535410e27a4SGerrit Renker }
536410e27a4SGerrit Renker 
537af3b867eSGerrit Renker /* The length of all options needs to be a multiple of 4 (5.8) */
dccp_insert_option_padding(struct sk_buff * skb)538af3b867eSGerrit Renker static void dccp_insert_option_padding(struct sk_buff *skb)
539af3b867eSGerrit Renker {
540af3b867eSGerrit Renker 	int padding = DCCP_SKB_CB(skb)->dccpd_opt_len % 4;
541af3b867eSGerrit Renker 
542af3b867eSGerrit Renker 	if (padding != 0) {
543af3b867eSGerrit Renker 		padding = 4 - padding;
544af3b867eSGerrit Renker 		memset(skb_push(skb, padding), 0, padding);
545af3b867eSGerrit Renker 		DCCP_SKB_CB(skb)->dccpd_opt_len += padding;
546af3b867eSGerrit Renker 	}
547af3b867eSGerrit Renker }
548af3b867eSGerrit Renker 
dccp_insert_options(struct sock * sk,struct sk_buff * skb)5492d0817d1SArnaldo Carvalho de Melo int dccp_insert_options(struct sock *sk, struct sk_buff *skb)
5507c657876SArnaldo Carvalho de Melo {
5517c657876SArnaldo Carvalho de Melo 	struct dccp_sock *dp = dccp_sk(sk);
5527c657876SArnaldo Carvalho de Melo 
5537c657876SArnaldo Carvalho de Melo 	DCCP_SKB_CB(skb)->dccpd_opt_len = 0;
5547c657876SArnaldo Carvalho de Melo 
5554098dce5SGerrit Renker 	if (dp->dccps_send_ndp_count && dccp_insert_option_ndp(sk, skb))
5562d0817d1SArnaldo Carvalho de Melo 		return -1;
5577c657876SArnaldo Carvalho de Melo 
5588b7b6c75SGerrit Renker 	if (DCCP_SKB_CB(skb)->dccpd_type != DCCP_PKT_DATA) {
5598b7b6c75SGerrit Renker 
5608b7b6c75SGerrit Renker 		/* Feature Negotiation */
5618b7b6c75SGerrit Renker 		if (dccp_feat_insert_opts(dp, NULL, skb))
5622d0817d1SArnaldo Carvalho de Melo 			return -1;
5638b7b6c75SGerrit Renker 
5648b7b6c75SGerrit Renker 		if (DCCP_SKB_CB(skb)->dccpd_type == DCCP_PKT_REQUEST) {
5658b7b6c75SGerrit Renker 			/*
5668b7b6c75SGerrit Renker 			 * Obtain RTT sample from Request/Response exchange.
56759b80802SGerrit Renker 			 * This is currently used for TFRC initialisation.
5688b7b6c75SGerrit Renker 			 */
569a7d13fbfSGerrit Renker 			if (dccp_insert_option_timestamp(skb))
5708b7b6c75SGerrit Renker 				return -1;
5718b7b6c75SGerrit Renker 
572b3d14bffSGerrit Renker 		} else if (dccp_ackvec_pending(sk) &&
5738b7b6c75SGerrit Renker 			   dccp_insert_option_ackvec(sk, skb)) {
5748b7b6c75SGerrit Renker 				return -1;
5758b7b6c75SGerrit Renker 		}
5767c657876SArnaldo Carvalho de Melo 	}
5777c657876SArnaldo Carvalho de Melo 
578507d37cfSArnaldo Carvalho de Melo 	if (dp->dccps_hc_rx_insert_options) {
5792d0817d1SArnaldo Carvalho de Melo 		if (ccid_hc_rx_insert_options(dp->dccps_hc_rx_ccid, sk, skb))
5802d0817d1SArnaldo Carvalho de Melo 			return -1;
581507d37cfSArnaldo Carvalho de Melo 		dp->dccps_hc_rx_insert_options = 0;
582507d37cfSArnaldo Carvalho de Melo 	}
5837c657876SArnaldo Carvalho de Melo 
584b4d4f7c7SGerrit Renker 	if (dp->dccps_timestamp_echo != 0 &&
585b4d4f7c7SGerrit Renker 	    dccp_insert_option_timestamp_echo(dp, NULL, skb))
586b4d4f7c7SGerrit Renker 		return -1;
587b4d4f7c7SGerrit Renker 
588af3b867eSGerrit Renker 	dccp_insert_option_padding(skb);
589af3b867eSGerrit Renker 	return 0;
5907c657876SArnaldo Carvalho de Melo }
5912d0817d1SArnaldo Carvalho de Melo 
dccp_insert_options_rsk(struct dccp_request_sock * dreq,struct sk_buff * skb)592af3b867eSGerrit Renker int dccp_insert_options_rsk(struct dccp_request_sock *dreq, struct sk_buff *skb)
593af3b867eSGerrit Renker {
594af3b867eSGerrit Renker 	DCCP_SKB_CB(skb)->dccpd_opt_len = 0;
595af3b867eSGerrit Renker 
5968b7b6c75SGerrit Renker 	if (dccp_feat_insert_opts(NULL, dreq, skb))
5978b7b6c75SGerrit Renker 		return -1;
5988b7b6c75SGerrit Renker 
59959b80802SGerrit Renker 	/* Obtain RTT sample from Response/Ack exchange (used by TFRC). */
60059b80802SGerrit Renker 	if (dccp_insert_option_timestamp(skb))
60159b80802SGerrit Renker 		return -1;
60259b80802SGerrit Renker 
603af3b867eSGerrit Renker 	if (dreq->dreq_timestamp_echo != 0 &&
604af3b867eSGerrit Renker 	    dccp_insert_option_timestamp_echo(NULL, dreq, skb))
605af3b867eSGerrit Renker 		return -1;
606af3b867eSGerrit Renker 
607af3b867eSGerrit Renker 	dccp_insert_option_padding(skb);
6082d0817d1SArnaldo Carvalho de Melo 	return 0;
6097c657876SArnaldo Carvalho de Melo }
610