linux/net/dccp/feat.c

650 lines
16 KiB
C
Raw Normal View History

/*
* net/dccp/feat.c
*
* An implementation of the DCCP protocol
* Andrea Bittau <a.bittau@cs.ucl.ac.uk>
*
* ASSUMPTIONS
* -----------
* 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.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/module.h>
#include "ccid.h"
#include "feat.h"
#define DCCP_FEAT_SP_NOAGREE (-123)
int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature,
u8 *val, u8 len, gfp_t gfp)
{
struct dccp_opt_pend *opt;
dccp_feat_debug(type, feature, *val);
if (len > 3) {
DCCP_WARN("invalid length %d\n", len);
return -EINVAL;
}
/* XXX add further sanity checks */
/* 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;
}
}
/* negotiation for a new feature */
opt = kmalloc(sizeof(*opt), gfp);
if (opt == NULL)
return -ENOMEM;
opt->dccpop_type = type;
opt->dccpop_feat = feature;
opt->dccpop_len = len;
opt->dccpop_val = val;
opt->dccpop_conf = 0;
opt->dccpop_sc = NULL;
BUG_ON(opt->dccpop_val == NULL);
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;
}
static int dccp_feat_update(struct sock *sk, u8 type, u8 feat, u8 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;
}
static int dccp_feat_reconcile(struct sock *sk, struct dccp_opt_pend *opt,
u8 *rpref, u8 rlen)
{
struct dccp_sock *dp = dccp_sk(sk);
u8 *spref, slen, *res = NULL;
int i, j, rc, agree = 1;
BUG_ON(rpref == NULL);
/* check if we are the black sheep */
if (dp->dccps_role == DCCP_ROLE_CLIENT) {
spref = rpref;
slen = rlen;
rpref = opt->dccpop_val;
rlen = opt->dccpop_len;
} else {
spref = opt->dccpop_val;
slen = opt->dccpop_len;
}
/*
* Now we have server preference list in spref and client preference in
* rpref
*/
BUG_ON(spref == NULL);
BUG_ON(rpref == NULL);
/* FIXME sanity check vals */
/* Are values in any order? XXX Lame "algorithm" here */
for (i = 0; i < slen; i++) {
for (j = 0; j < rlen; j++) {
if (spref[i] == rpref[j]) {
res = &spref[i];
break;
}
}
if (res)
break;
}
/* we didn't agree on anything */
if (res == NULL) {
/* confirm previous value */
switch (opt->dccpop_feat) {
case DCCPF_CCID:
/* XXX did i get this right? =P */
if (opt->dccpop_type == DCCPO_CHANGE_L)
res = &dccp_msk(sk)->dccpms_tx_ccid;
else
res = &dccp_msk(sk)->dccpms_rx_ccid;
break;
default:
DCCP_BUG("Fell through, feat=%d", opt->dccpop_feat);
/* XXX implement res */
return -EFAULT;
}
dccp_pr_debug("Don't agree... reconfirming %d\n", *res);
agree = 0; /* this is used for mandatory options... */
}
/* need to put result and our preference list */
rlen = 1 + opt->dccpop_len;
rpref = kmalloc(rlen, GFP_ATOMIC);
if (rpref == NULL)
return -ENOMEM;
*rpref = *res;
memcpy(&rpref[1], opt->dccpop_val, opt->dccpop_len);
/* put it in the "confirm queue" */
if (opt->dccpop_sc == NULL) {
opt->dccpop_sc = kmalloc(sizeof(*opt->dccpop_sc), GFP_ATOMIC);
if (opt->dccpop_sc == NULL) {
kfree(rpref);
return -ENOMEM;
}
} else {
/* recycle the confirm slot */
BUG_ON(opt->dccpop_sc->dccpoc_val == NULL);
kfree(opt->dccpop_sc->dccpoc_val);
dccp_pr_debug("recycling confirm slot\n");
}
memset(opt->dccpop_sc, 0, sizeof(*opt->dccpop_sc));
opt->dccpop_sc->dccpoc_val = rpref;
opt->dccpop_sc->dccpoc_len = rlen;
/* update the option on our side [we are about to send the confirm] */
rc = dccp_feat_update(sk, opt->dccpop_type, opt->dccpop_feat, *res);
if (rc) {
kfree(opt->dccpop_sc->dccpoc_val);
kfree(opt->dccpop_sc);
opt->dccpop_sc = NULL;
return rc;
}
dccp_pr_debug("Will confirm %d\n", *rpref);
/* say we want to change to X but we just got a confirm X, suppress our
* change
*/
if (!opt->dccpop_conf) {
if (*opt->dccpop_val == *res)
opt->dccpop_conf = 1;
dccp_pr_debug("won't ask for change of same feature\n");
}
return agree ? 0 : DCCP_FEAT_SP_NOAGREE; /* used for mandatory opts */
}
static int dccp_feat_sp(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len)
{
struct dccp_minisock *dmsk = dccp_msk(sk);
struct dccp_opt_pend *opt;
int rc = 1;
u8 t;
/*
* We received a CHANGE. We gotta match it against our own preference
* list. If we got a CHANGE_R it means it's a change for us, so we need
* to compare our CHANGE_L list.
*/
if (type == DCCPO_CHANGE_L)
t = DCCPO_CHANGE_R;
else
t = DCCPO_CHANGE_L;
/* find our preference list for this feature */
list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) {
if (opt->dccpop_type != t || opt->dccpop_feat != feature)
continue;
/* find the winner from the two preference lists */
rc = dccp_feat_reconcile(sk, opt, val, len);
break;
}
/* We didn't deal with the change. This can happen if we have no
* preference list for the feature. In fact, it just shouldn't
* happen---if we understand a feature, we should have a preference list
* with at least the default value.
*/
BUG_ON(rc == 1);
return rc;
}
static int dccp_feat_nn(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len)
{
struct dccp_opt_pend *opt;
struct dccp_minisock *dmsk = dccp_msk(sk);
u8 *copy;
int rc;
/* 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;
}
/* XXX sanity check opt val */
/* copy option so we can confirm it */
opt = kzalloc(sizeof(*opt), GFP_ATOMIC);
if (opt == NULL)
return -ENOMEM;
copy = kmemdup(val, len, GFP_ATOMIC);
if (copy == NULL) {
kfree(opt);
return -ENOMEM;
}
opt->dccpop_type = DCCPO_CONFIRM_R; /* NN can only confirm R */
opt->dccpop_feat = feature;
opt->dccpop_val = copy;
opt->dccpop_len = len;
/* change feature */
rc = dccp_feat_update(sk, type, feature, *val);
if (rc) {
kfree(opt->dccpop_val);
kfree(opt);
return rc;
}
dccp_feat_debug(type, feature, *copy);
list_add_tail(&opt->dccpop_node, &dmsk->dccpms_conf);
return 0;
}
static void dccp_feat_empty_confirm(struct dccp_minisock *dmsk,
u8 type, u8 feature)
{
/* XXX check if other confirms for that are queued and recycle slot */
struct dccp_opt_pend *opt = kzalloc(sizeof(*opt), GFP_ATOMIC);
if (opt == NULL) {
/* XXX what do we do? Ignoring should be fine. It's a change
* after all =P
*/
return;
}
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);
kfree(opt);
return;
}
opt->dccpop_feat = feature;
opt->dccpop_val = NULL;
opt->dccpop_len = 0;
/* change feature */
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_minisock *dmsk = dccp_msk(sk);
/* Check if there is anything to confirm in the first place */
int yes = !list_empty(&dmsk->dccpms_conf);
if (!yes) {
struct dccp_opt_pend *opt;
list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) {
if (opt->dccpop_conf) {
yes = 1;
break;
}
}
}
if (!yes)
return;
/* OK there is something to confirm... */
/* XXX check if packet is in flight? Send delayed ack?? */
if (sk->sk_state == DCCP_OPEN)
dccp_send_ack(sk);
}
int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len)
{
int rc;
dccp_feat_debug(type, feature, *val);
dccp: Support for the exchange of NN options in established state 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>
2008-09-04 05:30:19 +00:00
/* figure out if it's SP or NN feature */
switch (feature) {
/* deal with SP features */
case DCCPF_CCID:
rc = dccp_feat_sp(sk, type, feature, val, len);
break;
dccp: Support for the exchange of NN options in established state 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>
2008-09-04 05:30:19 +00:00
/* deal with NN features */
case DCCPF_ACK_RATIO:
rc = dccp_feat_nn(sk, type, feature, val, len);
break;
dccp: Support for the exchange of NN options in established state 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>
2008-09-04 05:30:19 +00:00
/* XXX implement other features */
default:
dccp_pr_debug("UNIMPLEMENTED: not handling %s(%d, ...)\n",
dccp_feat_typename(type), feature);
rc = -EFAULT;
break;
dccp: Support for the exchange of NN options in established state 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>
2008-09-04 05:30:19 +00:00
}
/* check if there were problems changing features */
if (rc) {
/* If we don't agree on SP, we sent a confirm for old value.
* However we propagate rc to caller in case option was
* mandatory
dccp: Resolve dependencies of features on choice of CCID This provides a missing link in the code chain, as several features implicitly depend and/or rely on the choice of CCID. Most notably, this is the Send Ack Vector feature, but also Ack Ratio and Send Loss Event Rate (also taken care of). For Send Ack Vector, the situation is as follows: * since CCID2 mandates the use of Ack Vectors, there is no point in allowing endpoints which use CCID2 to disable Ack Vector features such a connection; * a peer with a TX CCID of CCID2 will always expect Ack Vectors, and a peer with a RX CCID of CCID2 must always send Ack Vectors (RFC 4341, sec. 4); * for all other CCIDs, the use of (Send) Ack Vector is optional and thus negotiable. However, this implies that the code negotiating the use of Ack Vectors also supports it (i.e. is able to supply and to either parse or ignore received Ack Vectors). Since this is not the case (CCID-3 has no Ack Vector support), the use of Ack Vectors is here disabled, with a comment in the source code. An analogous consideration arises for the Send Loss Event Rate feature, since the CCID-3 implementation does not support the loss interval options of RFC 4342. To make such use explicit, corresponding feature-negotiation options are inserted which signal the use of the loss event rate option, as it is used by the CCID3 code. Lastly, the values of the Ack Ratio feature are matched to the choice of CCID. The patch implements this as a function which is called after the user has made all other registrations for changing default values of features. The table is variable-length, the reserved (and hence for feature-negotiation invalid, confirmed by considering section 19.4 of RFC 4340) feature number `0' is used to mark the end of the table. Signed-off-by: Gerrit Renker <gerrit@erg.abdn.ac.uk> Acked-by: Ian McDonald <ian.mcdonald@jandi.co.nz>
2008-09-04 05:30:19 +00:00
*/
if (rc != DCCP_FEAT_SP_NOAGREE)
dccp_feat_empty_confirm(dccp_msk(sk), type, feature);
dccp: Resolve dependencies of features on choice of CCID This provides a missing link in the code chain, as several features implicitly depend and/or rely on the choice of CCID. Most notably, this is the Send Ack Vector feature, but also Ack Ratio and Send Loss Event Rate (also taken care of). For Send Ack Vector, the situation is as follows: * since CCID2 mandates the use of Ack Vectors, there is no point in allowing endpoints which use CCID2 to disable Ack Vector features such a connection; * a peer with a TX CCID of CCID2 will always expect Ack Vectors, and a peer with a RX CCID of CCID2 must always send Ack Vectors (RFC 4341, sec. 4); * for all other CCIDs, the use of (Send) Ack Vector is optional and thus negotiable. However, this implies that the code negotiating the use of Ack Vectors also supports it (i.e. is able to supply and to either parse or ignore received Ack Vectors). Since this is not the case (CCID-3 has no Ack Vector support), the use of Ack Vectors is here disabled, with a comment in the source code. An analogous consideration arises for the Send Loss Event Rate feature, since the CCID-3 implementation does not support the loss interval options of RFC 4342. To make such use explicit, corresponding feature-negotiation options are inserted which signal the use of the loss event rate option, as it is used by the CCID3 code. Lastly, the values of the Ack Ratio feature are matched to the choice of CCID. The patch implements this as a function which is called after the user has made all other registrations for changing default values of features. The table is variable-length, the reserved (and hence for feature-negotiation invalid, confirmed by considering section 19.4 of RFC 4340) feature number `0' is used to mark the end of the table. Signed-off-by: Gerrit Renker <gerrit@erg.abdn.ac.uk> Acked-by: Ian McDonald <ian.mcdonald@jandi.co.nz>
2008-09-04 05:30:19 +00:00
}
/* generate the confirm [if required] */
dccp_feat_flush_confirm(sk);
dccp: Resolve dependencies of features on choice of CCID This provides a missing link in the code chain, as several features implicitly depend and/or rely on the choice of CCID. Most notably, this is the Send Ack Vector feature, but also Ack Ratio and Send Loss Event Rate (also taken care of). For Send Ack Vector, the situation is as follows: * since CCID2 mandates the use of Ack Vectors, there is no point in allowing endpoints which use CCID2 to disable Ack Vector features such a connection; * a peer with a TX CCID of CCID2 will always expect Ack Vectors, and a peer with a RX CCID of CCID2 must always send Ack Vectors (RFC 4341, sec. 4); * for all other CCIDs, the use of (Send) Ack Vector is optional and thus negotiable. However, this implies that the code negotiating the use of Ack Vectors also supports it (i.e. is able to supply and to either parse or ignore received Ack Vectors). Since this is not the case (CCID-3 has no Ack Vector support), the use of Ack Vectors is here disabled, with a comment in the source code. An analogous consideration arises for the Send Loss Event Rate feature, since the CCID-3 implementation does not support the loss interval options of RFC 4342. To make such use explicit, corresponding feature-negotiation options are inserted which signal the use of the loss event rate option, as it is used by the CCID3 code. Lastly, the values of the Ack Ratio feature are matched to the choice of CCID. The patch implements this as a function which is called after the user has made all other registrations for changing default values of features. The table is variable-length, the reserved (and hence for feature-negotiation invalid, confirmed by considering section 19.4 of RFC 4340) feature number `0' is used to mark the end of the table. Signed-off-by: Gerrit Renker <gerrit@erg.abdn.ac.uk> Acked-by: Ian McDonald <ian.mcdonald@jandi.co.nz>
2008-09-04 05:30:19 +00:00
return rc;
dccp: Resolve dependencies of features on choice of CCID This provides a missing link in the code chain, as several features implicitly depend and/or rely on the choice of CCID. Most notably, this is the Send Ack Vector feature, but also Ack Ratio and Send Loss Event Rate (also taken care of). For Send Ack Vector, the situation is as follows: * since CCID2 mandates the use of Ack Vectors, there is no point in allowing endpoints which use CCID2 to disable Ack Vector features such a connection; * a peer with a TX CCID of CCID2 will always expect Ack Vectors, and a peer with a RX CCID of CCID2 must always send Ack Vectors (RFC 4341, sec. 4); * for all other CCIDs, the use of (Send) Ack Vector is optional and thus negotiable. However, this implies that the code negotiating the use of Ack Vectors also supports it (i.e. is able to supply and to either parse or ignore received Ack Vectors). Since this is not the case (CCID-3 has no Ack Vector support), the use of Ack Vectors is here disabled, with a comment in the source code. An analogous consideration arises for the Send Loss Event Rate feature, since the CCID-3 implementation does not support the loss interval options of RFC 4342. To make such use explicit, corresponding feature-negotiation options are inserted which signal the use of the loss event rate option, as it is used by the CCID3 code. Lastly, the values of the Ack Ratio feature are matched to the choice of CCID. The patch implements this as a function which is called after the user has made all other registrations for changing default values of features. The table is variable-length, the reserved (and hence for feature-negotiation invalid, confirmed by considering section 19.4 of RFC 4340) feature number `0' is used to mark the end of the table. Signed-off-by: Gerrit Renker <gerrit@erg.abdn.ac.uk> Acked-by: Ian McDonald <ian.mcdonald@jandi.co.nz>
2008-09-04 05:30:19 +00:00
}
EXPORT_SYMBOL_GPL(dccp_feat_change_recv);
int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature,
u8 *val, u8 len)
{
u8 t;
struct dccp_opt_pend *opt;
struct dccp_minisock *dmsk = dccp_msk(sk);
int found = 0;
int all_confirmed = 1;
dccp_feat_debug(type, feature, *val);
/* locate our change request */
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, &dmsk->dccpms_pending, dccpop_node) {
if (!opt->dccpop_conf && opt->dccpop_type == t &&
opt->dccpop_feat == feature) {
found = 1;
dccp_pr_debug("feature %d found\n", opt->dccpop_feat);
/* XXX do sanity check */
opt->dccpop_conf = 1;
/* We got a confirmation---change the option */
dccp_feat_update(sk, opt->dccpop_type,
opt->dccpop_feat, *val);
/* XXX check the return value of dccp_feat_update */
break;
}
if (!opt->dccpop_conf)
all_confirmed = 0;
}
/* fix re-transmit timer */
/* XXX gotta make sure that no option negotiation occurs during
* connection shutdown. Consider that the CLOSEREQ is sent and timer is
* on. if all options are confirmed it might kill timer which should
* remain alive until close is received.
*/
if (all_confirmed) {
dccp_pr_debug("clear feat negotiation timer %p\n", sk);
inet_csk_clear_xmit_timer(sk, ICSK_TIME_RETRANS);
}
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 dccp_minisock *dmsk)
{
struct dccp_opt_pend *opt, *next;
list_for_each_entry_safe(opt, next, &dmsk->dccpms_pending,
dccpop_node) {
BUG_ON(opt->dccpop_val == NULL);
kfree(opt->dccpop_val);
if (opt->dccpop_sc != NULL) {
BUG_ON(opt->dccpop_sc->dccpoc_val == NULL);
kfree(opt->dccpop_sc->dccpoc_val);
kfree(opt->dccpop_sc);
}
kfree(opt);
}
INIT_LIST_HEAD(&dmsk->dccpms_pending);
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(&dmsk->dccpms_conf);
}
EXPORT_SYMBOL_GPL(dccp_feat_clean);
/* this is to be called only when a listening sock creates its child. It is
* assumed by the function---the confirm is not duplicated, but rather it is
* "passed on".
*/
int dccp_feat_clone(struct sock *oldsk, struct sock *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(&newdmsk->dccpms_pending);
INIT_LIST_HEAD(&newdmsk->dccpms_conf);
list_for_each_entry(opt, &olddmsk->dccpms_pending, dccpop_node) {
struct dccp_opt_pend *newopt;
/* copy the value of the option */
u8 *val = kmemdup(opt->dccpop_val, opt->dccpop_len, GFP_ATOMIC);
if (val == NULL)
goto out_clean;
newopt = kmemdup(opt, sizeof(*newopt), GFP_ATOMIC);
if (newopt == NULL) {
kfree(val);
goto out_clean;
}
/* insert the option */
newopt->dccpop_val = val;
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 = NULL; /* it's not a memleak---new socket has it */
/* reset state for a new socket */
opt->dccpop_conf = 0;
}
/* XXX not doing anything about the conf queue */
out:
return rc;
out_clean:
dccp_feat_clean(newdmsk);
rc = -ENOMEM;
goto out;
}
EXPORT_SYMBOL_GPL(dccp_feat_clone);
static int __dccp_feat_init(struct dccp_minisock *dmsk, u8 type, u8 feat,
u8 *val, u8 len)
{
int rc = -ENOMEM;
u8 *copy = kmemdup(val, len, GFP_KERNEL);
if (copy != NULL) {
rc = dccp_feat_change(dmsk, type, feat, copy, len, GFP_KERNEL);
if (rc)
kfree(copy);
}
return rc;
}
int dccp_feat_init(struct dccp_minisock *dmsk)
{
int rc;
INIT_LIST_HEAD(&dmsk->dccpms_pending);
INIT_LIST_HEAD(&dmsk->dccpms_conf);
/* CCID L */
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(dmsk, DCCPO_CHANGE_R, DCCPF_CCID,
&dmsk->dccpms_rx_ccid, 1);
if (rc)
goto out;
/* Ack ratio */
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_DATA_CHECKSUM && feat < DCCPF_MIN_CCID_SPECIFIC)
return feature_names[DCCPF_RESERVED];
if (feat >= DCCPF_MIN_CCID_SPECIFIC)
return "CCID-specific";
return feature_names[feat];
}
EXPORT_SYMBOL_GPL(dccp_feat_name);
#endif /* CONFIG_IP_DCCP_DEBUG */