blob: d361b5533309dc3310564adf483e9e1900c4573f [file] [log] [blame]
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001/*
2 * net/dccp/options.c
3 *
4 * An implementation of the DCCP protocol
Ian McDonald1bc09862005-08-20 00:23:43 -03005 * Copyright (c) 2005 Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
6 * Copyright (c) 2005 Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
Ian McDonalde6bccd32006-08-26 19:01:30 -07007 * Copyright (c) 2005 Ian McDonald <ian.mcdonald@jandi.co.nz>
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07008 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version
12 * 2 of the License, or (at your option) any later version.
13 */
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070014#include <linux/dccp.h>
15#include <linux/module.h>
16#include <linux/types.h>
17#include <linux/kernel.h>
18#include <linux/skbuff.h>
19
Arnaldo Carvalho de Meloae31c332005-09-18 00:17:51 -070020#include "ackvec.h"
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070021#include "ccid.h"
22#include "dccp.h"
Andrea Bittauafe00252006-03-20 17:43:56 -080023#include "feat.h"
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070024
Gerrit Renkerafb0a342006-11-13 13:25:41 -020025int sysctl_dccp_feat_sequence_window = DCCPF_INITIAL_SEQUENCE_WINDOW;
26int sysctl_dccp_feat_rx_ccid = DCCPF_INITIAL_CCID;
27int sysctl_dccp_feat_tx_ccid = DCCPF_INITIAL_CCID;
28int sysctl_dccp_feat_ack_ratio = DCCPF_INITIAL_ACK_RATIO;
29int sysctl_dccp_feat_send_ack_vector = DCCPF_INITIAL_SEND_ACK_VECTOR;
30int sysctl_dccp_feat_send_ndp_count = DCCPF_INITIAL_SEND_NDP_COUNT;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070031
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070032static u32 dccp_decode_value_var(const unsigned char *bf, const u8 len)
33{
34 u32 value = 0;
35
36 if (len > 3)
37 value += *bf++ << 24;
38 if (len > 2)
39 value += *bf++ << 16;
40 if (len > 1)
41 value += *bf++ << 8;
42 if (len > 0)
43 value += *bf;
44
45 return value;
46}
47
48int dccp_parse_options(struct sock *sk, struct sk_buff *skb)
49{
50 struct dccp_sock *dp = dccp_sk(sk);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070051 const struct dccp_hdr *dh = dccp_hdr(skb);
52 const u8 pkt_type = DCCP_SKB_CB(skb)->dccpd_type;
Andrea Bittaubdf13d22006-11-24 13:02:42 -020053 u64 ackno = DCCP_SKB_CB(skb)->dccpd_ack_seq;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070054 unsigned char *options = (unsigned char *)dh + dccp_hdr_len(skb);
55 unsigned char *opt_ptr = options;
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -030056 const unsigned char *opt_end = (unsigned char *)dh +
57 (dh->dccph_doff * 4);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070058 struct dccp_options_received *opt_recv = &dp->dccps_options_received;
59 unsigned char opt, len;
60 unsigned char *value;
Arnaldo Carvalho de Melo1c14ac02005-09-09 02:32:01 -030061 u32 elapsed_time;
Andrea Bittauafe00252006-03-20 17:43:56 -080062 int rc;
63 int mandatory = 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070064
65 memset(opt_recv, 0, sizeof(*opt_recv));
66
David S. Millerfb950492006-03-20 22:36:01 -080067 opt = len = 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070068 while (opt_ptr != opt_end) {
69 opt = *opt_ptr++;
70 len = 0;
71 value = NULL;
72
73 /* Check if this isn't a single byte option */
74 if (opt > DCCPO_MAX_RESERVED) {
75 if (opt_ptr == opt_end)
76 goto out_invalid_option;
77
78 len = *opt_ptr++;
79 if (len < 3)
80 goto out_invalid_option;
81 /*
82 * Remove the type and len fields, leaving
83 * just the value size
84 */
85 len -= 2;
86 value = opt_ptr;
87 opt_ptr += len;
88
89 if (opt_ptr > opt_end)
90 goto out_invalid_option;
91 }
92
93 switch (opt) {
94 case DCCPO_PADDING:
95 break;
Andrea Bittauafe00252006-03-20 17:43:56 -080096 case DCCPO_MANDATORY:
97 if (mandatory)
98 goto out_invalid_option;
Arnaldo Carvalho de Melo6df94242006-03-20 22:06:02 -080099 if (pkt_type != DCCP_PKT_DATA)
100 mandatory = 1;
Andrea Bittauafe00252006-03-20 17:43:56 -0800101 break;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700102 case DCCPO_NDP_COUNT:
103 if (len > 3)
104 goto out_invalid_option;
105
106 opt_recv->dccpor_ndp = dccp_decode_value_var(value, len);
Gerrit Renker09dbc382006-11-14 12:57:34 -0200107 dccp_pr_debug("%s rx opt: NDP count=%d\n", dccp_role(sk),
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -0300108 opt_recv->dccpor_ndp);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700109 break;
Andrea Bittauafe00252006-03-20 17:43:56 -0800110 case DCCPO_CHANGE_L:
111 /* fall through */
112 case DCCPO_CHANGE_R:
113 if (len < 2)
114 goto out_invalid_option;
115 rc = dccp_feat_change_recv(sk, opt, *value, value + 1,
116 len - 1);
117 /*
118 * When there is a change error, change_recv is
119 * responsible for dealing with it. i.e. reply with an
120 * empty confirm.
121 * If the change was mandatory, then we need to die.
122 */
123 if (rc && mandatory)
124 goto out_invalid_option;
125 break;
126 case DCCPO_CONFIRM_L:
127 /* fall through */
128 case DCCPO_CONFIRM_R:
129 if (len < 2)
130 goto out_invalid_option;
131 if (dccp_feat_confirm_recv(sk, opt, *value,
132 value + 1, len - 1))
133 goto out_invalid_option;
134 break;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700135 case DCCPO_ACK_VECTOR_0:
Arnaldo Carvalho de Meloae31c332005-09-18 00:17:51 -0700136 case DCCPO_ACK_VECTOR_1:
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700137 if (pkt_type == DCCP_PKT_DATA)
Arnaldo Carvalho de Meloe5a6de92006-03-20 22:30:51 -0800138 break;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700139
Arnaldo Carvalho de Meloa4bf3902006-03-20 22:50:58 -0800140 if (dccp_msk(sk)->dccpms_send_ack_vector &&
Andrea Bittaubdf13d22006-11-24 13:02:42 -0200141 dccp_ackvec_parse(sk, skb, &ackno, opt, value, len))
Arnaldo Carvalho de Meloae31c332005-09-18 00:17:51 -0700142 goto out_invalid_option;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700143 break;
144 case DCCPO_TIMESTAMP:
145 if (len != 4)
146 goto out_invalid_option;
147
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800148 opt_recv->dccpor_timestamp = ntohl(*(__be32 *)value);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700149
150 dp->dccps_timestamp_echo = opt_recv->dccpor_timestamp;
Arnaldo Carvalho de Melo19ac2142007-08-19 17:18:33 -0700151 dp->dccps_timestamp_time = ktime_get_real();
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700152
Gerrit Renker09dbc382006-11-14 12:57:34 -0200153 dccp_pr_debug("%s rx opt: TIMESTAMP=%u, ackno=%llu\n",
154 dccp_role(sk), opt_recv->dccpor_timestamp,
David S. Millerf6ccf552005-08-09 20:27:14 -0700155 (unsigned long long)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700156 DCCP_SKB_CB(skb)->dccpd_ack_seq);
157 break;
158 case DCCPO_TIMESTAMP_ECHO:
Ian McDonald1bc09862005-08-20 00:23:43 -0300159 if (len != 4 && len != 6 && len != 8)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700160 goto out_invalid_option;
161
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800162 opt_recv->dccpor_timestamp_echo = ntohl(*(__be32 *)value);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700163
Gerrit Renker09dbc382006-11-14 12:57:34 -0200164 dccp_pr_debug("%s rx opt: TIMESTAMP_ECHO=%u, len=%d, "
Gerrit Renkerf73f7092007-04-20 13:56:47 -0700165 "ackno=%llu", dccp_role(sk),
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -0300166 opt_recv->dccpor_timestamp_echo,
David S. Millerf6ccf552005-08-09 20:27:14 -0700167 len + 2,
168 (unsigned long long)
Ian McDonald1bc09862005-08-20 00:23:43 -0300169 DCCP_SKB_CB(skb)->dccpd_ack_seq);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700170
Ian McDonald1bc09862005-08-20 00:23:43 -0300171
Gerrit Renkerf73f7092007-04-20 13:56:47 -0700172 if (len == 4) {
173 dccp_pr_debug_cat("\n");
Arnaldo Carvalho de Melo1c14ac02005-09-09 02:32:01 -0300174 break;
Gerrit Renkerf73f7092007-04-20 13:56:47 -0700175 }
Arnaldo Carvalho de Melo1c14ac02005-09-09 02:32:01 -0300176
177 if (len == 6)
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800178 elapsed_time = ntohs(*(__be16 *)(value + 4));
Arnaldo Carvalho de Melo1c14ac02005-09-09 02:32:01 -0300179 else
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800180 elapsed_time = ntohl(*(__be32 *)(value + 4));
Arnaldo Carvalho de Melo1c14ac02005-09-09 02:32:01 -0300181
Gerrit Renkerdcad8562007-10-04 14:44:01 -0700182 dccp_pr_debug_cat(", ELAPSED_TIME=%u\n", elapsed_time);
Gerrit Renkerf73f7092007-04-20 13:56:47 -0700183
Arnaldo Carvalho de Melo1c14ac02005-09-09 02:32:01 -0300184 /* Give precedence to the biggest ELAPSED_TIME */
185 if (elapsed_time > opt_recv->dccpor_elapsed_time)
186 opt_recv->dccpor_elapsed_time = elapsed_time;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700187 break;
188 case DCCPO_ELAPSED_TIME:
Ian McDonald1bc09862005-08-20 00:23:43 -0300189 if (len != 2 && len != 4)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700190 goto out_invalid_option;
191
192 if (pkt_type == DCCP_PKT_DATA)
193 continue;
Ian McDonald1bc09862005-08-20 00:23:43 -0300194
195 if (len == 2)
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800196 elapsed_time = ntohs(*(__be16 *)value);
Ian McDonald1bc09862005-08-20 00:23:43 -0300197 else
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800198 elapsed_time = ntohl(*(__be32 *)value);
Arnaldo Carvalho de Melo1c14ac02005-09-09 02:32:01 -0300199
200 if (elapsed_time > opt_recv->dccpor_elapsed_time)
201 opt_recv->dccpor_elapsed_time = elapsed_time;
Ian McDonald1bc09862005-08-20 00:23:43 -0300202
Gerrit Renker09dbc382006-11-14 12:57:34 -0200203 dccp_pr_debug("%s rx opt: ELAPSED_TIME=%d\n",
204 dccp_role(sk), elapsed_time);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700205 break;
206 /*
Gerrit Renker0e64e942006-10-24 16:17:51 -0700207 * From RFC 4340, sec. 10.3:
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700208 *
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -0300209 * Option numbers 128 through 191 are for
210 * options sent from the HC-Sender to the
211 * HC-Receiver; option numbers 192 through 255
212 * are for options sent from the HC-Receiver to
213 * the HC-Sender.
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700214 */
215 case 128 ... 191: {
216 const u16 idx = value - options;
217
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -0300218 if (ccid_hc_rx_parse_options(dp->dccps_hc_rx_ccid, sk,
219 opt, len, idx,
220 value) != 0)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700221 goto out_invalid_option;
222 }
223 break;
224 case 192 ... 255: {
225 const u16 idx = value - options;
226
Arnaldo Carvalho de Melo7690af32005-08-13 20:34:54 -0300227 if (ccid_hc_tx_parse_options(dp->dccps_hc_tx_ccid, sk,
228 opt, len, idx,
229 value) != 0)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700230 goto out_invalid_option;
231 }
232 break;
233 default:
Gerrit Renker59348b12006-11-20 18:39:23 -0200234 DCCP_CRIT("DCCP(%p): option %d(len=%d) not "
235 "implemented, ignoring", sk, opt, len);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700236 break;
YOSHIFUJI Hideakic9eaf172007-02-09 23:24:38 +0900237 }
Andrea Bittauafe00252006-03-20 17:43:56 -0800238
239 if (opt != DCCPO_MANDATORY)
240 mandatory = 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700241 }
242
Arnaldo Carvalho de Melo6df94242006-03-20 22:06:02 -0800243 /* mandatory was the last byte in option list -> reset connection */
244 if (mandatory)
245 goto out_invalid_option;
246
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700247 return 0;
248
249out_invalid_option:
250 DCCP_INC_STATS_BH(DCCP_MIB_INVALIDOPT);
251 DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_OPTION_ERROR;
Gerrit Renker59348b12006-11-20 18:39:23 -0200252 DCCP_WARN("DCCP(%p): invalid option %d, len=%d", sk, opt, len);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700253 return -1;
254}
255
Arnaldo Carvalho de Melob61fafc2006-03-20 21:25:11 -0800256EXPORT_SYMBOL_GPL(dccp_parse_options);
257
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700258static void dccp_encode_value_var(const u32 value, unsigned char *to,
259 const unsigned int len)
260{
261 if (len > 3)
262 *to++ = (value & 0xFF000000) >> 24;
263 if (len > 2)
264 *to++ = (value & 0xFF0000) >> 16;
265 if (len > 1)
266 *to++ = (value & 0xFF00) >> 8;
267 if (len > 0)
268 *to++ = (value & 0xFF);
269}
270
271static inline int dccp_ndp_len(const int ndp)
272{
273 return likely(ndp <= 0xFF) ? 1 : ndp <= 0xFFFF ? 2 : 3;
274}
275
Arnaldo Carvalho de Melo2d0817d2006-03-20 22:32:06 -0800276int dccp_insert_option(struct sock *sk, struct sk_buff *skb,
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700277 const unsigned char option,
278 const void *value, const unsigned char len)
279{
280 unsigned char *to;
281
Arnaldo Carvalho de Melo2d0817d2006-03-20 22:32:06 -0800282 if (DCCP_SKB_CB(skb)->dccpd_opt_len + len + 2 > DCCP_MAX_OPT_LEN)
283 return -1;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700284
285 DCCP_SKB_CB(skb)->dccpd_opt_len += len + 2;
286
287 to = skb_push(skb, len + 2);
288 *to++ = option;
289 *to++ = len + 2;
290
291 memcpy(to, value, len);
Arnaldo Carvalho de Melo2d0817d2006-03-20 22:32:06 -0800292 return 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700293}
294
295EXPORT_SYMBOL_GPL(dccp_insert_option);
296
Arnaldo Carvalho de Melo2d0817d2006-03-20 22:32:06 -0800297static int dccp_insert_option_ndp(struct sock *sk, struct sk_buff *skb)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700298{
299 struct dccp_sock *dp = dccp_sk(sk);
300 int ndp = dp->dccps_ndp_count;
301
302 if (dccp_non_data_packet(skb))
303 ++dp->dccps_ndp_count;
304 else
305 dp->dccps_ndp_count = 0;
306
307 if (ndp > 0) {
308 unsigned char *ptr;
309 const int ndp_len = dccp_ndp_len(ndp);
310 const int len = ndp_len + 2;
311
312 if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN)
Arnaldo Carvalho de Melo2d0817d2006-03-20 22:32:06 -0800313 return -1;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700314
315 DCCP_SKB_CB(skb)->dccpd_opt_len += len;
316
317 ptr = skb_push(skb, len);
318 *ptr++ = DCCPO_NDP_COUNT;
319 *ptr++ = len;
320 dccp_encode_value_var(ndp, ptr, ndp_len);
321 }
Arnaldo Carvalho de Melo2d0817d2006-03-20 22:32:06 -0800322
323 return 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700324}
325
326static inline int dccp_elapsed_time_len(const u32 elapsed_time)
327{
Ian McDonaldb1c9fe72005-08-18 20:45:29 -0300328 return elapsed_time == 0 ? 0 : elapsed_time <= 0xFFFF ? 2 : 4;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700329}
330
Arnaldo Carvalho de Melo2d0817d2006-03-20 22:32:06 -0800331int dccp_insert_option_elapsed_time(struct sock *sk, struct sk_buff *skb,
332 u32 elapsed_time)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700333{
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700334 const int elapsed_time_len = dccp_elapsed_time_len(elapsed_time);
335 const int len = 2 + elapsed_time_len;
336 unsigned char *to;
337
Ian McDonald1bc09862005-08-20 00:23:43 -0300338 if (elapsed_time_len == 0)
Arnaldo Carvalho de Melo2d0817d2006-03-20 22:32:06 -0800339 return 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700340
Arnaldo Carvalho de Melo2d0817d2006-03-20 22:32:06 -0800341 if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN)
342 return -1;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700343
344 DCCP_SKB_CB(skb)->dccpd_opt_len += len;
345
346 to = skb_push(skb, len);
347 *to++ = DCCPO_ELAPSED_TIME;
348 *to++ = len;
349
Ian McDonald1bc09862005-08-20 00:23:43 -0300350 if (elapsed_time_len == 2) {
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800351 const __be16 var16 = htons((u16)elapsed_time);
Ian McDonald1bc09862005-08-20 00:23:43 -0300352 memcpy(to, &var16, 2);
353 } else {
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800354 const __be32 var32 = htonl(elapsed_time);
Ian McDonald1bc09862005-08-20 00:23:43 -0300355 memcpy(to, &var32, 4);
356 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700357
Arnaldo Carvalho de Melo2d0817d2006-03-20 22:32:06 -0800358 return 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700359}
360
Arnaldo Carvalho de Melod4b81ff2005-08-23 21:51:36 -0700361EXPORT_SYMBOL_GPL(dccp_insert_option_elapsed_time);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700362
Arnaldo Carvalho de Melo2d0817d2006-03-20 22:32:06 -0800363int dccp_insert_option_timestamp(struct sock *sk, struct sk_buff *skb)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700364{
Gerrit Renker4c70f382007-09-25 22:40:13 -0700365 __be32 now = htonl(dccp_timestamp());
Ian McDonald1bc09862005-08-20 00:23:43 -0300366 /* yes this will overflow but that is the point as we want a
367 * 10 usec 32 bit timer which mean it wraps every 11.9 hours */
368
Arnaldo Carvalho de Melo2d0817d2006-03-20 22:32:06 -0800369 return dccp_insert_option(sk, skb, DCCPO_TIMESTAMP, &now, sizeof(now));
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700370}
371
Arnaldo Carvalho de Melod4b81ff2005-08-23 21:51:36 -0700372EXPORT_SYMBOL_GPL(dccp_insert_option_timestamp);
373
Arnaldo Carvalho de Melo2d0817d2006-03-20 22:32:06 -0800374static int dccp_insert_option_timestamp_echo(struct sock *sk,
375 struct sk_buff *skb)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700376{
377 struct dccp_sock *dp = dccp_sk(sk);
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800378 __be32 tstamp_echo;
Arnaldo Carvalho de Melob0e56782005-09-09 02:38:35 -0300379 int len, elapsed_time_len;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700380 unsigned char *to;
Arnaldo Carvalho de Melo19ac2142007-08-19 17:18:33 -0700381 const suseconds_t delta = ktime_us_delta(ktime_get_real(),
382 dp->dccps_timestamp_time);
383 u32 elapsed_time = delta / 10;
Arnaldo Carvalho de Melob0e56782005-09-09 02:38:35 -0300384 elapsed_time_len = dccp_elapsed_time_len(elapsed_time);
385 len = 6 + elapsed_time_len;
386
Arnaldo Carvalho de Melo2d0817d2006-03-20 22:32:06 -0800387 if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN)
388 return -1;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700389
390 DCCP_SKB_CB(skb)->dccpd_opt_len += len;
391
392 to = skb_push(skb, len);
393 *to++ = DCCPO_TIMESTAMP_ECHO;
394 *to++ = len;
395
396 tstamp_echo = htonl(dp->dccps_timestamp_echo);
397 memcpy(to, &tstamp_echo, 4);
398 to += 4;
Andrea Bittauafe00252006-03-20 17:43:56 -0800399
Ian McDonald1bc09862005-08-20 00:23:43 -0300400 if (elapsed_time_len == 2) {
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800401 const __be16 var16 = htons((u16)elapsed_time);
Ian McDonald1bc09862005-08-20 00:23:43 -0300402 memcpy(to, &var16, 2);
403 } else if (elapsed_time_len == 4) {
Andrea Bittau60fe62e2006-03-20 19:23:32 -0800404 const __be32 var32 = htonl(elapsed_time);
Ian McDonald1bc09862005-08-20 00:23:43 -0300405 memcpy(to, &var32, 4);
406 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700407
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700408 dp->dccps_timestamp_echo = 0;
Arnaldo Carvalho de Melo19ac2142007-08-19 17:18:33 -0700409 dp->dccps_timestamp_time = ktime_set(0, 0);
Arnaldo Carvalho de Melo2d0817d2006-03-20 22:32:06 -0800410 return 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700411}
412
Andrea Bittauafe00252006-03-20 17:43:56 -0800413static int dccp_insert_feat_opt(struct sk_buff *skb, u8 type, u8 feat,
YOSHIFUJI Hideakic9eaf172007-02-09 23:24:38 +0900414 u8 *val, u8 len)
Andrea Bittauafe00252006-03-20 17:43:56 -0800415{
416 u8 *to;
417
418 if (DCCP_SKB_CB(skb)->dccpd_opt_len + len + 3 > DCCP_MAX_OPT_LEN) {
Gerrit Renker59348b12006-11-20 18:39:23 -0200419 DCCP_WARN("packet too small for feature %d option!\n", feat);
Andrea Bittauafe00252006-03-20 17:43:56 -0800420 return -1;
421 }
422
423 DCCP_SKB_CB(skb)->dccpd_opt_len += len + 3;
424
425 to = skb_push(skb, len + 3);
426 *to++ = type;
427 *to++ = len + 3;
428 *to++ = feat;
429
430 if (len)
431 memcpy(to, val, len);
Andrea Bittauafe00252006-03-20 17:43:56 -0800432
Gerrit Renkerc02fdc02006-11-14 12:48:10 -0200433 dccp_pr_debug("%s(%s (%d), ...), length %d\n",
434 dccp_feat_typename(type),
435 dccp_feat_name(feat), feat, len);
Andrea Bittauafe00252006-03-20 17:43:56 -0800436 return 0;
437}
438
Arnaldo Carvalho de Melo2d0817d2006-03-20 22:32:06 -0800439static int dccp_insert_options_feat(struct sock *sk, struct sk_buff *skb)
Andrea Bittauafe00252006-03-20 17:43:56 -0800440{
441 struct dccp_sock *dp = dccp_sk(sk);
Arnaldo Carvalho de Meloa4bf3902006-03-20 22:50:58 -0800442 struct dccp_minisock *dmsk = dccp_msk(sk);
Andrea Bittauafe00252006-03-20 17:43:56 -0800443 struct dccp_opt_pend *opt, *next;
444 int change = 0;
445
446 /* confirm any options [NN opts] */
Arnaldo Carvalho de Meloa4bf3902006-03-20 22:50:58 -0800447 list_for_each_entry_safe(opt, next, &dmsk->dccpms_conf, dccpop_node) {
Andrea Bittauafe00252006-03-20 17:43:56 -0800448 dccp_insert_feat_opt(skb, opt->dccpop_type,
449 opt->dccpop_feat, opt->dccpop_val,
450 opt->dccpop_len);
451 /* fear empty confirms */
452 if (opt->dccpop_val)
453 kfree(opt->dccpop_val);
454 kfree(opt);
455 }
Arnaldo Carvalho de Meloa4bf3902006-03-20 22:50:58 -0800456 INIT_LIST_HEAD(&dmsk->dccpms_conf);
Andrea Bittauafe00252006-03-20 17:43:56 -0800457
458 /* see which features we need to send */
Arnaldo Carvalho de Meloa4bf3902006-03-20 22:50:58 -0800459 list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) {
Andrea Bittauafe00252006-03-20 17:43:56 -0800460 /* see if we need to send any confirm */
461 if (opt->dccpop_sc) {
462 dccp_insert_feat_opt(skb, opt->dccpop_type + 1,
463 opt->dccpop_feat,
464 opt->dccpop_sc->dccpoc_val,
465 opt->dccpop_sc->dccpoc_len);
466
467 BUG_ON(!opt->dccpop_sc->dccpoc_val);
468 kfree(opt->dccpop_sc->dccpoc_val);
469 kfree(opt->dccpop_sc);
470 opt->dccpop_sc = NULL;
471 }
472
473 /* any option not confirmed, re-send it */
474 if (!opt->dccpop_conf) {
475 dccp_insert_feat_opt(skb, opt->dccpop_type,
476 opt->dccpop_feat, opt->dccpop_val,
477 opt->dccpop_len);
478 change++;
479 }
480 }
481
482 /* Retransmit timer.
483 * If this is the master listening sock, we don't set a timer on it. It
484 * should be fine because if the dude doesn't receive our RESPONSE
485 * [which will contain the CHANGE] he will send another REQUEST which
486 * will "retrnasmit" the change.
487 */
488 if (change && dp->dccps_role != DCCP_ROLE_LISTEN) {
489 dccp_pr_debug("reset feat negotiation timer %p\n", sk);
490
491 /* XXX don't reset the timer on re-transmissions. I.e. reset it
492 * only when sending new stuff i guess. Currently the timer
493 * never backs off because on re-transmission it just resets it!
494 */
495 inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
496 inet_csk(sk)->icsk_rto, DCCP_RTO_MAX);
497 }
Arnaldo Carvalho de Melo2d0817d2006-03-20 22:32:06 -0800498
499 return 0;
Andrea Bittauafe00252006-03-20 17:43:56 -0800500}
501
Arnaldo Carvalho de Melo2d0817d2006-03-20 22:32:06 -0800502int dccp_insert_options(struct sock *sk, struct sk_buff *skb)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700503{
504 struct dccp_sock *dp = dccp_sk(sk);
Arnaldo Carvalho de Meloa4bf3902006-03-20 22:50:58 -0800505 struct dccp_minisock *dmsk = dccp_msk(sk);
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700506
507 DCCP_SKB_CB(skb)->dccpd_opt_len = 0;
508
Arnaldo Carvalho de Meloa4bf3902006-03-20 22:50:58 -0800509 if (dmsk->dccpms_send_ndp_count &&
Arnaldo Carvalho de Melo2d0817d2006-03-20 22:32:06 -0800510 dccp_insert_option_ndp(sk, skb))
511 return -1;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700512
513 if (!dccp_packet_without_ack(skb)) {
Arnaldo Carvalho de Meloa4bf3902006-03-20 22:50:58 -0800514 if (dmsk->dccpms_send_ack_vector &&
Arnaldo Carvalho de Melo2d0817d2006-03-20 22:32:06 -0800515 dccp_ackvec_pending(dp->dccps_hc_rx_ackvec) &&
516 dccp_insert_option_ackvec(sk, skb))
517 return -1;
518
519 if (dp->dccps_timestamp_echo != 0 &&
520 dccp_insert_option_timestamp_echo(sk, skb))
521 return -1;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700522 }
523
Arnaldo Carvalho de Melo507d37c2005-09-09 02:30:07 -0300524 if (dp->dccps_hc_rx_insert_options) {
Arnaldo Carvalho de Melo2d0817d2006-03-20 22:32:06 -0800525 if (ccid_hc_rx_insert_options(dp->dccps_hc_rx_ccid, sk, skb))
526 return -1;
Arnaldo Carvalho de Melo507d37c2005-09-09 02:30:07 -0300527 dp->dccps_hc_rx_insert_options = 0;
528 }
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700529
Andrea Bittauafe00252006-03-20 17:43:56 -0800530 /* Feature negotiation */
Arnaldo Carvalho de Melo2d0817d2006-03-20 22:32:06 -0800531 /* Data packets can't do feat negotiation */
532 if (DCCP_SKB_CB(skb)->dccpd_type != DCCP_PKT_DATA &&
533 DCCP_SKB_CB(skb)->dccpd_type != DCCP_PKT_DATAACK &&
534 dccp_insert_options_feat(sk, skb))
535 return -1;
Andrea Bittauafe00252006-03-20 17:43:56 -0800536
Gerrit Renker89560b52007-03-20 15:27:17 -0300537 /*
538 * Obtain RTT sample from Request/Response exchange.
539 * This is currently used in CCID 3 initialisation.
540 */
541 if (DCCP_SKB_CB(skb)->dccpd_type == DCCP_PKT_REQUEST &&
542 dccp_insert_option_timestamp(sk, skb))
543 return -1;
544
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700545 /* XXX: insert other options when appropriate */
546
547 if (DCCP_SKB_CB(skb)->dccpd_opt_len != 0) {
548 /* The length of all options has to be a multiple of 4 */
549 int padding = DCCP_SKB_CB(skb)->dccpd_opt_len % 4;
550
551 if (padding != 0) {
552 padding = 4 - padding;
553 memset(skb_push(skb, padding), 0, padding);
554 DCCP_SKB_CB(skb)->dccpd_opt_len += padding;
555 }
556 }
Arnaldo Carvalho de Melo2d0817d2006-03-20 22:32:06 -0800557
558 return 0;
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700559}