]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/dccp/options.c
[DCCP]: Rename init_dccp_v4_mibs to dccp_mib_init
[linux-2.6-omap-h63xx.git] / net / dccp / options.c
index 0a76426c9aeab322f6eae5dd8e8e2309e1454d98..3ecd319c0f598b5869fb227786d4a0fb4a25b37a 100644 (file)
 #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) {