1afe00251SAndrea Bittau /* 2afe00251SAndrea Bittau * net/dccp/feat.c 3afe00251SAndrea Bittau * 4afe00251SAndrea Bittau * An implementation of the DCCP protocol 5afe00251SAndrea Bittau * Andrea Bittau <a.bittau@cs.ucl.ac.uk> 6afe00251SAndrea Bittau * 75cdae198SGerrit Renker * ASSUMPTIONS 85cdae198SGerrit Renker * ----------- 95cdae198SGerrit Renker * o All currently known SP features have 1-byte quantities. If in the future 105cdae198SGerrit Renker * extensions of RFCs 4340..42 define features with item lengths larger than 115cdae198SGerrit Renker * one byte, a feature-specific extension of the code will be required. 125cdae198SGerrit Renker * 13afe00251SAndrea Bittau * This program is free software; you can redistribute it and/or 14afe00251SAndrea Bittau * modify it under the terms of the GNU General Public License 15afe00251SAndrea Bittau * as published by the Free Software Foundation; either version 16afe00251SAndrea Bittau * 2 of the License, or (at your option) any later version. 17afe00251SAndrea Bittau */ 18afe00251SAndrea Bittau 19afe00251SAndrea Bittau #include <linux/module.h> 20afe00251SAndrea Bittau 216ffd30fbSAndrea Bittau #include "ccid.h" 22afe00251SAndrea Bittau #include "feat.h" 23afe00251SAndrea Bittau 24afe00251SAndrea Bittau #define DCCP_FEAT_SP_NOAGREE (-123) 25afe00251SAndrea Bittau 267d43d1a0SGerrit Renker static const struct { 277d43d1a0SGerrit Renker u8 feat_num; /* DCCPF_xxx */ 287d43d1a0SGerrit Renker enum dccp_feat_type rxtx; /* RX or TX */ 297d43d1a0SGerrit Renker enum dccp_feat_type reconciliation; /* SP or NN */ 307d43d1a0SGerrit Renker u8 default_value; /* as in 6.4 */ 317d43d1a0SGerrit Renker /* 327d43d1a0SGerrit Renker * Lookup table for location and type of features (from RFC 4340/4342) 337d43d1a0SGerrit Renker * +--------------------------+----+-----+----+----+---------+-----------+ 347d43d1a0SGerrit Renker * | Feature | Location | Reconc. | Initial | Section | 357d43d1a0SGerrit Renker * | | RX | TX | SP | NN | Value | Reference | 367d43d1a0SGerrit Renker * +--------------------------+----+-----+----+----+---------+-----------+ 377d43d1a0SGerrit Renker * | DCCPF_CCID | | X | X | | 2 | 10 | 387d43d1a0SGerrit Renker * | DCCPF_SHORT_SEQNOS | | X | X | | 0 | 7.6.1 | 397d43d1a0SGerrit Renker * | DCCPF_SEQUENCE_WINDOW | | X | | X | 100 | 7.5.2 | 407d43d1a0SGerrit Renker * | DCCPF_ECN_INCAPABLE | X | | X | | 0 | 12.1 | 417d43d1a0SGerrit Renker * | DCCPF_ACK_RATIO | | X | | X | 2 | 11.3 | 427d43d1a0SGerrit Renker * | DCCPF_SEND_ACK_VECTOR | X | | X | | 0 | 11.5 | 437d43d1a0SGerrit Renker * | DCCPF_SEND_NDP_COUNT | | X | X | | 0 | 7.7.2 | 447d43d1a0SGerrit Renker * | DCCPF_MIN_CSUM_COVER | X | | X | | 0 | 9.2.1 | 457d43d1a0SGerrit Renker * | DCCPF_DATA_CHECKSUM | X | | X | | 0 | 9.3.1 | 467d43d1a0SGerrit Renker * | DCCPF_SEND_LEV_RATE | X | | X | | 0 | 4342/8.4 | 477d43d1a0SGerrit Renker * +--------------------------+----+-----+----+----+---------+-----------+ 487d43d1a0SGerrit Renker */ 497d43d1a0SGerrit Renker } dccp_feat_table[] = { 507d43d1a0SGerrit Renker { DCCPF_CCID, FEAT_AT_TX, FEAT_SP, 2 }, 517d43d1a0SGerrit Renker { DCCPF_SHORT_SEQNOS, FEAT_AT_TX, FEAT_SP, 0 }, 527d43d1a0SGerrit Renker { DCCPF_SEQUENCE_WINDOW, FEAT_AT_TX, FEAT_NN, 100 }, 537d43d1a0SGerrit Renker { DCCPF_ECN_INCAPABLE, FEAT_AT_RX, FEAT_SP, 0 }, 547d43d1a0SGerrit Renker { DCCPF_ACK_RATIO, FEAT_AT_TX, FEAT_NN, 2 }, 557d43d1a0SGerrit Renker { DCCPF_SEND_ACK_VECTOR, FEAT_AT_RX, FEAT_SP, 0 }, 567d43d1a0SGerrit Renker { DCCPF_SEND_NDP_COUNT, FEAT_AT_TX, FEAT_SP, 0 }, 577d43d1a0SGerrit Renker { DCCPF_MIN_CSUM_COVER, FEAT_AT_RX, FEAT_SP, 0 }, 587d43d1a0SGerrit Renker { DCCPF_DATA_CHECKSUM, FEAT_AT_RX, FEAT_SP, 0 }, 597d43d1a0SGerrit Renker { DCCPF_SEND_LEV_RATE, FEAT_AT_RX, FEAT_SP, 0 }, 607d43d1a0SGerrit Renker }; 617d43d1a0SGerrit Renker #define DCCP_FEAT_SUPPORTED_MAX ARRAY_SIZE(dccp_feat_table) 627d43d1a0SGerrit Renker 6361e6473eSGerrit Renker /** 6461e6473eSGerrit Renker * dccp_feat_index - Hash function to map feature number into array position 6561e6473eSGerrit Renker * Returns consecutive array index or -1 if the feature is not understood. 6661e6473eSGerrit Renker */ 6761e6473eSGerrit Renker static int dccp_feat_index(u8 feat_num) 6861e6473eSGerrit Renker { 6961e6473eSGerrit Renker /* The first 9 entries are occupied by the types from RFC 4340, 6.4 */ 7061e6473eSGerrit Renker if (feat_num > DCCPF_RESERVED && feat_num <= DCCPF_DATA_CHECKSUM) 7161e6473eSGerrit Renker return feat_num - 1; 7261e6473eSGerrit Renker 7361e6473eSGerrit Renker /* 7461e6473eSGerrit Renker * Other features: add cases for new feature types here after adding 7561e6473eSGerrit Renker * them to the above table. 7661e6473eSGerrit Renker */ 7761e6473eSGerrit Renker switch (feat_num) { 7861e6473eSGerrit Renker case DCCPF_SEND_LEV_RATE: 7961e6473eSGerrit Renker return DCCP_FEAT_SUPPORTED_MAX - 1; 8061e6473eSGerrit Renker } 8161e6473eSGerrit Renker return -1; 8261e6473eSGerrit Renker } 8361e6473eSGerrit Renker 8461e6473eSGerrit Renker static u8 dccp_feat_type(u8 feat_num) 8561e6473eSGerrit Renker { 8661e6473eSGerrit Renker int idx = dccp_feat_index(feat_num); 8761e6473eSGerrit Renker 8861e6473eSGerrit Renker if (idx < 0) 8961e6473eSGerrit Renker return FEAT_UNKNOWN; 9061e6473eSGerrit Renker return dccp_feat_table[idx].reconciliation; 9161e6473eSGerrit Renker } 9261e6473eSGerrit Renker 93*ac75773cSGerrit Renker /* copy constructor, fval must not already contain allocated memory */ 94*ac75773cSGerrit Renker static int dccp_feat_clone_sp_val(dccp_feat_val *fval, u8 const *val, u8 len) 95*ac75773cSGerrit Renker { 96*ac75773cSGerrit Renker fval->sp.len = len; 97*ac75773cSGerrit Renker if (fval->sp.len > 0) { 98*ac75773cSGerrit Renker fval->sp.vec = kmemdup(val, len, gfp_any()); 99*ac75773cSGerrit Renker if (fval->sp.vec == NULL) { 100*ac75773cSGerrit Renker fval->sp.len = 0; 101*ac75773cSGerrit Renker return -ENOBUFS; 102*ac75773cSGerrit Renker } 103*ac75773cSGerrit Renker } 104*ac75773cSGerrit Renker return 0; 105*ac75773cSGerrit Renker } 106*ac75773cSGerrit Renker 10761e6473eSGerrit Renker static void dccp_feat_val_destructor(u8 feat_num, dccp_feat_val *val) 10861e6473eSGerrit Renker { 10961e6473eSGerrit Renker if (unlikely(val == NULL)) 11061e6473eSGerrit Renker return; 11161e6473eSGerrit Renker if (dccp_feat_type(feat_num) == FEAT_SP) 11261e6473eSGerrit Renker kfree(val->sp.vec); 11361e6473eSGerrit Renker memset(val, 0, sizeof(*val)); 11461e6473eSGerrit Renker } 11561e6473eSGerrit Renker 116*ac75773cSGerrit Renker static struct dccp_feat_entry * 117*ac75773cSGerrit Renker dccp_feat_clone_entry(struct dccp_feat_entry const *original) 118*ac75773cSGerrit Renker { 119*ac75773cSGerrit Renker struct dccp_feat_entry *new; 120*ac75773cSGerrit Renker u8 type = dccp_feat_type(original->feat_num); 121*ac75773cSGerrit Renker 122*ac75773cSGerrit Renker if (type == FEAT_UNKNOWN) 123*ac75773cSGerrit Renker return NULL; 124*ac75773cSGerrit Renker 125*ac75773cSGerrit Renker new = kmemdup(original, sizeof(struct dccp_feat_entry), gfp_any()); 126*ac75773cSGerrit Renker if (new == NULL) 127*ac75773cSGerrit Renker return NULL; 128*ac75773cSGerrit Renker 129*ac75773cSGerrit Renker if (type == FEAT_SP && dccp_feat_clone_sp_val(&new->val, 130*ac75773cSGerrit Renker original->val.sp.vec, 131*ac75773cSGerrit Renker original->val.sp.len)) { 132*ac75773cSGerrit Renker kfree(new); 133*ac75773cSGerrit Renker return NULL; 134*ac75773cSGerrit Renker } 135*ac75773cSGerrit Renker return new; 136*ac75773cSGerrit Renker } 137*ac75773cSGerrit Renker 13861e6473eSGerrit Renker static void dccp_feat_entry_destructor(struct dccp_feat_entry *entry) 13961e6473eSGerrit Renker { 14061e6473eSGerrit Renker if (entry != NULL) { 14161e6473eSGerrit Renker dccp_feat_val_destructor(entry->feat_num, &entry->val); 14261e6473eSGerrit Renker kfree(entry); 14361e6473eSGerrit Renker } 14461e6473eSGerrit Renker } 14561e6473eSGerrit Renker 14661e6473eSGerrit Renker /* 14761e6473eSGerrit Renker * List management functions 14861e6473eSGerrit Renker * 14961e6473eSGerrit Renker * Feature negotiation lists rely on and maintain the following invariants: 15061e6473eSGerrit Renker * - each feat_num in the list is known, i.e. we know its type and default value 15161e6473eSGerrit Renker * - each feat_num/is_local combination is unique (old entries are overwritten) 15261e6473eSGerrit Renker * - SP values are always freshly allocated 15361e6473eSGerrit Renker * - list is sorted in increasing order of feature number (faster lookup) 15461e6473eSGerrit Renker */ 15561e6473eSGerrit Renker 15661e6473eSGerrit Renker static inline void dccp_feat_list_pop(struct dccp_feat_entry *entry) 15761e6473eSGerrit Renker { 15861e6473eSGerrit Renker list_del(&entry->node); 15961e6473eSGerrit Renker dccp_feat_entry_destructor(entry); 16061e6473eSGerrit Renker } 16161e6473eSGerrit Renker 16261e6473eSGerrit Renker void dccp_feat_list_purge(struct list_head *fn_list) 16361e6473eSGerrit Renker { 16461e6473eSGerrit Renker struct dccp_feat_entry *entry, *next; 16561e6473eSGerrit Renker 16661e6473eSGerrit Renker list_for_each_entry_safe(entry, next, fn_list, node) 16761e6473eSGerrit Renker dccp_feat_entry_destructor(entry); 16861e6473eSGerrit Renker INIT_LIST_HEAD(fn_list); 16961e6473eSGerrit Renker } 17061e6473eSGerrit Renker EXPORT_SYMBOL_GPL(dccp_feat_list_purge); 17161e6473eSGerrit Renker 172*ac75773cSGerrit Renker /* generate @to as full clone of @from - @to must not contain any nodes */ 173*ac75773cSGerrit Renker int dccp_feat_clone_list(struct list_head const *from, struct list_head *to) 174*ac75773cSGerrit Renker { 175*ac75773cSGerrit Renker struct dccp_feat_entry *entry, *new; 176*ac75773cSGerrit Renker 177*ac75773cSGerrit Renker INIT_LIST_HEAD(to); 178*ac75773cSGerrit Renker list_for_each_entry(entry, from, node) { 179*ac75773cSGerrit Renker new = dccp_feat_clone_entry(entry); 180*ac75773cSGerrit Renker if (new == NULL) 181*ac75773cSGerrit Renker goto cloning_failed; 182*ac75773cSGerrit Renker list_add_tail(&new->node, to); 183*ac75773cSGerrit Renker } 184*ac75773cSGerrit Renker return 0; 185*ac75773cSGerrit Renker 186*ac75773cSGerrit Renker cloning_failed: 187*ac75773cSGerrit Renker dccp_feat_list_purge(to); 188*ac75773cSGerrit Renker return -ENOMEM; 189*ac75773cSGerrit Renker } 190*ac75773cSGerrit Renker 1918ca0d17bSArnaldo Carvalho de Melo int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature, 1928ca0d17bSArnaldo Carvalho de Melo u8 *val, u8 len, gfp_t gfp) 193afe00251SAndrea Bittau { 194afe00251SAndrea Bittau struct dccp_opt_pend *opt; 195afe00251SAndrea Bittau 196c02fdc0eSGerrit Renker dccp_feat_debug(type, feature, *val); 197afe00251SAndrea Bittau 198dd6303dfSGerrit Renker if (len > 3) { 19959348b19SGerrit Renker DCCP_WARN("invalid length %d\n", len); 20019443178SChris Wright return -EINVAL; 201c02fdc0eSGerrit Renker } 202c02fdc0eSGerrit Renker /* XXX add further sanity checks */ 2036ffd30fbSAndrea Bittau 204afe00251SAndrea Bittau /* check if that feature is already being negotiated */ 205a4bf3902SArnaldo Carvalho de Melo list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) { 206afe00251SAndrea Bittau /* ok we found a negotiation for this option already */ 207afe00251SAndrea Bittau if (opt->dccpop_feat == feature && opt->dccpop_type == type) { 208afe00251SAndrea Bittau dccp_pr_debug("Replacing old\n"); 209afe00251SAndrea Bittau /* replace */ 210afe00251SAndrea Bittau BUG_ON(opt->dccpop_val == NULL); 211afe00251SAndrea Bittau kfree(opt->dccpop_val); 212afe00251SAndrea Bittau opt->dccpop_val = val; 213afe00251SAndrea Bittau opt->dccpop_len = len; 214afe00251SAndrea Bittau opt->dccpop_conf = 0; 215afe00251SAndrea Bittau return 0; 216afe00251SAndrea Bittau } 217afe00251SAndrea Bittau } 218afe00251SAndrea Bittau 219afe00251SAndrea Bittau /* negotiation for a new feature */ 220afe00251SAndrea Bittau opt = kmalloc(sizeof(*opt), gfp); 221afe00251SAndrea Bittau if (opt == NULL) 222afe00251SAndrea Bittau return -ENOMEM; 223afe00251SAndrea Bittau 224afe00251SAndrea Bittau opt->dccpop_type = type; 225afe00251SAndrea Bittau opt->dccpop_feat = feature; 226afe00251SAndrea Bittau opt->dccpop_len = len; 227afe00251SAndrea Bittau opt->dccpop_val = val; 228afe00251SAndrea Bittau opt->dccpop_conf = 0; 229afe00251SAndrea Bittau opt->dccpop_sc = NULL; 230afe00251SAndrea Bittau 231afe00251SAndrea Bittau BUG_ON(opt->dccpop_val == NULL); 232afe00251SAndrea Bittau 233a4bf3902SArnaldo Carvalho de Melo list_add_tail(&opt->dccpop_node, &dmsk->dccpms_pending); 234afe00251SAndrea Bittau return 0; 235afe00251SAndrea Bittau } 236afe00251SAndrea Bittau 237afe00251SAndrea Bittau EXPORT_SYMBOL_GPL(dccp_feat_change); 238afe00251SAndrea Bittau 2396ffd30fbSAndrea Bittau static int dccp_feat_update_ccid(struct sock *sk, u8 type, u8 new_ccid_nr) 2406ffd30fbSAndrea Bittau { 2416ffd30fbSAndrea Bittau struct dccp_sock *dp = dccp_sk(sk); 242a4bf3902SArnaldo Carvalho de Melo struct dccp_minisock *dmsk = dccp_msk(sk); 2436ffd30fbSAndrea Bittau /* figure out if we are changing our CCID or the peer's */ 2446ffd30fbSAndrea Bittau const int rx = type == DCCPO_CHANGE_R; 245a4bf3902SArnaldo Carvalho de Melo const u8 ccid_nr = rx ? dmsk->dccpms_rx_ccid : dmsk->dccpms_tx_ccid; 2466ffd30fbSAndrea Bittau struct ccid *new_ccid; 2476ffd30fbSAndrea Bittau 2486ffd30fbSAndrea Bittau /* Check if nothing is being changed. */ 2496ffd30fbSAndrea Bittau if (ccid_nr == new_ccid_nr) 2506ffd30fbSAndrea Bittau return 0; 2516ffd30fbSAndrea Bittau 2526ffd30fbSAndrea Bittau new_ccid = ccid_new(new_ccid_nr, sk, rx, GFP_ATOMIC); 2536ffd30fbSAndrea Bittau if (new_ccid == NULL) 2546ffd30fbSAndrea Bittau return -ENOMEM; 2556ffd30fbSAndrea Bittau 2566ffd30fbSAndrea Bittau if (rx) { 2576ffd30fbSAndrea Bittau ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk); 2586ffd30fbSAndrea Bittau dp->dccps_hc_rx_ccid = new_ccid; 259a4bf3902SArnaldo Carvalho de Melo dmsk->dccpms_rx_ccid = new_ccid_nr; 2606ffd30fbSAndrea Bittau } else { 2616ffd30fbSAndrea Bittau ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk); 2626ffd30fbSAndrea Bittau dp->dccps_hc_tx_ccid = new_ccid; 263a4bf3902SArnaldo Carvalho de Melo dmsk->dccpms_tx_ccid = new_ccid_nr; 2646ffd30fbSAndrea Bittau } 2656ffd30fbSAndrea Bittau 2666ffd30fbSAndrea Bittau return 0; 2676ffd30fbSAndrea Bittau } 2686ffd30fbSAndrea Bittau 269afe00251SAndrea Bittau static int dccp_feat_update(struct sock *sk, u8 type, u8 feat, u8 val) 270afe00251SAndrea Bittau { 271c02fdc0eSGerrit Renker dccp_feat_debug(type, feat, val); 2726ffd30fbSAndrea Bittau 2736ffd30fbSAndrea Bittau switch (feat) { 2746ffd30fbSAndrea Bittau case DCCPF_CCID: 2756ffd30fbSAndrea Bittau return dccp_feat_update_ccid(sk, type, val); 2766ffd30fbSAndrea Bittau default: 277c02fdc0eSGerrit Renker dccp_pr_debug("UNIMPLEMENTED: %s(%d, ...)\n", 278c02fdc0eSGerrit Renker dccp_feat_typename(type), feat); 2796ffd30fbSAndrea Bittau break; 2806ffd30fbSAndrea Bittau } 281afe00251SAndrea Bittau return 0; 282afe00251SAndrea Bittau } 283afe00251SAndrea Bittau 284afe00251SAndrea Bittau static int dccp_feat_reconcile(struct sock *sk, struct dccp_opt_pend *opt, 285afe00251SAndrea Bittau u8 *rpref, u8 rlen) 286afe00251SAndrea Bittau { 287afe00251SAndrea Bittau struct dccp_sock *dp = dccp_sk(sk); 288afe00251SAndrea Bittau u8 *spref, slen, *res = NULL; 289afe00251SAndrea Bittau int i, j, rc, agree = 1; 290afe00251SAndrea Bittau 291afe00251SAndrea Bittau BUG_ON(rpref == NULL); 292afe00251SAndrea Bittau 293afe00251SAndrea Bittau /* check if we are the black sheep */ 294afe00251SAndrea Bittau if (dp->dccps_role == DCCP_ROLE_CLIENT) { 295afe00251SAndrea Bittau spref = rpref; 296afe00251SAndrea Bittau slen = rlen; 297afe00251SAndrea Bittau rpref = opt->dccpop_val; 298afe00251SAndrea Bittau rlen = opt->dccpop_len; 299afe00251SAndrea Bittau } else { 300afe00251SAndrea Bittau spref = opt->dccpop_val; 301afe00251SAndrea Bittau slen = opt->dccpop_len; 302afe00251SAndrea Bittau } 303afe00251SAndrea Bittau /* 304afe00251SAndrea Bittau * Now we have server preference list in spref and client preference in 305afe00251SAndrea Bittau * rpref 306afe00251SAndrea Bittau */ 307afe00251SAndrea Bittau BUG_ON(spref == NULL); 308afe00251SAndrea Bittau BUG_ON(rpref == NULL); 309afe00251SAndrea Bittau 310afe00251SAndrea Bittau /* FIXME sanity check vals */ 311afe00251SAndrea Bittau 312afe00251SAndrea Bittau /* Are values in any order? XXX Lame "algorithm" here */ 313afe00251SAndrea Bittau for (i = 0; i < slen; i++) { 314afe00251SAndrea Bittau for (j = 0; j < rlen; j++) { 315afe00251SAndrea Bittau if (spref[i] == rpref[j]) { 316afe00251SAndrea Bittau res = &spref[i]; 317afe00251SAndrea Bittau break; 318afe00251SAndrea Bittau } 319afe00251SAndrea Bittau } 320afe00251SAndrea Bittau if (res) 321afe00251SAndrea Bittau break; 322afe00251SAndrea Bittau } 323afe00251SAndrea Bittau 324afe00251SAndrea Bittau /* we didn't agree on anything */ 325afe00251SAndrea Bittau if (res == NULL) { 326afe00251SAndrea Bittau /* confirm previous value */ 327afe00251SAndrea Bittau switch (opt->dccpop_feat) { 328afe00251SAndrea Bittau case DCCPF_CCID: 329afe00251SAndrea Bittau /* XXX did i get this right? =P */ 330afe00251SAndrea Bittau if (opt->dccpop_type == DCCPO_CHANGE_L) 331a4bf3902SArnaldo Carvalho de Melo res = &dccp_msk(sk)->dccpms_tx_ccid; 332afe00251SAndrea Bittau else 333a4bf3902SArnaldo Carvalho de Melo res = &dccp_msk(sk)->dccpms_rx_ccid; 334afe00251SAndrea Bittau break; 335afe00251SAndrea Bittau 336afe00251SAndrea Bittau default: 33759348b19SGerrit Renker DCCP_BUG("Fell through, feat=%d", opt->dccpop_feat); 33859348b19SGerrit Renker /* XXX implement res */ 339afe00251SAndrea Bittau return -EFAULT; 340afe00251SAndrea Bittau } 341afe00251SAndrea Bittau 342afe00251SAndrea Bittau dccp_pr_debug("Don't agree... reconfirming %d\n", *res); 343afe00251SAndrea Bittau agree = 0; /* this is used for mandatory options... */ 344afe00251SAndrea Bittau } 345afe00251SAndrea Bittau 346afe00251SAndrea Bittau /* need to put result and our preference list */ 347afe00251SAndrea Bittau rlen = 1 + opt->dccpop_len; 348afe00251SAndrea Bittau rpref = kmalloc(rlen, GFP_ATOMIC); 349afe00251SAndrea Bittau if (rpref == NULL) 350afe00251SAndrea Bittau return -ENOMEM; 351afe00251SAndrea Bittau 352afe00251SAndrea Bittau *rpref = *res; 353afe00251SAndrea Bittau memcpy(&rpref[1], opt->dccpop_val, opt->dccpop_len); 354afe00251SAndrea Bittau 355afe00251SAndrea Bittau /* put it in the "confirm queue" */ 356afe00251SAndrea Bittau if (opt->dccpop_sc == NULL) { 357afe00251SAndrea Bittau opt->dccpop_sc = kmalloc(sizeof(*opt->dccpop_sc), GFP_ATOMIC); 358afe00251SAndrea Bittau if (opt->dccpop_sc == NULL) { 359afe00251SAndrea Bittau kfree(rpref); 360afe00251SAndrea Bittau return -ENOMEM; 361afe00251SAndrea Bittau } 362afe00251SAndrea Bittau } else { 363afe00251SAndrea Bittau /* recycle the confirm slot */ 364afe00251SAndrea Bittau BUG_ON(opt->dccpop_sc->dccpoc_val == NULL); 365afe00251SAndrea Bittau kfree(opt->dccpop_sc->dccpoc_val); 366afe00251SAndrea Bittau dccp_pr_debug("recycling confirm slot\n"); 367afe00251SAndrea Bittau } 368afe00251SAndrea Bittau memset(opt->dccpop_sc, 0, sizeof(*opt->dccpop_sc)); 369afe00251SAndrea Bittau 370afe00251SAndrea Bittau opt->dccpop_sc->dccpoc_val = rpref; 371afe00251SAndrea Bittau opt->dccpop_sc->dccpoc_len = rlen; 372afe00251SAndrea Bittau 373afe00251SAndrea Bittau /* update the option on our side [we are about to send the confirm] */ 374afe00251SAndrea Bittau rc = dccp_feat_update(sk, opt->dccpop_type, opt->dccpop_feat, *res); 375afe00251SAndrea Bittau if (rc) { 376afe00251SAndrea Bittau kfree(opt->dccpop_sc->dccpoc_val); 377afe00251SAndrea Bittau kfree(opt->dccpop_sc); 37868907dadSRandy Dunlap opt->dccpop_sc = NULL; 379afe00251SAndrea Bittau return rc; 380afe00251SAndrea Bittau } 381afe00251SAndrea Bittau 382afe00251SAndrea Bittau dccp_pr_debug("Will confirm %d\n", *rpref); 383afe00251SAndrea Bittau 384afe00251SAndrea Bittau /* say we want to change to X but we just got a confirm X, suppress our 385afe00251SAndrea Bittau * change 386afe00251SAndrea Bittau */ 387afe00251SAndrea Bittau if (!opt->dccpop_conf) { 388afe00251SAndrea Bittau if (*opt->dccpop_val == *res) 389afe00251SAndrea Bittau opt->dccpop_conf = 1; 390afe00251SAndrea Bittau dccp_pr_debug("won't ask for change of same feature\n"); 391afe00251SAndrea Bittau } 392afe00251SAndrea Bittau 393afe00251SAndrea Bittau return agree ? 0 : DCCP_FEAT_SP_NOAGREE; /* used for mandatory opts */ 394afe00251SAndrea Bittau } 395afe00251SAndrea Bittau 396afe00251SAndrea Bittau static int dccp_feat_sp(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len) 397afe00251SAndrea Bittau { 398a4bf3902SArnaldo Carvalho de Melo struct dccp_minisock *dmsk = dccp_msk(sk); 399afe00251SAndrea Bittau struct dccp_opt_pend *opt; 400afe00251SAndrea Bittau int rc = 1; 401afe00251SAndrea Bittau u8 t; 402afe00251SAndrea Bittau 403afe00251SAndrea Bittau /* 404afe00251SAndrea Bittau * We received a CHANGE. We gotta match it against our own preference 405afe00251SAndrea Bittau * list. If we got a CHANGE_R it means it's a change for us, so we need 406afe00251SAndrea Bittau * to compare our CHANGE_L list. 407afe00251SAndrea Bittau */ 408afe00251SAndrea Bittau if (type == DCCPO_CHANGE_L) 409afe00251SAndrea Bittau t = DCCPO_CHANGE_R; 410afe00251SAndrea Bittau else 411afe00251SAndrea Bittau t = DCCPO_CHANGE_L; 412afe00251SAndrea Bittau 413afe00251SAndrea Bittau /* find our preference list for this feature */ 414a4bf3902SArnaldo Carvalho de Melo list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) { 415afe00251SAndrea Bittau if (opt->dccpop_type != t || opt->dccpop_feat != feature) 416afe00251SAndrea Bittau continue; 417afe00251SAndrea Bittau 418afe00251SAndrea Bittau /* find the winner from the two preference lists */ 419afe00251SAndrea Bittau rc = dccp_feat_reconcile(sk, opt, val, len); 420afe00251SAndrea Bittau break; 421afe00251SAndrea Bittau } 422afe00251SAndrea Bittau 423afe00251SAndrea Bittau /* We didn't deal with the change. This can happen if we have no 424afe00251SAndrea Bittau * preference list for the feature. In fact, it just shouldn't 425afe00251SAndrea Bittau * happen---if we understand a feature, we should have a preference list 426afe00251SAndrea Bittau * with at least the default value. 427afe00251SAndrea Bittau */ 428afe00251SAndrea Bittau BUG_ON(rc == 1); 429afe00251SAndrea Bittau 430afe00251SAndrea Bittau return rc; 431afe00251SAndrea Bittau } 432afe00251SAndrea Bittau 433afe00251SAndrea Bittau static int dccp_feat_nn(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len) 434afe00251SAndrea Bittau { 435afe00251SAndrea Bittau struct dccp_opt_pend *opt; 436a4bf3902SArnaldo Carvalho de Melo struct dccp_minisock *dmsk = dccp_msk(sk); 437afe00251SAndrea Bittau u8 *copy; 438afe00251SAndrea Bittau int rc; 439afe00251SAndrea Bittau 440c02fdc0eSGerrit Renker /* NN features must be Change L (sec. 6.3.2) */ 441c02fdc0eSGerrit Renker if (type != DCCPO_CHANGE_L) { 442c02fdc0eSGerrit Renker dccp_pr_debug("received %s for NN feature %d\n", 443c02fdc0eSGerrit Renker dccp_feat_typename(type), feature); 444afe00251SAndrea Bittau return -EFAULT; 445afe00251SAndrea Bittau } 446afe00251SAndrea Bittau 447afe00251SAndrea Bittau /* XXX sanity check opt val */ 448afe00251SAndrea Bittau 449afe00251SAndrea Bittau /* copy option so we can confirm it */ 450afe00251SAndrea Bittau opt = kzalloc(sizeof(*opt), GFP_ATOMIC); 451afe00251SAndrea Bittau if (opt == NULL) 452afe00251SAndrea Bittau return -ENOMEM; 453afe00251SAndrea Bittau 454eed73417SArnaldo Carvalho de Melo copy = kmemdup(val, len, GFP_ATOMIC); 455afe00251SAndrea Bittau if (copy == NULL) { 456afe00251SAndrea Bittau kfree(opt); 457afe00251SAndrea Bittau return -ENOMEM; 458afe00251SAndrea Bittau } 459afe00251SAndrea Bittau 460afe00251SAndrea Bittau opt->dccpop_type = DCCPO_CONFIRM_R; /* NN can only confirm R */ 461afe00251SAndrea Bittau opt->dccpop_feat = feature; 462afe00251SAndrea Bittau opt->dccpop_val = copy; 463afe00251SAndrea Bittau opt->dccpop_len = len; 464afe00251SAndrea Bittau 465afe00251SAndrea Bittau /* change feature */ 466afe00251SAndrea Bittau rc = dccp_feat_update(sk, type, feature, *val); 467afe00251SAndrea Bittau if (rc) { 468afe00251SAndrea Bittau kfree(opt->dccpop_val); 469afe00251SAndrea Bittau kfree(opt); 470afe00251SAndrea Bittau return rc; 471afe00251SAndrea Bittau } 472afe00251SAndrea Bittau 473c02fdc0eSGerrit Renker dccp_feat_debug(type, feature, *copy); 474c02fdc0eSGerrit Renker 475a4bf3902SArnaldo Carvalho de Melo list_add_tail(&opt->dccpop_node, &dmsk->dccpms_conf); 476afe00251SAndrea Bittau 477afe00251SAndrea Bittau return 0; 478afe00251SAndrea Bittau } 479afe00251SAndrea Bittau 4808ca0d17bSArnaldo Carvalho de Melo static void dccp_feat_empty_confirm(struct dccp_minisock *dmsk, 4818ca0d17bSArnaldo Carvalho de Melo u8 type, u8 feature) 482afe00251SAndrea Bittau { 483afe00251SAndrea Bittau /* XXX check if other confirms for that are queued and recycle slot */ 484afe00251SAndrea Bittau struct dccp_opt_pend *opt = kzalloc(sizeof(*opt), GFP_ATOMIC); 485afe00251SAndrea Bittau 486afe00251SAndrea Bittau if (opt == NULL) { 487afe00251SAndrea Bittau /* XXX what do we do? Ignoring should be fine. It's a change 488afe00251SAndrea Bittau * after all =P 489afe00251SAndrea Bittau */ 490afe00251SAndrea Bittau return; 491afe00251SAndrea Bittau } 492afe00251SAndrea Bittau 493c02fdc0eSGerrit Renker switch (type) { 494e576de82SJesper Juhl case DCCPO_CHANGE_L: 495e576de82SJesper Juhl opt->dccpop_type = DCCPO_CONFIRM_R; 496e576de82SJesper Juhl break; 497e576de82SJesper Juhl case DCCPO_CHANGE_R: 498e576de82SJesper Juhl opt->dccpop_type = DCCPO_CONFIRM_L; 499e576de82SJesper Juhl break; 500e576de82SJesper Juhl default: 501e576de82SJesper Juhl DCCP_WARN("invalid type %d\n", type); 502e576de82SJesper Juhl kfree(opt); 503e576de82SJesper Juhl return; 504c02fdc0eSGerrit Renker } 505afe00251SAndrea Bittau opt->dccpop_feat = feature; 50668907dadSRandy Dunlap opt->dccpop_val = NULL; 507afe00251SAndrea Bittau opt->dccpop_len = 0; 508afe00251SAndrea Bittau 509afe00251SAndrea Bittau /* change feature */ 510c02fdc0eSGerrit Renker dccp_pr_debug("Empty %s(%d)\n", dccp_feat_typename(type), feature); 511c02fdc0eSGerrit Renker 512a4bf3902SArnaldo Carvalho de Melo list_add_tail(&opt->dccpop_node, &dmsk->dccpms_conf); 513afe00251SAndrea Bittau } 514afe00251SAndrea Bittau 515afe00251SAndrea Bittau static void dccp_feat_flush_confirm(struct sock *sk) 516afe00251SAndrea Bittau { 517a4bf3902SArnaldo Carvalho de Melo struct dccp_minisock *dmsk = dccp_msk(sk); 518afe00251SAndrea Bittau /* Check if there is anything to confirm in the first place */ 519a4bf3902SArnaldo Carvalho de Melo int yes = !list_empty(&dmsk->dccpms_conf); 520afe00251SAndrea Bittau 521afe00251SAndrea Bittau if (!yes) { 522afe00251SAndrea Bittau struct dccp_opt_pend *opt; 523afe00251SAndrea Bittau 524a4bf3902SArnaldo Carvalho de Melo list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) { 525afe00251SAndrea Bittau if (opt->dccpop_conf) { 526afe00251SAndrea Bittau yes = 1; 527afe00251SAndrea Bittau break; 528afe00251SAndrea Bittau } 529afe00251SAndrea Bittau } 530afe00251SAndrea Bittau } 531afe00251SAndrea Bittau 532afe00251SAndrea Bittau if (!yes) 533afe00251SAndrea Bittau return; 534afe00251SAndrea Bittau 535afe00251SAndrea Bittau /* OK there is something to confirm... */ 536afe00251SAndrea Bittau /* XXX check if packet is in flight? Send delayed ack?? */ 537afe00251SAndrea Bittau if (sk->sk_state == DCCP_OPEN) 538afe00251SAndrea Bittau dccp_send_ack(sk); 539afe00251SAndrea Bittau } 540afe00251SAndrea Bittau 541afe00251SAndrea Bittau int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len) 542afe00251SAndrea Bittau { 543afe00251SAndrea Bittau int rc; 544afe00251SAndrea Bittau 545c02fdc0eSGerrit Renker dccp_feat_debug(type, feature, *val); 546afe00251SAndrea Bittau 547afe00251SAndrea Bittau /* figure out if it's SP or NN feature */ 548afe00251SAndrea Bittau switch (feature) { 549afe00251SAndrea Bittau /* deal with SP features */ 550afe00251SAndrea Bittau case DCCPF_CCID: 551afe00251SAndrea Bittau rc = dccp_feat_sp(sk, type, feature, val, len); 552afe00251SAndrea Bittau break; 553afe00251SAndrea Bittau 554afe00251SAndrea Bittau /* deal with NN features */ 555afe00251SAndrea Bittau case DCCPF_ACK_RATIO: 556afe00251SAndrea Bittau rc = dccp_feat_nn(sk, type, feature, val, len); 557afe00251SAndrea Bittau break; 558afe00251SAndrea Bittau 559afe00251SAndrea Bittau /* XXX implement other features */ 560afe00251SAndrea Bittau default: 561c02fdc0eSGerrit Renker dccp_pr_debug("UNIMPLEMENTED: not handling %s(%d, ...)\n", 562c02fdc0eSGerrit Renker dccp_feat_typename(type), feature); 563afe00251SAndrea Bittau rc = -EFAULT; 564afe00251SAndrea Bittau break; 565afe00251SAndrea Bittau } 566afe00251SAndrea Bittau 567afe00251SAndrea Bittau /* check if there were problems changing features */ 568afe00251SAndrea Bittau if (rc) { 569afe00251SAndrea Bittau /* If we don't agree on SP, we sent a confirm for old value. 570afe00251SAndrea Bittau * However we propagate rc to caller in case option was 571afe00251SAndrea Bittau * mandatory 572afe00251SAndrea Bittau */ 573afe00251SAndrea Bittau if (rc != DCCP_FEAT_SP_NOAGREE) 5748ca0d17bSArnaldo Carvalho de Melo dccp_feat_empty_confirm(dccp_msk(sk), type, feature); 575afe00251SAndrea Bittau } 576afe00251SAndrea Bittau 577afe00251SAndrea Bittau /* generate the confirm [if required] */ 578afe00251SAndrea Bittau dccp_feat_flush_confirm(sk); 579afe00251SAndrea Bittau 580afe00251SAndrea Bittau return rc; 581afe00251SAndrea Bittau } 582afe00251SAndrea Bittau 583afe00251SAndrea Bittau EXPORT_SYMBOL_GPL(dccp_feat_change_recv); 584afe00251SAndrea Bittau 585afe00251SAndrea Bittau int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, 586afe00251SAndrea Bittau u8 *val, u8 len) 587afe00251SAndrea Bittau { 588afe00251SAndrea Bittau u8 t; 589afe00251SAndrea Bittau struct dccp_opt_pend *opt; 590a4bf3902SArnaldo Carvalho de Melo struct dccp_minisock *dmsk = dccp_msk(sk); 591c02fdc0eSGerrit Renker int found = 0; 592afe00251SAndrea Bittau int all_confirmed = 1; 593afe00251SAndrea Bittau 594c02fdc0eSGerrit Renker dccp_feat_debug(type, feature, *val); 595afe00251SAndrea Bittau 596afe00251SAndrea Bittau /* locate our change request */ 597c02fdc0eSGerrit Renker switch (type) { 598c02fdc0eSGerrit Renker case DCCPO_CONFIRM_L: t = DCCPO_CHANGE_R; break; 599c02fdc0eSGerrit Renker case DCCPO_CONFIRM_R: t = DCCPO_CHANGE_L; break; 60059348b19SGerrit Renker default: DCCP_WARN("invalid type %d\n", type); 601c02fdc0eSGerrit Renker return 1; 602c02fdc0eSGerrit Renker 603c02fdc0eSGerrit Renker } 604c02fdc0eSGerrit Renker /* XXX sanity check feature value */ 605afe00251SAndrea Bittau 606a4bf3902SArnaldo Carvalho de Melo list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) { 607afe00251SAndrea Bittau if (!opt->dccpop_conf && opt->dccpop_type == t && 608afe00251SAndrea Bittau opt->dccpop_feat == feature) { 609c02fdc0eSGerrit Renker found = 1; 610c02fdc0eSGerrit Renker dccp_pr_debug("feature %d found\n", opt->dccpop_feat); 611c02fdc0eSGerrit Renker 612afe00251SAndrea Bittau /* XXX do sanity check */ 613afe00251SAndrea Bittau 614afe00251SAndrea Bittau opt->dccpop_conf = 1; 615afe00251SAndrea Bittau 616afe00251SAndrea Bittau /* We got a confirmation---change the option */ 617afe00251SAndrea Bittau dccp_feat_update(sk, opt->dccpop_type, 618afe00251SAndrea Bittau opt->dccpop_feat, *val); 619afe00251SAndrea Bittau 620c02fdc0eSGerrit Renker /* XXX check the return value of dccp_feat_update */ 621afe00251SAndrea Bittau break; 622afe00251SAndrea Bittau } 623afe00251SAndrea Bittau 624afe00251SAndrea Bittau if (!opt->dccpop_conf) 625afe00251SAndrea Bittau all_confirmed = 0; 626afe00251SAndrea Bittau } 627afe00251SAndrea Bittau 628afe00251SAndrea Bittau /* fix re-transmit timer */ 629afe00251SAndrea Bittau /* XXX gotta make sure that no option negotiation occurs during 630afe00251SAndrea Bittau * connection shutdown. Consider that the CLOSEREQ is sent and timer is 631afe00251SAndrea Bittau * on. if all options are confirmed it might kill timer which should 632afe00251SAndrea Bittau * remain alive until close is received. 633afe00251SAndrea Bittau */ 634afe00251SAndrea Bittau if (all_confirmed) { 635afe00251SAndrea Bittau dccp_pr_debug("clear feat negotiation timer %p\n", sk); 636afe00251SAndrea Bittau inet_csk_clear_xmit_timer(sk, ICSK_TIME_RETRANS); 637afe00251SAndrea Bittau } 638afe00251SAndrea Bittau 639c02fdc0eSGerrit Renker if (!found) 640c02fdc0eSGerrit Renker dccp_pr_debug("%s(%d, ...) never requested\n", 641c02fdc0eSGerrit Renker dccp_feat_typename(type), feature); 642afe00251SAndrea Bittau return 0; 643afe00251SAndrea Bittau } 644afe00251SAndrea Bittau 645afe00251SAndrea Bittau EXPORT_SYMBOL_GPL(dccp_feat_confirm_recv); 646afe00251SAndrea Bittau 6478ca0d17bSArnaldo Carvalho de Melo void dccp_feat_clean(struct dccp_minisock *dmsk) 648afe00251SAndrea Bittau { 649afe00251SAndrea Bittau struct dccp_opt_pend *opt, *next; 650afe00251SAndrea Bittau 651a4bf3902SArnaldo Carvalho de Melo list_for_each_entry_safe(opt, next, &dmsk->dccpms_pending, 652afe00251SAndrea Bittau dccpop_node) { 653afe00251SAndrea Bittau BUG_ON(opt->dccpop_val == NULL); 654afe00251SAndrea Bittau kfree(opt->dccpop_val); 655afe00251SAndrea Bittau 656afe00251SAndrea Bittau if (opt->dccpop_sc != NULL) { 657afe00251SAndrea Bittau BUG_ON(opt->dccpop_sc->dccpoc_val == NULL); 658afe00251SAndrea Bittau kfree(opt->dccpop_sc->dccpoc_val); 659afe00251SAndrea Bittau kfree(opt->dccpop_sc); 660afe00251SAndrea Bittau } 661afe00251SAndrea Bittau 662afe00251SAndrea Bittau kfree(opt); 663afe00251SAndrea Bittau } 664a4bf3902SArnaldo Carvalho de Melo INIT_LIST_HEAD(&dmsk->dccpms_pending); 665afe00251SAndrea Bittau 666a4bf3902SArnaldo Carvalho de Melo list_for_each_entry_safe(opt, next, &dmsk->dccpms_conf, dccpop_node) { 667afe00251SAndrea Bittau BUG_ON(opt == NULL); 668afe00251SAndrea Bittau if (opt->dccpop_val != NULL) 669afe00251SAndrea Bittau kfree(opt->dccpop_val); 670afe00251SAndrea Bittau kfree(opt); 671afe00251SAndrea Bittau } 672a4bf3902SArnaldo Carvalho de Melo INIT_LIST_HEAD(&dmsk->dccpms_conf); 673afe00251SAndrea Bittau } 674afe00251SAndrea Bittau 675afe00251SAndrea Bittau EXPORT_SYMBOL_GPL(dccp_feat_clean); 676afe00251SAndrea Bittau 677afe00251SAndrea Bittau /* this is to be called only when a listening sock creates its child. It is 678afe00251SAndrea Bittau * assumed by the function---the confirm is not duplicated, but rather it is 679afe00251SAndrea Bittau * "passed on". 680afe00251SAndrea Bittau */ 681afe00251SAndrea Bittau int dccp_feat_clone(struct sock *oldsk, struct sock *newsk) 682afe00251SAndrea Bittau { 683a4bf3902SArnaldo Carvalho de Melo struct dccp_minisock *olddmsk = dccp_msk(oldsk); 684a4bf3902SArnaldo Carvalho de Melo struct dccp_minisock *newdmsk = dccp_msk(newsk); 685afe00251SAndrea Bittau struct dccp_opt_pend *opt; 686afe00251SAndrea Bittau int rc = 0; 687afe00251SAndrea Bittau 688a4bf3902SArnaldo Carvalho de Melo INIT_LIST_HEAD(&newdmsk->dccpms_pending); 689a4bf3902SArnaldo Carvalho de Melo INIT_LIST_HEAD(&newdmsk->dccpms_conf); 690afe00251SAndrea Bittau 691a4bf3902SArnaldo Carvalho de Melo list_for_each_entry(opt, &olddmsk->dccpms_pending, dccpop_node) { 692afe00251SAndrea Bittau struct dccp_opt_pend *newopt; 693afe00251SAndrea Bittau /* copy the value of the option */ 694eed73417SArnaldo Carvalho de Melo u8 *val = kmemdup(opt->dccpop_val, opt->dccpop_len, GFP_ATOMIC); 695afe00251SAndrea Bittau 696afe00251SAndrea Bittau if (val == NULL) 697afe00251SAndrea Bittau goto out_clean; 698afe00251SAndrea Bittau 699eed73417SArnaldo Carvalho de Melo newopt = kmemdup(opt, sizeof(*newopt), GFP_ATOMIC); 700afe00251SAndrea Bittau if (newopt == NULL) { 701afe00251SAndrea Bittau kfree(val); 702afe00251SAndrea Bittau goto out_clean; 703afe00251SAndrea Bittau } 704afe00251SAndrea Bittau 705afe00251SAndrea Bittau /* insert the option */ 706afe00251SAndrea Bittau newopt->dccpop_val = val; 707a4bf3902SArnaldo Carvalho de Melo list_add_tail(&newopt->dccpop_node, &newdmsk->dccpms_pending); 708afe00251SAndrea Bittau 709afe00251SAndrea Bittau /* XXX what happens with backlogs and multiple connections at 710afe00251SAndrea Bittau * once... 711afe00251SAndrea Bittau */ 712afe00251SAndrea Bittau /* the master socket no longer needs to worry about confirms */ 71368907dadSRandy Dunlap opt->dccpop_sc = NULL; /* it's not a memleak---new socket has it */ 714afe00251SAndrea Bittau 715afe00251SAndrea Bittau /* reset state for a new socket */ 716afe00251SAndrea Bittau opt->dccpop_conf = 0; 717afe00251SAndrea Bittau } 718afe00251SAndrea Bittau 719afe00251SAndrea Bittau /* XXX not doing anything about the conf queue */ 720afe00251SAndrea Bittau 721afe00251SAndrea Bittau out: 722afe00251SAndrea Bittau return rc; 723afe00251SAndrea Bittau 724afe00251SAndrea Bittau out_clean: 7258ca0d17bSArnaldo Carvalho de Melo dccp_feat_clean(newdmsk); 726afe00251SAndrea Bittau rc = -ENOMEM; 727afe00251SAndrea Bittau goto out; 728afe00251SAndrea Bittau } 729afe00251SAndrea Bittau 730afe00251SAndrea Bittau EXPORT_SYMBOL_GPL(dccp_feat_clone); 731afe00251SAndrea Bittau 7328ca0d17bSArnaldo Carvalho de Melo static int __dccp_feat_init(struct dccp_minisock *dmsk, u8 type, u8 feat, 7338ca0d17bSArnaldo Carvalho de Melo u8 *val, u8 len) 734afe00251SAndrea Bittau { 735afe00251SAndrea Bittau int rc = -ENOMEM; 736eed73417SArnaldo Carvalho de Melo u8 *copy = kmemdup(val, len, GFP_KERNEL); 737afe00251SAndrea Bittau 738afe00251SAndrea Bittau if (copy != NULL) { 7398ca0d17bSArnaldo Carvalho de Melo rc = dccp_feat_change(dmsk, type, feat, copy, len, GFP_KERNEL); 740afe00251SAndrea Bittau if (rc) 741afe00251SAndrea Bittau kfree(copy); 742afe00251SAndrea Bittau } 743afe00251SAndrea Bittau return rc; 744afe00251SAndrea Bittau } 745afe00251SAndrea Bittau 7468ca0d17bSArnaldo Carvalho de Melo int dccp_feat_init(struct dccp_minisock *dmsk) 747afe00251SAndrea Bittau { 748afe00251SAndrea Bittau int rc; 749afe00251SAndrea Bittau 750a4bf3902SArnaldo Carvalho de Melo INIT_LIST_HEAD(&dmsk->dccpms_pending); 751a4bf3902SArnaldo Carvalho de Melo INIT_LIST_HEAD(&dmsk->dccpms_conf); 752afe00251SAndrea Bittau 753afe00251SAndrea Bittau /* CCID L */ 7548ca0d17bSArnaldo Carvalho de Melo rc = __dccp_feat_init(dmsk, DCCPO_CHANGE_L, DCCPF_CCID, 755a4bf3902SArnaldo Carvalho de Melo &dmsk->dccpms_tx_ccid, 1); 756afe00251SAndrea Bittau if (rc) 757afe00251SAndrea Bittau goto out; 758afe00251SAndrea Bittau 759afe00251SAndrea Bittau /* CCID R */ 7608ca0d17bSArnaldo Carvalho de Melo rc = __dccp_feat_init(dmsk, DCCPO_CHANGE_R, DCCPF_CCID, 761a4bf3902SArnaldo Carvalho de Melo &dmsk->dccpms_rx_ccid, 1); 762afe00251SAndrea Bittau if (rc) 763afe00251SAndrea Bittau goto out; 764afe00251SAndrea Bittau 765afe00251SAndrea Bittau /* Ack ratio */ 7668ca0d17bSArnaldo Carvalho de Melo rc = __dccp_feat_init(dmsk, DCCPO_CHANGE_L, DCCPF_ACK_RATIO, 767a4bf3902SArnaldo Carvalho de Melo &dmsk->dccpms_ack_ratio, 1); 768afe00251SAndrea Bittau out: 769afe00251SAndrea Bittau return rc; 770afe00251SAndrea Bittau } 771afe00251SAndrea Bittau 772afe00251SAndrea Bittau EXPORT_SYMBOL_GPL(dccp_feat_init); 773c02fdc0eSGerrit Renker 774c02fdc0eSGerrit Renker #ifdef CONFIG_IP_DCCP_DEBUG 775c02fdc0eSGerrit Renker const char *dccp_feat_typename(const u8 type) 776c02fdc0eSGerrit Renker { 777c02fdc0eSGerrit Renker switch(type) { 778c02fdc0eSGerrit Renker case DCCPO_CHANGE_L: return("ChangeL"); 779c02fdc0eSGerrit Renker case DCCPO_CONFIRM_L: return("ConfirmL"); 780c02fdc0eSGerrit Renker case DCCPO_CHANGE_R: return("ChangeR"); 781c02fdc0eSGerrit Renker case DCCPO_CONFIRM_R: return("ConfirmR"); 782c02fdc0eSGerrit Renker /* the following case must not appear in feature negotation */ 783c02fdc0eSGerrit Renker default: dccp_pr_debug("unknown type %d [BUG!]\n", type); 784c02fdc0eSGerrit Renker } 785c02fdc0eSGerrit Renker return NULL; 786c02fdc0eSGerrit Renker } 787c02fdc0eSGerrit Renker 788c02fdc0eSGerrit Renker EXPORT_SYMBOL_GPL(dccp_feat_typename); 789c02fdc0eSGerrit Renker 790c02fdc0eSGerrit Renker const char *dccp_feat_name(const u8 feat) 791c02fdc0eSGerrit Renker { 792c02fdc0eSGerrit Renker static const char *feature_names[] = { 793c02fdc0eSGerrit Renker [DCCPF_RESERVED] = "Reserved", 794c02fdc0eSGerrit Renker [DCCPF_CCID] = "CCID", 795c02fdc0eSGerrit Renker [DCCPF_SHORT_SEQNOS] = "Allow Short Seqnos", 796c02fdc0eSGerrit Renker [DCCPF_SEQUENCE_WINDOW] = "Sequence Window", 797c02fdc0eSGerrit Renker [DCCPF_ECN_INCAPABLE] = "ECN Incapable", 798c02fdc0eSGerrit Renker [DCCPF_ACK_RATIO] = "Ack Ratio", 799c02fdc0eSGerrit Renker [DCCPF_SEND_ACK_VECTOR] = "Send ACK Vector", 800c02fdc0eSGerrit Renker [DCCPF_SEND_NDP_COUNT] = "Send NDP Count", 801c02fdc0eSGerrit Renker [DCCPF_MIN_CSUM_COVER] = "Min. Csum Coverage", 802c02fdc0eSGerrit Renker [DCCPF_DATA_CHECKSUM] = "Send Data Checksum", 803c02fdc0eSGerrit Renker }; 804dd6303dfSGerrit Renker if (feat > DCCPF_DATA_CHECKSUM && feat < DCCPF_MIN_CCID_SPECIFIC) 805dd6303dfSGerrit Renker return feature_names[DCCPF_RESERVED]; 806dd6303dfSGerrit Renker 8077d43d1a0SGerrit Renker if (feat == DCCPF_SEND_LEV_RATE) 8087d43d1a0SGerrit Renker return "Send Loss Event Rate"; 809c02fdc0eSGerrit Renker if (feat >= DCCPF_MIN_CCID_SPECIFIC) 810c02fdc0eSGerrit Renker return "CCID-specific"; 811c02fdc0eSGerrit Renker 812c02fdc0eSGerrit Renker return feature_names[feat]; 813c02fdc0eSGerrit Renker } 814c02fdc0eSGerrit Renker 815c02fdc0eSGerrit Renker EXPORT_SYMBOL_GPL(dccp_feat_name); 816c02fdc0eSGerrit Renker #endif /* CONFIG_IP_DCCP_DEBUG */ 817