]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - net/dccp/feat.c
dccp: Registration routines for changing feature values
[net-next-2.6.git] / net / dccp / feat.c
index 44b10afd3fb6d98cf0a8ee9bab775faa0c7ca4c7..192d494a381645029c970d1ceac86da69d908e96 100644 (file)
@@ -95,8 +95,13 @@ static u8 dccp_feat_type(u8 feat_num)
 static int dccp_feat_default_value(u8 feat_num)
 {
        int idx = dccp_feat_index(feat_num);
+       /*
+        * There are no default values for unknown features, so encountering a
+        * negative index here indicates a serious problem somewhere else.
+        */
+       DCCP_BUG_ON(idx < 0);
 
-       return idx < 0 ? : dccp_feat_table[idx].default_value;
+       return idx < 0 ? : dccp_feat_table[idx].default_value;
 }
 
 /* copy constructor, fval must not already contain allocated memory */
@@ -161,18 +166,6 @@ static void dccp_feat_entry_destructor(struct dccp_feat_entry *entry)
  * - SP values are always freshly allocated
  * - list is sorted in increasing order of feature number (faster lookup)
  */
-static struct dccp_feat_entry *dccp_feat_list_lookup(struct list_head *fn_list,
-                                                    u8 feat_num, bool is_local)
-{
-       struct dccp_feat_entry *entry;
-
-       list_for_each_entry(entry, fn_list, node)
-               if (entry->feat_num == feat_num && entry->is_local == is_local)
-                       return entry;
-               else if (entry->feat_num > feat_num)
-                       break;
-       return NULL;
-}
 
 /**
  * dccp_feat_entry_new  -  Central list update routine (called by all others)
@@ -231,40 +224,6 @@ static int dccp_feat_push_change(struct list_head *fn_list, u8 feat, u8 local,
        return 0;
 }
 
-/**
- * dccp_feat_push_confirm  -  Add a Confirm entry to the FN list
- * @fn_list: feature-negotiation list to add to
- * @feat: one of %dccp_feature_numbers
- * @local: whether local (1) or remote (0) @feat_num is being confirmed
- * @fval: pointer to NN/SP value to be inserted or NULL
- * Returns 0 on success, a Reset code for further processing otherwise.
- */
-static int dccp_feat_push_confirm(struct list_head *fn_list, u8 feat, u8 local,
-                                 dccp_feat_val *fval)
-{
-       struct dccp_feat_entry *new = dccp_feat_entry_new(fn_list, feat, local);
-
-       if (new == NULL)
-               return DCCP_RESET_CODE_TOO_BUSY;
-
-       new->feat_num        = feat;
-       new->is_local        = local;
-       new->state           = FEAT_STABLE;     /* transition in 6.6.2 */
-       new->needs_confirm   = 1;
-       new->empty_confirm   = (fval == NULL);
-       new->val.nn          = 0;               /* zeroes the whole structure */
-       if (!new->empty_confirm)
-               new->val     = *fval;
-       new->needs_mandatory = 0;
-
-       return 0;
-}
-
-static int dccp_push_empty_confirm(struct list_head *fn_list, u8 feat, u8 local)
-{
-       return dccp_feat_push_confirm(fn_list, feat, local, NULL);
-}
-
 static inline void dccp_feat_list_pop(struct dccp_feat_entry *entry)
 {
        list_del(&entry->node);
@@ -383,231 +342,60 @@ static int __feat_register_sp(struct list_head *fn, u8 feat, u8 is_local,
            !dccp_feat_sp_list_ok(feat, sp_val, sp_len))
                return -EINVAL;
 
-       /* Avoid negotiating alien CCIDs by only advertising supported ones */
-       if (feat == DCCPF_CCID && !ccid_support_check(sp_val, sp_len))
-               return -EOPNOTSUPP;
-
        if (dccp_feat_clone_sp_val(&fval, sp_val, sp_len))
                return -ENOMEM;
 
        return dccp_feat_push_change(fn, feat, is_local, mandatory, &fval);
 }
 
-/**
- * dccp_feat_register_sp  -  Register requests to change SP feature values
- * @sk: client or listening socket
- * @feat: one of %dccp_feature_numbers
- * @is_local: whether the local (1) or remote (0) @feat is meant
- * @list: array of preferred values, in descending order of preference
- * @len: length of @list in bytes
- */
-int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local,
-                         u8 const *list, u8 len)
-{       /* any changes must be registered before establishing the connection */
-       if (sk->sk_state != DCCP_CLOSED)
-               return -EISCONN;
-       if (dccp_feat_type(feat) != FEAT_SP)
-               return -EINVAL;
-       return __feat_register_sp(&dccp_sk(sk)->dccps_featneg, feat, is_local,
-                                 0, list, len);
-}
-
-/* Analogous to dccp_feat_register_sp(), but for non-negotiable values */
-int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val)
+int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature,
+                    u8 *val, u8 len, gfp_t gfp)
 {
-       /* any changes must be registered before establishing the connection */
-       if (sk->sk_state != DCCP_CLOSED)
-               return -EISCONN;
-       if (dccp_feat_type(feat) != FEAT_NN)
+       struct dccp_opt_pend *opt;
+
+       dccp_feat_debug(type, feature, *val);
+
+       if (len > 3) {
+               DCCP_WARN("invalid length %d\n", len);
                return -EINVAL;
-       return __feat_register_nn(&dccp_sk(sk)->dccps_featneg, feat, 0, val);
-}
+       }
+       /* XXX add further sanity checks */
 
-/*
- *     Tracking features whose value depend on the choice of CCID
- *
- * This is designed with an extension in mind so that a list walk could be done
- * before activating any features. However, the existing framework was found to
- * work satisfactorily up until now, the automatic verification is left open.
- * When adding new CCIDs, add a corresponding dependency table here.
- */
-static const struct ccid_dependency *dccp_feat_ccid_deps(u8 ccid, bool is_local)
-{
-       static const struct ccid_dependency ccid2_dependencies[2][2] = {
-               /*
-                * CCID2 mandates Ack Vectors (RFC 4341, 4.): as CCID is a TX
-                * feature and Send Ack Vector is an RX feature, `is_local'
-                * needs to be reversed.
-                */
-               {       /* Dependencies of the receiver-side (remote) CCID2 */
-                       {
-                               .dependent_feat = DCCPF_SEND_ACK_VECTOR,
-                               .is_local       = true,
-                               .is_mandatory   = true,
-                               .val            = 1
-                       },
-                       { 0, 0, 0, 0 }
-               },
-               {       /* Dependencies of the sender-side (local) CCID2 */
-                       {
-                               .dependent_feat = DCCPF_SEND_ACK_VECTOR,
-                               .is_local       = false,
-                               .is_mandatory   = true,
-                               .val            = 1
-                       },
-                       { 0, 0, 0, 0 }
-               }
-       };
-       static const struct ccid_dependency ccid3_dependencies[2][5] = {
-               {       /*
-                        * Dependencies of the receiver-side CCID3
-                        */
-                       {       /* locally disable Ack Vectors */
-                               .dependent_feat = DCCPF_SEND_ACK_VECTOR,
-                               .is_local       = true,
-                               .is_mandatory   = false,
-                               .val            = 0
-                       },
-                       {       /* see below why Send Loss Event Rate is on */
-                               .dependent_feat = DCCPF_SEND_LEV_RATE,
-                               .is_local       = true,
-                               .is_mandatory   = true,
-                               .val            = 1
-                       },
-                       {       /* NDP Count is needed as per RFC 4342, 6.1.1 */
-                               .dependent_feat = DCCPF_SEND_NDP_COUNT,
-                               .is_local       = false,
-                               .is_mandatory   = true,
-                               .val            = 1
-                       },
-                       { 0, 0, 0, 0 },
-               },
-               {       /*
-                        * CCID3 at the TX side: we request that the HC-receiver
-                        * will not send Ack Vectors (they will be ignored, so
-                        * Mandatory is not set); we enable Send Loss Event Rate
-                        * (Mandatory since the implementation does not support
-                        * the Loss Intervals option of RFC 4342, 8.6).
-                        * The last two options are for peer's information only.
-                       */
-                       {
-                               .dependent_feat = DCCPF_SEND_ACK_VECTOR,
-                               .is_local       = false,
-                               .is_mandatory   = false,
-                               .val            = 0
-                       },
-                       {
-                               .dependent_feat = DCCPF_SEND_LEV_RATE,
-                               .is_local       = false,
-                               .is_mandatory   = true,
-                               .val            = 1
-                       },
-                       {       /* this CCID does not support Ack Ratio */
-                               .dependent_feat = DCCPF_ACK_RATIO,
-                               .is_local       = true,
-                               .is_mandatory   = false,
-                               .val            = 0
-                       },
-                       {       /* tell receiver we are sending NDP counts */
-                               .dependent_feat = DCCPF_SEND_NDP_COUNT,
-                               .is_local       = true,
-                               .is_mandatory   = false,
-                               .val            = 1
-                       },
-                       { 0, 0, 0, 0 }
+       /* check if that feature is already being negotiated */
+       list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) {
+               /* ok we found a negotiation for this option already */
+               if (opt->dccpop_feat == feature && opt->dccpop_type == type) {
+                       dccp_pr_debug("Replacing old\n");
+                       /* replace */
+                       BUG_ON(opt->dccpop_val == NULL);
+                       kfree(opt->dccpop_val);
+                       opt->dccpop_val  = val;
+                       opt->dccpop_len  = len;
+                       opt->dccpop_conf = 0;
+                       return 0;
                }
-       };
-       switch (ccid) {
-       case DCCPC_CCID2:
-               return ccid2_dependencies[is_local];
-       case DCCPC_CCID3:
-               return ccid3_dependencies[is_local];
-       default:
-               return NULL;
        }
-}
-
-/**
- * dccp_feat_propagate_ccid - Resolve dependencies of features on choice of CCID
- * @fn: feature-negotiation list to update
- * @id: CCID number to track
- * @is_local: whether TX CCID (1) or RX CCID (0) is meant
- * This function needs to be called after registering all other features.
- */
-static int dccp_feat_propagate_ccid(struct list_head *fn, u8 id, bool is_local)
-{
-       const struct ccid_dependency *table = dccp_feat_ccid_deps(id, is_local);
-       int i, rc = (table == NULL);
-
-       for (i = 0; rc == 0 && table[i].dependent_feat != DCCPF_RESERVED; i++)
-               if (dccp_feat_type(table[i].dependent_feat) == FEAT_SP)
-                       rc = __feat_register_sp(fn, table[i].dependent_feat,
-                                                   table[i].is_local,
-                                                   table[i].is_mandatory,
-                                                   &table[i].val, 1);
-               else
-                       rc = __feat_register_nn(fn, table[i].dependent_feat,
-                                                   table[i].is_mandatory,
-                                                   table[i].val);
-       return rc;
-}
 
-/**
- * dccp_feat_finalise_settings  -  Finalise settings before starting negotiation
- * @dp: client or listening socket (settings will be inherited)
- * This is called after all registrations (socket initialisation, sysctls, and
- * sockopt calls), and before sending the first packet containing Change options
- * (ie. client-Request or server-Response), to ensure internal consistency.
- */
-int dccp_feat_finalise_settings(struct dccp_sock *dp)
-{
-       struct list_head *fn = &dp->dccps_featneg;
-       struct dccp_feat_entry *entry;
-       int i = 2, ccids[2] = { -1, -1 };
-
-       /*
-        * Propagating CCIDs:
-        * 1) not useful to propagate CCID settings if this host advertises more
-        *    than one CCID: the choice of CCID  may still change - if this is
-        *    the client, or if this is the server and the client sends
-        *    singleton CCID values.
-        * 2) since is that propagate_ccid changes the list, we defer changing
-        *    the sorted list until after the traversal.
-        */
-       list_for_each_entry(entry, fn, node)
-               if (entry->feat_num == DCCPF_CCID && entry->val.sp.len == 1)
-                       ccids[entry->is_local] = entry->val.sp.vec[0];
-       while (i--)
-               if (ccids[i] > 0 && dccp_feat_propagate_ccid(fn, ccids[i], i))
-                       return -1;
-       return 0;
-}
-
-/**
- * dccp_feat_server_ccid_dependencies  -  Resolve CCID-dependent features
- * It is the server which resolves the dependencies once the CCID has been
- * fully negotiated. If no CCID has been negotiated, it uses the default CCID.
- */
-int dccp_feat_server_ccid_dependencies(struct dccp_request_sock *dreq)
-{
-       struct list_head *fn = &dreq->dreq_featneg;
-       struct dccp_feat_entry *entry;
-       u8 is_local, ccid;
+       /* negotiation for a new feature */
+       opt = kmalloc(sizeof(*opt), gfp);
+       if (opt == NULL)
+               return -ENOMEM;
 
-       for (is_local = 0; is_local <= 1; is_local++) {
-               entry = dccp_feat_list_lookup(fn, DCCPF_CCID, is_local);
+       opt->dccpop_type = type;
+       opt->dccpop_feat = feature;
+       opt->dccpop_len  = len;
+       opt->dccpop_val  = val;
+       opt->dccpop_conf = 0;
+       opt->dccpop_sc   = NULL;
 
-               if (entry != NULL && !entry->empty_confirm)
-                       ccid = entry->val.sp.vec[0];
-               else
-                       ccid = dccp_feat_default_value(DCCPF_CCID);
+       BUG_ON(opt->dccpop_val == NULL);
 
-               if (dccp_feat_propagate_ccid(fn, ccid, is_local))
-                       return -1;
-       }
+       list_add_tail(&opt->dccpop_node, &dmsk->dccpms_pending);
        return 0;
 }
 
+EXPORT_SYMBOL_GPL(dccp_feat_change);
+
 static int dccp_feat_update_ccid(struct sock *sk, u8 type, u8 new_ccid_nr)
 {
        struct dccp_sock *dp = dccp_sk(sk);
@@ -1119,7 +907,7 @@ int dccp_feat_init(struct sock *sk)
 
        /* Ack ratio */
        rc = __feat_register_nn(&dp->dccps_featneg, DCCPF_ACK_RATIO, 0,
-                               dp->dccps_l_ack_ratio);
+                               dmsk->dccpms_ack_ratio);
 out:
        return rc;
 }