* 2 of the License, or (at your option) any later version.
*/
-#include <linux/config.h>
#include <linux/module.h>
-#include "dccp.h"
+#include "ccid.h"
#include "feat.h"
#define DCCP_FEAT_SP_NOAGREE (-123)
-int dccp_feat_change(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len,
- gfp_t gfp)
+int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature,
+ u8 *val, u8 len, gfp_t gfp)
{
- struct dccp_sock *dp = dccp_sk(sk);
struct dccp_opt_pend *opt;
- dccp_pr_debug("feat change type=%d feat=%d\n", type, feature);
+ dccp_feat_debug(type, feature, *val);
+
+ if (!dccp_feat_is_valid_type(type)) {
+ DCCP_WARN("option type %d invalid in negotiation\n", type);
+ return 1;
+ }
+ if (!dccp_feat_is_valid_length(type, feature, len)) {
+ DCCP_WARN("invalid length %d\n", len);
+ return 1;
+ }
+ /* XXX add further sanity checks */
/* check if that feature is already being negotiated */
- list_for_each_entry(opt, &dp->dccps_options.dccpo_pending,
- dccpop_node) {
+ 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");
BUG_ON(opt->dccpop_val == NULL);
- list_add_tail(&opt->dccpop_node, &dp->dccps_options.dccpo_pending);
+ 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);
+ struct dccp_minisock *dmsk = dccp_msk(sk);
+ /* figure out if we are changing our CCID or the peer's */
+ const int rx = type == DCCPO_CHANGE_R;
+ const u8 ccid_nr = rx ? dmsk->dccpms_rx_ccid : dmsk->dccpms_tx_ccid;
+ struct ccid *new_ccid;
+
+ /* Check if nothing is being changed. */
+ if (ccid_nr == new_ccid_nr)
+ return 0;
+
+ new_ccid = ccid_new(new_ccid_nr, sk, rx, GFP_ATOMIC);
+ if (new_ccid == NULL)
+ return -ENOMEM;
+
+ if (rx) {
+ ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);
+ dp->dccps_hc_rx_ccid = new_ccid;
+ dmsk->dccpms_rx_ccid = new_ccid_nr;
+ } else {
+ ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);
+ dp->dccps_hc_tx_ccid = new_ccid;
+ dmsk->dccpms_tx_ccid = new_ccid_nr;
+ }
+
+ return 0;
+}
+
/* XXX taking only u8 vals */
static int dccp_feat_update(struct sock *sk, u8 type, u8 feat, u8 val)
{
- /* FIXME implement */
- dccp_pr_debug("changing [%d] feat %d to %d\n", type, feat, val);
+ dccp_feat_debug(type, feat, val);
+
+ switch (feat) {
+ case DCCPF_CCID:
+ return dccp_feat_update_ccid(sk, type, val);
+ default:
+ dccp_pr_debug("UNIMPLEMENTED: %s(%d, ...)\n",
+ dccp_feat_typename(type), feat);
+ break;
+ }
return 0;
}
case DCCPF_CCID:
/* XXX did i get this right? =P */
if (opt->dccpop_type == DCCPO_CHANGE_L)
- res = &dp->dccps_options.dccpo_tx_ccid;
+ res = &dccp_msk(sk)->dccpms_tx_ccid;
else
- res = &dp->dccps_options.dccpo_rx_ccid;
+ res = &dccp_msk(sk)->dccpms_rx_ccid;
break;
default:
- WARN_ON(1); /* XXX implement res */
+ DCCP_BUG("Fell through, feat=%d", opt->dccpop_feat);
+ /* XXX implement res */
return -EFAULT;
}
if (rc) {
kfree(opt->dccpop_sc->dccpoc_val);
kfree(opt->dccpop_sc);
- opt->dccpop_sc = 0;
+ opt->dccpop_sc = NULL;
return rc;
}
static int dccp_feat_sp(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len)
{
- struct dccp_sock *dp = dccp_sk(sk);
+ struct dccp_minisock *dmsk = dccp_msk(sk);
struct dccp_opt_pend *opt;
int rc = 1;
u8 t;
t = DCCPO_CHANGE_L;
/* find our preference list for this feature */
- list_for_each_entry(opt, &dp->dccps_options.dccpo_pending,
- dccpop_node) {
+ list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) {
if (opt->dccpop_type != t || opt->dccpop_feat != feature)
continue;
static int dccp_feat_nn(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len)
{
struct dccp_opt_pend *opt;
- struct dccp_sock *dp = dccp_sk(sk);
+ struct dccp_minisock *dmsk = dccp_msk(sk);
u8 *copy;
int rc;
- /* NN features must be change L */
- if (type == DCCPO_CHANGE_R) {
- dccp_pr_debug("received CHANGE_R %d for NN feat %d\n",
- type, feature);
+ /* NN features must be Change L (sec. 6.3.2) */
+ if (type != DCCPO_CHANGE_L) {
+ dccp_pr_debug("received %s for NN feature %d\n",
+ dccp_feat_typename(type), feature);
return -EFAULT;
}
if (opt == NULL)
return -ENOMEM;
- copy = kmalloc(len, GFP_ATOMIC);
+ copy = kmemdup(val, len, GFP_ATOMIC);
if (copy == NULL) {
kfree(opt);
return -ENOMEM;
}
- memcpy(copy, val, len);
opt->dccpop_type = DCCPO_CONFIRM_R; /* NN can only confirm R */
opt->dccpop_feat = feature;
return rc;
}
- dccp_pr_debug("Confirming NN feature %d (val=%d)\n", feature, *copy);
- list_add_tail(&opt->dccpop_node, &dp->dccps_options.dccpo_conf);
+ dccp_feat_debug(type, feature, *copy);
+
+ list_add_tail(&opt->dccpop_node, &dmsk->dccpms_conf);
return 0;
}
-static void dccp_feat_empty_confirm(struct sock *sk, u8 type, u8 feature)
+static void dccp_feat_empty_confirm(struct dccp_minisock *dmsk,
+ u8 type, u8 feature)
{
- struct dccp_sock *dp = dccp_sk(sk);
/* XXX check if other confirms for that are queued and recycle slot */
struct dccp_opt_pend *opt = kzalloc(sizeof(*opt), GFP_ATOMIC);
return;
}
- opt->dccpop_type = type == DCCPO_CHANGE_L ? DCCPO_CONFIRM_R :
- DCCPO_CONFIRM_L;
+ switch (type) {
+ case DCCPO_CHANGE_L: opt->dccpop_type = DCCPO_CONFIRM_R; break;
+ case DCCPO_CHANGE_R: opt->dccpop_type = DCCPO_CONFIRM_L; break;
+ default: DCCP_WARN("invalid type %d\n", type); return;
+
+ }
opt->dccpop_feat = feature;
- opt->dccpop_val = 0;
+ opt->dccpop_val = NULL;
opt->dccpop_len = 0;
/* change feature */
- dccp_pr_debug("Empty confirm feature %d type %d\n", feature, type);
- list_add_tail(&opt->dccpop_node, &dp->dccps_options.dccpo_conf);
+ dccp_pr_debug("Empty %s(%d)\n", dccp_feat_typename(type), feature);
+
+ list_add_tail(&opt->dccpop_node, &dmsk->dccpms_conf);
}
static void dccp_feat_flush_confirm(struct sock *sk)
{
- struct dccp_sock *dp = dccp_sk(sk);
+ struct dccp_minisock *dmsk = dccp_msk(sk);
/* Check if there is anything to confirm in the first place */
- int yes = !list_empty(&dp->dccps_options.dccpo_conf);
+ int yes = !list_empty(&dmsk->dccpms_conf);
if (!yes) {
struct dccp_opt_pend *opt;
- list_for_each_entry(opt, &dp->dccps_options.dccpo_pending,
- dccpop_node) {
+ list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) {
if (opt->dccpop_conf) {
yes = 1;
break;
{
int rc;
- dccp_pr_debug("got feat change type=%d feat=%d\n", type, feature);
+ dccp_feat_debug(type, feature, *val);
/* figure out if it's SP or NN feature */
switch (feature) {
/* XXX implement other features */
default:
+ dccp_pr_debug("UNIMPLEMENTED: not handling %s(%d, ...)\n",
+ dccp_feat_typename(type), feature);
rc = -EFAULT;
break;
}
* mandatory
*/
if (rc != DCCP_FEAT_SP_NOAGREE)
- dccp_feat_empty_confirm(sk, type, feature);
+ dccp_feat_empty_confirm(dccp_msk(sk), type, feature);
}
/* generate the confirm [if required] */
{
u8 t;
struct dccp_opt_pend *opt;
- struct dccp_sock *dp = dccp_sk(sk);
- int rc = 1;
+ struct dccp_minisock *dmsk = dccp_msk(sk);
+ int found = 0;
int all_confirmed = 1;
- dccp_pr_debug("got feat confirm type=%d feat=%d\n", type, feature);
-
- /* XXX sanity check type & feat */
+ dccp_feat_debug(type, feature, *val);
/* locate our change request */
- t = type == DCCPO_CONFIRM_L ? DCCPO_CHANGE_R : DCCPO_CHANGE_L;
+ switch (type) {
+ case DCCPO_CONFIRM_L: t = DCCPO_CHANGE_R; break;
+ case DCCPO_CONFIRM_R: t = DCCPO_CHANGE_L; break;
+ default: DCCP_WARN("invalid type %d\n", type);
+ return 1;
+
+ }
+ /* XXX sanity check feature value */
- list_for_each_entry(opt, &dp->dccps_options.dccpo_pending,
- dccpop_node) {
+ list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) {
if (!opt->dccpop_conf && opt->dccpop_type == t &&
opt->dccpop_feat == feature) {
- /* we found it */
+ found = 1;
+ dccp_pr_debug("feature %d found\n", opt->dccpop_feat);
+
/* XXX do sanity check */
opt->dccpop_conf = 1;
dccp_feat_update(sk, opt->dccpop_type,
opt->dccpop_feat, *val);
- dccp_pr_debug("feat %d type %d confirmed %d\n",
- feature, type, *val);
- rc = 0;
+ /* XXX check the return value of dccp_feat_update */
break;
}
inet_csk_clear_xmit_timer(sk, ICSK_TIME_RETRANS);
}
- if (rc)
- dccp_pr_debug("feat %d type %d never requested\n",
- feature, type);
+ if (!found)
+ dccp_pr_debug("%s(%d, ...) never requested\n",
+ dccp_feat_typename(type), feature);
return 0;
}
EXPORT_SYMBOL_GPL(dccp_feat_confirm_recv);
-void dccp_feat_clean(struct sock *sk)
+void dccp_feat_clean(struct dccp_minisock *dmsk)
{
- struct dccp_sock *dp = dccp_sk(sk);
struct dccp_opt_pend *opt, *next;
- list_for_each_entry_safe(opt, next, &dp->dccps_options.dccpo_pending,
+ list_for_each_entry_safe(opt, next, &dmsk->dccpms_pending,
dccpop_node) {
BUG_ON(opt->dccpop_val == NULL);
kfree(opt->dccpop_val);
kfree(opt);
}
- INIT_LIST_HEAD(&dp->dccps_options.dccpo_pending);
+ INIT_LIST_HEAD(&dmsk->dccpms_pending);
- list_for_each_entry_safe(opt, next, &dp->dccps_options.dccpo_conf,
- dccpop_node) {
+ list_for_each_entry_safe(opt, next, &dmsk->dccpms_conf, dccpop_node) {
BUG_ON(opt == NULL);
if (opt->dccpop_val != NULL)
kfree(opt->dccpop_val);
kfree(opt);
}
- INIT_LIST_HEAD(&dp->dccps_options.dccpo_conf);
+ INIT_LIST_HEAD(&dmsk->dccpms_conf);
}
EXPORT_SYMBOL_GPL(dccp_feat_clean);
*/
int dccp_feat_clone(struct sock *oldsk, struct sock *newsk)
{
- struct dccp_sock *olddp = dccp_sk(oldsk);
- struct dccp_sock *newdp = dccp_sk(newsk);
+ struct dccp_minisock *olddmsk = dccp_msk(oldsk);
+ struct dccp_minisock *newdmsk = dccp_msk(newsk);
struct dccp_opt_pend *opt;
int rc = 0;
- INIT_LIST_HEAD(&newdp->dccps_options.dccpo_pending);
- INIT_LIST_HEAD(&newdp->dccps_options.dccpo_conf);
+ INIT_LIST_HEAD(&newdmsk->dccpms_pending);
+ INIT_LIST_HEAD(&newdmsk->dccpms_conf);
- list_for_each_entry(opt, &olddp->dccps_options.dccpo_pending,
- dccpop_node) {
+ list_for_each_entry(opt, &olddmsk->dccpms_pending, dccpop_node) {
struct dccp_opt_pend *newopt;
/* copy the value of the option */
- u8 *val = kmalloc(opt->dccpop_len, GFP_ATOMIC);
+ u8 *val = kmemdup(opt->dccpop_val, opt->dccpop_len, GFP_ATOMIC);
if (val == NULL)
goto out_clean;
- memcpy(val, opt->dccpop_val, opt->dccpop_len);
- newopt = kmalloc(sizeof(*newopt), GFP_ATOMIC);
+ newopt = kmemdup(opt, sizeof(*newopt), GFP_ATOMIC);
if (newopt == NULL) {
kfree(val);
goto out_clean;
}
/* insert the option */
- memcpy(newopt, opt, sizeof(*newopt));
newopt->dccpop_val = val;
- list_add_tail(&newopt->dccpop_node,
- &newdp->dccps_options.dccpo_pending);
+ list_add_tail(&newopt->dccpop_node, &newdmsk->dccpms_pending);
/* XXX what happens with backlogs and multiple connections at
* once...
*/
/* the master socket no longer needs to worry about confirms */
- opt->dccpop_sc = 0; /* it's not a memleak---new socket has it */
+ opt->dccpop_sc = NULL; /* it's not a memleak---new socket has it */
/* reset state for a new socket */
opt->dccpop_conf = 0;
return rc;
out_clean:
- dccp_feat_clean(newsk);
+ dccp_feat_clean(newdmsk);
rc = -ENOMEM;
goto out;
}
EXPORT_SYMBOL_GPL(dccp_feat_clone);
-static int __dccp_feat_init(struct sock *sk, u8 type, u8 feat, u8 *val, u8 len)
+static int __dccp_feat_init(struct dccp_minisock *dmsk, u8 type, u8 feat,
+ u8 *val, u8 len)
{
int rc = -ENOMEM;
- u8 *copy = kmalloc(len, GFP_KERNEL);
+ u8 *copy = kmemdup(val, len, GFP_KERNEL);
if (copy != NULL) {
- memcpy(copy, val, len);
- rc = dccp_feat_change(sk, type, feat, copy, len, GFP_KERNEL);
+ rc = dccp_feat_change(dmsk, type, feat, copy, len, GFP_KERNEL);
if (rc)
kfree(copy);
}
return rc;
}
-int dccp_feat_init(struct sock *sk)
+int dccp_feat_init(struct dccp_minisock *dmsk)
{
- struct dccp_sock *dp = dccp_sk(sk);
int rc;
- INIT_LIST_HEAD(&dp->dccps_options.dccpo_pending);
- INIT_LIST_HEAD(&dp->dccps_options.dccpo_conf);
+ INIT_LIST_HEAD(&dmsk->dccpms_pending);
+ INIT_LIST_HEAD(&dmsk->dccpms_conf);
/* CCID L */
- rc = __dccp_feat_init(sk, DCCPO_CHANGE_L, DCCPF_CCID,
- &dp->dccps_options.dccpo_tx_ccid, 1);
+ rc = __dccp_feat_init(dmsk, DCCPO_CHANGE_L, DCCPF_CCID,
+ &dmsk->dccpms_tx_ccid, 1);
if (rc)
goto out;
/* CCID R */
- rc = __dccp_feat_init(sk, DCCPO_CHANGE_R, DCCPF_CCID,
- &dp->dccps_options.dccpo_rx_ccid, 1);
+ rc = __dccp_feat_init(dmsk, DCCPO_CHANGE_R, DCCPF_CCID,
+ &dmsk->dccpms_rx_ccid, 1);
if (rc)
goto out;
/* Ack ratio */
- rc = __dccp_feat_init(sk, DCCPO_CHANGE_L, DCCPF_ACK_RATIO,
- &dp->dccps_options.dccpo_ack_ratio, 1);
+ rc = __dccp_feat_init(dmsk, DCCPO_CHANGE_L, DCCPF_ACK_RATIO,
+ &dmsk->dccpms_ack_ratio, 1);
out:
return rc;
}
EXPORT_SYMBOL_GPL(dccp_feat_init);
+
+#ifdef CONFIG_IP_DCCP_DEBUG
+const char *dccp_feat_typename(const u8 type)
+{
+ switch(type) {
+ case DCCPO_CHANGE_L: return("ChangeL");
+ case DCCPO_CONFIRM_L: return("ConfirmL");
+ case DCCPO_CHANGE_R: return("ChangeR");
+ case DCCPO_CONFIRM_R: return("ConfirmR");
+ /* the following case must not appear in feature negotation */
+ default: dccp_pr_debug("unknown type %d [BUG!]\n", type);
+ }
+ return NULL;
+}
+
+EXPORT_SYMBOL_GPL(dccp_feat_typename);
+
+const char *dccp_feat_name(const u8 feat)
+{
+ static const char *feature_names[] = {
+ [DCCPF_RESERVED] = "Reserved",
+ [DCCPF_CCID] = "CCID",
+ [DCCPF_SHORT_SEQNOS] = "Allow Short Seqnos",
+ [DCCPF_SEQUENCE_WINDOW] = "Sequence Window",
+ [DCCPF_ECN_INCAPABLE] = "ECN Incapable",
+ [DCCPF_ACK_RATIO] = "Ack Ratio",
+ [DCCPF_SEND_ACK_VECTOR] = "Send ACK Vector",
+ [DCCPF_SEND_NDP_COUNT] = "Send NDP Count",
+ [DCCPF_MIN_CSUM_COVER] = "Min. Csum Coverage",
+ [DCCPF_DATA_CHECKSUM] = "Send Data Checksum",
+ };
+ if (feat >= DCCPF_MIN_CCID_SPECIFIC)
+ return "CCID-specific";
+
+ if (dccp_feat_is_reserved(feat))
+ return feature_names[DCCPF_RESERVED];
+
+ return feature_names[feat];
+}
+
+EXPORT_SYMBOL_GPL(dccp_feat_name);
+#endif /* CONFIG_IP_DCCP_DEBUG */