diff options
Diffstat (limited to 'net/dccp/feat.c')
-rw-r--r-- | net/dccp/feat.c | 213 |
1 files changed, 203 insertions, 10 deletions
diff --git a/net/dccp/feat.c b/net/dccp/feat.c index bc00c038e4a5..b127189550f1 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c | |||
@@ -25,11 +25,101 @@ | |||
25 | 25 | ||
26 | #define DCCP_FEAT_SP_NOAGREE (-123) | 26 | #define DCCP_FEAT_SP_NOAGREE (-123) |
27 | 27 | ||
28 | /* | ||
29 | * Feature activation handlers. | ||
30 | * | ||
31 | * These all use an u64 argument, to provide enough room for NN/SP features. At | ||
32 | * this stage the negotiated values have been checked to be within their range. | ||
33 | */ | ||
34 | static int dccp_hdlr_ccid(struct sock *sk, u64 ccid, bool rx) | ||
35 | { | ||
36 | struct dccp_sock *dp = dccp_sk(sk); | ||
37 | struct ccid *new_ccid = ccid_new(ccid, sk, rx, gfp_any()); | ||
38 | |||
39 | if (new_ccid == NULL) | ||
40 | return -ENOMEM; | ||
41 | |||
42 | if (rx) { | ||
43 | ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk); | ||
44 | dp->dccps_hc_rx_ccid = new_ccid; | ||
45 | } else { | ||
46 | ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk); | ||
47 | dp->dccps_hc_tx_ccid = new_ccid; | ||
48 | } | ||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | static int dccp_hdlr_seq_win(struct sock *sk, u64 seq_win, bool rx) | ||
53 | { | ||
54 | if (!rx) | ||
55 | dccp_msk(sk)->dccpms_sequence_window = seq_win; | ||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | static int dccp_hdlr_ack_ratio(struct sock *sk, u64 ratio, bool rx) | ||
60 | { | ||
61 | if (rx) | ||
62 | dccp_sk(sk)->dccps_r_ack_ratio = ratio; | ||
63 | else | ||
64 | dccp_sk(sk)->dccps_l_ack_ratio = ratio; | ||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | static int dccp_hdlr_ackvec(struct sock *sk, u64 enable, bool rx) | ||
69 | { | ||
70 | struct dccp_sock *dp = dccp_sk(sk); | ||
71 | |||
72 | if (rx) { | ||
73 | if (enable && dp->dccps_hc_rx_ackvec == NULL) { | ||
74 | dp->dccps_hc_rx_ackvec = dccp_ackvec_alloc(gfp_any()); | ||
75 | if (dp->dccps_hc_rx_ackvec == NULL) | ||
76 | return -ENOMEM; | ||
77 | } else if (!enable) { | ||
78 | dccp_ackvec_free(dp->dccps_hc_rx_ackvec); | ||
79 | dp->dccps_hc_rx_ackvec = NULL; | ||
80 | } | ||
81 | } | ||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | static int dccp_hdlr_ndp(struct sock *sk, u64 enable, bool rx) | ||
86 | { | ||
87 | if (!rx) | ||
88 | dccp_msk(sk)->dccpms_send_ndp_count = (enable > 0); | ||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | /* | ||
93 | * Minimum Checksum Coverage is located at the RX side (9.2.1). This means that | ||
94 | * `rx' holds when the sending peer informs about his partial coverage via a | ||
95 | * ChangeR() option. In the other case, we are the sender and the receiver | ||
96 | * announces its coverage via ChangeL() options. The policy here is to honour | ||
97 | * such communication by enabling the corresponding partial coverage - but only | ||
98 | * if it has not been set manually before; the warning here means that all | ||
99 | * packets will be dropped. | ||
100 | */ | ||
101 | static int dccp_hdlr_min_cscov(struct sock *sk, u64 cscov, bool rx) | ||
102 | { | ||
103 | struct dccp_sock *dp = dccp_sk(sk); | ||
104 | |||
105 | if (rx) | ||
106 | dp->dccps_pcrlen = cscov; | ||
107 | else { | ||
108 | if (dp->dccps_pcslen == 0) | ||
109 | dp->dccps_pcslen = cscov; | ||
110 | else if (cscov > dp->dccps_pcslen) | ||
111 | DCCP_WARN("CsCov %u too small, peer requires >= %u\n", | ||
112 | dp->dccps_pcslen, (u8)cscov); | ||
113 | } | ||
114 | return 0; | ||
115 | } | ||
116 | |||
28 | static const struct { | 117 | static const struct { |
29 | u8 feat_num; /* DCCPF_xxx */ | 118 | u8 feat_num; /* DCCPF_xxx */ |
30 | enum dccp_feat_type rxtx; /* RX or TX */ | 119 | enum dccp_feat_type rxtx; /* RX or TX */ |
31 | enum dccp_feat_type reconciliation; /* SP or NN */ | 120 | enum dccp_feat_type reconciliation; /* SP or NN */ |
32 | u8 default_value; /* as in 6.4 */ | 121 | u8 default_value; /* as in 6.4 */ |
122 | int (*activation_hdlr)(struct sock *sk, u64 val, bool rx); | ||
33 | /* | 123 | /* |
34 | * Lookup table for location and type of features (from RFC 4340/4342) | 124 | * Lookup table for location and type of features (from RFC 4340/4342) |
35 | * +--------------------------+----+-----+----+----+---------+-----------+ | 125 | * +--------------------------+----+-----+----+----+---------+-----------+ |
@@ -49,16 +139,16 @@ static const struct { | |||
49 | * +--------------------------+----+-----+----+----+---------+-----------+ | 139 | * +--------------------------+----+-----+----+----+---------+-----------+ |
50 | */ | 140 | */ |
51 | } dccp_feat_table[] = { | 141 | } dccp_feat_table[] = { |
52 | { DCCPF_CCID, FEAT_AT_TX, FEAT_SP, 2 }, | 142 | { DCCPF_CCID, FEAT_AT_TX, FEAT_SP, 2, dccp_hdlr_ccid }, |
53 | { DCCPF_SHORT_SEQNOS, FEAT_AT_TX, FEAT_SP, 0 }, | 143 | { DCCPF_SHORT_SEQNOS, FEAT_AT_TX, FEAT_SP, 0, NULL }, |
54 | { DCCPF_SEQUENCE_WINDOW, FEAT_AT_TX, FEAT_NN, 100 }, | 144 | { DCCPF_SEQUENCE_WINDOW, FEAT_AT_TX, FEAT_NN, 100, dccp_hdlr_seq_win }, |
55 | { DCCPF_ECN_INCAPABLE, FEAT_AT_RX, FEAT_SP, 0 }, | 145 | { DCCPF_ECN_INCAPABLE, FEAT_AT_RX, FEAT_SP, 0, NULL }, |
56 | { DCCPF_ACK_RATIO, FEAT_AT_TX, FEAT_NN, 2 }, | 146 | { DCCPF_ACK_RATIO, FEAT_AT_TX, FEAT_NN, 2, dccp_hdlr_ack_ratio}, |
57 | { DCCPF_SEND_ACK_VECTOR, FEAT_AT_RX, FEAT_SP, 0 }, | 147 | { DCCPF_SEND_ACK_VECTOR, FEAT_AT_RX, FEAT_SP, 0, dccp_hdlr_ackvec }, |
58 | { DCCPF_SEND_NDP_COUNT, FEAT_AT_TX, FEAT_SP, 0 }, | 148 | { DCCPF_SEND_NDP_COUNT, FEAT_AT_TX, FEAT_SP, 0, dccp_hdlr_ndp }, |
59 | { DCCPF_MIN_CSUM_COVER, FEAT_AT_RX, FEAT_SP, 0 }, | 149 | { DCCPF_MIN_CSUM_COVER, FEAT_AT_RX, FEAT_SP, 0, dccp_hdlr_min_cscov}, |
60 | { DCCPF_DATA_CHECKSUM, FEAT_AT_RX, FEAT_SP, 0 }, | 150 | { DCCPF_DATA_CHECKSUM, FEAT_AT_RX, FEAT_SP, 0, NULL }, |
61 | { DCCPF_SEND_LEV_RATE, FEAT_AT_RX, FEAT_SP, 0 }, | 151 | { DCCPF_SEND_LEV_RATE, FEAT_AT_RX, FEAT_SP, 0, NULL }, |
62 | }; | 152 | }; |
63 | #define DCCP_FEAT_SUPPORTED_MAX ARRAY_SIZE(dccp_feat_table) | 153 | #define DCCP_FEAT_SUPPORTED_MAX ARRAY_SIZE(dccp_feat_table) |
64 | 154 | ||
@@ -104,6 +194,41 @@ static int dccp_feat_default_value(u8 feat_num) | |||
104 | return idx < 0 ? 0 : dccp_feat_table[idx].default_value; | 194 | return idx < 0 ? 0 : dccp_feat_table[idx].default_value; |
105 | } | 195 | } |
106 | 196 | ||
197 | static int __dccp_feat_activate(struct sock *sk, const int idx, | ||
198 | const bool is_local, dccp_feat_val const *fval) | ||
199 | { | ||
200 | bool rx; | ||
201 | u64 val; | ||
202 | |||
203 | if (idx < 0 || idx >= DCCP_FEAT_SUPPORTED_MAX) | ||
204 | return -1; | ||
205 | if (dccp_feat_table[idx].activation_hdlr == NULL) | ||
206 | return 0; | ||
207 | |||
208 | if (fval == NULL) { | ||
209 | val = dccp_feat_table[idx].default_value; | ||
210 | } else if (dccp_feat_table[idx].reconciliation == FEAT_SP) { | ||
211 | if (fval->sp.vec == NULL) { | ||
212 | /* | ||
213 | * This can happen when an empty Confirm is sent | ||
214 | * for an SP (i.e. known) feature. In this case | ||
215 | * we would be using the default anyway. | ||
216 | */ | ||
217 | DCCP_CRIT("Feature #%d undefined: using default", idx); | ||
218 | val = dccp_feat_table[idx].default_value; | ||
219 | } else { | ||
220 | val = fval->sp.vec[0]; | ||
221 | } | ||
222 | } else { | ||
223 | val = fval->nn; | ||
224 | } | ||
225 | |||
226 | /* Location is RX if this is a local-RX or remote-TX feature */ | ||
227 | rx = (is_local == (dccp_feat_table[idx].rxtx == FEAT_AT_RX)); | ||
228 | |||
229 | return dccp_feat_table[idx].activation_hdlr(sk, val, rx); | ||
230 | } | ||
231 | |||
107 | /* Test for "Req'd" feature (RFC 4340, 6.4) */ | 232 | /* Test for "Req'd" feature (RFC 4340, 6.4) */ |
108 | static inline int dccp_feat_must_be_understood(u8 feat_num) | 233 | static inline int dccp_feat_must_be_understood(u8 feat_num) |
109 | { | 234 | { |
@@ -1512,6 +1637,74 @@ out: | |||
1512 | 1637 | ||
1513 | EXPORT_SYMBOL_GPL(dccp_feat_init); | 1638 | EXPORT_SYMBOL_GPL(dccp_feat_init); |
1514 | 1639 | ||
1640 | int dccp_feat_activate_values(struct sock *sk, struct list_head *fn_list) | ||
1641 | { | ||
1642 | struct dccp_sock *dp = dccp_sk(sk); | ||
1643 | struct dccp_feat_entry *cur, *next; | ||
1644 | int idx; | ||
1645 | dccp_feat_val *fvals[DCCP_FEAT_SUPPORTED_MAX][2] = { | ||
1646 | [0 ... DCCP_FEAT_SUPPORTED_MAX-1] = { NULL, NULL } | ||
1647 | }; | ||
1648 | |||
1649 | list_for_each_entry(cur, fn_list, node) { | ||
1650 | /* | ||
1651 | * An empty Confirm means that either an unknown feature type | ||
1652 | * or an invalid value was present. In the first case there is | ||
1653 | * nothing to activate, in the other the default value is used. | ||
1654 | */ | ||
1655 | if (cur->empty_confirm) | ||
1656 | continue; | ||
1657 | |||
1658 | idx = dccp_feat_index(cur->feat_num); | ||
1659 | if (idx < 0) { | ||
1660 | DCCP_BUG("Unknown feature %u", cur->feat_num); | ||
1661 | goto activation_failed; | ||
1662 | } | ||
1663 | if (cur->state != FEAT_STABLE) { | ||
1664 | DCCP_CRIT("Negotiation of %s %u failed in state %u", | ||
1665 | cur->is_local ? "local" : "remote", | ||
1666 | cur->feat_num, cur->state); | ||
1667 | goto activation_failed; | ||
1668 | } | ||
1669 | fvals[idx][cur->is_local] = &cur->val; | ||
1670 | } | ||
1671 | |||
1672 | /* | ||
1673 | * Activate in decreasing order of index, so that the CCIDs are always | ||
1674 | * activated as the last feature. This avoids the case where a CCID | ||
1675 | * relies on the initialisation of one or more features that it depends | ||
1676 | * on (e.g. Send NDP Count, Send Ack Vector, and Ack Ratio features). | ||
1677 | */ | ||
1678 | for (idx = DCCP_FEAT_SUPPORTED_MAX; --idx >= 0;) | ||
1679 | if (__dccp_feat_activate(sk, idx, 0, fvals[idx][0]) || | ||
1680 | __dccp_feat_activate(sk, idx, 1, fvals[idx][1])) { | ||
1681 | DCCP_CRIT("Could not activate %d", idx); | ||
1682 | goto activation_failed; | ||
1683 | } | ||
1684 | |||
1685 | /* Clean up Change options which have been confirmed already */ | ||
1686 | list_for_each_entry_safe(cur, next, fn_list, node) | ||
1687 | if (!cur->needs_confirm) | ||
1688 | dccp_feat_list_pop(cur); | ||
1689 | |||
1690 | dccp_pr_debug("Activation OK\n"); | ||
1691 | return 0; | ||
1692 | |||
1693 | activation_failed: | ||
1694 | /* | ||
1695 | * We clean up everything that may have been allocated, since | ||
1696 | * it is difficult to track at which stage negotiation failed. | ||
1697 | * This is ok, since all allocation functions below are robust | ||
1698 | * against NULL arguments. | ||
1699 | */ | ||
1700 | ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk); | ||
1701 | ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk); | ||
1702 | dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL; | ||
1703 | dccp_ackvec_free(dp->dccps_hc_rx_ackvec); | ||
1704 | dp->dccps_hc_rx_ackvec = NULL; | ||
1705 | return -1; | ||
1706 | } | ||
1707 | |||
1515 | #ifdef CONFIG_IP_DCCP_DEBUG | 1708 | #ifdef CONFIG_IP_DCCP_DEBUG |
1516 | const char *dccp_feat_typename(const u8 type) | 1709 | const char *dccp_feat_typename(const u8 type) |
1517 | { | 1710 | { |