X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=net%2Fdccp%2Foptions.c;h=3ecd319c0f598b5869fb227786d4a0fb4a25b37a;hb=46f09ffa7db595f49fb42338e013417756816d37;hp=0a76426c9aeab322f6eae5dd8e8e2309e1454d98;hpb=2d300fece23a4ae638b43ce9a7ac1e566ac6c843;p=linux-2.6-omap-h63xx.git diff --git a/net/dccp/options.c b/net/dccp/options.c index 0a76426c9ae..3ecd319c0f5 100644 --- a/net/dccp/options.c +++ b/net/dccp/options.c @@ -21,19 +21,23 @@ #include "ackvec.h" #include "ccid.h" #include "dccp.h" +#include "feat.h" -/* stores the default values for new connection. may be changed with sysctl */ -static const struct dccp_options dccpo_default_values = { - .dccpo_sequence_window = DCCPF_INITIAL_SEQUENCE_WINDOW, - .dccpo_rx_ccid = DCCPF_INITIAL_CCID, - .dccpo_tx_ccid = DCCPF_INITIAL_CCID, - .dccpo_send_ack_vector = DCCPF_INITIAL_SEND_ACK_VECTOR, - .dccpo_send_ndp_count = DCCPF_INITIAL_SEND_NDP_COUNT, -}; +int dccp_feat_default_sequence_window = DCCPF_INITIAL_SEQUENCE_WINDOW; +int dccp_feat_default_rx_ccid = DCCPF_INITIAL_CCID; +int dccp_feat_default_tx_ccid = DCCPF_INITIAL_CCID; +int dccp_feat_default_ack_ratio = DCCPF_INITIAL_ACK_RATIO; +int dccp_feat_default_send_ack_vector = DCCPF_INITIAL_SEND_ACK_VECTOR; +int dccp_feat_default_send_ndp_count = DCCPF_INITIAL_SEND_NDP_COUNT; void dccp_options_init(struct dccp_options *dccpo) { - memcpy(dccpo, &dccpo_default_values, sizeof(*dccpo)); + dccpo->dccpo_sequence_window = dccp_feat_default_sequence_window; + dccpo->dccpo_rx_ccid = dccp_feat_default_rx_ccid; + dccpo->dccpo_tx_ccid = dccp_feat_default_tx_ccid; + dccpo->dccpo_ack_ratio = dccp_feat_default_ack_ratio; + dccpo->dccpo_send_ack_vector = dccp_feat_default_send_ack_vector; + dccpo->dccpo_send_ndp_count = dccp_feat_default_send_ndp_count; } static u32 dccp_decode_value_var(const unsigned char *bf, const u8 len) @@ -69,6 +73,8 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb) unsigned char opt, len; unsigned char *value; u32 elapsed_time; + int rc; + int mandatory = 0; memset(opt_recv, 0, sizeof(*opt_recv)); @@ -100,6 +106,11 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb) switch (opt) { case DCCPO_PADDING: break; + case DCCPO_MANDATORY: + if (mandatory) + goto out_invalid_option; + mandatory = 1; + break; case DCCPO_NDP_COUNT: if (len > 3) goto out_invalid_option; @@ -108,6 +119,31 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb) dccp_pr_debug("%sNDP count=%d\n", debug_prefix, opt_recv->dccpor_ndp); break; + case DCCPO_CHANGE_L: + /* fall through */ + case DCCPO_CHANGE_R: + if (len < 2) + goto out_invalid_option; + rc = dccp_feat_change_recv(sk, opt, *value, value + 1, + len - 1); + /* + * When there is a change error, change_recv is + * responsible for dealing with it. i.e. reply with an + * empty confirm. + * If the change was mandatory, then we need to die. + */ + if (rc && mandatory) + goto out_invalid_option; + break; + case DCCPO_CONFIRM_L: + /* fall through */ + case DCCPO_CONFIRM_R: + if (len < 2) + goto out_invalid_option; + if (dccp_feat_confirm_recv(sk, opt, *value, + value + 1, len - 1)) + goto out_invalid_option; + break; case DCCPO_ACK_VECTOR_0: case DCCPO_ACK_VECTOR_1: if (pkt_type == DCCP_PKT_DATA) @@ -121,7 +157,7 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb) if (len != 4) goto out_invalid_option; - opt_recv->dccpor_timestamp = ntohl(*(u32 *)value); + opt_recv->dccpor_timestamp = ntohl(*(__be32 *)value); dp->dccps_timestamp_echo = opt_recv->dccpor_timestamp; dccp_timestamp(sk, &dp->dccps_timestamp_time); @@ -135,7 +171,7 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb) if (len != 4 && len != 6 && len != 8) goto out_invalid_option; - opt_recv->dccpor_timestamp_echo = ntohl(*(u32 *)value); + opt_recv->dccpor_timestamp_echo = ntohl(*(__be32 *)value); dccp_pr_debug("%sTIMESTAMP_ECHO=%u, len=%d, ackno=%llu, ", debug_prefix, @@ -149,9 +185,9 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb) break; if (len == 6) - elapsed_time = ntohs(*(u16 *)(value + 4)); + elapsed_time = ntohs(*(__be16 *)(value + 4)); else - elapsed_time = ntohl(*(u32 *)(value + 4)); + elapsed_time = ntohl(*(__be32 *)(value + 4)); /* Give precedence to the biggest ELAPSED_TIME */ if (elapsed_time > opt_recv->dccpor_elapsed_time) @@ -165,9 +201,9 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb) continue; if (len == 2) - elapsed_time = ntohs(*(u16 *)value); + elapsed_time = ntohs(*(__be16 *)value); else - elapsed_time = ntohl(*(u32 *)value); + elapsed_time = ntohl(*(__be32 *)value); if (elapsed_time > opt_recv->dccpor_elapsed_time) opt_recv->dccpor_elapsed_time = elapsed_time; @@ -208,6 +244,9 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb) sk, opt, len); break; } + + if (opt != DCCPO_MANDATORY) + mandatory = 0; } return 0; @@ -321,10 +360,10 @@ void dccp_insert_option_elapsed_time(struct sock *sk, *to++ = len; if (elapsed_time_len == 2) { - const u16 var16 = htons((u16)elapsed_time); + const __be16 var16 = htons((u16)elapsed_time); memcpy(to, &var16, 2); } else { - const u32 var32 = htonl(elapsed_time); + const __be32 var32 = htonl(elapsed_time); memcpy(to, &var32, 4); } @@ -355,14 +394,13 @@ EXPORT_SYMBOL_GPL(dccp_timestamp); void dccp_insert_option_timestamp(struct sock *sk, struct sk_buff *skb) { struct timeval tv; - u32 now; - + __be32 now; + dccp_timestamp(sk, &tv); - now = timeval_usecs(&tv) / 10; + now = htonl(timeval_usecs(&tv) / 10); /* yes this will overflow but that is the point as we want a * 10 usec 32 bit timer which mean it wraps every 11.9 hours */ - now = htonl(now); dccp_insert_option(sk, skb, DCCPO_TIMESTAMP, &now, sizeof(now)); } @@ -377,7 +415,7 @@ static void dccp_insert_option_timestamp_echo(struct sock *sk, "CLIENT TX opt: " : "server TX opt: "; #endif struct timeval now; - u32 tstamp_echo; + __be32 tstamp_echo; u32 elapsed_time; int len, elapsed_time_len; unsigned char *to; @@ -402,12 +440,12 @@ static void dccp_insert_option_timestamp_echo(struct sock *sk, tstamp_echo = htonl(dp->dccps_timestamp_echo); memcpy(to, &tstamp_echo, 4); to += 4; - + if (elapsed_time_len == 2) { - const u16 var16 = htons((u16)elapsed_time); + const __be16 var16 = htons((u16)elapsed_time); memcpy(to, &var16, 2); } else if (elapsed_time_len == 4) { - const u32 var32 = htonl(elapsed_time); + const __be32 var32 = htonl(elapsed_time); memcpy(to, &var32, 4); } @@ -421,6 +459,93 @@ static void dccp_insert_option_timestamp_echo(struct sock *sk, dp->dccps_timestamp_time.tv_usec = 0; } +static int dccp_insert_feat_opt(struct sk_buff *skb, u8 type, u8 feat, + u8 *val, u8 len) +{ + u8 *to; + + if (DCCP_SKB_CB(skb)->dccpd_opt_len + len + 3 > DCCP_MAX_OPT_LEN) { + LIMIT_NETDEBUG(KERN_INFO "DCCP: packet too small" + " to insert feature %d option!\n", feat); + return -1; + } + + DCCP_SKB_CB(skb)->dccpd_opt_len += len + 3; + + to = skb_push(skb, len + 3); + *to++ = type; + *to++ = len + 3; + *to++ = feat; + + if (len) + memcpy(to, val, len); + dccp_pr_debug("option %d feat %d len %d\n", type, feat, len); + + return 0; +} + +static void dccp_insert_feat(struct sock *sk, struct sk_buff *skb) +{ + struct dccp_sock *dp = dccp_sk(sk); + struct dccp_opt_pend *opt, *next; + int change = 0; + + /* confirm any options [NN opts] */ + list_for_each_entry_safe(opt, next, &dp->dccps_options.dccpo_conf, + dccpop_node) { + dccp_insert_feat_opt(skb, opt->dccpop_type, + opt->dccpop_feat, opt->dccpop_val, + opt->dccpop_len); + /* fear empty confirms */ + if (opt->dccpop_val) + kfree(opt->dccpop_val); + kfree(opt); + } + INIT_LIST_HEAD(&dp->dccps_options.dccpo_conf); + + /* see which features we need to send */ + list_for_each_entry(opt, &dp->dccps_options.dccpo_pending, + dccpop_node) { + /* see if we need to send any confirm */ + if (opt->dccpop_sc) { + dccp_insert_feat_opt(skb, opt->dccpop_type + 1, + opt->dccpop_feat, + opt->dccpop_sc->dccpoc_val, + opt->dccpop_sc->dccpoc_len); + + BUG_ON(!opt->dccpop_sc->dccpoc_val); + kfree(opt->dccpop_sc->dccpoc_val); + kfree(opt->dccpop_sc); + opt->dccpop_sc = NULL; + } + + /* any option not confirmed, re-send it */ + if (!opt->dccpop_conf) { + dccp_insert_feat_opt(skb, opt->dccpop_type, + opt->dccpop_feat, opt->dccpop_val, + opt->dccpop_len); + change++; + } + } + + /* Retransmit timer. + * If this is the master listening sock, we don't set a timer on it. It + * should be fine because if the dude doesn't receive our RESPONSE + * [which will contain the CHANGE] he will send another REQUEST which + * will "retrnasmit" the change. + */ + if (change && dp->dccps_role != DCCP_ROLE_LISTEN) { + dccp_pr_debug("reset feat negotiation timer %p\n", sk); + + /* XXX don't reset the timer on re-transmissions. I.e. reset it + * only when sending new stuff i guess. Currently the timer + * never backs off because on re-transmission it just resets it! + */ + inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, + inet_csk(sk)->icsk_rto, DCCP_RTO_MAX); + } +} + void dccp_insert_options(struct sock *sk, struct sk_buff *skb) { struct dccp_sock *dp = dccp_sk(sk); @@ -447,6 +572,17 @@ void dccp_insert_options(struct sock *sk, struct sk_buff *skb) dp->dccps_hc_tx_insert_options = 0; } + /* Feature negotiation */ + switch(DCCP_SKB_CB(skb)->dccpd_type) { + /* Data packets can't do feat negotiation */ + case DCCP_PKT_DATA: + case DCCP_PKT_DATAACK: + break; + default: + dccp_insert_feat(sk, skb); + break; + } + /* XXX: insert other options when appropriate */ if (DCCP_SKB_CB(skb)->dccpd_opt_len != 0) {