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 63*61e6473eSGerrit Renker /** 64*61e6473eSGerrit Renker * dccp_feat_index - Hash function to map feature number into array position 65*61e6473eSGerrit Renker * Returns consecutive array index or -1 if the feature is not understood. 66*61e6473eSGerrit Renker */ 67*61e6473eSGerrit Renker static int dccp_feat_index(u8 feat_num) 68*61e6473eSGerrit Renker { 69*61e6473eSGerrit Renker /* The first 9 entries are occupied by the types from RFC 4340, 6.4 */ 70*61e6473eSGerrit Renker if (feat_num > DCCPF_RESERVED && feat_num <= DCCPF_DATA_CHECKSUM) 71*61e6473eSGerrit Renker return feat_num - 1; 72*61e6473eSGerrit Renker 73*61e6473eSGerrit Renker /* 74*61e6473eSGerrit Renker * Other features: add cases for new feature types here after adding 75*61e6473eSGerrit Renker * them to the above table. 76*61e6473eSGerrit Renker */ 77*61e6473eSGerrit Renker switch (feat_num) { 78*61e6473eSGerrit Renker case DCCPF_SEND_LEV_RATE: 79*61e6473eSGerrit Renker return DCCP_FEAT_SUPPORTED_MAX - 1; 80*61e6473eSGerrit Renker } 81*61e6473eSGerrit Renker return -1; 82*61e6473eSGerrit Renker } 83*61e6473eSGerrit Renker 84*61e6473eSGerrit Renker static u8 dccp_feat_type(u8 feat_num) 85*61e6473eSGerrit Renker { 86*61e6473eSGerrit Renker int idx = dccp_feat_index(feat_num); 87*61e6473eSGerrit Renker 88*61e6473eSGerrit Renker if (idx < 0) 89*61e6473eSGerrit Renker return FEAT_UNKNOWN; 90*61e6473eSGerrit Renker return dccp_feat_table[idx].reconciliation; 91*61e6473eSGerrit Renker } 92*61e6473eSGerrit Renker 93*61e6473eSGerrit Renker static void dccp_feat_val_destructor(u8 feat_num, dccp_feat_val *val) 94*61e6473eSGerrit Renker { 95*61e6473eSGerrit Renker if (unlikely(val == NULL)) 96*61e6473eSGerrit Renker return; 97*61e6473eSGerrit Renker if (dccp_feat_type(feat_num) == FEAT_SP) 98*61e6473eSGerrit Renker kfree(val->sp.vec); 99*61e6473eSGerrit Renker memset(val, 0, sizeof(*val)); 100*61e6473eSGerrit Renker } 101*61e6473eSGerrit Renker 102*61e6473eSGerrit Renker static void dccp_feat_entry_destructor(struct dccp_feat_entry *entry) 103*61e6473eSGerrit Renker { 104*61e6473eSGerrit Renker if (entry != NULL) { 105*61e6473eSGerrit Renker dccp_feat_val_destructor(entry->feat_num, &entry->val); 106*61e6473eSGerrit Renker kfree(entry); 107*61e6473eSGerrit Renker } 108*61e6473eSGerrit Renker } 109*61e6473eSGerrit Renker 110*61e6473eSGerrit Renker /* 111*61e6473eSGerrit Renker * List management functions 112*61e6473eSGerrit Renker * 113*61e6473eSGerrit Renker * Feature negotiation lists rely on and maintain the following invariants: 114*61e6473eSGerrit Renker * - each feat_num in the list is known, i.e. we know its type and default value 115*61e6473eSGerrit Renker * - each feat_num/is_local combination is unique (old entries are overwritten) 116*61e6473eSGerrit Renker * - SP values are always freshly allocated 117*61e6473eSGerrit Renker * - list is sorted in increasing order of feature number (faster lookup) 118*61e6473eSGerrit Renker */ 119*61e6473eSGerrit Renker 120*61e6473eSGerrit Renker static inline void dccp_feat_list_pop(struct dccp_feat_entry *entry) 121*61e6473eSGerrit Renker { 122*61e6473eSGerrit Renker list_del(&entry->node); 123*61e6473eSGerrit Renker dccp_feat_entry_destructor(entry); 124*61e6473eSGerrit Renker } 125*61e6473eSGerrit Renker 126*61e6473eSGerrit Renker void dccp_feat_list_purge(struct list_head *fn_list) 127*61e6473eSGerrit Renker { 128*61e6473eSGerrit Renker struct dccp_feat_entry *entry, *next; 129*61e6473eSGerrit Renker 130*61e6473eSGerrit Renker list_for_each_entry_safe(entry, next, fn_list, node) 131*61e6473eSGerrit Renker dccp_feat_entry_destructor(entry); 132*61e6473eSGerrit Renker INIT_LIST_HEAD(fn_list); 133*61e6473eSGerrit Renker } 134*61e6473eSGerrit Renker EXPORT_SYMBOL_GPL(dccp_feat_list_purge); 135*61e6473eSGerrit Renker 1368ca0d17bSArnaldo Carvalho de Melo int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature, 1378ca0d17bSArnaldo Carvalho de Melo u8 *val, u8 len, gfp_t gfp) 138afe00251SAndrea Bittau { 139afe00251SAndrea Bittau struct dccp_opt_pend *opt; 140afe00251SAndrea Bittau 141c02fdc0eSGerrit Renker dccp_feat_debug(type, feature, *val); 142afe00251SAndrea Bittau 143dd6303dfSGerrit Renker if (len > 3) { 14459348b19SGerrit Renker DCCP_WARN("invalid length %d\n", len); 14519443178SChris Wright return -EINVAL; 146c02fdc0eSGerrit Renker } 147c02fdc0eSGerrit Renker /* XXX add further sanity checks */ 1486ffd30fbSAndrea Bittau 149afe00251SAndrea Bittau /* check if that feature is already being negotiated */ 150a4bf3902SArnaldo Carvalho de Melo list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) { 151afe00251SAndrea Bittau /* ok we found a negotiation for this option already */ 152afe00251SAndrea Bittau if (opt->dccpop_feat == feature && opt->dccpop_type == type) { 153afe00251SAndrea Bittau dccp_pr_debug("Replacing old\n"); 154afe00251SAndrea Bittau /* replace */ 155afe00251SAndrea Bittau BUG_ON(opt->dccpop_val == NULL); 156afe00251SAndrea Bittau kfree(opt->dccpop_val); 157afe00251SAndrea Bittau opt->dccpop_val = val; 158afe00251SAndrea Bittau opt->dccpop_len = len; 159afe00251SAndrea Bittau opt->dccpop_conf = 0; 160afe00251SAndrea Bittau return 0; 161afe00251SAndrea Bittau } 162afe00251SAndrea Bittau } 163afe00251SAndrea Bittau 164afe00251SAndrea Bittau /* negotiation for a new feature */ 165afe00251SAndrea Bittau opt = kmalloc(sizeof(*opt), gfp); 166afe00251SAndrea Bittau if (opt == NULL) 167afe00251SAndrea Bittau return -ENOMEM; 168afe00251SAndrea Bittau 169afe00251SAndrea Bittau opt->dccpop_type = type; 170afe00251SAndrea Bittau opt->dccpop_feat = feature; 171afe00251SAndrea Bittau opt->dccpop_len = len; 172afe00251SAndrea Bittau opt->dccpop_val = val; 173afe00251SAndrea Bittau opt->dccpop_conf = 0; 174afe00251SAndrea Bittau opt->dccpop_sc = NULL; 175afe00251SAndrea Bittau 176afe00251SAndrea Bittau BUG_ON(opt->dccpop_val == NULL); 177afe00251SAndrea Bittau 178a4bf3902SArnaldo Carvalho de Melo list_add_tail(&opt->dccpop_node, &dmsk->dccpms_pending); 179afe00251SAndrea Bittau return 0; 180afe00251SAndrea Bittau } 181afe00251SAndrea Bittau 182afe00251SAndrea Bittau EXPORT_SYMBOL_GPL(dccp_feat_change); 183afe00251SAndrea Bittau 1846ffd30fbSAndrea Bittau static int dccp_feat_update_ccid(struct sock *sk, u8 type, u8 new_ccid_nr) 1856ffd30fbSAndrea Bittau { 1866ffd30fbSAndrea Bittau struct dccp_sock *dp = dccp_sk(sk); 187a4bf3902SArnaldo Carvalho de Melo struct dccp_minisock *dmsk = dccp_msk(sk); 1886ffd30fbSAndrea Bittau /* figure out if we are changing our CCID or the peer's */ 1896ffd30fbSAndrea Bittau const int rx = type == DCCPO_CHANGE_R; 190a4bf3902SArnaldo Carvalho de Melo const u8 ccid_nr = rx ? dmsk->dccpms_rx_ccid : dmsk->dccpms_tx_ccid; 1916ffd30fbSAndrea Bittau struct ccid *new_ccid; 1926ffd30fbSAndrea Bittau 1936ffd30fbSAndrea Bittau /* Check if nothing is being changed. */ 1946ffd30fbSAndrea Bittau if (ccid_nr == new_ccid_nr) 1956ffd30fbSAndrea Bittau return 0; 1966ffd30fbSAndrea Bittau 1976ffd30fbSAndrea Bittau new_ccid = ccid_new(new_ccid_nr, sk, rx, GFP_ATOMIC); 1986ffd30fbSAndrea Bittau if (new_ccid == NULL) 1996ffd30fbSAndrea Bittau return -ENOMEM; 2006ffd30fbSAndrea Bittau 2016ffd30fbSAndrea Bittau if (rx) { 2026ffd30fbSAndrea Bittau ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk); 2036ffd30fbSAndrea Bittau dp->dccps_hc_rx_ccid = new_ccid; 204a4bf3902SArnaldo Carvalho de Melo dmsk->dccpms_rx_ccid = new_ccid_nr; 2056ffd30fbSAndrea Bittau } else { 2066ffd30fbSAndrea Bittau ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk); 2076ffd30fbSAndrea Bittau dp->dccps_hc_tx_ccid = new_ccid; 208a4bf3902SArnaldo Carvalho de Melo dmsk->dccpms_tx_ccid = new_ccid_nr; 2096ffd30fbSAndrea Bittau } 2106ffd30fbSAndrea Bittau 2116ffd30fbSAndrea Bittau return 0; 2126ffd30fbSAndrea Bittau } 2136ffd30fbSAndrea Bittau 214afe00251SAndrea Bittau static int dccp_feat_update(struct sock *sk, u8 type, u8 feat, u8 val) 215afe00251SAndrea Bittau { 216c02fdc0eSGerrit Renker dccp_feat_debug(type, feat, val); 2176ffd30fbSAndrea Bittau 2186ffd30fbSAndrea Bittau switch (feat) { 2196ffd30fbSAndrea Bittau case DCCPF_CCID: 2206ffd30fbSAndrea Bittau return dccp_feat_update_ccid(sk, type, val); 2216ffd30fbSAndrea Bittau default: 222c02fdc0eSGerrit Renker dccp_pr_debug("UNIMPLEMENTED: %s(%d, ...)\n", 223c02fdc0eSGerrit Renker dccp_feat_typename(type), feat); 2246ffd30fbSAndrea Bittau break; 2256ffd30fbSAndrea Bittau } 226afe00251SAndrea Bittau return 0; 227afe00251SAndrea Bittau } 228afe00251SAndrea Bittau 229afe00251SAndrea Bittau static int dccp_feat_reconcile(struct sock *sk, struct dccp_opt_pend *opt, 230afe00251SAndrea Bittau u8 *rpref, u8 rlen) 231afe00251SAndrea Bittau { 232afe00251SAndrea Bittau struct dccp_sock *dp = dccp_sk(sk); 233afe00251SAndrea Bittau u8 *spref, slen, *res = NULL; 234afe00251SAndrea Bittau int i, j, rc, agree = 1; 235afe00251SAndrea Bittau 236afe00251SAndrea Bittau BUG_ON(rpref == NULL); 237afe00251SAndrea Bittau 238afe00251SAndrea Bittau /* check if we are the black sheep */ 239afe00251SAndrea Bittau if (dp->dccps_role == DCCP_ROLE_CLIENT) { 240afe00251SAndrea Bittau spref = rpref; 241afe00251SAndrea Bittau slen = rlen; 242afe00251SAndrea Bittau rpref = opt->dccpop_val; 243afe00251SAndrea Bittau rlen = opt->dccpop_len; 244afe00251SAndrea Bittau } else { 245afe00251SAndrea Bittau spref = opt->dccpop_val; 246afe00251SAndrea Bittau slen = opt->dccpop_len; 247afe00251SAndrea Bittau } 248afe00251SAndrea Bittau /* 249afe00251SAndrea Bittau * Now we have server preference list in spref and client preference in 250afe00251SAndrea Bittau * rpref 251afe00251SAndrea Bittau */ 252afe00251SAndrea Bittau BUG_ON(spref == NULL); 253afe00251SAndrea Bittau BUG_ON(rpref == NULL); 254afe00251SAndrea Bittau 255afe00251SAndrea Bittau /* FIXME sanity check vals */ 256afe00251SAndrea Bittau 257afe00251SAndrea Bittau /* Are values in any order? XXX Lame "algorithm" here */ 258afe00251SAndrea Bittau for (i = 0; i < slen; i++) { 259afe00251SAndrea Bittau for (j = 0; j < rlen; j++) { 260afe00251SAndrea Bittau if (spref[i] == rpref[j]) { 261afe00251SAndrea Bittau res = &spref[i]; 262afe00251SAndrea Bittau break; 263afe00251SAndrea Bittau } 264afe00251SAndrea Bittau } 265afe00251SAndrea Bittau if (res) 266afe00251SAndrea Bittau break; 267afe00251SAndrea Bittau } 268afe00251SAndrea Bittau 269afe00251SAndrea Bittau /* we didn't agree on anything */ 270afe00251SAndrea Bittau if (res == NULL) { 271afe00251SAndrea Bittau /* confirm previous value */ 272afe00251SAndrea Bittau switch (opt->dccpop_feat) { 273afe00251SAndrea Bittau case DCCPF_CCID: 274afe00251SAndrea Bittau /* XXX did i get this right? =P */ 275afe00251SAndrea Bittau if (opt->dccpop_type == DCCPO_CHANGE_L) 276a4bf3902SArnaldo Carvalho de Melo res = &dccp_msk(sk)->dccpms_tx_ccid; 277afe00251SAndrea Bittau else 278a4bf3902SArnaldo Carvalho de Melo res = &dccp_msk(sk)->dccpms_rx_ccid; 279afe00251SAndrea Bittau break; 280afe00251SAndrea Bittau 281afe00251SAndrea Bittau default: 28259348b19SGerrit Renker DCCP_BUG("Fell through, feat=%d", opt->dccpop_feat); 28359348b19SGerrit Renker /* XXX implement res */ 284afe00251SAndrea Bittau return -EFAULT; 285afe00251SAndrea Bittau } 286afe00251SAndrea Bittau 287afe00251SAndrea Bittau dccp_pr_debug("Don't agree... reconfirming %d\n", *res); 288afe00251SAndrea Bittau agree = 0; /* this is used for mandatory options... */ 289afe00251SAndrea Bittau } 290afe00251SAndrea Bittau 291afe00251SAndrea Bittau /* need to put result and our preference list */ 292afe00251SAndrea Bittau rlen = 1 + opt->dccpop_len; 293afe00251SAndrea Bittau rpref = kmalloc(rlen, GFP_ATOMIC); 294afe00251SAndrea Bittau if (rpref == NULL) 295afe00251SAndrea Bittau return -ENOMEM; 296afe00251SAndrea Bittau 297afe00251SAndrea Bittau *rpref = *res; 298afe00251SAndrea Bittau memcpy(&rpref[1], opt->dccpop_val, opt->dccpop_len); 299afe00251SAndrea Bittau 300afe00251SAndrea Bittau /* put it in the "confirm queue" */ 301afe00251SAndrea Bittau if (opt->dccpop_sc == NULL) { 302afe00251SAndrea Bittau opt->dccpop_sc = kmalloc(sizeof(*opt->dccpop_sc), GFP_ATOMIC); 303afe00251SAndrea Bittau if (opt->dccpop_sc == NULL) { 304afe00251SAndrea Bittau kfree(rpref); 305afe00251SAndrea Bittau return -ENOMEM; 306afe00251SAndrea Bittau } 307afe00251SAndrea Bittau } else { 308afe00251SAndrea Bittau /* recycle the confirm slot */ 309afe00251SAndrea Bittau BUG_ON(opt->dccpop_sc->dccpoc_val == NULL); 310afe00251SAndrea Bittau kfree(opt->dccpop_sc->dccpoc_val); 311afe00251SAndrea Bittau dccp_pr_debug("recycling confirm slot\n"); 312afe00251SAndrea Bittau } 313afe00251SAndrea Bittau memset(opt->dccpop_sc, 0, sizeof(*opt->dccpop_sc)); 314afe00251SAndrea Bittau 315afe00251SAndrea Bittau opt->dccpop_sc->dccpoc_val = rpref; 316afe00251SAndrea Bittau opt->dccpop_sc->dccpoc_len = rlen; 317afe00251SAndrea Bittau 318afe00251SAndrea Bittau /* update the option on our side [we are about to send the confirm] */ 319afe00251SAndrea Bittau rc = dccp_feat_update(sk, opt->dccpop_type, opt->dccpop_feat, *res); 320afe00251SAndrea Bittau if (rc) { 321afe00251SAndrea Bittau kfree(opt->dccpop_sc->dccpoc_val); 322afe00251SAndrea Bittau kfree(opt->dccpop_sc); 32368907dadSRandy Dunlap opt->dccpop_sc = NULL; 324afe00251SAndrea Bittau return rc; 325afe00251SAndrea Bittau } 326afe00251SAndrea Bittau 327afe00251SAndrea Bittau dccp_pr_debug("Will confirm %d\n", *rpref); 328afe00251SAndrea Bittau 329afe00251SAndrea Bittau /* say we want to change to X but we just got a confirm X, suppress our 330afe00251SAndrea Bittau * change 331afe00251SAndrea Bittau */ 332afe00251SAndrea Bittau if (!opt->dccpop_conf) { 333afe00251SAndrea Bittau if (*opt->dccpop_val == *res) 334afe00251SAndrea Bittau opt->dccpop_conf = 1; 335afe00251SAndrea Bittau dccp_pr_debug("won't ask for change of same feature\n"); 336afe00251SAndrea Bittau } 337afe00251SAndrea Bittau 338afe00251SAndrea Bittau return agree ? 0 : DCCP_FEAT_SP_NOAGREE; /* used for mandatory opts */ 339afe00251SAndrea Bittau } 340afe00251SAndrea Bittau 341afe00251SAndrea Bittau static int dccp_feat_sp(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len) 342afe00251SAndrea Bittau { 343a4bf3902SArnaldo Carvalho de Melo struct dccp_minisock *dmsk = dccp_msk(sk); 344afe00251SAndrea Bittau struct dccp_opt_pend *opt; 345afe00251SAndrea Bittau int rc = 1; 346afe00251SAndrea Bittau u8 t; 347afe00251SAndrea Bittau 348afe00251SAndrea Bittau /* 349afe00251SAndrea Bittau * We received a CHANGE. We gotta match it against our own preference 350afe00251SAndrea Bittau * list. If we got a CHANGE_R it means it's a change for us, so we need 351afe00251SAndrea Bittau * to compare our CHANGE_L list. 352afe00251SAndrea Bittau */ 353afe00251SAndrea Bittau if (type == DCCPO_CHANGE_L) 354afe00251SAndrea Bittau t = DCCPO_CHANGE_R; 355afe00251SAndrea Bittau else 356afe00251SAndrea Bittau t = DCCPO_CHANGE_L; 357afe00251SAndrea Bittau 358afe00251SAndrea Bittau /* find our preference list for this feature */ 359a4bf3902SArnaldo Carvalho de Melo list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) { 360afe00251SAndrea Bittau if (opt->dccpop_type != t || opt->dccpop_feat != feature) 361afe00251SAndrea Bittau continue; 362afe00251SAndrea Bittau 363afe00251SAndrea Bittau /* find the winner from the two preference lists */ 364afe00251SAndrea Bittau rc = dccp_feat_reconcile(sk, opt, val, len); 365afe00251SAndrea Bittau break; 366afe00251SAndrea Bittau } 367afe00251SAndrea Bittau 368afe00251SAndrea Bittau /* We didn't deal with the change. This can happen if we have no 369afe00251SAndrea Bittau * preference list for the feature. In fact, it just shouldn't 370afe00251SAndrea Bittau * happen---if we understand a feature, we should have a preference list 371afe00251SAndrea Bittau * with at least the default value. 372afe00251SAndrea Bittau */ 373afe00251SAndrea Bittau BUG_ON(rc == 1); 374afe00251SAndrea Bittau 375afe00251SAndrea Bittau return rc; 376afe00251SAndrea Bittau } 377afe00251SAndrea Bittau 378afe00251SAndrea Bittau static int dccp_feat_nn(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len) 379afe00251SAndrea Bittau { 380afe00251SAndrea Bittau struct dccp_opt_pend *opt; 381a4bf3902SArnaldo Carvalho de Melo struct dccp_minisock *dmsk = dccp_msk(sk); 382afe00251SAndrea Bittau u8 *copy; 383afe00251SAndrea Bittau int rc; 384afe00251SAndrea Bittau 385c02fdc0eSGerrit Renker /* NN features must be Change L (sec. 6.3.2) */ 386c02fdc0eSGerrit Renker if (type != DCCPO_CHANGE_L) { 387c02fdc0eSGerrit Renker dccp_pr_debug("received %s for NN feature %d\n", 388c02fdc0eSGerrit Renker dccp_feat_typename(type), feature); 389afe00251SAndrea Bittau return -EFAULT; 390afe00251SAndrea Bittau } 391afe00251SAndrea Bittau 392afe00251SAndrea Bittau /* XXX sanity check opt val */ 393afe00251SAndrea Bittau 394afe00251SAndrea Bittau /* copy option so we can confirm it */ 395afe00251SAndrea Bittau opt = kzalloc(sizeof(*opt), GFP_ATOMIC); 396afe00251SAndrea Bittau if (opt == NULL) 397afe00251SAndrea Bittau return -ENOMEM; 398afe00251SAndrea Bittau 399eed73417SArnaldo Carvalho de Melo copy = kmemdup(val, len, GFP_ATOMIC); 400afe00251SAndrea Bittau if (copy == NULL) { 401afe00251SAndrea Bittau kfree(opt); 402afe00251SAndrea Bittau return -ENOMEM; 403afe00251SAndrea Bittau } 404afe00251SAndrea Bittau 405afe00251SAndrea Bittau opt->dccpop_type = DCCPO_CONFIRM_R; /* NN can only confirm R */ 406afe00251SAndrea Bittau opt->dccpop_feat = feature; 407afe00251SAndrea Bittau opt->dccpop_val = copy; 408afe00251SAndrea Bittau opt->dccpop_len = len; 409afe00251SAndrea Bittau 410afe00251SAndrea Bittau /* change feature */ 411afe00251SAndrea Bittau rc = dccp_feat_update(sk, type, feature, *val); 412afe00251SAndrea Bittau if (rc) { 413afe00251SAndrea Bittau kfree(opt->dccpop_val); 414afe00251SAndrea Bittau kfree(opt); 415afe00251SAndrea Bittau return rc; 416afe00251SAndrea Bittau } 417afe00251SAndrea Bittau 418c02fdc0eSGerrit Renker dccp_feat_debug(type, feature, *copy); 419c02fdc0eSGerrit Renker 420a4bf3902SArnaldo Carvalho de Melo list_add_tail(&opt->dccpop_node, &dmsk->dccpms_conf); 421afe00251SAndrea Bittau 422afe00251SAndrea Bittau return 0; 423afe00251SAndrea Bittau } 424afe00251SAndrea Bittau 4258ca0d17bSArnaldo Carvalho de Melo static void dccp_feat_empty_confirm(struct dccp_minisock *dmsk, 4268ca0d17bSArnaldo Carvalho de Melo u8 type, u8 feature) 427afe00251SAndrea Bittau { 428afe00251SAndrea Bittau /* XXX check if other confirms for that are queued and recycle slot */ 429afe00251SAndrea Bittau struct dccp_opt_pend *opt = kzalloc(sizeof(*opt), GFP_ATOMIC); 430afe00251SAndrea Bittau 431afe00251SAndrea Bittau if (opt == NULL) { 432afe00251SAndrea Bittau /* XXX what do we do? Ignoring should be fine. It's a change 433afe00251SAndrea Bittau * after all =P 434afe00251SAndrea Bittau */ 435afe00251SAndrea Bittau return; 436afe00251SAndrea Bittau } 437afe00251SAndrea Bittau 438c02fdc0eSGerrit Renker switch (type) { 439e576de82SJesper Juhl case DCCPO_CHANGE_L: 440e576de82SJesper Juhl opt->dccpop_type = DCCPO_CONFIRM_R; 441e576de82SJesper Juhl break; 442e576de82SJesper Juhl case DCCPO_CHANGE_R: 443e576de82SJesper Juhl opt->dccpop_type = DCCPO_CONFIRM_L; 444e576de82SJesper Juhl break; 445e576de82SJesper Juhl default: 446e576de82SJesper Juhl DCCP_WARN("invalid type %d\n", type); 447e576de82SJesper Juhl kfree(opt); 448e576de82SJesper Juhl return; 449c02fdc0eSGerrit Renker } 450afe00251SAndrea Bittau opt->dccpop_feat = feature; 45168907dadSRandy Dunlap opt->dccpop_val = NULL; 452afe00251SAndrea Bittau opt->dccpop_len = 0; 453afe00251SAndrea Bittau 454afe00251SAndrea Bittau /* change feature */ 455c02fdc0eSGerrit Renker dccp_pr_debug("Empty %s(%d)\n", dccp_feat_typename(type), feature); 456c02fdc0eSGerrit Renker 457a4bf3902SArnaldo Carvalho de Melo list_add_tail(&opt->dccpop_node, &dmsk->dccpms_conf); 458afe00251SAndrea Bittau } 459afe00251SAndrea Bittau 460afe00251SAndrea Bittau static void dccp_feat_flush_confirm(struct sock *sk) 461afe00251SAndrea Bittau { 462a4bf3902SArnaldo Carvalho de Melo struct dccp_minisock *dmsk = dccp_msk(sk); 463afe00251SAndrea Bittau /* Check if there is anything to confirm in the first place */ 464a4bf3902SArnaldo Carvalho de Melo int yes = !list_empty(&dmsk->dccpms_conf); 465afe00251SAndrea Bittau 466afe00251SAndrea Bittau if (!yes) { 467afe00251SAndrea Bittau struct dccp_opt_pend *opt; 468afe00251SAndrea Bittau 469a4bf3902SArnaldo Carvalho de Melo list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) { 470afe00251SAndrea Bittau if (opt->dccpop_conf) { 471afe00251SAndrea Bittau yes = 1; 472afe00251SAndrea Bittau break; 473afe00251SAndrea Bittau } 474afe00251SAndrea Bittau } 475afe00251SAndrea Bittau } 476afe00251SAndrea Bittau 477afe00251SAndrea Bittau if (!yes) 478afe00251SAndrea Bittau return; 479afe00251SAndrea Bittau 480afe00251SAndrea Bittau /* OK there is something to confirm... */ 481afe00251SAndrea Bittau /* XXX check if packet is in flight? Send delayed ack?? */ 482afe00251SAndrea Bittau if (sk->sk_state == DCCP_OPEN) 483afe00251SAndrea Bittau dccp_send_ack(sk); 484afe00251SAndrea Bittau } 485afe00251SAndrea Bittau 486afe00251SAndrea Bittau int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len) 487afe00251SAndrea Bittau { 488afe00251SAndrea Bittau int rc; 489afe00251SAndrea Bittau 490c02fdc0eSGerrit Renker dccp_feat_debug(type, feature, *val); 491afe00251SAndrea Bittau 492afe00251SAndrea Bittau /* figure out if it's SP or NN feature */ 493afe00251SAndrea Bittau switch (feature) { 494afe00251SAndrea Bittau /* deal with SP features */ 495afe00251SAndrea Bittau case DCCPF_CCID: 496afe00251SAndrea Bittau rc = dccp_feat_sp(sk, type, feature, val, len); 497afe00251SAndrea Bittau break; 498afe00251SAndrea Bittau 499afe00251SAndrea Bittau /* deal with NN features */ 500afe00251SAndrea Bittau case DCCPF_ACK_RATIO: 501afe00251SAndrea Bittau rc = dccp_feat_nn(sk, type, feature, val, len); 502afe00251SAndrea Bittau break; 503afe00251SAndrea Bittau 504afe00251SAndrea Bittau /* XXX implement other features */ 505afe00251SAndrea Bittau default: 506c02fdc0eSGerrit Renker dccp_pr_debug("UNIMPLEMENTED: not handling %s(%d, ...)\n", 507c02fdc0eSGerrit Renker dccp_feat_typename(type), feature); 508afe00251SAndrea Bittau rc = -EFAULT; 509afe00251SAndrea Bittau break; 510afe00251SAndrea Bittau } 511afe00251SAndrea Bittau 512afe00251SAndrea Bittau /* check if there were problems changing features */ 513afe00251SAndrea Bittau if (rc) { 514afe00251SAndrea Bittau /* If we don't agree on SP, we sent a confirm for old value. 515afe00251SAndrea Bittau * However we propagate rc to caller in case option was 516afe00251SAndrea Bittau * mandatory 517afe00251SAndrea Bittau */ 518afe00251SAndrea Bittau if (rc != DCCP_FEAT_SP_NOAGREE) 5198ca0d17bSArnaldo Carvalho de Melo dccp_feat_empty_confirm(dccp_msk(sk), type, feature); 520afe00251SAndrea Bittau } 521afe00251SAndrea Bittau 522afe00251SAndrea Bittau /* generate the confirm [if required] */ 523afe00251SAndrea Bittau dccp_feat_flush_confirm(sk); 524afe00251SAndrea Bittau 525afe00251SAndrea Bittau return rc; 526afe00251SAndrea Bittau } 527afe00251SAndrea Bittau 528afe00251SAndrea Bittau EXPORT_SYMBOL_GPL(dccp_feat_change_recv); 529afe00251SAndrea Bittau 530afe00251SAndrea Bittau int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, 531afe00251SAndrea Bittau u8 *val, u8 len) 532afe00251SAndrea Bittau { 533afe00251SAndrea Bittau u8 t; 534afe00251SAndrea Bittau struct dccp_opt_pend *opt; 535a4bf3902SArnaldo Carvalho de Melo struct dccp_minisock *dmsk = dccp_msk(sk); 536c02fdc0eSGerrit Renker int found = 0; 537afe00251SAndrea Bittau int all_confirmed = 1; 538afe00251SAndrea Bittau 539c02fdc0eSGerrit Renker dccp_feat_debug(type, feature, *val); 540afe00251SAndrea Bittau 541afe00251SAndrea Bittau /* locate our change request */ 542c02fdc0eSGerrit Renker switch (type) { 543c02fdc0eSGerrit Renker case DCCPO_CONFIRM_L: t = DCCPO_CHANGE_R; break; 544c02fdc0eSGerrit Renker case DCCPO_CONFIRM_R: t = DCCPO_CHANGE_L; break; 54559348b19SGerrit Renker default: DCCP_WARN("invalid type %d\n", type); 546c02fdc0eSGerrit Renker return 1; 547c02fdc0eSGerrit Renker 548c02fdc0eSGerrit Renker } 549c02fdc0eSGerrit Renker /* XXX sanity check feature value */ 550afe00251SAndrea Bittau 551a4bf3902SArnaldo Carvalho de Melo list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) { 552afe00251SAndrea Bittau if (!opt->dccpop_conf && opt->dccpop_type == t && 553afe00251SAndrea Bittau opt->dccpop_feat == feature) { 554c02fdc0eSGerrit Renker found = 1; 555c02fdc0eSGerrit Renker dccp_pr_debug("feature %d found\n", opt->dccpop_feat); 556c02fdc0eSGerrit Renker 557afe00251SAndrea Bittau /* XXX do sanity check */ 558afe00251SAndrea Bittau 559afe00251SAndrea Bittau opt->dccpop_conf = 1; 560afe00251SAndrea Bittau 561afe00251SAndrea Bittau /* We got a confirmation---change the option */ 562afe00251SAndrea Bittau dccp_feat_update(sk, opt->dccpop_type, 563afe00251SAndrea Bittau opt->dccpop_feat, *val); 564afe00251SAndrea Bittau 565c02fdc0eSGerrit Renker /* XXX check the return value of dccp_feat_update */ 566afe00251SAndrea Bittau break; 567afe00251SAndrea Bittau } 568afe00251SAndrea Bittau 569afe00251SAndrea Bittau if (!opt->dccpop_conf) 570afe00251SAndrea Bittau all_confirmed = 0; 571afe00251SAndrea Bittau } 572afe00251SAndrea Bittau 573afe00251SAndrea Bittau /* fix re-transmit timer */ 574afe00251SAndrea Bittau /* XXX gotta make sure that no option negotiation occurs during 575afe00251SAndrea Bittau * connection shutdown. Consider that the CLOSEREQ is sent and timer is 576afe00251SAndrea Bittau * on. if all options are confirmed it might kill timer which should 577afe00251SAndrea Bittau * remain alive until close is received. 578afe00251SAndrea Bittau */ 579afe00251SAndrea Bittau if (all_confirmed) { 580afe00251SAndrea Bittau dccp_pr_debug("clear feat negotiation timer %p\n", sk); 581afe00251SAndrea Bittau inet_csk_clear_xmit_timer(sk, ICSK_TIME_RETRANS); 582afe00251SAndrea Bittau } 583afe00251SAndrea Bittau 584c02fdc0eSGerrit Renker if (!found) 585c02fdc0eSGerrit Renker dccp_pr_debug("%s(%d, ...) never requested\n", 586c02fdc0eSGerrit Renker dccp_feat_typename(type), feature); 587afe00251SAndrea Bittau return 0; 588afe00251SAndrea Bittau } 589afe00251SAndrea Bittau 590afe00251SAndrea Bittau EXPORT_SYMBOL_GPL(dccp_feat_confirm_recv); 591afe00251SAndrea Bittau 5928ca0d17bSArnaldo Carvalho de Melo void dccp_feat_clean(struct dccp_minisock *dmsk) 593afe00251SAndrea Bittau { 594afe00251SAndrea Bittau struct dccp_opt_pend *opt, *next; 595afe00251SAndrea Bittau 596a4bf3902SArnaldo Carvalho de Melo list_for_each_entry_safe(opt, next, &dmsk->dccpms_pending, 597afe00251SAndrea Bittau dccpop_node) { 598afe00251SAndrea Bittau BUG_ON(opt->dccpop_val == NULL); 599afe00251SAndrea Bittau kfree(opt->dccpop_val); 600afe00251SAndrea Bittau 601afe00251SAndrea Bittau if (opt->dccpop_sc != NULL) { 602afe00251SAndrea Bittau BUG_ON(opt->dccpop_sc->dccpoc_val == NULL); 603afe00251SAndrea Bittau kfree(opt->dccpop_sc->dccpoc_val); 604afe00251SAndrea Bittau kfree(opt->dccpop_sc); 605afe00251SAndrea Bittau } 606afe00251SAndrea Bittau 607afe00251SAndrea Bittau kfree(opt); 608afe00251SAndrea Bittau } 609a4bf3902SArnaldo Carvalho de Melo INIT_LIST_HEAD(&dmsk->dccpms_pending); 610afe00251SAndrea Bittau 611a4bf3902SArnaldo Carvalho de Melo list_for_each_entry_safe(opt, next, &dmsk->dccpms_conf, dccpop_node) { 612afe00251SAndrea Bittau BUG_ON(opt == NULL); 613afe00251SAndrea Bittau if (opt->dccpop_val != NULL) 614afe00251SAndrea Bittau kfree(opt->dccpop_val); 615afe00251SAndrea Bittau kfree(opt); 616afe00251SAndrea Bittau } 617a4bf3902SArnaldo Carvalho de Melo INIT_LIST_HEAD(&dmsk->dccpms_conf); 618afe00251SAndrea Bittau } 619afe00251SAndrea Bittau 620afe00251SAndrea Bittau EXPORT_SYMBOL_GPL(dccp_feat_clean); 621afe00251SAndrea Bittau 622afe00251SAndrea Bittau /* this is to be called only when a listening sock creates its child. It is 623afe00251SAndrea Bittau * assumed by the function---the confirm is not duplicated, but rather it is 624afe00251SAndrea Bittau * "passed on". 625afe00251SAndrea Bittau */ 626afe00251SAndrea Bittau int dccp_feat_clone(struct sock *oldsk, struct sock *newsk) 627afe00251SAndrea Bittau { 628a4bf3902SArnaldo Carvalho de Melo struct dccp_minisock *olddmsk = dccp_msk(oldsk); 629a4bf3902SArnaldo Carvalho de Melo struct dccp_minisock *newdmsk = dccp_msk(newsk); 630afe00251SAndrea Bittau struct dccp_opt_pend *opt; 631afe00251SAndrea Bittau int rc = 0; 632afe00251SAndrea Bittau 633a4bf3902SArnaldo Carvalho de Melo INIT_LIST_HEAD(&newdmsk->dccpms_pending); 634a4bf3902SArnaldo Carvalho de Melo INIT_LIST_HEAD(&newdmsk->dccpms_conf); 635afe00251SAndrea Bittau 636a4bf3902SArnaldo Carvalho de Melo list_for_each_entry(opt, &olddmsk->dccpms_pending, dccpop_node) { 637afe00251SAndrea Bittau struct dccp_opt_pend *newopt; 638afe00251SAndrea Bittau /* copy the value of the option */ 639eed73417SArnaldo Carvalho de Melo u8 *val = kmemdup(opt->dccpop_val, opt->dccpop_len, GFP_ATOMIC); 640afe00251SAndrea Bittau 641afe00251SAndrea Bittau if (val == NULL) 642afe00251SAndrea Bittau goto out_clean; 643afe00251SAndrea Bittau 644eed73417SArnaldo Carvalho de Melo newopt = kmemdup(opt, sizeof(*newopt), GFP_ATOMIC); 645afe00251SAndrea Bittau if (newopt == NULL) { 646afe00251SAndrea Bittau kfree(val); 647afe00251SAndrea Bittau goto out_clean; 648afe00251SAndrea Bittau } 649afe00251SAndrea Bittau 650afe00251SAndrea Bittau /* insert the option */ 651afe00251SAndrea Bittau newopt->dccpop_val = val; 652a4bf3902SArnaldo Carvalho de Melo list_add_tail(&newopt->dccpop_node, &newdmsk->dccpms_pending); 653afe00251SAndrea Bittau 654afe00251SAndrea Bittau /* XXX what happens with backlogs and multiple connections at 655afe00251SAndrea Bittau * once... 656afe00251SAndrea Bittau */ 657afe00251SAndrea Bittau /* the master socket no longer needs to worry about confirms */ 65868907dadSRandy Dunlap opt->dccpop_sc = NULL; /* it's not a memleak---new socket has it */ 659afe00251SAndrea Bittau 660afe00251SAndrea Bittau /* reset state for a new socket */ 661afe00251SAndrea Bittau opt->dccpop_conf = 0; 662afe00251SAndrea Bittau } 663afe00251SAndrea Bittau 664afe00251SAndrea Bittau /* XXX not doing anything about the conf queue */ 665afe00251SAndrea Bittau 666afe00251SAndrea Bittau out: 667afe00251SAndrea Bittau return rc; 668afe00251SAndrea Bittau 669afe00251SAndrea Bittau out_clean: 6708ca0d17bSArnaldo Carvalho de Melo dccp_feat_clean(newdmsk); 671afe00251SAndrea Bittau rc = -ENOMEM; 672afe00251SAndrea Bittau goto out; 673afe00251SAndrea Bittau } 674afe00251SAndrea Bittau 675afe00251SAndrea Bittau EXPORT_SYMBOL_GPL(dccp_feat_clone); 676afe00251SAndrea Bittau 6778ca0d17bSArnaldo Carvalho de Melo static int __dccp_feat_init(struct dccp_minisock *dmsk, u8 type, u8 feat, 6788ca0d17bSArnaldo Carvalho de Melo u8 *val, u8 len) 679afe00251SAndrea Bittau { 680afe00251SAndrea Bittau int rc = -ENOMEM; 681eed73417SArnaldo Carvalho de Melo u8 *copy = kmemdup(val, len, GFP_KERNEL); 682afe00251SAndrea Bittau 683afe00251SAndrea Bittau if (copy != NULL) { 6848ca0d17bSArnaldo Carvalho de Melo rc = dccp_feat_change(dmsk, type, feat, copy, len, GFP_KERNEL); 685afe00251SAndrea Bittau if (rc) 686afe00251SAndrea Bittau kfree(copy); 687afe00251SAndrea Bittau } 688afe00251SAndrea Bittau return rc; 689afe00251SAndrea Bittau } 690afe00251SAndrea Bittau 6918ca0d17bSArnaldo Carvalho de Melo int dccp_feat_init(struct dccp_minisock *dmsk) 692afe00251SAndrea Bittau { 693afe00251SAndrea Bittau int rc; 694afe00251SAndrea Bittau 695a4bf3902SArnaldo Carvalho de Melo INIT_LIST_HEAD(&dmsk->dccpms_pending); 696a4bf3902SArnaldo Carvalho de Melo INIT_LIST_HEAD(&dmsk->dccpms_conf); 697afe00251SAndrea Bittau 698afe00251SAndrea Bittau /* CCID L */ 6998ca0d17bSArnaldo Carvalho de Melo rc = __dccp_feat_init(dmsk, DCCPO_CHANGE_L, DCCPF_CCID, 700a4bf3902SArnaldo Carvalho de Melo &dmsk->dccpms_tx_ccid, 1); 701afe00251SAndrea Bittau if (rc) 702afe00251SAndrea Bittau goto out; 703afe00251SAndrea Bittau 704afe00251SAndrea Bittau /* CCID R */ 7058ca0d17bSArnaldo Carvalho de Melo rc = __dccp_feat_init(dmsk, DCCPO_CHANGE_R, DCCPF_CCID, 706a4bf3902SArnaldo Carvalho de Melo &dmsk->dccpms_rx_ccid, 1); 707afe00251SAndrea Bittau if (rc) 708afe00251SAndrea Bittau goto out; 709afe00251SAndrea Bittau 710afe00251SAndrea Bittau /* Ack ratio */ 7118ca0d17bSArnaldo Carvalho de Melo rc = __dccp_feat_init(dmsk, DCCPO_CHANGE_L, DCCPF_ACK_RATIO, 712a4bf3902SArnaldo Carvalho de Melo &dmsk->dccpms_ack_ratio, 1); 713afe00251SAndrea Bittau out: 714afe00251SAndrea Bittau return rc; 715afe00251SAndrea Bittau } 716afe00251SAndrea Bittau 717afe00251SAndrea Bittau EXPORT_SYMBOL_GPL(dccp_feat_init); 718c02fdc0eSGerrit Renker 719c02fdc0eSGerrit Renker #ifdef CONFIG_IP_DCCP_DEBUG 720c02fdc0eSGerrit Renker const char *dccp_feat_typename(const u8 type) 721c02fdc0eSGerrit Renker { 722c02fdc0eSGerrit Renker switch(type) { 723c02fdc0eSGerrit Renker case DCCPO_CHANGE_L: return("ChangeL"); 724c02fdc0eSGerrit Renker case DCCPO_CONFIRM_L: return("ConfirmL"); 725c02fdc0eSGerrit Renker case DCCPO_CHANGE_R: return("ChangeR"); 726c02fdc0eSGerrit Renker case DCCPO_CONFIRM_R: return("ConfirmR"); 727c02fdc0eSGerrit Renker /* the following case must not appear in feature negotation */ 728c02fdc0eSGerrit Renker default: dccp_pr_debug("unknown type %d [BUG!]\n", type); 729c02fdc0eSGerrit Renker } 730c02fdc0eSGerrit Renker return NULL; 731c02fdc0eSGerrit Renker } 732c02fdc0eSGerrit Renker 733c02fdc0eSGerrit Renker EXPORT_SYMBOL_GPL(dccp_feat_typename); 734c02fdc0eSGerrit Renker 735c02fdc0eSGerrit Renker const char *dccp_feat_name(const u8 feat) 736c02fdc0eSGerrit Renker { 737c02fdc0eSGerrit Renker static const char *feature_names[] = { 738c02fdc0eSGerrit Renker [DCCPF_RESERVED] = "Reserved", 739c02fdc0eSGerrit Renker [DCCPF_CCID] = "CCID", 740c02fdc0eSGerrit Renker [DCCPF_SHORT_SEQNOS] = "Allow Short Seqnos", 741c02fdc0eSGerrit Renker [DCCPF_SEQUENCE_WINDOW] = "Sequence Window", 742c02fdc0eSGerrit Renker [DCCPF_ECN_INCAPABLE] = "ECN Incapable", 743c02fdc0eSGerrit Renker [DCCPF_ACK_RATIO] = "Ack Ratio", 744c02fdc0eSGerrit Renker [DCCPF_SEND_ACK_VECTOR] = "Send ACK Vector", 745c02fdc0eSGerrit Renker [DCCPF_SEND_NDP_COUNT] = "Send NDP Count", 746c02fdc0eSGerrit Renker [DCCPF_MIN_CSUM_COVER] = "Min. Csum Coverage", 747c02fdc0eSGerrit Renker [DCCPF_DATA_CHECKSUM] = "Send Data Checksum", 748c02fdc0eSGerrit Renker }; 749dd6303dfSGerrit Renker if (feat > DCCPF_DATA_CHECKSUM && feat < DCCPF_MIN_CCID_SPECIFIC) 750dd6303dfSGerrit Renker return feature_names[DCCPF_RESERVED]; 751dd6303dfSGerrit Renker 7527d43d1a0SGerrit Renker if (feat == DCCPF_SEND_LEV_RATE) 7537d43d1a0SGerrit Renker return "Send Loss Event Rate"; 754c02fdc0eSGerrit Renker if (feat >= DCCPF_MIN_CCID_SPECIFIC) 755c02fdc0eSGerrit Renker return "CCID-specific"; 756c02fdc0eSGerrit Renker 757c02fdc0eSGerrit Renker return feature_names[feat]; 758c02fdc0eSGerrit Renker } 759c02fdc0eSGerrit Renker 760c02fdc0eSGerrit Renker EXPORT_SYMBOL_GPL(dccp_feat_name); 761c02fdc0eSGerrit Renker #endif /* CONFIG_IP_DCCP_DEBUG */ 762