dccp: Initialisation and type-checking of feature sysctls
[pandora-kernel.git] / net / dccp / feat.c
index 4152308..12006e9 100644 (file)
 #include "ccid.h"
 #include "feat.h"
 
+/* feature-specific sysctls - initialised to the defaults from RFC 4340, 6.4 */
+unsigned long  sysctl_dccp_sequence_window __read_mostly = 100;
+int            sysctl_dccp_rx_ccid         __read_mostly = 2,
+               sysctl_dccp_tx_ccid         __read_mostly = 2;
+
 /*
  * Feature activation handlers.
  *
@@ -51,8 +56,17 @@ static int dccp_hdlr_ccid(struct sock *sk, u64 ccid, bool rx)
 
 static int dccp_hdlr_seq_win(struct sock *sk, u64 seq_win, bool rx)
 {
-       if (!rx)
-               dccp_msk(sk)->dccpms_sequence_window = seq_win;
+       struct dccp_sock *dp = dccp_sk(sk);
+
+       if (rx) {
+               dp->dccps_r_seq_win = seq_win;
+               /* propagate changes to update SWL/SWH */
+               dccp_update_gsr(sk, dp->dccps_gsr);
+       } else {
+               dp->dccps_l_seq_win = seq_win;
+               /* propagate changes to update AWL */
+               dccp_update_gss(sk, dp->dccps_gss);
+       }
        return 0;
 }
 
@@ -1115,23 +1129,70 @@ int dccp_feat_parse_options(struct sock *sk, struct dccp_request_sock *dreq,
        return 0;       /* ignore FN options in all other states */
 }
 
+/**
+ * dccp_feat_init  -  Seed feature negotiation with host-specific defaults
+ * This initialises global defaults, depending on the value of the sysctls.
+ * These can later be overridden by registering changes via setsockopt calls.
+ * The last link in the chain is finalise_settings, to make sure that between
+ * here and the start of actual feature negotiation no inconsistencies enter.
+ *
+ * All features not appearing below use either defaults or are otherwise
+ * later adjusted through dccp_feat_finalise_settings().
+ */
 int dccp_feat_init(struct sock *sk)
 {
-       struct dccp_sock *dp = dccp_sk(sk);
-       struct dccp_minisock *dmsk = dccp_msk(sk);
+       struct list_head *fn = &dccp_sk(sk)->dccps_featneg;
+       u8 on = 1, off = 0;
        int rc;
+       struct {
+               u8 *val;
+               u8 len;
+       } tx, rx;
+
+       /* Non-negotiable (NN) features */
+       rc = __feat_register_nn(fn, DCCPF_SEQUENCE_WINDOW, 0,
+                                   sysctl_dccp_sequence_window);
+       if (rc)
+               return rc;
 
-       INIT_LIST_HEAD(&dmsk->dccpms_pending);  /* XXX no longer used */
-       INIT_LIST_HEAD(&dmsk->dccpms_conf);     /* XXX no longer used */
+       /* Server-priority (SP) features */
 
-       /* Ack ratio */
-       rc = __feat_register_nn(&dp->dccps_featneg, DCCPF_ACK_RATIO, 0,
-                               dp->dccps_l_ack_ratio);
+       /* Advertise that short seqnos are not supported (7.6.1) */
+       rc = __feat_register_sp(fn, DCCPF_SHORT_SEQNOS, true, true, &off, 1);
+       if (rc)
+               return rc;
+
+       /* RFC 4340 12.1: "If a DCCP is not ECN capable, ..." */
+       rc = __feat_register_sp(fn, DCCPF_ECN_INCAPABLE, true, true, &on, 1);
+       if (rc)
+               return rc;
+
+       /*
+        * We advertise the available list of CCIDs and reorder according to
+        * preferences, to avoid failure resulting from negotiating different
+        * singleton values (which always leads to failure).
+        * These settings can still (later) be overridden via sockopts.
+        */
+       if (ccid_get_builtin_ccids(&tx.val, &tx.len) ||
+           ccid_get_builtin_ccids(&rx.val, &rx.len))
+               return -ENOBUFS;
+
+       if (!dccp_feat_prefer(sysctl_dccp_tx_ccid, tx.val, tx.len) ||
+           !dccp_feat_prefer(sysctl_dccp_rx_ccid, rx.val, rx.len))
+               goto free_ccid_lists;
+
+       rc = __feat_register_sp(fn, DCCPF_CCID, true, false, tx.val, tx.len);
+       if (rc)
+               goto free_ccid_lists;
+
+       rc = __feat_register_sp(fn, DCCPF_CCID, false, false, rx.val, rx.len);
+
+free_ccid_lists:
+       kfree(tx.val);
+       kfree(rx.val);
        return rc;
 }
 
-EXPORT_SYMBOL_GPL(dccp_feat_init);
-
 int dccp_feat_activate_values(struct sock *sk, struct list_head *fn_list)
 {
        struct dccp_sock *dp = dccp_sk(sk);