// SPDX-License-Identifier: GPL-2.0-or-later /* * net/dccp/feat.c * * Feature negotiation for the DCCP protocol (RFC 4340, section 6) * * Copyright (c) 2008 Gerrit Renker <[email protected]> * Rewrote from scratch, some bits from earlier code by * Copyright (c) 2005 Andrea Bittau <[email protected]> * * ASSUMPTIONS * ----------- * o Feature negotiation is coordinated with connection setup (as in TCP), wild * changes of parameters of an established connection are not supported. * o Changing non-negotiable (NN) values is supported in state OPEN/PARTOPEN. * 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. */ #include <linux/module.h> #include <linux/slab.h> #include "ccid.h" #include "feat.h" /* feature-specific sysctls - initialised to the defaults from RFC 4340, 6.4 */ unsigned long sysctl_dccp_sequence_window __read_mostly = …; int sysctl_dccp_rx_ccid __read_mostly = …, sysctl_dccp_tx_ccid __read_mostly = …; /* * Feature activation handlers. * * These all use an u64 argument, to provide enough room for NN/SP features. At * this stage the negotiated values have been checked to be within their range. */ static int dccp_hdlr_ccid(struct sock *sk, u64 ccid, bool rx) { … } static int dccp_hdlr_seq_win(struct sock *sk, u64 seq_win, bool rx) { … } static int dccp_hdlr_ack_ratio(struct sock *sk, u64 ratio, bool rx) { … } static int dccp_hdlr_ackvec(struct sock *sk, u64 enable, bool rx) { … } static int dccp_hdlr_ndp(struct sock *sk, u64 enable, bool rx) { … } /* * Minimum Checksum Coverage is located at the RX side (9.2.1). This means that * `rx' holds when the sending peer informs about his partial coverage via a * ChangeR() option. In the other case, we are the sender and the receiver * announces its coverage via ChangeL() options. The policy here is to honour * such communication by enabling the corresponding partial coverage - but only * if it has not been set manually before; the warning here means that all * packets will be dropped. */ static int dccp_hdlr_min_cscov(struct sock *sk, u64 cscov, bool rx) { … } static const struct { … } dccp_feat_table[] = …; #define DCCP_FEAT_SUPPORTED_MAX … /** * dccp_feat_index - Hash function to map feature number into array position * @feat_num: feature to hash, one of %dccp_feature_numbers * * Returns consecutive array index or -1 if the feature is not understood. */ static int dccp_feat_index(u8 feat_num) { … } static u8 dccp_feat_type(u8 feat_num) { … } static int dccp_feat_default_value(u8 feat_num) { … } /* * Debugging and verbose-printing section */ static const char *dccp_feat_fname(const u8 feat) { … } static const char *const dccp_feat_sname[] = …; #ifdef CONFIG_IP_DCCP_DEBUG static const char *dccp_feat_oname(const u8 opt) { … } static void dccp_feat_printval(u8 feat_num, dccp_feat_val const *val) { … } static void dccp_feat_printvals(u8 feat_num, u8 *list, u8 len) { … } static void dccp_feat_print_entry(struct dccp_feat_entry const *entry) { … } #define dccp_feat_print_opt(opt, feat, val, len, mandatory) … #define dccp_feat_print_fnlist(fn_list) … #else /* ! CONFIG_IP_DCCP_DEBUG */ #define dccp_feat_print_opt … #define dccp_feat_print_fnlist … #endif static int __dccp_feat_activate(struct sock *sk, const int idx, const bool is_local, dccp_feat_val const *fval) { … } /** * dccp_feat_activate - Activate feature value on socket * @sk: fully connected DCCP socket (after handshake is complete) * @feat_num: feature to activate, one of %dccp_feature_numbers * @local: whether local (1) or remote (0) @feat_num is meant * @fval: the value (SP or NN) to activate, or NULL to use the default value * * For general use this function is preferable over __dccp_feat_activate(). */ static int dccp_feat_activate(struct sock *sk, u8 feat_num, bool local, dccp_feat_val const *fval) { … } /* Test for "Req'd" feature (RFC 4340, 6.4) */ static inline int dccp_feat_must_be_understood(u8 feat_num) { … } /* copy constructor, fval must not already contain allocated memory */ static int dccp_feat_clone_sp_val(dccp_feat_val *fval, u8 const *val, u8 len) { … } static void dccp_feat_val_destructor(u8 feat_num, dccp_feat_val *val) { … } static struct dccp_feat_entry * dccp_feat_clone_entry(struct dccp_feat_entry const *original) { … } static void dccp_feat_entry_destructor(struct dccp_feat_entry *entry) { … } /* * List management functions * * Feature negotiation lists rely on and maintain the following invariants: * - each feat_num in the list is known, i.e. we know its type and default value * - each feat_num/is_local combination is unique (old entries are overwritten) * - SP values are always freshly allocated * - list is sorted in increasing order of feature number (faster lookup) */ static struct dccp_feat_entry *dccp_feat_list_lookup(struct list_head *fn_list, u8 feat_num, bool is_local) { … } /** * dccp_feat_entry_new - Central list update routine (called by all others) * @head: list to add to * @feat: feature number * @local: whether the local (1) or remote feature with number @feat is meant * * This is the only constructor and serves to ensure the above invariants. */ static struct dccp_feat_entry * dccp_feat_entry_new(struct list_head *head, u8 feat, bool local) { … } /** * dccp_feat_push_change - Add/overwrite a Change option in the list * @fn_list: feature-negotiation list to update * @feat: one of %dccp_feature_numbers * @local: whether local (1) or remote (0) @feat_num is meant * @mandatory: whether to use Mandatory feature negotiation options * @fval: pointer to NN/SP value to be inserted (will be copied) */ static int dccp_feat_push_change(struct list_head *fn_list, u8 feat, u8 local, u8 mandatory, dccp_feat_val *fval) { … } /** * dccp_feat_push_confirm - Add a Confirm entry to the FN list * @fn_list: feature-negotiation list to add to * @feat: one of %dccp_feature_numbers * @local: whether local (1) or remote (0) @feat_num is being confirmed * @fval: pointer to NN/SP value to be inserted or NULL * * Returns 0 on success, a Reset code for further processing otherwise. */ static int dccp_feat_push_confirm(struct list_head *fn_list, u8 feat, u8 local, dccp_feat_val *fval) { … } static int dccp_push_empty_confirm(struct list_head *fn_list, u8 feat, u8 local) { … } static inline void dccp_feat_list_pop(struct dccp_feat_entry *entry) { … } void dccp_feat_list_purge(struct list_head *fn_list) { … } EXPORT_SYMBOL_GPL(…); /* generate @to as full clone of @from - @to must not contain any nodes */ int dccp_feat_clone_list(struct list_head const *from, struct list_head *to) { … } /** * dccp_feat_valid_nn_length - Enforce length constraints on NN options * @feat_num: feature to return length of, one of %dccp_feature_numbers * * Length is between 0 and %DCCP_OPTVAL_MAXLEN. Used for outgoing packets only, * incoming options are accepted as long as their values are valid. */ static u8 dccp_feat_valid_nn_length(u8 feat_num) { … } static u8 dccp_feat_is_valid_nn_val(u8 feat_num, u64 val) { … } /* check that SP values are within the ranges defined in RFC 4340 */ static u8 dccp_feat_is_valid_sp_val(u8 feat_num, u8 val) { … } static u8 dccp_feat_sp_list_ok(u8 feat_num, u8 const *sp_list, u8 sp_len) { … } /** * dccp_feat_insert_opts - Generate FN options from current list state * @skb: next sk_buff to be sent to the peer * @dp: for client during handshake and general negotiation * @dreq: used by the server only (all Changes/Confirms in LISTEN/RESPOND) */ int dccp_feat_insert_opts(struct dccp_sock *dp, struct dccp_request_sock *dreq, struct sk_buff *skb) { … } /** * __feat_register_nn - Register new NN value on socket * @fn: feature-negotiation list to register with * @feat: an NN feature from %dccp_feature_numbers * @mandatory: use Mandatory option if 1 * @nn_val: value to register (restricted to 4 bytes) * * Note that NN features are local by definition (RFC 4340, 6.3.2). */ static int __feat_register_nn(struct list_head *fn, u8 feat, u8 mandatory, u64 nn_val) { … } /** * __feat_register_sp - Register new SP value/list on socket * @fn: feature-negotiation list to register with * @feat: an SP feature from %dccp_feature_numbers * @is_local: whether the local (1) or the remote (0) @feat is meant * @mandatory: use Mandatory option if 1 * @sp_val: SP value followed by optional preference list * @sp_len: length of @sp_val in bytes */ static int __feat_register_sp(struct list_head *fn, u8 feat, u8 is_local, u8 mandatory, u8 const *sp_val, u8 sp_len) { … } /** * dccp_feat_register_sp - Register requests to change SP feature values * @sk: client or listening socket * @feat: one of %dccp_feature_numbers * @is_local: whether the local (1) or remote (0) @feat is meant * @list: array of preferred values, in descending order of preference * @len: length of @list in bytes */ int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local, u8 const *list, u8 len) { … } /** * dccp_feat_nn_get - Query current/pending value of NN feature * @sk: DCCP socket of an established connection * @feat: NN feature number from %dccp_feature_numbers * * For a known NN feature, returns value currently being negotiated, or * current (confirmed) value if no negotiation is going on. */ u64 dccp_feat_nn_get(struct sock *sk, u8 feat) { … } EXPORT_SYMBOL_GPL(…); /** * dccp_feat_signal_nn_change - Update NN values for an established connection * @sk: DCCP socket of an established connection * @feat: NN feature number from %dccp_feature_numbers * @nn_val: the new value to use * * This function is used to communicate NN updates out-of-band. */ int dccp_feat_signal_nn_change(struct sock *sk, u8 feat, u64 nn_val) { … } EXPORT_SYMBOL_GPL(…); /* * Tracking features whose value depend on the choice of CCID * * This is designed with an extension in mind so that a list walk could be done * before activating any features. However, the existing framework was found to * work satisfactorily up until now, the automatic verification is left open. * When adding new CCIDs, add a corresponding dependency table here. */ static const struct ccid_dependency *dccp_feat_ccid_deps(u8 ccid, bool is_local) { … } /** * dccp_feat_propagate_ccid - Resolve dependencies of features on choice of CCID * @fn: feature-negotiation list to update * @id: CCID number to track * @is_local: whether TX CCID (1) or RX CCID (0) is meant * * This function needs to be called after registering all other features. */ static int dccp_feat_propagate_ccid(struct list_head *fn, u8 id, bool is_local) { … } /** * dccp_feat_finalise_settings - Finalise settings before starting negotiation * @dp: client or listening socket (settings will be inherited) * * This is called after all registrations (socket initialisation, sysctls, and * sockopt calls), and before sending the first packet containing Change options * (ie. client-Request or server-Response), to ensure internal consistency. */ int dccp_feat_finalise_settings(struct dccp_sock *dp) { … } /** * dccp_feat_server_ccid_dependencies - Resolve CCID-dependent features * @dreq: server socket to resolve * * It is the server which resolves the dependencies once the CCID has been * fully negotiated. If no CCID has been negotiated, it uses the default CCID. */ int dccp_feat_server_ccid_dependencies(struct dccp_request_sock *dreq) { … } /* Select the first entry in @servlist that also occurs in @clilist (6.3.1) */ static int dccp_feat_preflist_match(u8 *servlist, u8 slen, u8 *clilist, u8 clen) { … } /** * dccp_feat_prefer - Move preferred entry to the start of array * @preferred_value: entry to move to start of array * @array: array of preferred entries * @array_len: size of the array * * Reorder the @array_len elements in @array so that @preferred_value comes * first. Returns >0 to indicate that @preferred_value does occur in @array. */ static u8 dccp_feat_prefer(u8 preferred_value, u8 *array, u8 array_len) { … } /** * dccp_feat_reconcile - Reconcile SP preference lists * @fv: SP list to reconcile into * @arr: received SP preference list * @len: length of @arr in bytes * @is_server: whether this side is the server (and @fv is the server's list) * @reorder: whether to reorder the list in @fv after reconciling with @arr * When successful, > 0 is returned and the reconciled list is in @fval. * A value of 0 means that negotiation failed (no shared entry). */ static int dccp_feat_reconcile(dccp_feat_val *fv, u8 *arr, u8 len, bool is_server, bool reorder) { … } /** * dccp_feat_change_recv - Process incoming ChangeL/R options * @fn: feature-negotiation list to update * @is_mandatory: whether the Change was preceded by a Mandatory option * @opt: %DCCPO_CHANGE_L or %DCCPO_CHANGE_R * @feat: one of %dccp_feature_numbers * @val: NN value or SP value/preference list * @len: length of @val in bytes * @server: whether this node is the server (1) or the client (0) */ static u8 dccp_feat_change_recv(struct list_head *fn, u8 is_mandatory, u8 opt, u8 feat, u8 *val, u8 len, const bool server) { … } /** * dccp_feat_confirm_recv - Process received Confirm options * @fn: feature-negotiation list to update * @is_mandatory: whether @opt was preceded by a Mandatory option * @opt: %DCCPO_CONFIRM_L or %DCCPO_CONFIRM_R * @feat: one of %dccp_feature_numbers * @val: NN value or SP value/preference list * @len: length of @val in bytes * @server: whether this node is server (1) or client (0) */ static u8 dccp_feat_confirm_recv(struct list_head *fn, u8 is_mandatory, u8 opt, u8 feat, u8 *val, u8 len, const bool server) { … } /** * dccp_feat_handle_nn_established - Fast-path reception of NN options * @sk: socket of an established DCCP connection * @mandatory: whether @opt was preceded by a Mandatory option * @opt: %DCCPO_CHANGE_L | %DCCPO_CONFIRM_R (NN only) * @feat: NN number, one of %dccp_feature_numbers * @val: NN value * @len: length of @val in bytes * * This function combines the functionality of change_recv/confirm_recv, with * the following differences (reset codes are the same): * - cleanup after receiving the Confirm; * - values are directly activated after successful parsing; * - deliberately restricted to NN features. * The restriction to NN features is essential since SP features can have non- * predictable outcomes (depending on the remote configuration), and are inter- * dependent (CCIDs for instance cause further dependencies). */ static u8 dccp_feat_handle_nn_established(struct sock *sk, u8 mandatory, u8 opt, u8 feat, u8 *val, u8 len) { … } /** * dccp_feat_parse_options - Process Feature-Negotiation Options * @sk: for general use and used by the client during connection setup * @dreq: used by the server during connection setup * @mandatory: whether @opt was preceded by a Mandatory option * @opt: %DCCPO_CHANGE_L | %DCCPO_CHANGE_R | %DCCPO_CONFIRM_L | %DCCPO_CONFIRM_R * @feat: one of %dccp_feature_numbers * @val: value contents of @opt * @len: length of @val in bytes * * Returns 0 on success, a Reset code for ending the connection otherwise. */ int dccp_feat_parse_options(struct sock *sk, struct dccp_request_sock *dreq, u8 mandatory, u8 opt, u8 feat, u8 *val, u8 len) { … } /** * dccp_feat_init - Seed feature negotiation with host-specific defaults * @sk: Socket to initialize. * * This initialises global defaults, depending on the value of the sysctls. * These can later be overridden by registering changes via setsockopt calls. * The last link in the chain is finalise_settings, to make sure that between * here and the start of actual feature negotiation no inconsistencies enter. * * All features not appearing below use either defaults or are otherwise * later adjusted through dccp_feat_finalise_settings(). */ int dccp_feat_init(struct sock *sk) { … } int dccp_feat_activate_values(struct sock *sk, struct list_head *fn_list) { … }