dccp: Support for the exchange of NN options in established state
authorGerrit Renker <gerrit@erg.abdn.ac.uk>
Thu, 4 Sep 2008 05:30:19 +0000 (07:30 +0200)
committerGerrit Renker <gerrit@erg.abdn.ac.uk>
Thu, 4 Sep 2008 05:45:32 +0000 (07:45 +0200)
In contrast to static feature negotiation at the begin of a connection, which
establishes the capabilities of both endpoints, this patch introduces support
for dynamic exchange of feature negotiation options.

Such a dynamic exchange is necessary in at least two cases:
 * CCID-2's Ack Ratio (RFC 4341, 6.1.2) which changes during the connection;
 * Sequence Window values that, as per RFC 4340, 7.5.2, should be sent "as
   as the connection progresses".

Both are NN (non-negotiable) features. Hence dynamic feature "negotiation" is
distinguished from static/pre-connection negotiation by the following:
 * no new capabilities are negotiated (those that matter for the connection
   are negotiated prior to setting up the connection, comparable to SIP);
 * features must be understood by each endpoint: as per RFC 4340, 6.4,
   Sequence Window is "Req'd" and Ack Ratio must be understood when CCID-2
   is used as per the note underneath Table 4.

These characteristics are reflected in the implementation:
 * only NN options can be exchanged after connection setup;
 * NN options are activated directly after validating them. The rationale is
   that a peer must accept every valid NN value (RFC 4340, 6.3.2), hence it
   will either accept the value and send a "Confirm R", or it will send an
   empty Confirm (which will reset the connection according to FN rules).
 * An Ack is scheduled directly after activation to accelerate communicating
   the update to the peer.

Signed-off-by: Gerrit Renker <gerrit@erg.abdn.ac.uk>
Acked-by: Ian McDonald <ian.mcdonald@jandi.co.nz>
net/dccp/dccp.h
net/dccp/feat.c

index 94ae6d41d724716d0daf909b2279c6189a358ffb..0a07b2e24e62aef4c2f4f238044005d038eec373 100644 (file)
@@ -436,6 +436,7 @@ static inline int dccp_ack_pending(const struct sock *sk)
               inet_csk_ack_scheduled(sk);
 }
 
+extern int  dccp_feat_signal_nn_change(struct sock *sk, u8 feat, u64 nn_val);
 extern int  dccp_feat_finalise_settings(struct dccp_sock *dp);
 extern int  dccp_feat_server_ccid_dependencies(struct dccp_request_sock *dreq);
 extern int  dccp_feat_insert_opts(struct dccp_sock*, struct dccp_request_sock*,
index 3abacad0b2247bd40f88f49788eb5b3f574e1267..5be8b85ac74bbfc0a3a24bda1e49af1f85f18016 100644 (file)
@@ -13,6 +13,7 @@
  *  -----------
  *  o Feature negotiation is coordinated with connection setup (as in TCP), wild
  *    changes of parameters of an established connection are not supported.
+ *  o Changing NN values (Ack Ratio only) is supported in state OPEN/PARTOPEN.
  *  o All currently known SP features have 1-byte quantities. If in the future
  *    extensions of RFCs 4340..42 define features with item lengths larger than
  *    one byte, a feature-specific extension of the code will be required.
@@ -337,6 +338,20 @@ static int __dccp_feat_activate(struct sock *sk, const int idx,
        return dccp_feat_table[idx].activation_hdlr(sk, val, rx);
 }
 
+/**
+ * dccp_feat_activate  -  Activate feature value on socket
+ * @sk: fully connected DCCP socket (after handshake is complete)
+ * @feat_num: feature to activate, one of %dccp_feature_numbers
+ * @local: whether local (1) or remote (0) @feat_num is meant
+ * @fval: the value (SP or NN) to activate, or NULL to use the default value
+ * For general use this function is preferable over __dccp_feat_activate().
+ */
+static int dccp_feat_activate(struct sock *sk, u8 feat_num, bool local,
+                             dccp_feat_val const *fval)
+{
+       return __dccp_feat_activate(sk, dccp_feat_index(feat_num), local, fval);
+}
+
 /* Test for "Req'd" feature (RFC 4340, 6.4) */
 static inline int dccp_feat_must_be_understood(u8 feat_num)
 {
@@ -734,6 +749,48 @@ int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val)
        return __feat_register_nn(&dccp_sk(sk)->dccps_featneg, feat, 0, val);
 }
 
+/**
+ * dccp_feat_signal_nn_change  -  Update NN values for an established connection
+ * @sk: DCCP socket of an established connection
+ * @feat: NN feature number from %dccp_feature_numbers
+ * @nn_val: the new value to use
+ * This function is used to communicate NN updates out-of-band. The difference
+ * to feature negotiation during connection setup is that values are activated
+ * immediately after validation, i.e. we don't wait for the Confirm: either the
+ * value is accepted by the peer (and then the waiting is futile), or it is not
+ * (Reset or empty Confirm). We don't accept empty Confirms - transmitted values
+ * are validated, and the peer "MUST accept any valid value" (RFC 4340, 6.3.2).
+ */
+int dccp_feat_signal_nn_change(struct sock *sk, u8 feat, u64 nn_val)
+{
+       struct list_head *fn = &dccp_sk(sk)->dccps_featneg;
+       dccp_feat_val fval = { .nn = nn_val };
+       struct dccp_feat_entry *entry;
+
+       if (sk->sk_state != DCCP_OPEN && sk->sk_state != DCCP_PARTOPEN)
+               return 0;
+
+       if (dccp_feat_type(feat) != FEAT_NN ||
+           !dccp_feat_is_valid_nn_val(feat, nn_val))
+               return -EINVAL;
+
+       entry = dccp_feat_list_lookup(fn, feat, 1);
+       if (entry != NULL) {
+               dccp_pr_debug("Ignoring %llu, entry %llu exists in state %s\n",
+                             (unsigned long long)nn_val,
+                             (unsigned long long)entry->val.nn,
+                             dccp_feat_sname[entry->state]);
+               return 0;
+       }
+
+       if (dccp_feat_activate(sk, feat, 1, &fval))
+               return -EADV;
+
+       inet_csk_schedule_ack(sk);
+       return dccp_feat_push_change(fn, feat, 1, 0, &fval);
+}
+EXPORT_SYMBOL_GPL(dccp_feat_signal_nn_change);
+
 /*
  *     Tracking features whose value depend on the choice of CCID
  *