aboutsummaryrefslogtreecommitdiffstats
path: root/net/dccp/options.c
diff options
context:
space:
mode:
authorAndrea Bittau <a.bittau@cs.ucl.ac.uk>2006-03-20 20:43:56 -0500
committerDavid S. Miller <davem@davemloft.net>2006-03-20 20:43:56 -0500
commitafe00251dd9b53d51de91ff0099961f42bbf3754 (patch)
treea56aa987140662cf3e6e65be402b8591298c5ced /net/dccp/options.c
parent2a91aa3967398fb94eccc8da67c82bce9f67afdf (diff)
[DCCP]: Initial feature negotiation implementation
Still needs more work, but boots and doesn't crashes, even does some negotiation! 18:38:52.174934 127.0.0.1.43458 > 127.0.0.1.5001: request <change_l ack_ratio 2, change_r ccid 2, change_l ccid 2> 18:38:52.218526 127.0.0.1.5001 > 127.0.0.1.43458: response <nop, nop, change_l ack_ratio 2, confirm_r ccid 2 2, confirm_l ccid 2 2, confirm_r ack_ratio 2> 18:38:52.185398 127.0.0.1.43458 > 127.0.0.1.5001: <nop, confirm_r ack_ratio 2, ack_vector0 0x00, elapsed_time 212> :-) Signed-off-by: Andrea Bittau <a.bittau@cs.ucl.ac.uk> Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/dccp/options.c')
-rw-r--r--net/dccp/options.c139
1 files changed, 137 insertions, 2 deletions
diff --git a/net/dccp/options.c b/net/dccp/options.c
index 0a76426c9aea..7f99306c8e99 100644
--- a/net/dccp/options.c
+++ b/net/dccp/options.c
@@ -21,12 +21,14 @@
21#include "ackvec.h" 21#include "ackvec.h"
22#include "ccid.h" 22#include "ccid.h"
23#include "dccp.h" 23#include "dccp.h"
24#include "feat.h"
24 25
25/* stores the default values for new connection. may be changed with sysctl */ 26/* stores the default values for new connection. may be changed with sysctl */
26static const struct dccp_options dccpo_default_values = { 27static const struct dccp_options dccpo_default_values = {
27 .dccpo_sequence_window = DCCPF_INITIAL_SEQUENCE_WINDOW, 28 .dccpo_sequence_window = DCCPF_INITIAL_SEQUENCE_WINDOW,
28 .dccpo_rx_ccid = DCCPF_INITIAL_CCID, 29 .dccpo_rx_ccid = DCCPF_INITIAL_CCID,
29 .dccpo_tx_ccid = DCCPF_INITIAL_CCID, 30 .dccpo_tx_ccid = DCCPF_INITIAL_CCID,
31 .dccpo_ack_ratio = DCCPF_INITIAL_ACK_RATIO,
30 .dccpo_send_ack_vector = DCCPF_INITIAL_SEND_ACK_VECTOR, 32 .dccpo_send_ack_vector = DCCPF_INITIAL_SEND_ACK_VECTOR,
31 .dccpo_send_ndp_count = DCCPF_INITIAL_SEND_NDP_COUNT, 33 .dccpo_send_ndp_count = DCCPF_INITIAL_SEND_NDP_COUNT,
32}; 34};
@@ -69,6 +71,8 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb)
69 unsigned char opt, len; 71 unsigned char opt, len;
70 unsigned char *value; 72 unsigned char *value;
71 u32 elapsed_time; 73 u32 elapsed_time;
74 int rc;
75 int mandatory = 0;
72 76
73 memset(opt_recv, 0, sizeof(*opt_recv)); 77 memset(opt_recv, 0, sizeof(*opt_recv));
74 78
@@ -100,6 +104,11 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb)
100 switch (opt) { 104 switch (opt) {
101 case DCCPO_PADDING: 105 case DCCPO_PADDING:
102 break; 106 break;
107 case DCCPO_MANDATORY:
108 if (mandatory)
109 goto out_invalid_option;
110 mandatory = 1;
111 break;
103 case DCCPO_NDP_COUNT: 112 case DCCPO_NDP_COUNT:
104 if (len > 3) 113 if (len > 3)
105 goto out_invalid_option; 114 goto out_invalid_option;
@@ -108,6 +117,31 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb)
108 dccp_pr_debug("%sNDP count=%d\n", debug_prefix, 117 dccp_pr_debug("%sNDP count=%d\n", debug_prefix,
109 opt_recv->dccpor_ndp); 118 opt_recv->dccpor_ndp);
110 break; 119 break;
120 case DCCPO_CHANGE_L:
121 /* fall through */
122 case DCCPO_CHANGE_R:
123 if (len < 2)
124 goto out_invalid_option;
125 rc = dccp_feat_change_recv(sk, opt, *value, value + 1,
126 len - 1);
127 /*
128 * When there is a change error, change_recv is
129 * responsible for dealing with it. i.e. reply with an
130 * empty confirm.
131 * If the change was mandatory, then we need to die.
132 */
133 if (rc && mandatory)
134 goto out_invalid_option;
135 break;
136 case DCCPO_CONFIRM_L:
137 /* fall through */
138 case DCCPO_CONFIRM_R:
139 if (len < 2)
140 goto out_invalid_option;
141 if (dccp_feat_confirm_recv(sk, opt, *value,
142 value + 1, len - 1))
143 goto out_invalid_option;
144 break;
111 case DCCPO_ACK_VECTOR_0: 145 case DCCPO_ACK_VECTOR_0:
112 case DCCPO_ACK_VECTOR_1: 146 case DCCPO_ACK_VECTOR_1:
113 if (pkt_type == DCCP_PKT_DATA) 147 if (pkt_type == DCCP_PKT_DATA)
@@ -208,6 +242,9 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb)
208 sk, opt, len); 242 sk, opt, len);
209 break; 243 break;
210 } 244 }
245
246 if (opt != DCCPO_MANDATORY)
247 mandatory = 0;
211 } 248 }
212 249
213 return 0; 250 return 0;
@@ -356,7 +393,7 @@ void dccp_insert_option_timestamp(struct sock *sk, struct sk_buff *skb)
356{ 393{
357 struct timeval tv; 394 struct timeval tv;
358 u32 now; 395 u32 now;
359 396
360 dccp_timestamp(sk, &tv); 397 dccp_timestamp(sk, &tv);
361 now = timeval_usecs(&tv) / 10; 398 now = timeval_usecs(&tv) / 10;
362 /* yes this will overflow but that is the point as we want a 399 /* yes this will overflow but that is the point as we want a
@@ -402,7 +439,7 @@ static void dccp_insert_option_timestamp_echo(struct sock *sk,
402 tstamp_echo = htonl(dp->dccps_timestamp_echo); 439 tstamp_echo = htonl(dp->dccps_timestamp_echo);
403 memcpy(to, &tstamp_echo, 4); 440 memcpy(to, &tstamp_echo, 4);
404 to += 4; 441 to += 4;
405 442
406 if (elapsed_time_len == 2) { 443 if (elapsed_time_len == 2) {
407 const u16 var16 = htons((u16)elapsed_time); 444 const u16 var16 = htons((u16)elapsed_time);
408 memcpy(to, &var16, 2); 445 memcpy(to, &var16, 2);
@@ -421,6 +458,93 @@ static void dccp_insert_option_timestamp_echo(struct sock *sk,
421 dp->dccps_timestamp_time.tv_usec = 0; 458 dp->dccps_timestamp_time.tv_usec = 0;
422} 459}
423 460
461static int dccp_insert_feat_opt(struct sk_buff *skb, u8 type, u8 feat,
462 u8 *val, u8 len)
463{
464 u8 *to;
465
466 if (DCCP_SKB_CB(skb)->dccpd_opt_len + len + 3 > DCCP_MAX_OPT_LEN) {
467 LIMIT_NETDEBUG(KERN_INFO "DCCP: packet too small"
468 " to insert feature %d option!\n", feat);
469 return -1;
470 }
471
472 DCCP_SKB_CB(skb)->dccpd_opt_len += len + 3;
473
474 to = skb_push(skb, len + 3);
475 *to++ = type;
476 *to++ = len + 3;
477 *to++ = feat;
478
479 if (len)
480 memcpy(to, val, len);
481 dccp_pr_debug("option %d feat %d len %d\n", type, feat, len);
482
483 return 0;
484}
485
486static void dccp_insert_feat(struct sock *sk, struct sk_buff *skb)
487{
488 struct dccp_sock *dp = dccp_sk(sk);
489 struct dccp_opt_pend *opt, *next;
490 int change = 0;
491
492 /* confirm any options [NN opts] */
493 list_for_each_entry_safe(opt, next, &dp->dccps_options.dccpo_conf,
494 dccpop_node) {
495 dccp_insert_feat_opt(skb, opt->dccpop_type,
496 opt->dccpop_feat, opt->dccpop_val,
497 opt->dccpop_len);
498 /* fear empty confirms */
499 if (opt->dccpop_val)
500 kfree(opt->dccpop_val);
501 kfree(opt);
502 }
503 INIT_LIST_HEAD(&dp->dccps_options.dccpo_conf);
504
505 /* see which features we need to send */
506 list_for_each_entry(opt, &dp->dccps_options.dccpo_pending,
507 dccpop_node) {
508 /* see if we need to send any confirm */
509 if (opt->dccpop_sc) {
510 dccp_insert_feat_opt(skb, opt->dccpop_type + 1,
511 opt->dccpop_feat,
512 opt->dccpop_sc->dccpoc_val,
513 opt->dccpop_sc->dccpoc_len);
514
515 BUG_ON(!opt->dccpop_sc->dccpoc_val);
516 kfree(opt->dccpop_sc->dccpoc_val);
517 kfree(opt->dccpop_sc);
518 opt->dccpop_sc = NULL;
519 }
520
521 /* any option not confirmed, re-send it */
522 if (!opt->dccpop_conf) {
523 dccp_insert_feat_opt(skb, opt->dccpop_type,
524 opt->dccpop_feat, opt->dccpop_val,
525 opt->dccpop_len);
526 change++;
527 }
528 }
529
530 /* Retransmit timer.
531 * If this is the master listening sock, we don't set a timer on it. It
532 * should be fine because if the dude doesn't receive our RESPONSE
533 * [which will contain the CHANGE] he will send another REQUEST which
534 * will "retrnasmit" the change.
535 */
536 if (change && dp->dccps_role != DCCP_ROLE_LISTEN) {
537 dccp_pr_debug("reset feat negotiation timer %p\n", sk);
538
539 /* XXX don't reset the timer on re-transmissions. I.e. reset it
540 * only when sending new stuff i guess. Currently the timer
541 * never backs off because on re-transmission it just resets it!
542 */
543 inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
544 inet_csk(sk)->icsk_rto, DCCP_RTO_MAX);
545 }
546}
547
424void dccp_insert_options(struct sock *sk, struct sk_buff *skb) 548void dccp_insert_options(struct sock *sk, struct sk_buff *skb)
425{ 549{
426 struct dccp_sock *dp = dccp_sk(sk); 550 struct dccp_sock *dp = dccp_sk(sk);
@@ -447,6 +571,17 @@ void dccp_insert_options(struct sock *sk, struct sk_buff *skb)
447 dp->dccps_hc_tx_insert_options = 0; 571 dp->dccps_hc_tx_insert_options = 0;
448 } 572 }
449 573
574 /* Feature negotiation */
575 switch(DCCP_SKB_CB(skb)->dccpd_type) {
576 /* Data packets can't do feat negotiation */
577 case DCCP_PKT_DATA:
578 case DCCP_PKT_DATAACK:
579 break;
580 default:
581 dccp_insert_feat(sk, skb);
582 break;
583 }
584
450 /* XXX: insert other options when appropriate */ 585 /* XXX: insert other options when appropriate */
451 586
452 if (DCCP_SKB_CB(skb)->dccpd_opt_len != 0) { 587 if (DCCP_SKB_CB(skb)->dccpd_opt_len != 0) {