From 8fe02e167efa8ed4a4503a5eedc0f49fcb7e3eb9 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 21 Oct 2013 19:22:25 +0200 Subject: cfg80211: consolidate passive-scan and no-ibss flags These two flags are used for the same purpose, just combine them into a no-ir flag to annotate no initiating radiation is allowed. Old userspace sending either flag will have it treated as the no-ir flag. To be considerate to older userspace we also send both the no-ir flag and the old no-ibss flags. Newer userspace will have to be aware of older kernels. Update all places in the tree using these flags with the following semantic patch: @@ @@ -NL80211_RRF_PASSIVE_SCAN +NL80211_RRF_NO_IR @@ @@ -NL80211_RRF_NO_IBSS +NL80211_RRF_NO_IR @@ @@ -IEEE80211_CHAN_PASSIVE_SCAN +IEEE80211_CHAN_NO_IR @@ @@ -IEEE80211_CHAN_NO_IBSS +IEEE80211_CHAN_NO_IR @@ @@ -NL80211_RRF_NO_IR | NL80211_RRF_NO_IR +NL80211_RRF_NO_IR @@ @@ -IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_IR +IEEE80211_CHAN_NO_IR @@ @@ -(NL80211_RRF_NO_IR) +NL80211_RRF_NO_IR @@ @@ -(IEEE80211_CHAN_NO_IR) +IEEE80211_CHAN_NO_IR Along with some hand-optimisations in documentation, to remove duplicates and to fix some indentation. Signed-off-by: Luis R. Rodriguez [do all the driver updates in one go] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 3eae46cb1acf..c1b887413234 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -91,9 +91,8 @@ enum ieee80211_band { * Channel flags set by the regulatory control code. * * @IEEE80211_CHAN_DISABLED: This channel is disabled. - * @IEEE80211_CHAN_PASSIVE_SCAN: Only passive scanning is permitted - * on this channel. - * @IEEE80211_CHAN_NO_IBSS: IBSS is not allowed on this channel. + * @IEEE80211_CHAN_NO_IR: do not initiate radiation, this includes + * sending probe requests or beaconing. * @IEEE80211_CHAN_RADAR: Radar detection is required on this channel. * @IEEE80211_CHAN_NO_HT40PLUS: extension channel above this channel * is not permitted. @@ -113,8 +112,8 @@ enum ieee80211_band { */ enum ieee80211_channel_flags { IEEE80211_CHAN_DISABLED = 1<<0, - IEEE80211_CHAN_PASSIVE_SCAN = 1<<1, - IEEE80211_CHAN_NO_IBSS = 1<<2, + IEEE80211_CHAN_NO_IR = 1<<1, + /* hole at 1<<2 */ IEEE80211_CHAN_RADAR = 1<<3, IEEE80211_CHAN_NO_HT40PLUS = 1<<4, IEEE80211_CHAN_NO_HT40MINUS = 1<<5, -- cgit v1.2.3 From 222ea5819901ed174db4df2e26aa5e982f857845 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 5 Nov 2013 09:18:00 -0800 Subject: cfg80211: force WIPHY_FLAG_CUSTOM_REGULATORY on wiphy_apply_custom_regulatory() wiphy_apply_custom_regulatory() implies WIPHY_FLAG_CUSTOM_REGULATORY but we never enforced it, do that now and warn if the driver didn't set it. All drivers should be following this today already. Having WIPHY_FLAG_CUSTOM_REGULATORY does not however mean you will use wiphy_apply_custom_regulatory() though, you may have your own _orig value set up tools / helpers. The intel drivers are examples of this type of driver. Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 7 ++++++- net/wireless/reg.c | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index c1b887413234..b7a825ecff56 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2441,7 +2441,9 @@ struct cfg80211_ops { * has its own custom regulatory domain and cannot identify the * ISO / IEC 3166 alpha2 it belongs to. When this is enabled * we will disregard the first regulatory hint (when the - * initiator is %REGDOM_SET_BY_CORE). + * initiator is %REGDOM_SET_BY_CORE). Drivers that use + * wiphy_apply_custom_regulatory() should have this flag set + * or the regulatory core will set it for wiphy. * @WIPHY_FLAG_STRICT_REGULATORY: tells us the driver for this device will * ignore regulatory domain settings until it gets its own regulatory * domain via its regulatory_hint() unless the regulatory hint is @@ -3471,6 +3473,9 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2); * custom regulatory domain will be trusted completely and as such previous * default channel settings will be disregarded. If no rule is found for a * channel on the regulatory domain the channel will be disabled. + * Drivers using this for a wiphy should also set the wiphy flag + * WIPHY_FLAG_CUSTOM_REGULATORY or cfg80211 will set it for the wiphy + * that called this helper. */ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, const struct ieee80211_regdomain *regd); diff --git a/net/wireless/reg.c b/net/wireless/reg.c index e4e3337ba296..04b6fe4b7653 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1292,6 +1292,10 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, enum ieee80211_band band; unsigned int bands_set = 0; + WARN(!(wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY), + "wiphy should have WIPHY_FLAG_CUSTOM_REGULATORY\n"); + wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (!wiphy->bands[band]) continue; -- cgit v1.2.3 From d2859df5e7f00469011482d850fba652517a2eab Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Wed, 6 Nov 2013 13:55:51 +0100 Subject: cfg80211/mac80211: DFS setup chandef for cac event To report channel width correctly we have to send correct channel parameters from mac80211 when calling cfg80211_cac_event(). This is required in case of using channel width higher than 20MHz and we have to set correct dfs channel state after CAC (NL80211_DFS_AVAILABLE). Signed-off-by: Janusz Dziedzic Reviewed-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ net/mac80211/cfg.c | 5 ++++- net/mac80211/iface.c | 5 ++++- net/mac80211/mlme.c | 6 ++++-- net/mac80211/util.c | 3 +++ net/wireless/mlme.c | 8 +++----- net/wireless/nl80211.c | 4 ++-- net/wireless/nl80211.h | 2 +- 8 files changed, 23 insertions(+), 12 deletions(-) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index b7a825ecff56..968f2ad40ccd 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4150,6 +4150,7 @@ void cfg80211_radar_event(struct wiphy *wiphy, /** * cfg80211_cac_event - Channel availability check (CAC) event * @netdev: network device + * @chandef: chandef for the current channel * @event: type of event * @gfp: context flags * @@ -4158,6 +4159,7 @@ void cfg80211_radar_event(struct wiphy *wiphy, * also by full-MAC drivers. */ void cfg80211_cac_event(struct net_device *netdev, + const struct cfg80211_chan_def *chandef, enum nl80211_radar_event event, gfp_t gfp); diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 95667b088c5b..5232b0102143 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1050,6 +1050,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) struct ieee80211_local *local = sdata->local; struct beacon_data *old_beacon; struct probe_resp *old_probe_resp; + struct cfg80211_chan_def chandef; old_beacon = rtnl_dereference(sdata->u.ap.beacon); if (!old_beacon) @@ -1091,8 +1092,10 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); if (sdata->wdev.cac_started) { + chandef = sdata->vif.bss_conf.chandef; cancel_delayed_work_sync(&sdata->dfs_cac_timer_work); - cfg80211_cac_event(sdata->dev, NL80211_RADAR_CAC_ABORTED, + cfg80211_cac_event(sdata->dev, &chandef, + NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); } diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index ff101ea1d9ae..c9b04425ffc7 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -749,6 +749,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, u32 hw_reconf_flags = 0; int i, flushed; struct ps_data *ps; + struct cfg80211_chan_def chandef; clear_bit(SDATA_STATE_RUNNING, &sdata->state); @@ -823,11 +824,13 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, cancel_delayed_work_sync(&sdata->dfs_cac_timer_work); if (sdata->wdev.cac_started) { + chandef = sdata->vif.bss_conf.chandef; WARN_ON(local->suspended); mutex_lock(&local->iflist_mtx); ieee80211_vif_release_channel(sdata); mutex_unlock(&local->iflist_mtx); - cfg80211_cac_event(sdata->dev, NL80211_RADAR_CAC_ABORTED, + cfg80211_cac_event(sdata->dev, &chandef, + NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); } diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index eb660310ea7c..d39d27feb594 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1398,10 +1398,12 @@ void ieee80211_dfs_cac_timer_work(struct work_struct *work) struct ieee80211_sub_if_data *sdata = container_of(delayed_work, struct ieee80211_sub_if_data, dfs_cac_timer_work); + struct cfg80211_chan_def chandef = sdata->vif.bss_conf.chandef; ieee80211_vif_release_channel(sdata); - - cfg80211_cac_event(sdata->dev, NL80211_RADAR_CAC_FINISHED, GFP_KERNEL); + cfg80211_cac_event(sdata->dev, &chandef, + NL80211_RADAR_CAC_FINISHED, + GFP_KERNEL); } /* MLME */ diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 592a18171f95..fd4fe5d61782 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2259,14 +2259,17 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, void ieee80211_dfs_cac_cancel(struct ieee80211_local *local) { struct ieee80211_sub_if_data *sdata; + struct cfg80211_chan_def chandef; mutex_lock(&local->iflist_mtx); list_for_each_entry(sdata, &local->interfaces, list) { cancel_delayed_work_sync(&sdata->dfs_cac_timer_work); if (sdata->wdev.cac_started) { + chandef = sdata->vif.bss_conf.chandef; ieee80211_vif_release_channel(sdata); cfg80211_cac_event(sdata->dev, + &chandef, NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); } diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 6a6b1c8e907d..31f541f7e4ea 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -763,12 +763,12 @@ void cfg80211_radar_event(struct wiphy *wiphy, EXPORT_SYMBOL(cfg80211_radar_event); void cfg80211_cac_event(struct net_device *netdev, + const struct cfg80211_chan_def *chandef, enum nl80211_radar_event event, gfp_t gfp) { struct wireless_dev *wdev = netdev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - struct cfg80211_chan_def chandef; unsigned long timeout; trace_cfg80211_cac_event(netdev, event); @@ -779,14 +779,12 @@ void cfg80211_cac_event(struct net_device *netdev, if (WARN_ON(!wdev->channel)) return; - cfg80211_chandef_create(&chandef, wdev->channel, NL80211_CHAN_NO_HT); - switch (event) { case NL80211_RADAR_CAC_FINISHED: timeout = wdev->cac_start_time + msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS); WARN_ON(!time_after_eq(jiffies, timeout)); - cfg80211_set_dfs_state(wiphy, &chandef, NL80211_DFS_AVAILABLE); + cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE); break; case NL80211_RADAR_CAC_ABORTED: break; @@ -796,6 +794,6 @@ void cfg80211_cac_event(struct net_device *netdev, } wdev->cac_started = false; - nl80211_radar_notify(rdev, &chandef, event, netdev, gfp); + nl80211_radar_notify(rdev, chandef, event, netdev, gfp); } EXPORT_SYMBOL(cfg80211_cac_event); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 1ad11de6dd2f..04fa8bb1b4bb 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2168,7 +2168,7 @@ static inline u64 wdev_id(struct wireless_dev *wdev) } static int nl80211_send_chandef(struct sk_buff *msg, - struct cfg80211_chan_def *chandef) + const struct cfg80211_chan_def *chandef) { WARN_ON(!cfg80211_chandef_valid(chandef)); @@ -10874,7 +10874,7 @@ EXPORT_SYMBOL(cfg80211_cqm_txe_notify); void nl80211_radar_notify(struct cfg80211_registered_device *rdev, - struct cfg80211_chan_def *chandef, + const struct cfg80211_chan_def *chandef, enum nl80211_radar_event event, struct net_device *netdev, gfp_t gfp) { diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 2c0f2b3c07cb..b1b231324e10 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -70,7 +70,7 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, void nl80211_radar_notify(struct cfg80211_registered_device *rdev, - struct cfg80211_chan_def *chandef, + const struct cfg80211_chan_def *chandef, enum nl80211_radar_event event, struct net_device *netdev, gfp_t gfp); -- cgit v1.2.3 From 2475b1cc0d5283a33144b79f3eba6d401d873962 Mon Sep 17 00:00:00 2001 From: Max Stepanov Date: Sun, 24 Mar 2013 14:23:27 +0200 Subject: mac80211: add generic cipher scheme support This adds generic cipher scheme support to mac80211, such schemes are fully under control by the driver. On hw registration drivers may specify additional HW ciphers with a scheme how these ciphers have to be handled by mac80211 TX/RR. A cipher scheme specifies a cipher suite value, a size of the security header to be added to or stripped from frames and how the PN is to be verified on RX. Signed-off-by: Max Stepanov Signed-off-by: Johannes Berg --- include/net/mac80211.h | 36 +++++++++++ net/mac80211/cfg.c | 31 ++++++++-- net/mac80211/ieee80211_i.h | 10 ++++ net/mac80211/iface.c | 5 ++ net/mac80211/key.c | 58 +++++++++++------- net/mac80211/key.h | 13 +++- net/mac80211/main.c | 145 +++++++++++++++++++++++++++++++-------------- net/mac80211/mesh_hwmp.c | 4 +- net/mac80211/mlme.c | 4 ++ net/mac80211/rx.c | 65 +++++++++++++++----- net/mac80211/sta_info.h | 10 +++- net/mac80211/tx.c | 14 +++-- net/mac80211/util.c | 68 +++++++++++++++++++++ net/mac80211/wpa.c | 116 ++++++++++++++++++++++++++++++++++++ net/mac80211/wpa.h | 2 + 15 files changed, 480 insertions(+), 101 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 7ceed99a05bc..cca02b2f5c7e 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1228,6 +1228,36 @@ struct ieee80211_key_conf { u8 key[0]; }; +/** + * struct ieee80211_cipher_scheme - cipher scheme + * + * This structure contains a cipher scheme information defining + * the secure packet crypto handling. + * + * @cipher: a cipher suite selector + * @iftype: a cipher iftype bit mask indicating an allowed cipher usage + * @hdr_len: a length of a security header used the cipher + * @pn_len: a length of a packet number in the security header + * @pn_off: an offset of pn from the beginning of the security header + * @key_idx_off: an offset of key index byte in the security header + * @key_idx_mask: a bit mask of key_idx bits + * @key_idx_shift: a bit shift needed to get key_idx + * key_idx value calculation: + * (sec_header_base[key_idx_off] & key_idx_mask) >> key_idx_shift + * @mic_len: a mic length in bytes + */ +struct ieee80211_cipher_scheme { + u32 cipher; + u16 iftype; + u8 hdr_len; + u8 pn_len; + u8 pn_off; + u8 key_idx_off; + u8 key_idx_mask; + u8 key_idx_shift; + u8 mic_len; +}; + /** * enum set_key_cmd - key command * @@ -1636,6 +1666,10 @@ enum ieee80211_hw_flags { * @uapsd_max_sp_len: maximum number of total buffered frames the WMM AP may * deliver to a WMM STA during any Service Period triggered by the WMM STA. * Use IEEE80211_WMM_IE_STA_QOSINFO_SP_* for correct values. + * + * @n_cipher_schemes: a size of an array of cipher schemes definitions. + * @cipher_schemes: a pointer to an array of cipher scheme definitions + * supported by HW. */ struct ieee80211_hw { struct ieee80211_conf conf; @@ -1663,6 +1697,8 @@ struct ieee80211_hw { netdev_features_t netdev_features; u8 uapsd_queues; u8 uapsd_max_sp_len; + u8 n_cipher_schemes; + const struct ieee80211_cipher_scheme *cipher_schemes; }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 5232b0102143..f6b9265cf04e 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -133,7 +133,9 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, struct key_params *params) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; struct sta_info *sta = NULL; + const struct ieee80211_cipher_scheme *cs = NULL; struct ieee80211_key *key; int err; @@ -145,22 +147,28 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_TKIP: case WLAN_CIPHER_SUITE_WEP104: - if (IS_ERR(sdata->local->wep_tx_tfm)) + if (IS_ERR(local->wep_tx_tfm)) return -EINVAL; break; + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_AES_CMAC: + case WLAN_CIPHER_SUITE_GCMP: + break; default: + cs = ieee80211_cs_get(local, params->cipher, sdata->vif.type); break; } key = ieee80211_key_alloc(params->cipher, key_idx, params->key_len, - params->key, params->seq_len, params->seq); + params->key, params->seq_len, params->seq, + cs); if (IS_ERR(key)) return PTR_ERR(key); if (pairwise) key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE; - mutex_lock(&sdata->local->sta_mtx); + mutex_lock(&local->sta_mtx); if (mac_addr) { if (ieee80211_vif_is_mesh(&sdata->vif)) @@ -216,10 +224,13 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, break; } + if (sta) + sta->cipher_scheme = cs; + err = ieee80211_key_link(key, sdata, sta); out_unlock: - mutex_unlock(&sdata->local->sta_mtx); + mutex_unlock(&local->sta_mtx); return err; } @@ -244,7 +255,7 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev, goto out_unlock; if (pairwise) - key = key_mtx_dereference(local, sta->ptk); + key = key_mtx_dereference(local, sta->ptk[key_idx]); else key = key_mtx_dereference(local, sta->gtk[key_idx]); } else @@ -291,7 +302,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, goto out; if (pairwise) - key = rcu_dereference(sta->ptk); + key = rcu_dereference(sta->ptk[key_idx]); else if (key_idx < NUM_DEFAULT_KEYS) key = rcu_dereference(sta->gtk[key_idx]); } else @@ -968,11 +979,19 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, */ sdata->control_port_protocol = params->crypto.control_port_ethertype; sdata->control_port_no_encrypt = params->crypto.control_port_no_encrypt; + sdata->encrypt_headroom = ieee80211_cs_headroom(sdata->local, + ¶ms->crypto, + sdata->vif.type); + list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) { vlan->control_port_protocol = params->crypto.control_port_ethertype; vlan->control_port_no_encrypt = params->crypto.control_port_no_encrypt; + vlan->encrypt_headroom = + ieee80211_cs_headroom(sdata->local, + ¶ms->crypto, + vlan->vif.type); } sdata->vif.bss_conf.beacon_int = params->beacon_interval; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 29dc505be125..16f5ba4a3252 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -728,6 +728,7 @@ struct ieee80211_sub_if_data { u16 sequence_number; __be16 control_port_protocol; bool control_port_no_encrypt; + int encrypt_headroom; struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS]; @@ -1749,6 +1750,15 @@ void ieee80211_dfs_radar_detected_work(struct work_struct *work); int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, struct cfg80211_csa_settings *csa_settings); +bool ieee80211_cs_valid(const struct ieee80211_cipher_scheme *cs); +bool ieee80211_cs_list_valid(const struct ieee80211_cipher_scheme *cs, int n); +const struct ieee80211_cipher_scheme * +ieee80211_cs_get(struct ieee80211_local *local, u32 cipher, + enum nl80211_iftype iftype); +int ieee80211_cs_headroom(struct ieee80211_local *local, + struct cfg80211_crypto_settings *crypto, + enum nl80211_iftype iftype); + #ifdef CONFIG_MAC80211_NOINLINE #define debug_noinline noinline #else diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index c9b04425ffc7..a851bf4f05e5 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -401,6 +401,8 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local) snprintf(sdata->name, IFNAMSIZ, "%s-monitor", wiphy_name(local->hw.wiphy)); + sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM; + ieee80211_set_default_queues(sdata); ret = drv_add_interface(local, sdata); @@ -1273,6 +1275,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, sdata->control_port_protocol = cpu_to_be16(ETH_P_PAE); sdata->control_port_no_encrypt = false; + sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM; sdata->noack_map = 0; @@ -1689,6 +1692,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL; sdata->user_power_level = local->user_power_level; + sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM; + /* setup type-dependent data */ ieee80211_setup_sdata(sdata, type); diff --git a/net/mac80211/key.c b/net/mac80211/key.c index ab8468047200..e568d98167d0 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -267,22 +267,22 @@ static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, if (new) list_add_tail(&new->list, &sdata->key_list); - if (sta && pairwise) { - rcu_assign_pointer(sta->ptk, new); - } else if (sta) { - if (old) - idx = old->conf.keyidx; - else - idx = new->conf.keyidx; - rcu_assign_pointer(sta->gtk[idx], new); - } else { - WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx); + WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx); - if (old) - idx = old->conf.keyidx; - else - idx = new->conf.keyidx; + if (old) + idx = old->conf.keyidx; + else + idx = new->conf.keyidx; + if (sta) { + if (pairwise) { + rcu_assign_pointer(sta->ptk[idx], new); + sta->ptk_idx = idx; + } else { + rcu_assign_pointer(sta->gtk[idx], new); + sta->gtk_idx = idx; + } + } else { defunikey = old && old == key_mtx_dereference(sdata->local, sdata->default_unicast_key); @@ -316,9 +316,11 @@ static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, list_del(&old->list); } -struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, - const u8 *key_data, - size_t seq_len, const u8 *seq) +struct ieee80211_key * +ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, + const u8 *key_data, + size_t seq_len, const u8 *seq, + const struct ieee80211_cipher_scheme *cs) { struct ieee80211_key *key; int i, j, err; @@ -397,6 +399,18 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, return ERR_PTR(err); } break; + default: + if (cs) { + size_t len = (seq_len > MAX_PN_LEN) ? + MAX_PN_LEN : seq_len; + + key->conf.iv_len = cs->hdr_len; + key->conf.icv_len = cs->mic_len; + for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) + for (j = 0; j < len; j++) + key->u.gen.rx_pn[i][j] = + seq[len - j - 1]; + } } memcpy(key->conf.key, key_data, key_len); INIT_LIST_HEAD(&key->list); @@ -479,7 +493,7 @@ int ieee80211_key_link(struct ieee80211_key *key, mutex_lock(&sdata->local->key_mtx); if (sta && pairwise) - old_key = key_mtx_dereference(sdata->local, sta->ptk); + old_key = key_mtx_dereference(sdata->local, sta->ptk[idx]); else if (sta) old_key = key_mtx_dereference(sdata->local, sta->gtk[idx]); else @@ -629,8 +643,10 @@ void ieee80211_free_sta_keys(struct ieee80211_local *local, list_add(&key->list, &keys); } - key = key_mtx_dereference(local, sta->ptk); - if (key) { + for (i = 0; i < NUM_DEFAULT_KEYS; i++) { + key = key_mtx_dereference(local, sta->ptk[i]); + if (!key) + continue; ieee80211_key_replace(key->sdata, key->sta, key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE, key, NULL); @@ -881,7 +897,7 @@ ieee80211_gtk_rekey_add(struct ieee80211_vif *vif, key = ieee80211_key_alloc(keyconf->cipher, keyconf->keyidx, keyconf->keylen, keyconf->key, - 0, NULL); + 0, NULL, NULL); if (IS_ERR(key)) return ERR_CAST(key); diff --git a/net/mac80211/key.h b/net/mac80211/key.h index aaae0ed37004..0aebb889caba 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h @@ -18,6 +18,7 @@ #define NUM_DEFAULT_KEYS 4 #define NUM_DEFAULT_MGMT_KEYS 2 +#define MAX_PN_LEN 16 struct ieee80211_local; struct ieee80211_sub_if_data; @@ -93,6 +94,10 @@ struct ieee80211_key { u32 replays; /* dot11RSNAStatsCMACReplays */ u32 icverrors; /* dot11RSNAStatsCMACICVErrors */ } aes_cmac; + struct { + /* generic cipher scheme */ + u8 rx_pn[IEEE80211_NUM_TIDS + 1][MAX_PN_LEN]; + } gen; } u; /* number of times this key has been used */ @@ -113,9 +118,11 @@ struct ieee80211_key { struct ieee80211_key_conf conf; }; -struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, - const u8 *key_data, - size_t seq_len, const u8 *seq); +struct ieee80211_key * +ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, + const u8 *key_data, + size_t seq_len, const u8 *seq, + const struct ieee80211_cipher_scheme *cs); /* * Insert a key into data structures (sdata, sta if necessary) * to make it used, free old key. On failure, also free the new key. diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 21d5d44444d0..bdb0b6c104b5 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -651,15 +651,14 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, } EXPORT_SYMBOL(ieee80211_alloc_hw); -int ieee80211_register_hw(struct ieee80211_hw *hw) +static int ieee80211_init_cipher_suites(struct ieee80211_local *local) { - struct ieee80211_local *local = hw_to_local(hw); - int result, i; - enum ieee80211_band band; - int channels, max_bitrates; - bool supp_ht, supp_vht; - netdev_features_t feature_whitelist; - struct cfg80211_chan_def dflt_chandef = {}; + bool have_wep = !(IS_ERR(local->wep_tx_tfm) || + IS_ERR(local->wep_rx_tfm)); + bool have_mfp = local->hw.flags & IEEE80211_HW_MFP_CAPABLE; + const struct ieee80211_cipher_scheme *cs = local->hw.cipher_schemes; + int n_suites = 0, r = 0, w = 0; + u32 *suites; static const u32 cipher_suites[] = { /* keep WEP first, it may be removed below */ WLAN_CIPHER_SUITE_WEP40, @@ -671,6 +670,93 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) WLAN_CIPHER_SUITE_AES_CMAC }; + /* Driver specifies the ciphers, we have nothing to do... */ + if (local->hw.wiphy->cipher_suites && have_wep) + return 0; + + /* Set up cipher suites if driver relies on mac80211 cipher defs */ + if (!local->hw.wiphy->cipher_suites && !cs) { + local->hw.wiphy->cipher_suites = cipher_suites; + local->hw.wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); + + if (!have_mfp) + local->hw.wiphy->n_cipher_suites--; + + if (!have_wep) { + local->hw.wiphy->cipher_suites += 2; + local->hw.wiphy->n_cipher_suites -= 2; + } + + return 0; + } + + if (!local->hw.wiphy->cipher_suites) { + /* + * Driver specifies cipher schemes only + * We start counting ciphers defined by schemes, TKIP and CCMP + */ + n_suites = local->hw.n_cipher_schemes + 2; + + /* check if we have WEP40 and WEP104 */ + if (have_wep) + n_suites += 2; + + /* check if we have AES_CMAC */ + if (have_mfp) + n_suites++; + + suites = kmalloc(sizeof(u32) * n_suites, GFP_KERNEL); + if (!suites) + return -ENOMEM; + + suites[w++] = WLAN_CIPHER_SUITE_CCMP; + suites[w++] = WLAN_CIPHER_SUITE_TKIP; + + if (have_wep) { + suites[w++] = WLAN_CIPHER_SUITE_WEP40; + suites[w++] = WLAN_CIPHER_SUITE_WEP104; + } + + if (have_mfp) + suites[w++] = WLAN_CIPHER_SUITE_AES_CMAC; + + for (r = 0; r < local->hw.n_cipher_schemes; r++) + suites[w++] = cs[r].cipher; + } else { + /* Driver provides cipher suites, but we need to exclude WEP */ + suites = kmemdup(local->hw.wiphy->cipher_suites, + sizeof(u32) * local->hw.wiphy->n_cipher_suites, + GFP_KERNEL); + if (!suites) + return -ENOMEM; + + for (r = 0; r < local->hw.wiphy->n_cipher_suites; r++) { + u32 suite = local->hw.wiphy->cipher_suites[r]; + + if (suite == WLAN_CIPHER_SUITE_WEP40 || + suite == WLAN_CIPHER_SUITE_WEP104) + continue; + suites[w++] = suite; + } + } + + local->hw.wiphy->cipher_suites = suites; + local->hw.wiphy->n_cipher_suites = w; + local->wiphy_ciphers_allocated = true; + + return 0; +} + +int ieee80211_register_hw(struct ieee80211_hw *hw) +{ + struct ieee80211_local *local = hw_to_local(hw); + int result, i; + enum ieee80211_band band; + int channels, max_bitrates; + bool supp_ht, supp_vht; + netdev_features_t feature_whitelist; + struct cfg80211_chan_def dflt_chandef = {}; + if (hw->flags & IEEE80211_HW_QUEUE_CONTROL && (local->hw.offchannel_tx_hw_queue == IEEE80211_INVAL_HW_QUEUE || local->hw.offchannel_tx_hw_queue >= local->hw.queues)) @@ -851,43 +937,12 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (local->hw.wiphy->max_scan_ie_len) local->hw.wiphy->max_scan_ie_len -= local->scan_ies_len; - /* Set up cipher suites unless driver already did */ - if (!local->hw.wiphy->cipher_suites) { - local->hw.wiphy->cipher_suites = cipher_suites; - local->hw.wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); - if (!(local->hw.flags & IEEE80211_HW_MFP_CAPABLE)) - local->hw.wiphy->n_cipher_suites--; - } - if (IS_ERR(local->wep_tx_tfm) || IS_ERR(local->wep_rx_tfm)) { - if (local->hw.wiphy->cipher_suites == cipher_suites) { - local->hw.wiphy->cipher_suites += 2; - local->hw.wiphy->n_cipher_suites -= 2; - } else { - u32 *suites; - int r, w = 0; - - /* Filter out WEP */ - - suites = kmemdup( - local->hw.wiphy->cipher_suites, - sizeof(u32) * local->hw.wiphy->n_cipher_suites, - GFP_KERNEL); - if (!suites) { - result = -ENOMEM; - goto fail_wiphy_register; - } - for (r = 0; r < local->hw.wiphy->n_cipher_suites; r++) { - u32 suite = local->hw.wiphy->cipher_suites[r]; - if (suite == WLAN_CIPHER_SUITE_WEP40 || - suite == WLAN_CIPHER_SUITE_WEP104) - continue; - suites[w++] = suite; - } - local->hw.wiphy->cipher_suites = suites; - local->hw.wiphy->n_cipher_suites = w; - local->wiphy_ciphers_allocated = true; - } - } + WARN_ON(!ieee80211_cs_list_valid(local->hw.cipher_schemes, + local->hw.n_cipher_schemes)); + + result = ieee80211_init_cipher_suites(local); + if (result < 0) + goto fail_wiphy_register; if (!local->ops->remain_on_channel) local->hw.wiphy->max_remain_on_channel_duration = 5000; diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 486819cd02cd..56e0c072007a 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -254,13 +254,13 @@ int mesh_path_error_tx(struct ieee80211_sub_if_data *sdata, return -EAGAIN; skb = dev_alloc_skb(local->tx_headroom + - IEEE80211_ENCRYPT_HEADROOM + + sdata->encrypt_headroom + IEEE80211_ENCRYPT_TAILROOM + hdr_len + 2 + 15 /* PERR IE */); if (!skb) return -1; - skb_reserve(skb, local->tx_headroom + IEEE80211_ENCRYPT_HEADROOM); + skb_reserve(skb, local->tx_headroom + sdata->encrypt_headroom); mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); memset(mgmt, 0, hdr_len); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index d39d27feb594..f8dca58b7e52 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1747,6 +1747,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, ifmgd->flags = 0; ieee80211_vif_release_channel(sdata); + + sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM; } void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, @@ -4191,6 +4193,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, sdata->control_port_protocol = req->crypto.control_port_ethertype; sdata->control_port_no_encrypt = req->crypto.control_port_no_encrypt; + sdata->encrypt_headroom = ieee80211_cs_headroom(local, &req->crypto, + sdata->vif.type); /* kick off associate process */ diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 401f3c26e707..79f76791f77a 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -638,6 +638,27 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb) return le16_to_cpu(mmie->key_id); } +static int iwl80211_get_cs_keyid(const struct ieee80211_cipher_scheme *cs, + struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + __le16 fc; + int hdrlen; + u8 keyid; + + fc = hdr->frame_control; + hdrlen = ieee80211_hdrlen(fc); + + if (skb->len < hdrlen + cs->hdr_len) + return -EINVAL; + + skb_copy_bits(skb, hdrlen + cs->key_idx_off, &keyid, 1); + keyid &= cs->key_idx_mask; + keyid >>= cs->key_idx_shift; + + return keyid; +} + static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; @@ -1360,6 +1381,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) struct ieee80211_key *sta_ptk = NULL; int mmie_keyidx = -1; __le16 fc; + const struct ieee80211_cipher_scheme *cs = NULL; /* * Key selection 101 @@ -1397,11 +1419,19 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) /* start without a key */ rx->key = NULL; + fc = hdr->frame_control; - if (rx->sta) - sta_ptk = rcu_dereference(rx->sta->ptk); + if (rx->sta) { + int keyid = rx->sta->ptk_idx; - fc = hdr->frame_control; + if (ieee80211_has_protected(fc) && rx->sta->cipher_scheme) { + cs = rx->sta->cipher_scheme; + keyid = iwl80211_get_cs_keyid(cs, rx->skb); + if (unlikely(keyid < 0)) + return RX_DROP_UNUSABLE; + } + sta_ptk = rcu_dereference(rx->sta->ptk[keyid]); + } if (!ieee80211_has_protected(fc)) mmie_keyidx = ieee80211_get_mmie_keyidx(rx->skb); @@ -1463,6 +1493,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) return RX_CONTINUE; } else { u8 keyid; + /* * The device doesn't give us the IV so we won't be * able to look up the key. That's ok though, we @@ -1478,15 +1509,21 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) hdrlen = ieee80211_hdrlen(fc); - if (rx->skb->len < 8 + hdrlen) - return RX_DROP_UNUSABLE; /* TODO: count this? */ + if (cs) { + keyidx = iwl80211_get_cs_keyid(cs, rx->skb); - /* - * no need to call ieee80211_wep_get_keyidx, - * it verifies a bunch of things we've done already - */ - skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1); - keyidx = keyid >> 6; + if (unlikely(keyidx < 0)) + return RX_DROP_UNUSABLE; + } else { + if (rx->skb->len < 8 + hdrlen) + return RX_DROP_UNUSABLE; /* TODO: count this? */ + /* + * no need to call ieee80211_wep_get_keyidx, + * it verifies a bunch of things we've done already + */ + skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1); + keyidx = keyid >> 6; + } /* check per-station GTK first, if multicast packet */ if (is_multicast_ether_addr(hdr->addr1) && rx->sta) @@ -1534,11 +1571,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) result = ieee80211_crypto_aes_cmac_decrypt(rx); break; default: - /* - * We can reach here only with HW-only algorithms - * but why didn't it decrypt the frame?! - */ - return RX_DROP_UNUSABLE; + result = ieee80211_crypto_hw_decrypt(rx); } /* the hdr variable is invalid after the decrypt handlers */ diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 3ef06a26b9cb..6b0d6c2dcba7 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -231,8 +231,10 @@ struct sta_ampdu_mlme { * @hnext: hash table linked list pointer * @local: pointer to the global information * @sdata: virtual interface this station belongs to - * @ptk: peer key negotiated with this station, if any + * @ptk: peer keys negotiated with this station, if any + * @ptk_idx: last installed peer key index * @gtk: group keys negotiated with this station, if any + * @gtk_idx: last installed group key index * @rate_ctrl: rate control algorithm reference * @rate_ctrl_priv: rate control private per-STA pointer * @last_tx_rate: rate used for last transmit, to report to userspace as @@ -303,6 +305,7 @@ struct sta_ampdu_mlme { * @chain_signal_avg: signal average (per chain) * @known_smps_mode: the smps_mode the client thinks we are in. Relevant for * AP only. + * @cipher_scheme: optional cipher scheme for this station */ struct sta_info { /* General information, mostly static */ @@ -312,7 +315,9 @@ struct sta_info { struct ieee80211_local *local; struct ieee80211_sub_if_data *sdata; struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS]; - struct ieee80211_key __rcu *ptk; + struct ieee80211_key __rcu *ptk[NUM_DEFAULT_KEYS]; + u8 gtk_idx; + u8 ptk_idx; struct rate_control_ref *rate_ctrl; void *rate_ctrl_priv; spinlock_t lock; @@ -414,6 +419,7 @@ struct sta_info { unsigned int beacon_loss_count; enum ieee80211_smps_mode known_smps_mode; + const struct ieee80211_cipher_scheme *cipher_scheme; /* keep last! */ struct ieee80211_sta sta; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 5ad2e8b1f92c..e541856b4007 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -557,7 +557,8 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) if (unlikely(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT)) tx->key = NULL; - else if (tx->sta && (key = rcu_dereference(tx->sta->ptk))) + else if (tx->sta && + (key = rcu_dereference(tx->sta->ptk[tx->sta->ptk_idx]))) tx->key = key; else if (ieee80211_is_mgmt(hdr->frame_control) && is_multicast_ether_addr(hdr->addr1) && @@ -840,15 +841,16 @@ static int ieee80211_fragment(struct ieee80211_tx_data *tx, rem -= fraglen; tmp = dev_alloc_skb(local->tx_headroom + frag_threshold + - IEEE80211_ENCRYPT_HEADROOM + + tx->sdata->encrypt_headroom + IEEE80211_ENCRYPT_TAILROOM); if (!tmp) return -ENOMEM; __skb_queue_tail(&tx->skbs, tmp); - skb_reserve(tmp, local->tx_headroom + - IEEE80211_ENCRYPT_HEADROOM); + skb_reserve(tmp, + local->tx_headroom + tx->sdata->encrypt_headroom); + /* copy control information */ memcpy(tmp->cb, skb->cb, sizeof(tmp->cb)); @@ -1485,7 +1487,7 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, headroom = local->tx_headroom; if (may_encrypt) - headroom += IEEE80211_ENCRYPT_HEADROOM; + headroom += sdata->encrypt_headroom; headroom -= skb_headroom(skb); headroom = max_t(int, 0, headroom); @@ -2108,7 +2110,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, */ if (head_need > 0 || skb_cloned(skb)) { - head_need += IEEE80211_ENCRYPT_HEADROOM; + head_need += sdata->encrypt_headroom; head_need += local->tx_headroom; head_need = max_t(int, 0, head_need); if (ieee80211_skb_resize(sdata, skb, head_need, true)) { diff --git a/net/mac80211/util.c b/net/mac80211/util.c index fd4fe5d61782..5dfa41abdf8b 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2475,3 +2475,71 @@ int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, ieee80211_tx_skb(sdata, skb); return 0; } + +bool ieee80211_cs_valid(const struct ieee80211_cipher_scheme *cs) +{ + return !(cs == NULL || cs->cipher == 0 || + cs->hdr_len < cs->pn_len + cs->pn_off || + cs->hdr_len <= cs->key_idx_off || + cs->key_idx_shift > 7 || + cs->key_idx_mask == 0); +} + +bool ieee80211_cs_list_valid(const struct ieee80211_cipher_scheme *cs, int n) +{ + int i; + + /* Ensure we have enough iftype bitmap space for all iftype values */ + WARN_ON((NUM_NL80211_IFTYPES / 8 + 1) > sizeof(cs[0].iftype)); + + for (i = 0; i < n; i++) + if (!ieee80211_cs_valid(&cs[i])) + return false; + + return true; +} + +const struct ieee80211_cipher_scheme * +ieee80211_cs_get(struct ieee80211_local *local, u32 cipher, + enum nl80211_iftype iftype) +{ + const struct ieee80211_cipher_scheme *l = local->hw.cipher_schemes; + int n = local->hw.n_cipher_schemes; + int i; + const struct ieee80211_cipher_scheme *cs = NULL; + + for (i = 0; i < n; i++) { + if (l[i].cipher == cipher) { + cs = &l[i]; + break; + } + } + + if (!cs || !(cs->iftype & BIT(iftype))) + return NULL; + + return cs; +} + +int ieee80211_cs_headroom(struct ieee80211_local *local, + struct cfg80211_crypto_settings *crypto, + enum nl80211_iftype iftype) +{ + const struct ieee80211_cipher_scheme *cs; + int headroom = IEEE80211_ENCRYPT_HEADROOM; + int i; + + for (i = 0; i < crypto->n_ciphers_pairwise; i++) { + cs = ieee80211_cs_get(local, crypto->ciphers_pairwise[i], + iftype); + + if (cs && headroom < cs->hdr_len) + headroom = cs->hdr_len; + } + + cs = ieee80211_cs_get(local, crypto->cipher_group, iftype); + if (cs && headroom < cs->hdr_len) + headroom = cs->hdr_len; + + return headroom; +} diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index d65728220763..7313d379c0d3 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -545,6 +545,106 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx) return RX_CONTINUE; } +static ieee80211_tx_result +ieee80211_crypto_cs_encrypt(struct ieee80211_tx_data *tx, + struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_key *key = tx->key; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + const struct ieee80211_cipher_scheme *cs = key->sta->cipher_scheme; + int hdrlen; + u8 *pos; + + if (info->control.hw_key && + !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) { + /* hwaccel has no need for preallocated head room */ + return TX_CONTINUE; + } + + if (unlikely(skb_headroom(skb) < cs->hdr_len && + pskb_expand_head(skb, cs->hdr_len, 0, GFP_ATOMIC))) + return TX_DROP; + + hdrlen = ieee80211_hdrlen(hdr->frame_control); + + pos = skb_push(skb, cs->hdr_len); + memmove(pos, pos + cs->hdr_len, hdrlen); + skb_set_network_header(skb, skb_network_offset(skb) + cs->hdr_len); + + return TX_CONTINUE; +} + +static inline int ieee80211_crypto_cs_pn_compare(u8 *pn1, u8 *pn2, int len) +{ + int i; + + /* pn is little endian */ + for (i = len - 1; i >= 0; i--) { + if (pn1[i] < pn2[i]) + return -1; + else if (pn1[i] > pn2[i]) + return 1; + } + + return 0; +} + +static ieee80211_rx_result +ieee80211_crypto_cs_decrypt(struct ieee80211_rx_data *rx) +{ + struct ieee80211_key *key = rx->key; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; + const struct ieee80211_cipher_scheme *cs = NULL; + int hdrlen = ieee80211_hdrlen(hdr->frame_control); + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); + int data_len; + u8 *rx_pn; + u8 *skb_pn; + u8 qos_tid; + + if (!rx->sta || !rx->sta->cipher_scheme || + !(status->flag & RX_FLAG_DECRYPTED)) + return RX_DROP_UNUSABLE; + + if (!ieee80211_is_data(hdr->frame_control)) + return RX_CONTINUE; + + cs = rx->sta->cipher_scheme; + + data_len = rx->skb->len - hdrlen - cs->hdr_len; + + if (data_len < 0) + return RX_DROP_UNUSABLE; + + if (ieee80211_is_data_qos(hdr->frame_control)) + qos_tid = *ieee80211_get_qos_ctl(hdr) & + IEEE80211_QOS_CTL_TID_MASK; + else + qos_tid = 0; + + if (skb_linearize(rx->skb)) + return RX_DROP_UNUSABLE; + + hdr = (struct ieee80211_hdr *)rx->skb->data; + + rx_pn = key->u.gen.rx_pn[qos_tid]; + skb_pn = rx->skb->data + hdrlen + cs->pn_off; + + if (ieee80211_crypto_cs_pn_compare(skb_pn, rx_pn, cs->pn_len) <= 0) + return RX_DROP_UNUSABLE; + + memcpy(rx_pn, skb_pn, cs->pn_len); + + /* remove security header and MIC */ + if (pskb_trim(rx->skb, rx->skb->len - cs->mic_len)) + return RX_DROP_UNUSABLE; + + memmove(rx->skb->data + cs->hdr_len, rx->skb->data, hdrlen); + skb_pull(rx->skb, cs->hdr_len); + + return RX_CONTINUE; +} static void bip_aad(struct sk_buff *skb, u8 *aad) { @@ -685,6 +785,7 @@ ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx) { struct sk_buff *skb; struct ieee80211_tx_info *info = NULL; + ieee80211_tx_result res; skb_queue_walk(&tx->skbs, skb) { info = IEEE80211_SKB_CB(skb); @@ -692,9 +793,24 @@ ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx) /* handle hw-only algorithm */ if (!info->control.hw_key) return TX_DROP; + + if (tx->key->sta->cipher_scheme) { + res = ieee80211_crypto_cs_encrypt(tx, skb); + if (res != TX_CONTINUE) + return res; + } } ieee80211_tx_set_protected(tx); return TX_CONTINUE; } + +ieee80211_rx_result +ieee80211_crypto_hw_decrypt(struct ieee80211_rx_data *rx) +{ + if (rx->sta->cipher_scheme) + return ieee80211_crypto_cs_decrypt(rx); + + return RX_DROP_UNUSABLE; +} diff --git a/net/mac80211/wpa.h b/net/mac80211/wpa.h index 07e33f899c71..62e5a12dfe0a 100644 --- a/net/mac80211/wpa.h +++ b/net/mac80211/wpa.h @@ -34,5 +34,7 @@ ieee80211_rx_result ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx); ieee80211_tx_result ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx); +ieee80211_rx_result +ieee80211_crypto_hw_decrypt(struct ieee80211_rx_data *rx); #endif /* WPA_H */ -- cgit v1.2.3 From a2f73b6c5db3c272d87eaebb5bed355d75a0f25f Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 11 Nov 2013 22:15:29 +0100 Subject: cfg80211: move regulatory flags to their own variable We'll expand this later, this will make it easier to classify and review what things are related to regulatory or not. Coccinelle only missed 4 hits, which I had to do manually, supplying the SmPL in case of merge conflicts. @@ struct wiphy *wiphy; @@ -wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY +wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG @@ expression e; @@ -e->flags |= WIPHY_FLAG_CUSTOM_REGULATORY +e->regulatory_flags |= REGULATORY_CUSTOM_REG @@ struct wiphy *wiphy; @@ -wiphy->flags &= ~WIPHY_FLAG_CUSTOM_REGULATORY +wiphy->regulatory_flags &= ~REGULATORY_CUSTOM_REG @@ struct wiphy *wiphy; @@ -wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY +wiphy->regulatory_flags & REGULATORY_CUSTOM_REG @@ struct wiphy *wiphy; @@ -wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY +wiphy->regulatory_flags |= REGULATORY_STRICT_REG @@ expression e; @@ -e->flags |= WIPHY_FLAG_STRICT_REGULATORY +e->regulatory_flags |= REGULATORY_STRICT_REG @@ struct wiphy *wiphy; @@ -wiphy->flags &= ~WIPHY_FLAG_STRICT_REGULATORY +wiphy->regulatory_flags &= ~REGULATORY_STRICT_REG @@ struct wiphy *wiphy; @@ -wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY +wiphy->regulatory_flags & REGULATORY_STRICT_REG @@ struct wiphy *wiphy; @@ -wiphy->flags |= WIPHY_FLAG_DISABLE_BEACON_HINTS +wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS @@ expression e; @@ -e->flags |= WIPHY_FLAG_DISABLE_BEACON_HINTS +e->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS @@ struct wiphy *wiphy; @@ -wiphy->flags &= ~WIPHY_FLAG_DISABLE_BEACON_HINTS +wiphy->regulatory_flags &= ~REGULATORY_DISABLE_BEACON_HINTS @@ struct wiphy *wiphy; @@ -wiphy->flags & WIPHY_FLAG_DISABLE_BEACON_HINTS +wiphy->regulatory_flags & REGULATORY_DISABLE_BEACON_HINTS Generated-by: Coccinelle SmPL Cc: Julia Lawall Cc: Peter Senna Tschudin Cc: Mihir Shete Cc: Henri Bahini Cc: Tushnim Bhattacharyya Signed-off-by: Luis R. Rodriguez [fix up whitespace damage, overly long lines] Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/regd.c | 4 +- .../net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 2 +- drivers/net/wireless/brcm80211/brcmsmac/channel.c | 4 +- drivers/net/wireless/iwlegacy/3945-mac.c | 6 +-- drivers/net/wireless/iwlegacy/4965-mac.c | 6 +-- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 6 +-- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 6 +-- drivers/net/wireless/mac80211_hwsim.c | 25 +++++---- drivers/net/wireless/mwifiex/cfg80211.c | 5 +- drivers/net/wireless/rtlwifi/regd.c | 6 +-- include/net/cfg80211.h | 33 +++--------- include/net/regulatory.h | 59 +++++++++++++++++----- net/wireless/core.c | 2 +- net/wireless/reg.c | 32 ++++++------ 14 files changed, 108 insertions(+), 88 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index e93e517a699f..aba782cd09a2 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -601,7 +601,7 @@ ath_regd_init_wiphy(struct ath_regulatory *reg, const struct ieee80211_regdomain *regd; wiphy->reg_notifier = reg_notifier; - wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY; + wiphy->regulatory_flags |= REGULATORY_STRICT_REG; if (ath_is_world_regd(reg)) { /* @@ -609,7 +609,7 @@ ath_regd_init_wiphy(struct ath_regulatory *reg, * saved on the wiphy orig_* parameters */ regd = ath_world_regdomain(reg); - wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; + wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; } else { /* * This gets applied in the case of the absence of CRDA, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index b6a09f97f9a3..1850efa83cf8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -4341,7 +4341,7 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev) wiphy->max_remain_on_channel_duration = 5000; brcmf_wiphy_pno_params(wiphy); brcmf_dbg(INFO, "Registering custom regulatory\n"); - wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; + wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom); err = wiphy_register(wiphy); if (err < 0) { diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c index 7d8f3fd69a87..ef05df04136b 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/channel.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.c @@ -766,8 +766,8 @@ void brcms_c_regd_init(struct brcms_c_info *wlc) } wlc->wiphy->reg_notifier = brcms_reg_notifier; - wlc->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY | - WIPHY_FLAG_STRICT_REGULATORY; + wlc->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | + REGULATORY_STRICT_REG; wiphy_apply_custom_regulatory(wlc->wiphy, regd->regdomain); brcms_reg_apply_beaconing_flags(wiphy, NL80211_REGDOM_SET_BY_DRIVER); } diff --git a/drivers/net/wireless/iwlegacy/3945-mac.c b/drivers/net/wireless/iwlegacy/3945-mac.c index 5a66595b0faf..5c3bcedd679b 100644 --- a/drivers/net/wireless/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/iwlegacy/3945-mac.c @@ -3575,9 +3575,9 @@ il3945_setup_mac(struct il_priv *il) hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); - hw->wiphy->flags |= - WIPHY_FLAG_CUSTOM_REGULATORY | WIPHY_FLAG_DISABLE_BEACON_HINTS | - WIPHY_FLAG_IBSS_RSN; + hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | + REGULATORY_DISABLE_BEACON_HINTS; hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index 1c5f8cdbd3a5..43f488a8cda2 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -5778,9 +5778,9 @@ il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length) hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); - hw->wiphy->flags |= - WIPHY_FLAG_CUSTOM_REGULATORY | WIPHY_FLAG_DISABLE_BEACON_HINTS | - WIPHY_FLAG_IBSS_RSN; + hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | + REGULATORY_DISABLE_BEACON_HINTS; /* * For now, disable PS by default because it affects diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index cae4d3182e33..217f1ca321a0 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -155,9 +155,9 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv, ARRAY_SIZE(iwlagn_iface_combinations_dualmode); } - hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY | - WIPHY_FLAG_DISABLE_BEACON_HINTS | - WIPHY_FLAG_IBSS_RSN; + hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | + REGULATORY_DISABLE_BEACON_HINTS; #ifdef CONFIG_PM_SLEEP if (priv->fw->img[IWL_UCODE_WOWLAN].sec[0].len && diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 74bc2c8af06d..b56c989ad784 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -199,9 +199,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) if (IWL_UCODE_API(mvm->fw->ucode_ver) >= 8) hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC); - hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY | - WIPHY_FLAG_DISABLE_BEACON_HINTS | - WIPHY_FLAG_IBSS_RSN; + hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | + REGULATORY_DISABLE_BEACON_HINTS; hw->wiphy->iface_combinations = iwl_mvm_iface_combinations; hw->wiphy->n_iface_combinations = diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 8c7f4ab70d29..bcc7d8208f23 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -2456,46 +2456,53 @@ static int __init init_mac80211_hwsim(void) break; case HWSIM_REGTEST_WORLD_ROAM: if (i == 0) { - hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; + hw->wiphy->regulatory_flags |= + REGULATORY_CUSTOM_REG; wiphy_apply_custom_regulatory(hw->wiphy, &hwsim_world_regdom_custom_01); } break; case HWSIM_REGTEST_CUSTOM_WORLD: - hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; + hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; wiphy_apply_custom_regulatory(hw->wiphy, &hwsim_world_regdom_custom_01); break; case HWSIM_REGTEST_CUSTOM_WORLD_2: if (i == 0) { - hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; + hw->wiphy->regulatory_flags |= + REGULATORY_CUSTOM_REG; wiphy_apply_custom_regulatory(hw->wiphy, &hwsim_world_regdom_custom_01); } else if (i == 1) { - hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; + hw->wiphy->regulatory_flags |= + REGULATORY_CUSTOM_REG; wiphy_apply_custom_regulatory(hw->wiphy, &hwsim_world_regdom_custom_02); } break; case HWSIM_REGTEST_STRICT_ALL: - hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY; + hw->wiphy->regulatory_flags |= REGULATORY_STRICT_REG; break; case HWSIM_REGTEST_STRICT_FOLLOW: case HWSIM_REGTEST_STRICT_AND_DRIVER_REG: if (i == 0) - hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY; + hw->wiphy->regulatory_flags |= + REGULATORY_STRICT_REG; break; case HWSIM_REGTEST_ALL: if (i == 0) { - hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; + hw->wiphy->regulatory_flags |= + REGULATORY_CUSTOM_REG; wiphy_apply_custom_regulatory(hw->wiphy, &hwsim_world_regdom_custom_01); } else if (i == 1) { - hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; + hw->wiphy->regulatory_flags |= + REGULATORY_CUSTOM_REG; wiphy_apply_custom_regulatory(hw->wiphy, &hwsim_world_regdom_custom_02); } else if (i == 4) - hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY; + hw->wiphy->regulatory_flags |= + REGULATORY_STRICT_REG; break; default: break; diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 7f68943f02c2..d6d1d91a26dc 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -2687,9 +2687,10 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | WIPHY_FLAG_AP_UAPSD | - WIPHY_FLAG_CUSTOM_REGULATORY | - WIPHY_FLAG_STRICT_REGULATORY | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + wiphy->regulatory_flags |= + REGULATORY_CUSTOM_REG | + REGULATORY_STRICT_REG; wiphy_apply_custom_regulatory(wiphy, &mwifiex_world_regdom_custom); diff --git a/drivers/net/wireless/rtlwifi/regd.c b/drivers/net/wireless/rtlwifi/regd.c index 8453c53143f5..89e36568e70f 100644 --- a/drivers/net/wireless/rtlwifi/regd.c +++ b/drivers/net/wireless/rtlwifi/regd.c @@ -344,9 +344,9 @@ static int _rtl_regd_init_wiphy(struct rtl_regulatory *reg, wiphy->reg_notifier = reg_notifier; - wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; - wiphy->flags &= ~WIPHY_FLAG_STRICT_REGULATORY; - wiphy->flags &= ~WIPHY_FLAG_DISABLE_BEACON_HINTS; + wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; + wiphy->regulatory_flags &= ~REGULATORY_STRICT_REG; + wiphy->regulatory_flags &= ~REGULATORY_DISABLE_BEACON_HINTS; regd = _rtl_regdomain_select(reg); wiphy_apply_custom_regulatory(wiphy, regd); diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 968f2ad40ccd..bacc5033f0b6 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2437,29 +2437,6 @@ struct cfg80211_ops { /** * enum wiphy_flags - wiphy capability flags * - * @WIPHY_FLAG_CUSTOM_REGULATORY: tells us the driver for this device - * has its own custom regulatory domain and cannot identify the - * ISO / IEC 3166 alpha2 it belongs to. When this is enabled - * we will disregard the first regulatory hint (when the - * initiator is %REGDOM_SET_BY_CORE). Drivers that use - * wiphy_apply_custom_regulatory() should have this flag set - * or the regulatory core will set it for wiphy. - * @WIPHY_FLAG_STRICT_REGULATORY: tells us the driver for this device will - * ignore regulatory domain settings until it gets its own regulatory - * domain via its regulatory_hint() unless the regulatory hint is - * from a country IE. After its gets its own regulatory domain it will - * only allow further regulatory domain settings to further enhance - * compliance. For example if channel 13 and 14 are disabled by this - * regulatory domain no user regulatory domain can enable these channels - * at a later time. This can be used for devices which do not have - * calibration information guaranteed for frequencies or settings - * outside of its regulatory domain. If used in combination with - * WIPHY_FLAG_CUSTOM_REGULATORY the inspected country IE power settings - * will be followed. - * @WIPHY_FLAG_DISABLE_BEACON_HINTS: enable this if your driver needs to ensure - * that passive scan flags and beaconing flags may not be lifted by - * cfg80211 due to regulatory beacon hints. For more information on beacon - * hints read the documenation for regulatory_hint_found_beacon() * @WIPHY_FLAG_NETNS_OK: if not set, do not allow changing the netns of this * wiphy at all * @WIPHY_FLAG_PS_ON_BY_DEFAULT: if set to true, powersave will be enabled @@ -2498,9 +2475,9 @@ struct cfg80211_ops { * beaconing mode (AP, IBSS, Mesh, ...). */ enum wiphy_flags { - WIPHY_FLAG_CUSTOM_REGULATORY = BIT(0), - WIPHY_FLAG_STRICT_REGULATORY = BIT(1), - WIPHY_FLAG_DISABLE_BEACON_HINTS = BIT(2), + /* use hole at 0 */ + /* use hole at 1 */ + /* use hole at 2 */ WIPHY_FLAG_NETNS_OK = BIT(3), WIPHY_FLAG_PS_ON_BY_DEFAULT = BIT(4), WIPHY_FLAG_4ADDR_AP = BIT(5), @@ -2722,6 +2699,8 @@ struct wiphy_coalesce_support { * @software_iftypes: bitmask of software interface types, these are not * subject to any restrictions since they are purely managed in SW. * @flags: wiphy flags, see &enum wiphy_flags + * @regulatory_flags: wiphy regulatory flags, see + * &enum ieee80211_regulatory_flags * @features: features advertised to nl80211, see &enum nl80211_feature_flags. * @bss_priv_size: each BSS struct has private data allocated with it, * this variable determines its size @@ -2810,7 +2789,7 @@ struct wiphy { u16 max_acl_mac_addrs; - u32 flags, features; + u32 flags, regulatory_flags, features; u32 ap_sme_capa; diff --git a/include/net/regulatory.h b/include/net/regulatory.h index f17ed590d64a..a6a20e2a54c4 100644 --- a/include/net/regulatory.h +++ b/include/net/regulatory.h @@ -38,17 +38,17 @@ enum environment_cap { * * @rcu_head: RCU head struct used to free the request * @wiphy_idx: this is set if this request's initiator is - * %REGDOM_SET_BY_COUNTRY_IE or %REGDOM_SET_BY_DRIVER. This - * can be used by the wireless core to deal with conflicts - * and potentially inform users of which devices specifically - * cased the conflicts. + * %REGDOM_SET_BY_COUNTRY_IE or %REGDOM_SET_BY_DRIVER. This + * can be used by the wireless core to deal with conflicts + * and potentially inform users of which devices specifically + * cased the conflicts. * @initiator: indicates who sent this request, could be any of - * of those set in nl80211_reg_initiator (%NL80211_REGDOM_SET_BY_*) + * of those set in nl80211_reg_initiator (%NL80211_REGDOM_SET_BY_*) * @alpha2: the ISO / IEC 3166 alpha2 country code of the requested - * regulatory domain. We have a few special codes: - * 00 - World regulatory domain - * 99 - built by driver but a specific alpha2 cannot be determined - * 98 - result of an intersection between two regulatory domains + * regulatory domain. We have a few special codes: + * 00 - World regulatory domain + * 99 - built by driver but a specific alpha2 cannot be determined + * 98 - result of an intersection between two regulatory domains * 97 - regulatory domain has not yet been configured * @dfs_region: If CRDA responded with a regulatory domain that requires * DFS master operation on a known DFS region (NL80211_DFS_*), @@ -59,8 +59,8 @@ enum environment_cap { * of hint passed. This could be any of the %NL80211_USER_REG_HINT_* * types. * @intersect: indicates whether the wireless core should intersect - * the requested regulatory domain with the presently set regulatory - * domain. + * the requested regulatory domain with the presently set regulatory + * domain. * @processed: indicates whether or not this requests has already been * processed. When the last request is processed it means that the * currently regulatory domain set on cfg80211 is updated from @@ -68,9 +68,9 @@ enum environment_cap { * the last request is not yet processed we must yield until it * is processed before processing any new requests. * @country_ie_checksum: checksum of the last processed and accepted - * country IE + * country IE * @country_ie_env: lets us know if the AP is telling us we are outdoor, - * indoor, or if it doesn't matter + * indoor, or if it doesn't matter * @list: used to insert into the reg_requests_list linked list */ struct regulatory_request { @@ -86,6 +86,39 @@ struct regulatory_request { struct list_head list; }; +/** + * enum ieee80211_regulatory_flags - device regulatory flags + * + * @REGULATORY_CUSTOM_REG: tells us the driver for this device + * has its own custom regulatory domain and cannot identify the + * ISO / IEC 3166 alpha2 it belongs to. When this is enabled + * we will disregard the first regulatory hint (when the + * initiator is %REGDOM_SET_BY_CORE). Drivers that use + * wiphy_apply_custom_regulatory() should have this flag set + * or the regulatory core will set it for the wiphy. + * @REGULATORY_STRICT_REG: tells us the driver for this device will + * ignore regulatory domain settings until it gets its own regulatory + * domain via its regulatory_hint() unless the regulatory hint is + * from a country IE. After its gets its own regulatory domain it will + * only allow further regulatory domain settings to further enhance + * compliance. For example if channel 13 and 14 are disabled by this + * regulatory domain no user regulatory domain can enable these channels + * at a later time. This can be used for devices which do not have + * calibration information guaranteed for frequencies or settings + * outside of its regulatory domain. If used in combination with + * REGULATORY_FLAG_CUSTOM_REG the inspected country IE power settings + * will be followed. + * @REGULATORY_DISABLE_BEACON_HINTS: enable this if your driver needs to + * ensure that passive scan flags and beaconing flags may not be lifted by + * cfg80211 due to regulatory beacon hints. For more information on beacon + * hints read the documenation for regulatory_hint_found_beacon() + */ +enum ieee80211_regulatory_flags { + REGULATORY_CUSTOM_REG = BIT(0), + REGULATORY_STRICT_REG = BIT(1), + REGULATORY_DISABLE_BEACON_HINTS = BIT(2), +}; + struct ieee80211_freq_range { u32 start_freq_khz; u32 end_freq_khz; diff --git a/net/wireless/core.c b/net/wireless/core.c index 9dca0925a1a9..fc968c861ee4 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -586,7 +586,7 @@ int wiphy_register(struct wiphy *wiphy) if (IS_ERR(rdev->wiphy.debugfsdir)) rdev->wiphy.debugfsdir = NULL; - if (wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) { + if (wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) { struct regulatory_request request; request.wiphy_idx = get_wiphy_idx(wiphy); diff --git a/net/wireless/reg.c b/net/wireless/reg.c index a75c5eddd25f..e44b4bb20b92 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -868,7 +868,7 @@ static void handle_channel(struct wiphy *wiphy, if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && request_wiphy && request_wiphy == wiphy && - request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { + request_wiphy->regulatory_flags & REGULATORY_STRICT_REG) { REG_DBG_PRINT("Disabling freq %d MHz for good\n", chan->center_freq); chan->orig_flags |= IEEE80211_CHAN_DISABLED; @@ -895,7 +895,7 @@ static void handle_channel(struct wiphy *wiphy, if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && request_wiphy && request_wiphy == wiphy && - request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { + request_wiphy->regulatory_flags & REGULATORY_STRICT_REG) { /* * This guarantees the driver's requested regulatory domain * will always be used as a base for further regulatory @@ -922,12 +922,12 @@ static void handle_channel(struct wiphy *wiphy, if (chan->orig_mpwr) { /* * Devices that have their own custom regulatory domain - * but also use WIPHY_FLAG_STRICT_REGULATORY will follow the + * but also use REGULATORY_STRICT_REG will follow the * passed country IE power settings. */ if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && - wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY && - wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) + wiphy->regulatory_flags & REGULATORY_CUSTOM_REG && + wiphy->regulatory_flags & REGULATORY_STRICT_REG) chan->max_power = chan->max_reg_power; else chan->max_power = min(chan->orig_mpwr, @@ -997,8 +997,8 @@ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) static bool wiphy_strict_alpha2_regd(struct wiphy *wiphy) { - if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && - !(wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY)) + if (wiphy->regulatory_flags & REGULATORY_STRICT_REG && + !(wiphy->regulatory_flags & REGULATORY_CUSTOM_REG)) return true; return false; } @@ -1016,7 +1016,7 @@ static bool ignore_reg_update(struct wiphy *wiphy, } if (initiator == NL80211_REGDOM_SET_BY_CORE && - wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) { + wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) { REG_DBG_PRINT("Ignoring regulatory request set by %s " "since the driver uses its own custom " "regulatory domain\n", @@ -1054,7 +1054,7 @@ static bool reg_is_world_roaming(struct wiphy *wiphy) return true; if (lr && lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && - wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) + wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) return true; return false; @@ -1082,7 +1082,7 @@ static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx, if (!reg_is_world_roaming(wiphy)) return; - if (wiphy->flags & WIPHY_FLAG_DISABLE_BEACON_HINTS) + if (wiphy->regulatory_flags & REGULATORY_DISABLE_BEACON_HINTS) return; chan_before.center_freq = chan->center_freq; @@ -1242,7 +1242,7 @@ static void wiphy_update_regulatory(struct wiphy *wiphy, * as some drivers used this to restore its orig_* reg domain. */ if (initiator == NL80211_REGDOM_SET_BY_CORE && - wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) + wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) reg_call_notifier(wiphy, lr); return; } @@ -1328,9 +1328,9 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, enum ieee80211_band band; unsigned int bands_set = 0; - WARN(!(wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY), - "wiphy should have WIPHY_FLAG_CUSTOM_REGULATORY\n"); - wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; + WARN(!(wiphy->regulatory_flags & REGULATORY_CUSTOM_REG), + "wiphy should have REGULATORY_CUSTOM_REG\n"); + wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (!wiphy->bands[band]) @@ -1659,7 +1659,7 @@ static void reg_process_hint(struct regulatory_request *reg_request) /* This is required so that the orig_* parameters are saved */ if (treatment == REG_REQ_ALREADY_SET && wiphy && - wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) + wiphy->regulatory_flags & REGULATORY_STRICT_REG) wiphy_update_regulatory(wiphy, reg_request->initiator); } @@ -1986,7 +1986,7 @@ static void restore_regulatory_settings(bool reset_user) world_alpha2[1] = cfg80211_world_regdom->alpha2[1]; list_for_each_entry(rdev, &cfg80211_rdev_list, list) { - if (rdev->wiphy.flags & WIPHY_FLAG_CUSTOM_REGULATORY) + if (rdev->wiphy.regulatory_flags & REGULATORY_CUSTOM_REG) restore_custom_reg_settings(&rdev->wiphy); } -- cgit v1.2.3 From a09a85a013523a8b572dc5732b5c30e0785195f3 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 11 Nov 2013 22:15:30 +0100 Subject: cfg80211: add flags to define country IE processing rules 802.11 cards may have different country IE parsing behavioural preferences and vendors may want to support these. These preferences were managed by the REGULATORY_CUSTOM_REG and the REGULATORY_STRICT_REG flags and their combination. Instead of using this existing notation, split out the country IE behavioural preferences as a new flag. This will allow us to add more customizations easily and make the code more maintainable. Cc: Mihir Shete Cc: Henri Bahini Cc: Tushnim Bhattacharyya Signed-off-by: Luis R. Rodriguez [fix up conflicts] Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/regd.c | 3 ++- include/net/regulatory.h | 36 ++++++++++++++++++++++++------------ net/wireless/reg.c | 8 +++----- 3 files changed, 29 insertions(+), 18 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index aba782cd09a2..bef5539192c6 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -609,7 +609,8 @@ ath_regd_init_wiphy(struct ath_regulatory *reg, * saved on the wiphy orig_* parameters */ regd = ath_world_regdomain(reg); - wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; + wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | + REGULATORY_COUNTRY_IE_FOLLOW_POWER; } else { /* * This gets applied in the case of the absence of CRDA, diff --git a/include/net/regulatory.h b/include/net/regulatory.h index a6a20e2a54c4..b03ddee3341d 100644 --- a/include/net/regulatory.h +++ b/include/net/regulatory.h @@ -96,27 +96,39 @@ struct regulatory_request { * initiator is %REGDOM_SET_BY_CORE). Drivers that use * wiphy_apply_custom_regulatory() should have this flag set * or the regulatory core will set it for the wiphy. - * @REGULATORY_STRICT_REG: tells us the driver for this device will - * ignore regulatory domain settings until it gets its own regulatory - * domain via its regulatory_hint() unless the regulatory hint is - * from a country IE. After its gets its own regulatory domain it will - * only allow further regulatory domain settings to further enhance - * compliance. For example if channel 13 and 14 are disabled by this - * regulatory domain no user regulatory domain can enable these channels - * at a later time. This can be used for devices which do not have - * calibration information guaranteed for frequencies or settings - * outside of its regulatory domain. If used in combination with - * REGULATORY_FLAG_CUSTOM_REG the inspected country IE power settings - * will be followed. + * @REGULATORY_STRICT_REG: tells us that the wiphy for this device + * has regulatory domain that it wishes to be considered as the + * superset for regulatory rules. After this device gets its regulatory + * domain programmed further regulatory hints shall only be considered + * for this device to enhance regulatory compliance, forcing the + * device to only possibly use subsets of the original regulatory + * rules. For example if channel 13 and 14 are disabled by this + * device's regulatory domain no user specified regulatory hint which + * has these channels enabled would enable them for this wiphy, + * the device's original regulatory domain will be trusted as the + * base. You can program the superset of regulatory rules for this + * wiphy with regulatory_hint() for cards programmed with an + * ISO3166-alpha2 country code. wiphys that use regulatory_hint() + * will have their wiphy->regd programmed once the regulatory + * domain is set, and all other regulatory hints will be ignored + * until their own regulatory domain gets programmed. * @REGULATORY_DISABLE_BEACON_HINTS: enable this if your driver needs to * ensure that passive scan flags and beaconing flags may not be lifted by * cfg80211 due to regulatory beacon hints. For more information on beacon * hints read the documenation for regulatory_hint_found_beacon() + * @REGULATORY_COUNTRY_IE_FOLLOW_POWER: for devices that have a preference + * that even though they may have programmed their own custom power + * setting prior to wiphy registration, they want to ensure their channel + * power settings are updated for this connection with the power settings + * derived from the regulatory domain. The regulatory domain used will be + * based on the ISO3166-alpha2 from country IE provided through + * regulatory_hint_country_ie() */ enum ieee80211_regulatory_flags { REGULATORY_CUSTOM_REG = BIT(0), REGULATORY_STRICT_REG = BIT(1), REGULATORY_DISABLE_BEACON_HINTS = BIT(2), + REGULATORY_COUNTRY_IE_FOLLOW_POWER = BIT(3), }; struct ieee80211_freq_range { diff --git a/net/wireless/reg.c b/net/wireless/reg.c index e44b4bb20b92..6b3051f6a8a7 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -921,13 +921,11 @@ static void handle_channel(struct wiphy *wiphy, chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp); if (chan->orig_mpwr) { /* - * Devices that have their own custom regulatory domain - * but also use REGULATORY_STRICT_REG will follow the - * passed country IE power settings. + * Devices that use REGULATORY_COUNTRY_IE_FOLLOW_POWER + * will always follow the passed country IE power settings. */ if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && - wiphy->regulatory_flags & REGULATORY_CUSTOM_REG && - wiphy->regulatory_flags & REGULATORY_STRICT_REG) + wiphy->regulatory_flags & REGULATORY_COUNTRY_IE_FOLLOW_POWER) chan->max_power = chan->max_reg_power; else chan->max_power = min(chan->orig_mpwr, -- cgit v1.2.3 From 2a901468c221e778af52603e006a53d286e81f90 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 11 Nov 2013 22:15:31 +0100 Subject: cfg80211: add an option to disable processing country IEs Certain vendors may want to disable the processing of country IEs so that they can continue using the regulatory domain the driver or user has set. Currently there is no way to stop the core from processing country IEs, so add support to the core to ignore country IE hints. Cc: Mihir Shete Cc: Henri Bahini Cc: Tushnim Bhattacharyya Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- include/net/regulatory.h | 5 +++++ net/wireless/reg.c | 3 +++ 2 files changed, 8 insertions(+) (limited to 'include/net') diff --git a/include/net/regulatory.h b/include/net/regulatory.h index b03ddee3341d..92ab80f69efe 100644 --- a/include/net/regulatory.h +++ b/include/net/regulatory.h @@ -123,12 +123,17 @@ struct regulatory_request { * derived from the regulatory domain. The regulatory domain used will be * based on the ISO3166-alpha2 from country IE provided through * regulatory_hint_country_ie() + * @REGULATORY_COUNTRY_IE_IGNORE: for devices that have a preference to ignore + * all country IE information processed by the regulatory core. This will + * override %REGULATORY_COUNTRY_IE_FOLLOW_POWER as all country IEs will + * be ignored. */ enum ieee80211_regulatory_flags { REGULATORY_CUSTOM_REG = BIT(0), REGULATORY_STRICT_REG = BIT(1), REGULATORY_DISABLE_BEACON_HINTS = BIT(2), REGULATORY_COUNTRY_IE_FOLLOW_POWER = BIT(3), + REGULATORY_COUNTRY_IE_IGNORE = BIT(4), }; struct ieee80211_freq_range { diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 6b3051f6a8a7..067c1f63a1ae 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1543,6 +1543,9 @@ __reg_process_hint_country_ie(struct wiphy *wiphy, if (regdom_changes(country_ie_request->alpha2)) return REG_REQ_IGNORE; return REG_REQ_ALREADY_SET; + } else { + if (wiphy->regulatory_flags & REGULATORY_COUNTRY_IE_IGNORE) + return REG_REQ_IGNORE; } if (unlikely(!is_an_alpha2(country_ie_request->alpha2))) -- cgit v1.2.3 From 21f659bf1f93f7052b977d95cca560f02dc2edce Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 11 Nov 2013 20:14:01 +0200 Subject: mac80211: add min required channel definition field Add a new field to ieee80211_chanctx_conf to indicate the min required channel configuration. Tuning to a narrower channel might help reducing the noise level and saving some power. The min required channel definition is the max of all min required channel definitions of the interfaces bound to this channel context. In AP mode, use 20MHz when there are no connected station. When a new station is added/removed, calculate the new max bandwidth supported by any of the stations (e.g. 80MHz when 80MHz and 40MHz stations are connected). In other cases, simply use bss_conf.chandef as the min required chandef. Notify drivers about changes to this field by calling drv_change_chanctx with a new CHANGE_MIN_WIDTH notification. Signed-off-by: Eliad Peller Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- include/net/mac80211.h | 4 ++ net/mac80211/chan.c | 139 +++++++++++++++++++++++++++++++++++++++++++++ net/mac80211/ieee80211_i.h | 3 + net/mac80211/sta_info.c | 2 + net/mac80211/trace.h | 21 ++++++- net/mac80211/util.c | 20 +++++++ 6 files changed, 187 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index cca02b2f5c7e..3cd408b326de 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -154,12 +154,14 @@ struct ieee80211_low_level_stats { * @IEEE80211_CHANCTX_CHANGE_RADAR: radar detection flag changed * @IEEE80211_CHANCTX_CHANGE_CHANNEL: switched to another operating channel, * this is used only with channel switching with CSA + * @IEEE80211_CHANCTX_CHANGE_MIN_WIDTH: The min required channel width changed */ enum ieee80211_chanctx_change { IEEE80211_CHANCTX_CHANGE_WIDTH = BIT(0), IEEE80211_CHANCTX_CHANGE_RX_CHAINS = BIT(1), IEEE80211_CHANCTX_CHANGE_RADAR = BIT(2), IEEE80211_CHANCTX_CHANGE_CHANNEL = BIT(3), + IEEE80211_CHANCTX_CHANGE_MIN_WIDTH = BIT(4), }; /** @@ -169,6 +171,7 @@ enum ieee80211_chanctx_change { * that contains it is visible in mac80211 only. * * @def: the channel definition + * @min_def: the minimum channel definition currently required. * @rx_chains_static: The number of RX chains that must always be * active on the channel to receive MIMO transmissions * @rx_chains_dynamic: The number of RX chains that must be enabled @@ -180,6 +183,7 @@ enum ieee80211_chanctx_change { */ struct ieee80211_chanctx_conf { struct cfg80211_chan_def def; + struct cfg80211_chan_def min_def; u8 rx_chains_static, rx_chains_dynamic; diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index cefee2be0b8e..a57d5d9466bc 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -9,6 +9,140 @@ #include "ieee80211_i.h" #include "driver-ops.h" +static enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta) +{ + switch (sta->bandwidth) { + case IEEE80211_STA_RX_BW_20: + if (sta->ht_cap.ht_supported) + return NL80211_CHAN_WIDTH_20; + else + return NL80211_CHAN_WIDTH_20_NOHT; + case IEEE80211_STA_RX_BW_40: + return NL80211_CHAN_WIDTH_40; + case IEEE80211_STA_RX_BW_80: + return NL80211_CHAN_WIDTH_80; + case IEEE80211_STA_RX_BW_160: + /* + * This applied for both 160 and 80+80. since we use + * the returned value to consider degradation of + * ctx->conf.min_def, we have to make sure to take + * the bigger one (NL80211_CHAN_WIDTH_160). + * Otherwise we might try degrading even when not + * needed, as the max required sta_bw returned (80+80) + * might be smaller than the configured bw (160). + */ + return NL80211_CHAN_WIDTH_160; + default: + WARN_ON(1); + return NL80211_CHAN_WIDTH_20; + } +} + +static enum nl80211_chan_width +ieee80211_get_max_required_bw(struct ieee80211_sub_if_data *sdata) +{ + enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT; + struct sta_info *sta; + + rcu_read_lock(); + list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) { + if (sdata != sta->sdata && + !(sta->sdata->bss && sta->sdata->bss == sdata->bss)) + continue; + + if (!sta->uploaded) + continue; + + max_bw = max(max_bw, ieee80211_get_sta_bw(&sta->sta)); + } + rcu_read_unlock(); + + return max_bw; +} + +static enum nl80211_chan_width +ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local, + struct ieee80211_chanctx_conf *conf) +{ + struct ieee80211_sub_if_data *sdata; + enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT; + + rcu_read_lock(); + list_for_each_entry_rcu(sdata, &local->interfaces, list) { + struct ieee80211_vif *vif = &sdata->vif; + enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20_NOHT; + + if (!ieee80211_sdata_running(sdata)) + continue; + + if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf) + continue; + + switch (vif->type) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + width = ieee80211_get_max_required_bw(sdata); + break; + case NL80211_IFTYPE_P2P_DEVICE: + continue; + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_WDS: + case NL80211_IFTYPE_MESH_POINT: + width = vif->bss_conf.chandef.width; + break; + case NL80211_IFTYPE_UNSPECIFIED: + case NUM_NL80211_IFTYPES: + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + WARN_ON_ONCE(1); + } + max_bw = max(max_bw, width); + } + rcu_read_unlock(); + + return max_bw; +} + +/* + * recalc the min required chan width of the channel context, which is + * the max of min required widths of all the interfaces bound to this + * channel context. + */ +void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx) +{ + enum nl80211_chan_width max_bw; + struct cfg80211_chan_def min_def; + + lockdep_assert_held(&local->chanctx_mtx); + + /* don't optimize 5MHz, 10MHz, and radar_enabled confs */ + if (ctx->conf.def.width == NL80211_CHAN_WIDTH_5 || + ctx->conf.def.width == NL80211_CHAN_WIDTH_10 || + ctx->conf.radar_enabled) { + ctx->conf.min_def = ctx->conf.def; + return; + } + + max_bw = ieee80211_get_chanctx_max_required_bw(local, &ctx->conf); + + /* downgrade chandef up to max_bw */ + min_def = ctx->conf.def; + while (min_def.width > max_bw) + ieee80211_chandef_downgrade(&min_def); + + if (cfg80211_chandef_identical(&ctx->conf.min_def, &min_def)) + return; + + ctx->conf.min_def = min_def; + if (!ctx->driver_present) + return; + + drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_MIN_WIDTH); +} + static void ieee80211_change_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *ctx, const struct cfg80211_chan_def *chandef) @@ -20,6 +154,7 @@ static void ieee80211_change_chanctx(struct ieee80211_local *local, ctx->conf.def = *chandef; drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH); + ieee80211_recalc_chanctx_min_def(local, ctx); if (!local->use_chanctx) { local->_oper_chandef = *chandef; @@ -93,6 +228,7 @@ ieee80211_new_chanctx(struct ieee80211_local *local, ctx->conf.rx_chains_dynamic = 1; ctx->mode = mode; ctx->conf.radar_enabled = ieee80211_is_radar_required(local); + ieee80211_recalc_chanctx_min_def(local, ctx); if (!local->use_chanctx) local->hw.conf.radar_enabled = ctx->conf.radar_enabled; @@ -179,6 +315,7 @@ static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata, ctx->refcount++; ieee80211_recalc_txpower(sdata); + ieee80211_recalc_chanctx_min_def(local, ctx); sdata->vif.bss_conf.idle = false; if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && @@ -243,6 +380,7 @@ static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata, ieee80211_recalc_chanctx_chantype(sdata->local, ctx); ieee80211_recalc_smps_chanctx(local, ctx); ieee80211_recalc_radar_chanctx(local, ctx); + ieee80211_recalc_chanctx_min_def(local, ctx); } } @@ -456,6 +594,7 @@ int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata, ieee80211_recalc_chanctx_chantype(local, ctx); ieee80211_recalc_smps_chanctx(local, ctx); ieee80211_recalc_radar_chanctx(local, ctx); + ieee80211_recalc_chanctx_min_def(local, ctx); ret = 0; out: diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 0faadf15cd5c..4022ee1afb9f 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1693,6 +1693,7 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata, int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata, enum ieee80211_smps_mode smps_mode); void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata); +void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata); size_t ieee80211_ie_split(const u8 *ies, size_t ielen, const u8 *ids, int n_ids, size_t offset); @@ -1741,6 +1742,8 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *chanctx); void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *chanctx); +void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx); void ieee80211_dfs_cac_timer(unsigned long data); void ieee80211_dfs_cac_timer_work(struct work_struct *work); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 45b5f610674e..7b69d4c3db55 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -507,6 +507,7 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) set_sta_flag(sta, WLAN_STA_INSERTED); + ieee80211_recalc_min_chandef(sdata); ieee80211_sta_debugfs_add(sta); rate_control_add_sta_debugfs(sta); @@ -869,6 +870,7 @@ int __must_check __sta_info_destroy(struct sta_info *sta) rate_control_remove_sta_debugfs(sta); ieee80211_sta_debugfs_remove(sta); + ieee80211_recalc_min_chandef(sdata); call_rcu(&sta->rcu_head, free_sta_rcu); diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 5d62c5804819..8db560190ca6 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -41,14 +41,31 @@ #define CHANDEF_PR_ARG __entry->control_freq, __entry->chan_width, \ __entry->center_freq1, __entry->center_freq2 +#define MIN_CHANDEF_ENTRY \ + __field(u32, min_control_freq) \ + __field(u32, min_chan_width) \ + __field(u32, min_center_freq1) \ + __field(u32, min_center_freq2) + +#define MIN_CHANDEF_ASSIGN(c) \ + __entry->min_control_freq = (c)->chan ? (c)->chan->center_freq : 0; \ + __entry->min_chan_width = (c)->width; \ + __entry->min_center_freq1 = (c)->center_freq1; \ + __entry->min_center_freq2 = (c)->center_freq2; +#define MIN_CHANDEF_PR_FMT " min_control:%d MHz min_width:%d min_center: %d/%d MHz" +#define MIN_CHANDEF_PR_ARG __entry->min_control_freq, __entry->min_chan_width, \ + __entry->min_center_freq1, __entry->min_center_freq2 + #define CHANCTX_ENTRY CHANDEF_ENTRY \ + MIN_CHANDEF_ENTRY \ __field(u8, rx_chains_static) \ __field(u8, rx_chains_dynamic) #define CHANCTX_ASSIGN CHANDEF_ASSIGN(&ctx->conf.def) \ + MIN_CHANDEF_ASSIGN(&ctx->conf.min_def) \ __entry->rx_chains_static = ctx->conf.rx_chains_static; \ __entry->rx_chains_dynamic = ctx->conf.rx_chains_dynamic -#define CHANCTX_PR_FMT CHANDEF_PR_FMT " chains:%d/%d" -#define CHANCTX_PR_ARG CHANDEF_PR_ARG, \ +#define CHANCTX_PR_FMT CHANDEF_PR_FMT MIN_CHANDEF_PR_FMT " chains:%d/%d" +#define CHANCTX_PR_ARG CHANDEF_PR_ARG, MIN_CHANDEF_PR_ARG, \ __entry->rx_chains_static, __entry->rx_chains_dynamic diff --git a/net/mac80211/util.c b/net/mac80211/util.c index bb92f8e0f84e..06265d7f8cc3 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1804,6 +1804,26 @@ void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata) mutex_unlock(&local->chanctx_mtx); } +void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_chanctx *chanctx; + + mutex_lock(&local->chanctx_mtx); + + chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf, + lockdep_is_held(&local->chanctx_mtx)); + + if (WARN_ON_ONCE(!chanctx_conf)) + goto unlock; + + chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); + ieee80211_recalc_chanctx_min_def(local, chanctx); + unlock: + mutex_unlock(&local->chanctx_mtx); +} + static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) { int i; -- cgit v1.2.3 From 4c7d3982a6e37831382b9ef90aa0dbadc0bf3a22 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 13 Nov 2013 18:54:02 +0100 Subject: cfg80211: use enum nl80211_dfs_regions for dfs_region everywhere u8 was used in some other places, just stick to the enum, this forces us to express the values that are expected. Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- include/net/regulatory.h | 4 ++-- net/wireless/nl80211.c | 2 +- net/wireless/reg.c | 4 ++-- net/wireless/reg.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include/net') diff --git a/include/net/regulatory.h b/include/net/regulatory.h index 92ab80f69efe..c96a0b86f342 100644 --- a/include/net/regulatory.h +++ b/include/net/regulatory.h @@ -79,7 +79,7 @@ struct regulatory_request { enum nl80211_reg_initiator initiator; enum nl80211_user_reg_hint_type user_reg_hint_type; char alpha2[2]; - u8 dfs_region; + enum nl80211_dfs_regions dfs_region; bool intersect; bool processed; enum environment_cap country_ie_env; @@ -157,7 +157,7 @@ struct ieee80211_regdomain { struct rcu_head rcu_head; u32 n_reg_rules; char alpha2[2]; - u8 dfs_region; + enum nl80211_dfs_regions dfs_region; struct ieee80211_reg_rule reg_rules[]; }; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 7b73132910b7..79632edebb6c 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5079,7 +5079,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) char *alpha2 = NULL; int rem_reg_rules = 0, r = 0; u32 num_rules = 0, rule_idx = 0, size_of_regd; - u8 dfs_region = 0; + enum nl80211_dfs_regions dfs_region = NL80211_DFS_UNSET; struct ieee80211_regdomain *rd = NULL; if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 067c1f63a1ae..2796b622890f 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2115,7 +2115,7 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd) } } -bool reg_supported_dfs_region(u8 dfs_region) +bool reg_supported_dfs_region(enum nl80211_dfs_regions dfs_region) { switch (dfs_region) { case NL80211_DFS_UNSET: @@ -2130,7 +2130,7 @@ bool reg_supported_dfs_region(u8 dfs_region) } } -static void print_dfs_region(u8 dfs_region) +static void print_dfs_region(enum nl80211_dfs_regions dfs_region) { if (!dfs_region) return; diff --git a/net/wireless/reg.h b/net/wireless/reg.h index b4076babaf47..cc4c2c0a6723 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -20,7 +20,7 @@ extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain; bool reg_is_valid_request(const char *alpha2); bool is_world_regdom(const char *alpha2); -bool reg_supported_dfs_region(u8 dfs_region); +bool reg_supported_dfs_region(enum nl80211_dfs_regions dfs_region); int regulatory_hint_user(const char *alpha2, enum nl80211_user_reg_hint_type user_reg_hint_type); -- cgit v1.2.3 From b176e629402f41f2b984d3aa842ddae23ed5562e Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Mon, 18 Nov 2013 19:06:49 +0200 Subject: cfg80211: aggregate mgmt_tx parameters into a struct Change cfg80211 and mac80211 to use cfg80211_mgmt_tx_params struct to aggregate parameters for mgmt_tx functions. This makes the functions' signatures less clumsy and allows less painful parameters extension. Signed-off-by: Andrei Otcheretianski [fix all other drivers] Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 9 ++++-- .../net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 7 +++-- drivers/net/wireless/mwifiex/cfg80211.c | 6 ++-- include/net/cfg80211.h | 28 +++++++++++++++++-- net/mac80211/cfg.c | 29 ++++++++++---------- net/wireless/core.h | 5 ++-- net/wireless/mlme.c | 12 +++----- net/wireless/nl80211.c | 32 +++++++++++----------- net/wireless/rdev-ops.h | 12 +++----- net/wireless/trace.h | 15 +++++----- 10 files changed, 86 insertions(+), 69 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 2437ad26949d..36dc61da8336 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -3169,12 +3169,15 @@ static bool ath6kl_is_p2p_go_ssid(const u8 *buf, size_t len) } static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, - struct ieee80211_channel *chan, bool offchan, - unsigned int wait, const u8 *buf, size_t len, - bool no_cck, bool dont_wait_for_ack, u64 *cookie) + struct cfg80211_mgmt_tx_params *params, u64 *cookie) { struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev); struct ath6kl *ar = ath6kl_priv(vif->ndev); + struct ieee80211_channel *chan = params->chan; + const u8 *buf = params->buf; + size_t len = params->len; + unsigned int wait = params->wait; + bool no_cck = params->no_cck; u32 id, freq; const struct ieee80211_mgmt *mgmt; bool more_data, queued; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 1850efa83cf8..f0bdfb120667 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -3973,11 +3973,12 @@ brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy, static int brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, - struct ieee80211_channel *chan, bool offchan, - unsigned int wait, const u8 *buf, size_t len, - bool no_cck, bool dont_wait_for_ack, u64 *cookie) + struct cfg80211_mgmt_tx_params *params, u64 *cookie) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct ieee80211_channel *chan = params->chan; + const u8 *buf = params->buf; + size_t len = params->len; const struct ieee80211_mgmt *mgmt; struct brcmf_cfg80211_vif *vif; s32 err = 0; diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index d6d1d91a26dc..1c8116d46845 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -184,10 +184,10 @@ mwifiex_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len) */ static int mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, - struct ieee80211_channel *chan, bool offchan, - unsigned int wait, const u8 *buf, size_t len, - bool no_cck, bool dont_wait_for_ack, u64 *cookie) + struct cfg80211_mgmt_tx_params *params, u64 *cookie) { + const u8 *buf = params->buf; + size_t len = params->len; struct sk_buff *skb; u16 pkt_len; const struct ieee80211_mgmt *mgmt; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index bacc5033f0b6..6c2bc329a900 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1943,6 +1943,29 @@ struct cfg80211_update_ft_ies_params { size_t ie_len; }; +/** + * struct cfg80211_mgmt_tx_params - mgmt tx parameters + * + * This structure provides information needed to transmit a mgmt frame + * + * @chan: channel to use + * @offchan: indicates wether off channel operation is required + * @wait: duration for ROC + * @buf: buffer to transmit + * @len: buffer length + * @no_cck: don't use cck rates for this frame + * @dont_wait_for_ack: tells the low level not to wait for an ack + */ +struct cfg80211_mgmt_tx_params { + struct ieee80211_channel *chan; + bool offchan; + unsigned int wait; + const u8 *buf; + size_t len; + bool no_cck; + bool dont_wait_for_ack; +}; + /** * struct cfg80211_ops - backend description for wireless configuration * @@ -2341,9 +2364,8 @@ struct cfg80211_ops { u64 cookie); int (*mgmt_tx)(struct wiphy *wiphy, struct wireless_dev *wdev, - struct ieee80211_channel *chan, bool offchan, - unsigned int wait, const u8 *buf, size_t len, - bool no_cck, bool dont_wait_for_ack, u64 *cookie); + struct cfg80211_mgmt_tx_params *params, + u64 *cookie); int (*mgmt_tx_cancel_wait)(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie); diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index c7b3e57aec04..267d3aca9947 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -3167,26 +3167,25 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, } static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, - struct ieee80211_channel *chan, bool offchan, - unsigned int wait, const u8 *buf, size_t len, - bool no_cck, bool dont_wait_for_ack, u64 *cookie) + struct cfg80211_mgmt_tx_params *params, + u64 *cookie) { struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct sta_info *sta; - const struct ieee80211_mgmt *mgmt = (void *)buf; + const struct ieee80211_mgmt *mgmt = (void *)params->buf; bool need_offchan = false; u32 flags; int ret; - if (dont_wait_for_ack) + if (params->dont_wait_for_ack) flags = IEEE80211_TX_CTL_NO_ACK; else flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX | IEEE80211_TX_CTL_REQ_TX_STATUS; - if (no_cck) + if (params->no_cck) flags |= IEEE80211_TX_CTL_NO_CCK_RATE; switch (sdata->vif.type) { @@ -3234,7 +3233,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, /* configurations requiring offchan cannot work if no channel has been * specified */ - if (need_offchan && !chan) + if (need_offchan && !params->chan) return -EINVAL; mutex_lock(&local->mtx); @@ -3247,8 +3246,10 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); if (chanctx_conf) { - need_offchan = chan && (chan != chanctx_conf->def.chan); - } else if (!chan) { + need_offchan = params->chan && + (params->chan != + chanctx_conf->def.chan); + } else if (!params->chan) { ret = -EINVAL; rcu_read_unlock(); goto out_unlock; @@ -3258,19 +3259,19 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, rcu_read_unlock(); } - if (need_offchan && !offchan) { + if (need_offchan && !params->offchan) { ret = -EBUSY; goto out_unlock; } - skb = dev_alloc_skb(local->hw.extra_tx_headroom + len); + skb = dev_alloc_skb(local->hw.extra_tx_headroom + params->len); if (!skb) { ret = -ENOMEM; goto out_unlock; } skb_reserve(skb, local->hw.extra_tx_headroom); - memcpy(skb_put(skb, len), buf, len); + memcpy(skb_put(skb, params->len), params->buf, params->len); IEEE80211_SKB_CB(skb)->flags = flags; @@ -3290,8 +3291,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, local->hw.offchannel_tx_hw_queue; /* This will handle all kinds of coalescing and immediate TX */ - ret = ieee80211_start_roc_work(local, sdata, chan, - wait, cookie, skb, + ret = ieee80211_start_roc_work(local, sdata, params->chan, + params->wait, cookie, skb, IEEE80211_ROC_TYPE_MGMT_TX); if (ret) kfree_skb(skb); diff --git a/net/wireless/core.h b/net/wireless/core.h index 2888867ee7c5..6716c5c3f748 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -317,9 +317,8 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid); void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev); int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, - struct ieee80211_channel *chan, bool offchan, - unsigned int wait, const u8 *buf, size_t len, - bool no_cck, bool dont_wait_for_ack, u64 *cookie); + struct cfg80211_mgmt_tx_params *params, + u64 *cookie); void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa, const struct ieee80211_ht_cap *ht_capa_mask); void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa, diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 31f541f7e4ea..52cca05044a8 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -520,9 +520,7 @@ void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev) int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, - struct ieee80211_channel *chan, bool offchan, - unsigned int wait, const u8 *buf, size_t len, - bool no_cck, bool dont_wait_for_ack, u64 *cookie) + struct cfg80211_mgmt_tx_params *params, u64 *cookie) { const struct ieee80211_mgmt *mgmt; u16 stype; @@ -533,10 +531,10 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, if (!rdev->ops->mgmt_tx) return -EOPNOTSUPP; - if (len < 24 + 1) + if (params->len < 24 + 1) return -EINVAL; - mgmt = (const struct ieee80211_mgmt *) buf; + mgmt = (const struct ieee80211_mgmt *)params->buf; if (!ieee80211_is_mgmt(mgmt->frame_control)) return -EINVAL; @@ -615,9 +613,7 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, return -EINVAL; /* Transmit the Action frame as requested by user space */ - return rdev_mgmt_tx(rdev, wdev, chan, offchan, - wait, buf, len, no_cck, dont_wait_for_ack, - cookie); + return rdev_mgmt_tx(rdev, wdev, params, cookie); } bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 8c83fbb3824f..703155b1aa7a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -7428,10 +7428,10 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) void *hdr = NULL; u64 cookie; struct sk_buff *msg = NULL; - unsigned int wait = 0; - bool offchan, no_cck, dont_wait_for_ack; - - dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK]; + struct cfg80211_mgmt_tx_params params = { + .dont_wait_for_ack = + info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK], + }; if (!info->attrs[NL80211_ATTR_FRAME]) return -EINVAL; @@ -7458,24 +7458,24 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_DURATION]) { if (!(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX)) return -EINVAL; - wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); + params.wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); /* * We should wait on the channel for at least a minimum amount * of time (10ms) but no longer than the driver supports. */ - if (wait < NL80211_MIN_REMAIN_ON_CHANNEL_TIME || - wait > rdev->wiphy.max_remain_on_channel_duration) + if (params.wait < NL80211_MIN_REMAIN_ON_CHANNEL_TIME || + params.wait > rdev->wiphy.max_remain_on_channel_duration) return -EINVAL; } - offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK]; + params.offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK]; - if (offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX)) + if (params.offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX)) return -EINVAL; - no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); + params.no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); /* get the channel if any has been specified, otherwise pass NULL to * the driver. The latter will use the current one @@ -7487,10 +7487,10 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) return err; } - if (!chandef.chan && offchan) + if (!chandef.chan && params.offchan) return -EINVAL; - if (!dont_wait_for_ack) { + if (!params.dont_wait_for_ack) { msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; @@ -7503,10 +7503,10 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) } } - err = cfg80211_mlme_mgmt_tx(rdev, wdev, chandef.chan, offchan, wait, - nla_data(info->attrs[NL80211_ATTR_FRAME]), - nla_len(info->attrs[NL80211_ATTR_FRAME]), - no_cck, dont_wait_for_ack, &cookie); + params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]); + params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]); + params.chan = chandef.chan; + err = cfg80211_mlme_mgmt_tx(rdev, wdev, ¶ms, &cookie); if (err) goto free_msg; diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 37ce9fdfe934..a6c03ab14a0d 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -624,16 +624,12 @@ rdev_cancel_remain_on_channel(struct cfg80211_registered_device *rdev, static inline int rdev_mgmt_tx(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, - struct ieee80211_channel *chan, bool offchan, - unsigned int wait, const u8 *buf, size_t len, - bool no_cck, bool dont_wait_for_ack, u64 *cookie) + struct cfg80211_mgmt_tx_params *params, + u64 *cookie) { int ret; - trace_rdev_mgmt_tx(&rdev->wiphy, wdev, chan, offchan, - wait, no_cck, dont_wait_for_ack); - ret = rdev->ops->mgmt_tx(&rdev->wiphy, wdev, chan, offchan, - wait, buf, len, no_cck, - dont_wait_for_ack, cookie); + trace_rdev_mgmt_tx(&rdev->wiphy, wdev, params); + ret = rdev->ops->mgmt_tx(&rdev->wiphy, wdev, params, cookie); trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie); return ret; } diff --git a/net/wireless/trace.h b/net/wireless/trace.h index ba5f0d6614d5..f7aa7a72d9bc 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1653,9 +1653,8 @@ TRACE_EVENT(rdev_cancel_remain_on_channel, TRACE_EVENT(rdev_mgmt_tx, TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, - struct ieee80211_channel *chan, bool offchan, - unsigned int wait, bool no_cck, bool dont_wait_for_ack), - TP_ARGS(wiphy, wdev, chan, offchan, wait, no_cck, dont_wait_for_ack), + struct cfg80211_mgmt_tx_params *params), + TP_ARGS(wiphy, wdev, params), TP_STRUCT__entry( WIPHY_ENTRY WDEV_ENTRY @@ -1668,11 +1667,11 @@ TRACE_EVENT(rdev_mgmt_tx, TP_fast_assign( WIPHY_ASSIGN; WDEV_ASSIGN; - CHAN_ASSIGN(chan); - __entry->offchan = offchan; - __entry->wait = wait; - __entry->no_cck = no_cck; - __entry->dont_wait_for_ack = dont_wait_for_ack; + CHAN_ASSIGN(params->chan); + __entry->offchan = params->offchan; + __entry->wait = params->wait; + __entry->no_cck = params->no_cck; + __entry->dont_wait_for_ack = params->dont_wait_for_ack; ), TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", " CHAN_PR_FMT ", offchan: %s," " wait: %u, no cck: %s, dont wait for ack: %s", -- cgit v1.2.3 From e487eaeb076a44c69dc61348cbc903151bb8fcbd Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Thu, 21 Nov 2013 18:19:51 +0100 Subject: cfg80211/mac80211/ath6kl: acquire wdev lock outside ch_switch_notify The channel switch notification should be sent under the wdev/sdata-lock, preferably in the same moment as the channel change happens, to avoid races by other callers (e.g. start/stop_ap). This also adds the previously missing sdata_lock protection in csa_finalize_work. Reported-by: Johannes Berg Signed-off-by: Simon Wunderlich Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 2 ++ include/net/cfg80211.h | 3 ++- net/mac80211/cfg.c | 21 +++++++++++++++------ net/wireless/nl80211.c | 9 +++------ 4 files changed, 22 insertions(+), 13 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 36dc61da8336..fd4c89df67e1 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1109,7 +1109,9 @@ void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq, (mode == WMI_11G_HT20) ? NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT); + mutex_lock(&vif->wdev.mtx); cfg80211_ch_switch_notify(vif->ndev, &chandef); + mutex_unlock(&vif->wdev.mtx); } static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 6c2bc329a900..e9abc7b536cd 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4286,7 +4286,8 @@ bool cfg80211_reg_can_beacon(struct wiphy *wiphy, * @dev: the device which switched channels * @chandef: the new channel definition * - * Acquires wdev_lock, so must only be called from sleepable driver context! + * Caller must acquire wdev_lock, therefore must only be called from sleepable + * driver context! */ void cfg80211_ch_switch_notify(struct net_device *dev, struct cfg80211_chan_def *chandef); diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 4a5c21ed64d1..1d446ac97ab5 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2988,13 +2988,18 @@ void ieee80211_csa_finalize_work(struct work_struct *work) struct ieee80211_local *local = sdata->local; int err, changed = 0; + sdata_lock(sdata); + /* AP might have been stopped while waiting for the lock. */ + if (!sdata->vif.csa_active) + goto unlock; + if (!ieee80211_sdata_running(sdata)) - return; + goto unlock; sdata->radar_required = sdata->csa_radar_required; err = ieee80211_vif_change_channel(sdata, &changed); if (WARN_ON(err < 0)) - return; + goto unlock; if (!local->use_chanctx) { local->_oper_chandef = sdata->csa_chandef; @@ -3003,11 +3008,13 @@ void ieee80211_csa_finalize_work(struct work_struct *work) ieee80211_bss_info_change_notify(sdata, changed); + sdata->vif.csa_active = false; switch (sdata->vif.type) { case NL80211_IFTYPE_AP: err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon); if (err < 0) - return; + goto unlock; + changed |= err; kfree(sdata->u.ap.next_beacon); sdata->u.ap.next_beacon = NULL; @@ -3021,20 +3028,22 @@ void ieee80211_csa_finalize_work(struct work_struct *work) case NL80211_IFTYPE_MESH_POINT: err = ieee80211_mesh_finish_csa(sdata); if (err < 0) - return; + goto unlock; break; #endif default: WARN_ON(1); - return; + goto unlock; } - sdata->vif.csa_active = false; ieee80211_wake_queues_by_reason(&sdata->local->hw, IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_CSA); cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef); + +unlock: + sdata_unlock(sdata); } static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 398756c226c3..95882a788b5b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -10821,21 +10821,18 @@ void cfg80211_ch_switch_notify(struct net_device *dev, struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - trace_cfg80211_ch_switch_notify(dev, chandef); + ASSERT_WDEV_LOCK(wdev); - wdev_lock(wdev); + trace_cfg80211_ch_switch_notify(dev, chandef); if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && wdev->iftype != NL80211_IFTYPE_P2P_GO && wdev->iftype != NL80211_IFTYPE_ADHOC && wdev->iftype != NL80211_IFTYPE_MESH_POINT)) - goto out; + return; wdev->channel = chandef->chan; nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL); -out: - wdev_unlock(wdev); - return; } EXPORT_SYMBOL(cfg80211_ch_switch_notify); -- cgit v1.2.3 From 18cfd3bfc904e2360af3a1c059c84b3f1944afb4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 3 Dec 2013 13:21:36 +0100 Subject: Revert "mac80211: add driver callback for per-interface multicast filter" This reverts commit 488b366a452934141959384c7a1b52b22d6154ef. The code isn't used by anyone, and the Intel driver isn't planning to use it either right now. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 7 ------- net/mac80211/driver-ops.h | 16 ---------------- net/mac80211/iface.c | 11 ----------- net/mac80211/trace.h | 24 ------------------------ 4 files changed, 58 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 3cd408b326de..73d99bc3e636 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2398,9 +2398,6 @@ enum ieee80211_roc_type { * See the section "Frame filtering" for more information. * This callback must be implemented and can sleep. * - * @set_multicast_list: Configure the device's interface specific RX multicast - * filter. This callback is optional. This callback must be atomic. - * * @set_tim: Set TIM bit. mac80211 calls this function when a TIM bit * must be set or cleared for a given STA. Must be atomic. * @@ -2764,10 +2761,6 @@ struct ieee80211_ops { unsigned int changed_flags, unsigned int *total_flags, u64 multicast); - void (*set_multicast_list)(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, bool allmulti, - struct netdev_hw_addr_list *mc_list); - int (*set_tim)(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set); int (*set_key)(struct ieee80211_hw *hw, enum set_key_cmd cmd, diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 5d03c47c0a4c..f98059a45aa5 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -242,22 +242,6 @@ static inline u64 drv_prepare_multicast(struct ieee80211_local *local, return ret; } -static inline void drv_set_multicast_list(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata, - struct netdev_hw_addr_list *mc_list) -{ - bool allmulti = sdata->flags & IEEE80211_SDATA_ALLMULTI; - - trace_drv_set_multicast_list(local, sdata, mc_list->count); - - check_sdata_in_driver(sdata); - - if (local->ops->set_multicast_list) - local->ops->set_multicast_list(&local->hw, &sdata->vif, - allmulti, mc_list); - trace_drv_return_void(local); -} - static inline void drv_configure_filter(struct ieee80211_local *local, unsigned int changed_flags, unsigned int *total_flags, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index d226751ba63a..3e5c89a295b9 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1018,17 +1018,6 @@ static void ieee80211_set_multicast_list(struct net_device *dev) atomic_dec(&local->iff_promiscs); sdata->flags ^= IEEE80211_SDATA_PROMISC; } - - /* - * TODO: If somebody needs this on AP interfaces, - * it can be enabled easily but multicast - * addresses from VLANs need to be synced. - */ - if (sdata->vif.type != NL80211_IFTYPE_MONITOR && - sdata->vif.type != NL80211_IFTYPE_AP_VLAN && - sdata->vif.type != NL80211_IFTYPE_AP) - drv_set_multicast_list(local, sdata, &dev->mc); - spin_lock_bh(&local->filter_lock); __hw_addr_sync(&local->mc_list, &dev->mc, dev->addr_len); spin_unlock_bh(&local->filter_lock); diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 8db560190ca6..9d213e82326e 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -443,30 +443,6 @@ TRACE_EVENT(drv_prepare_multicast, ) ); -TRACE_EVENT(drv_set_multicast_list, - TP_PROTO(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata, int mc_count), - - TP_ARGS(local, sdata, mc_count), - - TP_STRUCT__entry( - LOCAL_ENTRY - __field(bool, allmulti) - __field(int, mc_count) - ), - - TP_fast_assign( - LOCAL_ASSIGN; - __entry->allmulti = sdata->flags & IEEE80211_SDATA_ALLMULTI; - __entry->mc_count = mc_count; - ), - - TP_printk( - LOCAL_PR_FMT " configure mc filter, count=%d, allmulti=%d", - LOCAL_PR_ARG, __entry->mc_count, __entry->allmulti - ) -); - TRACE_EVENT(drv_configure_filter, TP_PROTO(struct ieee80211_local *local, unsigned int changed_flags, -- cgit v1.2.3 From 60f4a7b1676c9028edb90e22f6994ebb698c9088 Mon Sep 17 00:00:00 2001 From: Marek Kwaczynski Date: Tue, 3 Dec 2013 10:04:59 +0100 Subject: nl80211/cfg80211: Set Operating Mode Notification This attribute is needed for setting Operating Mode Notification in AP mode from User Space. This functionality is required when User Space received Assoc Request contains Operation Mode Notification element. Signed-off-by: Marek Kwaczynski [fix typos, nl80211 documentation] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 4 ++++ include/uapi/linux/nl80211.h | 6 ++++++ net/wireless/nl80211.c | 7 +++++++ 3 files changed, 17 insertions(+) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e9abc7b536cd..47290a4059ae 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -747,6 +747,8 @@ enum station_parameters_apply_mask { * @supported_channels_len: number of supported channels * @supported_oper_classes: supported oper classes in IEEE 802.11 format * @supported_oper_classes_len: number of supported operating classes + * @opmode_notif: operating mode field from Operating Mode Notification + * @opmode_notif_used: information if operating mode field is used */ struct station_parameters { const u8 *supported_rates; @@ -770,6 +772,8 @@ struct station_parameters { u8 supported_channels_len; const u8 *supported_oper_classes; u8 supported_oper_classes_len; + u8 opmode_notif; + bool opmode_notif_used; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 129b7b087148..9a4d0e18251c 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1520,6 +1520,10 @@ enum nl80211_commands { * @NL80211_ATTR_SUPPORT_10_MHZ: A flag indicating that the device supports * 10 MHz channel bandwidth. * + * @NL80211_ATTR_OPMODE_NOTIF: Operating mode field from Operating Mode + * Notification Element based on association request when used with + * %NL80211_CMD_NEW_STATION; u8 attribute. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1839,6 +1843,8 @@ enum nl80211_attrs { NL80211_ATTR_SUPPORT_5_MHZ, NL80211_ATTR_SUPPORT_10_MHZ, + NL80211_ATTR_OPMODE_NOTIF, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 95882a788b5b..93e3356091ff 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -357,6 +357,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY }, [NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY }, [NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG }, + [NL80211_ATTR_OPMODE_NOTIF] = { .type = NLA_U8 }, }; /* policy for the key attributes */ @@ -4132,6 +4133,12 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) params.vht_capa = nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]); + if (info->attrs[NL80211_ATTR_OPMODE_NOTIF]) { + params.opmode_notif_used = true; + params.opmode_notif = + nla_get_u8(info->attrs[NL80211_ATTR_OPMODE_NOTIF]); + } + if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) { params.plink_action = nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); -- cgit v1.2.3 From ad7e718c9b4f717823fd920a0103f7b0fb06183f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 13 Nov 2013 13:37:47 +0100 Subject: nl80211: vendor command support Add support for vendor-specific commands to nl80211. This is intended to be used for really vendor-specific functionality that can't be implemented in a generic fashion for any reason. It's *NOT* intended to be used for any normal/generic feature or any optimisations that could be implemented across drivers. Currently, only vendor commands (with replies) are supported, no dump operations or vendor-specific notifications. Also add a function wdev_to_ieee80211_vif() to mac80211 which is needed for mac80211-based drivers wanting to implement any vendor commands. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 116 ++++++++++++++++++++- include/net/mac80211.h | 13 +++ include/uapi/linux/nl80211.h | 41 ++++++++ net/mac80211/util.c | 11 ++ net/wireless/core.h | 4 +- net/wireless/nl80211.c | 237 ++++++++++++++++++++++++++++++------------- 6 files changed, 348 insertions(+), 74 deletions(-) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 47290a4059ae..884ac69b5e55 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2679,6 +2679,34 @@ struct wiphy_coalesce_support { int max_pkt_offset; }; +/** + * enum wiphy_vendor_command_flags - validation flags for vendor commands + * @WIPHY_VENDOR_CMD_NEED_WDEV: vendor command requires wdev + * @WIPHY_VENDOR_CMD_NEED_NETDEV: vendor command requires netdev + * @WIPHY_VENDOR_CMD_NEED_RUNNING: interface/wdev must be up & running + * (must be combined with %_WDEV or %_NETDEV) + */ +enum wiphy_vendor_command_flags { + WIPHY_VENDOR_CMD_NEED_WDEV = BIT(0), + WIPHY_VENDOR_CMD_NEED_NETDEV = BIT(1), + WIPHY_VENDOR_CMD_NEED_RUNNING = BIT(2), +}; + +/** + * struct wiphy_vendor_command - vendor command definition + * @info: vendor command identifying information, as used in nl80211 + * @flags: flags, see &enum wiphy_vendor_command_flags + * @doit: callback for the operation, note that wdev is %NULL if the + * flags didn't ask for a wdev and non-%NULL otherwise; the data + * pointer may be %NULL if userspace provided no data at all + */ +struct wiphy_vendor_command { + struct nl80211_vendor_cmd_info info; + u32 flags; + int (*doit)(struct wiphy *wiphy, struct wireless_dev *wdev, + const void *data, int data_len); +}; + /** * struct wiphy - wireless hardware description * @reg_notifier: the driver's regulatory notification callback, @@ -2792,6 +2820,9 @@ struct wiphy_coalesce_support { * @extended_capabilities_mask: mask of the valid values * @extended_capabilities_len: length of the extended capabilities * @coalesce: packet coalescing support information + * + * @vendor_commands: array of vendor commands supported by the hardware + * @n_vendor_commands: number of vendor commands */ struct wiphy { /* assign these fields before you register the wiphy */ @@ -2903,6 +2934,9 @@ struct wiphy { const struct wiphy_coalesce_support *coalesce; + const struct wiphy_vendor_command *vendor_commands; + int n_vendor_commands; + char priv[0] __aligned(NETDEV_ALIGN); }; @@ -3847,6 +3881,75 @@ void wiphy_rfkill_start_polling(struct wiphy *wiphy); */ void wiphy_rfkill_stop_polling(struct wiphy *wiphy); +/** + * DOC: Vendor commands + * + * Occasionally, there are special protocol or firmware features that + * can't be implemented very openly. For this and similar cases, the + * vendor command functionality allows implementing the features with + * (typically closed-source) userspace and firmware, using nl80211 as + * the configuration mechanism. + * + * A driver supporting vendor commands must register them as an array + * in struct wiphy, with handlers for each one, each command has an + * OUI and sub command ID to identify it. + * + * Note that this feature should not be (ab)used to implement protocol + * features that could openly be shared across drivers. In particular, + * it must never be required to use vendor commands to implement any + * "normal" functionality that higher-level userspace like connection + * managers etc. need. + */ + +struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy, + enum nl80211_commands cmd, + enum nl80211_attrs attr, + int approxlen); + +/** + * cfg80211_vendor_cmd_alloc_reply_skb - allocate vendor command reply + * @wiphy: the wiphy + * @approxlen: an upper bound of the length of the data that will + * be put into the skb + * + * This function allocates and pre-fills an skb for a reply to + * a vendor command. Since it is intended for a reply, calling + * it outside of a vendor command's doit() operation is invalid. + * + * The returned skb is pre-filled with some identifying data in + * a way that any data that is put into the skb (with skb_put(), + * nla_put() or similar) will end up being within the + * %NL80211_ATTR_VENDOR_DATA attribute, so all that needs to be done + * with the skb is adding data for the corresponding userspace tool + * which can then read that data out of the vendor data attribute. + * You must not modify the skb in any other way. + * + * When done, call cfg80211_vendor_cmd_reply() with the skb and return + * its error code as the result of the doit() operation. + * + * Return: An allocated and pre-filled skb. %NULL if any errors happen. + */ +static inline struct sk_buff * +cfg80211_vendor_cmd_alloc_reply_skb(struct wiphy *wiphy, int approxlen) +{ + return __cfg80211_alloc_reply_skb(wiphy, NL80211_CMD_VENDOR, + NL80211_ATTR_VENDOR_DATA, approxlen); +} + +/** + * cfg80211_vendor_cmd_reply - send the reply skb + * @skb: The skb, must have been allocated with + * cfg80211_vendor_cmd_alloc_reply_skb() + * + * Since calling this function will usually be the last thing + * before returning from the vendor command doit() you should + * return the error code. Note that this function consumes the + * skb regardless of the return value. + * + * Return: An error code or 0 on success. + */ +int cfg80211_vendor_cmd_reply(struct sk_buff *skb); + #ifdef CONFIG_NL80211_TESTMODE /** * DOC: Test mode @@ -3882,8 +3985,12 @@ void wiphy_rfkill_stop_polling(struct wiphy *wiphy); * * Return: An allocated and pre-filled skb. %NULL if any errors happen. */ -struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, - int approxlen); +static inline struct sk_buff * +cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, int approxlen) +{ + return __cfg80211_alloc_reply_skb(wiphy, NL80211_CMD_TESTMODE, + NL80211_ATTR_TESTDATA, approxlen); +} /** * cfg80211_testmode_reply - send the reply skb @@ -3897,7 +4004,10 @@ struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, * * Return: An error code or 0 on success. */ -int cfg80211_testmode_reply(struct sk_buff *skb); +static inline int cfg80211_testmode_reply(struct sk_buff *skb) +{ + return cfg80211_vendor_cmd_reply(skb); +} /** * cfg80211_testmode_alloc_event_skb - allocate testmode event diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 73d99bc3e636..c014acc09ebc 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1162,6 +1162,19 @@ static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif) return false; } +/** + * wdev_to_ieee80211_vif - return a vif struct from a wdev + * @wdev: the wdev to get the vif for + * + * This can be used by mac80211 drivers with direct cfg80211 APIs + * (like the vendor commands) that get a wdev. + * + * Note that this function may return %NULL if the given wdev isn't + * associated with a vif that the driver knows about (e.g. monitor + * or AP_VLAN interfaces.) + */ +struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev); + /** * enum ieee80211_key_flags - key flags * diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 9a4d0e18251c..72ba3584c90d 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -693,6 +693,15 @@ * other station that transmission must be blocked until the channel * switch is complete. * + * @NL80211_CMD_VENDOR: Vendor-specified command/event. The command is specified + * by the %NL80211_ATTR_VENDOR_ID attribute and a sub-command in + * %NL80211_ATTR_VENDOR_SUBCMD. Parameter(s) can be transported in + * %NL80211_ATTR_VENDOR_DATA. + * For feature advertisement, the %NL80211_ATTR_VENDOR_DATA attribute is + * used in the wiphy data as a nested attribute containing descriptions + * (&struct nl80211_vendor_cmd_info) of the supported vendor commands. + * This may also be sent as an event with the same attributes. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -860,6 +869,8 @@ enum nl80211_commands { NL80211_CMD_CHANNEL_SWITCH, + NL80211_CMD_VENDOR, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1524,6 +1535,12 @@ enum nl80211_commands { * Notification Element based on association request when used with * %NL80211_CMD_NEW_STATION; u8 attribute. * + * @NL80211_ATTR_VENDOR_ID: The vendor ID, either a 24-bit OUI or, if + * %NL80211_VENDOR_ID_IS_LINUX is set, a special Linux ID (not used yet) + * @NL80211_ATTR_VENDOR_SUBCMD: vendor sub-command + * @NL80211_ATTR_VENDOR_DATA: data for the vendor command, if any; this + * attribute is also used for vendor command feature advertisement + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1845,6 +1862,10 @@ enum nl80211_attrs { NL80211_ATTR_OPMODE_NOTIF, + NL80211_ATTR_VENDOR_ID, + NL80211_ATTR_VENDOR_SUBCMD, + NL80211_ATTR_VENDOR_DATA, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3965,4 +3986,24 @@ enum nl80211_rxmgmt_flags { NL80211_RXMGMT_FLAG_ANSWERED = 1 << 0, }; +/* + * If this flag is unset, the lower 24 bits are an OUI, if set + * a Linux nl80211 vendor ID is used (no such IDs are allocated + * yet, so that's not valid so far) + */ +#define NL80211_VENDOR_ID_IS_LINUX 0x80000000 + +/** + * struct nl80211_vendor_cmd_info - vendor command data + * @vendor_id: If the %NL80211_VENDOR_ID_IS_LINUX flag is clear, then the + * value is a 24-bit OUI; if it is set then a separately allocated ID + * may be used, but no such IDs are allocated yet. New IDs should be + * added to this file when needed. + * @subcmd: sub-command ID for the command + */ +struct nl80211_vendor_cmd_info { + __u32 vendor_id; + __u32 subcmd; +}; + #endif /* __LINUX_NL80211_H */ diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 06265d7f8cc3..4a376a724153 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -642,6 +642,17 @@ void ieee80211_iterate_active_interfaces_rtnl( } EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl); +struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); + + if (!ieee80211_sdata_running(sdata) || + !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) + return NULL; + return &sdata->vif; +} +EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif); + /* * Nothing should have been stuffed into the workqueue during * the suspend->resume cycle. If this WARN is seen then there diff --git a/net/wireless/core.h b/net/wireless/core.h index 6716c5c3f748..ac77644cf894 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -67,9 +67,7 @@ struct cfg80211_registered_device { struct work_struct scan_done_wk; struct work_struct sched_scan_results_wk; -#ifdef CONFIG_NL80211_TESTMODE - struct genl_info *testmode_info; -#endif + struct genl_info *cur_cmd_info; struct work_struct conn_work; struct work_struct event_work; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index bdcf256e3628..6989989de092 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -358,6 +358,9 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY }, [NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG }, [NL80211_ATTR_OPMODE_NOTIF] = { .type = NLA_U8 }, + [NL80211_ATTR_VENDOR_ID] = { .type = NLA_U32 }, + [NL80211_ATTR_VENDOR_SUBCMD] = { .type = NLA_U32 }, + [NL80211_ATTR_VENDOR_DATA] = { .type = NLA_BINARY }, }; /* policy for the key attributes */ @@ -1166,6 +1169,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, struct nlattr *nl_bands, *nl_band; struct nlattr *nl_freqs, *nl_freq; struct nlattr *nl_cmds; + struct nlattr *nl_vendor_cmds; enum ieee80211_band band; struct ieee80211_channel *chan; int i; @@ -1561,6 +1565,19 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, (nla_put_flag(msg, NL80211_ATTR_SUPPORT_5_MHZ) || nla_put_flag(msg, NL80211_ATTR_SUPPORT_10_MHZ))) goto nla_put_failure; + state->split_start++; + break; + case 11: + nl_vendor_cmds = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (!nl_vendor_cmds) + goto nla_put_failure; + + for (i = 0; i < dev->wiphy.n_vendor_commands; i++) + if (nla_put(msg, i + 1, + sizeof(struct nl80211_vendor_cmd_info), + &dev->wiphy.vendor_commands[i].info)) + goto nla_put_failure; + nla_nest_end(msg, nl_vendor_cmds); /* done */ state->split_start = 0; @@ -6682,6 +6699,40 @@ static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info) return err; } +static struct sk_buff * +__cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev, + int approxlen, u32 portid, u32 seq, + enum nl80211_commands cmd, + enum nl80211_attrs attr, gfp_t gfp) +{ + struct sk_buff *skb; + void *hdr; + struct nlattr *data; + + skb = nlmsg_new(approxlen + 100, gfp); + if (!skb) + return NULL; + + hdr = nl80211hdr_put(skb, portid, seq, 0, cmd); + if (!hdr) { + kfree_skb(skb); + return NULL; + } + + if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx)) + goto nla_put_failure; + data = nla_nest_start(skb, attr); + + ((void **)skb->cb)[0] = rdev; + ((void **)skb->cb)[1] = hdr; + ((void **)skb->cb)[2] = data; + + return skb; + + nla_put_failure: + kfree_skb(skb); + return NULL; +} #ifdef CONFIG_NL80211_TESTMODE static struct genl_multicast_group nl80211_testmode_mcgrp = { @@ -6710,11 +6761,11 @@ static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info) if (!info->attrs[NL80211_ATTR_TESTDATA]) return -EINVAL; - rdev->testmode_info = info; + rdev->cur_cmd_info = info; err = rdev_testmode_cmd(rdev, wdev, nla_data(info->attrs[NL80211_ATTR_TESTDATA]), nla_len(info->attrs[NL80211_ATTR_TESTDATA])); - rdev->testmode_info = NULL; + rdev->cur_cmd_info = NULL; return err; } @@ -6814,77 +6865,14 @@ static int nl80211_testmode_dump(struct sk_buff *skb, return err; } -static struct sk_buff * -__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev, - int approxlen, u32 portid, u32 seq, gfp_t gfp) -{ - struct sk_buff *skb; - void *hdr; - struct nlattr *data; - - skb = nlmsg_new(approxlen + 100, gfp); - if (!skb) - return NULL; - - hdr = nl80211hdr_put(skb, portid, seq, 0, NL80211_CMD_TESTMODE); - if (!hdr) { - kfree_skb(skb); - return NULL; - } - - if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx)) - goto nla_put_failure; - data = nla_nest_start(skb, NL80211_ATTR_TESTDATA); - - ((void **)skb->cb)[0] = rdev; - ((void **)skb->cb)[1] = hdr; - ((void **)skb->cb)[2] = data; - - return skb; - - nla_put_failure: - kfree_skb(skb); - return NULL; -} - -struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, - int approxlen) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - if (WARN_ON(!rdev->testmode_info)) - return NULL; - - return __cfg80211_testmode_alloc_skb(rdev, approxlen, - rdev->testmode_info->snd_portid, - rdev->testmode_info->snd_seq, - GFP_KERNEL); -} -EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb); - -int cfg80211_testmode_reply(struct sk_buff *skb) -{ - struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; - void *hdr = ((void **)skb->cb)[1]; - struct nlattr *data = ((void **)skb->cb)[2]; - - if (WARN_ON(!rdev->testmode_info)) { - kfree_skb(skb); - return -EINVAL; - } - - nla_nest_end(skb, data); - genlmsg_end(skb, hdr); - return genlmsg_reply(skb, rdev->testmode_info); -} -EXPORT_SYMBOL(cfg80211_testmode_reply); - struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, int approxlen, gfp_t gfp) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp); + return __cfg80211_alloc_vendor_skb(rdev, approxlen, 0, 0, + NL80211_CMD_TESTMODE, + NL80211_ATTR_TESTDATA, gfp); } EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb); @@ -8867,6 +8855,111 @@ static int nl80211_crit_protocol_stop(struct sk_buff *skb, return 0; } +static int nl80211_vendor_cmd(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct wireless_dev *wdev = + __cfg80211_wdev_from_attrs(genl_info_net(info), info->attrs); + int i, err; + u32 vid, subcmd; + + if (!rdev->wiphy.vendor_commands) + return -EOPNOTSUPP; + + if (IS_ERR(wdev)) { + err = PTR_ERR(wdev); + if (err != -EINVAL) + return err; + wdev = NULL; + } else if (wdev->wiphy != &rdev->wiphy) { + return -EINVAL; + } + + if (!info->attrs[NL80211_ATTR_VENDOR_ID] || + !info->attrs[NL80211_ATTR_VENDOR_SUBCMD]) + return -EINVAL; + + vid = nla_get_u32(info->attrs[NL80211_ATTR_VENDOR_ID]); + subcmd = nla_get_u32(info->attrs[NL80211_ATTR_VENDOR_SUBCMD]); + for (i = 0; i < rdev->wiphy.n_vendor_commands; i++) { + const struct wiphy_vendor_command *vcmd; + void *data = NULL; + int len = 0; + + vcmd = &rdev->wiphy.vendor_commands[i]; + + if (vcmd->info.vendor_id != vid || vcmd->info.subcmd != subcmd) + continue; + + if (vcmd->flags & (WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV)) { + if (!wdev) + return -EINVAL; + if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_NETDEV && + !wdev->netdev) + return -EINVAL; + + if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_RUNNING) { + if (wdev->netdev && + !netif_running(wdev->netdev)) + return -ENETDOWN; + if (!wdev->netdev && !wdev->p2p_started) + return -ENETDOWN; + } + } else { + wdev = NULL; + } + + if (info->attrs[NL80211_ATTR_VENDOR_DATA]) { + data = nla_data(info->attrs[NL80211_ATTR_VENDOR_DATA]); + len = nla_len(info->attrs[NL80211_ATTR_VENDOR_DATA]); + } + + rdev->cur_cmd_info = info; + err = rdev->wiphy.vendor_commands[i].doit(&rdev->wiphy, wdev, + data, len); + rdev->cur_cmd_info = NULL; + return err; + } + + return -EOPNOTSUPP; +} + +struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy, + enum nl80211_commands cmd, + enum nl80211_attrs attr, + int approxlen) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + if (WARN_ON(!rdev->cur_cmd_info)) + return NULL; + + return __cfg80211_alloc_vendor_skb(rdev, approxlen, + rdev->cur_cmd_info->snd_portid, + rdev->cur_cmd_info->snd_seq, + cmd, attr, GFP_KERNEL); +} +EXPORT_SYMBOL(__cfg80211_alloc_reply_skb); + +int cfg80211_vendor_cmd_reply(struct sk_buff *skb) +{ + struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; + void *hdr = ((void **)skb->cb)[1]; + struct nlattr *data = ((void **)skb->cb)[2]; + + if (WARN_ON(!rdev->cur_cmd_info)) { + kfree_skb(skb); + return -EINVAL; + } + + nla_nest_end(skb, data); + genlmsg_end(skb, hdr); + return genlmsg_reply(skb, rdev->cur_cmd_info); +} +EXPORT_SYMBOL_GPL(cfg80211_vendor_cmd_reply); + + #define NL80211_FLAG_NEED_WIPHY 0x01 #define NL80211_FLAG_NEED_NETDEV 0x02 #define NL80211_FLAG_NEED_RTNL 0x04 @@ -9591,6 +9684,14 @@ static struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, + { + .cmd = NL80211_CMD_VENDOR, + .doit = nl80211_vendor_cmd, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_WIPHY | + NL80211_FLAG_NEED_RTNL, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { -- cgit v1.2.3 From 9149761ad74f618371b58fd37141d4c3706d88fc Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 2 Dec 2013 11:20:20 +0200 Subject: Bluetooth: Add module parameter to enable LE CoC support Along with the L2CAP Connection Oriented Channels features it is now allowed to use both custom fixed CIDs as well as PSM based (connection oriented connections). Since the support for this (with the subsequent patches) is still on an experimental stage, add a module parameter to enable it. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap_sock.c | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index c853b16de4ef..94645d56fea7 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -809,6 +809,7 @@ static inline long l2cap_chan_no_get_sndtimeo(struct l2cap_chan *chan) } extern bool disable_ertm; +extern bool enable_lecoc; int l2cap_init_sockets(void); void l2cap_cleanup_sockets(void); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 7cc24d263caa..5a1d0cb0b8d5 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -27,6 +27,7 @@ /* Bluetooth L2CAP sockets. */ +#include #include #include @@ -35,6 +36,8 @@ #include "smp.h" +bool enable_lecoc; + static struct bt_sock_list l2cap_sk_list = { .lock = __RW_LOCK_UNLOCKED(l2cap_sk_list.lock) }; @@ -73,11 +76,11 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) return -EINVAL; if (bdaddr_type_is_le(la.l2_bdaddr_type)) { - /* Connection oriented channels are not supported on LE */ - if (la.l2_psm) + if (!enable_lecoc && la.l2_psm) return -EINVAL; /* We only allow ATT user space socket */ - if (la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT)) + if (la.l2_cid && + la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT)) return -EINVAL; } @@ -189,11 +192,11 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, return -EINVAL; if (bdaddr_type_is_le(la.l2_bdaddr_type)) { - /* Connection oriented channels are not supported on LE */ - if (la.l2_psm) + if (!enable_lecoc && la.l2_psm) return -EINVAL; /* We only allow ATT user space socket */ - if (la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT)) + if (la.l2_cid && + la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT)) return -EINVAL; } @@ -1469,3 +1472,6 @@ void l2cap_cleanup_sockets(void) bt_sock_unregister(BTPROTO_L2CAP); proto_unregister(&l2cap_proto); } + +module_param(enable_lecoc, bool, 0644); +MODULE_PARM_DESC(enable_lecoc, "Enable support for LE CoC"); -- cgit v1.2.3 From ee5ec5cf00760b8810f7e3350b82434092a960e3 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 13 May 2013 12:15:39 +0300 Subject: Bluetooth: Add definitions for LE connection oriented channels This patch adds the necessary defines and structs for LE connection oriented channels. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 94645d56fea7..84c0754b87dc 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -112,6 +112,9 @@ struct l2cap_conninfo { #define L2CAP_MOVE_CHAN_CFM_RSP 0x11 #define L2CAP_CONN_PARAM_UPDATE_REQ 0x12 #define L2CAP_CONN_PARAM_UPDATE_RSP 0x13 +#define L2CAP_LE_CONN_REQ 0x14 +#define L2CAP_LE_CONN_RSP 0x15 +#define L2CAP_LE_CREDITS 0x16 /* L2CAP extended feature mask */ #define L2CAP_FEAT_FLOWCTL 0x00000001 @@ -257,6 +260,10 @@ struct l2cap_conn_rsp { #define L2CAP_CR_SEC_BLOCK 0x0003 #define L2CAP_CR_NO_MEM 0x0004 #define L2CAP_CR_BAD_AMP 0x0005 +#define L2CAP_CR_AUTHENTICATION 0x0005 +#define L2CAP_CR_AUTHORIZATION 0x0006 +#define L2CAP_CR_BAD_KEY_SIZE 0x0007 +#define L2CAP_CR_ENCRYPTION 0x0008 /* connect/create channel status */ #define L2CAP_CS_NO_INFO 0x0000 @@ -423,6 +430,30 @@ struct l2cap_conn_param_update_rsp { #define L2CAP_CONN_PARAM_ACCEPTED 0x0000 #define L2CAP_CONN_PARAM_REJECTED 0x0001 +#define L2CAP_LE_MAX_CREDITS 10 +#define L2CAP_LE_DEFAULT_MPS 230 + +struct l2cap_le_conn_req { + __le16 psm; + __le16 scid; + __le16 mtu; + __le16 mps; + __le16 credits; +} __packed; + +struct l2cap_le_conn_rsp { + __le16 dcid; + __le16 mtu; + __le16 mps; + __le16 credits; + __le16 result; +} __packed; + +struct l2cap_le_credits { + __le16 cid; + __le16 credits; +} __packed; + /* ----- L2CAP channels and connections ----- */ struct l2cap_seq_list { __u16 head; -- cgit v1.2.3 From 27e2d4c8d28be1d1b4ecfbffab572d7dbd35254d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 14 May 2013 13:27:21 +0300 Subject: Bluetooth: Add basic LE L2CAP connect request receiving support This patch adds the necessary boiler plate code to handle receiving L2CAP connect requests over LE and respond to them with a proper connect response. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap_core.c | 152 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 84c0754b87dc..84b25202596e 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -846,6 +846,7 @@ int l2cap_init_sockets(void); void l2cap_cleanup_sockets(void); bool l2cap_is_socket(struct socket *sock); +void __l2cap_le_connect_rsp_defer(struct l2cap_chan *chan); void __l2cap_connect_rsp_defer(struct l2cap_chan *chan); int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 5f9287fd86df..d6f518d7e61b 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -617,6 +617,29 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) return; } +static void l2cap_chan_le_connect_reject(struct l2cap_chan *chan) +{ + struct l2cap_conn *conn = chan->conn; + struct l2cap_le_conn_rsp rsp; + u16 result; + + if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) + result = L2CAP_CR_AUTHORIZATION; + else + result = L2CAP_CR_BAD_PSM; + + l2cap_state_change(chan, BT_DISCONN); + + rsp.dcid = cpu_to_le16(chan->scid); + rsp.mtu = cpu_to_le16(chan->imtu); + rsp.mps = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS); + rsp.credits = __constant_cpu_to_le16(L2CAP_LE_MAX_CREDITS); + rsp.result = cpu_to_le16(result); + + l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_RSP, sizeof(rsp), + &rsp); +} + static void l2cap_chan_connect_reject(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; @@ -663,6 +686,8 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED) { if (conn->hcon->type == ACL_LINK) l2cap_chan_connect_reject(chan); + else if (conn->hcon->type == LE_LINK) + l2cap_chan_le_connect_reject(chan); } l2cap_chan_del(chan, reason); @@ -3641,6 +3666,23 @@ static int l2cap_build_conf_rsp(struct l2cap_chan *chan, void *data, return ptr - data; } +void __l2cap_le_connect_rsp_defer(struct l2cap_chan *chan) +{ + struct l2cap_le_conn_rsp rsp; + struct l2cap_conn *conn = chan->conn; + + BT_DBG("chan %p", chan); + + rsp.dcid = cpu_to_le16(chan->scid); + rsp.mtu = cpu_to_le16(chan->imtu); + rsp.mps = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS); + rsp.credits = __constant_cpu_to_le16(L2CAP_LE_MAX_CREDITS); + rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS); + + l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_RSP, sizeof(rsp), + &rsp); +} + void __l2cap_connect_rsp_defer(struct l2cap_chan *chan) { struct l2cap_conn_rsp rsp; @@ -5382,6 +5424,113 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, return err; } +static int l2cap_le_connect_req(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u16 cmd_len, + u8 *data) +{ + struct l2cap_le_conn_req *req = (struct l2cap_le_conn_req *) data; + struct l2cap_le_conn_rsp rsp; + struct l2cap_chan *chan, *pchan; + u16 dcid, scid, mtu, mps; + __le16 psm; + u8 result; + + if (cmd_len != sizeof(*req)) + return -EPROTO; + + scid = __le16_to_cpu(req->scid); + mtu = __le16_to_cpu(req->mtu); + mps = __le16_to_cpu(req->mps); + psm = req->psm; + dcid = 0; + + if (mtu < 23 || mps < 23) + return -EPROTO; + + BT_DBG("psm 0x%2.2x scid 0x%4.4x mtu %u mps %u", __le16_to_cpu(psm), + scid, mtu, mps); + + /* Check if we have socket listening on psm */ + pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, &conn->hcon->src, + &conn->hcon->dst, LE_LINK); + if (!pchan) { + result = L2CAP_CR_BAD_PSM; + chan = NULL; + goto response; + } + + mutex_lock(&conn->chan_lock); + l2cap_chan_lock(pchan); + + if (!smp_sufficient_security(conn->hcon, pchan->sec_level)) { + result = L2CAP_CR_AUTHENTICATION; + chan = NULL; + goto response_unlock; + } + + /* Check if we already have channel with that dcid */ + if (__l2cap_get_chan_by_dcid(conn, scid)) { + result = L2CAP_CR_NO_MEM; + chan = NULL; + goto response_unlock; + } + + chan = pchan->ops->new_connection(pchan); + if (!chan) { + result = L2CAP_CR_NO_MEM; + goto response_unlock; + } + + bacpy(&chan->src, &conn->hcon->src); + bacpy(&chan->dst, &conn->hcon->dst); + chan->src_type = bdaddr_type(conn->hcon, conn->hcon->src_type); + chan->dst_type = bdaddr_type(conn->hcon, conn->hcon->dst_type); + chan->psm = psm; + chan->dcid = scid; + chan->omtu = mtu; + chan->remote_mps = mps; + + __l2cap_chan_add(conn, chan); + dcid = chan->scid; + + __set_chan_timer(chan, chan->ops->get_sndtimeo(chan)); + + chan->ident = cmd->ident; + + if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) { + l2cap_state_change(chan, BT_CONNECT2); + result = L2CAP_CR_PEND; + chan->ops->defer(chan); + } else { + l2cap_chan_ready(chan); + result = L2CAP_CR_SUCCESS; + } + +response_unlock: + l2cap_chan_unlock(pchan); + mutex_unlock(&conn->chan_lock); + + if (result == L2CAP_CR_PEND) + return 0; + +response: + if (chan) { + rsp.mtu = cpu_to_le16(chan->imtu); + rsp.mps = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS); + } else { + rsp.mtu = 0; + rsp.mps = 0; + } + + rsp.dcid = cpu_to_le16(dcid); + rsp.credits = __constant_cpu_to_le16(L2CAP_LE_MAX_CREDITS); + rsp.result = cpu_to_le16(result); + + l2cap_send_cmd(conn, cmd->ident, L2CAP_LE_CONN_RSP, sizeof(rsp), &rsp); + + return 0; +} + static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data) @@ -5400,6 +5549,9 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn, l2cap_le_connect_rsp(conn, cmd, cmd_len, data); return 0; + case L2CAP_LE_CONN_REQ: + return l2cap_le_connect_req(conn, cmd, cmd_len, data); + default: BT_ERR("Unknown LE signaling command 0x%2.2x", cmd->code); return -EINVAL; -- cgit v1.2.3 From 3831971355d901ccfb76533a422b4395072849a3 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 17 May 2013 12:49:23 +0300 Subject: Bluetooth: Add LE L2CAP flow control mode The LE connection oriented channels have their own mode with its own data transfer rules. In order to implement this properly we need to distinguish L2CAP channels operating in this mode from other modes. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 7 +++++++ net/bluetooth/l2cap_core.c | 13 +++++++++++++ net/bluetooth/l2cap_sock.c | 21 ++++++++++++++++++--- 3 files changed, 38 insertions(+), 3 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 84b25202596e..063d786b0991 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -328,6 +328,12 @@ struct l2cap_conf_rfc { #define L2CAP_MODE_ERTM 0x03 #define L2CAP_MODE_STREAMING 0x04 +/* Unlike the above this one doesn't actually map to anything that would + * ever be sent over the air. Therefore, use a value that's unlikely to + * ever be used in the BR/EDR configuration phase. + */ +#define L2CAP_MODE_LE_FLOWCTL 0x80 + struct l2cap_conf_efs { __u8 id; __u8 stype; @@ -861,6 +867,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, void l2cap_chan_busy(struct l2cap_chan *chan, int busy); int l2cap_chan_check_security(struct l2cap_chan *chan); void l2cap_chan_set_defaults(struct l2cap_chan *chan); +void l2cap_le_flowctl_init(struct l2cap_chan *chan); int l2cap_ertm_init(struct l2cap_chan *chan); void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 907fc1745321..e260753184b6 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -490,6 +490,13 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan) set_bit(FLAG_FORCE_ACTIVE, &chan->flags); } +void l2cap_le_flowctl_init(struct l2cap_chan *chan) +{ + chan->imtu = L2CAP_DEFAULT_MTU; + chan->omtu = L2CAP_LE_MIN_MTU; + chan->mode = L2CAP_MODE_LE_FLOWCTL; +} + void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) { BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, @@ -597,6 +604,9 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) case L2CAP_MODE_BASIC: break; + case L2CAP_MODE_LE_FLOWCTL: + break; + case L2CAP_MODE_ERTM: __clear_retrans_timer(chan); __clear_monitor_timer(chan); @@ -1849,6 +1859,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, switch (chan->mode) { case L2CAP_MODE_BASIC: + case L2CAP_MODE_LE_FLOWCTL: break; case L2CAP_MODE_ERTM: case L2CAP_MODE_STREAMING: @@ -2530,6 +2541,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, switch (chan->mode) { case L2CAP_MODE_BASIC: + case L2CAP_MODE_LE_FLOWCTL: /* Check outgoing MTU */ if (len > chan->omtu) return -EMSGSIZE; @@ -6621,6 +6633,7 @@ static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid, goto drop; switch (chan->mode) { + case L2CAP_MODE_LE_FLOWCTL: case L2CAP_MODE_BASIC: /* If socket recv buffers overflows we drop data here * which is *bad* because L2CAP has to be reliable. diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 5a1d0cb0b8d5..485ca349fed5 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -130,6 +130,9 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) bacpy(&chan->src, &la.l2_bdaddr); chan->src_type = la.l2_bdaddr_type; + if (chan->psm && bdaddr_type_is_le(chan->src_type)) + l2cap_le_flowctl_init(chan); + chan->state = BT_BOUND; sk->sk_state = BT_BOUND; @@ -200,6 +203,9 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, return -EINVAL; } + if (chan->psm && bdaddr_type_is_le(chan->src_type)) + l2cap_le_flowctl_init(chan); + err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid), &la.l2_bdaddr, la.l2_bdaddr_type); if (err) @@ -237,6 +243,7 @@ static int l2cap_sock_listen(struct socket *sock, int backlog) switch (chan->mode) { case L2CAP_MODE_BASIC: + case L2CAP_MODE_LE_FLOWCTL: break; case L2CAP_MODE_ERTM: case L2CAP_MODE_STREAMING: @@ -588,6 +595,8 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, chan->mode = opts.mode; switch (chan->mode) { + case L2CAP_MODE_LE_FLOWCTL: + break; case L2CAP_MODE_BASIC: clear_bit(CONF_STATE2_DEVICE, &chan->conf_state); break; @@ -862,10 +871,16 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, if (sk->sk_state == BT_CONNECT2 && test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) { - sk->sk_state = BT_CONFIG; - pi->chan->state = BT_CONFIG; + if (bdaddr_type_is_le(pi->chan->src_type)) { + sk->sk_state = BT_CONNECTED; + pi->chan->state = BT_CONNECTED; + __l2cap_le_connect_rsp_defer(pi->chan); + } else { + sk->sk_state = BT_CONFIG; + pi->chan->state = BT_CONFIG; + __l2cap_connect_rsp_defer(pi->chan); + } - __l2cap_connect_rsp_defer(pi->chan); err = 0; goto done; } -- cgit v1.2.3 From 0cd75f7ed740a8c605fe55ac71a9b5162c612422 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 17 May 2013 13:09:05 +0300 Subject: Bluetooth: Track LE L2CAP credits in l2cap_chan This patch adds tracking of L2CAP connection oriented channel local and remote credits to struct l2cap_chan and ensures that connect requests and responses contain the right values. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 3 +++ net/bluetooth/l2cap_core.c | 16 +++++++++++----- net/bluetooth/l2cap_sock.c | 2 ++ 3 files changed, 16 insertions(+), 5 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 063d786b0991..d7cd1ddfe576 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -514,6 +514,9 @@ struct l2cap_chan { __u16 monitor_timeout; __u16 mps; + __u16 tx_credits; + __u16 rx_credits; + __u8 tx_state; __u8 rx_state; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index e260753184b6..fe55162947f8 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -495,6 +495,8 @@ void l2cap_le_flowctl_init(struct l2cap_chan *chan) chan->imtu = L2CAP_DEFAULT_MTU; chan->omtu = L2CAP_LE_MIN_MTU; chan->mode = L2CAP_MODE_LE_FLOWCTL; + chan->tx_credits = 0; + chan->rx_credits = L2CAP_LE_MAX_CREDITS; } void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) @@ -643,7 +645,7 @@ static void l2cap_chan_le_connect_reject(struct l2cap_chan *chan) rsp.dcid = cpu_to_le16(chan->scid); rsp.mtu = cpu_to_le16(chan->imtu); rsp.mps = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS); - rsp.credits = __constant_cpu_to_le16(L2CAP_LE_MAX_CREDITS); + rsp.credits = cpu_to_le16(chan->rx_credits); rsp.result = cpu_to_le16(result); l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_RSP, sizeof(rsp), @@ -1212,7 +1214,7 @@ static void l2cap_le_connect(struct l2cap_chan *chan) req.scid = cpu_to_le16(chan->scid); req.mtu = cpu_to_le16(chan->imtu); req.mps = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS); - req.credits = __constant_cpu_to_le16(L2CAP_LE_MAX_CREDITS); + req.credits = cpu_to_le16(chan->rx_credits); chan->ident = l2cap_get_ident(conn); @@ -3690,7 +3692,7 @@ void __l2cap_le_connect_rsp_defer(struct l2cap_chan *chan) rsp.dcid = cpu_to_le16(chan->scid); rsp.mtu = cpu_to_le16(chan->imtu); rsp.mps = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS); - rsp.credits = __constant_cpu_to_le16(L2CAP_LE_MAX_CREDITS); + rsp.credits = cpu_to_le16(chan->rx_credits); rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS); l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_RSP, sizeof(rsp), @@ -5342,6 +5344,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, chan->dcid = dcid; chan->omtu = mtu; chan->remote_mps = mps; + chan->tx_credits = credits; l2cap_chan_ready(chan); break; @@ -5445,7 +5448,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn, struct l2cap_le_conn_req *req = (struct l2cap_le_conn_req *) data; struct l2cap_le_conn_rsp rsp; struct l2cap_chan *chan, *pchan; - u16 dcid, scid, mtu, mps; + u16 dcid, scid, credits, mtu, mps; __le16 psm; u8 result; @@ -5457,6 +5460,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn, mps = __le16_to_cpu(req->mps); psm = req->psm; dcid = 0; + credits = 0; if (mtu < 23 || mps < 23) return -EPROTO; @@ -5503,9 +5507,11 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn, chan->dcid = scid; chan->omtu = mtu; chan->remote_mps = mps; + chan->tx_credits = __le16_to_cpu(req->credits); __l2cap_chan_add(conn, chan); dcid = chan->scid; + credits = chan->rx_credits; __set_chan_timer(chan, chan->ops->get_sndtimeo(chan)); @@ -5537,7 +5543,7 @@ response: } rsp.dcid = cpu_to_le16(dcid); - rsp.credits = __constant_cpu_to_le16(L2CAP_LE_MAX_CREDITS); + rsp.credits = cpu_to_le16(credits); rsp.result = cpu_to_le16(result); l2cap_send_cmd(conn, cmd->ident, L2CAP_LE_CONN_RSP, sizeof(rsp), &rsp); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 485ca349fed5..61e25bafdf43 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1321,6 +1321,8 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->tx_win_max = pchan->tx_win_max; chan->sec_level = pchan->sec_level; chan->flags = pchan->flags; + chan->tx_credits = pchan->tx_credits; + chan->rx_credits = pchan->rx_credits; security_sk_clone(parent, sk); } else { -- cgit v1.2.3 From 1f435424ce2c93c31c3887ec67e3afb6056f18f6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 2 Dec 2013 16:34:18 +0200 Subject: Bluetooth: Add new BT_SNDMTU and BT_RCVMTU socket options This patch adds new socket options for LE sockets since the existing L2CAP_OPTIONS socket option is not usable for LE. For now, the new socket options also require LE CoC support to be explicitly enabled to leave some playroom in case something needs to be changed in a backwards incompatible way. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/bluetooth.h | 3 ++ net/bluetooth/l2cap_sock.c | 76 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 2a628b28249f..f4f9ee466791 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -115,6 +115,9 @@ struct bt_voice { #define BT_VOICE_TRANSPARENT 0x0003 #define BT_VOICE_CVSD_16BIT 0x0060 +#define BT_SNDMTU 12 +#define BT_RCVMTU 13 + __printf(1, 2) int bt_info(const char *fmt, ...); __printf(1, 2) diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index a20fcc3ddcd9..01d65bc99b0c 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -534,6 +534,41 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, err = -EFAULT; break; + case BT_SNDMTU: + if (!enable_lecoc) { + err = -EPROTONOSUPPORT; + break; + } + + if (!bdaddr_type_is_le(chan->src_type)) { + err = -EINVAL; + break; + } + + if (sk->sk_state != BT_CONNECTED) { + err = -ENOTCONN; + break; + } + + if (put_user(chan->omtu, (u16 __user *) optval)) + err = -EFAULT; + break; + + case BT_RCVMTU: + if (!enable_lecoc) { + err = -EPROTONOSUPPORT; + break; + } + + if (!bdaddr_type_is_le(chan->src_type)) { + err = -EINVAL; + break; + } + + if (put_user(chan->imtu, (u16 __user *) optval)) + err = -EFAULT; + break; + default: err = -ENOPROTOOPT; break; @@ -834,6 +869,47 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, break; + case BT_SNDMTU: + if (!enable_lecoc) { + err = -EPROTONOSUPPORT; + break; + } + + if (!bdaddr_type_is_le(chan->src_type)) { + err = -EINVAL; + break; + } + + /* Setting is not supported as it's the remote side that + * decides this. + */ + err = -EPERM; + break; + + case BT_RCVMTU: + if (!enable_lecoc) { + err = -EPROTONOSUPPORT; + break; + } + + if (!bdaddr_type_is_le(chan->src_type)) { + err = -EINVAL; + break; + } + + if (sk->sk_state == BT_CONNECTED) { + err = -EISCONN; + break; + } + + if (get_user(opt, (u32 __user *) optval)) { + err = -EFAULT; + break; + } + + chan->imtu = opt; + break; + default: err = -ENOPROTOOPT; break; -- cgit v1.2.3 From 837776f7904024df451422f32b09c67e88ae2aa2 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 15 Oct 2013 11:03:18 +0300 Subject: Bluetooth: Introduce L2CAP channel callback for suspending Setting the BT_SK_SUSPEND socket flag from the L2CAP core is causing a dependency on the socket. So instead of doing that, use a channel callback into the socket handling to suspend. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap_sock.c | 9 +++++++++ 2 files changed, 10 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index d7cd1ddfe576..b0d7e4adb167 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -593,6 +593,7 @@ struct l2cap_ops { void (*ready) (struct l2cap_chan *chan); void (*defer) (struct l2cap_chan *chan); void (*resume) (struct l2cap_chan *chan); + void (*suspend) (struct l2cap_chan *chan); void (*set_shutdown) (struct l2cap_chan *chan); long (*get_sndtimeo) (struct l2cap_chan *chan); struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan, diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 01d65bc99b0c..a51844a8c5eb 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1345,6 +1345,14 @@ static long l2cap_sock_get_sndtimeo_cb(struct l2cap_chan *chan) return sk->sk_sndtimeo; } +static void l2cap_sock_suspend_cb(struct l2cap_chan *chan) +{ + struct sock *sk = chan->data; + + set_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags); + sk->sk_state_change(sk); +} + static struct l2cap_ops l2cap_chan_ops = { .name = "L2CAP Socket Interface", .new_connection = l2cap_sock_new_connection_cb, @@ -1355,6 +1363,7 @@ static struct l2cap_ops l2cap_chan_ops = { .ready = l2cap_sock_ready_cb, .defer = l2cap_sock_defer_cb, .resume = l2cap_sock_resume_cb, + .suspend = l2cap_sock_suspend_cb, .set_shutdown = l2cap_sock_set_shutdown_cb, .get_sndtimeo = l2cap_sock_get_sndtimeo_cb, .alloc_skb = l2cap_sock_alloc_skb_cb, -- cgit v1.2.3 From 595177f311f5d42804f729e927f7eac87bbcc21b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 2 Dec 2013 22:12:22 +0200 Subject: Bluetooth: Fix LE L2CAP Connect Request handling together with SMP Unlike BR/EDR, for LE when we're in the BT_CONNECT state we may or may not have already have sent the Connect Request. This means that we need some extra tracking of the request. This patch adds an extra channel flag to prevent the request from being sent a second time. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap_core.c | 3 +++ 2 files changed, 4 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index b0d7e4adb167..292749c0f4bc 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -695,6 +695,7 @@ enum { FLAG_EXT_CTRL, FLAG_EFS_ENABLE, FLAG_DEFER_SETUP, + FLAG_LE_CONN_REQ_SENT, }; enum { diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 1c94e51a28fe..407e9d61d061 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1223,6 +1223,9 @@ static void l2cap_le_connect(struct l2cap_chan *chan) struct l2cap_conn *conn = chan->conn; struct l2cap_le_conn_req req; + if (test_and_set_bit(FLAG_LE_CONN_REQ_SENT, &chan->flags)) + return; + req.psm = chan->psm; req.scid = cpu_to_le16(chan->scid); req.mtu = cpu_to_le16(chan->imtu); -- cgit v1.2.3 From e77af7559238895ec5fd1706762e1c9f890dcc7e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 8 Oct 2013 10:31:00 +0200 Subject: Bluetooth: Fix CID ranges for LE CoC CID allocations LE CoC used differend CIC ranges than BR/EDR L2CAP. The start of the range is the same (0x0040) but the range ends at 0x007f (unlike BR/EDR where it goes all the way to 0xffff). Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap_core.c | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 292749c0f4bc..abba765c174e 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -252,6 +252,7 @@ struct l2cap_conn_rsp { #define L2CAP_CID_SMP 0x0006 #define L2CAP_CID_DYN_START 0x0040 #define L2CAP_CID_DYN_END 0xffff +#define L2CAP_CID_LE_DYN_END 0x007f /* connect/create channel results */ #define L2CAP_CR_SUCCESS 0x0000 diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index eb5604e06106..76ebd18ff968 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -213,9 +213,14 @@ int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid) static u16 l2cap_alloc_cid(struct l2cap_conn *conn) { - u16 cid = L2CAP_CID_DYN_START; + u16 cid, dyn_end; - for (; cid < L2CAP_CID_DYN_END; cid++) { + if (conn->hcon->type == LE_LINK) + dyn_end = L2CAP_CID_LE_DYN_END; + else + dyn_end = L2CAP_CID_DYN_END; + + for (cid = L2CAP_CID_DYN_START; cid < dyn_end; cid++) { if (!__l2cap_get_chan_by_scid(conn, cid)) return cid; } -- cgit v1.2.3 From 0ce43ce60d5e0c079d33be1fe33ba92828c7e5da Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 5 Dec 2013 14:55:33 +0200 Subject: Bluetooth: Simplify l2cap_chan initialization for LE CoC The values in l2cap_chan that are used for actually transmitting data only need to be initialized right after we've received an L2CAP Connect Request or just before we send one. The only thing that we need to initialize though bind() and connect() is the chan->mode value. This way all other initializations can be done in the l2cap_le_flowctl_init function (which now becomes private to l2cap_core.c) and the l2cap_le_flowctl_start function can be completely removed. Also, since the l2cap_sock_init function initializes the imtu and omtu to adequate values these do not need to be part of l2cap_le_flowctl_init. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 1 - net/bluetooth/l2cap_core.c | 35 ++++++++++++----------------------- net/bluetooth/l2cap_sock.c | 4 ++-- 3 files changed, 14 insertions(+), 26 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index abba765c174e..e149e992fdae 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -873,7 +873,6 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, void l2cap_chan_busy(struct l2cap_chan *chan, int busy); int l2cap_chan_check_security(struct l2cap_chan *chan); void l2cap_chan_set_defaults(struct l2cap_chan *chan); -void l2cap_le_flowctl_init(struct l2cap_chan *chan); int l2cap_ertm_init(struct l2cap_chan *chan); void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 8e9e883874ce..6e6f308af036 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -498,11 +498,11 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan) set_bit(FLAG_FORCE_ACTIVE, &chan->flags); } -void l2cap_le_flowctl_init(struct l2cap_chan *chan) +static void l2cap_le_flowctl_init(struct l2cap_chan *chan) { - chan->imtu = L2CAP_DEFAULT_MTU; - chan->omtu = L2CAP_LE_MIN_MTU; - chan->mode = L2CAP_MODE_LE_FLOWCTL; + chan->sdu = NULL; + chan->sdu_last_frag = NULL; + chan->sdu_len = 0; chan->tx_credits = 0; chan->rx_credits = le_max_credits; @@ -510,6 +510,8 @@ void l2cap_le_flowctl_init(struct l2cap_chan *chan) chan->mps = chan->imtu; else chan->mps = L2CAP_LE_DEFAULT_MPS; + + skb_queue_head_init(&chan->tx_q); } void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) @@ -1208,31 +1210,14 @@ static void l2cap_move_done(struct l2cap_chan *chan) } } -static void l2cap_le_flowctl_start(struct l2cap_chan *chan) -{ - chan->sdu = NULL; - chan->sdu_last_frag = NULL; - chan->sdu_len = 0; - - if (chan->imtu < L2CAP_LE_DEFAULT_MPS) - chan->mps = chan->imtu; - else - chan->mps = le_default_mps; - - skb_queue_head_init(&chan->tx_q); - - if (!chan->tx_credits) - chan->ops->suspend(chan); -} - static void l2cap_chan_ready(struct l2cap_chan *chan) { /* This clears all conf flags, including CONF_NOT_COMPLETE */ chan->conf_state = 0; __clear_chan_timer(chan); - if (chan->mode == L2CAP_MODE_LE_FLOWCTL) - l2cap_le_flowctl_start(chan); + if (chan->mode == L2CAP_MODE_LE_FLOWCTL && !chan->tx_credits) + chan->ops->suspend(chan); chan->state = BT_CONNECTED; @@ -1909,7 +1894,9 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, switch (chan->mode) { case L2CAP_MODE_BASIC: + break; case L2CAP_MODE_LE_FLOWCTL: + l2cap_le_flowctl_init(chan); break; case L2CAP_MODE_ERTM: case L2CAP_MODE_STREAMING: @@ -5663,6 +5650,8 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn, goto response_unlock; } + l2cap_le_flowctl_init(chan); + bacpy(&chan->src, &conn->hcon->src); bacpy(&chan->dst, &conn->hcon->dst); chan->src_type = bdaddr_type(conn->hcon, conn->hcon->src_type); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 88fa9c07c503..e7806e6d282c 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -153,7 +153,7 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) chan->src_type = la.l2_bdaddr_type; if (chan->psm && bdaddr_type_is_le(chan->src_type)) - l2cap_le_flowctl_init(chan); + chan->mode = L2CAP_MODE_LE_FLOWCTL; chan->state = BT_BOUND; sk->sk_state = BT_BOUND; @@ -226,7 +226,7 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, } if (chan->psm && bdaddr_type_is_le(chan->src_type)) - l2cap_le_flowctl_init(chan); + chan->mode = L2CAP_MODE_LE_FLOWCTL; err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid), &la.l2_bdaddr, la.l2_bdaddr_type); -- cgit v1.2.3 From d1e33e654ef6bb3dee766353ed9dd31e7dcb8a94 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Thu, 5 Dec 2013 10:02:15 +0100 Subject: cfg80211: in bitrate_mask, rename mcs to ht_mcs Rename NL80211_TXRATE_MCS to NL80211_TXRATE_HT and also rename mcs to ht_mcs in struct cfg80211_bitrate_mask. Signed-off-by: Janusz Dziedzic [reword commit message] Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/wmi.c | 6 +++--- drivers/net/wireless/mwifiex/cfg80211.c | 6 +++--- include/net/cfg80211.h | 2 +- include/uapi/linux/nl80211.h | 6 ++++-- net/mac80211/cfg.c | 4 ++-- net/wireless/nl80211.c | 18 +++++++++--------- 6 files changed, 22 insertions(+), 20 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 546d5da0b894..4f16d79c9eb1 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -2754,9 +2754,9 @@ static int ath6kl_set_bitrate_mask64(struct wmi *wmi, u8 if_idx, mask->control[band].legacy << 4; /* copy mcs rate mask */ - mcsrate = mask->control[band].mcs[1]; + mcsrate = mask->control[band].ht_mcs[1]; mcsrate <<= 8; - mcsrate |= mask->control[band].mcs[0]; + mcsrate |= mask->control[band].ht_mcs[0]; ratemask[band] |= mcsrate << 12; ratemask[band] |= mcsrate << 28; } @@ -2806,7 +2806,7 @@ static int ath6kl_set_bitrate_mask32(struct wmi *wmi, u8 if_idx, mask->control[band].legacy << 4; /* copy mcs rate mask */ - mcsrate = mask->control[band].mcs[0]; + mcsrate = mask->control[band].ht_mcs[0]; ratemask[band] |= mcsrate << 12; ratemask[band] |= mcsrate << 20; } diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 1c8116d46845..6f5e49b32e79 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1170,10 +1170,10 @@ static int mwifiex_cfg80211_set_bitrate_mask(struct wiphy *wiphy, else bitmap_rates[1] = mask->control[band].legacy; - /* Fill MCS rates */ - bitmap_rates[2] = mask->control[band].mcs[0]; + /* Fill HT MCS rates */ + bitmap_rates[2] = mask->control[band].ht_mcs[0]; if (priv->adapter->hw_dev_mcs_support == HT_STREAM_2X2) - bitmap_rates[2] |= mask->control[band].mcs[1] << 8; + bitmap_rates[2] |= mask->control[band].ht_mcs[1] << 8; return mwifiex_send_cmd_sync(priv, HostCmd_CMD_TX_RATE_CFG, HostCmd_ACT_GEN_SET, 0, bitmap_rates); diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 884ac69b5e55..6c3a650f3a8f 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1766,7 +1766,7 @@ enum wiphy_params_flags { struct cfg80211_bitrate_mask { struct { u32 legacy; - u8 mcs[IEEE80211_HT_MCS_MASK_LEN]; + u8 ht_mcs[IEEE80211_HT_MCS_MASK_LEN]; } control[IEEE80211_NUM_BANDS]; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 72ba3584c90d..6e700645cd27 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3110,7 +3110,7 @@ enum nl80211_key_attributes { * in an array of rates as defined in IEEE 802.11 7.3.2.2 (u8 values with * 1 = 500 kbps) but without the IE length restriction (at most * %NL80211_MAX_SUPP_RATES in a single array). - * @NL80211_TXRATE_MCS: HT (MCS) rates allowed for TX rate selection + * @NL80211_TXRATE_HT: HT (MCS) rates allowed for TX rate selection * in an array of MCS numbers. * @__NL80211_TXRATE_AFTER_LAST: internal * @NL80211_TXRATE_MAX: highest TX rate attribute @@ -3118,13 +3118,15 @@ enum nl80211_key_attributes { enum nl80211_tx_rate_attributes { __NL80211_TXRATE_INVALID, NL80211_TXRATE_LEGACY, - NL80211_TXRATE_MCS, + NL80211_TXRATE_HT, /* keep last */ __NL80211_TXRATE_AFTER_LAST, NL80211_TXRATE_MAX = __NL80211_TXRATE_AFTER_LAST - 1 }; +#define NL80211_TXRATE_MCS NL80211_TXRATE_HT + /** * enum nl80211_band - Frequency band * @NL80211_BAND_2GHZ: 2.4 GHz ISM band diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 754069cbb756..a74d61d520b0 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2588,8 +2588,8 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, int j; sdata->rc_rateidx_mask[i] = mask->control[i].legacy; - memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].mcs, - sizeof(mask->control[i].mcs)); + memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].ht_mcs, + sizeof(mask->control[i].ht_mcs)); sdata->rc_has_mcs_mask[i] = false; if (!sband) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index f61b74c40d28..801e57da88b4 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -7312,8 +7312,8 @@ static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband, static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = { [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY, .len = NL80211_MAX_SUPP_RATES }, - [NL80211_TXRATE_MCS] = { .type = NLA_BINARY, - .len = NL80211_MAX_SUPP_HT_RATES }, + [NL80211_TXRATE_HT] = { .type = NLA_BINARY, + .len = NL80211_MAX_SUPP_HT_RATES }, }; static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, @@ -7339,9 +7339,9 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, continue; mask.control[i].legacy = (1 << sband->n_bitrates) - 1; - memcpy(mask.control[i].mcs, + memcpy(mask.control[i].ht_mcs, sband->ht_cap.mcs.rx_mask, - sizeof(mask.control[i].mcs)); + sizeof(mask.control[i].ht_mcs)); } /* if no rates are given set it back to the defaults */ @@ -7372,12 +7372,12 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, nla_len(tb[NL80211_TXRATE_LEGACY])) return -EINVAL; } - if (tb[NL80211_TXRATE_MCS]) { + if (tb[NL80211_TXRATE_HT]) { if (!ht_rateset_to_mask( sband, - nla_data(tb[NL80211_TXRATE_MCS]), - nla_len(tb[NL80211_TXRATE_MCS]), - mask.control[band].mcs)) + nla_data(tb[NL80211_TXRATE_HT]), + nla_len(tb[NL80211_TXRATE_HT]), + mask.control[band].ht_mcs)) return -EINVAL; } @@ -7388,7 +7388,7 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, return -EINVAL; for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) - if (mask.control[band].mcs[i]) + if (mask.control[band].ht_mcs[i]) break; /* legacy and mcs rates may not be both empty */ -- cgit v1.2.3 From 8d549c4f5d92d80fc6f888fd314e10972ae0ec37 Mon Sep 17 00:00:00 2001 From: Fan Du Date: Thu, 7 Nov 2013 17:47:49 +0800 Subject: xfrm: Using the right namespace to migrate key info because the home agent could surely be run on a different net namespace other than init_net. The original behavior could lead into inconsistent of key info. Signed-off-by: Fan Du Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 2 +- net/key/af_key.c | 3 ++- net/xfrm/xfrm_policy.c | 10 +++++----- net/xfrm/xfrm_user.c | 3 ++- 4 files changed, 10 insertions(+), 8 deletions(-) (limited to 'include/net') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 6b82fdf4ba71..5b522c5f0188 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1581,7 +1581,7 @@ struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x, struct xfrm_migrate *m); int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, struct xfrm_migrate *m, int num_bundles, - struct xfrm_kmaddress *k); + struct xfrm_kmaddress *k, struct net *net); #endif int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport); diff --git a/net/key/af_key.c b/net/key/af_key.c index 545f047868ad..3fa811c46913 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -2485,6 +2485,7 @@ static int pfkey_migrate(struct sock *sk, struct sk_buff *skb, struct xfrm_selector sel; struct xfrm_migrate m[XFRM_MAX_DEPTH]; struct xfrm_kmaddress k; + struct net *net = sock_net(sk); if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC - 1], ext_hdrs[SADB_EXT_ADDRESS_DST - 1]) || @@ -2558,7 +2559,7 @@ static int pfkey_migrate(struct sock *sk, struct sk_buff *skb, } return xfrm_migrate(&sel, dir, XFRM_POLICY_TYPE_MAIN, m, i, - kma ? &k : NULL); + kma ? &k : NULL, net); out: return err; diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index a5bbdfb2874b..907fd2fa70bc 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -3076,14 +3076,14 @@ static bool xfrm_migrate_selector_match(const struct xfrm_selector *sel_cmp, } static struct xfrm_policy * xfrm_migrate_policy_find(const struct xfrm_selector *sel, - u8 dir, u8 type) + u8 dir, u8 type, struct net *net) { struct xfrm_policy *pol, *ret = NULL; struct hlist_head *chain; u32 priority = ~0U; read_lock_bh(&xfrm_policy_lock); - chain = policy_hash_direct(&init_net, &sel->daddr, &sel->saddr, sel->family, dir); + chain = policy_hash_direct(net, &sel->daddr, &sel->saddr, sel->family, dir); hlist_for_each_entry(pol, chain, bydst) { if (xfrm_migrate_selector_match(sel, &pol->selector) && pol->type == type) { @@ -3092,7 +3092,7 @@ static struct xfrm_policy * xfrm_migrate_policy_find(const struct xfrm_selector break; } } - chain = &init_net.xfrm.policy_inexact[dir]; + chain = &net->xfrm.policy_inexact[dir]; hlist_for_each_entry(pol, chain, bydst) { if (xfrm_migrate_selector_match(sel, &pol->selector) && pol->type == type && @@ -3216,7 +3216,7 @@ static int xfrm_migrate_check(const struct xfrm_migrate *m, int num_migrate) int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, struct xfrm_migrate *m, int num_migrate, - struct xfrm_kmaddress *k) + struct xfrm_kmaddress *k, struct net *net) { int i, err, nx_cur = 0, nx_new = 0; struct xfrm_policy *pol = NULL; @@ -3229,7 +3229,7 @@ int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, goto out; /* Stage 1 - find policy */ - if ((pol = xfrm_migrate_policy_find(sel, dir, type)) == NULL) { + if ((pol = xfrm_migrate_policy_find(sel, dir, type, net)) == NULL) { err = -ENOENT; goto out; } diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 4e0546e9bb0a..840cc8d9fbe4 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -2137,6 +2137,7 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, u8 type; int err; int n = 0; + struct net *net = sock_net(skb->sk); if (attrs[XFRMA_MIGRATE] == NULL) return -EINVAL; @@ -2154,7 +2155,7 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh, if (!n) return 0; - xfrm_migrate(&pi->sel, pi->dir, type, m, n, kmp); + xfrm_migrate(&pi->sel, pi->dir, type, m, n, kmp, net); return 0; } -- cgit v1.2.3 From 283bc9f35bbbcb0e9ab4e6d2427da7f9f710d52d Mon Sep 17 00:00:00 2001 From: Fan Du Date: Thu, 7 Nov 2013 17:47:50 +0800 Subject: xfrm: Namespacify xfrm state/policy locks By semantics, xfrm layer is fully name space aware, so will the locks, e.g. xfrm_state/pocliy_lock. Ensure exclusive access into state/policy link list for different name space with one global lock is not right in terms of semantics aspect at first place, as they are indeed mutually independent with each other, but also more seriously causes scalability problem. One practical scenario is on a Open Network Stack, more than hundreds of lxc tenants acts as routers within one host, a global xfrm_state/policy_lock becomes the bottleneck. But onces those locks are decoupled in a per-namespace fashion, locks contend is just with in specific name space scope, without causing additional SPD/SAD access delay for other name space. Also this patch improve scalability while as without changing original xfrm behavior. Signed-off-by: Fan Du Signed-off-by: Steffen Klassert --- include/net/netns/xfrm.h | 4 ++ include/net/xfrm.h | 11 +++-- net/key/af_key.c | 15 ++++--- net/xfrm/xfrm_policy.c | 104 +++++++++++++++++++++++++---------------------- net/xfrm/xfrm_state.c | 104 ++++++++++++++++++++++++----------------------- net/xfrm/xfrm_user.c | 14 +++++-- 6 files changed, 138 insertions(+), 114 deletions(-) (limited to 'include/net') diff --git a/include/net/netns/xfrm.h b/include/net/netns/xfrm.h index 5299e69a32af..ea28404e9d79 100644 --- a/include/net/netns/xfrm.h +++ b/include/net/netns/xfrm.h @@ -59,6 +59,10 @@ struct netns_xfrm { #if IS_ENABLED(CONFIG_IPV6) struct dst_ops xfrm6_dst_ops; #endif + spinlock_t xfrm_state_lock; + spinlock_t xfrm_policy_sk_bundle_lock; + rwlock_t xfrm_policy_lock; + struct mutex xfrm_cfg_mutex; }; #endif diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 5b522c5f0188..59f5d0a6c7f8 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -53,7 +53,6 @@ #define XFRM_INC_STATS_USER(net, field) ((void)(net)) #endif -extern struct mutex xfrm_cfg_mutex; /* Organization of SPD aka "XFRM rules" ------------------------------------ @@ -1409,7 +1408,7 @@ static inline void xfrm_sysctl_fini(struct net *net) void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto); int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk, int (*func)(struct xfrm_state *, int, void*), void *); -void xfrm_state_walk_done(struct xfrm_state_walk *walk); +void xfrm_state_walk_done(struct xfrm_state_walk *walk, struct net *net); struct xfrm_state *xfrm_state_alloc(struct net *net); struct xfrm_state *xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr, @@ -1436,12 +1435,12 @@ struct xfrm_state *xfrm_state_lookup_byaddr(struct net *net, u32 mark, unsigned short family); #ifdef CONFIG_XFRM_SUB_POLICY int xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n, - unsigned short family); + unsigned short family, struct net *net); int xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n, unsigned short family); #else static inline int xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, - int n, unsigned short family) + int n, unsigned short family, struct net *net) { return -ENOSYS; } @@ -1553,7 +1552,7 @@ void xfrm_policy_walk_init(struct xfrm_policy_walk *walk, u8 type); int xfrm_policy_walk(struct net *net, struct xfrm_policy_walk *walk, int (*func)(struct xfrm_policy *, int, int, void*), void *); -void xfrm_policy_walk_done(struct xfrm_policy_walk *walk); +void xfrm_policy_walk_done(struct xfrm_policy_walk *walk, struct net *net); int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl); struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u8 type, int dir, @@ -1576,7 +1575,7 @@ int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol); int km_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, const struct xfrm_migrate *m, int num_bundles, const struct xfrm_kmaddress *k); -struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m); +struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *net); struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x, struct xfrm_migrate *m); int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, diff --git a/net/key/af_key.c b/net/key/af_key.c index 3fa811c46913..9a039acf37e8 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -1785,7 +1785,9 @@ static int pfkey_dump_sa(struct pfkey_sock *pfk) static void pfkey_dump_sa_done(struct pfkey_sock *pfk) { - xfrm_state_walk_done(&pfk->dump.u.state); + struct net *net = sock_net(&pfk->sk); + + xfrm_state_walk_done(&pfk->dump.u.state, net); } static int pfkey_dump(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs) @@ -1861,7 +1863,7 @@ static u32 gen_reqid(struct net *net) reqid = IPSEC_MANUAL_REQID_MAX+1; xfrm_policy_walk_init(&walk, XFRM_POLICY_TYPE_MAIN); rc = xfrm_policy_walk(net, &walk, check_reqid, (void*)&reqid); - xfrm_policy_walk_done(&walk); + xfrm_policy_walk_done(&walk, net); if (rc != -EEXIST) return reqid; } while (reqid != start); @@ -2660,7 +2662,9 @@ static int pfkey_dump_sp(struct pfkey_sock *pfk) static void pfkey_dump_sp_done(struct pfkey_sock *pfk) { - xfrm_policy_walk_done(&pfk->dump.u.policy); + struct net *net = sock_net((struct sock *)pfk); + + xfrm_policy_walk_done(&pfk->dump.u.policy, net); } static int pfkey_spddump(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs) @@ -3570,6 +3574,7 @@ static int pfkey_sendmsg(struct kiocb *kiocb, struct sk_buff *skb = NULL; struct sadb_msg *hdr = NULL; int err; + struct net *net = sock_net(sk); err = -EOPNOTSUPP; if (msg->msg_flags & MSG_OOB) @@ -3592,9 +3597,9 @@ static int pfkey_sendmsg(struct kiocb *kiocb, if (!hdr) goto out; - mutex_lock(&xfrm_cfg_mutex); + mutex_lock(&net->xfrm.xfrm_cfg_mutex); err = pfkey_process(sk, skb, hdr); - mutex_unlock(&xfrm_cfg_mutex); + mutex_unlock(&net->xfrm.xfrm_cfg_mutex); out: if (err && hdr && pfkey_error(hdr, err, sk) == 0) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 907fd2fa70bc..73b04d3df44e 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -39,12 +39,7 @@ #define XFRM_QUEUE_TMO_MAX ((unsigned)(60*HZ)) #define XFRM_MAX_QUEUE_LEN 100 -DEFINE_MUTEX(xfrm_cfg_mutex); -EXPORT_SYMBOL(xfrm_cfg_mutex); - -static DEFINE_SPINLOCK(xfrm_policy_sk_bundle_lock); static struct dst_entry *xfrm_policy_sk_bundles; -static DEFINE_RWLOCK(xfrm_policy_lock); static DEFINE_SPINLOCK(xfrm_policy_afinfo_lock); static struct xfrm_policy_afinfo __rcu *xfrm_policy_afinfo[NPROTO] @@ -438,7 +433,7 @@ static void xfrm_bydst_resize(struct net *net, int dir) if (!ndst) return; - write_lock_bh(&xfrm_policy_lock); + write_lock_bh(&net->xfrm.xfrm_policy_lock); for (i = hmask; i >= 0; i--) xfrm_dst_hash_transfer(odst + i, ndst, nhashmask); @@ -446,7 +441,7 @@ static void xfrm_bydst_resize(struct net *net, int dir) net->xfrm.policy_bydst[dir].table = ndst; net->xfrm.policy_bydst[dir].hmask = nhashmask; - write_unlock_bh(&xfrm_policy_lock); + write_unlock_bh(&net->xfrm.xfrm_policy_lock); xfrm_hash_free(odst, (hmask + 1) * sizeof(struct hlist_head)); } @@ -463,7 +458,7 @@ static void xfrm_byidx_resize(struct net *net, int total) if (!nidx) return; - write_lock_bh(&xfrm_policy_lock); + write_lock_bh(&net->xfrm.xfrm_policy_lock); for (i = hmask; i >= 0; i--) xfrm_idx_hash_transfer(oidx + i, nidx, nhashmask); @@ -471,7 +466,7 @@ static void xfrm_byidx_resize(struct net *net, int total) net->xfrm.policy_byidx = nidx; net->xfrm.policy_idx_hmask = nhashmask; - write_unlock_bh(&xfrm_policy_lock); + write_unlock_bh(&net->xfrm.xfrm_policy_lock); xfrm_hash_free(oidx, (hmask + 1) * sizeof(struct hlist_head)); } @@ -504,7 +499,7 @@ static inline int xfrm_byidx_should_resize(struct net *net, int total) void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si) { - read_lock_bh(&xfrm_policy_lock); + read_lock_bh(&net->xfrm.xfrm_policy_lock); si->incnt = net->xfrm.policy_count[XFRM_POLICY_IN]; si->outcnt = net->xfrm.policy_count[XFRM_POLICY_OUT]; si->fwdcnt = net->xfrm.policy_count[XFRM_POLICY_FWD]; @@ -513,7 +508,7 @@ void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si) si->fwdscnt = net->xfrm.policy_count[XFRM_POLICY_FWD+XFRM_POLICY_MAX]; si->spdhcnt = net->xfrm.policy_idx_hmask; si->spdhmcnt = xfrm_policy_hashmax; - read_unlock_bh(&xfrm_policy_lock); + read_unlock_bh(&net->xfrm.xfrm_policy_lock); } EXPORT_SYMBOL(xfrm_spd_getinfo); @@ -636,7 +631,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl) struct hlist_head *chain; struct hlist_node *newpos; - write_lock_bh(&xfrm_policy_lock); + write_lock_bh(&net->xfrm.xfrm_policy_lock); chain = policy_hash_bysel(net, &policy->selector, policy->family, dir); delpol = NULL; newpos = NULL; @@ -647,7 +642,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl) xfrm_sec_ctx_match(pol->security, policy->security) && !WARN_ON(delpol)) { if (excl) { - write_unlock_bh(&xfrm_policy_lock); + write_unlock_bh(&net->xfrm.xfrm_policy_lock); return -EEXIST; } delpol = pol; @@ -685,7 +680,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl) if (!mod_timer(&policy->timer, jiffies + HZ)) xfrm_pol_hold(policy); list_add(&policy->walk.all, &net->xfrm.policy_all); - write_unlock_bh(&xfrm_policy_lock); + write_unlock_bh(&net->xfrm.xfrm_policy_lock); if (delpol) xfrm_policy_kill(delpol); @@ -705,7 +700,7 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u8 type, struct hlist_head *chain; *err = 0; - write_lock_bh(&xfrm_policy_lock); + write_lock_bh(&net->xfrm.xfrm_policy_lock); chain = policy_hash_bysel(net, sel, sel->family, dir); ret = NULL; hlist_for_each_entry(pol, chain, bydst) { @@ -718,7 +713,7 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u8 type, *err = security_xfrm_policy_delete( pol->security); if (*err) { - write_unlock_bh(&xfrm_policy_lock); + write_unlock_bh(&net->xfrm.xfrm_policy_lock); return pol; } __xfrm_policy_unlink(pol, dir); @@ -727,7 +722,7 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u8 type, break; } } - write_unlock_bh(&xfrm_policy_lock); + write_unlock_bh(&net->xfrm.xfrm_policy_lock); if (ret && delete) xfrm_policy_kill(ret); @@ -746,7 +741,7 @@ struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8 type, return NULL; *err = 0; - write_lock_bh(&xfrm_policy_lock); + write_lock_bh(&net->xfrm.xfrm_policy_lock); chain = net->xfrm.policy_byidx + idx_hash(net, id); ret = NULL; hlist_for_each_entry(pol, chain, byidx) { @@ -757,7 +752,7 @@ struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8 type, *err = security_xfrm_policy_delete( pol->security); if (*err) { - write_unlock_bh(&xfrm_policy_lock); + write_unlock_bh(&net->xfrm.xfrm_policy_lock); return pol; } __xfrm_policy_unlink(pol, dir); @@ -766,7 +761,7 @@ struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8 type, break; } } - write_unlock_bh(&xfrm_policy_lock); + write_unlock_bh(&net->xfrm.xfrm_policy_lock); if (ret && delete) xfrm_policy_kill(ret); @@ -829,7 +824,7 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info) { int dir, err = 0, cnt = 0; - write_lock_bh(&xfrm_policy_lock); + write_lock_bh(&net->xfrm.xfrm_policy_lock); err = xfrm_policy_flush_secctx_check(net, type, audit_info); if (err) @@ -845,7 +840,7 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info) if (pol->type != type) continue; __xfrm_policy_unlink(pol, dir); - write_unlock_bh(&xfrm_policy_lock); + write_unlock_bh(&net->xfrm.xfrm_policy_lock); cnt++; xfrm_audit_policy_delete(pol, 1, audit_info->loginuid, @@ -854,7 +849,7 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info) xfrm_policy_kill(pol); - write_lock_bh(&xfrm_policy_lock); + write_lock_bh(&net->xfrm.xfrm_policy_lock); goto again1; } @@ -866,7 +861,7 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info) if (pol->type != type) continue; __xfrm_policy_unlink(pol, dir); - write_unlock_bh(&xfrm_policy_lock); + write_unlock_bh(&net->xfrm.xfrm_policy_lock); cnt++; xfrm_audit_policy_delete(pol, 1, @@ -875,7 +870,7 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info) audit_info->secid); xfrm_policy_kill(pol); - write_lock_bh(&xfrm_policy_lock); + write_lock_bh(&net->xfrm.xfrm_policy_lock); goto again2; } } @@ -884,7 +879,7 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info) if (!cnt) err = -ESRCH; out: - write_unlock_bh(&xfrm_policy_lock); + write_unlock_bh(&net->xfrm.xfrm_policy_lock); return err; } EXPORT_SYMBOL(xfrm_policy_flush); @@ -904,7 +899,7 @@ int xfrm_policy_walk(struct net *net, struct xfrm_policy_walk *walk, if (list_empty(&walk->walk.all) && walk->seq != 0) return 0; - write_lock_bh(&xfrm_policy_lock); + write_lock_bh(&net->xfrm.xfrm_policy_lock); if (list_empty(&walk->walk.all)) x = list_first_entry(&net->xfrm.policy_all, struct xfrm_policy_walk_entry, all); else @@ -930,7 +925,7 @@ int xfrm_policy_walk(struct net *net, struct xfrm_policy_walk *walk, } list_del_init(&walk->walk.all); out: - write_unlock_bh(&xfrm_policy_lock); + write_unlock_bh(&net->xfrm.xfrm_policy_lock); return error; } EXPORT_SYMBOL(xfrm_policy_walk); @@ -944,14 +939,14 @@ void xfrm_policy_walk_init(struct xfrm_policy_walk *walk, u8 type) } EXPORT_SYMBOL(xfrm_policy_walk_init); -void xfrm_policy_walk_done(struct xfrm_policy_walk *walk) +void xfrm_policy_walk_done(struct xfrm_policy_walk *walk, struct net *net) { if (list_empty(&walk->walk.all)) return; - write_lock_bh(&xfrm_policy_lock); + write_lock_bh(&net->xfrm.xfrm_policy_lock); /*FIXME where is net? */ list_del(&walk->walk.all); - write_unlock_bh(&xfrm_policy_lock); + write_unlock_bh(&net->xfrm.xfrm_policy_lock); } EXPORT_SYMBOL(xfrm_policy_walk_done); @@ -996,7 +991,7 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type, if (unlikely(!daddr || !saddr)) return NULL; - read_lock_bh(&xfrm_policy_lock); + read_lock_bh(&net->xfrm.xfrm_policy_lock); chain = policy_hash_direct(net, daddr, saddr, family, dir); ret = NULL; hlist_for_each_entry(pol, chain, bydst) { @@ -1032,7 +1027,7 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type, if (ret) xfrm_pol_hold(ret); fail: - read_unlock_bh(&xfrm_policy_lock); + read_unlock_bh(&net->xfrm.xfrm_policy_lock); return ret; } @@ -1109,8 +1104,9 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, const struct flowi *fl) { struct xfrm_policy *pol; + struct net *net = sock_net(sk); - read_lock_bh(&xfrm_policy_lock); + read_lock_bh(&net->xfrm.xfrm_policy_lock); if ((pol = sk->sk_policy[dir]) != NULL) { bool match = xfrm_selector_match(&pol->selector, fl, sk->sk_family); @@ -1134,7 +1130,7 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, pol = NULL; } out: - read_unlock_bh(&xfrm_policy_lock); + read_unlock_bh(&net->xfrm.xfrm_policy_lock); return pol; } @@ -1172,9 +1168,11 @@ static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol, int xfrm_policy_delete(struct xfrm_policy *pol, int dir) { - write_lock_bh(&xfrm_policy_lock); + struct net *net = xp_net(pol); + + write_lock_bh(&net->xfrm.xfrm_policy_lock); pol = __xfrm_policy_unlink(pol, dir); - write_unlock_bh(&xfrm_policy_lock); + write_unlock_bh(&net->xfrm.xfrm_policy_lock); if (pol) { xfrm_policy_kill(pol); return 0; @@ -1193,7 +1191,7 @@ int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol) return -EINVAL; #endif - write_lock_bh(&xfrm_policy_lock); + write_lock_bh(&net->xfrm.xfrm_policy_lock); old_pol = sk->sk_policy[dir]; sk->sk_policy[dir] = pol; if (pol) { @@ -1210,7 +1208,7 @@ int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol) */ __xfrm_policy_unlink(old_pol, XFRM_POLICY_MAX+dir); } - write_unlock_bh(&xfrm_policy_lock); + write_unlock_bh(&net->xfrm.xfrm_policy_lock); if (old_pol) { xfrm_policy_kill(old_pol); @@ -1221,6 +1219,7 @@ int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol) static struct xfrm_policy *clone_policy(const struct xfrm_policy *old, int dir) { struct xfrm_policy *newp = xfrm_policy_alloc(xp_net(old), GFP_ATOMIC); + struct net *net = xp_net(old); if (newp) { newp->selector = old->selector; @@ -1239,9 +1238,9 @@ static struct xfrm_policy *clone_policy(const struct xfrm_policy *old, int dir) newp->type = old->type; memcpy(newp->xfrm_vec, old->xfrm_vec, newp->xfrm_nr*sizeof(struct xfrm_tmpl)); - write_lock_bh(&xfrm_policy_lock); + write_lock_bh(&net->xfrm.xfrm_policy_lock); __xfrm_policy_link(newp, XFRM_POLICY_MAX+dir); - write_unlock_bh(&xfrm_policy_lock); + write_unlock_bh(&net->xfrm.xfrm_policy_lock); xfrm_pol_put(newp); } return newp; @@ -2112,10 +2111,10 @@ restart: dst_hold(&xdst->u.dst); - spin_lock_bh(&xfrm_policy_sk_bundle_lock); + spin_lock_bh(&net->xfrm.xfrm_policy_sk_bundle_lock); xdst->u.dst.next = xfrm_policy_sk_bundles; xfrm_policy_sk_bundles = &xdst->u.dst; - spin_unlock_bh(&xfrm_policy_sk_bundle_lock); + spin_unlock_bh(&net->xfrm.xfrm_policy_sk_bundle_lock); route = xdst->route; } @@ -2440,7 +2439,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, } xfrm_nr = ti; if (npols > 1) { - xfrm_tmpl_sort(stp, tpp, xfrm_nr, family); + xfrm_tmpl_sort(stp, tpp, xfrm_nr, family, net); tpp = stp; } @@ -2569,10 +2568,10 @@ static void __xfrm_garbage_collect(struct net *net) { struct dst_entry *head, *next; - spin_lock_bh(&xfrm_policy_sk_bundle_lock); + spin_lock_bh(&net->xfrm.xfrm_policy_sk_bundle_lock); head = xfrm_policy_sk_bundles; xfrm_policy_sk_bundles = NULL; - spin_unlock_bh(&xfrm_policy_sk_bundle_lock); + spin_unlock_bh(&net->xfrm.xfrm_policy_sk_bundle_lock); while (head) { next = head->next; @@ -2956,6 +2955,13 @@ static int __net_init xfrm_net_init(struct net *net) rv = xfrm_sysctl_init(net); if (rv < 0) goto out_sysctl; + + /* Initialize the per-net locks here */ + spin_lock_init(&net->xfrm.xfrm_state_lock); + rwlock_init(&net->xfrm.xfrm_policy_lock); + spin_lock_init(&net->xfrm.xfrm_policy_sk_bundle_lock); + mutex_init(&net->xfrm.xfrm_cfg_mutex); + return 0; out_sysctl: @@ -3082,7 +3088,7 @@ static struct xfrm_policy * xfrm_migrate_policy_find(const struct xfrm_selector struct hlist_head *chain; u32 priority = ~0U; - read_lock_bh(&xfrm_policy_lock); + read_lock_bh(&net->xfrm.xfrm_policy_lock); /*FIXME*/ chain = policy_hash_direct(net, &sel->daddr, &sel->saddr, sel->family, dir); hlist_for_each_entry(pol, chain, bydst) { if (xfrm_migrate_selector_match(sel, &pol->selector) && @@ -3105,7 +3111,7 @@ static struct xfrm_policy * xfrm_migrate_policy_find(const struct xfrm_selector if (ret) xfrm_pol_hold(ret); - read_unlock_bh(&xfrm_policy_lock); + read_unlock_bh(&net->xfrm.xfrm_policy_lock); return ret; } @@ -3236,7 +3242,7 @@ int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, /* Stage 2 - find and update state(s) */ for (i = 0, mp = m; i < num_migrate; i++, mp++) { - if ((x = xfrm_migrate_state_find(mp))) { + if ((x = xfrm_migrate_state_find(mp, net))) { x_cur[nx_cur] = x; nx_cur++; if ((xc = xfrm_state_migrate(x, mp))) { diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 68c2f357a183..290479d0746d 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -35,8 +35,6 @@ destination/tunnel endpoint. (output) */ -static DEFINE_SPINLOCK(xfrm_state_lock); - static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024; static inline unsigned int xfrm_dst_hash(struct net *net, @@ -127,7 +125,7 @@ static void xfrm_hash_resize(struct work_struct *work) goto out_unlock; } - spin_lock_bh(&xfrm_state_lock); + spin_lock_bh(&net->xfrm.xfrm_state_lock); nhashmask = (nsize / sizeof(struct hlist_head)) - 1U; for (i = net->xfrm.state_hmask; i >= 0; i--) @@ -144,7 +142,7 @@ static void xfrm_hash_resize(struct work_struct *work) net->xfrm.state_byspi = nspi; net->xfrm.state_hmask = nhashmask; - spin_unlock_bh(&xfrm_state_lock); + spin_unlock_bh(&net->xfrm.xfrm_state_lock); osize = (ohashmask + 1) * sizeof(struct hlist_head); xfrm_hash_free(odst, osize); @@ -535,14 +533,14 @@ int __xfrm_state_delete(struct xfrm_state *x) if (x->km.state != XFRM_STATE_DEAD) { x->km.state = XFRM_STATE_DEAD; - spin_lock(&xfrm_state_lock); + spin_lock(&net->xfrm.xfrm_state_lock); list_del(&x->km.all); hlist_del(&x->bydst); hlist_del(&x->bysrc); if (x->id.spi) hlist_del(&x->byspi); net->xfrm.state_num--; - spin_unlock(&xfrm_state_lock); + spin_unlock(&net->xfrm.xfrm_state_lock); /* All xfrm_state objects are created by xfrm_state_alloc. * The xfrm_state_alloc call gives a reference, and that @@ -603,7 +601,7 @@ int xfrm_state_flush(struct net *net, u8 proto, struct xfrm_audit *audit_info) { int i, err = 0, cnt = 0; - spin_lock_bh(&xfrm_state_lock); + spin_lock_bh(&net->xfrm.xfrm_state_lock); err = xfrm_state_flush_secctx_check(net, proto, audit_info); if (err) goto out; @@ -616,7 +614,7 @@ restart: if (!xfrm_state_kern(x) && xfrm_id_proto_match(x->id.proto, proto)) { xfrm_state_hold(x); - spin_unlock_bh(&xfrm_state_lock); + spin_unlock_bh(&net->xfrm.xfrm_state_lock); err = xfrm_state_delete(x); xfrm_audit_state_delete(x, err ? 0 : 1, @@ -627,7 +625,7 @@ restart: if (!err) cnt++; - spin_lock_bh(&xfrm_state_lock); + spin_lock_bh(&net->xfrm.xfrm_state_lock); goto restart; } } @@ -636,7 +634,7 @@ restart: err = 0; out: - spin_unlock_bh(&xfrm_state_lock); + spin_unlock_bh(&net->xfrm.xfrm_state_lock); wake_up(&net->xfrm.km_waitq); return err; } @@ -644,11 +642,11 @@ EXPORT_SYMBOL(xfrm_state_flush); void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si) { - spin_lock_bh(&xfrm_state_lock); + spin_lock_bh(&net->xfrm.xfrm_state_lock); si->sadcnt = net->xfrm.state_num; si->sadhcnt = net->xfrm.state_hmask; si->sadhmcnt = xfrm_state_hashmax; - spin_unlock_bh(&xfrm_state_lock); + spin_unlock_bh(&net->xfrm.xfrm_state_lock); } EXPORT_SYMBOL(xfrm_sad_getinfo); @@ -801,7 +799,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr, to_put = NULL; - spin_lock_bh(&xfrm_state_lock); + spin_lock_bh(&net->xfrm.xfrm_state_lock); h = xfrm_dst_hash(net, daddr, saddr, tmpl->reqid, encap_family); hlist_for_each_entry(x, net->xfrm.state_bydst+h, bydst) { if (x->props.family == encap_family && @@ -886,7 +884,7 @@ out: xfrm_state_hold(x); else *err = acquire_in_progress ? -EAGAIN : error; - spin_unlock_bh(&xfrm_state_lock); + spin_unlock_bh(&net->xfrm.xfrm_state_lock); if (to_put) xfrm_state_put(to_put); return x; @@ -900,7 +898,7 @@ xfrm_stateonly_find(struct net *net, u32 mark, unsigned int h; struct xfrm_state *rx = NULL, *x = NULL; - spin_lock(&xfrm_state_lock); + spin_lock(&net->xfrm.xfrm_state_lock); h = xfrm_dst_hash(net, daddr, saddr, reqid, family); hlist_for_each_entry(x, net->xfrm.state_bydst+h, bydst) { if (x->props.family == family && @@ -918,7 +916,7 @@ xfrm_stateonly_find(struct net *net, u32 mark, if (rx) xfrm_state_hold(rx); - spin_unlock(&xfrm_state_lock); + spin_unlock(&net->xfrm.xfrm_state_lock); return rx; @@ -957,7 +955,7 @@ static void __xfrm_state_insert(struct xfrm_state *x) xfrm_hash_grow_check(net, x->bydst.next != NULL); } -/* xfrm_state_lock is held */ +/* net->xfrm.xfrm_state_lock is held */ static void __xfrm_state_bump_genids(struct xfrm_state *xnew) { struct net *net = xs_net(xnew); @@ -980,14 +978,16 @@ static void __xfrm_state_bump_genids(struct xfrm_state *xnew) void xfrm_state_insert(struct xfrm_state *x) { - spin_lock_bh(&xfrm_state_lock); + struct net *net = xs_net(x); + + spin_lock_bh(&net->xfrm.xfrm_state_lock); __xfrm_state_bump_genids(x); __xfrm_state_insert(x); - spin_unlock_bh(&xfrm_state_lock); + spin_unlock_bh(&net->xfrm.xfrm_state_lock); } EXPORT_SYMBOL(xfrm_state_insert); -/* xfrm_state_lock is held */ +/* net->xfrm.xfrm_state_lock is held */ static struct xfrm_state *__find_acq_core(struct net *net, const struct xfrm_mark *m, unsigned short family, u8 mode, @@ -1079,7 +1079,7 @@ int xfrm_state_add(struct xfrm_state *x) to_put = NULL; - spin_lock_bh(&xfrm_state_lock); + spin_lock_bh(&net->xfrm.xfrm_state_lock); x1 = __xfrm_state_locate(x, use_spi, family); if (x1) { @@ -1108,7 +1108,7 @@ int xfrm_state_add(struct xfrm_state *x) err = 0; out: - spin_unlock_bh(&xfrm_state_lock); + spin_unlock_bh(&net->xfrm.xfrm_state_lock); if (x1) { xfrm_state_delete(x1); @@ -1203,16 +1203,16 @@ out: return NULL; } -/* xfrm_state_lock is held */ -struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m) +/* net->xfrm.xfrm_state_lock is held */ +struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *net) { unsigned int h; struct xfrm_state *x; if (m->reqid) { - h = xfrm_dst_hash(&init_net, &m->old_daddr, &m->old_saddr, + h = xfrm_dst_hash(net, &m->old_daddr, &m->old_saddr, m->reqid, m->old_family); - hlist_for_each_entry(x, init_net.xfrm.state_bydst+h, bydst) { + hlist_for_each_entry(x, net->xfrm.state_bydst+h, bydst) { if (x->props.mode != m->mode || x->id.proto != m->proto) continue; @@ -1227,9 +1227,9 @@ struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m) return x; } } else { - h = xfrm_src_hash(&init_net, &m->old_daddr, &m->old_saddr, + h = xfrm_src_hash(net, &m->old_daddr, &m->old_saddr, m->old_family); - hlist_for_each_entry(x, init_net.xfrm.state_bysrc+h, bysrc) { + hlist_for_each_entry(x, net->xfrm.state_bysrc+h, bysrc) { if (x->props.mode != m->mode || x->id.proto != m->proto) continue; @@ -1283,10 +1283,11 @@ int xfrm_state_update(struct xfrm_state *x) struct xfrm_state *x1, *to_put; int err; int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY); + struct net *net = xs_net(x); to_put = NULL; - spin_lock_bh(&xfrm_state_lock); + spin_lock_bh(&net->xfrm.xfrm_state_lock); x1 = __xfrm_state_locate(x, use_spi, x->props.family); err = -ESRCH; @@ -1306,7 +1307,7 @@ int xfrm_state_update(struct xfrm_state *x) err = 0; out: - spin_unlock_bh(&xfrm_state_lock); + spin_unlock_bh(&net->xfrm.xfrm_state_lock); if (to_put) xfrm_state_put(to_put); @@ -1377,9 +1378,9 @@ xfrm_state_lookup(struct net *net, u32 mark, const xfrm_address_t *daddr, __be32 { struct xfrm_state *x; - spin_lock_bh(&xfrm_state_lock); + spin_lock_bh(&net->xfrm.xfrm_state_lock); x = __xfrm_state_lookup(net, mark, daddr, spi, proto, family); - spin_unlock_bh(&xfrm_state_lock); + spin_unlock_bh(&net->xfrm.xfrm_state_lock); return x; } EXPORT_SYMBOL(xfrm_state_lookup); @@ -1391,9 +1392,9 @@ xfrm_state_lookup_byaddr(struct net *net, u32 mark, { struct xfrm_state *x; - spin_lock_bh(&xfrm_state_lock); + spin_lock_bh(&net->xfrm.xfrm_state_lock); x = __xfrm_state_lookup_byaddr(net, mark, daddr, saddr, proto, family); - spin_unlock_bh(&xfrm_state_lock); + spin_unlock_bh(&net->xfrm.xfrm_state_lock); return x; } EXPORT_SYMBOL(xfrm_state_lookup_byaddr); @@ -1405,9 +1406,9 @@ xfrm_find_acq(struct net *net, const struct xfrm_mark *mark, u8 mode, u32 reqid, { struct xfrm_state *x; - spin_lock_bh(&xfrm_state_lock); + spin_lock_bh(&net->xfrm.xfrm_state_lock); x = __find_acq_core(net, mark, family, mode, reqid, proto, daddr, saddr, create); - spin_unlock_bh(&xfrm_state_lock); + spin_unlock_bh(&net->xfrm.xfrm_state_lock); return x; } @@ -1416,17 +1417,17 @@ EXPORT_SYMBOL(xfrm_find_acq); #ifdef CONFIG_XFRM_SUB_POLICY int xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n, - unsigned short family) + unsigned short family, struct net *net) { int err = 0; struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); if (!afinfo) return -EAFNOSUPPORT; - spin_lock_bh(&xfrm_state_lock); + spin_lock_bh(&net->xfrm.xfrm_state_lock); /*FIXME*/ if (afinfo->tmpl_sort) err = afinfo->tmpl_sort(dst, src, n); - spin_unlock_bh(&xfrm_state_lock); + spin_unlock_bh(&net->xfrm.xfrm_state_lock); xfrm_state_put_afinfo(afinfo); return err; } @@ -1438,13 +1439,15 @@ xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n, { int err = 0; struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); + struct net *net = xs_net(*dst); + if (!afinfo) return -EAFNOSUPPORT; - spin_lock_bh(&xfrm_state_lock); + spin_lock_bh(&net->xfrm.xfrm_state_lock); if (afinfo->state_sort) err = afinfo->state_sort(dst, src, n); - spin_unlock_bh(&xfrm_state_lock); + spin_unlock_bh(&net->xfrm.xfrm_state_lock); xfrm_state_put_afinfo(afinfo); return err; } @@ -1476,9 +1479,9 @@ struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq) { struct xfrm_state *x; - spin_lock_bh(&xfrm_state_lock); + spin_lock_bh(&net->xfrm.xfrm_state_lock); x = __xfrm_find_acq_byseq(net, mark, seq); - spin_unlock_bh(&xfrm_state_lock); + spin_unlock_bh(&net->xfrm.xfrm_state_lock); return x; } EXPORT_SYMBOL(xfrm_find_acq_byseq); @@ -1536,10 +1539,10 @@ int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high) } } if (x->id.spi) { - spin_lock_bh(&xfrm_state_lock); + spin_lock_bh(&net->xfrm.xfrm_state_lock); h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, x->props.family); hlist_add_head(&x->byspi, net->xfrm.state_byspi+h); - spin_unlock_bh(&xfrm_state_lock); + spin_unlock_bh(&net->xfrm.xfrm_state_lock); err = 0; } @@ -1562,7 +1565,7 @@ int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk, if (walk->seq != 0 && list_empty(&walk->all)) return 0; - spin_lock_bh(&xfrm_state_lock); + spin_lock_bh(&net->xfrm.xfrm_state_lock); if (list_empty(&walk->all)) x = list_first_entry(&net->xfrm.state_all, struct xfrm_state_walk, all); else @@ -1586,7 +1589,7 @@ int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk, } list_del_init(&walk->all); out: - spin_unlock_bh(&xfrm_state_lock); + spin_unlock_bh(&net->xfrm.xfrm_state_lock); return err; } EXPORT_SYMBOL(xfrm_state_walk); @@ -1600,14 +1603,14 @@ void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto) } EXPORT_SYMBOL(xfrm_state_walk_init); -void xfrm_state_walk_done(struct xfrm_state_walk *walk) +void xfrm_state_walk_done(struct xfrm_state_walk *walk, struct net *net) { if (list_empty(&walk->all)) return; - spin_lock_bh(&xfrm_state_lock); + spin_lock_bh(&net->xfrm.xfrm_state_lock); list_del(&walk->all); - spin_unlock_bh(&xfrm_state_lock); + spin_unlock_bh(&net->xfrm.xfrm_state_lock); } EXPORT_SYMBOL(xfrm_state_walk_done); @@ -2026,6 +2029,7 @@ int __net_init xfrm_state_init(struct net *net) INIT_HLIST_HEAD(&net->xfrm.state_gc_list); INIT_WORK(&net->xfrm.state_gc_work, xfrm_state_gc_task); init_waitqueue_head(&net->xfrm.km_waitq); + spin_lock_init(&net->xfrm.xfrm_state_lock); return 0; out_byspi: diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 840cc8d9fbe4..16c84608e81e 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -877,7 +877,10 @@ static int dump_one_state(struct xfrm_state *x, int count, void *ptr) static int xfrm_dump_sa_done(struct netlink_callback *cb) { struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1]; - xfrm_state_walk_done(walk); + struct sock *sk = cb->skb->sk; + struct net *net = sock_net(sk); + + xfrm_state_walk_done(walk, net); return 0; } @@ -1555,8 +1558,9 @@ static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr static int xfrm_dump_policy_done(struct netlink_callback *cb) { struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1]; + struct net *net = sock_net(cb->skb->sk); - xfrm_policy_walk_done(walk); + xfrm_policy_walk_done(walk, net); return 0; } @@ -2403,9 +2407,11 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) static void xfrm_netlink_rcv(struct sk_buff *skb) { - mutex_lock(&xfrm_cfg_mutex); + struct net *net = sock_net(skb->sk); + + mutex_lock(&net->xfrm.xfrm_cfg_mutex); netlink_rcv_skb(skb, &xfrm_user_rcv_msg); - mutex_unlock(&xfrm_cfg_mutex); + mutex_unlock(&net->xfrm.xfrm_cfg_mutex); } static inline size_t xfrm_expire_msgsize(void) -- cgit v1.2.3 From 5b8ef3415a21f173ab115e90ec92c071a03f22d7 Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Tue, 27 Aug 2013 13:43:30 +0200 Subject: xfrm: Remove ancient sleeping when the SA is in acquire state We now queue packets to the policy if the states are not yet resolved, this replaces the ancient sleeping code. Also the sleeping can cause indefinite task hangs if the needed state does not get resolved. Signed-off-by: Steffen Klassert --- include/net/netns/xfrm.h | 2 -- net/key/af_key.c | 5 ++--- net/xfrm/xfrm_policy.c | 21 ++------------------- net/xfrm/xfrm_state.c | 21 +-------------------- 4 files changed, 5 insertions(+), 44 deletions(-) (limited to 'include/net') diff --git a/include/net/netns/xfrm.h b/include/net/netns/xfrm.h index ea28404e9d79..1006a265beb3 100644 --- a/include/net/netns/xfrm.h +++ b/include/net/netns/xfrm.h @@ -33,8 +33,6 @@ struct netns_xfrm { struct hlist_head state_gc_list; struct work_struct state_gc_work; - wait_queue_head_t km_waitq; - struct list_head policy_all; struct hlist_head *policy_byidx; unsigned int policy_idx_hmask; diff --git a/net/key/af_key.c b/net/key/af_key.c index 9a039acf37e8..5beabd8ba772 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -1380,10 +1380,9 @@ static int pfkey_acquire(struct sock *sk, struct sk_buff *skb, const struct sadb return 0; spin_lock_bh(&x->lock); - if (x->km.state == XFRM_STATE_ACQ) { + if (x->km.state == XFRM_STATE_ACQ) x->km.state = XFRM_STATE_ERROR; - wake_up(&net->xfrm.km_waitq); - } + spin_unlock_bh(&x->lock); xfrm_state_put(x); return 0; diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 73b04d3df44e..a7487f34e813 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1901,8 +1901,7 @@ static struct xfrm_dst *xfrm_create_dummy_bundle(struct net *net, if (IS_ERR(xdst)) return xdst; - if (net->xfrm.sysctl_larval_drop || num_xfrms <= 0 || - (fl->flowi_flags & FLOWI_FLAG_CAN_SLEEP)) + if (net->xfrm.sysctl_larval_drop || num_xfrms <= 0) return xdst; dst1 = &xdst->u.dst; @@ -2077,7 +2076,6 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig, u8 dir = policy_to_flow_dir(XFRM_POLICY_OUT); int i, err, num_pols, num_xfrms = 0, drop_pols = 0; -restart: dst = NULL; xdst = NULL; route = NULL; @@ -2157,23 +2155,8 @@ restart: return make_blackhole(net, family, dst_orig); } - if (fl->flowi_flags & FLOWI_FLAG_CAN_SLEEP) { - DECLARE_WAITQUEUE(wait, current); - add_wait_queue(&net->xfrm.km_waitq, &wait); - set_current_state(TASK_INTERRUPTIBLE); - schedule(); - set_current_state(TASK_RUNNING); - remove_wait_queue(&net->xfrm.km_waitq, &wait); - - if (!signal_pending(current)) { - dst_release(dst); - goto restart; - } - - err = -ERESTART; - } else - err = -EAGAIN; + err = -EAGAIN; XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTNOSTATES); goto error; diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 290479d0746d..e845066547c4 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -372,8 +372,6 @@ static void xfrm_state_gc_task(struct work_struct *work) hlist_for_each_entry_safe(x, tmp, &gc_list, gclist) xfrm_state_gc_destroy(x); - - wake_up(&net->xfrm.km_waitq); } static inline unsigned long make_jiffies(long secs) @@ -388,7 +386,6 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer * me) { struct tasklet_hrtimer *thr = container_of(me, struct tasklet_hrtimer, timer); struct xfrm_state *x = container_of(thr, struct xfrm_state, mtimer); - struct net *net = xs_net(x); unsigned long now = get_seconds(); long next = LONG_MAX; int warn = 0; @@ -458,12 +455,8 @@ resched: goto out; expired: - if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) { + if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) x->km.state = XFRM_STATE_EXPIRED; - wake_up(&net->xfrm.km_waitq); - next = 2; - goto resched; - } err = __xfrm_state_delete(x); if (!err) @@ -635,7 +628,6 @@ restart: out: spin_unlock_bh(&net->xfrm.xfrm_state_lock); - wake_up(&net->xfrm.km_waitq); return err; } EXPORT_SYMBOL(xfrm_state_flush); @@ -948,8 +940,6 @@ static void __xfrm_state_insert(struct xfrm_state *x) if (x->replay_maxage) mod_timer(&x->rtimer, jiffies + x->replay_maxage); - wake_up(&net->xfrm.km_waitq); - net->xfrm.state_num++; xfrm_hash_grow_check(net, x->bydst.next != NULL); @@ -1658,16 +1648,12 @@ EXPORT_SYMBOL(km_state_notify); void km_state_expired(struct xfrm_state *x, int hard, u32 portid) { - struct net *net = xs_net(x); struct km_event c; c.data.hard = hard; c.portid = portid; c.event = XFRM_MSG_EXPIRE; km_state_notify(x, &c); - - if (hard) - wake_up(&net->xfrm.km_waitq); } EXPORT_SYMBOL(km_state_expired); @@ -1710,16 +1696,12 @@ EXPORT_SYMBOL(km_new_mapping); void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 portid) { - struct net *net = xp_net(pol); struct km_event c; c.data.hard = hard; c.portid = portid; c.event = XFRM_MSG_POLEXPIRE; km_policy_notify(pol, dir, &c); - - if (hard) - wake_up(&net->xfrm.km_waitq); } EXPORT_SYMBOL(km_policy_expired); @@ -2028,7 +2010,6 @@ int __net_init xfrm_state_init(struct net *net) INIT_WORK(&net->xfrm.state_hash_work, xfrm_hash_resize); INIT_HLIST_HEAD(&net->xfrm.state_gc_list); INIT_WORK(&net->xfrm.state_gc_work, xfrm_state_gc_task); - init_waitqueue_head(&net->xfrm.km_waitq); spin_lock_init(&net->xfrm.xfrm_state_lock); return 0; -- cgit v1.2.3 From 0e0d44ab4275549998567cd4700b43f7496eb62b Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Wed, 28 Aug 2013 08:04:14 +0200 Subject: net: Remove FLOWI_FLAG_CAN_SLEEP FLOWI_FLAG_CAN_SLEEP was used to notify xfrm about the posibility to sleep until the needed states are resolved. This code is gone, so FLOWI_FLAG_CAN_SLEEP is not needed anymore. Signed-off-by: Steffen Klassert --- include/net/flow.h | 3 +-- include/net/ipv6.h | 6 ++---- include/net/route.h | 8 +++----- net/dccp/ipv4.c | 2 +- net/dccp/ipv6.c | 8 ++++---- net/decnet/dn_route.c | 2 -- net/ipv4/af_inet.c | 2 +- net/ipv4/datagram.c | 2 +- net/ipv4/raw.c | 2 +- net/ipv4/tcp_ipv4.c | 2 +- net/ipv4/udp.c | 2 +- net/ipv6/af_inet6.c | 2 +- net/ipv6/datagram.c | 2 +- net/ipv6/inet6_connection_sock.c | 4 ++-- net/ipv6/ip6_output.c | 12 ++---------- net/ipv6/ping.c | 2 +- net/ipv6/raw.c | 2 +- net/ipv6/syncookies.c | 2 +- net/ipv6/tcp_ipv6.c | 4 ++-- net/ipv6/udp.c | 2 +- net/l2tp/l2tp_ip6.c | 2 +- net/sctp/ipv6.c | 4 ++-- 22 files changed, 31 insertions(+), 46 deletions(-) (limited to 'include/net') diff --git a/include/net/flow.h b/include/net/flow.h index 65ce471d2ab5..d23e7fa2042e 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -20,8 +20,7 @@ struct flowi_common { __u8 flowic_proto; __u8 flowic_flags; #define FLOWI_FLAG_ANYSRC 0x01 -#define FLOWI_FLAG_CAN_SLEEP 0x02 -#define FLOWI_FLAG_KNOWN_NH 0x04 +#define FLOWI_FLAG_KNOWN_NH 0x02 __u32 flowic_secid; }; diff --git a/include/net/ipv6.h b/include/net/ipv6.h index eb198acaac1d..26c4199ddc36 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -711,11 +711,9 @@ void ip6_flush_pending_frames(struct sock *sk); int ip6_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi6 *fl6); struct dst_entry *ip6_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6, - const struct in6_addr *final_dst, - bool can_sleep); + const struct in6_addr *final_dst); struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6, - const struct in6_addr *final_dst, - bool can_sleep); + const struct in6_addr *final_dst); struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *orig_dst); diff --git a/include/net/route.h b/include/net/route.h index f68c167280a7..638e3ebf76f3 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -239,14 +239,12 @@ static inline char rt_tos2priority(u8 tos) static inline void ip_route_connect_init(struct flowi4 *fl4, __be32 dst, __be32 src, u32 tos, int oif, u8 protocol, __be16 sport, __be16 dport, - struct sock *sk, bool can_sleep) + struct sock *sk) { __u8 flow_flags = 0; if (inet_sk(sk)->transparent) flow_flags |= FLOWI_FLAG_ANYSRC; - if (can_sleep) - flow_flags |= FLOWI_FLAG_CAN_SLEEP; flowi4_init_output(fl4, oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, protocol, flow_flags, dst, src, dport, sport); @@ -256,13 +254,13 @@ static inline struct rtable *ip_route_connect(struct flowi4 *fl4, __be32 dst, __be32 src, u32 tos, int oif, u8 protocol, __be16 sport, __be16 dport, - struct sock *sk, bool can_sleep) + struct sock *sk) { struct net *net = sock_net(sk); struct rtable *rt; ip_route_connect_init(fl4, dst, src, tos, oif, protocol, - sport, dport, sk, can_sleep); + sport, dport, sk); if (!dst || !src) { rt = __ip_route_output_key(net, fl4); diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index d9f65fc66db5..88299c29101d 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -75,7 +75,7 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) rt = ip_route_connect(fl4, nexthop, inet->inet_saddr, RT_CONN_FLAGS(sk), sk->sk_bound_dev_if, IPPROTO_DCCP, - orig_sport, orig_dport, sk, true); + orig_sport, orig_dport, sk); if (IS_ERR(rt)) return PTR_ERR(rt); diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 4ac71ff7c2e4..699a2d2a35ea 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -237,7 +237,7 @@ static int dccp_v6_send_response(struct sock *sk, struct request_sock *req) final_p = fl6_update_dst(&fl6, np->opt, &final); - dst = ip6_dst_lookup_flow(sk, &fl6, final_p, false); + dst = ip6_dst_lookup_flow(sk, &fl6, final_p); if (IS_ERR(dst)) { err = PTR_ERR(dst); dst = NULL; @@ -301,7 +301,7 @@ static void dccp_v6_ctl_send_reset(struct sock *sk, struct sk_buff *rxskb) security_skb_classify_flow(rxskb, flowi6_to_flowi(&fl6)); /* sk = NULL, but it is safe for now. RST socket required. */ - dst = ip6_dst_lookup_flow(ctl_sk, &fl6, NULL, false); + dst = ip6_dst_lookup_flow(ctl_sk, &fl6, NULL); if (!IS_ERR(dst)) { skb_dst_set(skb, dst); ip6_xmit(ctl_sk, skb, &fl6, NULL, 0); @@ -512,7 +512,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, fl6.fl6_sport = htons(ireq->ir_num); security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); - dst = ip6_dst_lookup_flow(sk, &fl6, final_p, false); + dst = ip6_dst_lookup_flow(sk, &fl6, final_p); if (IS_ERR(dst)) goto out; } @@ -932,7 +932,7 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, final_p = fl6_update_dst(&fl6, np->opt, &final); - dst = ip6_dst_lookup_flow(sk, &fl6, final_p, true); + dst = ip6_dst_lookup_flow(sk, &fl6, final_p); if (IS_ERR(dst)) { err = PTR_ERR(dst); goto failure; diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index fe32388ea24f..ad2efa5b861b 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -1288,8 +1288,6 @@ int dn_route_output_sock(struct dst_entry __rcu **pprt, struct flowidn *fl, stru err = __dn_route_output_key(pprt, fl, flags & MSG_TRYHARD); if (err == 0 && fl->flowidn_proto) { - if (!(flags & MSG_DONTWAIT)) - fl->flowidn_flags |= FLOWI_FLAG_CAN_SLEEP; *pprt = xfrm_lookup(&init_net, *pprt, flowidn_to_flowi(fl), sk, 0); if (IS_ERR(*pprt)) { diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 70011e029ac1..4f6d50bda093 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1133,7 +1133,7 @@ static int inet_sk_reselect_saddr(struct sock *sk) fl4 = &inet->cork.fl.u.ip4; rt = ip_route_connect(fl4, daddr, 0, RT_CONN_FLAGS(sk), sk->sk_bound_dev_if, sk->sk_protocol, - inet->inet_sport, inet->inet_dport, sk, false); + inet->inet_sport, inet->inet_dport, sk); if (IS_ERR(rt)) return PTR_ERR(rt); diff --git a/net/ipv4/datagram.c b/net/ipv4/datagram.c index 19e36376d2a0..8b5134c582f1 100644 --- a/net/ipv4/datagram.c +++ b/net/ipv4/datagram.c @@ -53,7 +53,7 @@ int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) rt = ip_route_connect(fl4, usin->sin_addr.s_addr, saddr, RT_CONN_FLAGS(sk), oif, sk->sk_protocol, - inet->inet_sport, usin->sin_port, sk, true); + inet->inet_sport, usin->sin_port, sk); if (IS_ERR(rt)) { err = PTR_ERR(rt); if (err == -ENETUNREACH) diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 23c3e5b5bb53..81e6cfd5a365 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -575,7 +575,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol, - inet_sk_flowi_flags(sk) | FLOWI_FLAG_CAN_SLEEP | + inet_sk_flowi_flags(sk) | (inet->hdrincl ? FLOWI_FLAG_KNOWN_NH : 0), daddr, saddr, 0, 0); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 067213924751..bbaf8cb45eb2 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -173,7 +173,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) rt = ip_route_connect(fl4, nexthop, inet->inet_saddr, RT_CONN_FLAGS(sk), sk->sk_bound_dev_if, IPPROTO_TCP, - orig_sport, orig_dport, sk, true); + orig_sport, orig_dport, sk); if (IS_ERR(rt)) { err = PTR_ERR(rt); if (err == -ENETUNREACH) diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 44f6a20fa29d..480fb95df2b6 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -990,7 +990,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, fl4 = &fl4_stack; flowi4_init_output(fl4, ipc.oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, sk->sk_protocol, - inet_sk_flowi_flags(sk)|FLOWI_FLAG_CAN_SLEEP, + inet_sk_flowi_flags(sk), faddr, saddr, dport, inet->inet_sport); security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 4fbdb7046d28..c33f98a4cf72 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -661,7 +661,7 @@ int inet6_sk_rebuild_header(struct sock *sk) final_p = fl6_update_dst(&fl6, np->opt, &final); - dst = ip6_dst_lookup_flow(sk, &fl6, final_p, false); + dst = ip6_dst_lookup_flow(sk, &fl6, final_p); if (IS_ERR(dst)) { sk->sk_route_caps = 0; sk->sk_err_soft = -PTR_ERR(dst); diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 8dfe1f4d3c1a..d7ef682cd984 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -171,7 +171,7 @@ ipv4_connected: opt = flowlabel ? flowlabel->opt : np->opt; final_p = fl6_update_dst(&fl6, opt, &final); - dst = ip6_dst_lookup_flow(sk, &fl6, final_p, true); + dst = ip6_dst_lookup_flow(sk, &fl6, final_p); err = 0; if (IS_ERR(dst)) { err = PTR_ERR(dst); diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index 77bb8afb141d..c9138189415a 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -86,7 +86,7 @@ struct dst_entry *inet6_csk_route_req(struct sock *sk, fl6->fl6_sport = htons(ireq->ir_num); security_req_classify_flow(req, flowi6_to_flowi(fl6)); - dst = ip6_dst_lookup_flow(sk, fl6, final_p, false); + dst = ip6_dst_lookup_flow(sk, fl6, final_p); if (IS_ERR(dst)) return NULL; @@ -216,7 +216,7 @@ static struct dst_entry *inet6_csk_route_socket(struct sock *sk, dst = __inet6_csk_dst_check(sk, np->dst_cookie); if (!dst) { - dst = ip6_dst_lookup_flow(sk, fl6, final_p, false); + dst = ip6_dst_lookup_flow(sk, fl6, final_p); if (!IS_ERR(dst)) __inet6_csk_dst_store(sk, dst, NULL, NULL); diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 4acdb63495db..3915cd883657 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -938,7 +938,6 @@ EXPORT_SYMBOL_GPL(ip6_dst_lookup); * @sk: socket which provides route info * @fl6: flow to lookup * @final_dst: final destination address for ipsec lookup - * @can_sleep: we are in a sleepable context * * This function performs a route lookup on the given flow. * @@ -946,8 +945,7 @@ EXPORT_SYMBOL_GPL(ip6_dst_lookup); * error code. */ struct dst_entry *ip6_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6, - const struct in6_addr *final_dst, - bool can_sleep) + const struct in6_addr *final_dst) { struct dst_entry *dst = NULL; int err; @@ -957,8 +955,6 @@ struct dst_entry *ip6_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6, return ERR_PTR(err); if (final_dst) fl6->daddr = *final_dst; - if (can_sleep) - fl6->flowi6_flags |= FLOWI_FLAG_CAN_SLEEP; return xfrm_lookup(sock_net(sk), dst, flowi6_to_flowi(fl6), sk, 0); } @@ -969,7 +965,6 @@ EXPORT_SYMBOL_GPL(ip6_dst_lookup_flow); * @sk: socket which provides the dst cache and route info * @fl6: flow to lookup * @final_dst: final destination address for ipsec lookup - * @can_sleep: we are in a sleepable context * * This function performs a route lookup on the given flow with the * possibility of using the cached route in the socket if it is valid. @@ -980,8 +975,7 @@ EXPORT_SYMBOL_GPL(ip6_dst_lookup_flow); * error code. */ struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6, - const struct in6_addr *final_dst, - bool can_sleep) + const struct in6_addr *final_dst) { struct dst_entry *dst = sk_dst_check(sk, inet6_sk(sk)->dst_cookie); int err; @@ -993,8 +987,6 @@ struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6, return ERR_PTR(err); if (final_dst) fl6->daddr = *final_dst; - if (can_sleep) - fl6->flowi6_flags |= FLOWI_FLAG_CAN_SLEEP; return xfrm_lookup(sock_net(sk), dst, flowi6_to_flowi(fl6), sk, 0); } diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index a83243c3d656..15d23b8c2129 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -145,7 +145,7 @@ int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, else if (!fl6.flowi6_oif) fl6.flowi6_oif = np->ucast_oif; - dst = ip6_sk_dst_lookup_flow(sk, &fl6, daddr, 1); + dst = ip6_sk_dst_lookup_flow(sk, &fl6, daddr); if (IS_ERR(dst)) return PTR_ERR(dst); rt = (struct rt6_info *) dst; diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 7fb4e14c467f..4945fb6d60a2 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -865,7 +865,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, fl6.flowi6_oif = np->ucast_oif; security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); - dst = ip6_dst_lookup_flow(sk, &fl6, final_p, true); + dst = ip6_dst_lookup_flow(sk, &fl6, final_p); if (IS_ERR(dst)) { err = PTR_ERR(dst); goto out; diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 535a3ad262f1..bb53a5e73c1a 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -247,7 +247,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) fl6.fl6_sport = inet_sk(sk)->inet_sport; security_req_classify_flow(req, flowi6_to_flowi(&fl6)); - dst = ip6_dst_lookup_flow(sk, &fl6, final_p, false); + dst = ip6_dst_lookup_flow(sk, &fl6, final_p); if (IS_ERR(dst)) goto out_free; } diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 0740f93a114a..f14456f40d02 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -258,7 +258,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); - dst = ip6_dst_lookup_flow(sk, &fl6, final_p, true); + dst = ip6_dst_lookup_flow(sk, &fl6, final_p); if (IS_ERR(dst)) { err = PTR_ERR(dst); goto failure; @@ -801,7 +801,7 @@ static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, * Underlying function will use this to retrieve the network * namespace */ - dst = ip6_dst_lookup_flow(ctl_sk, &fl6, NULL, false); + dst = ip6_dst_lookup_flow(ctl_sk, &fl6, NULL); if (!IS_ERR(dst)) { skb_dst_set(buff, dst); ip6_xmit(ctl_sk, buff, &fl6, NULL, tclass); diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index bcd5699313c3..ec3eb7f3f87c 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1221,7 +1221,7 @@ do_udp_sendmsg: security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); - dst = ip6_sk_dst_lookup_flow(sk, &fl6, final_p, true); + dst = ip6_sk_dst_lookup_flow(sk, &fl6, final_p); if (IS_ERR(dst)) { err = PTR_ERR(dst); dst = NULL; diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c index d9b437e55007..0c231f676049 100644 --- a/net/l2tp/l2tp_ip6.c +++ b/net/l2tp/l2tp_ip6.c @@ -598,7 +598,7 @@ static int l2tp_ip6_sendmsg(struct kiocb *iocb, struct sock *sk, security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); - dst = ip6_dst_lookup_flow(sk, &fl6, final_p, true); + dst = ip6_dst_lookup_flow(sk, &fl6, final_p); if (IS_ERR(dst)) { err = PTR_ERR(dst); goto out; diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 7567e6f1a920..dd15caec154d 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -263,7 +263,7 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, } final_p = fl6_update_dst(fl6, np->opt, &final); - dst = ip6_dst_lookup_flow(sk, fl6, final_p, false); + dst = ip6_dst_lookup_flow(sk, fl6, final_p); if (!asoc || saddr) goto out; @@ -322,7 +322,7 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, fl6->saddr = baddr->v6.sin6_addr; fl6->fl6_sport = baddr->v6.sin6_port; final_p = fl6_update_dst(fl6, np->opt, &final); - dst = ip6_dst_lookup_flow(sk, fl6, final_p, false); + dst = ip6_dst_lookup_flow(sk, fl6, final_p); } out: -- cgit v1.2.3 From 4b2f13a25133b115eb56771bd4a8e71a82aea968 Mon Sep 17 00:00:00 2001 From: Jeff Kirsher Date: Fri, 6 Dec 2013 06:28:48 -0800 Subject: sctp: Fix FSF address in file headers Several files refer to an old address for the Free Software Foundation in the file header comment. Resolve by replacing the address with the URL so that we do not have to keep updating the header comments anytime the address changes. CC: Vlad Yasevich CC: Neil Horman Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- include/linux/sctp.h | 5 ++--- include/net/sctp/sctp.h | 5 ++--- include/net/sctp/sm.h | 5 ++--- include/net/sctp/structs.h | 5 ++--- include/net/sctp/tsnmap.h | 5 ++--- include/net/sctp/ulpevent.h | 5 ++--- include/net/sctp/ulpqueue.h | 5 ++--- include/uapi/linux/sctp.h | 5 ++--- net/sctp/associola.c | 5 ++--- net/sctp/auth.c | 5 ++--- net/sctp/bind_addr.c | 5 ++--- net/sctp/chunk.c | 5 ++--- net/sctp/command.c | 5 ++--- net/sctp/debug.c | 5 ++--- net/sctp/endpointola.c | 5 ++--- net/sctp/input.c | 5 ++--- net/sctp/inqueue.c | 5 ++--- net/sctp/ipv6.c | 5 ++--- net/sctp/objcnt.c | 5 ++--- net/sctp/output.c | 5 ++--- net/sctp/outqueue.c | 5 ++--- net/sctp/primitive.c | 5 ++--- net/sctp/proc.c | 5 ++--- net/sctp/protocol.c | 5 ++--- net/sctp/sm_make_chunk.c | 5 ++--- net/sctp/sm_sideeffect.c | 5 ++--- net/sctp/sm_statefuns.c | 5 ++--- net/sctp/sm_statetable.c | 5 ++--- net/sctp/socket.c | 5 ++--- net/sctp/ssnmap.c | 5 ++--- net/sctp/sysctl.c | 5 ++--- net/sctp/transport.c | 5 ++--- net/sctp/tsnmap.c | 5 ++--- net/sctp/ulpevent.c | 5 ++--- net/sctp/ulpqueue.c | 5 ++--- 35 files changed, 70 insertions(+), 105 deletions(-) (limited to 'include/net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index 3bfe8d6ee248..c4c5eba26d9f 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -23,9 +23,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index c5fe80697f8d..610a8c8738fa 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -21,9 +21,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index 4ef75af340b6..7f4eeb340a54 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -21,9 +21,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email addresses: diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index ea0ca5f6e629..e416d6ac9c70 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -19,9 +19,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email addresses: diff --git a/include/net/sctp/tsnmap.h b/include/net/sctp/tsnmap.h index 54bbbe547303..31b8dbaad45a 100644 --- a/include/net/sctp/tsnmap.h +++ b/include/net/sctp/tsnmap.h @@ -22,9 +22,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/include/net/sctp/ulpevent.h b/include/net/sctp/ulpevent.h index 27b9f5c90153..daacb32b55b5 100644 --- a/include/net/sctp/ulpevent.h +++ b/include/net/sctp/ulpevent.h @@ -25,9 +25,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/include/net/sctp/ulpqueue.h b/include/net/sctp/ulpqueue.h index b0cf5d54d717..e0dce07b8794 100644 --- a/include/net/sctp/ulpqueue.h +++ b/include/net/sctp/ulpqueue.h @@ -24,9 +24,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email addresses: diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h index ca451e99b28b..266022a2be4a 100644 --- a/include/uapi/linux/sctp.h +++ b/include/uapi/linux/sctp.h @@ -22,9 +22,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 68a27f9796d2..66882a26713b 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -22,9 +22,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/auth.c b/net/sctp/auth.c index 46b5977978a1..5c9f64c1c906 100644 --- a/net/sctp/auth.c +++ b/net/sctp/auth.c @@ -16,9 +16,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c index 077bb070052b..871cdf9567e6 100644 --- a/net/sctp/bind_addr.c +++ b/net/sctp/bind_addr.c @@ -21,9 +21,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c index f2044fcb9dd1..5573e425b0c0 100644 --- a/net/sctp/chunk.c +++ b/net/sctp/chunk.c @@ -18,9 +18,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/command.c b/net/sctp/command.c index 3d9a9ff69c03..dd7375851618 100644 --- a/net/sctp/command.c +++ b/net/sctp/command.c @@ -19,9 +19,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/debug.c b/net/sctp/debug.c index e89015d8935a..95d7b15dad21 100644 --- a/net/sctp/debug.c +++ b/net/sctp/debug.c @@ -22,9 +22,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index 09b8daac87c8..6ffb6c1b13b7 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c @@ -23,9 +23,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/input.c b/net/sctp/input.c index 98b69bbecdd9..66038533ca5c 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -23,9 +23,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c index 5856932fdc38..4de12afa13d4 100644 --- a/net/sctp/inqueue.c +++ b/net/sctp/inqueue.c @@ -24,9 +24,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 7567e6f1a920..32db816ffbaa 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -21,9 +21,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/objcnt.c b/net/sctp/objcnt.c index 647396baa56f..0c28e8a55324 100644 --- a/net/sctp/objcnt.c +++ b/net/sctp/objcnt.c @@ -20,9 +20,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/output.c b/net/sctp/output.c index 0e2644d0a773..6371337e1fe7 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -20,9 +20,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index f51ba985a36e..b6b09f3f1a81 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -22,9 +22,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/primitive.c b/net/sctp/primitive.c index ce1ffd811775..ab8d9f96a177 100644 --- a/net/sctp/primitive.c +++ b/net/sctp/primitive.c @@ -23,9 +23,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/proc.c b/net/sctp/proc.c index 0c0642156842..de32e14f7398 100644 --- a/net/sctp/proc.c +++ b/net/sctp/proc.c @@ -16,9 +16,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 5e17092f4ada..19bd4c5bdae9 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -23,9 +23,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index fe690320b1e4..d9aaf9641aaa 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -23,9 +23,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 1a6eef39ab2f..02b7ad1ff467 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -22,9 +22,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index dfe3f36ff2aa..dd0eba919a8b 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -22,9 +22,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c index c5999b2dde7d..a4f17437fb22 100644 --- a/net/sctp/sm_statetable.c +++ b/net/sctp/sm_statetable.c @@ -22,9 +22,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 72046b9729a8..191cd9257806 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -28,9 +28,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/ssnmap.c b/net/sctp/ssnmap.c index 6007124aefa0..b9c8521c1a98 100644 --- a/net/sctp/ssnmap.c +++ b/net/sctp/ssnmap.c @@ -18,9 +18,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c index 6b36561a1b3b..80b17b5df6bb 100644 --- a/net/sctp/sysctl.c +++ b/net/sctp/sysctl.c @@ -19,9 +19,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/transport.c b/net/sctp/transport.c index e332efb124cc..1d4f3ac29baf 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -24,9 +24,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/tsnmap.c b/net/sctp/tsnmap.c index fbda20028285..7635f9f2311d 100644 --- a/net/sctp/tsnmap.c +++ b/net/sctp/tsnmap.c @@ -21,9 +21,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c index 81089ed65456..85c64658bd0b 100644 --- a/net/sctp/ulpevent.c +++ b/net/sctp/ulpevent.c @@ -22,9 +22,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c index 1c1484ed605d..a67470083be8 100644 --- a/net/sctp/ulpqueue.c +++ b/net/sctp/ulpqueue.c @@ -21,9 +21,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): -- cgit v1.2.3 From a6227e26d946bc56df47ca5fe418660a07ef8288 Mon Sep 17 00:00:00 2001 From: Jeff Kirsher Date: Fri, 6 Dec 2013 09:13:40 -0800 Subject: include/net/: Fix FSF address in file headers Several files refer to an old address for the Free Software Foundation in the file header comment. Resolve by replacing the address with the URL so that we do not have to keep updating the header comments anytime the address changes. Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- include/net/cipso_ipv4.h | 3 +-- include/net/dcbevent.h | 3 +-- include/net/dcbnl.h | 3 +-- include/net/irda/discovery.h | 4 +--- include/net/irda/ircomm_core.h | 4 +--- include/net/irda/ircomm_event.h | 4 +--- include/net/irda/ircomm_lmp.h | 4 +--- include/net/irda/ircomm_param.h | 4 +--- include/net/irda/ircomm_ttp.h | 4 +--- include/net/irda/ircomm_tty.h | 4 +--- include/net/irda/ircomm_tty_attach.h | 4 +--- include/net/irda/irda_device.h | 4 +--- include/net/irda/irlap_event.h | 4 +--- include/net/irda/irlap_frame.h | 4 +--- include/net/irda/parameters.h | 4 +--- include/net/irda/qos.h | 4 +--- include/net/mip6.h | 3 +-- include/net/netlabel.h | 3 +-- include/net/nfc/hci.h | 4 +--- include/net/nfc/llc.h | 4 +--- include/net/nfc/nci.h | 3 +-- include/net/nfc/nci_core.h | 3 +-- include/net/nfc/nfc.h | 4 +--- include/net/sctp/auth.h | 5 ++--- include/net/sctp/checksum.h | 5 ++--- include/net/sctp/command.h | 5 ++--- include/net/sctp/constants.h | 5 ++--- include/net/tc_act/tc_skbedit.h | 3 +-- 28 files changed, 32 insertions(+), 76 deletions(-) (limited to 'include/net') diff --git a/include/net/cipso_ipv4.h b/include/net/cipso_ipv4.h index a8c2ef6d3b93..2179d071f68f 100644 --- a/include/net/cipso_ipv4.h +++ b/include/net/cipso_ipv4.h @@ -26,8 +26,7 @@ * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see . * */ diff --git a/include/net/dcbevent.h b/include/net/dcbevent.h index d2f3041c0dfa..aec07c8a660a 100644 --- a/include/net/dcbevent.h +++ b/include/net/dcbevent.h @@ -11,8 +11,7 @@ * more details. * * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. + * this program; if not, see . * * Author: John Fastabend */ diff --git a/include/net/dcbnl.h b/include/net/dcbnl.h index fc5d5dcebb00..a975edf21b22 100644 --- a/include/net/dcbnl.h +++ b/include/net/dcbnl.h @@ -11,8 +11,7 @@ * more details. * * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. + * this program; if not, see . * * Author: Lucy Liu */ diff --git a/include/net/irda/discovery.h b/include/net/irda/discovery.h index 0ce93398720d..63ae32530567 100644 --- a/include/net/irda/discovery.h +++ b/include/net/irda/discovery.h @@ -23,9 +23,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * along with this program; if not, see . * ********************************************************************/ diff --git a/include/net/irda/ircomm_core.h b/include/net/irda/ircomm_core.h index 69b610acd2df..2a580ce9edad 100644 --- a/include/net/irda/ircomm_core.h +++ b/include/net/irda/ircomm_core.h @@ -22,9 +22,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * along with this program; if not, see . * ********************************************************************/ diff --git a/include/net/irda/ircomm_event.h b/include/net/irda/ircomm_event.h index bc0c6f31f1c6..5bbc32998d57 100644 --- a/include/net/irda/ircomm_event.h +++ b/include/net/irda/ircomm_event.h @@ -22,9 +22,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * along with this program; if not, see . * ********************************************************************/ diff --git a/include/net/irda/ircomm_lmp.h b/include/net/irda/ircomm_lmp.h index ae02106be590..5042a5021a04 100644 --- a/include/net/irda/ircomm_lmp.h +++ b/include/net/irda/ircomm_lmp.h @@ -22,9 +22,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * along with this program; if not, see . * ********************************************************************/ diff --git a/include/net/irda/ircomm_param.h b/include/net/irda/ircomm_param.h index e6678800c41f..1f67432321c4 100644 --- a/include/net/irda/ircomm_param.h +++ b/include/net/irda/ircomm_param.h @@ -22,9 +22,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * along with this program; if not, see . * ********************************************************************/ diff --git a/include/net/irda/ircomm_ttp.h b/include/net/irda/ircomm_ttp.h index 403081ed725c..c5627288bca3 100644 --- a/include/net/irda/ircomm_ttp.h +++ b/include/net/irda/ircomm_ttp.h @@ -22,9 +22,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * along with this program; if not, see . * ********************************************************************/ diff --git a/include/net/irda/ircomm_tty.h b/include/net/irda/ircomm_tty.h index 0224402260a7..8d4f588974bc 100644 --- a/include/net/irda/ircomm_tty.h +++ b/include/net/irda/ircomm_tty.h @@ -22,9 +22,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * along with this program; if not, see . * ********************************************************************/ diff --git a/include/net/irda/ircomm_tty_attach.h b/include/net/irda/ircomm_tty_attach.h index 0a63bbb972d7..20dcbdf258cf 100644 --- a/include/net/irda/ircomm_tty_attach.h +++ b/include/net/irda/ircomm_tty_attach.h @@ -22,9 +22,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * along with this program; if not, see . * ********************************************************************/ diff --git a/include/net/irda/irda_device.h b/include/net/irda/irda_device.h index 11417475a6c3..664bf8178412 100644 --- a/include/net/irda/irda_device.h +++ b/include/net/irda/irda_device.h @@ -24,9 +24,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * along with this program; if not, see . * ********************************************************************/ diff --git a/include/net/irda/irlap_event.h b/include/net/irda/irlap_event.h index f9d88da97af2..e4325fee1267 100644 --- a/include/net/irda/irlap_event.h +++ b/include/net/irda/irlap_event.h @@ -25,9 +25,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * along with this program; if not, see . * ********************************************************************/ diff --git a/include/net/irda/irlap_frame.h b/include/net/irda/irlap_frame.h index 57173ae398ae..cbc12a926e5f 100644 --- a/include/net/irda/irlap_frame.h +++ b/include/net/irda/irlap_frame.h @@ -24,9 +24,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * along with this program; if not, see . * ********************************************************************/ diff --git a/include/net/irda/parameters.h b/include/net/irda/parameters.h index c0d938847bd3..42713c931d1f 100644 --- a/include/net/irda/parameters.h +++ b/include/net/irda/parameters.h @@ -22,9 +22,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * along with this program; if not, see . * * Michel Dänzer , 10/2001 * - simplify irda_pv_t to avoid endianness issues diff --git a/include/net/irda/qos.h b/include/net/irda/qos.h index cc577dc0a0ef..05a5a249956f 100644 --- a/include/net/irda/qos.h +++ b/include/net/irda/qos.h @@ -22,9 +22,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * along with this program; if not, see . * ********************************************************************/ diff --git a/include/net/mip6.h b/include/net/mip6.h index 26ba99b5a4b1..0386b618908c 100644 --- a/include/net/mip6.h +++ b/include/net/mip6.h @@ -13,8 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see . */ /* * Authors: diff --git a/include/net/netlabel.h b/include/net/netlabel.h index 2c95d55f7914..24948bedb64c 100644 --- a/include/net/netlabel.h +++ b/include/net/netlabel.h @@ -22,8 +22,7 @@ * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see . * */ diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index 2eca2960ca9c..03c4650b548c 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -12,9 +12,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program; if not, see . */ #ifndef __NET_HCI_H diff --git a/include/net/nfc/llc.h b/include/net/nfc/llc.h index 400ab7ae749d..c25fbdee0d61 100644 --- a/include/net/nfc/llc.h +++ b/include/net/nfc/llc.h @@ -13,9 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program; if not, see . */ #ifndef __NFC_LLC_H_ diff --git a/include/net/nfc/nci.h b/include/net/nfc/nci.h index e5aa5acafea0..fbfa4e471abb 100644 --- a/include/net/nfc/nci.h +++ b/include/net/nfc/nci.h @@ -20,8 +20,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see . * */ diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index 6126f1f992b4..0ff070e8f8de 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -21,8 +21,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see . * */ diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index 82fc4e43fc6e..e80894bca1d0 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -16,9 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program; if not, see . */ #ifndef __NET_NFC_H diff --git a/include/net/sctp/auth.h b/include/net/sctp/auth.h index aa80bef3c9d5..f2d58aa37a6f 100644 --- a/include/net/sctp/auth.h +++ b/include/net/sctp/auth.h @@ -16,9 +16,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/include/net/sctp/checksum.h b/include/net/sctp/checksum.h index 6bd44fe94c26..4a5b9a306c69 100644 --- a/include/net/sctp/checksum.h +++ b/include/net/sctp/checksum.h @@ -19,9 +19,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/include/net/sctp/command.h b/include/net/sctp/command.h index 832f2191489c..4b7cd695e431 100644 --- a/include/net/sctp/command.h +++ b/include/net/sctp/command.h @@ -19,9 +19,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h index 2f0a565a0fd5..307728f622ef 100644 --- a/include/net/sctp/constants.h +++ b/include/net/sctp/constants.h @@ -19,9 +19,8 @@ * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, write to - * the Free Software Foundation, 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * along with GNU CC; see the file COPYING. If not, see + * . * * Please send any bug reports or fixes you make to the * email address(es): diff --git a/include/net/tc_act/tc_skbedit.h b/include/net/tc_act/tc_skbedit.h index e103fe02f375..dd5d86fab030 100644 --- a/include/net/tc_act/tc_skbedit.h +++ b/include/net/tc_act/tc_skbedit.h @@ -11,8 +11,7 @@ * more details. * * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. + * this program; if not, see . * * Author: Alexander Duyck */ -- cgit v1.2.3 From f54b311142a92ea2e42598e347b84e1655caf8e3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 5 Dec 2013 22:36:05 -0800 Subject: tcp: auto corking With the introduction of TCP Small Queues, TSO auto sizing, and TCP pacing, we can implement Automatic Corking in the kernel, to help applications doing small write()/sendmsg() to TCP sockets. Idea is to change tcp_push() to check if the current skb payload is under skb optimal size (a multiple of MSS bytes) If under 'size_goal', and at least one packet is still in Qdisc or NIC TX queues, set the TCP Small Queue Throttled bit, so that the push will be delayed up to TX completion time. This delay might allow the application to coalesce more bytes in the skb in following write()/sendmsg()/sendfile() system calls. The exact duration of the delay is depending on the dynamics of the system, and might be zero if no packet for this flow is actually held in Qdisc or NIC TX ring. Using FQ/pacing is a way to increase the probability of autocorking being triggered. Add a new sysctl (/proc/sys/net/ipv4/tcp_autocorking) to control this feature and default it to 1 (enabled) Add a new SNMP counter : nstat -a | grep TcpExtTCPAutoCorking This counter is incremented every time we detected skb was under used and its flush was deferred. Tested: Interesting effects when using line buffered commands under ssh. Excellent performance results in term of cpu usage and total throughput. lpq83:~# echo 1 >/proc/sys/net/ipv4/tcp_autocorking lpq83:~# perf stat ./super_netperf 4 -t TCP_STREAM -H lpq84 -- -m 128 9410.39 Performance counter stats for './super_netperf 4 -t TCP_STREAM -H lpq84 -- -m 128': 35209.439626 task-clock # 2.901 CPUs utilized 2,294 context-switches # 0.065 K/sec 101 CPU-migrations # 0.003 K/sec 4,079 page-faults # 0.116 K/sec 97,923,241,298 cycles # 2.781 GHz [83.31%] 51,832,908,236 stalled-cycles-frontend # 52.93% frontend cycles idle [83.30%] 25,697,986,603 stalled-cycles-backend # 26.24% backend cycles idle [66.70%] 102,225,978,536 instructions # 1.04 insns per cycle # 0.51 stalled cycles per insn [83.38%] 18,657,696,819 branches # 529.906 M/sec [83.29%] 91,679,646 branch-misses # 0.49% of all branches [83.40%] 12.136204899 seconds time elapsed lpq83:~# echo 0 >/proc/sys/net/ipv4/tcp_autocorking lpq83:~# perf stat ./super_netperf 4 -t TCP_STREAM -H lpq84 -- -m 128 6624.89 Performance counter stats for './super_netperf 4 -t TCP_STREAM -H lpq84 -- -m 128': 40045.864494 task-clock # 3.301 CPUs utilized 171 context-switches # 0.004 K/sec 53 CPU-migrations # 0.001 K/sec 4,080 page-faults # 0.102 K/sec 111,340,458,645 cycles # 2.780 GHz [83.34%] 61,778,039,277 stalled-cycles-frontend # 55.49% frontend cycles idle [83.31%] 29,295,522,759 stalled-cycles-backend # 26.31% backend cycles idle [66.67%] 108,654,349,355 instructions # 0.98 insns per cycle # 0.57 stalled cycles per insn [83.34%] 19,552,170,748 branches # 488.244 M/sec [83.34%] 157,875,417 branch-misses # 0.81% of all branches [83.34%] 12.130267788 seconds time elapsed Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 10 ++++++ include/net/tcp.h | 1 + include/uapi/linux/snmp.h | 1 + net/ipv4/proc.c | 1 + net/ipv4/sysctl_net_ipv4.c | 9 +++++ net/ipv4/tcp.c | 63 +++++++++++++++++++++++++++------- 6 files changed, 72 insertions(+), 13 deletions(-) (limited to 'include/net') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 3c12d9a7ed00..12ba2cd9f03d 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -156,6 +156,16 @@ tcp_app_win - INTEGER buffer. Value 0 is special, it means that nothing is reserved. Default: 31 +tcp_autocorking - BOOLEAN + Enable TCP auto corking : + When applications do consecutive small write()/sendmsg() system calls, + we try to coalesce these small writes as much as possible, to lower + total amount of sent packets. This is done if at least one prior + packet for the flow is waiting in Qdisc queues or device transmit + queue. Applications can still use TCP_CORK for optimal behavior + when they know how/when to uncork their sockets. + Default : 1 + tcp_available_congestion_control - STRING Shows the available congestion control choices that are registered. More congestion control algorithms may be available as modules, diff --git a/include/net/tcp.h b/include/net/tcp.h index 70e55d200610..f7e1ab2139ef 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -282,6 +282,7 @@ extern int sysctl_tcp_limit_output_bytes; extern int sysctl_tcp_challenge_ack_limit; extern unsigned int sysctl_tcp_notsent_lowat; extern int sysctl_tcp_min_tso_segs; +extern int sysctl_tcp_autocorking; extern atomic_long_t tcp_memory_allocated; extern struct percpu_counter tcp_sockets_allocated; diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h index 1bdb4a39d1e1..bbaba22f2d1b 100644 --- a/include/uapi/linux/snmp.h +++ b/include/uapi/linux/snmp.h @@ -258,6 +258,7 @@ enum LINUX_MIB_TCPFASTOPENCOOKIEREQD, /* TCPFastOpenCookieReqd */ LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES, /* TCPSpuriousRtxHostQueues */ LINUX_MIB_BUSYPOLLRXPACKETS, /* BusyPollRxPackets */ + LINUX_MIB_TCPAUTOCORKING, /* TCPAutoCorking */ __LINUX_MIB_MAX }; diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 4a0335854b89..8ecd7ad959b4 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -279,6 +279,7 @@ static const struct snmp_mib snmp4_net_list[] = { SNMP_MIB_ITEM("TCPFastOpenCookieReqd", LINUX_MIB_TCPFASTOPENCOOKIEREQD), SNMP_MIB_ITEM("TCPSpuriousRtxHostQueues", LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES), SNMP_MIB_ITEM("BusyPollRxPackets", LINUX_MIB_BUSYPOLLRXPACKETS), + SNMP_MIB_ITEM("TCPAutoCorking", LINUX_MIB_TCPAUTOCORKING), SNMP_MIB_SENTINEL }; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 3d69ec8dac57..38c8ec90ff68 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -732,6 +732,15 @@ static struct ctl_table ipv4_table[] = { .extra1 = &zero, .extra2 = &gso_max_segs, }, + { + .procname = "tcp_autocorking", + .data = &sysctl_tcp_autocorking, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &one, + }, { .procname = "udp_mem", .data = &sysctl_udp_mem, diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index c4638e6f0238..0ca87547becb 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -285,6 +285,8 @@ int sysctl_tcp_fin_timeout __read_mostly = TCP_FIN_TIMEOUT; int sysctl_tcp_min_tso_segs __read_mostly = 2; +int sysctl_tcp_autocorking __read_mostly = 1; + struct percpu_counter tcp_orphan_count; EXPORT_SYMBOL_GPL(tcp_orphan_count); @@ -619,19 +621,52 @@ static inline void tcp_mark_urg(struct tcp_sock *tp, int flags) tp->snd_up = tp->write_seq; } -static inline void tcp_push(struct sock *sk, int flags, int mss_now, - int nonagle) +/* If a not yet filled skb is pushed, do not send it if + * we have packets in Qdisc or NIC queues : + * Because TX completion will happen shortly, it gives a chance + * to coalesce future sendmsg() payload into this skb, without + * need for a timer, and with no latency trade off. + * As packets containing data payload have a bigger truesize + * than pure acks (dataless) packets, the last check prevents + * autocorking if we only have an ACK in Qdisc/NIC queues. + */ +static bool tcp_should_autocork(struct sock *sk, struct sk_buff *skb, + int size_goal) { - if (tcp_send_head(sk)) { - struct tcp_sock *tp = tcp_sk(sk); + return skb->len < size_goal && + sysctl_tcp_autocorking && + atomic_read(&sk->sk_wmem_alloc) > skb->truesize; +} + +static void tcp_push(struct sock *sk, int flags, int mss_now, + int nonagle, int size_goal) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct sk_buff *skb; - if (!(flags & MSG_MORE) || forced_push(tp)) - tcp_mark_push(tp, tcp_write_queue_tail(sk)); + if (!tcp_send_head(sk)) + return; + + skb = tcp_write_queue_tail(sk); + if (!(flags & MSG_MORE) || forced_push(tp)) + tcp_mark_push(tp, skb); + + tcp_mark_urg(tp, flags); + + if (tcp_should_autocork(sk, skb, size_goal)) { - tcp_mark_urg(tp, flags); - __tcp_push_pending_frames(sk, mss_now, - (flags & MSG_MORE) ? TCP_NAGLE_CORK : nonagle); + /* avoid atomic op if TSQ_THROTTLED bit is already set */ + if (!test_bit(TSQ_THROTTLED, &tp->tsq_flags)) { + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAUTOCORKING); + set_bit(TSQ_THROTTLED, &tp->tsq_flags); + } + return; } + + if (flags & MSG_MORE) + nonagle = TCP_NAGLE_CORK; + + __tcp_push_pending_frames(sk, mss_now, nonagle); } static int tcp_splice_data_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, @@ -934,7 +969,8 @@ new_segment: wait_for_sndbuf: set_bit(SOCK_NOSPACE, &sk->sk_socket->flags); wait_for_memory: - tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH); + tcp_push(sk, flags & ~MSG_MORE, mss_now, + TCP_NAGLE_PUSH, size_goal); if ((err = sk_stream_wait_memory(sk, &timeo)) != 0) goto do_error; @@ -944,7 +980,7 @@ wait_for_memory: out: if (copied && !(flags & MSG_SENDPAGE_NOTLAST)) - tcp_push(sk, flags, mss_now, tp->nonagle); + tcp_push(sk, flags, mss_now, tp->nonagle, size_goal); return copied; do_error: @@ -1225,7 +1261,8 @@ wait_for_sndbuf: set_bit(SOCK_NOSPACE, &sk->sk_socket->flags); wait_for_memory: if (copied) - tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH); + tcp_push(sk, flags & ~MSG_MORE, mss_now, + TCP_NAGLE_PUSH, size_goal); if ((err = sk_stream_wait_memory(sk, &timeo)) != 0) goto do_error; @@ -1236,7 +1273,7 @@ wait_for_memory: out: if (copied) - tcp_push(sk, flags, mss_now, tp->nonagle); + tcp_push(sk, flags, mss_now, tp->nonagle, size_goal); release_sock(sk); return copied + copied_syn; -- cgit v1.2.3 From 479840ffdbe4242e8a25349218c8e0859223aa35 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 6 Dec 2013 09:45:21 +0100 Subject: ipv6 addrconf: extend ifa_flags to u32 There is no more space in u8 ifa_flags. So do what davem suffested and add another netlink attr called IFA_FLAGS for carry more flags. Signed-off-by: Jiri Pirko Signed-off-by: Thomas Haller Signed-off-by: David S. Miller --- include/net/addrconf.h | 4 ++-- include/net/if_inet6.h | 2 +- include/uapi/linux/if_addr.h | 4 ++++ net/ipv6/addrconf.c | 32 ++++++++++++++++++++------------ 4 files changed, 27 insertions(+), 15 deletions(-) (limited to 'include/net') diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 86505bfa5d2c..e70278eef12a 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -81,9 +81,9 @@ int ipv6_dev_get_saddr(struct net *net, const struct net_device *dev, const struct in6_addr *daddr, unsigned int srcprefs, struct in6_addr *saddr); int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr, - unsigned char banned_flags); + u32 banned_flags); int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, - unsigned char banned_flags); + u32 banned_flags); int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2); void addrconf_join_solict(struct net_device *dev, const struct in6_addr *addr); void addrconf_leave_solict(struct inet6_dev *idev, const struct in6_addr *addr); diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h index 76d54270f2e2..b58c36c1c3f6 100644 --- a/include/net/if_inet6.h +++ b/include/net/if_inet6.h @@ -50,8 +50,8 @@ struct inet6_ifaddr { int state; + __u32 flags; __u8 dad_probes; - __u8 flags; __u16 scope; diff --git a/include/uapi/linux/if_addr.h b/include/uapi/linux/if_addr.h index 23357ab81a77..8ab0c2cc9b73 100644 --- a/include/uapi/linux/if_addr.h +++ b/include/uapi/linux/if_addr.h @@ -18,6 +18,9 @@ struct ifaddrmsg { * It makes no difference for normally configured broadcast interfaces, * but for point-to-point IFA_ADDRESS is DESTINATION address, * local address is supplied in IFA_LOCAL attribute. + * + * IFA_FLAGS is a u32 attribute that extends the u8 field ifa_flags. + * If present, the value from struct ifaddrmsg will be ignored. */ enum { IFA_UNSPEC, @@ -28,6 +31,7 @@ enum { IFA_ANYCAST, IFA_CACHEINFO, IFA_MULTICAST, + IFA_FLAGS, __IFA_MAX, }; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index d5fa5b8c443e..334a7e114e1d 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -1407,7 +1407,7 @@ try_nextdev: EXPORT_SYMBOL(ipv6_dev_get_saddr); int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr, - unsigned char banned_flags) + u32 banned_flags) { struct inet6_ifaddr *ifp; int err = -EADDRNOTAVAIL; @@ -1424,7 +1424,7 @@ int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr, } int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, - unsigned char banned_flags) + u32 banned_flags) { struct inet6_dev *idev; int err = -EADDRNOTAVAIL; @@ -2178,7 +2178,7 @@ ok: } if (ifp) { - int flags; + u32 flags; unsigned long now; struct inet6_ifaddr *ift; u32 stored_lft; @@ -2363,10 +2363,11 @@ err_exit: /* * Manual configuration of address on an interface */ -static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *pfx, +static int inet6_addr_add(struct net *net, int ifindex, + const struct in6_addr *pfx, const struct in6_addr *peer_pfx, - unsigned int plen, __u8 ifa_flags, __u32 prefered_lft, - __u32 valid_lft) + unsigned int plen, __u32 ifa_flags, + __u32 prefered_lft, __u32 valid_lft) { struct inet6_ifaddr *ifp; struct inet6_dev *idev; @@ -3351,7 +3352,7 @@ static void if6_seq_stop(struct seq_file *seq, void *v) static int if6_seq_show(struct seq_file *seq, void *v) { struct inet6_ifaddr *ifp = (struct inet6_ifaddr *)v; - seq_printf(seq, "%pi6 %02x %02x %02x %02x %8s\n", + seq_printf(seq, "%pi6 %02x %02x %02x %03x %8s\n", &ifp->addr, ifp->idev->dev->ifindex, ifp->prefix_len, @@ -3572,6 +3573,7 @@ static const struct nla_policy ifa_ipv6_policy[IFA_MAX+1] = { [IFA_ADDRESS] = { .len = sizeof(struct in6_addr) }, [IFA_LOCAL] = { .len = sizeof(struct in6_addr) }, [IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) }, + [IFA_FLAGS] = { .len = sizeof(u32) }, }; static int @@ -3595,7 +3597,7 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh) return inet6_addr_del(net, ifm->ifa_index, pfx, ifm->ifa_prefixlen); } -static int inet6_addr_modify(struct inet6_ifaddr *ifp, u8 ifa_flags, +static int inet6_addr_modify(struct inet6_ifaddr *ifp, u32 ifa_flags, u32 prefered_lft, u32 valid_lft) { u32 flags; @@ -3650,7 +3652,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) struct inet6_ifaddr *ifa; struct net_device *dev; u32 valid_lft = INFINITY_LIFE_TIME, preferred_lft = INFINITY_LIFE_TIME; - u8 ifa_flags; + u32 ifa_flags; int err; err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy); @@ -3677,8 +3679,10 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) if (dev == NULL) return -ENODEV; + ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : ifm->ifa_flags; + /* We ignore other flags so far. */ - ifa_flags = ifm->ifa_flags & (IFA_F_NODAD | IFA_F_HOMEADDRESS); + ifa_flags &= IFA_F_NODAD | IFA_F_HOMEADDRESS; ifa = ipv6_get_ifaddr(net, pfx, dev, 1); if (ifa == NULL) { @@ -3702,7 +3706,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) return err; } -static void put_ifaddrmsg(struct nlmsghdr *nlh, u8 prefixlen, u8 flags, +static void put_ifaddrmsg(struct nlmsghdr *nlh, u8 prefixlen, u32 flags, u8 scope, int ifindex) { struct ifaddrmsg *ifm; @@ -3745,7 +3749,8 @@ static inline int inet6_ifaddr_msgsize(void) return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) + nla_total_size(16) /* IFA_LOCAL */ + nla_total_size(16) /* IFA_ADDRESS */ - + nla_total_size(sizeof(struct ifa_cacheinfo)); + + nla_total_size(sizeof(struct ifa_cacheinfo)) + + nla_total_size(4) /* IFA_FLAGS */; } static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa, @@ -3793,6 +3798,9 @@ static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa, if (put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0) goto error; + if (nla_put_u32(skb, IFA_FLAGS, ifa->flags) < 0) + goto error; + return nlmsg_end(skb, nlh); error: -- cgit v1.2.3 From 97a2d41c47a2246c3387a937c62126c9faefe875 Mon Sep 17 00:00:00 2001 From: Eric Leblond Date: Fri, 6 Dec 2013 00:24:12 +0100 Subject: netfilter: xt_NFQUEUE: separate reusable code This patch prepares the addition of nft_queue module by moving reusable code into a header file. This patch also converts NFQUEUE to use prandom_u32 to initialize the random jhash seed as suggested by Florian Westphal. Signed-off-by: Eric Leblond Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_queue.h | 62 +++++++++++++++++++++++++++++++ net/netfilter/xt_NFQUEUE.c | 80 ++++++---------------------------------- 2 files changed, 74 insertions(+), 68 deletions(-) (limited to 'include/net') diff --git a/include/net/netfilter/nf_queue.h b/include/net/netfilter/nf_queue.h index c1d5b3e34a21..84a53d780306 100644 --- a/include/net/netfilter/nf_queue.h +++ b/include/net/netfilter/nf_queue.h @@ -1,6 +1,10 @@ #ifndef _NF_QUEUE_H #define _NF_QUEUE_H +#include +#include +#include + /* Each queued (to userspace) skbuff has one of these. */ struct nf_queue_entry { struct list_head list; @@ -33,4 +37,62 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict); bool nf_queue_entry_get_refs(struct nf_queue_entry *entry); void nf_queue_entry_release_refs(struct nf_queue_entry *entry); +static inline void init_hashrandom(u32 *jhash_initval) +{ + while (*jhash_initval == 0) + *jhash_initval = prandom_u32(); +} + +static inline u32 hash_v4(const struct sk_buff *skb, u32 jhash_initval) +{ + const struct iphdr *iph = ip_hdr(skb); + + /* packets in either direction go into same queue */ + if ((__force u32)iph->saddr < (__force u32)iph->daddr) + return jhash_3words((__force u32)iph->saddr, + (__force u32)iph->daddr, iph->protocol, jhash_initval); + + return jhash_3words((__force u32)iph->daddr, + (__force u32)iph->saddr, iph->protocol, jhash_initval); +} + +#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) +static inline u32 hash_v6(const struct sk_buff *skb, u32 jhash_initval) +{ + const struct ipv6hdr *ip6h = ipv6_hdr(skb); + u32 a, b, c; + + if ((__force u32)ip6h->saddr.s6_addr32[3] < + (__force u32)ip6h->daddr.s6_addr32[3]) { + a = (__force u32) ip6h->saddr.s6_addr32[3]; + b = (__force u32) ip6h->daddr.s6_addr32[3]; + } else { + b = (__force u32) ip6h->saddr.s6_addr32[3]; + a = (__force u32) ip6h->daddr.s6_addr32[3]; + } + + if ((__force u32)ip6h->saddr.s6_addr32[1] < + (__force u32)ip6h->daddr.s6_addr32[1]) + c = (__force u32) ip6h->saddr.s6_addr32[1]; + else + c = (__force u32) ip6h->daddr.s6_addr32[1]; + + return jhash_3words(a, b, c, jhash_initval); +} +#endif + +static inline u32 +nfqueue_hash(const struct sk_buff *skb, u16 queue, u16 queues_total, u8 family, + u32 jhash_initval) +{ + if (family == NFPROTO_IPV4) + queue += ((u64) hash_v4(skb, jhash_initval) * queues_total) >> 32; +#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) + else if (family == NFPROTO_IPV6) + queue += ((u64) hash_v6(skb, jhash_initval) * queues_total) >> 32; +#endif + + return queue; +} + #endif /* _NF_QUEUE_H */ diff --git a/net/netfilter/xt_NFQUEUE.c b/net/netfilter/xt_NFQUEUE.c index ed00fef58996..8f1779ff7e30 100644 --- a/net/netfilter/xt_NFQUEUE.c +++ b/net/netfilter/xt_NFQUEUE.c @@ -11,15 +11,13 @@ #include #include -#include -#include -#include - #include #include #include #include +#include + MODULE_AUTHOR("Harald Welte "); MODULE_DESCRIPTION("Xtables: packet forwarding to netlink"); MODULE_LICENSE("GPL"); @@ -28,7 +26,6 @@ MODULE_ALIAS("ip6t_NFQUEUE"); MODULE_ALIAS("arpt_NFQUEUE"); static u32 jhash_initval __read_mostly; -static bool rnd_inited __read_mostly; static unsigned int nfqueue_tg(struct sk_buff *skb, const struct xt_action_param *par) @@ -38,69 +35,16 @@ nfqueue_tg(struct sk_buff *skb, const struct xt_action_param *par) return NF_QUEUE_NR(tinfo->queuenum); } -static u32 hash_v4(const struct sk_buff *skb) -{ - const struct iphdr *iph = ip_hdr(skb); - - /* packets in either direction go into same queue */ - if ((__force u32)iph->saddr < (__force u32)iph->daddr) - return jhash_3words((__force u32)iph->saddr, - (__force u32)iph->daddr, iph->protocol, jhash_initval); - - return jhash_3words((__force u32)iph->daddr, - (__force u32)iph->saddr, iph->protocol, jhash_initval); -} - -#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) -static u32 hash_v6(const struct sk_buff *skb) -{ - const struct ipv6hdr *ip6h = ipv6_hdr(skb); - u32 a, b, c; - - if ((__force u32)ip6h->saddr.s6_addr32[3] < - (__force u32)ip6h->daddr.s6_addr32[3]) { - a = (__force u32) ip6h->saddr.s6_addr32[3]; - b = (__force u32) ip6h->daddr.s6_addr32[3]; - } else { - b = (__force u32) ip6h->saddr.s6_addr32[3]; - a = (__force u32) ip6h->daddr.s6_addr32[3]; - } - - if ((__force u32)ip6h->saddr.s6_addr32[1] < - (__force u32)ip6h->daddr.s6_addr32[1]) - c = (__force u32) ip6h->saddr.s6_addr32[1]; - else - c = (__force u32) ip6h->daddr.s6_addr32[1]; - - return jhash_3words(a, b, c, jhash_initval); -} -#endif - -static u32 -nfqueue_hash(const struct sk_buff *skb, const struct xt_action_param *par) -{ - const struct xt_NFQ_info_v1 *info = par->targinfo; - u32 queue = info->queuenum; - - if (par->family == NFPROTO_IPV4) - queue += ((u64) hash_v4(skb) * info->queues_total) >> 32; -#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) - else if (par->family == NFPROTO_IPV6) - queue += ((u64) hash_v6(skb) * info->queues_total) >> 32; -#endif - - return queue; -} - static unsigned int nfqueue_tg_v1(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_NFQ_info_v1 *info = par->targinfo; u32 queue = info->queuenum; - if (info->queues_total > 1) - queue = nfqueue_hash(skb, par); - + if (info->queues_total > 1) { + queue = nfqueue_hash(skb, queue, info->queues_total, + par->family, jhash_initval); + } return NF_QUEUE_NR(queue); } @@ -120,10 +64,8 @@ static int nfqueue_tg_check(const struct xt_tgchk_param *par) const struct xt_NFQ_info_v3 *info = par->targinfo; u32 maxid; - if (unlikely(!rnd_inited)) { - get_random_bytes(&jhash_initval, sizeof(jhash_initval)); - rnd_inited = true; - } + init_hashrandom(&jhash_initval); + if (info->queues_total == 0) { pr_err("NFQUEUE: number of total queues is 0\n"); return -EINVAL; @@ -154,8 +96,10 @@ nfqueue_tg_v3(struct sk_buff *skb, const struct xt_action_param *par) int cpu = smp_processor_id(); queue = info->queuenum + cpu % info->queues_total; - } else - queue = nfqueue_hash(skb, par); + } else { + queue = nfqueue_hash(skb, queue, info->queues_total, + par->family, jhash_initval); + } } ret = NF_QUEUE_NR(queue); -- cgit v1.2.3 From 53b834d2333aec1e60fcbfadfddd4ad472329570 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 8 Dec 2013 11:55:33 -0800 Subject: Bluetooth: Use macros for connectionless slave broadcast features Add the LMP feature constants for connectionless slave broadcast and use them for capability testing. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 6 ++++++ include/net/bluetooth/hci_core.h | 6 ++++++ net/bluetooth/hci_core.c | 6 +++--- 3 files changed, 15 insertions(+), 3 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 1784c48699f0..cc2da73055fa 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -275,6 +275,12 @@ enum { #define LMP_EXTFEATURES 0x80 /* Extended LMP features */ +#define LMP_CSB_MASTER 0x01 +#define LMP_CSB_SLAVE 0x02 +#define LMP_SYNC_TRAIN 0x04 +#define LMP_SYNC_SCAN 0x08 + +/* Host features */ #define LMP_HOST_SSP 0x01 #define LMP_HOST_LE 0x02 #define LMP_HOST_LE_BREDR 0x04 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index f8555ad7b104..b796161fb04e 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -798,6 +798,12 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #define lmp_transp_capable(dev) ((dev)->features[0][2] & LMP_TRANSPARENT) /* ----- Extended LMP capabilities ----- */ +#define lmp_csb_master_capable(dev) ((dev)->features[2][0] & LMP_CSB_MASTER) +#define lmp_csb_slave_capable(dev) ((dev)->features[2][0] & LMP_CSB_SLAVE) +#define lmp_sync_train_capable(dev) ((dev)->features[2][0] & LMP_SYNC_TRAIN) +#define lmp_sync_scan_capable(dev) ((dev)->features[2][0] & LMP_SYNC_SCAN) + +/* ----- Host capabilities ----- */ #define lmp_host_ssp_capable(dev) ((dev)->features[1][0] & LMP_HOST_SSP) #define lmp_host_le_capable(dev) (!!((dev)->features[1][0] & LMP_HOST_LE)) #define lmp_host_le_br_capable(dev) (!!((dev)->features[1][0] & LMP_HOST_LE_BREDR)) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 03e83558a411..8b8b5f80dd89 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1228,7 +1228,7 @@ static void hci_set_event_mask_page_2(struct hci_request *req) /* If Connectionless Slave Broadcast master role is supported * enable all necessary events for it. */ - if (hdev->features[2][0] & 0x01) { + if (lmp_csb_master_capable(hdev)) { events[1] |= 0x40; /* Triggered Clock Capture */ events[1] |= 0x80; /* Synchronization Train Complete */ events[2] |= 0x10; /* Slave Page Response Timeout */ @@ -1238,7 +1238,7 @@ static void hci_set_event_mask_page_2(struct hci_request *req) /* If Connectionless Slave Broadcast slave role is supported * enable all necessary events for it. */ - if (hdev->features[2][0] & 0x02) { + if (lmp_csb_slave_capable(hdev)) { events[2] |= 0x01; /* Synchronization Train Received */ events[2] |= 0x02; /* CSB Receive */ events[2] |= 0x04; /* CSB Timeout */ @@ -1309,7 +1309,7 @@ static void hci_init4_req(struct hci_request *req, unsigned long opt) hci_set_event_mask_page_2(req); /* Check for Synchronization Train support */ - if (hdev->features[2][0] & 0x04) + if (lmp_sync_train_capable(hdev)) hci_req_add(req, HCI_OP_READ_SYNC_TRAIN_PARAMS, 0, NULL); } -- cgit v1.2.3 From 95dc19299f741c986227ec33e23cbf9b3321f812 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 5 Dec 2013 11:12:02 -0800 Subject: pkt_sched: give visibility to mq slave qdiscs Commit 6da7c8fcbcbd ("qdisc: allow setting default queuing discipline") added the ability to change default qdisc from pfifo_fast to say fq But as most modern ethernet devices are multiqueue, we cant really see all the statistics from "tc -s qdisc show", as the default root qdisc is mq. This patch adds the calls to qdisc_list_add() to mq and mqprio Signed-off-by: Eric Dumazet Cc: Stephen Hemminger Signed-off-by: David S. Miller --- include/net/pkt_sched.h | 1 + net/sched/sch_api.c | 3 ++- net/sched/sch_mq.c | 13 +++++++++---- net/sched/sch_mqprio.c | 10 ++++++---- 4 files changed, 18 insertions(+), 9 deletions(-) (limited to 'include/net') diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index 59ec3cd15d68..891d80d2c4d2 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -88,6 +88,7 @@ int unregister_qdisc(struct Qdisc_ops *qops); void qdisc_get_default(char *id, size_t len); int qdisc_set_default(const char *id); +void qdisc_list_add(struct Qdisc *q); void qdisc_list_del(struct Qdisc *q); struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle); struct Qdisc *qdisc_lookup_class(struct net_device *dev, u32 handle); diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index cd81505662b8..547b4a88ae2a 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -271,11 +271,12 @@ static struct Qdisc *qdisc_match_from_root(struct Qdisc *root, u32 handle) return NULL; } -static void qdisc_list_add(struct Qdisc *q) +void qdisc_list_add(struct Qdisc *q) { if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS)) list_add_tail(&q->list, &qdisc_dev(q)->qdisc->list); } +EXPORT_SYMBOL(qdisc_list_add); void qdisc_list_del(struct Qdisc *q) { diff --git a/net/sched/sch_mq.c b/net/sched/sch_mq.c index 2e56185736d6..a8b2864a696b 100644 --- a/net/sched/sch_mq.c +++ b/net/sched/sch_mq.c @@ -78,14 +78,19 @@ static void mq_attach(struct Qdisc *sch) { struct net_device *dev = qdisc_dev(sch); struct mq_sched *priv = qdisc_priv(sch); - struct Qdisc *qdisc; + struct Qdisc *qdisc, *old; unsigned int ntx; for (ntx = 0; ntx < dev->num_tx_queues; ntx++) { qdisc = priv->qdiscs[ntx]; - qdisc = dev_graft_qdisc(qdisc->dev_queue, qdisc); - if (qdisc) - qdisc_destroy(qdisc); + old = dev_graft_qdisc(qdisc->dev_queue, qdisc); + if (old) + qdisc_destroy(old); +#ifdef CONFIG_NET_SCHED + if (ntx < dev->real_num_tx_queues) + qdisc_list_add(qdisc); +#endif + } kfree(priv->qdiscs); priv->qdiscs = NULL; diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c index d44c868cb537..6749e2f540d0 100644 --- a/net/sched/sch_mqprio.c +++ b/net/sched/sch_mqprio.c @@ -167,15 +167,17 @@ static void mqprio_attach(struct Qdisc *sch) { struct net_device *dev = qdisc_dev(sch); struct mqprio_sched *priv = qdisc_priv(sch); - struct Qdisc *qdisc; + struct Qdisc *qdisc, *old; unsigned int ntx; /* Attach underlying qdisc */ for (ntx = 0; ntx < dev->num_tx_queues; ntx++) { qdisc = priv->qdiscs[ntx]; - qdisc = dev_graft_qdisc(qdisc->dev_queue, qdisc); - if (qdisc) - qdisc_destroy(qdisc); + old = dev_graft_qdisc(qdisc->dev_queue, qdisc); + if (old) + qdisc_destroy(old); + if (ntx < dev->real_num_tx_queues) + qdisc_list_add(qdisc); } kfree(priv->qdiscs); priv->qdiscs = NULL; -- cgit v1.2.3 From 1f9248e5606afc6485255e38ad57bdac08fa7711 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sat, 7 Dec 2013 19:26:53 +0100 Subject: neigh: convert parms to an array This patch converts the neigh param members to an array. This allows easier manipulation which will be needed later on to provide better management of default values. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/neighbour.h | 48 +++++-- net/802/hippi.c | 4 +- net/core/neighbour.c | 327 ++++++++++++++++++++---------------------------- net/decnet/dn_neigh.c | 28 +++-- net/ipv4/arp.c | 33 ++--- net/ipv6/addrconf.c | 12 +- net/ipv6/ndisc.c | 43 ++++--- 7 files changed, 238 insertions(+), 257 deletions(-) (limited to 'include/net') diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 536501a3e58d..4cb5478e2f6e 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -37,6 +37,32 @@ struct neighbour; +enum { + NEIGH_VAR_MCAST_PROBES, + NEIGH_VAR_UCAST_PROBES, + NEIGH_VAR_APP_PROBES, + NEIGH_VAR_RETRANS_TIME, + NEIGH_VAR_BASE_REACHABLE_TIME, + NEIGH_VAR_DELAY_PROBE_TIME, + NEIGH_VAR_GC_STALETIME, + NEIGH_VAR_QUEUE_LEN_BYTES, + NEIGH_VAR_PROXY_QLEN, + NEIGH_VAR_ANYCAST_DELAY, + NEIGH_VAR_PROXY_DELAY, + NEIGH_VAR_LOCKTIME, +#define NEIGH_VAR_DATA_MAX (NEIGH_VAR_LOCKTIME + 1) + /* Following are used as a second way to access one of the above */ + NEIGH_VAR_QUEUE_LEN, /* same data as NEIGH_VAR_QUEUE_LEN_BYTES */ + NEIGH_VAR_RETRANS_TIME_MS, /* same data as NEIGH_VAR_RETRANS_TIME */ + NEIGH_VAR_BASE_REACHABLE_TIME_MS, /* same data as NEIGH_VAR_BASE_REACHABLE_TIME */ + /* Following are used by "default" only */ + NEIGH_VAR_GC_INTERVAL, + NEIGH_VAR_GC_THRESH1, + NEIGH_VAR_GC_THRESH2, + NEIGH_VAR_GC_THRESH3, + NEIGH_VAR_MAX +}; + struct neigh_parms { #ifdef CONFIG_NET_NS struct net *net; @@ -53,22 +79,18 @@ struct neigh_parms { atomic_t refcnt; struct rcu_head rcu_head; - int base_reachable_time; - int retrans_time; - int gc_staletime; int reachable_time; - int delay_probe_time; - - int queue_len_bytes; - int ucast_probes; - int app_probes; - int mcast_probes; - int anycast_delay; - int proxy_delay; - int proxy_qlen; - int locktime; + int data[NEIGH_VAR_DATA_MAX]; }; +static inline void neigh_var_set(struct neigh_parms *p, int index, int val) +{ + p->data[index] = val; +} + +#define NEIGH_VAR(p, attr) ((p)->data[NEIGH_VAR_ ## attr]) +#define NEIGH_VAR_SET(p, attr, val) neigh_var_set(p, NEIGH_VAR_ ## attr, val) + struct neigh_statistics { unsigned long allocs; /* number of allocated neighs */ unsigned long destroys; /* number of destroyed neighs */ diff --git a/net/802/hippi.c b/net/802/hippi.c index 51a1f530417d..a97a3bde77bb 100644 --- a/net/802/hippi.c +++ b/net/802/hippi.c @@ -172,14 +172,14 @@ EXPORT_SYMBOL(hippi_mac_addr); int hippi_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p) { /* Never send broadcast/multicast ARP messages */ - p->mcast_probes = 0; + NEIGH_VAR_SET(p, MCAST_PROBES, 0); /* In IPv6 unicast probes are valid even on NBMA, * because they are encapsulated in normal IPv6 protocol. * Should be a generic flag. */ if (p->tbl->family != AF_INET6) - p->ucast_probes = 0; + NEIGH_VAR_SET(p, UCAST_PROBES, 0); return 0; } EXPORT_SYMBOL(hippi_neigh_setup_dev); diff --git a/net/core/neighbour.c b/net/core/neighbour.c index ca15f32821fb..742fa10d4372 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -497,7 +497,7 @@ struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey, goto out_neigh_release; } - n->confirmed = jiffies - (n->parms->base_reachable_time << 1); + n->confirmed = jiffies - (NEIGH_VAR(n->parms, BASE_REACHABLE_TIME) << 1); write_lock_bh(&tbl->lock); nht = rcu_dereference_protected(tbl->nht, @@ -776,7 +776,7 @@ static void neigh_periodic_work(struct work_struct *work) tbl->last_rand = jiffies; for (p = &tbl->parms; p; p = p->next) p->reachable_time = - neigh_rand_reach_time(p->base_reachable_time); + neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME)); } for (i = 0 ; i < (1 << nht->hash_shift); i++) { @@ -799,7 +799,7 @@ static void neigh_periodic_work(struct work_struct *work) if (atomic_read(&n->refcnt) == 1 && (state == NUD_FAILED || - time_after(jiffies, n->used + n->parms->gc_staletime))) { + time_after(jiffies, n->used + NEIGH_VAR(n->parms, GC_STALETIME)))) { *np = n->next; n->dead = 1; write_unlock(&n->lock); @@ -822,12 +822,12 @@ next_elt: lockdep_is_held(&tbl->lock)); } out: - /* Cycle through all hash buckets every base_reachable_time/2 ticks. - * ARP entry timeouts range from 1/2 base_reachable_time to 3/2 - * base_reachable_time. + /* Cycle through all hash buckets every BASE_REACHABLE_TIME/2 ticks. + * ARP entry timeouts range from 1/2 BASE_REACHABLE_TIME to 3/2 + * BASE_REACHABLE_TIME. */ schedule_delayed_work(&tbl->gc_work, - tbl->parms.base_reachable_time >> 1); + NEIGH_VAR(&tbl->parms, BASE_REACHABLE_TIME) >> 1); write_unlock_bh(&tbl->lock); } @@ -835,8 +835,9 @@ static __inline__ int neigh_max_probes(struct neighbour *n) { struct neigh_parms *p = n->parms; return (n->nud_state & NUD_PROBE) ? - p->ucast_probes : - p->ucast_probes + p->app_probes + p->mcast_probes; + NEIGH_VAR(p, UCAST_PROBES) : + NEIGH_VAR(p, UCAST_PROBES) + NEIGH_VAR(p, APP_PROBES) + + NEIGH_VAR(p, MCAST_PROBES); } static void neigh_invalidate(struct neighbour *neigh) @@ -901,12 +902,13 @@ static void neigh_timer_handler(unsigned long arg) neigh_dbg(2, "neigh %p is still alive\n", neigh); next = neigh->confirmed + neigh->parms->reachable_time; } else if (time_before_eq(now, - neigh->used + neigh->parms->delay_probe_time)) { + neigh->used + + NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME))) { neigh_dbg(2, "neigh %p is delayed\n", neigh); neigh->nud_state = NUD_DELAY; neigh->updated = jiffies; neigh_suspect(neigh); - next = now + neigh->parms->delay_probe_time; + next = now + NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME); } else { neigh_dbg(2, "neigh %p is suspected\n", neigh); neigh->nud_state = NUD_STALE; @@ -916,7 +918,8 @@ static void neigh_timer_handler(unsigned long arg) } } else if (state & NUD_DELAY) { if (time_before_eq(now, - neigh->confirmed + neigh->parms->delay_probe_time)) { + neigh->confirmed + + NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME))) { neigh_dbg(2, "neigh %p is now reachable\n", neigh); neigh->nud_state = NUD_REACHABLE; neigh->updated = jiffies; @@ -928,11 +931,11 @@ static void neigh_timer_handler(unsigned long arg) neigh->nud_state = NUD_PROBE; neigh->updated = jiffies; atomic_set(&neigh->probes, 0); - next = now + neigh->parms->retrans_time; + next = now + NEIGH_VAR(neigh->parms, RETRANS_TIME); } } else { /* NUD_PROBE|NUD_INCOMPLETE */ - next = now + neigh->parms->retrans_time; + next = now + NEIGH_VAR(neigh->parms, RETRANS_TIME); } if ((neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) && @@ -973,13 +976,16 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb) goto out_unlock_bh; if (!(neigh->nud_state & (NUD_STALE | NUD_INCOMPLETE))) { - if (neigh->parms->mcast_probes + neigh->parms->app_probes) { + if (NEIGH_VAR(neigh->parms, MCAST_PROBES) + + NEIGH_VAR(neigh->parms, APP_PROBES)) { unsigned long next, now = jiffies; - atomic_set(&neigh->probes, neigh->parms->ucast_probes); + atomic_set(&neigh->probes, + NEIGH_VAR(neigh->parms, UCAST_PROBES)); neigh->nud_state = NUD_INCOMPLETE; neigh->updated = now; - next = now + max(neigh->parms->retrans_time, HZ/2); + next = now + max(NEIGH_VAR(neigh->parms, RETRANS_TIME), + HZ/2); neigh_add_timer(neigh, next); immediate_probe = true; } else { @@ -994,14 +1000,14 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb) neigh_dbg(2, "neigh %p is delayed\n", neigh); neigh->nud_state = NUD_DELAY; neigh->updated = jiffies; - neigh_add_timer(neigh, - jiffies + neigh->parms->delay_probe_time); + neigh_add_timer(neigh, jiffies + + NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME)); } if (neigh->nud_state == NUD_INCOMPLETE) { if (skb) { while (neigh->arp_queue_len_bytes + skb->truesize > - neigh->parms->queue_len_bytes) { + NEIGH_VAR(neigh->parms, QUEUE_LEN_BYTES)) { struct sk_buff *buff; buff = __skb_dequeue(&neigh->arp_queue); @@ -1170,7 +1176,7 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, neigh_update_hhs(neigh); if (!(new & NUD_CONNECTED)) neigh->confirmed = jiffies - - (neigh->parms->base_reachable_time << 1); + (NEIGH_VAR(neigh->parms, BASE_REACHABLE_TIME) << 1); notify = 1; } if (new == old) @@ -1391,9 +1397,10 @@ void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p, struct sk_buff *skb) { unsigned long now = jiffies; - unsigned long sched_next = now + (net_random() % p->proxy_delay); + unsigned long sched_next = now + (net_random() % + NEIGH_VAR(p, PROXY_DELAY)); - if (tbl->proxy_queue.qlen > p->proxy_qlen) { + if (tbl->proxy_queue.qlen > NEIGH_VAR(p, PROXY_QLEN)) { kfree_skb(skb); return; } @@ -1440,7 +1447,7 @@ struct neigh_parms *neigh_parms_alloc(struct net_device *dev, p->tbl = tbl; atomic_set(&p->refcnt, 1); p->reachable_time = - neigh_rand_reach_time(p->base_reachable_time); + neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME)); dev_hold(dev); p->dev = dev; write_pnet(&p->net, hold_net(net)); @@ -1509,7 +1516,7 @@ static void neigh_table_init_no_netlink(struct neigh_table *tbl) write_pnet(&tbl->parms.net, &init_net); atomic_set(&tbl->parms.refcnt, 1); tbl->parms.reachable_time = - neigh_rand_reach_time(tbl->parms.base_reachable_time); + neigh_rand_reach_time(NEIGH_VAR(&tbl->parms, BASE_REACHABLE_TIME)); tbl->stats = alloc_percpu(struct neigh_statistics); if (!tbl->stats) @@ -1777,24 +1784,32 @@ static int neightbl_fill_parms(struct sk_buff *skb, struct neigh_parms *parms) if ((parms->dev && nla_put_u32(skb, NDTPA_IFINDEX, parms->dev->ifindex)) || nla_put_u32(skb, NDTPA_REFCNT, atomic_read(&parms->refcnt)) || - nla_put_u32(skb, NDTPA_QUEUE_LENBYTES, parms->queue_len_bytes) || + nla_put_u32(skb, NDTPA_QUEUE_LENBYTES, + NEIGH_VAR(parms, QUEUE_LEN_BYTES)) || /* approximative value for deprecated QUEUE_LEN (in packets) */ nla_put_u32(skb, NDTPA_QUEUE_LEN, - parms->queue_len_bytes / SKB_TRUESIZE(ETH_FRAME_LEN)) || - nla_put_u32(skb, NDTPA_PROXY_QLEN, parms->proxy_qlen) || - nla_put_u32(skb, NDTPA_APP_PROBES, parms->app_probes) || - nla_put_u32(skb, NDTPA_UCAST_PROBES, parms->ucast_probes) || - nla_put_u32(skb, NDTPA_MCAST_PROBES, parms->mcast_probes) || + NEIGH_VAR(parms, QUEUE_LEN_BYTES) / SKB_TRUESIZE(ETH_FRAME_LEN)) || + nla_put_u32(skb, NDTPA_PROXY_QLEN, NEIGH_VAR(parms, PROXY_QLEN)) || + nla_put_u32(skb, NDTPA_APP_PROBES, NEIGH_VAR(parms, APP_PROBES)) || + nla_put_u32(skb, NDTPA_UCAST_PROBES, + NEIGH_VAR(parms, UCAST_PROBES)) || + nla_put_u32(skb, NDTPA_MCAST_PROBES, + NEIGH_VAR(parms, MCAST_PROBES)) || nla_put_msecs(skb, NDTPA_REACHABLE_TIME, parms->reachable_time) || nla_put_msecs(skb, NDTPA_BASE_REACHABLE_TIME, - parms->base_reachable_time) || - nla_put_msecs(skb, NDTPA_GC_STALETIME, parms->gc_staletime) || + NEIGH_VAR(parms, BASE_REACHABLE_TIME)) || + nla_put_msecs(skb, NDTPA_GC_STALETIME, + NEIGH_VAR(parms, GC_STALETIME)) || nla_put_msecs(skb, NDTPA_DELAY_PROBE_TIME, - parms->delay_probe_time) || - nla_put_msecs(skb, NDTPA_RETRANS_TIME, parms->retrans_time) || - nla_put_msecs(skb, NDTPA_ANYCAST_DELAY, parms->anycast_delay) || - nla_put_msecs(skb, NDTPA_PROXY_DELAY, parms->proxy_delay) || - nla_put_msecs(skb, NDTPA_LOCKTIME, parms->locktime)) + NEIGH_VAR(parms, DELAY_PROBE_TIME)) || + nla_put_msecs(skb, NDTPA_RETRANS_TIME, + NEIGH_VAR(parms, RETRANS_TIME)) || + nla_put_msecs(skb, NDTPA_ANYCAST_DELAY, + NEIGH_VAR(parms, ANYCAST_DELAY)) || + nla_put_msecs(skb, NDTPA_PROXY_DELAY, + NEIGH_VAR(parms, PROXY_DELAY)) || + nla_put_msecs(skb, NDTPA_LOCKTIME, + NEIGH_VAR(parms, LOCKTIME))) goto nla_put_failure; return nla_nest_end(skb, nest); @@ -2010,44 +2025,54 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh) switch (i) { case NDTPA_QUEUE_LEN: - p->queue_len_bytes = nla_get_u32(tbp[i]) * - SKB_TRUESIZE(ETH_FRAME_LEN); + NEIGH_VAR_SET(p, QUEUE_LEN_BYTES, + nla_get_u32(tbp[i]) * + SKB_TRUESIZE(ETH_FRAME_LEN)); break; case NDTPA_QUEUE_LENBYTES: - p->queue_len_bytes = nla_get_u32(tbp[i]); + NEIGH_VAR_SET(p, QUEUE_LEN_BYTES, + nla_get_u32(tbp[i])); break; case NDTPA_PROXY_QLEN: - p->proxy_qlen = nla_get_u32(tbp[i]); + NEIGH_VAR_SET(p, PROXY_QLEN, + nla_get_u32(tbp[i])); break; case NDTPA_APP_PROBES: - p->app_probes = nla_get_u32(tbp[i]); + NEIGH_VAR_SET(p, APP_PROBES, + nla_get_u32(tbp[i])); break; case NDTPA_UCAST_PROBES: - p->ucast_probes = nla_get_u32(tbp[i]); + NEIGH_VAR_SET(p, UCAST_PROBES, + nla_get_u32(tbp[i])); break; case NDTPA_MCAST_PROBES: - p->mcast_probes = nla_get_u32(tbp[i]); + NEIGH_VAR_SET(p, MCAST_PROBES, + nla_get_u32(tbp[i])); break; case NDTPA_BASE_REACHABLE_TIME: - p->base_reachable_time = nla_get_msecs(tbp[i]); + NEIGH_VAR_SET(p, BASE_REACHABLE_TIME, + nla_get_msecs(tbp[i])); break; case NDTPA_GC_STALETIME: - p->gc_staletime = nla_get_msecs(tbp[i]); + NEIGH_VAR_SET(p, GC_STALETIME, + nla_get_msecs(tbp[i])); break; case NDTPA_DELAY_PROBE_TIME: - p->delay_probe_time = nla_get_msecs(tbp[i]); + NEIGH_VAR_SET(p, DELAY_PROBE_TIME, + nla_get_msecs(tbp[i])); break; case NDTPA_RETRANS_TIME: - p->retrans_time = nla_get_msecs(tbp[i]); + NEIGH_VAR_SET(p, RETRANS_TIME, + nla_get_msecs(tbp[i])); break; case NDTPA_ANYCAST_DELAY: - p->anycast_delay = nla_get_msecs(tbp[i]); + NEIGH_VAR_SET(p, ANYCAST_DELAY, nla_get_msecs(tbp[i])); break; case NDTPA_PROXY_DELAY: - p->proxy_delay = nla_get_msecs(tbp[i]); + NEIGH_VAR_SET(p, PROXY_DELAY, nla_get_msecs(tbp[i])); break; case NDTPA_LOCKTIME: - p->locktime = nla_get_msecs(tbp[i]); + NEIGH_VAR_SET(p, LOCKTIME, nla_get_msecs(tbp[i])); break; } } @@ -2788,133 +2813,68 @@ static int proc_unres_qlen(struct ctl_table *ctl, int write, return ret; } -enum { - NEIGH_VAR_MCAST_PROBE, - NEIGH_VAR_UCAST_PROBE, - NEIGH_VAR_APP_PROBE, - NEIGH_VAR_RETRANS_TIME, - NEIGH_VAR_BASE_REACHABLE_TIME, - NEIGH_VAR_DELAY_PROBE_TIME, - NEIGH_VAR_GC_STALETIME, - NEIGH_VAR_QUEUE_LEN, - NEIGH_VAR_QUEUE_LEN_BYTES, - NEIGH_VAR_PROXY_QLEN, - NEIGH_VAR_ANYCAST_DELAY, - NEIGH_VAR_PROXY_DELAY, - NEIGH_VAR_LOCKTIME, - NEIGH_VAR_RETRANS_TIME_MS, - NEIGH_VAR_BASE_REACHABLE_TIME_MS, - NEIGH_VAR_GC_INTERVAL, - NEIGH_VAR_GC_THRESH1, - NEIGH_VAR_GC_THRESH2, - NEIGH_VAR_GC_THRESH3, - NEIGH_VAR_MAX -}; +static int neigh_proc_dointvec_zero_intmax(struct ctl_table *ctl, int write, + void __user *buffer, + size_t *lenp, loff_t *ppos) +{ + struct ctl_table tmp = *ctl; + + tmp.extra1 = &zero; + tmp.extra2 = &int_max; + + return proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); +} + +#define NEIGH_PARMS_DATA_OFFSET(index) \ + (&((struct neigh_parms *) 0)->data[index]) + +#define NEIGH_SYSCTL_ENTRY(attr, data_attr, name, mval, proc) \ + [NEIGH_VAR_ ## attr] = { \ + .procname = name, \ + .data = NEIGH_PARMS_DATA_OFFSET(NEIGH_VAR_ ## data_attr), \ + .maxlen = sizeof(int), \ + .mode = mval, \ + .proc_handler = proc, \ + } + +#define NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(attr, name) \ + NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_zero_intmax) + +#define NEIGH_SYSCTL_JIFFIES_ENTRY(attr, name) \ + NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, proc_dointvec_jiffies) + +#define NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(attr, name) \ + NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, proc_dointvec_userhz_jiffies) + +#define NEIGH_SYSCTL_MS_JIFFIES_ENTRY(attr, name) \ + NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, proc_dointvec_ms_jiffies) + +#define NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(attr, data_attr, name) \ + NEIGH_SYSCTL_ENTRY(attr, data_attr, name, 0644, proc_dointvec_ms_jiffies) + +#define NEIGH_SYSCTL_UNRES_QLEN_REUSED_ENTRY(attr, data_attr, name) \ + NEIGH_SYSCTL_ENTRY(attr, data_attr, name, 0644, proc_unres_qlen) static struct neigh_sysctl_table { struct ctl_table_header *sysctl_header; struct ctl_table neigh_vars[NEIGH_VAR_MAX + 1]; } neigh_sysctl_template __read_mostly = { .neigh_vars = { - [NEIGH_VAR_MCAST_PROBE] = { - .procname = "mcast_solicit", - .maxlen = sizeof(int), - .mode = 0644, - .extra1 = &zero, - .extra2 = &int_max, - .proc_handler = proc_dointvec_minmax, - }, - [NEIGH_VAR_UCAST_PROBE] = { - .procname = "ucast_solicit", - .maxlen = sizeof(int), - .mode = 0644, - .extra1 = &zero, - .extra2 = &int_max, - .proc_handler = proc_dointvec_minmax, - }, - [NEIGH_VAR_APP_PROBE] = { - .procname = "app_solicit", - .maxlen = sizeof(int), - .mode = 0644, - .extra1 = &zero, - .extra2 = &int_max, - .proc_handler = proc_dointvec_minmax, - }, - [NEIGH_VAR_RETRANS_TIME] = { - .procname = "retrans_time", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_userhz_jiffies, - }, - [NEIGH_VAR_BASE_REACHABLE_TIME] = { - .procname = "base_reachable_time", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_jiffies, - }, - [NEIGH_VAR_DELAY_PROBE_TIME] = { - .procname = "delay_first_probe_time", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_jiffies, - }, - [NEIGH_VAR_GC_STALETIME] = { - .procname = "gc_stale_time", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_jiffies, - }, - [NEIGH_VAR_QUEUE_LEN] = { - .procname = "unres_qlen", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_unres_qlen, - }, - [NEIGH_VAR_QUEUE_LEN_BYTES] = { - .procname = "unres_qlen_bytes", - .maxlen = sizeof(int), - .mode = 0644, - .extra1 = &zero, - .proc_handler = proc_dointvec_minmax, - }, - [NEIGH_VAR_PROXY_QLEN] = { - .procname = "proxy_qlen", - .maxlen = sizeof(int), - .mode = 0644, - .extra1 = &zero, - .extra2 = &int_max, - .proc_handler = proc_dointvec_minmax, - }, - [NEIGH_VAR_ANYCAST_DELAY] = { - .procname = "anycast_delay", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_userhz_jiffies, - }, - [NEIGH_VAR_PROXY_DELAY] = { - .procname = "proxy_delay", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_userhz_jiffies, - }, - [NEIGH_VAR_LOCKTIME] = { - .procname = "locktime", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_userhz_jiffies, - }, - [NEIGH_VAR_RETRANS_TIME_MS] = { - .procname = "retrans_time_ms", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_ms_jiffies, - }, - [NEIGH_VAR_BASE_REACHABLE_TIME_MS] = { - .procname = "base_reachable_time_ms", - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_ms_jiffies, - }, + NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(MCAST_PROBES, "mcast_solicit"), + NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(UCAST_PROBES, "ucast_solicit"), + NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(APP_PROBES, "app_solicit"), + NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(RETRANS_TIME, "retrans_time"), + NEIGH_SYSCTL_JIFFIES_ENTRY(BASE_REACHABLE_TIME, "base_reachable_time"), + NEIGH_SYSCTL_JIFFIES_ENTRY(DELAY_PROBE_TIME, "delay_first_probe_time"), + NEIGH_SYSCTL_JIFFIES_ENTRY(GC_STALETIME, "gc_stale_time"), + NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(QUEUE_LEN_BYTES, "unres_qlen_bytes"), + NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(PROXY_QLEN, "proxy_qlen"), + NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(ANYCAST_DELAY, "anycast_delay"), + NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(PROXY_DELAY, "proxy_delay"), + NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(LOCKTIME, "locktime"), + NEIGH_SYSCTL_UNRES_QLEN_REUSED_ENTRY(QUEUE_LEN, QUEUE_LEN_BYTES, "unres_qlen"), + NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(RETRANS_TIME_MS, RETRANS_TIME, "retrans_time_ms"), + NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(BASE_REACHABLE_TIME_MS, BASE_REACHABLE_TIME, "base_reachable_time_ms"), [NEIGH_VAR_GC_INTERVAL] = { .procname = "gc_interval", .maxlen = sizeof(int), @@ -2952,29 +2912,17 @@ static struct neigh_sysctl_table { int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p, char *p_name, proc_handler *handler) { + int i; struct neigh_sysctl_table *t; - const char *dev_name_source = NULL; + const char *dev_name_source; char neigh_path[ sizeof("net//neigh/") + IFNAMSIZ + IFNAMSIZ ]; t = kmemdup(&neigh_sysctl_template, sizeof(*t), GFP_KERNEL); if (!t) goto err; - t->neigh_vars[NEIGH_VAR_MCAST_PROBE].data = &p->mcast_probes; - t->neigh_vars[NEIGH_VAR_UCAST_PROBE].data = &p->ucast_probes; - t->neigh_vars[NEIGH_VAR_APP_PROBE].data = &p->app_probes; - t->neigh_vars[NEIGH_VAR_RETRANS_TIME].data = &p->retrans_time; - t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME].data = &p->base_reachable_time; - t->neigh_vars[NEIGH_VAR_DELAY_PROBE_TIME].data = &p->delay_probe_time; - t->neigh_vars[NEIGH_VAR_GC_STALETIME].data = &p->gc_staletime; - t->neigh_vars[NEIGH_VAR_QUEUE_LEN].data = &p->queue_len_bytes; - t->neigh_vars[NEIGH_VAR_QUEUE_LEN_BYTES].data = &p->queue_len_bytes; - t->neigh_vars[NEIGH_VAR_PROXY_QLEN].data = &p->proxy_qlen; - t->neigh_vars[NEIGH_VAR_ANYCAST_DELAY].data = &p->anycast_delay; - t->neigh_vars[NEIGH_VAR_PROXY_DELAY].data = &p->proxy_delay; - t->neigh_vars[NEIGH_VAR_LOCKTIME].data = &p->locktime; - t->neigh_vars[NEIGH_VAR_RETRANS_TIME_MS].data = &p->retrans_time; - t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME_MS].data = &p->base_reachable_time; + for (i = 0; i < ARRAY_SIZE(t->neigh_vars); i++) + t->neigh_vars[i].data += (long) p; if (dev) { dev_name_source = dev->name; @@ -2989,7 +2937,6 @@ int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p, t->neigh_vars[NEIGH_VAR_GC_THRESH3].data = (int *)(p + 1) + 3; } - if (handler) { /* RetransTime */ t->neigh_vars[NEIGH_VAR_RETRANS_TIME].proc_handler = handler; diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c index f8637f93d318..c8121ceddb9e 100644 --- a/net/decnet/dn_neigh.c +++ b/net/decnet/dn_neigh.c @@ -102,19 +102,21 @@ struct neigh_table dn_neigh_table = { .id = "dn_neigh_cache", .parms ={ .tbl = &dn_neigh_table, - .base_reachable_time = 30 * HZ, - .retrans_time = 1 * HZ, - .gc_staletime = 60 * HZ, - .reachable_time = 30 * HZ, - .delay_probe_time = 5 * HZ, - .queue_len_bytes = 64*1024, - .ucast_probes = 0, - .app_probes = 0, - .mcast_probes = 0, - .anycast_delay = 0, - .proxy_delay = 0, - .proxy_qlen = 0, - .locktime = 1 * HZ, + .reachable_time = 30 * HZ, + .data = { + [NEIGH_VAR_MCAST_PROBES] = 0, + [NEIGH_VAR_UCAST_PROBES] = 0, + [NEIGH_VAR_APP_PROBES] = 0, + [NEIGH_VAR_RETRANS_TIME] = 1 * HZ, + [NEIGH_VAR_BASE_REACHABLE_TIME] = 30 * HZ, + [NEIGH_VAR_DELAY_PROBE_TIME] = 5 * HZ, + [NEIGH_VAR_GC_STALETIME] = 60 * HZ, + [NEIGH_VAR_QUEUE_LEN_BYTES] = 64*1024, + [NEIGH_VAR_PROXY_QLEN] = 0, + [NEIGH_VAR_ANYCAST_DELAY] = 0, + [NEIGH_VAR_PROXY_DELAY] = 0, + [NEIGH_VAR_LOCKTIME] = 1 * HZ, + }, }, .gc_interval = 30 * HZ, .gc_thresh1 = 128, diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 7808093cede6..3a37c7df2a2f 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -166,18 +166,20 @@ struct neigh_table arp_tbl = { .id = "arp_cache", .parms = { .tbl = &arp_tbl, - .base_reachable_time = 30 * HZ, - .retrans_time = 1 * HZ, - .gc_staletime = 60 * HZ, .reachable_time = 30 * HZ, - .delay_probe_time = 5 * HZ, - .queue_len_bytes = 64*1024, - .ucast_probes = 3, - .mcast_probes = 3, - .anycast_delay = 1 * HZ, - .proxy_delay = (8 * HZ) / 10, - .proxy_qlen = 64, - .locktime = 1 * HZ, + .data = { + [NEIGH_VAR_MCAST_PROBES] = 3, + [NEIGH_VAR_UCAST_PROBES] = 3, + [NEIGH_VAR_RETRANS_TIME] = 1 * HZ, + [NEIGH_VAR_BASE_REACHABLE_TIME] = 30 * HZ, + [NEIGH_VAR_DELAY_PROBE_TIME] = 5 * HZ, + [NEIGH_VAR_GC_STALETIME] = 60 * HZ, + [NEIGH_VAR_QUEUE_LEN_BYTES] = 64 * 1024, + [NEIGH_VAR_PROXY_QLEN] = 64, + [NEIGH_VAR_ANYCAST_DELAY] = 1 * HZ, + [NEIGH_VAR_PROXY_DELAY] = (8 * HZ) / 10, + [NEIGH_VAR_LOCKTIME] = 1 * HZ, + }, }, .gc_interval = 30 * HZ, .gc_thresh1 = 128, @@ -359,14 +361,14 @@ static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb) if (!saddr) saddr = inet_select_addr(dev, target, RT_SCOPE_LINK); - probes -= neigh->parms->ucast_probes; + probes -= NEIGH_VAR(neigh->parms, UCAST_PROBES); if (probes < 0) { if (!(neigh->nud_state & NUD_VALID)) pr_debug("trying to ucast probe in NUD_INVALID\n"); neigh_ha_snapshot(dst_ha, neigh, dev); dst_hw = dst_ha; } else { - probes -= neigh->parms->app_probes; + probes -= NEIGH_VAR(neigh->parms, APP_PROBES); if (probes < 0) { neigh_app_ns(neigh); return; @@ -871,7 +873,7 @@ static int arp_process(struct sk_buff *skb) if (NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED || skb->pkt_type == PACKET_HOST || - in_dev->arp_parms->proxy_delay == 0) { + NEIGH_VAR(in_dev->arp_parms, PROXY_DELAY) == 0) { arp_send(ARPOP_REPLY, ETH_P_ARP, sip, dev, tip, sha, dev->dev_addr, sha); @@ -910,7 +912,8 @@ static int arp_process(struct sk_buff *skb) agents are active. Taking the first reply prevents arp trashing and chooses the fastest router. */ - override = time_after(jiffies, n->updated + n->parms->locktime); + override = time_after(jiffies, n->updated + + NEIGH_VAR(n->parms, LOCKTIME)); /* Broadcast replies and request packets do not assert neighbour reachability. diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 5087cc5cb810..55dc56f9072c 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -1071,7 +1071,7 @@ retry: regen_advance = idev->cnf.regen_max_retry * idev->cnf.dad_transmits * - idev->nd_parms->retrans_time / HZ; + NEIGH_VAR(idev->nd_parms, RETRANS_TIME) / HZ; write_unlock_bh(&idev->lock); /* A temporary address is created only if this calculated Preferred @@ -1888,7 +1888,8 @@ static void ipv6_regen_rndid(unsigned long data) expires = jiffies + idev->cnf.temp_prefered_lft * HZ - - idev->cnf.regen_max_retry * idev->cnf.dad_transmits * idev->nd_parms->retrans_time - + idev->cnf.regen_max_retry * idev->cnf.dad_transmits * + NEIGH_VAR(idev->nd_parms, RETRANS_TIME) - idev->cnf.max_desync_factor * HZ; if (time_before(expires, jiffies)) { pr_warn("%s: too short regeneration interval; timer disabled for %s\n", @@ -3188,7 +3189,8 @@ static void addrconf_dad_timer(unsigned long data) } ifp->dad_probes--; - addrconf_mod_dad_timer(ifp, ifp->idev->nd_parms->retrans_time); + addrconf_mod_dad_timer(ifp, + NEIGH_VAR(ifp->idev->nd_parms, RETRANS_TIME)); spin_unlock(&ifp->lock); write_unlock(&idev->lock); @@ -3509,7 +3511,7 @@ restart: !(ifp->flags&IFA_F_TENTATIVE)) { unsigned long regen_advance = ifp->idev->cnf.regen_max_retry * ifp->idev->cnf.dad_transmits * - ifp->idev->nd_parms->retrans_time / HZ; + NEIGH_VAR(ifp->idev->nd_parms, RETRANS_TIME) / HZ; if (age >= ifp->prefered_lft - regen_advance) { struct inet6_ifaddr *ifpub = ifp->ifpub; @@ -4231,7 +4233,7 @@ static int inet6_fill_ifla6_attrs(struct sk_buff *skb, struct inet6_dev *idev) ci.max_reasm_len = IPV6_MAXPLEN; ci.tstamp = cstamp_delta(idev->tstamp); ci.reachable_time = jiffies_to_msecs(idev->nd_parms->reachable_time); - ci.retrans_time = jiffies_to_msecs(idev->nd_parms->retrans_time); + ci.retrans_time = jiffies_to_msecs(NEIGH_VAR(idev->nd_parms, RETRANS_TIME)); if (nla_put(skb, IFLA_INET6_CACHEINFO, sizeof(ci), &ci)) goto nla_put_failure; nla = nla_reserve(skb, IFLA_INET6_CONF, DEVCONF_MAX * sizeof(s32)); diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 300865171394..0da0986a9292 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -125,17 +125,19 @@ struct neigh_table nd_tbl = { .id = "ndisc_cache", .parms = { .tbl = &nd_tbl, - .base_reachable_time = ND_REACHABLE_TIME, - .retrans_time = ND_RETRANS_TIMER, - .gc_staletime = 60 * HZ, .reachable_time = ND_REACHABLE_TIME, - .delay_probe_time = 5 * HZ, - .queue_len_bytes = 64*1024, - .ucast_probes = 3, - .mcast_probes = 3, - .anycast_delay = 1 * HZ, - .proxy_delay = (8 * HZ) / 10, - .proxy_qlen = 64, + .data = { + [NEIGH_VAR_MCAST_PROBES] = 3, + [NEIGH_VAR_UCAST_PROBES] = 3, + [NEIGH_VAR_RETRANS_TIME] = ND_RETRANS_TIMER, + [NEIGH_VAR_BASE_REACHABLE_TIME] = ND_REACHABLE_TIME, + [NEIGH_VAR_DELAY_PROBE_TIME] = 5 * HZ, + [NEIGH_VAR_GC_STALETIME] = 60 * HZ, + [NEIGH_VAR_QUEUE_LEN_BYTES] = 64 * 1024, + [NEIGH_VAR_PROXY_QLEN] = 64, + [NEIGH_VAR_ANYCAST_DELAY] = 1 * HZ, + [NEIGH_VAR_PROXY_DELAY] = (8 * HZ) / 10, + }, }, .gc_interval = 30 * HZ, .gc_thresh1 = 128, @@ -656,14 +658,14 @@ static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb) if (skb && ipv6_chk_addr(dev_net(dev), &ipv6_hdr(skb)->saddr, dev, 1)) saddr = &ipv6_hdr(skb)->saddr; - if ((probes -= neigh->parms->ucast_probes) < 0) { + if ((probes -= NEIGH_VAR(neigh->parms, UCAST_PROBES)) < 0) { if (!(neigh->nud_state & NUD_VALID)) { ND_PRINTK(1, dbg, "%s: trying to ucast probe in NUD_INVALID: %pI6\n", __func__, target); } ndisc_send_ns(dev, neigh, target, target, saddr); - } else if ((probes -= neigh->parms->app_probes) < 0) { + } else if ((probes -= NEIGH_VAR(neigh->parms, APP_PROBES)) < 0) { neigh_app_ns(neigh); } else { addrconf_addr_solict_mult(target, &mcaddr); @@ -790,7 +792,7 @@ static void ndisc_recv_ns(struct sk_buff *skb) if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) && skb->pkt_type != PACKET_HOST && inc && - idev->nd_parms->proxy_delay != 0) { + NEIGH_VAR(idev->nd_parms, PROXY_DELAY) != 0) { /* * for anycast or proxy, * sender should delay its response @@ -1210,7 +1212,7 @@ skip_defrtr: rtime = (rtime*HZ)/1000; if (rtime < HZ/10) rtime = HZ/10; - in6_dev->nd_parms->retrans_time = rtime; + NEIGH_VAR_SET(in6_dev->nd_parms, RETRANS_TIME, rtime); in6_dev->tstamp = jiffies; inet6_ifinfo_notify(RTM_NEWLINK, in6_dev); } @@ -1222,9 +1224,11 @@ skip_defrtr: if (rtime < HZ/10) rtime = HZ/10; - if (rtime != in6_dev->nd_parms->base_reachable_time) { - in6_dev->nd_parms->base_reachable_time = rtime; - in6_dev->nd_parms->gc_staletime = 3 * rtime; + if (rtime != NEIGH_VAR(in6_dev->nd_parms, BASE_REACHABLE_TIME)) { + NEIGH_VAR_SET(in6_dev->nd_parms, + BASE_REACHABLE_TIME, rtime); + NEIGH_VAR_SET(in6_dev->nd_parms, + GC_STALETIME, 3 * rtime); in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime); in6_dev->tstamp = jiffies; inet6_ifinfo_notify(RTM_NEWLINK, in6_dev); @@ -1665,8 +1669,9 @@ int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, void __user *bu ret = -1; if (write && ret == 0 && dev && (idev = in6_dev_get(dev)) != NULL) { - if (ctl->data == &idev->nd_parms->base_reachable_time) - idev->nd_parms->reachable_time = neigh_rand_reach_time(idev->nd_parms->base_reachable_time); + if (ctl->data == &NEIGH_VAR(idev->nd_parms, BASE_REACHABLE_TIME)) + idev->nd_parms->reachable_time = + neigh_rand_reach_time(NEIGH_VAR(idev->nd_parms, BASE_REACHABLE_TIME)); idev->tstamp = jiffies; inet6_ifinfo_notify(RTM_NEWLINK, idev); in6_dev_put(idev); -- cgit v1.2.3 From cb5b09c17fe60056bc8f127ffc987d361c40ed4b Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sat, 7 Dec 2013 19:26:54 +0100 Subject: neigh: wrap proc dointvec functions This will be needed later on to provide better management of default values. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/neighbour.h | 9 ++++++++ net/core/neighbour.c | 55 ++++++++++++++++++++++++++++++++++++++++--------- net/ipv6/ndisc.c | 10 ++++----- 3 files changed, 59 insertions(+), 15 deletions(-) (limited to 'include/net') diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 4cb5478e2f6e..ed7eba045297 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -296,6 +296,15 @@ void *neigh_seq_start(struct seq_file *, loff_t *, struct neigh_table *, void *neigh_seq_next(struct seq_file *, void *, loff_t *); void neigh_seq_stop(struct seq_file *, void *); +int neigh_proc_dointvec(struct ctl_table *ctl, int write, + void __user *buffer, size_t *lenp, loff_t *ppos); +int neigh_proc_dointvec_jiffies(struct ctl_table *ctl, int write, + void __user *buffer, + size_t *lenp, loff_t *ppos); +int neigh_proc_dointvec_ms_jiffies(struct ctl_table *ctl, int write, + void __user *buffer, + size_t *lenp, loff_t *ppos); + int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p, char *p_name, proc_handler *proc_handler); void neigh_sysctl_unregister(struct neigh_parms *p); diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 742fa10d4372..60edd97ad2bc 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -2825,6 +2825,43 @@ static int neigh_proc_dointvec_zero_intmax(struct ctl_table *ctl, int write, return proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); } +int neigh_proc_dointvec(struct ctl_table *ctl, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + return proc_dointvec(ctl, write, buffer, lenp, ppos); +} +EXPORT_SYMBOL(neigh_proc_dointvec); + +int neigh_proc_dointvec_jiffies(struct ctl_table *ctl, int write, + void __user *buffer, + size_t *lenp, loff_t *ppos) +{ + return proc_dointvec_jiffies(ctl, write, buffer, lenp, ppos); +} +EXPORT_SYMBOL(neigh_proc_dointvec_jiffies); + +static int neigh_proc_dointvec_userhz_jiffies(struct ctl_table *ctl, int write, + void __user *buffer, + size_t *lenp, loff_t *ppos) +{ + return proc_dointvec_userhz_jiffies(ctl, write, buffer, lenp, ppos); +} + +int neigh_proc_dointvec_ms_jiffies(struct ctl_table *ctl, int write, + void __user *buffer, + size_t *lenp, loff_t *ppos) +{ + return proc_dointvec_ms_jiffies(ctl, write, buffer, lenp, ppos); +} +EXPORT_SYMBOL(neigh_proc_dointvec_ms_jiffies); + +static int neigh_proc_dointvec_unres_qlen(struct ctl_table *ctl, int write, + void __user *buffer, + size_t *lenp, loff_t *ppos) +{ + return proc_unres_qlen(ctl, write, buffer, lenp, ppos); +} + #define NEIGH_PARMS_DATA_OFFSET(index) \ (&((struct neigh_parms *) 0)->data[index]) @@ -2841,19 +2878,19 @@ static int neigh_proc_dointvec_zero_intmax(struct ctl_table *ctl, int write, NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_zero_intmax) #define NEIGH_SYSCTL_JIFFIES_ENTRY(attr, name) \ - NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, proc_dointvec_jiffies) + NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_jiffies) #define NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(attr, name) \ - NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, proc_dointvec_userhz_jiffies) + NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_userhz_jiffies) #define NEIGH_SYSCTL_MS_JIFFIES_ENTRY(attr, name) \ - NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, proc_dointvec_ms_jiffies) + NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_ms_jiffies) #define NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(attr, data_attr, name) \ - NEIGH_SYSCTL_ENTRY(attr, data_attr, name, 0644, proc_dointvec_ms_jiffies) + NEIGH_SYSCTL_ENTRY(attr, data_attr, name, 0644, neigh_proc_dointvec_ms_jiffies) #define NEIGH_SYSCTL_UNRES_QLEN_REUSED_ENTRY(attr, data_attr, name) \ - NEIGH_SYSCTL_ENTRY(attr, data_attr, name, 0644, proc_unres_qlen) + NEIGH_SYSCTL_ENTRY(attr, data_attr, name, 0644, neigh_proc_dointvec_unres_qlen) static struct neigh_sysctl_table { struct ctl_table_header *sysctl_header; @@ -2921,8 +2958,10 @@ int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p, if (!t) goto err; - for (i = 0; i < ARRAY_SIZE(t->neigh_vars); i++) + for (i = 0; i < ARRAY_SIZE(t->neigh_vars); i++) { t->neigh_vars[i].data += (long) p; + t->neigh_vars[i].extra1 = dev; + } if (dev) { dev_name_source = dev->name; @@ -2940,16 +2979,12 @@ int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p, if (handler) { /* RetransTime */ t->neigh_vars[NEIGH_VAR_RETRANS_TIME].proc_handler = handler; - t->neigh_vars[NEIGH_VAR_RETRANS_TIME].extra1 = dev; /* ReachableTime */ t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME].proc_handler = handler; - t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME].extra1 = dev; /* RetransTime (in milliseconds)*/ t->neigh_vars[NEIGH_VAR_RETRANS_TIME_MS].proc_handler = handler; - t->neigh_vars[NEIGH_VAR_RETRANS_TIME_MS].extra1 = dev; /* ReachableTime (in milliseconds) */ t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME_MS].proc_handler = handler; - t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME_MS].extra1 = dev; } /* Don't export sysctls to unprivileged users */ diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 0da0986a9292..5fc6f69652b8 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1655,16 +1655,16 @@ int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, void __user *bu ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default"); if (strcmp(ctl->procname, "retrans_time") == 0) - ret = proc_dointvec(ctl, write, buffer, lenp, ppos); + ret = neigh_proc_dointvec(ctl, write, buffer, lenp, ppos); else if (strcmp(ctl->procname, "base_reachable_time") == 0) - ret = proc_dointvec_jiffies(ctl, write, - buffer, lenp, ppos); + ret = neigh_proc_dointvec_jiffies(ctl, write, + buffer, lenp, ppos); else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) || (strcmp(ctl->procname, "base_reachable_time_ms") == 0)) - ret = proc_dointvec_ms_jiffies(ctl, write, - buffer, lenp, ppos); + ret = neigh_proc_dointvec_ms_jiffies(ctl, write, + buffer, lenp, ppos); else ret = -1; -- cgit v1.2.3 From 73af614aedd221df8495fc8c9993c50e87f899f2 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sat, 7 Dec 2013 19:26:55 +0100 Subject: neigh: use tbl->family to distinguish ipv4 from ipv6 Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/neighbour.h | 7 ++++++- net/core/neighbour.c | 14 +++++++++++++- net/ipv4/arp.c | 2 +- net/ipv4/devinet.c | 2 +- net/ipv6/addrconf.c | 2 +- net/ipv6/ndisc.c | 2 +- 6 files changed, 23 insertions(+), 6 deletions(-) (limited to 'include/net') diff --git a/include/net/neighbour.h b/include/net/neighbour.h index ed7eba045297..95615c9ad13a 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -202,6 +202,11 @@ struct neigh_table { struct pneigh_entry **phash_buckets; }; +static inline int neigh_parms_family(struct neigh_parms *p) +{ + return p->tbl->family; +} + #define NEIGH_PRIV_ALIGN sizeof(long long) #define NEIGH_ENTRY_SIZE(size) ALIGN((size), NEIGH_PRIV_ALIGN) @@ -306,7 +311,7 @@ int neigh_proc_dointvec_ms_jiffies(struct ctl_table *ctl, int write, size_t *lenp, loff_t *ppos); int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p, - char *p_name, proc_handler *proc_handler); + proc_handler *proc_handler); void neigh_sysctl_unregister(struct neigh_parms *p); static inline void __neigh_parms_put(struct neigh_parms *parms) diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 60edd97ad2bc..65ead080167b 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -2947,12 +2947,13 @@ static struct neigh_sysctl_table { }; int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p, - char *p_name, proc_handler *handler) + proc_handler *handler) { int i; struct neigh_sysctl_table *t; const char *dev_name_source; char neigh_path[ sizeof("net//neigh/") + IFNAMSIZ + IFNAMSIZ ]; + char *p_name; t = kmemdup(&neigh_sysctl_template, sizeof(*t), GFP_KERNEL); if (!t) @@ -2991,6 +2992,17 @@ int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p, if (neigh_parms_net(p)->user_ns != &init_user_ns) t->neigh_vars[0].procname = NULL; + switch (neigh_parms_family(p)) { + case AF_INET: + p_name = "ipv4"; + break; + case AF_INET6: + p_name = "ipv6"; + break; + default: + BUG(); + } + snprintf(neigh_path, sizeof(neigh_path), "net/%s/neigh/%s", p_name, dev_name_source); t->sysctl_header = diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 3a37c7df2a2f..f04185863940 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -1287,7 +1287,7 @@ void __init arp_init(void) dev_add_pack(&arp_packet_type); arp_proc_init(); #ifdef CONFIG_SYSCTL - neigh_sysctl_register(NULL, &arp_tbl.parms, "ipv4", NULL); + neigh_sysctl_register(NULL, &arp_tbl.parms, NULL); #endif register_netdevice_notifier(&arp_netdev_notifier); } diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index a1b5bcbd04ae..e1c19535fe38 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -2160,7 +2160,7 @@ static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf) static void devinet_sysctl_register(struct in_device *idev) { - neigh_sysctl_register(idev->dev, idev->arp_parms, "ipv4", NULL); + neigh_sysctl_register(idev->dev, idev->arp_parms, NULL); __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name, &idev->cnf); } diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 55dc56f9072c..ea8a475fa8c5 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -5028,7 +5028,7 @@ static void __addrconf_sysctl_unregister(struct ipv6_devconf *p) static void addrconf_sysctl_register(struct inet6_dev *idev) { - neigh_sysctl_register(idev->dev, idev->nd_parms, "ipv6", + neigh_sysctl_register(idev->dev, idev->nd_parms, &ndisc_ifinfo_sysctl_change); __addrconf_sysctl_register(dev_net(idev->dev), idev->dev->name, idev, &idev->cnf); diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 5fc6f69652b8..09a22f4f36c9 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1730,7 +1730,7 @@ int __init ndisc_init(void) neigh_table_init(&nd_tbl); #ifdef CONFIG_SYSCTL - err = neigh_sysctl_register(NULL, &nd_tbl.parms, "ipv6", + err = neigh_sysctl_register(NULL, &nd_tbl.parms, &ndisc_ifinfo_sysctl_change); if (err) goto out_unregister_pernet; -- cgit v1.2.3 From 1d4c8c29841b9991cdf3c7cc4ba7f96a94f104ca Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sat, 7 Dec 2013 19:26:56 +0100 Subject: neigh: restore old behaviour of default parms values Previously inet devices were only constructed when addresses are added. Therefore the default neigh parms values they get are the ones at the time of these operations. Now that we're creating inet devices earlier, this changes the behaviour of default neigh parms values in an incompatible way (see bug #8519). This patch creates a compromise by setting the default values at the same point as before but only for those that have not been explicitly set by the user since the inet device's creation. Introduced by: commit 8030f54499925d073a88c09f30d5d844fb1b3190 Author: Herbert Xu Date: Thu Feb 22 01:53:47 2007 +0900 [IPV4] devinet: Register inetdev earlier. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/linux/inetdevice.h | 7 +++++ include/net/neighbour.h | 13 ++++++++ net/core/neighbour.c | 74 ++++++++++++++++++++++++++++++++++++++++++---- net/ipv4/devinet.c | 2 ++ net/ipv4/ipmr.c | 2 ++ 5 files changed, 92 insertions(+), 6 deletions(-) (limited to 'include/net') diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index 0d678aefe69d..ae174ca565c9 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -220,6 +220,13 @@ static inline struct in_device *__in_dev_get_rtnl(const struct net_device *dev) return rtnl_dereference(dev->ip_ptr); } +static inline struct neigh_parms *__in_dev_arp_parms_get_rcu(const struct net_device *dev) +{ + struct in_device *in_dev = __in_dev_get_rcu(dev); + + return in_dev ? in_dev->arp_parms : NULL; +} + void in_dev_finish_destroy(struct in_device *idev); static inline void in_dev_put(struct in_device *idev) diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 95615c9ad13a..41b1ce6c96a8 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -81,16 +82,28 @@ struct neigh_parms { int reachable_time; int data[NEIGH_VAR_DATA_MAX]; + DECLARE_BITMAP(data_state, NEIGH_VAR_DATA_MAX); }; static inline void neigh_var_set(struct neigh_parms *p, int index, int val) { + set_bit(index, p->data_state); p->data[index] = val; } #define NEIGH_VAR(p, attr) ((p)->data[NEIGH_VAR_ ## attr]) #define NEIGH_VAR_SET(p, attr, val) neigh_var_set(p, NEIGH_VAR_ ## attr, val) +static inline void neigh_parms_data_state_setall(struct neigh_parms *p) +{ + bitmap_fill(p->data_state, NEIGH_VAR_DATA_MAX); +} + +static inline void neigh_parms_data_state_cleanall(struct neigh_parms *p) +{ + bitmap_zero(p->data_state, NEIGH_VAR_DATA_MAX); +} + struct neigh_statistics { unsigned long allocs; /* number of allocated neighs */ unsigned long destroys; /* number of destroyed neighs */ diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 65ead080167b..c4a7879bfb15 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -38,6 +38,7 @@ #include #include #include +#include #define DEBUG #define NEIGH_DEBUG 1 @@ -1464,6 +1465,8 @@ struct neigh_parms *neigh_parms_alloc(struct net_device *dev, p->next = tbl->parms.next; tbl->parms.next = p; write_unlock_bh(&tbl->lock); + + neigh_parms_data_state_cleanall(p); } return p; } @@ -2813,22 +2816,68 @@ static int proc_unres_qlen(struct ctl_table *ctl, int write, return ret; } +static struct neigh_parms *neigh_get_dev_parms_rcu(struct net_device *dev, + int family) +{ + if (family == AF_INET) + return __in_dev_arp_parms_get_rcu(dev); + return NULL; +} + +static void neigh_copy_dflt_parms(struct net *net, struct neigh_parms *p, + int index) +{ + struct net_device *dev; + int family = neigh_parms_family(p); + + rcu_read_lock(); + for_each_netdev_rcu(net, dev) { + struct neigh_parms *dst_p = + neigh_get_dev_parms_rcu(dev, family); + + if (dst_p && !test_bit(index, dst_p->data_state)) + dst_p->data[index] = p->data[index]; + } + rcu_read_unlock(); +} + +static void neigh_proc_update(struct ctl_table *ctl, int write) +{ + struct net_device *dev = ctl->extra1; + struct neigh_parms *p = ctl->extra2; + struct net *net = p->net; + int index = (int *) ctl->data - p->data; + + if (!write) + return; + + set_bit(index, p->data_state); + if (!dev) /* NULL dev means this is default value */ + neigh_copy_dflt_parms(net, p, index); +} + static int neigh_proc_dointvec_zero_intmax(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table tmp = *ctl; + int ret; tmp.extra1 = &zero; tmp.extra2 = &int_max; - return proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); + ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); + neigh_proc_update(ctl, write); + return ret; } int neigh_proc_dointvec(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { - return proc_dointvec(ctl, write, buffer, lenp, ppos); + int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); + + neigh_proc_update(ctl, write); + return ret; } EXPORT_SYMBOL(neigh_proc_dointvec); @@ -2836,7 +2885,10 @@ int neigh_proc_dointvec_jiffies(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { - return proc_dointvec_jiffies(ctl, write, buffer, lenp, ppos); + int ret = proc_dointvec_jiffies(ctl, write, buffer, lenp, ppos); + + neigh_proc_update(ctl, write); + return ret; } EXPORT_SYMBOL(neigh_proc_dointvec_jiffies); @@ -2844,14 +2896,20 @@ static int neigh_proc_dointvec_userhz_jiffies(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { - return proc_dointvec_userhz_jiffies(ctl, write, buffer, lenp, ppos); + int ret = proc_dointvec_userhz_jiffies(ctl, write, buffer, lenp, ppos); + + neigh_proc_update(ctl, write); + return ret; } int neigh_proc_dointvec_ms_jiffies(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { - return proc_dointvec_ms_jiffies(ctl, write, buffer, lenp, ppos); + int ret = proc_dointvec_ms_jiffies(ctl, write, buffer, lenp, ppos); + + neigh_proc_update(ctl, write); + return ret; } EXPORT_SYMBOL(neigh_proc_dointvec_ms_jiffies); @@ -2859,7 +2917,10 @@ static int neigh_proc_dointvec_unres_qlen(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { - return proc_unres_qlen(ctl, write, buffer, lenp, ppos); + int ret = proc_unres_qlen(ctl, write, buffer, lenp, ppos); + + neigh_proc_update(ctl, write); + return ret; } #define NEIGH_PARMS_DATA_OFFSET(index) \ @@ -2962,6 +3023,7 @@ int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p, for (i = 0; i < ARRAY_SIZE(t->neigh_vars); i++) { t->neigh_vars[i].data += (long) p; t->neigh_vars[i].extra1 = dev; + t->neigh_vars[i].extra2 = p; } if (dev) { diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index e1c19535fe38..43065be36301 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -500,6 +500,7 @@ static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa) return -ENOBUFS; } ipv4_devconf_setall(in_dev); + neigh_parms_data_state_setall(in_dev->arp_parms); if (ifa->ifa_dev != in_dev) { WARN_ON(ifa->ifa_dev); in_dev_hold(in_dev); @@ -747,6 +748,7 @@ static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh, goto errout; ipv4_devconf_setall(in_dev); + neigh_parms_data_state_setall(in_dev->arp_parms); in_dev_hold(in_dev); if (tb[IFA_ADDRESS] == NULL) diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 62212c772a4b..421a24934ffd 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -425,6 +425,7 @@ struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v) goto failure; ipv4_devconf_setall(in_dev); + neigh_parms_data_state_setall(in_dev->arp_parms); IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0; if (dev_open(dev)) @@ -517,6 +518,7 @@ static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt) } ipv4_devconf_setall(in_dev); + neigh_parms_data_state_setall(in_dev->arp_parms); IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0; rcu_read_unlock(); -- cgit v1.2.3 From bba24896f022d4d239494bebf18e713cd8aec7a5 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sat, 7 Dec 2013 19:26:57 +0100 Subject: neigh: ipv6: respect default values set before an address is assigned to device Make the behaviour similar to ipv4. This will allow user to set sysctl default neigh param values and these values will be respected even by devices registered before (that ones what do not have address set yet). Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/addrconf.h | 7 +++++++ net/core/neighbour.c | 7 ++++++- net/ipv6/addrconf.c | 2 ++ 3 files changed, 15 insertions(+), 1 deletion(-) (limited to 'include/net') diff --git a/include/net/addrconf.h b/include/net/addrconf.h index e70278eef12a..66c4a44d8f5c 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -248,6 +248,13 @@ static inline struct inet6_dev *in6_dev_get(const struct net_device *dev) return idev; } +static inline struct neigh_parms *__in6_dev_nd_parms_get_rcu(const struct net_device *dev) +{ + struct inet6_dev *idev = __in6_dev_get(dev); + + return idev ? idev->nd_parms : NULL; +} + void in6_dev_finish_destroy(struct inet6_dev *idev); static inline void in6_dev_put(struct inet6_dev *idev) diff --git a/net/core/neighbour.c b/net/core/neighbour.c index c4a7879bfb15..ce2b77515a9e 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -39,6 +39,7 @@ #include #include #include +#include #define DEBUG #define NEIGH_DEBUG 1 @@ -2819,8 +2820,12 @@ static int proc_unres_qlen(struct ctl_table *ctl, int write, static struct neigh_parms *neigh_get_dev_parms_rcu(struct net_device *dev, int family) { - if (family == AF_INET) + switch (family) { + case AF_INET: return __in_dev_arp_parms_get_rcu(dev); + case AF_INET6: + return __in6_dev_nd_parms_get_rcu(dev); + } return NULL; } diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index ea8a475fa8c5..be4dbbd17d3b 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -834,6 +834,8 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, goto out; } + neigh_parms_data_state_setall(idev->nd_parms); + ifa->addr = *addr; if (peer_addr) ifa->peer_addr = *peer_addr; -- cgit v1.2.3 From 37cfee909c5a396abccf43e3e0a63a5f5ac998af Mon Sep 17 00:00:00 2001 From: Florent Fourcot Date: Sun, 8 Dec 2013 15:46:58 +0100 Subject: ipv6: move IPV6_TCLASS_MASK definition in ipv6.h Signed-off-by: Florent Fourcot Reviewed-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/net/ipv6.h | 1 + net/ipv6/ip6_gre.c | 1 - net/ipv6/ip6_tunnel.c | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 488316e339a1..bce802e7bcfd 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -238,6 +238,7 @@ struct ip6_flowlabel { #define IPV6_FLOWINFO_MASK cpu_to_be32(0x0FFFFFFF) #define IPV6_FLOWLABEL_MASK cpu_to_be32(0x000FFFFF) +#define IPV6_TCLASS_MASK (IPV6_FLOWINFO_MASK & ~IPV6_FLOWLABEL_MASK) struct ipv6_fl_socklist { struct ipv6_fl_socklist __rcu *next; diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 8acb28621f9c..c50de068bea2 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -61,7 +61,6 @@ static bool log_ecn_error = true; module_param(log_ecn_error, bool, 0644); MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); -#define IPV6_TCLASS_MASK (IPV6_FLOWINFO_MASK & ~IPV6_FLOWLABEL_MASK) #define IPV6_TCLASS_SHIFT 20 #define HASH_SIZE_SHIFT 5 diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index d6062325db08..f5e8a98eb55e 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -70,7 +70,6 @@ MODULE_ALIAS_NETDEV("ip6tnl0"); #define IP6_TNL_TRACE(x...) do {;} while(0) #endif -#define IPV6_TCLASS_MASK (IPV6_FLOWINFO_MASK & ~IPV6_FLOWLABEL_MASK) #define IPV6_TCLASS_SHIFT 20 #define HASH_SIZE_SHIFT 5 -- cgit v1.2.3 From 3308de2b841e5f25bdc4a7208a451f52f8cff969 Mon Sep 17 00:00:00 2001 From: Florent Fourcot Date: Sun, 8 Dec 2013 15:47:00 +0100 Subject: ipv6: add ip6_flowlabel helper And use it if possible. Signed-off-by: Florent Fourcot Reviewed-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/net/ipv6.h | 5 +++++ net/ipv6/ip6_gre.c | 2 +- net/ipv6/ip6_tunnel.c | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/ipv6.h b/include/net/ipv6.h index bce802e7bcfd..d0bfe3eeb824 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -679,6 +679,11 @@ static inline __be32 ip6_flowinfo(const struct ipv6hdr *hdr) return *(__be32 *)hdr & IPV6_FLOWINFO_MASK; } +static inline __be32 ip6_flowlabel(const struct ipv6hdr *hdr) +{ + return *(__be32 *)hdr & IPV6_FLOWLABEL_MASK; +} + /* * Prototypes exported by ipv6 */ diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index c50de068bea2..e27fb78c61f2 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -845,7 +845,7 @@ static inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev) if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS) fl6.flowlabel |= (*(__be32 *) ipv6h & IPV6_TCLASS_MASK); if (t->parms.flags & IP6_TNL_F_USE_ORIG_FLOWLABEL) - fl6.flowlabel |= (*(__be32 *) ipv6h & IPV6_FLOWLABEL_MASK); + fl6.flowlabel |= ip6_flowlabel(ipv6h); if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK) fl6.flowi6_mark = skb->mark; diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index f5e8a98eb55e..8d7c9867a445 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1130,7 +1130,7 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS) fl6.flowlabel |= (*(__be32 *) ipv6h & IPV6_TCLASS_MASK); if (t->parms.flags & IP6_TNL_F_USE_ORIG_FLOWLABEL) - fl6.flowlabel |= (*(__be32 *) ipv6h & IPV6_FLOWLABEL_MASK); + fl6.flowlabel |= ip6_flowlabel(ipv6h); if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK) fl6.flowi6_mark = skb->mark; -- cgit v1.2.3 From 10ae76faa97cb3e1020dcf2cdae6b1ea8359abab Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 10 Dec 2013 15:00:50 -0500 Subject: cipso: cleanup cipso_v4_translate() when !CONFIG_NETLABEL Don't needlessly recompute 'opt[opt_iter + 1]' as we already have it stored in 'tag_len'. Signed-off-by: Paul Moore Signed-off-by: David S. Miller --- include/net/cipso_ipv4.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/net') diff --git a/include/net/cipso_ipv4.h b/include/net/cipso_ipv4.h index 2179d071f68f..a6fd939f202d 100644 --- a/include/net/cipso_ipv4.h +++ b/include/net/cipso_ipv4.h @@ -303,7 +303,7 @@ static inline int cipso_v4_validate(const struct sk_buff *skb, for (opt_iter = 6; opt_iter < opt_len;) { tag_len = opt[opt_iter + 1]; - if ((tag_len == 0) || (opt[opt_iter + 1] > (opt_len - opt_iter))) { + if ((tag_len == 0) || (tag_len > (opt_len - opt_iter))) { err_offset = opt_iter + 1; goto out; } -- cgit v1.2.3 From 9a32b860431aee0cba50ab2414bfa29f4bb2f0d7 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sun, 8 Dec 2013 12:16:09 +0100 Subject: dn_dev: add support for IFA_FLAGS nl attribute Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/dn_dev.h | 2 +- net/decnet/dn_dev.c | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) (limited to 'include/net') diff --git a/include/net/dn_dev.h b/include/net/dn_dev.h index 20b5ab06032d..197886cd7bdd 100644 --- a/include/net/dn_dev.h +++ b/include/net/dn_dev.h @@ -9,7 +9,7 @@ struct dn_ifaddr { struct dn_dev *ifa_dev; __le16 ifa_local; __le16 ifa_address; - __u8 ifa_flags; + __u32 ifa_flags; __u8 ifa_scope; char ifa_label[IFNAMSIZ]; struct rcu_head rcu; diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c index dd0dfb25f4b1..a603823a3e27 100644 --- a/net/decnet/dn_dev.c +++ b/net/decnet/dn_dev.c @@ -561,6 +561,7 @@ static const struct nla_policy dn_ifa_policy[IFA_MAX+1] = { [IFA_LOCAL] = { .type = NLA_U16 }, [IFA_LABEL] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 }, + [IFA_FLAGS] = { .type = NLA_U32 }, }; static int dn_nl_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh) @@ -648,7 +649,8 @@ static int dn_nl_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) ifa->ifa_local = nla_get_le16(tb[IFA_LOCAL]); ifa->ifa_address = nla_get_le16(tb[IFA_ADDRESS]); - ifa->ifa_flags = ifm->ifa_flags; + ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : + ifm->ifa_flags; ifa->ifa_scope = ifm->ifa_scope; ifa->ifa_dev = dn_db; @@ -669,7 +671,8 @@ static inline size_t dn_ifaddr_nlmsg_size(void) return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) + nla_total_size(IFNAMSIZ) /* IFA_LABEL */ + nla_total_size(2) /* IFA_ADDRESS */ - + nla_total_size(2); /* IFA_LOCAL */ + + nla_total_size(2) /* IFA_LOCAL */ + + nla_total_size(4); /* IFA_FLAGS */ } static int dn_nl_fill_ifaddr(struct sk_buff *skb, struct dn_ifaddr *ifa, @@ -677,6 +680,7 @@ static int dn_nl_fill_ifaddr(struct sk_buff *skb, struct dn_ifaddr *ifa, { struct ifaddrmsg *ifm; struct nlmsghdr *nlh; + u32 ifa_flags = ifa->ifa_flags | IFA_F_PERMANENT; nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), flags); if (nlh == NULL) @@ -685,7 +689,7 @@ static int dn_nl_fill_ifaddr(struct sk_buff *skb, struct dn_ifaddr *ifa, ifm = nlmsg_data(nlh); ifm->ifa_family = AF_DECnet; ifm->ifa_prefixlen = 16; - ifm->ifa_flags = ifa->ifa_flags | IFA_F_PERMANENT; + ifm->ifa_flags = ifa_flags; ifm->ifa_scope = ifa->ifa_scope; ifm->ifa_index = ifa->ifa_dev->dev->ifindex; @@ -694,7 +698,8 @@ static int dn_nl_fill_ifaddr(struct sk_buff *skb, struct dn_ifaddr *ifa, (ifa->ifa_local && nla_put_le16(skb, IFA_LOCAL, ifa->ifa_local)) || (ifa->ifa_label[0] && - nla_put_string(skb, IFA_LABEL, ifa->ifa_label))) + nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) || + nla_put_u32(skb, IFA_FLAGS, ifa_flags)) goto nla_put_failure; return nlmsg_end(skb, nlh); -- cgit v1.2.3 From 18722c247023035b9e2e2a08a887adec2a9a6e49 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 11 Dec 2013 17:05:37 +0200 Subject: Bluetooth: Enable 6LoWPAN support for BT LE devices This is initial version of http://tools.ietf.org/html/draft-ietf-6lo-btle-00 By default the 6LoWPAN support is not activated and user needs to tweak /sys/kernel/debug/bluetooth/hci0/6lowpan file. The kernel needs IPv6 support before 6LoWPAN is usable. Signed-off-by: Jukka Rissanen Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 1 + include/net/bluetooth/hci_core.h | 1 + include/net/bluetooth/l2cap.h | 1 + net/bluetooth/6lowpan.c | 886 +++++++++++++++++++++++++++++++++++++++ net/bluetooth/6lowpan.h | 26 ++ net/bluetooth/Makefile | 6 +- net/bluetooth/hci_event.c | 3 + net/bluetooth/l2cap_core.c | 12 + 8 files changed, 935 insertions(+), 1 deletion(-) create mode 100644 net/bluetooth/6lowpan.c create mode 100644 net/bluetooth/6lowpan.h (limited to 'include/net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index cc2da73055fa..5dc3d9072650 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -131,6 +131,7 @@ enum { HCI_PERIODIC_INQ, HCI_FAST_CONNECTABLE, HCI_BREDR_ENABLED, + HCI_6LOWPAN_ENABLED, }; /* A mask for the flags that are supposed to remain when a reset happens diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b796161fb04e..f2f0cf5865c4 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -448,6 +448,7 @@ enum { HCI_CONN_SSP_ENABLED, HCI_CONN_POWER_SAVE, HCI_CONN_REMOTE_OOB, + HCI_CONN_6LOWPAN, }; static inline bool hci_conn_ssp_enabled(struct hci_conn *conn) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index e149e992fdae..dbc4a89984ca 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -136,6 +136,7 @@ struct l2cap_conninfo { #define L2CAP_FC_L2CAP 0x02 #define L2CAP_FC_CONNLESS 0x04 #define L2CAP_FC_A2MP 0x08 +#define L2CAP_FC_6LOWPAN 0x3e /* reserved and temporary value */ /* L2CAP Control Field bit masks */ #define L2CAP_CTRL_SAR 0xC000 diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c new file mode 100644 index 000000000000..ba840fe2c555 --- /dev/null +++ b/net/bluetooth/6lowpan.c @@ -0,0 +1,886 @@ +/* + Copyright (c) 2013 Intel Corp. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 and + only version 2 as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +*/ + +#include +#include +#include +#include + +#include +#include +#include + +#include /* to get the address type */ + +#include +#include +#include + +#include "../ieee802154/6lowpan.h" /* for the compression support */ + +#define IFACE_NAME_TEMPLATE "bt%d" +#define EUI64_ADDR_LEN 8 + +struct skb_cb { + struct in6_addr addr; + struct l2cap_conn *conn; +}; +#define lowpan_cb(skb) ((struct skb_cb *)((skb)->cb)) + +/* The devices list contains those devices that we are acting + * as a proxy. The BT 6LoWPAN device is a virtual device that + * connects to the Bluetooth LE device. The real connection to + * BT device is done via l2cap layer. There exists one + * virtual device / one BT 6LoWPAN network (=hciX device). + * The list contains struct lowpan_dev elements. + */ +static LIST_HEAD(bt_6lowpan_devices); +static DEFINE_RWLOCK(devices_lock); + +struct lowpan_peer { + struct list_head list; + struct l2cap_conn *conn; + + /* peer addresses in various formats */ + unsigned char eui64_addr[EUI64_ADDR_LEN]; + struct in6_addr peer_addr; +}; + +struct lowpan_dev { + struct list_head list; + + struct hci_dev *hdev; + struct net_device *netdev; + struct list_head peers; + atomic_t peer_count; /* number of items in peers list */ + + struct work_struct delete_netdev; + struct delayed_work notify_peers; +}; + +static inline struct lowpan_dev *lowpan_dev(const struct net_device *netdev) +{ + return netdev_priv(netdev); +} + +static inline void peer_add(struct lowpan_dev *dev, struct lowpan_peer *peer) +{ + list_add(&peer->list, &dev->peers); + atomic_inc(&dev->peer_count); +} + +static inline bool peer_del(struct lowpan_dev *dev, struct lowpan_peer *peer) +{ + list_del(&peer->list); + + if (atomic_dec_and_test(&dev->peer_count)) { + BT_DBG("last peer"); + return true; + } + + return false; +} + +static inline struct lowpan_peer *peer_lookup_ba(struct lowpan_dev *dev, + bdaddr_t *ba, __u8 type) +{ + struct lowpan_peer *peer, *tmp; + + BT_DBG("peers %d addr %pMR type %d", atomic_read(&dev->peer_count), + ba, type); + + list_for_each_entry_safe(peer, tmp, &dev->peers, list) { + BT_DBG("addr %pMR type %d", + &peer->conn->hcon->dst, peer->conn->hcon->dst_type); + + if (bacmp(&peer->conn->hcon->dst, ba)) + continue; + + if (type == peer->conn->hcon->dst_type) + return peer; + } + + return NULL; +} + +static inline struct lowpan_peer *peer_lookup_conn(struct lowpan_dev *dev, + struct l2cap_conn *conn) +{ + struct lowpan_peer *peer, *tmp; + + list_for_each_entry_safe(peer, tmp, &dev->peers, list) { + if (peer->conn == conn) + return peer; + } + + return NULL; +} + +static struct lowpan_peer *lookup_peer(struct l2cap_conn *conn) +{ + struct lowpan_dev *entry, *tmp; + struct lowpan_peer *peer = NULL; + unsigned long flags; + + read_lock_irqsave(&devices_lock, flags); + + list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) { + peer = peer_lookup_conn(entry, conn); + if (peer) + break; + } + + read_unlock_irqrestore(&devices_lock, flags); + + return peer; +} + +static struct lowpan_dev *lookup_dev(struct l2cap_conn *conn) +{ + struct lowpan_dev *entry, *tmp; + struct lowpan_dev *dev = NULL; + unsigned long flags; + + read_lock_irqsave(&devices_lock, flags); + + list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) { + if (conn->hcon->hdev == entry->hdev) { + dev = entry; + break; + } + } + + read_unlock_irqrestore(&devices_lock, flags); + + return dev; +} + +/* print data in line */ +static inline void raw_dump_inline(const char *caller, char *msg, + unsigned char *buf, int len) +{ + if (msg) + pr_debug("%s():%s: ", caller, msg); + + print_hex_dump_debug("", DUMP_PREFIX_NONE, + 16, 1, buf, len, false); +} + +/* print data in a table format: + * + * addr: xx xx xx xx xx xx + * addr: xx xx xx xx xx xx + * ... + */ +static inline void raw_dump_table(const char *caller, char *msg, + unsigned char *buf, int len) +{ + if (msg) + pr_debug("%s():%s:\n", caller, msg); + + print_hex_dump_debug("\t", DUMP_PREFIX_OFFSET, + 16, 1, buf, len, false); +} + +static int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev) +{ + struct sk_buff *skb_cp; + int ret; + + skb_cp = skb_copy(skb, GFP_ATOMIC); + if (!skb_cp) + return -ENOMEM; + + ret = netif_rx(skb_cp); + + BT_DBG("receive skb %d", ret); + if (ret < 0) + return NET_RX_DROP; + + return ret; +} + +static int process_data(struct sk_buff *skb, struct net_device *netdev, + struct l2cap_conn *conn) +{ + const u8 *saddr, *daddr; + u8 iphc0, iphc1; + struct lowpan_dev *dev; + struct lowpan_peer *peer; + unsigned long flags; + + dev = lowpan_dev(netdev); + + read_lock_irqsave(&devices_lock, flags); + peer = peer_lookup_conn(dev, conn); + read_unlock_irqrestore(&devices_lock, flags); + if (!peer) + goto drop; + + saddr = peer->eui64_addr; + daddr = dev->netdev->dev_addr; + + /* at least two bytes will be used for the encoding */ + if (skb->len < 2) + goto drop; + + if (lowpan_fetch_skb_u8(skb, &iphc0)) + goto drop; + + if (lowpan_fetch_skb_u8(skb, &iphc1)) + goto drop; + + return lowpan_process_data(skb, netdev, + saddr, IEEE802154_ADDR_LONG, EUI64_ADDR_LEN, + daddr, IEEE802154_ADDR_LONG, EUI64_ADDR_LEN, + iphc0, iphc1, give_skb_to_upper); + +drop: + kfree_skb(skb); + return -EINVAL; +} + +static int recv_pkt(struct sk_buff *skb, struct net_device *dev, + struct l2cap_conn *conn) +{ + struct sk_buff *local_skb; + int ret; + + if (!netif_running(dev)) + goto drop; + + if (dev->type != ARPHRD_6LOWPAN) + goto drop; + + /* check that it's our buffer */ + if (skb->data[0] == LOWPAN_DISPATCH_IPV6) { + /* Copy the packet so that the IPv6 header is + * properly aligned. + */ + local_skb = skb_copy_expand(skb, NET_SKB_PAD - 1, + skb_tailroom(skb), GFP_ATOMIC); + if (!local_skb) + goto drop; + + local_skb->protocol = htons(ETH_P_IPV6); + local_skb->pkt_type = PACKET_HOST; + + skb_reset_network_header(local_skb); + skb_set_transport_header(local_skb, sizeof(struct ipv6hdr)); + + if (give_skb_to_upper(local_skb, dev) != NET_RX_SUCCESS) { + kfree_skb(local_skb); + goto drop; + } + + dev->stats.rx_bytes += skb->len; + dev->stats.rx_packets++; + + kfree_skb(local_skb); + kfree_skb(skb); + } else { + switch (skb->data[0] & 0xe0) { + case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */ + local_skb = skb_clone(skb, GFP_ATOMIC); + if (!local_skb) + goto drop; + + ret = process_data(local_skb, dev, conn); + if (ret != NET_RX_SUCCESS) + goto drop; + + dev->stats.rx_bytes += skb->len; + dev->stats.rx_packets++; + + kfree_skb(skb); + break; + default: + break; + } + } + + return NET_RX_SUCCESS; + +drop: + kfree_skb(skb); + return NET_RX_DROP; +} + +/* Packet from BT LE device */ +int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb) +{ + struct lowpan_dev *dev; + struct lowpan_peer *peer; + int err; + + peer = lookup_peer(conn); + if (!peer) + return -ENOENT; + + dev = lookup_dev(conn); + if (dev && !dev->netdev) + return -ENOENT; + + err = recv_pkt(skb, dev->netdev, conn); + BT_DBG("recv pkt %d", err); + + return err; +} + +static inline int skbuff_copy(void *msg, int len, int count, int mtu, + struct sk_buff *skb, struct net_device *dev) +{ + struct sk_buff **frag; + int sent = 0; + + memcpy(skb_put(skb, count), msg, count); + + sent += count; + msg += count; + len -= count; + + dev->stats.tx_bytes += count; + dev->stats.tx_packets++; + + raw_dump_table(__func__, "Sending", skb->data, skb->len); + + /* Continuation fragments (no L2CAP header) */ + frag = &skb_shinfo(skb)->frag_list; + while (len > 0) { + struct sk_buff *tmp; + + count = min_t(unsigned int, mtu, len); + + tmp = bt_skb_alloc(count, GFP_ATOMIC); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + + *frag = tmp; + + memcpy(skb_put(*frag, count), msg, count); + + raw_dump_table(__func__, "Sending fragment", + (*frag)->data, count); + + (*frag)->priority = skb->priority; + + sent += count; + msg += count; + len -= count; + + skb->len += (*frag)->len; + skb->data_len += (*frag)->len; + + frag = &(*frag)->next; + + dev->stats.tx_bytes += count; + dev->stats.tx_packets++; + } + + return sent; +} + +static struct sk_buff *create_pdu(struct l2cap_conn *conn, void *msg, + size_t len, u32 priority, + struct net_device *dev) +{ + struct sk_buff *skb; + int err, count; + struct l2cap_hdr *lh; + + /* FIXME: This mtu check should be not needed and atm is only used for + * testing purposes + */ + if (conn->mtu > (L2CAP_LE_MIN_MTU + L2CAP_HDR_SIZE)) + conn->mtu = L2CAP_LE_MIN_MTU + L2CAP_HDR_SIZE; + + count = min_t(unsigned int, (conn->mtu - L2CAP_HDR_SIZE), len); + + BT_DBG("conn %p len %zu mtu %d count %d", conn, len, conn->mtu, count); + + skb = bt_skb_alloc(count + L2CAP_HDR_SIZE, GFP_ATOMIC); + if (IS_ERR(skb)) + return skb; + + skb->priority = priority; + + lh = (struct l2cap_hdr *)skb_put(skb, L2CAP_HDR_SIZE); + lh->cid = cpu_to_le16(L2CAP_FC_6LOWPAN); + lh->len = cpu_to_le16(len); + + err = skbuff_copy(msg, len, count, conn->mtu, skb, dev); + if (unlikely(err < 0)) { + kfree_skb(skb); + BT_DBG("skbuff copy %d failed", err); + return ERR_PTR(err); + } + + return skb; +} + +static int conn_send(struct l2cap_conn *conn, + void *msg, size_t len, u32 priority, + struct net_device *dev) +{ + struct sk_buff *skb; + + skb = create_pdu(conn, msg, len, priority, dev); + if (IS_ERR(skb)) + return -EINVAL; + + BT_DBG("conn %p skb %p len %d priority %u", conn, skb, skb->len, + skb->priority); + + hci_send_acl(conn->hchan, skb, ACL_START); + + return 0; +} + +static void get_dest_bdaddr(struct in6_addr *ip6_daddr, + bdaddr_t *addr, u8 *addr_type) +{ + u8 *eui64; + + eui64 = ip6_daddr->s6_addr + 8; + + addr->b[0] = eui64[7]; + addr->b[1] = eui64[6]; + addr->b[2] = eui64[5]; + addr->b[3] = eui64[2]; + addr->b[4] = eui64[1]; + addr->b[5] = eui64[0]; + + addr->b[5] ^= 2; + + /* Set universal/local bit to 0 */ + if (addr->b[5] & 1) { + addr->b[5] &= ~1; + *addr_type = BDADDR_LE_PUBLIC; + } else { + *addr_type = BDADDR_LE_RANDOM; + } +} + +static int header_create(struct sk_buff *skb, struct net_device *netdev, + unsigned short type, const void *_daddr, + const void *_saddr, unsigned int len) +{ + struct ipv6hdr *hdr; + struct lowpan_dev *dev; + struct lowpan_peer *peer; + bdaddr_t addr, *any = BDADDR_ANY; + u8 *saddr, *daddr = any->b; + u8 addr_type; + + if (type != ETH_P_IPV6) + return -EINVAL; + + hdr = ipv6_hdr(skb); + + dev = lowpan_dev(netdev); + + if (ipv6_addr_is_multicast(&hdr->daddr)) { + memcpy(&lowpan_cb(skb)->addr, &hdr->daddr, + sizeof(struct in6_addr)); + lowpan_cb(skb)->conn = NULL; + } else { + unsigned long flags; + + /* Get destination BT device from skb. + * If there is no such peer then discard the packet. + */ + get_dest_bdaddr(&hdr->daddr, &addr, &addr_type); + + BT_DBG("dest addr %pMR type %d", &addr, addr_type); + + read_lock_irqsave(&devices_lock, flags); + peer = peer_lookup_ba(dev, &addr, addr_type); + read_unlock_irqrestore(&devices_lock, flags); + + if (!peer) { + BT_DBG("no such peer %pMR found", &addr); + return -ENOENT; + } + + daddr = peer->eui64_addr; + + memcpy(&lowpan_cb(skb)->addr, &hdr->daddr, + sizeof(struct in6_addr)); + lowpan_cb(skb)->conn = peer->conn; + } + + saddr = dev->netdev->dev_addr; + + return lowpan_header_compress(skb, netdev, type, daddr, saddr, len); +} + +/* Packet to BT LE device */ +static int send_pkt(struct l2cap_conn *conn, const void *saddr, + const void *daddr, struct sk_buff *skb, + struct net_device *netdev) +{ + raw_dump_table(__func__, "raw skb data dump before fragmentation", + skb->data, skb->len); + + return conn_send(conn, skb->data, skb->len, 0, netdev); +} + +static void send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev) +{ + struct sk_buff *local_skb; + struct lowpan_dev *entry, *tmp; + unsigned long flags; + + read_lock_irqsave(&devices_lock, flags); + + list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) { + struct lowpan_peer *pentry, *ptmp; + struct lowpan_dev *dev; + + if (entry->netdev != netdev) + continue; + + dev = lowpan_dev(entry->netdev); + + list_for_each_entry_safe(pentry, ptmp, &dev->peers, list) { + local_skb = skb_clone(skb, GFP_ATOMIC); + + send_pkt(pentry->conn, netdev->dev_addr, + pentry->eui64_addr, local_skb, netdev); + + kfree_skb(local_skb); + } + } + + read_unlock_irqrestore(&devices_lock, flags); +} + +static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + int err = 0; + unsigned char *eui64_addr; + struct lowpan_dev *dev; + struct lowpan_peer *peer; + bdaddr_t addr; + u8 addr_type; + + if (ipv6_addr_is_multicast(&lowpan_cb(skb)->addr)) { + /* We need to send the packet to every device + * behind this interface. + */ + send_mcast_pkt(skb, netdev); + } else { + unsigned long flags; + + get_dest_bdaddr(&lowpan_cb(skb)->addr, &addr, &addr_type); + eui64_addr = lowpan_cb(skb)->addr.s6_addr + 8; + dev = lowpan_dev(netdev); + + read_lock_irqsave(&devices_lock, flags); + peer = peer_lookup_ba(dev, &addr, addr_type); + read_unlock_irqrestore(&devices_lock, flags); + + BT_DBG("xmit from %s to %pMR (%pI6c) peer %p", netdev->name, + &addr, &lowpan_cb(skb)->addr, peer); + + if (peer && peer->conn) + err = send_pkt(peer->conn, netdev->dev_addr, + eui64_addr, skb, netdev); + } + dev_kfree_skb(skb); + + if (err) + BT_DBG("ERROR: xmit failed (%d)", err); + + return (err < 0) ? NET_XMIT_DROP : err; +} + +static const struct net_device_ops netdev_ops = { + .ndo_start_xmit = bt_xmit, +}; + +static struct header_ops header_ops = { + .create = header_create, +}; + +static void netdev_setup(struct net_device *dev) +{ + dev->addr_len = EUI64_ADDR_LEN; + dev->type = ARPHRD_6LOWPAN; + + dev->hard_header_len = 0; + dev->needed_tailroom = 0; + dev->mtu = IPV6_MIN_MTU; + dev->tx_queue_len = 0; + dev->flags = IFF_RUNNING | IFF_POINTOPOINT; + dev->watchdog_timeo = 0; + + dev->netdev_ops = &netdev_ops; + dev->header_ops = &header_ops; + dev->destructor = free_netdev; +} + +static struct device_type bt_type = { + .name = "bluetooth", +}; + +static void set_addr(u8 *eui, u8 *addr, u8 addr_type) +{ + /* addr is the BT address in little-endian format */ + eui[0] = addr[5]; + eui[1] = addr[4]; + eui[2] = addr[3]; + eui[3] = 0xFF; + eui[4] = 0xFE; + eui[5] = addr[2]; + eui[6] = addr[1]; + eui[7] = addr[0]; + + eui[0] ^= 2; + + /* Universal/local bit set, RFC 4291 */ + if (addr_type == BDADDR_LE_PUBLIC) + eui[0] |= 1; + else + eui[0] &= ~1; +} + +static void set_dev_addr(struct net_device *netdev, bdaddr_t *addr, + u8 addr_type) +{ + netdev->addr_assign_type = NET_ADDR_PERM; + set_addr(netdev->dev_addr, addr->b, addr_type); + netdev->dev_addr[0] ^= 2; +} + +static void ifup(struct net_device *netdev) +{ + int err; + + rtnl_lock(); + err = dev_open(netdev); + if (err < 0) + BT_INFO("iface %s cannot be opened (%d)", netdev->name, err); + rtnl_unlock(); +} + +static void do_notify_peers(struct work_struct *work) +{ + struct lowpan_dev *dev = container_of(work, struct lowpan_dev, + notify_peers.work); + + netdev_notify_peers(dev->netdev); /* send neighbour adv at startup */ +} + +static bool is_bt_6lowpan(struct hci_conn *hcon) +{ + if (hcon->type != LE_LINK) + return false; + + return test_bit(HCI_CONN_6LOWPAN, &hcon->flags); +} + +static int add_peer_conn(struct l2cap_conn *conn, struct lowpan_dev *dev) +{ + struct lowpan_peer *peer; + unsigned long flags; + + peer = kzalloc(sizeof(*peer), GFP_ATOMIC); + if (!peer) + return -ENOMEM; + + peer->conn = conn; + memset(&peer->peer_addr, 0, sizeof(struct in6_addr)); + + /* RFC 2464 ch. 5 */ + peer->peer_addr.s6_addr[0] = 0xFE; + peer->peer_addr.s6_addr[1] = 0x80; + set_addr((u8 *)&peer->peer_addr.s6_addr + 8, conn->hcon->dst.b, + conn->hcon->dst_type); + + memcpy(&peer->eui64_addr, (u8 *)&peer->peer_addr.s6_addr + 8, + EUI64_ADDR_LEN); + peer->eui64_addr[0] ^= 2; /* second bit-flip (Universe/Local) + * is done according RFC2464 + */ + + raw_dump_inline(__func__, "peer IPv6 address", + (unsigned char *)&peer->peer_addr, 16); + raw_dump_inline(__func__, "peer EUI64 address", peer->eui64_addr, 8); + + write_lock_irqsave(&devices_lock, flags); + INIT_LIST_HEAD(&peer->list); + peer_add(dev, peer); + write_unlock_irqrestore(&devices_lock, flags); + + /* Notifying peers about us needs to be done without locks held */ + INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers); + schedule_delayed_work(&dev->notify_peers, msecs_to_jiffies(100)); + + return 0; +} + +/* This gets called when BT LE 6LoWPAN device is connected. We then + * create network device that acts as a proxy between BT LE device + * and kernel network stack. + */ +int bt_6lowpan_add_conn(struct l2cap_conn *conn) +{ + struct lowpan_peer *peer = NULL; + struct lowpan_dev *dev; + struct net_device *netdev; + int err = 0; + unsigned long flags; + + if (!is_bt_6lowpan(conn->hcon)) + return 0; + + peer = lookup_peer(conn); + if (peer) + return -EEXIST; + + dev = lookup_dev(conn); + if (dev) + return add_peer_conn(conn, dev); + + netdev = alloc_netdev(sizeof(*dev), IFACE_NAME_TEMPLATE, netdev_setup); + if (!netdev) + return -ENOMEM; + + set_dev_addr(netdev, &conn->hcon->src, conn->hcon->src_type); + + netdev->netdev_ops = &netdev_ops; + SET_NETDEV_DEV(netdev, &conn->hcon->dev); + SET_NETDEV_DEVTYPE(netdev, &bt_type); + + err = register_netdev(netdev); + if (err < 0) { + BT_INFO("register_netdev failed %d", err); + free_netdev(netdev); + goto out; + } + + BT_DBG("ifindex %d peer bdaddr %pMR my addr %pMR", + netdev->ifindex, &conn->hcon->dst, &conn->hcon->src); + set_bit(__LINK_STATE_PRESENT, &netdev->state); + + dev = netdev_priv(netdev); + dev->netdev = netdev; + dev->hdev = conn->hcon->hdev; + INIT_LIST_HEAD(&dev->peers); + + write_lock_irqsave(&devices_lock, flags); + INIT_LIST_HEAD(&dev->list); + list_add(&dev->list, &bt_6lowpan_devices); + write_unlock_irqrestore(&devices_lock, flags); + + ifup(netdev); + + return add_peer_conn(conn, dev); + +out: + return err; +} + +static void delete_netdev(struct work_struct *work) +{ + struct lowpan_dev *entry = container_of(work, struct lowpan_dev, + delete_netdev); + + unregister_netdev(entry->netdev); + + /* The entry pointer is deleted in device_event() */ +} + +int bt_6lowpan_del_conn(struct l2cap_conn *conn) +{ + struct lowpan_dev *entry, *tmp; + struct lowpan_dev *dev = NULL; + struct lowpan_peer *peer; + int err = -ENOENT; + unsigned long flags; + bool last = false; + + if (!is_bt_6lowpan(conn->hcon)) + return 0; + + write_lock_irqsave(&devices_lock, flags); + + list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) { + dev = lowpan_dev(entry->netdev); + peer = peer_lookup_conn(dev, conn); + if (peer) { + last = peer_del(dev, peer); + err = 0; + break; + } + } + + if (!err && last && dev && !atomic_read(&dev->peer_count)) { + write_unlock_irqrestore(&devices_lock, flags); + + cancel_delayed_work_sync(&dev->notify_peers); + + /* bt_6lowpan_del_conn() is called with hci dev lock held which + * means that we must delete the netdevice in worker thread. + */ + INIT_WORK(&entry->delete_netdev, delete_netdev); + schedule_work(&entry->delete_netdev); + } else { + write_unlock_irqrestore(&devices_lock, flags); + } + + return err; +} + +static int device_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *netdev = netdev_notifier_info_to_dev(ptr); + struct lowpan_dev *entry, *tmp; + unsigned long flags; + + if (netdev->type != ARPHRD_6LOWPAN) + return NOTIFY_DONE; + + switch (event) { + case NETDEV_UNREGISTER: + write_lock_irqsave(&devices_lock, flags); + list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, + list) { + if (entry->netdev == netdev) { + list_del(&entry->list); + kfree(entry); + break; + } + } + write_unlock_irqrestore(&devices_lock, flags); + break; + } + + return NOTIFY_DONE; +} + +static struct notifier_block bt_6lowpan_dev_notifier = { + .notifier_call = device_event, +}; + +int bt_6lowpan_init(void) +{ + return register_netdevice_notifier(&bt_6lowpan_dev_notifier); +} + +void bt_6lowpan_cleanup(void) +{ + unregister_netdevice_notifier(&bt_6lowpan_dev_notifier); +} diff --git a/net/bluetooth/6lowpan.h b/net/bluetooth/6lowpan.h new file mode 100644 index 000000000000..680eac808d74 --- /dev/null +++ b/net/bluetooth/6lowpan.h @@ -0,0 +1,26 @@ +/* + Copyright (c) 2013 Intel Corp. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 and + only version 2 as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +*/ + +#ifndef __6LOWPAN_H +#define __6LOWPAN_H + +#include +#include + +int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb); +int bt_6lowpan_add_conn(struct l2cap_conn *conn); +int bt_6lowpan_del_conn(struct l2cap_conn *conn); +int bt_6lowpan_init(void); +void bt_6lowpan_cleanup(void); + +#endif /* __6LOWPAN_H */ diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index 6a791e73e39d..cc6827e2ce68 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -10,6 +10,10 @@ obj-$(CONFIG_BT_HIDP) += hidp/ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \ hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \ - a2mp.o amp.o + a2mp.o amp.o 6lowpan.o + +ifeq ($(CONFIG_IEEE802154_6LOWPAN),) + bluetooth-y += ../ieee802154/6lowpan_iphc.o +endif subdir-ccflags-y += -D__CHECK_ENDIAN__ diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 5fb3df66c2cd..5f812455a450 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3533,6 +3533,9 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) conn->handle = __le16_to_cpu(ev->handle); conn->state = BT_CONNECTED; + if (test_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags)) + set_bit(HCI_CONN_6LOWPAN, &conn->flags); + hci_conn_add_sysfs(conn); hci_proto_connect_cfm(conn, ev->status); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index b6bca64b320d..b0ad2c752d73 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -40,6 +40,7 @@ #include "smp.h" #include "a2mp.h" #include "amp.h" +#include "6lowpan.h" bool disable_ertm; @@ -1468,6 +1469,8 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) BT_DBG(""); + bt_6lowpan_add_conn(conn); + /* Check if we have socket listening on cid */ pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_ATT, &hcon->src, &hcon->dst); @@ -7119,6 +7122,10 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) l2cap_conn_del(conn->hcon, EACCES); break; + case L2CAP_FC_6LOWPAN: + bt_6lowpan_recv(conn, skb); + break; + default: l2cap_data_channel(conn, cid, skb); break; @@ -7186,6 +7193,8 @@ void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) { BT_DBG("hcon %p reason %d", hcon, reason); + bt_6lowpan_del_conn(hcon->l2cap_data); + l2cap_conn_del(hcon, bt_to_errno(reason)); } @@ -7467,11 +7476,14 @@ int __init l2cap_init(void) debugfs_create_u16("l2cap_le_default_mps", 0466, bt_debugfs, &le_default_mps); + bt_6lowpan_init(); + return 0; } void l2cap_exit(void) { + bt_6lowpan_cleanup(); debugfs_remove(l2cap_debugfs); l2cap_cleanup_sockets(); } -- cgit v1.2.3 From 7e9805696428113e34625a65a30dbc62cb78acc5 Mon Sep 17 00:00:00 2001 From: Jiri Benc Date: Wed, 11 Dec 2013 13:48:20 +0100 Subject: ipv6: router reachability probing RFC 4191 states in 3.5: When a host avoids using any non-reachable router X and instead sends a data packet to another router Y, and the host would have used router X if router X were reachable, then the host SHOULD probe each such router X's reachability by sending a single Neighbor Solicitation to that router's address. A host MUST NOT probe a router's reachability in the absence of useful traffic that the host would have sent to the router if it were reachable. In any case, these probes MUST be rate-limited to no more than one per minute per router. Currently, when the neighbour corresponding to a router falls into NUD_FAILED, it's never considered again. Introduce a new rt6_nud_state value, RT6_NUD_FAIL_PROBE, which suggests the route should not be used but should be probed with a single NS. The probe is ratelimited by the existing code. To better distinguish meanings of the failure values, rename RT6_NUD_FAIL_SOFT to RT6_NUD_FAIL_DO_RR. Signed-off-by: Jiri Benc Acked-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/net/neighbour.h | 1 + net/core/neighbour.c | 15 +++++++++++++++ net/ipv6/route.c | 16 ++++++++++------ 3 files changed, 26 insertions(+), 6 deletions(-) (limited to 'include/net') diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 41b1ce6c96a8..4c09bd23b832 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -252,6 +252,7 @@ static inline struct neighbour *neigh_create(struct neigh_table *tbl, void neigh_destroy(struct neighbour *neigh); int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb); int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, u32 flags); +void __neigh_set_probe_once(struct neighbour *neigh); void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev); int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev); int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb); diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 8be82a7f0e0f..bf6f404c04aa 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1238,6 +1238,21 @@ out: } EXPORT_SYMBOL(neigh_update); +/* Update the neigh to listen temporarily for probe responses, even if it is + * in a NUD_FAILED state. The caller has to hold neigh->lock for writing. + */ +void __neigh_set_probe_once(struct neighbour *neigh) +{ + neigh->updated = jiffies; + if (!(neigh->nud_state & NUD_FAILED)) + return; + neigh->nud_state = NUD_PROBE; + atomic_set(&neigh->probes, NEIGH_VAR(neigh->parms, UCAST_PROBES)); + neigh_add_timer(neigh, + jiffies + NEIGH_VAR(neigh->parms, RETRANS_TIME)); +} +EXPORT_SYMBOL(__neigh_set_probe_once); + struct neighbour *neigh_event_ns(struct neigh_table *tbl, u8 *lladdr, void *saddr, struct net_device *dev) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index ddb9d41c8eea..a1a57523b158 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -66,8 +66,9 @@ #endif enum rt6_nud_state { - RT6_NUD_FAIL_HARD = -2, - RT6_NUD_FAIL_SOFT = -1, + RT6_NUD_FAIL_HARD = -3, + RT6_NUD_FAIL_PROBE = -2, + RT6_NUD_FAIL_DO_RR = -1, RT6_NUD_SUCCEED = 1 }; @@ -521,7 +522,7 @@ static void rt6_probe(struct rt6_info *rt) work = kmalloc(sizeof(*work), GFP_ATOMIC); if (neigh && work) - neigh->updated = jiffies; + __neigh_set_probe_once(neigh); if (neigh) write_unlock(&neigh->lock); @@ -577,11 +578,13 @@ static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt) #ifdef CONFIG_IPV6_ROUTER_PREF else if (!(neigh->nud_state & NUD_FAILED)) ret = RT6_NUD_SUCCEED; + else + ret = RT6_NUD_FAIL_PROBE; #endif read_unlock(&neigh->lock); } else { ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ? - RT6_NUD_SUCCEED : RT6_NUD_FAIL_SOFT; + RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR; } rcu_read_unlock_bh(); @@ -618,16 +621,17 @@ static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict, goto out; m = rt6_score_route(rt, oif, strict); - if (m == RT6_NUD_FAIL_SOFT) { + if (m == RT6_NUD_FAIL_DO_RR) { match_do_rr = true; m = 0; /* lowest valid score */ - } else if (m < 0) { + } else if (m == RT6_NUD_FAIL_HARD) { goto out; } if (strict & RT6_LOOKUP_F_REACHABLE) rt6_probe(rt); + /* note that m can be RT6_NUD_FAIL_PROBE at this point */ if (m > *mpri) { *do_rr = match_do_rr; *mpri = m; -- cgit v1.2.3 From 8cf4d6a224a0226987d9cba69cb46d93814fe449 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Fri, 15 Nov 2013 15:57:53 +0100 Subject: net: reorder struct netns_ct for better cache-line usage Reorder struct netns_ct so that atomic_t "count" changes don't slowdown users of read mostly fields. This is based on Eric Dumazet's proposed patch: "netfilter: conntrack: remove the central spinlock" http://thread.gmane.org/gmane.linux.network/268758/focus=47306 The tricky part of cache-aligning this structure, that it is getting inlined in struct net (include/net/net_namespace.h), thus changes to other netns_xxx structures affects our alignment. Eric's original patch contained an ambiguity on 32-bit regarding alignment in struct net. This patch also takes 32-bit into account, and in case of changed (struct net) alignment sysctl_xxx entries have been ordered according to how often they are accessed. Signed-off-by: Jesper Dangaard Brouer Reviewed-by: Jiri Benc Signed-off-by: Pablo Neira Ayuso --- include/net/netns/conntrack.h | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) (limited to 'include/net') diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h index c9c0c538b68b..fbcc7fa536dc 100644 --- a/include/net/netns/conntrack.h +++ b/include/net/netns/conntrack.h @@ -65,6 +65,23 @@ struct nf_ip_net { struct netns_ct { atomic_t count; unsigned int expect_count; +#ifdef CONFIG_SYSCTL + struct ctl_table_header *sysctl_header; + struct ctl_table_header *acct_sysctl_header; + struct ctl_table_header *tstamp_sysctl_header; + struct ctl_table_header *event_sysctl_header; + struct ctl_table_header *helper_sysctl_header; +#endif + char *slabname; + unsigned int sysctl_log_invalid; /* Log invalid packets */ + unsigned int sysctl_events_retry_timeout; + int sysctl_events; + int sysctl_acct; + int sysctl_auto_assign_helper; + bool auto_assign_helper_warned; + int sysctl_tstamp; + int sysctl_checksum; + unsigned int htable_size; struct kmem_cache *nf_conntrack_cachep; struct hlist_nulls_head *hash; @@ -75,14 +92,6 @@ struct netns_ct { struct ip_conntrack_stat __percpu *stat; struct nf_ct_event_notifier __rcu *nf_conntrack_event_cb; struct nf_exp_event_notifier __rcu *nf_expect_event_cb; - int sysctl_events; - unsigned int sysctl_events_retry_timeout; - int sysctl_acct; - int sysctl_tstamp; - int sysctl_checksum; - unsigned int sysctl_log_invalid; /* Log invalid packets */ - int sysctl_auto_assign_helper; - bool auto_assign_helper_warned; struct nf_ip_net nf_ct_proto; #if defined(CONFIG_NF_CONNTRACK_LABELS) unsigned int labels_used; @@ -92,13 +101,5 @@ struct netns_ct { struct hlist_head *nat_bysource; unsigned int nat_htable_size; #endif -#ifdef CONFIG_SYSCTL - struct ctl_table_header *sysctl_header; - struct ctl_table_header *acct_sysctl_header; - struct ctl_table_header *tstamp_sysctl_header; - struct ctl_table_header *event_sysctl_header; - struct ctl_table_header *helper_sysctl_header; -#endif - char *slabname; }; #endif -- cgit v1.2.3 From 6a9d1b91f34df1935bc0ad98114801a44db0f98c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 4 Dec 2013 22:39:17 +0100 Subject: mac80211: add pre-RCU-sync sta removal driver operation Currently, mac80211 allows drivers to keep RCU-protected station references that are cleared when the station is removed from the driver and consequently needs to synchronize twice, once before removing the station from the driver (so it can guarantee that the station is no longer used in TX towards the driver) and once after the station is removed from the driver. Add a new pre-RCU-synchronisation station removal operation to the API to allow drivers to clear/invalidate their RCU-protected station pointers before the RCU synchronisation. This will allow removing the second synchronisation by changing the driver API so that the driver may no longer assume a valid RCU-protected pointer after sta_remove/sta_state returns. The alternative to this would be to synchronize_rcu() in all the drivers that currently rely on this behaviour (only iwlmvm) but that would defeat the purpose. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 21 +++++++++++++++++++-- net/mac80211/driver-ops.h | 16 ++++++++++++++++ net/mac80211/sta_info.c | 2 ++ net/mac80211/trace.h | 34 +++++++++++++++------------------- 4 files changed, 52 insertions(+), 21 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index c014acc09ebc..22b6dd96dbc6 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2495,7 +2495,11 @@ enum ieee80211_roc_type { * AP, IBSS/WDS/mesh peer etc. This callback can sleep. * * @sta_remove: Notifies low level driver about removal of an associated - * station, AP, IBSS/WDS/mesh peer etc. This callback can sleep. + * station, AP, IBSS/WDS/mesh peer etc. Note that after the callback + * returns it isn't safe to use the pointer, not even RCU protected; + * no RCU grace period is guaranteed between returning here and freeing + * the station. See @sta_pre_rcu_remove if needed. + * This callback can sleep. * * @sta_add_debugfs: Drivers can use this callback to add debugfs files * when a station is added to mac80211's station list. This callback @@ -2514,7 +2518,17 @@ enum ieee80211_roc_type { * station (which can be the AP, a client, IBSS/WDS/mesh peer etc.) * This callback is mutually exclusive with @sta_add/@sta_remove. * It must not fail for down transitions but may fail for transitions - * up the list of states. + * up the list of states. Also note that after the callback returns it + * isn't safe to use the pointer, not even RCU protected - no RCU grace + * period is guaranteed between returning here and freeing the station. + * See @sta_pre_rcu_remove if needed. + * The callback can sleep. + * + * @sta_pre_rcu_remove: Notify driver about station removal before RCU + * synchronisation. This is useful if a driver needs to have station + * pointers protected using RCU, it can then use this call to clear + * the pointers instead of waiting for an RCU grace period to elapse + * in @sta_state. * The callback can sleep. * * @sta_rc_update: Notifies the driver of changes to the bitrates that can be @@ -2827,6 +2841,9 @@ struct ieee80211_ops { struct ieee80211_sta *sta, enum ieee80211_sta_state old_state, enum ieee80211_sta_state new_state); + void (*sta_pre_rcu_remove)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta); void (*sta_rc_update)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index f98059a45aa5..ef8b385eff04 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -534,6 +534,22 @@ static inline void drv_sta_remove_debugfs(struct ieee80211_local *local, } #endif +static inline void drv_sta_pre_rcu_remove(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct sta_info *sta) +{ + might_sleep(); + + sdata = get_bss_sdata(sdata); + check_sdata_in_driver(sdata); + + trace_drv_sta_pre_rcu_remove(local, sdata, &sta->sta); + if (local->ops->sta_pre_rcu_remove) + local->ops->sta_pre_rcu_remove(&local->hw, &sdata->vif, + &sta->sta); + trace_drv_return_void(local); +} + static inline __must_check int drv_sta_state(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 8ae37f60d60c..1e147742eccf 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -873,6 +873,8 @@ int __must_check __sta_info_destroy(struct sta_info *sta) list_del_rcu(&sta->list); + drv_sta_pre_rcu_remove(local, sta->sdata, sta); + /* this always calls synchronize_net() */ ieee80211_free_sta_keys(local, sta); diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 854bc5943127..3a669d7ec7ad 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -766,7 +766,7 @@ TRACE_EVENT(drv_sta_rc_update, ) ); -TRACE_EVENT(drv_sta_add, +DECLARE_EVENT_CLASS(sta_event, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_sta *sta), @@ -791,29 +791,25 @@ TRACE_EVENT(drv_sta_add, ) ); -TRACE_EVENT(drv_sta_remove, +DEFINE_EVENT(sta_event, drv_sta_add, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_sta *sta), + TP_ARGS(local, sdata, sta) +); - TP_ARGS(local, sdata, sta), - - TP_STRUCT__entry( - LOCAL_ENTRY - VIF_ENTRY - STA_ENTRY - ), - - TP_fast_assign( - LOCAL_ASSIGN; - VIF_ASSIGN; - STA_ASSIGN; - ), +DEFINE_EVENT(sta_event, drv_sta_remove, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta *sta), + TP_ARGS(local, sdata, sta) +); - TP_printk( - LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT, - LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG - ) +DEFINE_EVENT(sta_event, drv_sta_pre_rcu_remove, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta *sta), + TP_ARGS(local, sdata, sta) ); TRACE_EVENT(drv_conf_tx, -- cgit v1.2.3 From 70dabeb74ee3bff71b65f47546dafb83edbb06b9 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 14 Dec 2013 13:54:53 +0100 Subject: mac80211: let the driver reserve extra tailroom in beacons Can be used to add extra IEs (such as P2P NoA) without having to reallocate the buffer. Signed-off-by: Felix Fietkau Signed-off-by: Johannes Berg --- include/net/mac80211.h | 4 ++++ net/mac80211/tx.c | 9 ++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 22b6dd96dbc6..531785f5819e 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1613,6 +1613,9 @@ enum ieee80211_hw_flags { * @extra_tx_headroom: headroom to reserve in each transmit skb * for use by the driver (e.g. for transmit headers.) * + * @extra_beacon_tailroom: tailroom to reserve in each beacon tx skb. + * Can be used by drivers to add extra IEs. + * * @channel_change_time: time (in microseconds) it takes to change channels. * * @max_signal: Maximum value for signal (rssi) in RX information, used @@ -1695,6 +1698,7 @@ struct ieee80211_hw { void *priv; u32 flags; unsigned int extra_tx_headroom; + unsigned int extra_beacon_tailroom; int channel_change_time; int vif_data_size; int sta_data_size; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 6d59e21cdb9f..002ded2c01f6 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2549,7 +2549,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, */ skb = dev_alloc_skb(local->tx_headroom + beacon->head_len + - beacon->tail_len + 256); + beacon->tail_len + 256 + + local->hw.extra_beacon_tailroom); if (!skb) goto out; @@ -2581,7 +2582,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, ieee80211_update_csa(sdata, presp); - skb = dev_alloc_skb(local->tx_headroom + presp->head_len); + skb = dev_alloc_skb(local->tx_headroom + presp->head_len + + local->hw.extra_beacon_tailroom); if (!skb) goto out; skb_reserve(skb, local->tx_headroom); @@ -2608,7 +2610,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, skb = dev_alloc_skb(local->tx_headroom + bcn->head_len + 256 + /* TIM IE */ - bcn->tail_len); + bcn->tail_len + + local->hw.extra_beacon_tailroom); if (!skb) goto out; skb_reserve(skb, local->tx_headroom); -- cgit v1.2.3 From 776e9dd90ca223b82166eb2835389493b5914cba Mon Sep 17 00:00:00 2001 From: Fan Du Date: Mon, 16 Dec 2013 18:47:49 +0800 Subject: xfrm: export verify_userspi_info for pkfey and netlink interface In order to check against valid IPcomp spi range, export verify_userspi_info for both pfkey and netlink interface. Signed-off-by: Fan Du Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 1 + net/key/af_key.c | 6 ++++++ net/xfrm/xfrm_state.c | 24 ++++++++++++++++++++++++ net/xfrm/xfrm_user.c | 25 +------------------------ 4 files changed, 32 insertions(+), 24 deletions(-) (limited to 'include/net') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 59f5d0a6c7f8..b7635ef4d436 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1563,6 +1563,7 @@ struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8, int dir, u32 id, int delete, int *err); int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info); u32 xfrm_get_acqseq(void); +int verify_spi_info(u8 proto, u32 min, u32 max); int xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi); struct xfrm_state *xfrm_find_acq(struct net *net, const struct xfrm_mark *mark, u8 mode, u32 reqid, u8 proto, diff --git a/net/key/af_key.c b/net/key/af_key.c index 5beabd8ba772..1a04c1329362 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -1340,6 +1340,12 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, const struct sadb_ max_spi = range->sadb_spirange_max; } + err = verify_spi_info(x->id.proto, min_spi, max_spi); + if (err) { + xfrm_state_put(x); + return err; + } + err = xfrm_alloc_spi(x, min_spi, max_spi); resp_skb = err ? ERR_PTR(err) : pfkey_xfrm_state2msg(x); diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index e845066547c4..a62c25ea3631 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -1489,6 +1489,30 @@ u32 xfrm_get_acqseq(void) } EXPORT_SYMBOL(xfrm_get_acqseq); +int verify_spi_info(u8 proto, u32 min, u32 max) +{ + switch (proto) { + case IPPROTO_AH: + case IPPROTO_ESP: + break; + + case IPPROTO_COMP: + /* IPCOMP spi is 16-bits. */ + if (max >= 0x10000) + return -EINVAL; + break; + + default: + return -EINVAL; + } + + if (min > max) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL(verify_spi_info); + int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high) { struct net *net = xs_net(x); diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 4027c4266a87..97681a390402 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -1079,29 +1079,6 @@ out_noput: return err; } -static int verify_userspi_info(struct xfrm_userspi_info *p) -{ - switch (p->info.id.proto) { - case IPPROTO_AH: - case IPPROTO_ESP: - break; - - case IPPROTO_COMP: - /* IPCOMP spi is 16-bits. */ - if (p->max >= 0x10000) - return -EINVAL; - break; - - default: - return -EINVAL; - } - - if (p->min > p->max) - return -EINVAL; - - return 0; -} - static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, struct nlattr **attrs) { @@ -1116,7 +1093,7 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, struct xfrm_mark m; p = nlmsg_data(nlh); - err = verify_userspi_info(p); + err = verify_spi_info(p->info.id.proto, p->min, p->max); if (err) goto out_noput; -- cgit v1.2.3 From 204e35a91c4b3327b7239d7687fbd4923edbbf08 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Thu, 5 Dec 2013 20:42:58 +0100 Subject: nl80211: add VHT support for set_bitrate_mask Add VHT MCS/NSS set support for nl80211_set_tx_bitrate_mask(). This should be used mainly for test purpose, to check different MCS/NSS VHT combinations. Signed-off-by: Janusz Dziedzic Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 1 + include/uapi/linux/nl80211.h | 12 ++++++ net/wireless/nl80211.c | 92 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 99 insertions(+), 6 deletions(-) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 6c3a650f3a8f..80a10212b1b9 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1767,6 +1767,7 @@ struct cfg80211_bitrate_mask { struct { u32 legacy; u8 ht_mcs[IEEE80211_HT_MCS_MASK_LEN]; + u16 vht_mcs[NL80211_VHT_NSS_MAX]; } control[IEEE80211_NUM_BANDS]; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 6e700645cd27..e1307909ecf1 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3112,6 +3112,8 @@ enum nl80211_key_attributes { * %NL80211_MAX_SUPP_RATES in a single array). * @NL80211_TXRATE_HT: HT (MCS) rates allowed for TX rate selection * in an array of MCS numbers. + * @NL80211_TXRATE_VHT: VHT rates allowed for TX rate selection, + * see &struct nl80211_txrate_vht * @__NL80211_TXRATE_AFTER_LAST: internal * @NL80211_TXRATE_MAX: highest TX rate attribute */ @@ -3119,6 +3121,7 @@ enum nl80211_tx_rate_attributes { __NL80211_TXRATE_INVALID, NL80211_TXRATE_LEGACY, NL80211_TXRATE_HT, + NL80211_TXRATE_VHT, /* keep last */ __NL80211_TXRATE_AFTER_LAST, @@ -3126,6 +3129,15 @@ enum nl80211_tx_rate_attributes { }; #define NL80211_TXRATE_MCS NL80211_TXRATE_HT +#define NL80211_VHT_NSS_MAX 8 + +/** + * struct nl80211_txrate_vht - VHT MCS/NSS txrate bitmap + * @mcs: MCS bitmap table for each NSS (array index 0 for 1 stream, etc.) + */ +struct nl80211_txrate_vht { + __u16 mcs[NL80211_VHT_NSS_MAX]; +}; /** * enum nl80211_band - Frequency band diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 2d0c19c6133b..04681a46eda8 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -7328,11 +7328,72 @@ static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband, return true; } +static u16 vht_mcs_map_to_mcs_mask(u8 vht_mcs_map) +{ + u16 mcs_mask = 0; + + switch (vht_mcs_map) { + case IEEE80211_VHT_MCS_NOT_SUPPORTED: + break; + case IEEE80211_VHT_MCS_SUPPORT_0_7: + mcs_mask = 0x00FF; + break; + case IEEE80211_VHT_MCS_SUPPORT_0_8: + mcs_mask = 0x01FF; + break; + case IEEE80211_VHT_MCS_SUPPORT_0_9: + mcs_mask = 0x03FF; + break; + default: + break; + } + + return mcs_mask; +} + +static void vht_build_mcs_mask(u16 vht_mcs_map, + u16 vht_mcs_mask[NL80211_VHT_NSS_MAX]) +{ + u8 nss; + + for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) { + vht_mcs_mask[nss] = vht_mcs_map_to_mcs_mask(vht_mcs_map & 0x03); + vht_mcs_map >>= 2; + } +} + +static bool vht_set_mcs_mask(struct ieee80211_supported_band *sband, + struct nl80211_txrate_vht *txrate, + u16 mcs[NL80211_VHT_NSS_MAX]) +{ + u16 tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map); + u16 tx_mcs_mask[NL80211_VHT_NSS_MAX] = {}; + u8 i; + + if (!sband->vht_cap.vht_supported) + return false; + + memset(mcs, 0, sizeof(u16) * NL80211_VHT_NSS_MAX); + + /* Build vht_mcs_mask from VHT capabilities */ + vht_build_mcs_mask(tx_mcs_map, tx_mcs_mask); + + for (i = 0; i < NL80211_VHT_NSS_MAX; i++) { + if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i]) + mcs[i] = txrate->mcs[i]; + else + return false; + } + + return true; +} + static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = { [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY, .len = NL80211_MAX_SUPP_RATES }, [NL80211_TXRATE_HT] = { .type = NLA_BINARY, .len = NL80211_MAX_SUPP_HT_RATES }, + [NL80211_TXRATE_VHT] = { .len = sizeof(struct nl80211_txrate_vht)}, }; static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, @@ -7345,6 +7406,7 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, struct net_device *dev = info->user_ptr[1]; struct nlattr *tx_rates; struct ieee80211_supported_band *sband; + u16 vht_tx_mcs_map; if (!rdev->ops->set_bitrate_mask) return -EOPNOTSUPP; @@ -7361,6 +7423,12 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, memcpy(mask.control[i].ht_mcs, sband->ht_cap.mcs.rx_mask, sizeof(mask.control[i].ht_mcs)); + + if (!sband->vht_cap.vht_supported) + continue; + + vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map); + vht_build_mcs_mask(vht_tx_mcs_map, mask.control[i].vht_mcs); } /* if no rates are given set it back to the defaults */ @@ -7399,20 +7467,32 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, mask.control[band].ht_mcs)) return -EINVAL; } + if (tb[NL80211_TXRATE_VHT]) { + if (!vht_set_mcs_mask( + sband, + nla_data(tb[NL80211_TXRATE_VHT]), + mask.control[band].vht_mcs)) + return -EINVAL; + } if (mask.control[band].legacy == 0) { - /* don't allow empty legacy rates if HT - * is not even supported. */ - if (!rdev->wiphy.bands[band]->ht_cap.ht_supported) + /* don't allow empty legacy rates if HT or VHT + * are not even supported. + */ + if (!(rdev->wiphy.bands[band]->ht_cap.ht_supported || + rdev->wiphy.bands[band]->vht_cap.vht_supported)) return -EINVAL; for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) if (mask.control[band].ht_mcs[i]) - break; + goto out; + + for (i = 0; i < NL80211_VHT_NSS_MAX; i++) + if (mask.control[band].vht_mcs[i]) + goto out; /* legacy and mcs rates may not be both empty */ - if (i == IEEE80211_HT_MCS_MASK_LEN) - return -EINVAL; + return -EINVAL; } } -- cgit v1.2.3 From be78cfcb25fd163ad7c415cdc0ff343fbc8d22ec Mon Sep 17 00:00:00 2001 From: wangweidong Date: Fri, 13 Dec 2013 13:51:04 +0800 Subject: sctp: Reorder 'struc association' members to reduce its size Members of 'struct association' are not in appropriate order to reuse compiler added padding on 64bit architectures. In this patch we reorder those struct members and help reduce the size of the structure from 2776 bytes to 2720 bytes on 64 bit architectures. Signed-off-by: Wang Weidong Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 59 +++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 30 deletions(-) (limited to 'include/net') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index e416d6ac9c70..e8b2ff5c2804 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1360,12 +1360,6 @@ struct sctp_association { /* This is all information about our peer. */ struct { - /* rwnd - * - * Peer Rwnd : Current calculated value of the peer's rwnd. - */ - __u32 rwnd; - /* transport_addr_list * * Peer : A list of SCTP transport addresses that the @@ -1383,6 +1377,12 @@ struct sctp_association { */ struct list_head transport_addr_list; + /* rwnd + * + * Peer Rwnd : Current calculated value of the peer's rwnd. + */ + __u32 rwnd; + /* transport_count * * Peer : A count of the number of peer addresses @@ -1465,6 +1465,20 @@ struct sctp_association { */ struct sctp_tsnmap tsn_map; + /* This mask is used to disable sending the ASCONF chunk + * with specified parameter to peer. + */ + __be16 addip_disabled_mask; + + /* These are capabilities which our peer advertised. */ + __u8 ecn_capable:1, /* Can peer do ECN? */ + ipv4_address:1, /* Peer understands IPv4 addresses? */ + ipv6_address:1, /* Peer understands IPv6 addresses? */ + hostname_address:1, /* Peer understands DNS addresses? */ + asconf_capable:1, /* Does peer support ADDIP? */ + prsctp_capable:1, /* Can peer do PR-SCTP? */ + auth_capable:1; /* Is peer doing SCTP-AUTH? */ + /* Ack State : This flag indicates if the next received * : packet is to be responded to with a * : SACK. This is initializedto 0. When a packet @@ -1479,25 +1493,11 @@ struct sctp_association { __u32 sack_cnt; __u32 sack_generation; - /* These are capabilities which our peer advertised. */ - __u8 ecn_capable:1, /* Can peer do ECN? */ - ipv4_address:1, /* Peer understands IPv4 addresses? */ - ipv6_address:1, /* Peer understands IPv6 addresses? */ - hostname_address:1, /* Peer understands DNS addresses? */ - asconf_capable:1, /* Does peer support ADDIP? */ - prsctp_capable:1, /* Can peer do PR-SCTP? */ - auth_capable:1; /* Is peer doing SCTP-AUTH? */ - __u32 adaptation_ind; /* Adaptation Code point. */ - /* This mask is used to disable sending the ASCONF chunk - * with specified parameter to peer. - */ - __be16 addip_disabled_mask; - struct sctp_inithdr_host i; - int cookie_len; void *cookie; + int cookie_len; /* ADDIP Section 4.2 Upon reception of an ASCONF Chunk. * C1) ... "Peer-Serial-Number'. This value MUST be initialized to the @@ -1529,14 +1529,14 @@ struct sctp_association { */ sctp_state_t state; - /* The cookie life I award for any cookie. */ - ktime_t cookie_life; - /* Overall : The overall association error count. * Error Count : [Clear this any time I get something.] */ int overall_error_count; + /* The cookie life I award for any cookie. */ + ktime_t cookie_life; + /* These are the association's initial, max, and min RTO values. * These values will be initialized by system defaults, but can * be modified via the SCTP_RTOINFO socket option. @@ -1591,10 +1591,9 @@ struct sctp_association { /* Flags controlling Heartbeat, SACK delay, and Path MTU Discovery. */ __u32 param_flags; + __u32 sackfreq; /* SACK delay timeout */ unsigned long sackdelay; - __u32 sackfreq; - unsigned long timeouts[SCTP_NUM_TIMEOUT_TYPES]; struct timer_list timers[SCTP_NUM_TIMEOUT_TYPES]; @@ -1602,12 +1601,12 @@ struct sctp_association { /* Transport to which SHUTDOWN chunk was last sent. */ struct sctp_transport *shutdown_last_sent_to; - /* How many times have we resent a SHUTDOWN */ - int shutdown_retries; - /* Transport to which INIT chunk was last sent. */ struct sctp_transport *init_last_sent_to; + /* How many times have we resent a SHUTDOWN */ + int shutdown_retries; + /* Next TSN : The next TSN number to be assigned to a new * : DATA chunk. This is sent in the INIT or INIT * : ACK chunk to the peer and incremented each @@ -1818,8 +1817,8 @@ struct sctp_association { * after reaching 4294967295. */ __u32 addip_serial; - union sctp_addr *asconf_addr_del_pending; int src_out_of_asoc_ok; + union sctp_addr *asconf_addr_del_pending; struct sctp_transport *new_transport; /* SCTP AUTH: list of the endpoint shared keys. These -- cgit v1.2.3 From d4589926d7a9a4b88e650bb9154bab71e8d2a7dd Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 13 Dec 2013 13:51:23 -0800 Subject: tcp: refine TSO splits While investigating performance problems on small RPC workloads, I noticed linux TCP stack was always splitting the last TSO skb into two parts (skbs). One being a multiple of MSS, and a small one with the Push flag. This split is done even if TCP_NODELAY is set, or if no small packet is in flight. Example with request/response of 4K/4K IP A > B: . ack 68432 win 2783 IP A > B: . 65537:68433(2896) ack 69632 win 2783 IP A > B: P 68433:69633(1200) ack 69632 win 2783 IP B > A: . ack 68433 win 2768 IP B > A: . 69632:72528(2896) ack 69633 win 2768 IP B > A: P 72528:73728(1200) ack 69633 win 2768 IP A > B: . ack 72528 win 2783 IP A > B: . 69633:72529(2896) ack 73728 win 2783 IP A > B: P 72529:73729(1200) ack 73728 win 2783 We can avoid this split by including the Nagle tests at the right place. Note : If some NIC had trouble sending TSO packets with a partial last segment, we would have hit the problem in GRO/forwarding workload already. tcp_minshall_update() is moved to tcp_output.c and is updated as we might feed a TSO packet with a partial last segment. This patch tremendously improves performance, as the traffic now looks like : IP A > B: . ack 98304 win 2783 IP A > B: P 94209:98305(4096) ack 98304 win 2783 IP B > A: . ack 98305 win 2768 IP B > A: P 98304:102400(4096) ack 98305 win 2768 IP A > B: . ack 102400 win 2783 IP A > B: P 98305:102401(4096) ack 102400 win 2783 IP B > A: . ack 102401 win 2768 IP B > A: P 102400:106496(4096) ack 102401 win 2768 IP A > B: . ack 106496 win 2783 IP A > B: P 102401:106497(4096) ack 106496 win 2783 IP B > A: . ack 106497 win 2768 IP B > A: P 106496:110592(4096) ack 106497 win 2768 Before : lpq83:~# nstat >/dev/null;perf stat ./super_netperf 200 -t TCP_RR -H lpq84 -l 20 -- -r 4K,4K 280774 Performance counter stats for './super_netperf 200 -t TCP_RR -H lpq84 -l 20 -- -r 4K,4K': 205719.049006 task-clock # 9.278 CPUs utilized 8,449,968 context-switches # 0.041 M/sec 1,935,997 CPU-migrations # 0.009 M/sec 160,541 page-faults # 0.780 K/sec 548,478,722,290 cycles # 2.666 GHz [83.20%] 455,240,670,857 stalled-cycles-frontend # 83.00% frontend cycles idle [83.48%] 272,881,454,275 stalled-cycles-backend # 49.75% backend cycles idle [66.73%] 166,091,460,030 instructions # 0.30 insns per cycle # 2.74 stalled cycles per insn [83.39%] 29,150,229,399 branches # 141.699 M/sec [83.30%] 1,943,814,026 branch-misses # 6.67% of all branches [83.32%] 22.173517844 seconds time elapsed lpq83:~# nstat | egrep "IpOutRequests|IpExtOutOctets" IpOutRequests 16851063 0.0 IpExtOutOctets 23878580777 0.0 After patch : lpq83:~# nstat >/dev/null;perf stat ./super_netperf 200 -t TCP_RR -H lpq84 -l 20 -- -r 4K,4K 280877 Performance counter stats for './super_netperf 200 -t TCP_RR -H lpq84 -l 20 -- -r 4K,4K': 107496.071918 task-clock # 4.847 CPUs utilized 5,635,458 context-switches # 0.052 M/sec 1,374,707 CPU-migrations # 0.013 M/sec 160,920 page-faults # 0.001 M/sec 281,500,010,924 cycles # 2.619 GHz [83.28%] 228,865,069,307 stalled-cycles-frontend # 81.30% frontend cycles idle [83.38%] 142,462,742,658 stalled-cycles-backend # 50.61% backend cycles idle [66.81%] 95,227,712,566 instructions # 0.34 insns per cycle # 2.40 stalled cycles per insn [83.43%] 16,209,868,171 branches # 150.795 M/sec [83.20%] 874,252,952 branch-misses # 5.39% of all branches [83.37%] 22.175821286 seconds time elapsed lpq83:~# nstat | egrep "IpOutRequests|IpExtOutOctets" IpOutRequests 11239428 0.0 IpExtOutOctets 23595191035 0.0 Indeed, the occupancy of tx skbs (IpExtOutOctets/IpOutRequests) is higher : 2099 instead of 1417, thus helping GRO to be more efficient when using FQ packet scheduler. Many thanks to Neal for review and ideas. Signed-off-by: Eric Dumazet Cc: Yuchung Cheng Cc: Neal Cardwell Cc: Nandita Dukkipati Cc: Van Jacobson Acked-by: Neal Cardwell Tested-by: Neal Cardwell Signed-off-by: David S. Miller --- include/net/tcp.h | 7 ---- net/ipv4/tcp_output.c | 93 ++++++++++++++++++++++++++++++--------------------- 2 files changed, 54 insertions(+), 46 deletions(-) (limited to 'include/net') diff --git a/include/net/tcp.h b/include/net/tcp.h index f7e1ab2139ef..9cd62bc09055 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -978,13 +978,6 @@ static inline u32 tcp_wnd_end(const struct tcp_sock *tp) } bool tcp_is_cwnd_limited(const struct sock *sk, u32 in_flight); -static inline void tcp_minshall_update(struct tcp_sock *tp, unsigned int mss, - const struct sk_buff *skb) -{ - if (skb->len < mss) - tp->snd_sml = TCP_SKB_CB(skb)->end_seq; -} - static inline void tcp_check_probe_timer(struct sock *sk) { const struct tcp_sock *tp = tcp_sk(sk); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 2a69f42e51ca..9e7aec7ee67e 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1384,23 +1384,51 @@ static void tcp_cwnd_validate(struct sock *sk) } } -/* Returns the portion of skb which can be sent right away without - * introducing MSS oddities to segment boundaries. In rare cases where - * mss_now != mss_cache, we will request caller to create a small skb - * per input skb which could be mostly avoided here (if desired). - * - * We explicitly want to create a request for splitting write queue tail - * to a small skb for Nagle purposes while avoiding unnecessary modulos, - * thus all the complexity (cwnd_len is always MSS multiple which we - * return whenever allowed by the other factors). Basically we need the - * modulo only when the receiver window alone is the limiting factor or - * when we would be allowed to send the split-due-to-Nagle skb fully. +/* Minshall's variant of the Nagle send check. */ +static bool tcp_minshall_check(const struct tcp_sock *tp) +{ + return after(tp->snd_sml, tp->snd_una) && + !after(tp->snd_sml, tp->snd_nxt); +} + +/* Update snd_sml if this skb is under mss + * Note that a TSO packet might end with a sub-mss segment + * The test is really : + * if ((skb->len % mss) != 0) + * tp->snd_sml = TCP_SKB_CB(skb)->end_seq; + * But we can avoid doing the divide again given we already have + * skb_pcount = skb->len / mss_now */ -static unsigned int tcp_mss_split_point(const struct sock *sk, const struct sk_buff *skb, - unsigned int mss_now, unsigned int max_segs) +static void tcp_minshall_update(struct tcp_sock *tp, unsigned int mss_now, + const struct sk_buff *skb) +{ + if (skb->len < tcp_skb_pcount(skb) * mss_now) + tp->snd_sml = TCP_SKB_CB(skb)->end_seq; +} + +/* Return false, if packet can be sent now without violation Nagle's rules: + * 1. It is full sized. (provided by caller in %partial bool) + * 2. Or it contains FIN. (already checked by caller) + * 3. Or TCP_CORK is not set, and TCP_NODELAY is set. + * 4. Or TCP_CORK is not set, and all sent packets are ACKed. + * With Minshall's modification: all sent small packets are ACKed. + */ +static bool tcp_nagle_check(bool partial, const struct tcp_sock *tp, + unsigned int mss_now, int nonagle) +{ + return partial && + ((nonagle & TCP_NAGLE_CORK) || + (!nonagle && tp->packets_out && tcp_minshall_check(tp))); +} +/* Returns the portion of skb which can be sent right away */ +static unsigned int tcp_mss_split_point(const struct sock *sk, + const struct sk_buff *skb, + unsigned int mss_now, + unsigned int max_segs, + int nonagle) { const struct tcp_sock *tp = tcp_sk(sk); - u32 needed, window, max_len; + u32 partial, needed, window, max_len; window = tcp_wnd_end(tp) - TCP_SKB_CB(skb)->seq; max_len = mss_now * max_segs; @@ -1413,7 +1441,15 @@ static unsigned int tcp_mss_split_point(const struct sock *sk, const struct sk_b if (max_len <= needed) return max_len; - return needed - needed % mss_now; + partial = needed % mss_now; + /* If last segment is not a full MSS, check if Nagle rules allow us + * to include this last segment in this skb. + * Otherwise, we'll split the skb at last MSS boundary + */ + if (tcp_nagle_check(partial != 0, tp, mss_now, nonagle)) + return needed - partial; + + return needed; } /* Can at least one segment of SKB be sent right now, according to the @@ -1453,28 +1489,6 @@ static int tcp_init_tso_segs(const struct sock *sk, struct sk_buff *skb, return tso_segs; } -/* Minshall's variant of the Nagle send check. */ -static inline bool tcp_minshall_check(const struct tcp_sock *tp) -{ - return after(tp->snd_sml, tp->snd_una) && - !after(tp->snd_sml, tp->snd_nxt); -} - -/* Return false, if packet can be sent now without violation Nagle's rules: - * 1. It is full sized. - * 2. Or it contains FIN. (already checked by caller) - * 3. Or TCP_CORK is not set, and TCP_NODELAY is set. - * 4. Or TCP_CORK is not set, and all sent packets are ACKed. - * With Minshall's modification: all sent small packets are ACKed. - */ -static inline bool tcp_nagle_check(const struct tcp_sock *tp, - const struct sk_buff *skb, - unsigned int mss_now, int nonagle) -{ - return skb->len < mss_now && - ((nonagle & TCP_NAGLE_CORK) || - (!nonagle && tp->packets_out && tcp_minshall_check(tp))); -} /* Return true if the Nagle test allows this packet to be * sent now. @@ -1495,7 +1509,7 @@ static inline bool tcp_nagle_test(const struct tcp_sock *tp, const struct sk_buf if (tcp_urg_mode(tp) || (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)) return true; - if (!tcp_nagle_check(tp, skb, cur_mss, nonagle)) + if (!tcp_nagle_check(skb->len < cur_mss, tp, cur_mss, nonagle)) return true; return false; @@ -1898,7 +1912,8 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, limit = tcp_mss_split_point(sk, skb, mss_now, min_t(unsigned int, cwnd_quota, - sk->sk_gso_max_segs)); + sk->sk_gso_max_segs), + nonagle); if (skb->len > limit && unlikely(tso_fragment(sk, skb, limit, mss_now, gfp))) -- cgit v1.2.3 From 7539fadcb8146a5f0db51e80d99c9e724efec7b0 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Sun, 15 Dec 2013 22:12:18 -0800 Subject: net: Add utility functions to clear rxhash In several places 'skb->rxhash = 0' is being done to clear the rxhash value in an skb. This does not clear l4_rxhash which could still be set so that the rxhash wouldn't be recalculated on subsequent call to skb_get_rxhash. This patch adds an explict function to clear all the rxhash related information in the skb properly. skb_clear_hash_if_not_l4 clears the rxhash only if it is not marked as l4_rxhash. Fixed up places where 'skb->rxhash = 0' was being called. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/linux/skbuff.h | 12 ++++++++++++ include/net/dst.h | 5 ++--- net/ipv4/ip_fragment.c | 2 +- net/ipv4/ip_tunnel_core.c | 5 ++--- net/openvswitch/actions.c | 10 +++++----- 5 files changed, 22 insertions(+), 12 deletions(-) (limited to 'include/net') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 4725b953e00d..7deb7ad65914 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -712,6 +712,18 @@ static inline __u32 skb_get_hash(struct sk_buff *skb) return skb->rxhash; } +static inline void skb_clear_hash(struct sk_buff *skb) +{ + skb->rxhash = 0; + skb->l4_rxhash = 0; +} + +static inline void skb_clear_hash_if_not_l4(struct sk_buff *skb) +{ + if (!skb->l4_rxhash) + skb_clear_hash(skb); +} + #ifdef NET_SKBUFF_DATA_USES_OFFSET static inline unsigned char *skb_end_pointer(const struct sk_buff *skb) { diff --git a/include/net/dst.h b/include/net/dst.h index 44995c13e941..77eb53fabfb0 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -322,12 +322,11 @@ static inline void __skb_tunnel_rx(struct sk_buff *skb, struct net_device *dev, skb->dev = dev; /* - * Clear rxhash so that we can recalulate the hash for the + * Clear hash so that we can recalulate the hash for the * encapsulated packet, unless we have already determine the hash * over the L4 4-tuple. */ - if (!skb->l4_rxhash) - skb->rxhash = 0; + skb_clear_hash_if_not_l4(skb); skb_set_queue_mapping(skb, 0); skb_scrub_packet(skb, !net_eq(net, dev_net(dev))); } diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 2481993a4970..c10a3ce5cbff 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -704,7 +704,7 @@ struct sk_buff *ip_check_defrag(struct sk_buff *skb, u32 user) memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); if (ip_defrag(skb, user)) return NULL; - skb->rxhash = 0; + skb_clear_hash(skb); } } return skb; diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c index 42ffbc8d65c6..6156f4ef5e91 100644 --- a/net/ipv4/ip_tunnel_core.c +++ b/net/ipv4/ip_tunnel_core.c @@ -56,7 +56,7 @@ int iptunnel_xmit(struct rtable *rt, struct sk_buff *skb, skb_scrub_packet(skb, xnet); - skb->rxhash = 0; + skb_clear_hash(skb); skb_dst_set(skb, &rt->dst); memset(IPCB(skb), 0, sizeof(*IPCB(skb))); @@ -107,8 +107,7 @@ int iptunnel_pull_header(struct sk_buff *skb, int hdr_len, __be16 inner_proto) nf_reset(skb); secpath_reset(skb); - if (!skb->l4_rxhash) - skb->rxhash = 0; + skb_clear_hash_if_not_l4(skb); skb_dst_drop(skb); skb->vlan_tci = 0; skb_set_queue_mapping(skb, 0); diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 65cfaa816075..716b7eebfe70 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -165,7 +165,7 @@ static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh, } csum_replace4(&nh->check, *addr, new_addr); - skb->rxhash = 0; + skb_clear_hash(skb); *addr = new_addr; } @@ -199,7 +199,7 @@ static void set_ipv6_addr(struct sk_buff *skb, u8 l4_proto, if (recalculate_csum) update_ipv6_checksum(skb, l4_proto, addr, new_addr); - skb->rxhash = 0; + skb_clear_hash(skb); memcpy(addr, new_addr, sizeof(__be32[4])); } @@ -296,7 +296,7 @@ static void set_tp_port(struct sk_buff *skb, __be16 *port, { inet_proto_csum_replace2(check, skb, *port, new_port, 0); *port = new_port; - skb->rxhash = 0; + skb_clear_hash(skb); } static void set_udp_port(struct sk_buff *skb, __be16 *port, __be16 new_port) @@ -310,7 +310,7 @@ static void set_udp_port(struct sk_buff *skb, __be16 *port, __be16 new_port) uh->check = CSUM_MANGLED_0; } else { *port = new_port; - skb->rxhash = 0; + skb_clear_hash(skb); } } @@ -381,7 +381,7 @@ static int set_sctp(struct sk_buff *skb, /* Carry any checksum errors through. */ sh->checksum = old_csum ^ old_correct_csum ^ new_csum; - skb->rxhash = 0; + skb_clear_hash(skb); } return 0; -- cgit v1.2.3 From d84231d3a2b20bea26327d9b83c8bd8ba55dc68c Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Sun, 15 Dec 2013 20:15:04 -0800 Subject: net_sched: remove get_stats from tc_action_ops It is not used. Cc: Jamal Hadi Salim Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/net/act_api.h | 1 - net/sched/act_api.c | 4 ---- 2 files changed, 5 deletions(-) (limited to 'include/net') diff --git a/include/net/act_api.h b/include/net/act_api.h index 9e90fdff470d..04c6825e172b 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -72,7 +72,6 @@ struct tc_action_ops { __u32 capab; /* capabilities includes 4 bit version */ struct module *owner; int (*act)(struct sk_buff *, const struct tc_action *, struct tcf_result *); - int (*get_stats)(struct sk_buff *, struct tc_action *); int (*dump)(struct sk_buff *, struct tc_action *, int, int); int (*cleanup)(struct tc_action *, int bind); int (*lookup)(struct tc_action *, u32); diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 4adbce8f8314..51e28f7a4638 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -637,10 +637,6 @@ int tcf_action_copy_stats(struct sk_buff *skb, struct tc_action *a, if (err < 0) goto errout; - if (a->ops != NULL && a->ops->get_stats != NULL) - if (a->ops->get_stats(skb, a) < 0) - goto errout; - if (gnet_stats_copy_basic(&d, &h->tcf_bstats) < 0 || gnet_stats_copy_rate_est(&d, &h->tcf_bstats, &h->tcf_rate_est) < 0 || -- cgit v1.2.3 From 33be627159913b094bb578e83e9a7fdc66c10208 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Sun, 15 Dec 2013 20:15:05 -0800 Subject: net_sched: act: use standard struct list_head Currently actions are chained by a singly linked list, therefore it is a bit hard to add and remove a specific entry. Convert it to struct list_head so that in the latter patch we can remove an action without finding its head. Cc: Jamal Hadi Salim Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/net/act_api.h | 12 +++--- include/net/pkt_cls.h | 16 ++++++-- net/sched/act_api.c | 105 +++++++++++++++++++++--------------------------- net/sched/cls_api.c | 54 ++++++++++++------------- net/sched/cls_basic.c | 1 + net/sched/cls_bpf.c | 1 + net/sched/cls_cgroup.c | 1 + net/sched/cls_flow.c | 1 + net/sched/cls_fw.c | 1 + net/sched/cls_route.c | 1 + net/sched/cls_rsvp.h | 1 + net/sched/cls_tcindex.c | 5 ++- net/sched/cls_u32.c | 1 + 13 files changed, 101 insertions(+), 99 deletions(-) (limited to 'include/net') diff --git a/include/net/act_api.h b/include/net/act_api.h index 04c6825e172b..a72642610790 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -60,7 +60,7 @@ struct tc_action { const struct tc_action_ops *ops; __u32 type; /* for backward compat(TCA_OLD_COMPAT) */ __u32 order; - struct tc_action *next; + struct list_head list; }; #define TCA_CAP_NONE 0 @@ -99,16 +99,16 @@ void tcf_hash_insert(struct tcf_common *p, struct tcf_hashinfo *hinfo); int tcf_register_action(struct tc_action_ops *a); int tcf_unregister_action(struct tc_action_ops *a); -void tcf_action_destroy(struct tc_action *a, int bind); -int tcf_action_exec(struct sk_buff *skb, const struct tc_action *a, +void tcf_action_destroy(struct list_head *actions, int bind); +int tcf_action_exec(struct sk_buff *skb, const struct list_head *actions, struct tcf_result *res); -struct tc_action *tcf_action_init(struct net *net, struct nlattr *nla, +int tcf_action_init(struct net *net, struct nlattr *nla, struct nlattr *est, char *n, int ovr, - int bind); + int bind, struct list_head *); struct tc_action *tcf_action_init_1(struct net *net, struct nlattr *nla, struct nlattr *est, char *n, int ovr, int bind); -int tcf_action_dump(struct sk_buff *skb, struct tc_action *a, int, int); +int tcf_action_dump(struct sk_buff *skb, struct list_head *, int, int); int tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int, int); int tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int, int); int tcf_action_copy_stats(struct sk_buff *, struct tc_action *, int); diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 2ebef77a2f9a..34fe693ddf9a 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -62,7 +62,8 @@ tcf_unbind_filter(struct tcf_proto *tp, struct tcf_result *r) struct tcf_exts { #ifdef CONFIG_NET_CLS_ACT - struct tc_action *action; + __u32 type; /* for backward compat(TCA_OLD_COMPAT) */ + struct list_head actions; #endif }; @@ -74,6 +75,13 @@ struct tcf_ext_map { int police; }; +static inline void tcf_exts_init(struct tcf_exts *exts) +{ +#ifdef CONFIG_NET_CLS_ACT + INIT_LIST_HEAD(&exts->actions); +#endif +} + /** * tcf_exts_is_predicative - check if a predicative extension is present * @exts: tc filter extensions handle @@ -85,7 +93,7 @@ static inline int tcf_exts_is_predicative(struct tcf_exts *exts) { #ifdef CONFIG_NET_CLS_ACT - return !!exts->action; + return !list_empty(&exts->actions); #else return 0; #endif @@ -120,8 +128,8 @@ tcf_exts_exec(struct sk_buff *skb, struct tcf_exts *exts, struct tcf_result *res) { #ifdef CONFIG_NET_CLS_ACT - if (exts->action) - return tcf_action_exec(skb, exts->action, res); + if (!list_empty(&exts->actions)) + return tcf_action_exec(skb, &exts->actions, res); #endif return 0; } diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 51e28f7a4638..7d84183b633e 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -379,7 +379,7 @@ static struct tc_action_ops *tc_lookup_action_id(u32 type) } #endif -int tcf_action_exec(struct sk_buff *skb, const struct tc_action *act, +int tcf_action_exec(struct sk_buff *skb, const struct list_head *actions, struct tcf_result *res) { const struct tc_action *a; @@ -390,7 +390,7 @@ int tcf_action_exec(struct sk_buff *skb, const struct tc_action *act, ret = TC_ACT_OK; goto exec_done; } - while ((a = act) != NULL) { + list_for_each_entry(a, actions, list) { repeat: if (a->ops) { ret = a->ops->act(skb, a, res); @@ -404,27 +404,26 @@ repeat: if (ret != TC_ACT_PIPE) goto exec_done; } - act = a->next; } exec_done: return ret; } EXPORT_SYMBOL(tcf_action_exec); -void tcf_action_destroy(struct tc_action *act, int bind) +void tcf_action_destroy(struct list_head *actions, int bind) { - struct tc_action *a; + struct tc_action *a, *tmp; - for (a = act; a; a = act) { + list_for_each_entry_safe(a, tmp, actions, list) { if (a->ops) { if (a->ops->cleanup(a, bind) == ACT_P_DELETED) module_put(a->ops->owner); - act = act->next; + list_del(&a->list); kfree(a); } else { /*FIXME: Remove later - catch insertion bugs*/ WARN(1, "tcf_action_destroy: BUG? destroying NULL ops\n"); - act = act->next; + list_del(&a->list); kfree(a); } } @@ -470,14 +469,13 @@ nla_put_failure: EXPORT_SYMBOL(tcf_action_dump_1); int -tcf_action_dump(struct sk_buff *skb, struct tc_action *act, int bind, int ref) +tcf_action_dump(struct sk_buff *skb, struct list_head *actions, int bind, int ref) { struct tc_action *a; int err = -EINVAL; struct nlattr *nest; - while ((a = act) != NULL) { - act = a->next; + list_for_each_entry(a, actions, list) { nest = nla_nest_start(skb, a->order); if (nest == NULL) goto nla_put_failure; @@ -552,6 +550,7 @@ struct tc_action *tcf_action_init_1(struct net *net, struct nlattr *nla, if (a == NULL) goto err_mod; + INIT_LIST_HEAD(&a->list); /* backward compatibility for policer */ if (name == NULL) err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, a, ovr, bind); @@ -578,37 +577,33 @@ err_out: return ERR_PTR(err); } -struct tc_action *tcf_action_init(struct net *net, struct nlattr *nla, +int tcf_action_init(struct net *net, struct nlattr *nla, struct nlattr *est, char *name, int ovr, - int bind) + int bind, struct list_head *actions) { struct nlattr *tb[TCA_ACT_MAX_PRIO + 1]; - struct tc_action *head = NULL, *act, *act_prev = NULL; + struct tc_action *act; int err; int i; err = nla_parse_nested(tb, TCA_ACT_MAX_PRIO, nla, NULL); if (err < 0) - return ERR_PTR(err); + return err; for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) { act = tcf_action_init_1(net, tb[i], est, name, ovr, bind); - if (IS_ERR(act)) + if (IS_ERR(act)) { + err = PTR_ERR(act); goto err; + } act->order = i; - - if (head == NULL) - head = act; - else - act_prev->next = act; - act_prev = act; + list_add_tail(&act->list, actions); } - return head; + return 0; err: - if (head != NULL) - tcf_action_destroy(head, bind); - return act; + tcf_action_destroy(actions, bind); + return err; } int tcf_action_copy_stats(struct sk_buff *skb, struct tc_action *a, @@ -653,7 +648,7 @@ errout: } static int -tca_get_fill(struct sk_buff *skb, struct tc_action *a, u32 portid, u32 seq, +tca_get_fill(struct sk_buff *skb, struct list_head *actions, u32 portid, u32 seq, u16 flags, int event, int bind, int ref) { struct tcamsg *t; @@ -673,7 +668,7 @@ tca_get_fill(struct sk_buff *skb, struct tc_action *a, u32 portid, u32 seq, if (nest == NULL) goto out_nlmsg_trim; - if (tcf_action_dump(skb, a, bind, ref) < 0) + if (tcf_action_dump(skb, actions, bind, ref) < 0) goto out_nlmsg_trim; nla_nest_end(skb, nest); @@ -688,14 +683,14 @@ out_nlmsg_trim: static int act_get_notify(struct net *net, u32 portid, struct nlmsghdr *n, - struct tc_action *a, int event) + struct list_head *actions, int event) { struct sk_buff *skb; skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); if (!skb) return -ENOBUFS; - if (tca_get_fill(skb, a, portid, n->nlmsg_seq, 0, event, 0, 0) <= 0) { + if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, event, 0, 0) <= 0) { kfree_skb(skb); return -EINVAL; } @@ -726,6 +721,7 @@ tcf_action_get_1(struct nlattr *nla, struct nlmsghdr *n, u32 portid) if (a == NULL) goto err_out; + INIT_LIST_HEAD(&a->list); err = -EINVAL; a->ops = tc_lookup_action(tb[TCA_ACT_KIND]); if (a->ops == NULL) @@ -745,12 +741,12 @@ err_out: return ERR_PTR(err); } -static void cleanup_a(struct tc_action *act) +static void cleanup_a(struct list_head *actions) { - struct tc_action *a; + struct tc_action *a, *tmp; - for (a = act; a; a = act) { - act = a->next; + list_for_each_entry_safe(a, tmp, actions, list) { + list_del(&a->list); kfree(a); } } @@ -765,6 +761,7 @@ static struct tc_action *create_a(int i) return NULL; } act->order = i; + INIT_LIST_HEAD(&act->list); return act; } @@ -852,7 +849,8 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n, { int i, ret; struct nlattr *tb[TCA_ACT_MAX_PRIO + 1]; - struct tc_action *head = NULL, *act, *act_prev = NULL; + struct tc_action *act; + LIST_HEAD(actions); ret = nla_parse_nested(tb, TCA_ACT_MAX_PRIO, nla, NULL); if (ret < 0) @@ -872,16 +870,11 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n, goto err; } act->order = i; - - if (head == NULL) - head = act; - else - act_prev->next = act; - act_prev = act; + list_add_tail(&act->list, &actions); } if (event == RTM_GETACTION) - ret = act_get_notify(net, portid, n, head, event); + ret = act_get_notify(net, portid, n, &actions, event); else { /* delete */ struct sk_buff *skb; @@ -891,7 +884,7 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n, goto err; } - if (tca_get_fill(skb, head, portid, n->nlmsg_seq, 0, event, + if (tca_get_fill(skb, &actions, portid, n->nlmsg_seq, 0, event, 0, 1) <= 0) { kfree_skb(skb); ret = -EINVAL; @@ -899,7 +892,7 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n, } /* now do the delete */ - tcf_action_destroy(head, 0); + tcf_action_destroy(&actions, 0); ret = rtnetlink_send(skb, net, portid, RTNLGRP_TC, n->nlmsg_flags & NLM_F_ECHO); if (ret > 0) @@ -907,11 +900,11 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n, return ret; } err: - cleanup_a(head); + cleanup_a(&actions); return ret; } -static int tcf_add_notify(struct net *net, struct tc_action *a, +static int tcf_add_notify(struct net *net, struct list_head *actions, u32 portid, u32 seq, int event, u16 flags) { struct tcamsg *t; @@ -939,7 +932,7 @@ static int tcf_add_notify(struct net *net, struct tc_action *a, if (nest == NULL) goto out_kfree_skb; - if (tcf_action_dump(skb, a, 0, 0) < 0) + if (tcf_action_dump(skb, actions, 0, 0) < 0) goto out_kfree_skb; nla_nest_end(skb, nest); @@ -963,26 +956,18 @@ tcf_action_add(struct net *net, struct nlattr *nla, struct nlmsghdr *n, u32 portid, int ovr) { int ret = 0; - struct tc_action *act; - struct tc_action *a; + LIST_HEAD(actions); u32 seq = n->nlmsg_seq; - act = tcf_action_init(net, nla, NULL, NULL, ovr, 0); - if (act == NULL) - goto done; - if (IS_ERR(act)) { - ret = PTR_ERR(act); + ret = tcf_action_init(net, nla, NULL, NULL, ovr, 0, &actions); + if (ret) goto done; - } /* dump then free all the actions after update; inserted policy * stays intact */ - ret = tcf_add_notify(net, act, portid, seq, RTM_NEWACTION, n->nlmsg_flags); - for (a = act; a; a = act) { - act = a->next; - kfree(a); - } + ret = tcf_add_notify(net, &actions, portid, seq, RTM_NEWACTION, n->nlmsg_flags); + cleanup_a(&actions); done: return ret; } diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 8e118af90973..3c056d73d394 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -500,10 +500,8 @@ out: void tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts) { #ifdef CONFIG_NET_CLS_ACT - if (exts->action) { - tcf_action_destroy(exts->action, TCA_ACT_UNBIND); - exts->action = NULL; - } + tcf_action_destroy(&exts->actions, TCA_ACT_UNBIND); + INIT_LIST_HEAD(&exts->actions); #endif } EXPORT_SYMBOL(tcf_exts_destroy); @@ -518,6 +516,7 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb, { struct tc_action *act; + INIT_LIST_HEAD(&exts->actions); if (map->police && tb[map->police]) { act = tcf_action_init_1(net, tb[map->police], rate_tlv, "police", TCA_ACT_NOREPLACE, @@ -525,16 +524,15 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb, if (IS_ERR(act)) return PTR_ERR(act); - act->type = TCA_OLD_COMPAT; - exts->action = act; + act->type = exts->type = TCA_OLD_COMPAT; + list_add(&act->list, &exts->actions); } else if (map->action && tb[map->action]) { - act = tcf_action_init(net, tb[map->action], rate_tlv, + int err; + err = tcf_action_init(net, tb[map->action], rate_tlv, NULL, TCA_ACT_NOREPLACE, - TCA_ACT_BIND); - if (IS_ERR(act)) - return PTR_ERR(act); - - exts->action = act; + TCA_ACT_BIND, &exts->actions); + if (err) + return err; } } #else @@ -551,43 +549,45 @@ void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst, struct tcf_exts *src) { #ifdef CONFIG_NET_CLS_ACT - if (src->action) { - struct tc_action *act; + if (!list_empty(&src->actions)) { + LIST_HEAD(tmp); tcf_tree_lock(tp); - act = dst->action; - dst->action = src->action; + list_splice_init(&dst->actions, &tmp); + list_splice(&src->actions, &dst->actions); tcf_tree_unlock(tp); - if (act) - tcf_action_destroy(act, TCA_ACT_UNBIND); + tcf_action_destroy(&tmp, TCA_ACT_UNBIND); } #endif } EXPORT_SYMBOL(tcf_exts_change); +#define tcf_exts_first_act(ext) \ + list_first_entry(&(exts)->actions, struct tc_action, list) + int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts, const struct tcf_ext_map *map) { #ifdef CONFIG_NET_CLS_ACT - if (map->action && exts->action) { + if (map->action && !list_empty(&exts->actions)) { /* * again for backward compatible mode - we want * to work with both old and new modes of entering * tc data even if iproute2 was newer - jhs */ struct nlattr *nest; - - if (exts->action->type != TCA_OLD_COMPAT) { + if (exts->type != TCA_OLD_COMPAT) { nest = nla_nest_start(skb, map->action); if (nest == NULL) goto nla_put_failure; - if (tcf_action_dump(skb, exts->action, 0, 0) < 0) + if (tcf_action_dump(skb, &exts->actions, 0, 0) < 0) goto nla_put_failure; nla_nest_end(skb, nest); } else if (map->police) { + struct tc_action *act = tcf_exts_first_act(exts); nest = nla_nest_start(skb, map->police); if (nest == NULL) goto nla_put_failure; - if (tcf_action_dump_old(skb, exts->action, 0, 0) < 0) + if (tcf_action_dump_old(skb, act, 0, 0) < 0) goto nla_put_failure; nla_nest_end(skb, nest); } @@ -604,13 +604,11 @@ int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts, const struct tcf_ext_map *map) { #ifdef CONFIG_NET_CLS_ACT - if (exts->action) - if (tcf_action_copy_stats(skb, exts->action, 1) < 0) - goto nla_put_failure; + struct tc_action *a = tcf_exts_first_act(exts); + if (tcf_action_copy_stats(skb, a, 1) < 0) + return -1; #endif return 0; -nla_put_failure: __attribute__ ((unused)) - return -1; } EXPORT_SYMBOL(tcf_exts_dump_stats); diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c index 636d9131d870..7b9b4602b5b7 100644 --- a/net/sched/cls_basic.c +++ b/net/sched/cls_basic.c @@ -191,6 +191,7 @@ static int basic_change(struct net *net, struct sk_buff *in_skb, if (f == NULL) goto errout; + tcf_exts_init(&f->exts); err = -EINVAL; if (handle) f->handle = handle; diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index d7c72be121f3..90fe275e580c 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -271,6 +271,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb, if (prog == NULL) return -ENOBUFS; + tcf_exts_init(&prog->exts); if (handle == 0) prog->handle = cls_bpf_grab_new_handle(tp, head); else diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index 16006c92c3fd..e4fae032bcfb 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c @@ -203,6 +203,7 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb, if (head == NULL) return -ENOBUFS; + tcf_exts_init(&head->exts); head->handle = handle; tcf_tree_lock(tp); diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index 83a6322b7751..f05904e3848f 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -455,6 +455,7 @@ static int flow_change(struct net *net, struct sk_buff *in_skb, f->handle = handle; f->mask = ~0U; + tcf_exts_init(&f->exts); get_random_bytes(&f->hashrnd, 4); f->perturb_timer.function = flow_perturbation; diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index 9b97172db84a..d1cebad19410 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -280,6 +280,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb, if (f == NULL) return -ENOBUFS; + tcf_exts_init(&f->exts); f->id = handle; err = fw_change_attrs(net, tp, f, tb, tca, base); diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index 37da567d833e..f1f1dfdf4eb3 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -481,6 +481,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb, if (f == NULL) goto errout; + tcf_exts_init(&f->exts); err = route4_set_parms(net, tp, base, f, handle, head, tb, tca[TCA_RATE], 1); if (err < 0) diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h index 252d8b05872e..b1d3ce5c5ff8 100644 --- a/net/sched/cls_rsvp.h +++ b/net/sched/cls_rsvp.h @@ -471,6 +471,7 @@ static int rsvp_change(struct net *net, struct sk_buff *in_skb, if (f == NULL) goto errout2; + tcf_exts_init(&f->exts); h2 = 16; if (tb[TCA_RSVP_SRC]) { memcpy(f->src, nla_data(tb[TCA_RSVP_SRC]), sizeof(f->src)); diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c index b86535a40169..c39bbfc0300a 100644 --- a/net/sched/cls_tcindex.c +++ b/net/sched/cls_tcindex.c @@ -215,11 +215,14 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, memcpy(&cp, p, sizeof(cp)); memset(&new_filter_result, 0, sizeof(new_filter_result)); + tcf_exts_init(&new_filter_result.exts); if (old_r) memcpy(&cr, r, sizeof(cr)); - else + else { memset(&cr, 0, sizeof(cr)); + tcf_exts_init(&cr.exts); + } if (tb[TCA_TCINDEX_HASH]) cp.hash = nla_get_u32(tb[TCA_TCINDEX_HASH]); diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 59e546c2ac98..492d9a6401ce 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -646,6 +646,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, n->ht_up = ht; n->handle = handle; n->fshift = s->hmask ? ffs(ntohl(s->hmask)) - 1 : 0; + tcf_exts_init(&n->exts); #ifdef CONFIG_CLS_U32_MARK if (tb[TCA_U32_MARK]) { -- cgit v1.2.3 From 5da57f422d89c504a1d72dadd4e19d3dca8e974e Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Sun, 15 Dec 2013 20:15:07 -0800 Subject: net_sched: cls: refactor out struct tcf_ext_map These information can be saved in tcf_exts, and this will simplify the code. Cc: Jamal Hadi Salim Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/net/pkt_cls.h | 23 ++++++++++------------- net/sched/cls_api.c | 31 +++++++++++++------------------ net/sched/cls_basic.c | 14 +++++--------- net/sched/cls_bpf.c | 14 +++++--------- net/sched/cls_cgroup.c | 15 +++++---------- net/sched/cls_flow.c | 14 +++++--------- net/sched/cls_fw.c | 14 +++++--------- net/sched/cls_route.c | 14 +++++--------- net/sched/cls_rsvp.h | 14 +++++--------- net/sched/cls_tcindex.c | 16 ++++++---------- net/sched/cls_u32.c | 14 +++++--------- 11 files changed, 69 insertions(+), 114 deletions(-) (limited to 'include/net') diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 34fe693ddf9a..50ea07969c09 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -65,21 +65,21 @@ struct tcf_exts { __u32 type; /* for backward compat(TCA_OLD_COMPAT) */ struct list_head actions; #endif -}; - -/* Map to export classifier specific extension TLV types to the - * generic extensions API. Unsupported extensions must be set to 0. - */ -struct tcf_ext_map { + /* Map to export classifier specific extension TLV types to the + * generic extensions API. Unsupported extensions must be set to 0. + */ int action; int police; }; -static inline void tcf_exts_init(struct tcf_exts *exts) +static inline void tcf_exts_init(struct tcf_exts *exts, int action, int police) { #ifdef CONFIG_NET_CLS_ACT + exts->type = 0; INIT_LIST_HEAD(&exts->actions); #endif + exts->action = action; + exts->police = police; } /** @@ -136,15 +136,12 @@ tcf_exts_exec(struct sk_buff *skb, struct tcf_exts *exts, int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb, struct nlattr *rate_tlv, - struct tcf_exts *exts, - const struct tcf_ext_map *map); + struct tcf_exts *exts); void tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts); void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst, struct tcf_exts *src); -int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts, - const struct tcf_ext_map *map); -int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts, - const struct tcf_ext_map *map); +int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts); +int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts); /** * struct tcf_pkt_info - packet information diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 3c056d73d394..028c980ef87d 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -507,18 +507,15 @@ void tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts) EXPORT_SYMBOL(tcf_exts_destroy); int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb, - struct nlattr *rate_tlv, struct tcf_exts *exts, - const struct tcf_ext_map *map) + struct nlattr *rate_tlv, struct tcf_exts *exts) { - memset(exts, 0, sizeof(*exts)); - #ifdef CONFIG_NET_CLS_ACT { struct tc_action *act; INIT_LIST_HEAD(&exts->actions); - if (map->police && tb[map->police]) { - act = tcf_action_init_1(net, tb[map->police], rate_tlv, + if (exts->police && tb[exts->police]) { + act = tcf_action_init_1(net, tb[exts->police], rate_tlv, "police", TCA_ACT_NOREPLACE, TCA_ACT_BIND); if (IS_ERR(act)) @@ -526,9 +523,9 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb, act->type = exts->type = TCA_OLD_COMPAT; list_add(&act->list, &exts->actions); - } else if (map->action && tb[map->action]) { + } else if (exts->action && tb[exts->action]) { int err; - err = tcf_action_init(net, tb[map->action], rate_tlv, + err = tcf_action_init(net, tb[exts->action], rate_tlv, NULL, TCA_ACT_NOREPLACE, TCA_ACT_BIND, &exts->actions); if (err) @@ -536,8 +533,8 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb, } } #else - if ((map->action && tb[map->action]) || - (map->police && tb[map->police])) + if ((exts->action && tb[exts->action]) || + (exts->police && tb[exts->police])) return -EOPNOTSUPP; #endif @@ -564,11 +561,10 @@ EXPORT_SYMBOL(tcf_exts_change); #define tcf_exts_first_act(ext) \ list_first_entry(&(exts)->actions, struct tc_action, list) -int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts, - const struct tcf_ext_map *map) +int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts) { #ifdef CONFIG_NET_CLS_ACT - if (map->action && !list_empty(&exts->actions)) { + if (exts->action && !list_empty(&exts->actions)) { /* * again for backward compatible mode - we want * to work with both old and new modes of entering @@ -576,15 +572,15 @@ int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts, */ struct nlattr *nest; if (exts->type != TCA_OLD_COMPAT) { - nest = nla_nest_start(skb, map->action); + nest = nla_nest_start(skb, exts->action); if (nest == NULL) goto nla_put_failure; if (tcf_action_dump(skb, &exts->actions, 0, 0) < 0) goto nla_put_failure; nla_nest_end(skb, nest); - } else if (map->police) { + } else if (exts->police) { struct tc_action *act = tcf_exts_first_act(exts); - nest = nla_nest_start(skb, map->police); + nest = nla_nest_start(skb, exts->police); if (nest == NULL) goto nla_put_failure; if (tcf_action_dump_old(skb, act, 0, 0) < 0) @@ -600,8 +596,7 @@ nla_put_failure: __attribute__ ((unused)) EXPORT_SYMBOL(tcf_exts_dump); -int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts, - const struct tcf_ext_map *map) +int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts) { #ifdef CONFIG_NET_CLS_ACT struct tc_action *a = tcf_exts_first_act(exts); diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c index 7b9b4602b5b7..b6552035d1f4 100644 --- a/net/sched/cls_basic.c +++ b/net/sched/cls_basic.c @@ -34,11 +34,6 @@ struct basic_filter { struct list_head link; }; -static const struct tcf_ext_map basic_ext_map = { - .action = TCA_BASIC_ACT, - .police = TCA_BASIC_POLICE -}; - static int basic_classify(struct sk_buff *skb, const struct tcf_proto *tp, struct tcf_result *res) { @@ -141,7 +136,8 @@ static int basic_set_parms(struct net *net, struct tcf_proto *tp, struct tcf_exts e; struct tcf_ematch_tree t; - err = tcf_exts_validate(net, tp, tb, est, &e, &basic_ext_map); + tcf_exts_init(&e, TCA_BASIC_ACT, TCA_BASIC_POLICE); + err = tcf_exts_validate(net, tp, tb, est, &e); if (err < 0) return err; @@ -191,7 +187,7 @@ static int basic_change(struct net *net, struct sk_buff *in_skb, if (f == NULL) goto errout; - tcf_exts_init(&f->exts); + tcf_exts_init(&f->exts, TCA_BASIC_ACT, TCA_BASIC_POLICE); err = -EINVAL; if (handle) f->handle = handle; @@ -264,13 +260,13 @@ static int basic_dump(struct tcf_proto *tp, unsigned long fh, nla_put_u32(skb, TCA_BASIC_CLASSID, f->res.classid)) goto nla_put_failure; - if (tcf_exts_dump(skb, &f->exts, &basic_ext_map) < 0 || + if (tcf_exts_dump(skb, &f->exts) < 0 || tcf_em_tree_dump(skb, &f->ematches, TCA_BASIC_EMATCHES) < 0) goto nla_put_failure; nla_nest_end(skb, nest); - if (tcf_exts_dump_stats(skb, &f->exts, &basic_ext_map) < 0) + if (tcf_exts_dump_stats(skb, &f->exts) < 0) goto nla_put_failure; return skb->len; diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index 90fe275e580c..00a5a585e5f1 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -46,11 +46,6 @@ static const struct nla_policy bpf_policy[TCA_BPF_MAX + 1] = { .len = sizeof(struct sock_filter) * BPF_MAXINSNS }, }; -static const struct tcf_ext_map bpf_ext_map = { - .action = TCA_BPF_ACT, - .police = TCA_BPF_POLICE, -}; - static int cls_bpf_classify(struct sk_buff *skb, const struct tcf_proto *tp, struct tcf_result *res) { @@ -174,7 +169,8 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp, if (!tb[TCA_BPF_OPS_LEN] || !tb[TCA_BPF_OPS] || !tb[TCA_BPF_CLASSID]) return -EINVAL; - ret = tcf_exts_validate(net, tp, tb, est, &exts, &bpf_ext_map); + tcf_exts_init(&exts, TCA_BPF_ACT, TCA_BPF_POLICE); + ret = tcf_exts_validate(net, tp, tb, est, &exts); if (ret < 0) return ret; @@ -271,7 +267,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb, if (prog == NULL) return -ENOBUFS; - tcf_exts_init(&prog->exts); + tcf_exts_init(&prog->exts, TCA_BPF_ACT, TCA_BPF_POLICE); if (handle == 0) prog->handle = cls_bpf_grab_new_handle(tp, head); else @@ -326,12 +322,12 @@ static int cls_bpf_dump(struct tcf_proto *tp, unsigned long fh, memcpy(nla_data(nla), prog->bpf_ops, nla_len(nla)); - if (tcf_exts_dump(skb, &prog->exts, &bpf_ext_map) < 0) + if (tcf_exts_dump(skb, &prog->exts) < 0) goto nla_put_failure; nla_nest_end(skb, nest); - if (tcf_exts_dump_stats(skb, &prog->exts, &bpf_ext_map) < 0) + if (tcf_exts_dump_stats(skb, &prog->exts) < 0) goto nla_put_failure; return skb->len; diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index e4fae032bcfb..f9d212583ea2 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c @@ -172,11 +172,6 @@ static int cls_cgroup_init(struct tcf_proto *tp) return 0; } -static const struct tcf_ext_map cgroup_ext_map = { - .action = TCA_CGROUP_ACT, - .police = TCA_CGROUP_POLICE, -}; - static const struct nla_policy cgroup_policy[TCA_CGROUP_MAX + 1] = { [TCA_CGROUP_EMATCHES] = { .type = NLA_NESTED }, }; @@ -203,7 +198,7 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb, if (head == NULL) return -ENOBUFS; - tcf_exts_init(&head->exts); + tcf_exts_init(&head->exts, TCA_CGROUP_ACT, TCA_CGROUP_POLICE); head->handle = handle; tcf_tree_lock(tp); @@ -219,8 +214,8 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb, if (err < 0) return err; - err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, - &cgroup_ext_map); + tcf_exts_init(&e, TCA_CGROUP_ACT, TCA_CGROUP_POLICE); + err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e); if (err < 0) return err; @@ -278,13 +273,13 @@ static int cls_cgroup_dump(struct tcf_proto *tp, unsigned long fh, if (nest == NULL) goto nla_put_failure; - if (tcf_exts_dump(skb, &head->exts, &cgroup_ext_map) < 0 || + if (tcf_exts_dump(skb, &head->exts) < 0 || tcf_em_tree_dump(skb, &head->ematches, TCA_CGROUP_EMATCHES) < 0) goto nla_put_failure; nla_nest_end(skb, nest); - if (tcf_exts_dump_stats(skb, &head->exts, &cgroup_ext_map) < 0) + if (tcf_exts_dump_stats(skb, &head->exts) < 0) goto nla_put_failure; return skb->len; diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index f05904e3848f..dfd18a5c3e81 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -56,11 +56,6 @@ struct flow_filter { u32 hashrnd; }; -static const struct tcf_ext_map flow_ext_map = { - .action = TCA_FLOW_ACT, - .police = TCA_FLOW_POLICE, -}; - static inline u32 addr_fold(void *addr) { unsigned long a = (unsigned long)addr; @@ -397,7 +392,8 @@ static int flow_change(struct net *net, struct sk_buff *in_skb, return -EOPNOTSUPP; } - err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, &flow_ext_map); + tcf_exts_init(&e, TCA_FLOW_ACT, TCA_FLOW_POLICE); + err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e); if (err < 0) return err; @@ -455,7 +451,7 @@ static int flow_change(struct net *net, struct sk_buff *in_skb, f->handle = handle; f->mask = ~0U; - tcf_exts_init(&f->exts); + tcf_exts_init(&f->exts, TCA_FLOW_ACT, TCA_FLOW_POLICE); get_random_bytes(&f->hashrnd, 4); f->perturb_timer.function = flow_perturbation; @@ -609,7 +605,7 @@ static int flow_dump(struct tcf_proto *tp, unsigned long fh, nla_put_u32(skb, TCA_FLOW_PERTURB, f->perturb_period / HZ)) goto nla_put_failure; - if (tcf_exts_dump(skb, &f->exts, &flow_ext_map) < 0) + if (tcf_exts_dump(skb, &f->exts) < 0) goto nla_put_failure; #ifdef CONFIG_NET_EMATCH if (f->ematches.hdr.nmatches && @@ -618,7 +614,7 @@ static int flow_dump(struct tcf_proto *tp, unsigned long fh, #endif nla_nest_end(skb, nest); - if (tcf_exts_dump_stats(skb, &f->exts, &flow_ext_map) < 0) + if (tcf_exts_dump_stats(skb, &f->exts) < 0) goto nla_put_failure; return skb->len; diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index d1cebad19410..3f9cece13807 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -46,11 +46,6 @@ struct fw_filter { struct tcf_exts exts; }; -static const struct tcf_ext_map fw_ext_map = { - .action = TCA_FW_ACT, - .police = TCA_FW_POLICE -}; - static inline int fw_hash(u32 handle) { if (HTSIZE == 4096) @@ -200,7 +195,8 @@ fw_change_attrs(struct net *net, struct tcf_proto *tp, struct fw_filter *f, u32 mask; int err; - err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, &fw_ext_map); + tcf_exts_init(&e, TCA_FW_ACT, TCA_FW_POLICE); + err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e); if (err < 0) return err; @@ -280,7 +276,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb, if (f == NULL) return -ENOBUFS; - tcf_exts_init(&f->exts); + tcf_exts_init(&f->exts, TCA_FW_ACT, TCA_FW_POLICE); f->id = handle; err = fw_change_attrs(net, tp, f, tb, tca, base); @@ -360,12 +356,12 @@ static int fw_dump(struct tcf_proto *tp, unsigned long fh, nla_put_u32(skb, TCA_FW_MASK, head->mask)) goto nla_put_failure; - if (tcf_exts_dump(skb, &f->exts, &fw_ext_map) < 0) + if (tcf_exts_dump(skb, &f->exts) < 0) goto nla_put_failure; nla_nest_end(skb, nest); - if (tcf_exts_dump_stats(skb, &f->exts, &fw_ext_map) < 0) + if (tcf_exts_dump_stats(skb, &f->exts) < 0) goto nla_put_failure; return skb->len; diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index f1f1dfdf4eb3..2473953a5948 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -59,11 +59,6 @@ struct route4_filter { #define ROUTE4_FAILURE ((struct route4_filter *)(-1L)) -static const struct tcf_ext_map route_ext_map = { - .police = TCA_ROUTE4_POLICE, - .action = TCA_ROUTE4_ACT -}; - static inline int route4_fastmap_hash(u32 id, int iif) { return id & 0xF; @@ -347,7 +342,8 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp, struct route4_bucket *b; struct tcf_exts e; - err = tcf_exts_validate(net, tp, tb, est, &e, &route_ext_map); + tcf_exts_init(&e, TCA_ROUTE4_ACT, TCA_ROUTE4_POLICE); + err = tcf_exts_validate(net, tp, tb, est, &e); if (err < 0) return err; @@ -481,7 +477,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb, if (f == NULL) goto errout; - tcf_exts_init(&f->exts); + tcf_exts_init(&f->exts, TCA_ROUTE4_ACT, TCA_ROUTE4_POLICE); err = route4_set_parms(net, tp, base, f, handle, head, tb, tca[TCA_RATE], 1); if (err < 0) @@ -590,12 +586,12 @@ static int route4_dump(struct tcf_proto *tp, unsigned long fh, nla_put_u32(skb, TCA_ROUTE4_CLASSID, f->res.classid)) goto nla_put_failure; - if (tcf_exts_dump(skb, &f->exts, &route_ext_map) < 0) + if (tcf_exts_dump(skb, &f->exts) < 0) goto nla_put_failure; nla_nest_end(skb, nest); - if (tcf_exts_dump_stats(skb, &f->exts, &route_ext_map) < 0) + if (tcf_exts_dump_stats(skb, &f->exts) < 0) goto nla_put_failure; return skb->len; diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h index b1d3ce5c5ff8..4f25c2ac825b 100644 --- a/net/sched/cls_rsvp.h +++ b/net/sched/cls_rsvp.h @@ -116,11 +116,6 @@ static inline unsigned int hash_src(__be32 *src) return h & 0xF; } -static struct tcf_ext_map rsvp_ext_map = { - .police = TCA_RSVP_POLICE, - .action = TCA_RSVP_ACT -}; - #define RSVP_APPLY_RESULT() \ { \ int r = tcf_exts_exec(skb, &f->exts, res); \ @@ -440,7 +435,8 @@ static int rsvp_change(struct net *net, struct sk_buff *in_skb, if (err < 0) return err; - err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, &rsvp_ext_map); + tcf_exts_init(&e, TCA_RSVP_ACT, TCA_RSVP_POLICE); + err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e); if (err < 0) return err; @@ -471,7 +467,7 @@ static int rsvp_change(struct net *net, struct sk_buff *in_skb, if (f == NULL) goto errout2; - tcf_exts_init(&f->exts); + tcf_exts_init(&f->exts, TCA_RSVP_ACT, TCA_RSVP_POLICE); h2 = 16; if (tb[TCA_RSVP_SRC]) { memcpy(f->src, nla_data(tb[TCA_RSVP_SRC]), sizeof(f->src)); @@ -634,12 +630,12 @@ static int rsvp_dump(struct tcf_proto *tp, unsigned long fh, nla_put(skb, TCA_RSVP_SRC, sizeof(f->src), f->src)) goto nla_put_failure; - if (tcf_exts_dump(skb, &f->exts, &rsvp_ext_map) < 0) + if (tcf_exts_dump(skb, &f->exts) < 0) goto nla_put_failure; nla_nest_end(skb, nest); - if (tcf_exts_dump_stats(skb, &f->exts, &rsvp_ext_map) < 0) + if (tcf_exts_dump_stats(skb, &f->exts) < 0) goto nla_put_failure; return skb->len; diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c index c39bbfc0300a..ffad18791c93 100644 --- a/net/sched/cls_tcindex.c +++ b/net/sched/cls_tcindex.c @@ -50,11 +50,6 @@ struct tcindex_data { int fall_through; /* 0: only classify if explicit match */ }; -static const struct tcf_ext_map tcindex_ext_map = { - .police = TCA_TCINDEX_POLICE, - .action = TCA_TCINDEX_ACT -}; - static inline int tcindex_filter_is_set(struct tcindex_filter_result *r) { @@ -209,19 +204,20 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, struct tcindex_filter *f = NULL; /* make gcc behave */ struct tcf_exts e; - err = tcf_exts_validate(net, tp, tb, est, &e, &tcindex_ext_map); + tcf_exts_init(&e, TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE); + err = tcf_exts_validate(net, tp, tb, est, &e); if (err < 0) return err; memcpy(&cp, p, sizeof(cp)); memset(&new_filter_result, 0, sizeof(new_filter_result)); - tcf_exts_init(&new_filter_result.exts); + tcf_exts_init(&new_filter_result.exts, TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE); if (old_r) memcpy(&cr, r, sizeof(cr)); else { memset(&cr, 0, sizeof(cr)); - tcf_exts_init(&cr.exts); + tcf_exts_init(&cr.exts, TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE); } if (tb[TCA_TCINDEX_HASH]) @@ -471,11 +467,11 @@ static int tcindex_dump(struct tcf_proto *tp, unsigned long fh, nla_put_u32(skb, TCA_TCINDEX_CLASSID, r->res.classid)) goto nla_put_failure; - if (tcf_exts_dump(skb, &r->exts, &tcindex_ext_map) < 0) + if (tcf_exts_dump(skb, &r->exts) < 0) goto nla_put_failure; nla_nest_end(skb, nest); - if (tcf_exts_dump_stats(skb, &r->exts, &tcindex_ext_map) < 0) + if (tcf_exts_dump_stats(skb, &r->exts) < 0) goto nla_put_failure; } diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 492d9a6401ce..20f2fb79c747 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -79,11 +79,6 @@ struct tc_u_common { u32 hgenerator; }; -static const struct tcf_ext_map u32_ext_map = { - .action = TCA_U32_ACT, - .police = TCA_U32_POLICE -}; - static inline unsigned int u32_hash_fold(__be32 key, const struct tc_u32_sel *sel, u8 fshift) @@ -496,7 +491,8 @@ static int u32_set_parms(struct net *net, struct tcf_proto *tp, int err; struct tcf_exts e; - err = tcf_exts_validate(net, tp, tb, est, &e, &u32_ext_map); + tcf_exts_init(&e, TCA_U32_ACT, TCA_U32_POLICE); + err = tcf_exts_validate(net, tp, tb, est, &e); if (err < 0) return err; @@ -646,7 +642,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, n->ht_up = ht; n->handle = handle; n->fshift = s->hmask ? ffs(ntohl(s->hmask)) - 1 : 0; - tcf_exts_init(&n->exts); + tcf_exts_init(&n->exts, TCA_U32_ACT, TCA_U32_POLICE); #ifdef CONFIG_CLS_U32_MARK if (tb[TCA_U32_MARK]) { @@ -760,7 +756,7 @@ static int u32_dump(struct tcf_proto *tp, unsigned long fh, goto nla_put_failure; #endif - if (tcf_exts_dump(skb, &n->exts, &u32_ext_map) < 0) + if (tcf_exts_dump(skb, &n->exts) < 0) goto nla_put_failure; #ifdef CONFIG_NET_CLS_IND @@ -779,7 +775,7 @@ static int u32_dump(struct tcf_proto *tp, unsigned long fh, nla_nest_end(skb, nest); if (TC_U32_KEY(n->handle)) - if (tcf_exts_dump_stats(skb, &n->exts, &u32_ext_map) < 0) + if (tcf_exts_dump_stats(skb, &n->exts) < 0) goto nla_put_failure; return skb->len; -- cgit v1.2.3 From 369ba56787d7469c0afda70bb9ff76ad5faaead5 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Sun, 15 Dec 2013 20:15:08 -0800 Subject: net_sched: init struct tcf_hashinfo at register time It looks weird to store the lock out of the struct but still points to a static variable. Just move them into the struct. Cc: Jamal Hadi Salim Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/net/act_api.h | 18 +++++++++++++++++- net/sched/act_api.c | 16 ++++++++-------- net/sched/act_csum.c | 13 +++++-------- net/sched/act_gact.c | 13 +++++-------- net/sched/act_ipt.c | 21 ++++++++++----------- net/sched/act_mirred.c | 16 +++++++--------- net/sched/act_nat.c | 12 +++++------- net/sched/act_pedit.c | 12 +++++------- net/sched/act_police.c | 38 +++++++++++++++++++------------------- net/sched/act_simple.c | 20 +++++++++++--------- net/sched/act_skbedit.c | 13 +++++-------- 11 files changed, 97 insertions(+), 95 deletions(-) (limited to 'include/net') diff --git a/include/net/act_api.h b/include/net/act_api.h index a72642610790..2b5ec5abfeb3 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -38,7 +38,7 @@ struct tcf_common { struct tcf_hashinfo { struct tcf_common **htab; unsigned int hmask; - rwlock_t *lock; + rwlock_t lock; }; static inline unsigned int tcf_hash(u32 index, unsigned int hmask) @@ -46,6 +46,22 @@ static inline unsigned int tcf_hash(u32 index, unsigned int hmask) return index & hmask; } +static inline int tcf_hashinfo_init(struct tcf_hashinfo *hf, unsigned int mask) +{ + rwlock_init(&hf->lock); + hf->hmask = mask; + hf->htab = kzalloc((mask + 1) * sizeof(struct tcf_common *), + GFP_KERNEL); + if (!hf->htab) + return -ENOMEM; + return 0; +} + +static inline void tcf_hashinfo_destroy(struct tcf_hashinfo *hf) +{ + kfree(hf->htab); +} + #ifdef CONFIG_NET_CLS_ACT #define ACT_P_CREATED 1 diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 7d84183b633e..125673d5d877 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -34,9 +34,9 @@ void tcf_hash_destroy(struct tcf_common *p, struct tcf_hashinfo *hinfo) for (p1p = &hinfo->htab[h]; *p1p; p1p = &(*p1p)->tcfc_next) { if (*p1p == p) { - write_lock_bh(hinfo->lock); + write_lock_bh(&hinfo->lock); *p1p = p->tcfc_next; - write_unlock_bh(hinfo->lock); + write_unlock_bh(&hinfo->lock); gen_kill_estimator(&p->tcfc_bstats, &p->tcfc_rate_est); /* @@ -77,7 +77,7 @@ static int tcf_dump_walker(struct sk_buff *skb, struct netlink_callback *cb, int err = 0, index = -1, i = 0, s_i = 0, n_i = 0; struct nlattr *nest; - read_lock_bh(hinfo->lock); + read_lock_bh(&hinfo->lock); s_i = cb->args[0]; @@ -107,7 +107,7 @@ static int tcf_dump_walker(struct sk_buff *skb, struct netlink_callback *cb, } } done: - read_unlock_bh(hinfo->lock); + read_unlock_bh(&hinfo->lock); if (n_i) cb->args[0] += n_i; return n_i; @@ -170,13 +170,13 @@ struct tcf_common *tcf_hash_lookup(u32 index, struct tcf_hashinfo *hinfo) { struct tcf_common *p; - read_lock_bh(hinfo->lock); + read_lock_bh(&hinfo->lock); for (p = hinfo->htab[tcf_hash(index, hinfo->hmask)]; p; p = p->tcfc_next) { if (p->tcfc_index == index) break; } - read_unlock_bh(hinfo->lock); + read_unlock_bh(&hinfo->lock); return p; } @@ -257,10 +257,10 @@ void tcf_hash_insert(struct tcf_common *p, struct tcf_hashinfo *hinfo) { unsigned int h = tcf_hash(p->tcfc_index, hinfo->hmask); - write_lock_bh(hinfo->lock); + write_lock_bh(&hinfo->lock); p->tcfc_next = hinfo->htab[h]; hinfo->htab[h] = p; - write_unlock_bh(hinfo->lock); + write_unlock_bh(&hinfo->lock); } EXPORT_SYMBOL(tcf_hash_insert); diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c index 5c5edf56adbd..5d350c57af3f 100644 --- a/net/sched/act_csum.c +++ b/net/sched/act_csum.c @@ -37,15 +37,8 @@ #include #define CSUM_TAB_MASK 15 -static struct tcf_common *tcf_csum_ht[CSUM_TAB_MASK + 1]; static u32 csum_idx_gen; -static DEFINE_RWLOCK(csum_lock); - -static struct tcf_hashinfo csum_hash_info = { - .htab = tcf_csum_ht, - .hmask = CSUM_TAB_MASK, - .lock = &csum_lock, -}; +static struct tcf_hashinfo csum_hash_info; static const struct nla_policy csum_policy[TCA_CSUM_MAX + 1] = { [TCA_CSUM_PARMS] = { .len = sizeof(struct tc_csum), }, @@ -593,6 +586,10 @@ MODULE_LICENSE("GPL"); static int __init csum_init_module(void) { + int err = tcf_hashinfo_init(&csum_hash_info, CSUM_TAB_MASK+1); + if (err) + return err; + return tcf_register_action(&act_csum_ops); } diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c index 5645a4d32abd..1e6e0e765243 100644 --- a/net/sched/act_gact.c +++ b/net/sched/act_gact.c @@ -24,15 +24,8 @@ #include #define GACT_TAB_MASK 15 -static struct tcf_common *tcf_gact_ht[GACT_TAB_MASK + 1]; static u32 gact_idx_gen; -static DEFINE_RWLOCK(gact_lock); - -static struct tcf_hashinfo gact_hash_info = { - .htab = tcf_gact_ht, - .hmask = GACT_TAB_MASK, - .lock = &gact_lock, -}; +static struct tcf_hashinfo gact_hash_info; #ifdef CONFIG_GACT_PROB static int gact_net_rand(struct tcf_gact *gact) @@ -215,6 +208,9 @@ MODULE_LICENSE("GPL"); static int __init gact_init_module(void) { + int err = tcf_hashinfo_init(&gact_hash_info, GACT_TAB_MASK+1); + if (err) + return err; #ifdef CONFIG_GACT_PROB pr_info("GACT probability on\n"); #else @@ -226,6 +222,7 @@ static int __init gact_init_module(void) static void __exit gact_cleanup_module(void) { tcf_unregister_action(&act_gact_ops); + tcf_hashinfo_destroy(&gact_hash_info); } module_init(gact_init_module); diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index 882a89762f77..8344380ebaf1 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -29,15 +29,8 @@ #define IPT_TAB_MASK 15 -static struct tcf_common *tcf_ipt_ht[IPT_TAB_MASK + 1]; static u32 ipt_idx_gen; -static DEFINE_RWLOCK(ipt_lock); - -static struct tcf_hashinfo ipt_hash_info = { - .htab = tcf_ipt_ht, - .hmask = IPT_TAB_MASK, - .lock = &ipt_lock, -}; +static struct tcf_hashinfo ipt_hash_info; static int ipt_init_target(struct xt_entry_target *t, char *table, unsigned int hook) { @@ -320,7 +313,11 @@ MODULE_ALIAS("act_xt"); static int __init ipt_init_module(void) { - int ret1, ret2; + int ret1, ret2, err; + err = tcf_hashinfo_init(&ipt_hash_info, IPT_TAB_MASK+1); + if (err) + return err; + ret1 = tcf_register_action(&act_xt_ops); if (ret1 < 0) printk("Failed to load xt action\n"); @@ -328,9 +325,10 @@ static int __init ipt_init_module(void) if (ret2 < 0) printk("Failed to load ipt action\n"); - if (ret1 < 0 && ret2 < 0) + if (ret1 < 0 && ret2 < 0) { + tcf_hashinfo_destroy(&ipt_hash_info); return ret1; - else + } else return 0; } @@ -338,6 +336,7 @@ static void __exit ipt_cleanup_module(void) { tcf_unregister_action(&act_xt_ops); tcf_unregister_action(&act_ipt_ops); + tcf_hashinfo_destroy(&ipt_hash_info); } module_init(ipt_init_module); diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index 252378121ce7..199fc9838af3 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -30,16 +30,9 @@ #include #define MIRRED_TAB_MASK 7 -static struct tcf_common *tcf_mirred_ht[MIRRED_TAB_MASK + 1]; static u32 mirred_idx_gen; -static DEFINE_RWLOCK(mirred_lock); static LIST_HEAD(mirred_list); - -static struct tcf_hashinfo mirred_hash_info = { - .htab = tcf_mirred_ht, - .hmask = MIRRED_TAB_MASK, - .lock = &mirred_lock, -}; +static struct tcf_hashinfo mirred_hash_info; static int tcf_mirred_release(struct tcf_mirred *m, int bind) { @@ -261,7 +254,6 @@ static struct notifier_block mirred_device_notifier = { .notifier_call = mirred_device_event, }; - static struct tc_action_ops act_mirred_ops = { .kind = "mirred", .hinfo = &mirred_hash_info, @@ -284,6 +276,11 @@ static int __init mirred_init_module(void) if (err) return err; + err = tcf_hashinfo_init(&mirred_hash_info, MIRRED_TAB_MASK+1); + if (err) { + unregister_netdevice_notifier(&mirred_device_notifier); + return err; + } pr_info("Mirror/redirect action on\n"); return tcf_register_action(&act_mirred_ops); } @@ -291,6 +288,7 @@ static int __init mirred_init_module(void) static void __exit mirred_cleanup_module(void) { unregister_netdevice_notifier(&mirred_device_notifier); + tcf_hashinfo_destroy(&mirred_hash_info); tcf_unregister_action(&act_mirred_ops); } diff --git a/net/sched/act_nat.c b/net/sched/act_nat.c index 6a15ace00241..409fe7181c5f 100644 --- a/net/sched/act_nat.c +++ b/net/sched/act_nat.c @@ -30,15 +30,9 @@ #define NAT_TAB_MASK 15 -static struct tcf_common *tcf_nat_ht[NAT_TAB_MASK + 1]; static u32 nat_idx_gen; -static DEFINE_RWLOCK(nat_lock); -static struct tcf_hashinfo nat_hash_info = { - .htab = tcf_nat_ht, - .hmask = NAT_TAB_MASK, - .lock = &nat_lock, -}; +static struct tcf_hashinfo nat_hash_info; static const struct nla_policy nat_policy[TCA_NAT_MAX + 1] = { [TCA_NAT_PARMS] = { .len = sizeof(struct tc_nat) }, @@ -316,12 +310,16 @@ MODULE_LICENSE("GPL"); static int __init nat_init_module(void) { + int err = tcf_hashinfo_init(&nat_hash_info, NAT_TAB_MASK+1); + if (err) + return err; return tcf_register_action(&act_nat_ops); } static void __exit nat_cleanup_module(void) { tcf_unregister_action(&act_nat_ops); + tcf_hashinfo_destroy(&nat_hash_info); } module_init(nat_init_module); diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index 03b67674169c..aa5347c1b9f1 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -24,15 +24,9 @@ #include #define PEDIT_TAB_MASK 15 -static struct tcf_common *tcf_pedit_ht[PEDIT_TAB_MASK + 1]; static u32 pedit_idx_gen; -static DEFINE_RWLOCK(pedit_lock); -static struct tcf_hashinfo pedit_hash_info = { - .htab = tcf_pedit_ht, - .hmask = PEDIT_TAB_MASK, - .lock = &pedit_lock, -}; +static struct tcf_hashinfo pedit_hash_info; static const struct nla_policy pedit_policy[TCA_PEDIT_MAX + 1] = { [TCA_PEDIT_PARMS] = { .len = sizeof(struct tc_pedit) }, @@ -252,11 +246,15 @@ MODULE_LICENSE("GPL"); static int __init pedit_init_module(void) { + int err = tcf_hashinfo_init(&pedit_hash_info, PEDIT_TAB_MASK+1); + if (err) + return err; return tcf_register_action(&act_pedit_ops); } static void __exit pedit_cleanup_module(void) { + tcf_hashinfo_destroy(&pedit_hash_info); tcf_unregister_action(&act_pedit_ops); } diff --git a/net/sched/act_police.c b/net/sched/act_police.c index 16a62c36928a..f201576d25c2 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -41,15 +41,8 @@ struct tcf_police { container_of(pc, struct tcf_police, common) #define POL_TAB_MASK 15 -static struct tcf_common *tcf_police_ht[POL_TAB_MASK + 1]; static u32 police_idx_gen; -static DEFINE_RWLOCK(police_lock); - -static struct tcf_hashinfo police_hash_info = { - .htab = tcf_police_ht, - .hmask = POL_TAB_MASK, - .lock = &police_lock, -}; +static struct tcf_hashinfo police_hash_info; /* old policer structure from before tc actions */ struct tc_police_compat { @@ -71,12 +64,12 @@ static int tcf_act_police_walker(struct sk_buff *skb, struct netlink_callback *c int err = 0, index = -1, i = 0, s_i = 0, n_i = 0; struct nlattr *nest; - read_lock_bh(&police_lock); + read_lock_bh(&police_hash_info.lock); s_i = cb->args[0]; for (i = 0; i < (POL_TAB_MASK + 1); i++) { - p = tcf_police_ht[tcf_hash(i, POL_TAB_MASK)]; + p = police_hash_info.htab[tcf_hash(i, POL_TAB_MASK)]; for (; p; p = p->tcfc_next) { index++; @@ -101,7 +94,7 @@ static int tcf_act_police_walker(struct sk_buff *skb, struct netlink_callback *c } } done: - read_unlock_bh(&police_lock); + read_unlock_bh(&police_hash_info.lock); if (n_i) cb->args[0] += n_i; return n_i; @@ -116,11 +109,11 @@ static void tcf_police_destroy(struct tcf_police *p) unsigned int h = tcf_hash(p->tcf_index, POL_TAB_MASK); struct tcf_common **p1p; - for (p1p = &tcf_police_ht[h]; *p1p; p1p = &(*p1p)->tcfc_next) { + for (p1p = &police_hash_info.htab[h]; *p1p; p1p = &(*p1p)->tcfc_next) { if (*p1p == &p->common) { - write_lock_bh(&police_lock); + write_lock_bh(&police_hash_info.lock); *p1p = p->tcf_next; - write_unlock_bh(&police_lock); + write_unlock_bh(&police_hash_info.lock); gen_kill_estimator(&p->tcf_bstats, &p->tcf_rate_est); /* @@ -266,10 +259,10 @@ override: police->tcf_index = parm->index ? parm->index : tcf_hash_new_index(&police_idx_gen, &police_hash_info); h = tcf_hash(police->tcf_index, POL_TAB_MASK); - write_lock_bh(&police_lock); - police->tcf_next = tcf_police_ht[h]; - tcf_police_ht[h] = &police->common; - write_unlock_bh(&police_lock); + write_lock_bh(&police_hash_info.lock); + police->tcf_next = police_hash_info.htab[h]; + police_hash_info.htab[h] = &police->common; + write_unlock_bh(&police_hash_info.lock); a->priv = police; return ret; @@ -414,12 +407,19 @@ static struct tc_action_ops act_police_ops = { static int __init police_init_module(void) { - return tcf_register_action(&act_police_ops); + int err = tcf_hashinfo_init(&police_hash_info, POL_TAB_MASK+1); + if (err) + return err; + err = tcf_register_action(&act_police_ops); + if (err) + tcf_hashinfo_destroy(&police_hash_info); + return err; } static void __exit police_cleanup_module(void) { + tcf_hashinfo_destroy(&police_hash_info); tcf_unregister_action(&act_police_ops); } diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c index 31157d3e729c..2d7a0eb11c69 100644 --- a/net/sched/act_simple.c +++ b/net/sched/act_simple.c @@ -25,15 +25,8 @@ #include #define SIMP_TAB_MASK 7 -static struct tcf_common *tcf_simp_ht[SIMP_TAB_MASK + 1]; static u32 simp_idx_gen; -static DEFINE_RWLOCK(simp_lock); - -static struct tcf_hashinfo simp_hash_info = { - .htab = tcf_simp_ht, - .hmask = SIMP_TAB_MASK, - .lock = &simp_lock, -}; +static struct tcf_hashinfo simp_hash_info; #define SIMP_MAX_DATA 32 static int tcf_simp(struct sk_buff *skb, const struct tc_action *a, @@ -209,14 +202,23 @@ MODULE_LICENSE("GPL"); static int __init simp_init_module(void) { - int ret = tcf_register_action(&act_simp_ops); + int err, ret; + err = tcf_hashinfo_init(&simp_hash_info, SIMP_TAB_MASK+1); + if (err) + return err; + + ret = tcf_register_action(&act_simp_ops); if (!ret) pr_info("Simple TC action Loaded\n"); + else + tcf_hashinfo_destroy(&simp_hash_info); + return ret; } static void __exit simp_cleanup_module(void) { + tcf_hashinfo_destroy(&simp_hash_info); tcf_unregister_action(&act_simp_ops); } diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c index cf20add1c3ff..90ed04a83cf3 100644 --- a/net/sched/act_skbedit.c +++ b/net/sched/act_skbedit.c @@ -28,15 +28,8 @@ #include #define SKBEDIT_TAB_MASK 15 -static struct tcf_common *tcf_skbedit_ht[SKBEDIT_TAB_MASK + 1]; static u32 skbedit_idx_gen; -static DEFINE_RWLOCK(skbedit_lock); - -static struct tcf_hashinfo skbedit_hash_info = { - .htab = tcf_skbedit_ht, - .hmask = SKBEDIT_TAB_MASK, - .lock = &skbedit_lock, -}; +static struct tcf_hashinfo skbedit_hash_info; static int tcf_skbedit(struct sk_buff *skb, const struct tc_action *a, struct tcf_result *res) @@ -210,11 +203,15 @@ MODULE_LICENSE("GPL"); static int __init skbedit_init_module(void) { + int err = tcf_hashinfo_init(&skbedit_hash_info, SKBEDIT_TAB_MASK+1); + if (err) + return err; return tcf_register_action(&act_skbedit_ops); } static void __exit skbedit_cleanup_module(void) { + tcf_hashinfo_destroy(&skbedit_hash_info); tcf_unregister_action(&act_skbedit_ops); } -- cgit v1.2.3 From 89819dc01f4c5920783f561597a48d9d75220e9e Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Sun, 15 Dec 2013 20:15:09 -0800 Subject: net_sched: convert tcf_hashinfo to hlist and use spinlock So that we don't need to play with singly linked list, and since the code is not on hot path, we can use spinlock instead of rwlock. Cc: Jamal Hadi Salim Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/net/act_api.h | 16 +++++++----- net/sched/act_api.c | 69 ++++++++++++++++++++++---------------------------- net/sched/act_police.c | 45 +++++++++++++------------------- 3 files changed, 58 insertions(+), 72 deletions(-) (limited to 'include/net') diff --git a/include/net/act_api.h b/include/net/act_api.h index 2b5ec5abfeb3..22418d1a8396 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -9,7 +9,7 @@ #include struct tcf_common { - struct tcf_common *tcfc_next; + struct hlist_node tcfc_head; u32 tcfc_index; int tcfc_refcnt; int tcfc_bindcnt; @@ -22,7 +22,7 @@ struct tcf_common { spinlock_t tcfc_lock; struct rcu_head tcfc_rcu; }; -#define tcf_next common.tcfc_next +#define tcf_head common.tcfc_head #define tcf_index common.tcfc_index #define tcf_refcnt common.tcfc_refcnt #define tcf_bindcnt common.tcfc_bindcnt @@ -36,9 +36,9 @@ struct tcf_common { #define tcf_rcu common.tcfc_rcu struct tcf_hashinfo { - struct tcf_common **htab; + struct hlist_head *htab; unsigned int hmask; - rwlock_t lock; + spinlock_t lock; }; static inline unsigned int tcf_hash(u32 index, unsigned int hmask) @@ -48,12 +48,16 @@ static inline unsigned int tcf_hash(u32 index, unsigned int hmask) static inline int tcf_hashinfo_init(struct tcf_hashinfo *hf, unsigned int mask) { - rwlock_init(&hf->lock); + int i; + + spin_lock_init(&hf->lock); hf->hmask = mask; - hf->htab = kzalloc((mask + 1) * sizeof(struct tcf_common *), + hf->htab = kzalloc((mask + 1) * sizeof(struct hlist_head), GFP_KERNEL); if (!hf->htab) return -ENOMEM; + for (i = 0; i < mask + 1; i++) + INIT_HLIST_HEAD(&hf->htab[i]); return 0; } diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 125673d5d877..dc457c957656 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -29,25 +29,16 @@ void tcf_hash_destroy(struct tcf_common *p, struct tcf_hashinfo *hinfo) { - unsigned int h = tcf_hash(p->tcfc_index, hinfo->hmask); - struct tcf_common **p1p; - - for (p1p = &hinfo->htab[h]; *p1p; p1p = &(*p1p)->tcfc_next) { - if (*p1p == p) { - write_lock_bh(&hinfo->lock); - *p1p = p->tcfc_next; - write_unlock_bh(&hinfo->lock); - gen_kill_estimator(&p->tcfc_bstats, - &p->tcfc_rate_est); - /* - * gen_estimator est_timer() might access p->tcfc_lock - * or bstats, wait a RCU grace period before freeing p - */ - kfree_rcu(p, tcfc_rcu); - return; - } - } - WARN_ON(1); + spin_lock_bh(&hinfo->lock); + hlist_del(&p->tcfc_head); + spin_unlock_bh(&hinfo->lock); + gen_kill_estimator(&p->tcfc_bstats, + &p->tcfc_rate_est); + /* + * gen_estimator est_timer() might access p->tcfc_lock + * or bstats, wait a RCU grace period before freeing p + */ + kfree_rcu(p, tcfc_rcu); } EXPORT_SYMBOL(tcf_hash_destroy); @@ -73,18 +64,19 @@ EXPORT_SYMBOL(tcf_hash_release); static int tcf_dump_walker(struct sk_buff *skb, struct netlink_callback *cb, struct tc_action *a, struct tcf_hashinfo *hinfo) { + struct hlist_head *head; struct tcf_common *p; int err = 0, index = -1, i = 0, s_i = 0, n_i = 0; struct nlattr *nest; - read_lock_bh(&hinfo->lock); + spin_lock_bh(&hinfo->lock); s_i = cb->args[0]; for (i = 0; i < (hinfo->hmask + 1); i++) { - p = hinfo->htab[tcf_hash(i, hinfo->hmask)]; + head = &hinfo->htab[tcf_hash(i, hinfo->hmask)]; - for (; p; p = p->tcfc_next) { + hlist_for_each_entry_rcu(p, head, tcfc_head) { index++; if (index < s_i) continue; @@ -107,7 +99,7 @@ static int tcf_dump_walker(struct sk_buff *skb, struct netlink_callback *cb, } } done: - read_unlock_bh(&hinfo->lock); + spin_unlock_bh(&hinfo->lock); if (n_i) cb->args[0] += n_i; return n_i; @@ -120,7 +112,9 @@ nla_put_failure: static int tcf_del_walker(struct sk_buff *skb, struct tc_action *a, struct tcf_hashinfo *hinfo) { - struct tcf_common *p, *s_p; + struct hlist_head *head; + struct hlist_node *n; + struct tcf_common *p; struct nlattr *nest; int i = 0, n_i = 0; @@ -130,14 +124,11 @@ static int tcf_del_walker(struct sk_buff *skb, struct tc_action *a, if (nla_put_string(skb, TCA_KIND, a->ops->kind)) goto nla_put_failure; for (i = 0; i < (hinfo->hmask + 1); i++) { - p = hinfo->htab[tcf_hash(i, hinfo->hmask)]; - - while (p != NULL) { - s_p = p->tcfc_next; + head = &hinfo->htab[tcf_hash(i, hinfo->hmask)]; + hlist_for_each_entry_safe(p, n, head, tcfc_head) { if (ACT_P_DELETED == tcf_hash_release(p, 0, hinfo)) module_put(a->ops->owner); n_i++; - p = s_p; } } if (nla_put_u32(skb, TCA_FCNT, n_i)) @@ -168,15 +159,15 @@ EXPORT_SYMBOL(tcf_generic_walker); struct tcf_common *tcf_hash_lookup(u32 index, struct tcf_hashinfo *hinfo) { - struct tcf_common *p; + struct tcf_common *p = NULL; + struct hlist_head *head; - read_lock_bh(&hinfo->lock); - for (p = hinfo->htab[tcf_hash(index, hinfo->hmask)]; p; - p = p->tcfc_next) { + spin_lock_bh(&hinfo->lock); + head = &hinfo->htab[tcf_hash(index, hinfo->hmask)]; + hlist_for_each_entry_rcu(p, head, tcfc_head) if (p->tcfc_index == index) break; - } - read_unlock_bh(&hinfo->lock); + spin_unlock_bh(&hinfo->lock); return p; } @@ -236,6 +227,7 @@ struct tcf_common *tcf_hash_create(u32 index, struct nlattr *est, p->tcfc_bindcnt = 1; spin_lock_init(&p->tcfc_lock); + INIT_HLIST_NODE(&p->tcfc_head); p->tcfc_index = index ? index : tcf_hash_new_index(idx_gen, hinfo); p->tcfc_tm.install = jiffies; p->tcfc_tm.lastuse = jiffies; @@ -257,10 +249,9 @@ void tcf_hash_insert(struct tcf_common *p, struct tcf_hashinfo *hinfo) { unsigned int h = tcf_hash(p->tcfc_index, hinfo->hmask); - write_lock_bh(&hinfo->lock); - p->tcfc_next = hinfo->htab[h]; - hinfo->htab[h] = p; - write_unlock_bh(&hinfo->lock); + spin_lock_bh(&hinfo->lock); + hlist_add_head(&p->tcfc_head, &hinfo->htab[h]); + spin_unlock_bh(&hinfo->lock); } EXPORT_SYMBOL(tcf_hash_insert); diff --git a/net/sched/act_police.c b/net/sched/act_police.c index f201576d25c2..0cc305e7e469 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -60,18 +60,19 @@ struct tc_police_compat { static int tcf_act_police_walker(struct sk_buff *skb, struct netlink_callback *cb, int type, struct tc_action *a) { + struct hlist_head *head; struct tcf_common *p; int err = 0, index = -1, i = 0, s_i = 0, n_i = 0; struct nlattr *nest; - read_lock_bh(&police_hash_info.lock); + spin_lock_bh(&police_hash_info.lock); s_i = cb->args[0]; for (i = 0; i < (POL_TAB_MASK + 1); i++) { - p = police_hash_info.htab[tcf_hash(i, POL_TAB_MASK)]; + head = &police_hash_info.htab[tcf_hash(i, POL_TAB_MASK)]; - for (; p; p = p->tcfc_next) { + hlist_for_each_entry_rcu(p, head, tcfc_head) { index++; if (index < s_i) continue; @@ -94,7 +95,7 @@ static int tcf_act_police_walker(struct sk_buff *skb, struct netlink_callback *c } } done: - read_unlock_bh(&police_hash_info.lock); + spin_unlock_bh(&police_hash_info.lock); if (n_i) cb->args[0] += n_i; return n_i; @@ -106,25 +107,16 @@ nla_put_failure: static void tcf_police_destroy(struct tcf_police *p) { - unsigned int h = tcf_hash(p->tcf_index, POL_TAB_MASK); - struct tcf_common **p1p; - - for (p1p = &police_hash_info.htab[h]; *p1p; p1p = &(*p1p)->tcfc_next) { - if (*p1p == &p->common) { - write_lock_bh(&police_hash_info.lock); - *p1p = p->tcf_next; - write_unlock_bh(&police_hash_info.lock); - gen_kill_estimator(&p->tcf_bstats, - &p->tcf_rate_est); - /* - * gen_estimator est_timer() might access p->tcf_lock - * or bstats, wait a RCU grace period before freeing p - */ - kfree_rcu(p, tcf_rcu); - return; - } - } - WARN_ON(1); + spin_lock_bh(&police_hash_info.lock); + hlist_del(&p->tcf_head); + spin_unlock_bh(&police_hash_info.lock); + gen_kill_estimator(&p->tcf_bstats, + &p->tcf_rate_est); + /* + * gen_estimator est_timer() might access p->tcf_lock + * or bstats, wait a RCU grace period before freeing p + */ + kfree_rcu(p, tcf_rcu); } static const struct nla_policy police_policy[TCA_POLICE_MAX + 1] = { @@ -259,10 +251,9 @@ override: police->tcf_index = parm->index ? parm->index : tcf_hash_new_index(&police_idx_gen, &police_hash_info); h = tcf_hash(police->tcf_index, POL_TAB_MASK); - write_lock_bh(&police_hash_info.lock); - police->tcf_next = police_hash_info.htab[h]; - police_hash_info.htab[h] = &police->common; - write_unlock_bh(&police_hash_info.lock); + spin_lock_bh(&police_hash_info.lock); + hlist_add_head(&police->tcf_head, &police_hash_info.htab[h]); + spin_unlock_bh(&police_hash_info.lock); a->priv = police; return ret; -- cgit v1.2.3 From 1f747c26c48bb290c79c34e155860c7e2ec3926a Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Sun, 15 Dec 2013 20:15:10 -0800 Subject: net_sched: convert tc_action_ops to use struct list_head We don't need to maintain our own singly linked list code. Cc: Jamal Hadi Salim Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/net/act_api.h | 2 +- net/sched/act_api.c | 20 +++++++++----------- 2 files changed, 10 insertions(+), 12 deletions(-) (limited to 'include/net') diff --git a/include/net/act_api.h b/include/net/act_api.h index 22418d1a8396..77d5d8156efc 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -85,7 +85,7 @@ struct tc_action { #define TCA_CAP_NONE 0 struct tc_action_ops { - struct tc_action_ops *next; + struct list_head head; struct tcf_hashinfo *hinfo; char kind[IFNAMSIZ]; __u32 type; /* TBD to match kind */ diff --git a/net/sched/act_api.c b/net/sched/act_api.c index dc457c957656..8114fef308d9 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -255,12 +255,12 @@ void tcf_hash_insert(struct tcf_common *p, struct tcf_hashinfo *hinfo) } EXPORT_SYMBOL(tcf_hash_insert); -static struct tc_action_ops *act_base = NULL; +static LIST_HEAD(act_base); static DEFINE_RWLOCK(act_mod_lock); int tcf_register_action(struct tc_action_ops *act) { - struct tc_action_ops *a, **ap; + struct tc_action_ops *a; /* Must supply act, dump, cleanup and init */ if (!act->act || !act->dump || !act->cleanup || !act->init) @@ -273,14 +273,13 @@ int tcf_register_action(struct tc_action_ops *act) act->walk = tcf_generic_walker; write_lock(&act_mod_lock); - for (ap = &act_base; (a = *ap) != NULL; ap = &a->next) { + list_for_each_entry(a, &act_base, head) { if (act->type == a->type || (strcmp(act->kind, a->kind) == 0)) { write_unlock(&act_mod_lock); return -EEXIST; } } - act->next = NULL; - *ap = act; + list_add_tail(&act->head, &act_base); write_unlock(&act_mod_lock); return 0; } @@ -288,16 +287,15 @@ EXPORT_SYMBOL(tcf_register_action); int tcf_unregister_action(struct tc_action_ops *act) { - struct tc_action_ops *a, **ap; + struct tc_action_ops *a; int err = -ENOENT; write_lock(&act_mod_lock); - for (ap = &act_base; (a = *ap) != NULL; ap = &a->next) + list_for_each_entry(a, &act_base, head) if (a == act) break; if (a) { - *ap = a->next; - a->next = NULL; + list_del(&act->head); err = 0; } write_unlock(&act_mod_lock); @@ -312,7 +310,7 @@ static struct tc_action_ops *tc_lookup_action_n(char *kind) if (kind) { read_lock(&act_mod_lock); - for (a = act_base; a; a = a->next) { + list_for_each_entry(a, &act_base, head) { if (strcmp(kind, a->kind) == 0) { if (!try_module_get(a->owner)) { read_unlock(&act_mod_lock); @@ -333,7 +331,7 @@ static struct tc_action_ops *tc_lookup_action(struct nlattr *kind) if (kind) { read_lock(&act_mod_lock); - for (a = act_base; a; a = a->next) { + list_for_each_entry(a, &act_base, head) { if (nla_strcmp(kind, a->kind) == 0) { if (!try_module_get(a->owner)) { read_unlock(&act_mod_lock); -- cgit v1.2.3 From 3627287463b4acddb83d24fabb1e0a304e39565c Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Sun, 15 Dec 2013 20:15:11 -0800 Subject: net_sched: convert tcf_proto_ops to use struct list_head We don't need to maintain our own singly linked list code. Cc: Jamal Hadi Salim Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/net/sch_generic.h | 2 +- net/sched/cls_api.c | 18 ++++++++---------- 2 files changed, 9 insertions(+), 11 deletions(-) (limited to 'include/net') diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index d0a6321c302e..013d96dc6918 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -185,7 +185,7 @@ struct tcf_result { }; struct tcf_proto_ops { - struct tcf_proto_ops *next; + struct list_head head; char kind[IFNAMSIZ]; int (*classify)(struct sk_buff *, diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 028c980ef87d..6b085cf27a65 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -31,8 +31,7 @@ #include /* The list of all installed classifier types */ - -static struct tcf_proto_ops *tcf_proto_base __read_mostly; +static LIST_HEAD(tcf_proto_base); /* Protects list of registered TC modules. It is pure SMP lock. */ static DEFINE_RWLOCK(cls_mod_lock); @@ -45,7 +44,7 @@ static const struct tcf_proto_ops *tcf_proto_lookup_ops(struct nlattr *kind) if (kind) { read_lock(&cls_mod_lock); - for (t = tcf_proto_base; t; t = t->next) { + list_for_each_entry(t, &tcf_proto_base, head) { if (nla_strcmp(kind, t->kind) == 0) { if (!try_module_get(t->owner)) t = NULL; @@ -61,16 +60,15 @@ static const struct tcf_proto_ops *tcf_proto_lookup_ops(struct nlattr *kind) int register_tcf_proto_ops(struct tcf_proto_ops *ops) { - struct tcf_proto_ops *t, **tp; + struct tcf_proto_ops *t; int rc = -EEXIST; write_lock(&cls_mod_lock); - for (tp = &tcf_proto_base; (t = *tp) != NULL; tp = &t->next) + list_for_each_entry(t, &tcf_proto_base, head) if (!strcmp(ops->kind, t->kind)) goto out; - ops->next = NULL; - *tp = ops; + list_add_tail(&ops->head, &tcf_proto_base); rc = 0; out: write_unlock(&cls_mod_lock); @@ -80,17 +78,17 @@ EXPORT_SYMBOL(register_tcf_proto_ops); int unregister_tcf_proto_ops(struct tcf_proto_ops *ops) { - struct tcf_proto_ops *t, **tp; + struct tcf_proto_ops *t; int rc = -ENOENT; write_lock(&cls_mod_lock); - for (tp = &tcf_proto_base; (t = *tp) != NULL; tp = &t->next) + list_for_each_entry(t, &tcf_proto_base, head) if (t == ops) break; if (!t) goto out; - *tp = t->next; + list_del(&t->head); rc = 0; out: write_unlock(&cls_mod_lock); -- cgit v1.2.3 From 974eda11c54290a1be8f8b155edae7d791e5ce57 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Sat, 14 Dec 2013 05:13:38 +0100 Subject: inet: make no_pmtu_disc per namespace and kill ipv4_config The other field in ipv4_config, log_martians, was converted to a per-interface setting, so we can just remove the whole structure. Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/net/ip.h | 6 ------ include/net/netns/ipv4.h | 1 + net/ipv4/af_inet.c | 5 +---- net/ipv4/icmp.c | 2 +- net/ipv4/sysctl_net_ipv4.c | 14 +++++++------- net/ipv4/xfrm4_state.c | 2 +- net/ipv6/af_inet6.c | 2 +- 7 files changed, 12 insertions(+), 20 deletions(-) (limited to 'include/net') diff --git a/include/net/ip.h b/include/net/ip.h index 5a25f36fe3a7..535664477c4a 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -177,12 +177,6 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr, __be32 saddr, const struct ip_reply_arg *arg, unsigned int len); -struct ipv4_config { - int log_martians; - int no_pmtu_disc; -}; - -extern struct ipv4_config ipv4_config; #define IP_INC_STATS(net, field) SNMP_INC_STATS64((net)->mib.ip_statistics, field) #define IP_INC_STATS_BH(net, field) SNMP_INC_STATS64_BH((net)->mib.ip_statistics, field) #define IP_ADD_STATS(net, field, val) SNMP_ADD_STATS64((net)->mib.ip_statistics, field, val) diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index ee520cba2ec2..929a668e91a9 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -69,6 +69,7 @@ struct netns_ipv4 { struct local_ports sysctl_local_ports; int sysctl_tcp_ecn; + int sysctl_ip_no_pmtu_disc; kgid_t sysctl_ping_group_range[2]; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index ef4f9df6d698..6b1193e63911 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -126,9 +126,6 @@ static struct list_head inetsw[SOCK_MAX]; static DEFINE_SPINLOCK(inetsw_lock); -struct ipv4_config ipv4_config; -EXPORT_SYMBOL(ipv4_config); - /* New destruction routine */ void inet_sock_destruct(struct sock *sk) @@ -342,7 +339,7 @@ lookup_protocol: inet->hdrincl = 1; } - if (ipv4_config.no_pmtu_disc) + if (net->ipv4.sysctl_ip_no_pmtu_disc) inet->pmtudisc = IP_PMTUDISC_DONT; else inet->pmtudisc = IP_PMTUDISC_WANT; diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 5c0e8bc6e5ba..1a4ee84ca3f3 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -705,7 +705,7 @@ static void icmp_unreach(struct sk_buff *skb) case ICMP_PORT_UNREACH: break; case ICMP_FRAG_NEEDED: - if (ipv4_config.no_pmtu_disc) { + if (net->ipv4.sysctl_ip_no_pmtu_disc) { LIMIT_NETDEBUG(KERN_INFO pr_fmt("%pI4: fragmentation needed and DF set\n"), &iph->daddr); } else { diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 38c8ec90ff68..d7b63a614454 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -285,13 +285,6 @@ static struct ctl_table ipv4_table[] = { .extra1 = &ip_ttl_min, .extra2 = &ip_ttl_max, }, - { - .procname = "ip_no_pmtu_disc", - .data = &ipv4_config.no_pmtu_disc, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, { .procname = "ip_nonlocal_bind", .data = &sysctl_ip_nonlocal_bind, @@ -831,6 +824,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = ipv4_local_port_range, }, + { + .procname = "ip_no_pmtu_disc", + .data = &init_net.ipv4.sysctl_ip_no_pmtu_disc, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { } }; diff --git a/net/ipv4/xfrm4_state.c b/net/ipv4/xfrm4_state.c index 0b2a0641526a..542074c00c78 100644 --- a/net/ipv4/xfrm4_state.c +++ b/net/ipv4/xfrm4_state.c @@ -16,7 +16,7 @@ static int xfrm4_init_flags(struct xfrm_state *x) { - if (ipv4_config.no_pmtu_disc) + if (xs_net(x)->ipv4.sysctl_ip_no_pmtu_disc) x->props.flags |= XFRM_STATE_NOPMTUDISC; return 0; } diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 027615ac1b4e..5e76dfa765c4 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -213,7 +213,7 @@ lookup_protocol: inet->mc_list = NULL; inet->rcv_tos = 0; - if (ipv4_config.no_pmtu_disc) + if (net->ipv4.sysctl_ip_no_pmtu_disc) inet->pmtudisc = IP_PMTUDISC_DONT; else inet->pmtudisc = IP_PMTUDISC_WANT; -- cgit v1.2.3 From 93b36cf3425b9bd9c56df7680fb237686b9c82ae Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Sun, 15 Dec 2013 03:41:14 +0100 Subject: ipv6: support IPV6_PMTU_INTERFACE on sockets IPV6_PMTU_INTERFACE is the same as IPV6_PMTU_PROBE for ipv6. Add it nontheless for symmetry with IPv4 sockets. Also drop incoming MTU information if this mode is enabled. The additional bit in ipv6_pinfo just eats in the padding behind the bitfield. There are no changes to the layout of the struct at all. Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/linux/ipv6.h | 2 +- include/net/ip6_route.h | 7 ++++++- include/uapi/linux/in6.h | 4 ++++ net/dccp/ipv6.c | 3 +++ net/ipv6/ip6_output.c | 6 +++--- net/ipv6/ipv6_sockglue.c | 2 +- net/ipv6/tcp_ipv6.c | 3 +++ net/ipv6/udp.c | 5 ++++- net/sctp/input.c | 3 +++ 9 files changed, 28 insertions(+), 7 deletions(-) (limited to 'include/net') diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 3fde06645553..7e1ded0d8e45 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -191,7 +191,7 @@ struct ipv6_pinfo { /* sockopt flags */ __u16 recverr:1, sndflow:1, - pmtudisc:2, + pmtudisc:3, ipv6only:1, srcprefs:3, /* 001: prefer temporary address * 010: prefer public address diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 733747ce163c..c2626ce1f2ad 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -178,10 +178,15 @@ static inline int ip6_skb_dst_mtu(struct sk_buff *skb) { struct ipv6_pinfo *np = skb->sk ? inet6_sk(skb->sk) : NULL; - return (np && np->pmtudisc == IPV6_PMTUDISC_PROBE) ? + return (np && np->pmtudisc >= IPV6_PMTUDISC_PROBE) ? skb_dst(skb)->dev->mtu : dst_mtu(skb_dst(skb)); } +static inline bool ip6_sk_accept_pmtu(const struct sock *sk) +{ + return inet6_sk(sk)->pmtudisc != IPV6_PMTUDISC_INTERFACE; +} + static inline struct in6_addr *rt6_nexthop(struct rt6_info *rt) { return &rt->rt6i_gateway; diff --git a/include/uapi/linux/in6.h b/include/uapi/linux/in6.h index 440d5c479145..f94f1d013bf2 100644 --- a/include/uapi/linux/in6.h +++ b/include/uapi/linux/in6.h @@ -188,6 +188,10 @@ enum { #define IPV6_PMTUDISC_WANT 1 #define IPV6_PMTUDISC_DO 2 #define IPV6_PMTUDISC_PROBE 3 +/* same as IPV6_PMTUDISC_PROBE, provided for symetry with IPv4 + * also see comments on IP_PMTUDISC_INTERFACE + */ +#define IPV6_PMTUDISC_INTERFACE 4 /* Flowlabel */ #define IPV6_FLOWLABEL_MGR 32 diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 2b90a786e475..629019e6f8e9 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -141,6 +141,9 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (type == ICMPV6_PKT_TOOBIG) { struct dst_entry *dst = NULL; + if (!ip6_sk_accept_pmtu(sk)) + goto out; + if (sock_owned_by_user(sk)) goto out; if ((1 << sk->sk_state) & (DCCPF_LISTEN | DCCPF_CLOSED)) diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 9a311cc79672..bc4e1bcdf4c0 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1165,10 +1165,10 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, np->cork.hop_limit = hlimit; np->cork.tclass = tclass; if (rt->dst.flags & DST_XFRM_TUNNEL) - mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ? + mtu = np->pmtudisc >= IPV6_PMTUDISC_PROBE ? rt->dst.dev->mtu : dst_mtu(&rt->dst); else - mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ? + mtu = np->pmtudisc >= IPV6_PMTUDISC_PROBE ? rt->dst.dev->mtu : dst_mtu(rt->dst.path); if (np->frag_size < mtu) { if (np->frag_size) @@ -1270,7 +1270,7 @@ alloc_new_skb: if (skb == NULL || skb_prev == NULL) ip6_append_data_mtu(&mtu, &maxfraglen, fragheaderlen, skb, rt, - np->pmtudisc == + np->pmtudisc >= IPV6_PMTUDISC_PROBE); skb_prev = skb; diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 759fbf96515b..af0ecb94b3b4 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -722,7 +722,7 @@ done: case IPV6_MTU_DISCOVER: if (optlen < sizeof(int)) goto e_inval; - if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_PROBE) + if (val < IPV6_PMTUDISC_DONT || val > IPV6_PMTUDISC_INTERFACE) goto e_inval; np->pmtudisc = val; retv = 0; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index da046a5d7ffb..d955487f2c54 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -397,6 +397,9 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (sk->sk_state == TCP_LISTEN) goto out; + if (!ip6_sk_accept_pmtu(sk)) + goto out; + tp->mtu_info = ntohl(info); if (!sock_owned_by_user(sk)) tcp_v6_mtu_reduced(sk); diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 089c741a3992..65ed5cd79264 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -538,8 +538,11 @@ void __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (sk == NULL) return; - if (type == ICMPV6_PKT_TOOBIG) + if (type == ICMPV6_PKT_TOOBIG) { + if (!ip6_sk_accept_pmtu(sk)) + goto out; ip6_sk_update_pmtu(skb, sk, info); + } if (type == NDISC_REDIRECT) { ip6_sk_redirect(skb, sk); goto out; diff --git a/net/sctp/input.c b/net/sctp/input.c index 2a192a7c5d81..042ec6c9ae24 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -389,6 +389,9 @@ void sctp_icmp_frag_needed(struct sock *sk, struct sctp_association *asoc, if (!t || (t->pathmtu <= pmtu)) return; + if (!ip6_sk_accept_pmtu(sk)) + return; + if (sock_owned_by_user(sk)) { asoc->pmtu_pending = 1; t->pmtu_pending = 1; -- cgit v1.2.3 From a7022e65c68ad89d6eb64f21aa4831c3822403d4 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 16 Dec 2013 21:49:14 +0100 Subject: mac80211: add helper functions for tracking P2P NoA state Signed-off-by: Felix Fietkau Signed-off-by: Johannes Berg --- include/net/mac80211.h | 47 +++++++++++++++++ net/mac80211/util.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 187 insertions(+) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 531785f5819e..f838af816b56 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -4652,4 +4652,51 @@ bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct sk_buff *skb, int band, struct ieee80211_sta **sta); +/** + * struct ieee80211_noa_data - holds temporary data for tracking P2P NoA state + * + * @next_tsf: TSF timestamp of the next absent state change + * @has_next_tsf: next absent state change event pending + * + * @absent: descriptor bitmask, set if GO is currently absent + * + * private: + * + * @count: count fields from the NoA descriptors + * @desc: adjusted data from the NoA + */ +struct ieee80211_noa_data { + u32 next_tsf; + bool has_next_tsf; + + u8 absent; + + u8 count[IEEE80211_P2P_NOA_DESC_MAX]; + struct { + u32 start; + u32 duration; + u32 interval; + } desc[IEEE80211_P2P_NOA_DESC_MAX]; +}; + +/** + * ieee80211_parse_p2p_noa - initialize NoA tracking data from P2P IE + * + * @attr: P2P NoA IE + * @data: NoA tracking data + * @tsf: current TSF timestamp + * + * Return: number of successfully parsed descriptors + */ +int ieee80211_parse_p2p_noa(const struct ieee80211_p2p_noa_attr *attr, + struct ieee80211_noa_data *data, u32 tsf); + +/** + * ieee80211_update_p2p_noa - get next pending P2P GO absent state change + * + * @data: NoA tracking data + * @tsf: current TSF timestamp + */ +void ieee80211_update_p2p_noa(struct ieee80211_noa_data *data, u32 tsf); + #endif /* MAC80211_H */ diff --git a/net/mac80211/util.c b/net/mac80211/util.c index ed93504d24b8..df00f1978a77 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2594,3 +2594,143 @@ int ieee80211_cs_headroom(struct ieee80211_local *local, return headroom; } + +static bool +ieee80211_extend_noa_desc(struct ieee80211_noa_data *data, u32 tsf, int i) +{ + s32 end = data->desc[i].start + data->desc[i].duration - (tsf + 1); + int skip; + + if (end > 0) + return false; + + /* End time is in the past, check for repetitions */ + skip = DIV_ROUND_UP(-end, data->desc[i].interval); + if (data->count[i] < 255) { + if (data->count[i] <= skip) { + data->count[i] = 0; + return false; + } + + data->count[i] -= skip; + } + + data->desc[i].start += skip * data->desc[i].interval; + + return true; +} + +static bool +ieee80211_extend_absent_time(struct ieee80211_noa_data *data, u32 tsf, + s32 *offset) +{ + bool ret = false; + int i; + + for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) { + s32 cur; + + if (!data->count[i]) + continue; + + if (ieee80211_extend_noa_desc(data, tsf + *offset, i)) + ret = true; + + cur = data->desc[i].start - tsf; + if (cur > *offset) + continue; + + cur = data->desc[i].start + data->desc[i].duration - tsf; + if (cur > *offset) + *offset = cur; + } + + return ret; +} + +static u32 +ieee80211_get_noa_absent_time(struct ieee80211_noa_data *data, u32 tsf) +{ + s32 offset = 0; + int tries = 0; + /* + * arbitrary limit, used to avoid infinite loops when combined NoA + * descriptors cover the full time period. + */ + int max_tries = 5; + + ieee80211_extend_absent_time(data, tsf, &offset); + do { + if (!ieee80211_extend_absent_time(data, tsf, &offset)) + break; + + tries++; + } while (tries < max_tries); + + return offset; +} + +void ieee80211_update_p2p_noa(struct ieee80211_noa_data *data, u32 tsf) +{ + u32 next_offset = BIT(31) - 1; + int i; + + data->absent = 0; + data->has_next_tsf = false; + for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) { + s32 start; + + if (!data->count[i]) + continue; + + ieee80211_extend_noa_desc(data, tsf, i); + start = data->desc[i].start - tsf; + if (start <= 0) + data->absent |= BIT(i); + + if (next_offset > start) + next_offset = start; + + data->has_next_tsf = true; + } + + if (data->absent) + next_offset = ieee80211_get_noa_absent_time(data, tsf); + + data->next_tsf = tsf + next_offset; +} +EXPORT_SYMBOL(ieee80211_update_p2p_noa); + +int ieee80211_parse_p2p_noa(const struct ieee80211_p2p_noa_attr *attr, + struct ieee80211_noa_data *data, u32 tsf) +{ + int ret = 0; + int i; + + memset(data, 0, sizeof(*data)); + + for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) { + const struct ieee80211_p2p_noa_desc *desc = &attr->desc[i]; + + if (!desc->count || !desc->duration) + continue; + + data->count[i] = desc->count; + data->desc[i].start = le32_to_cpu(desc->start_time); + data->desc[i].duration = le32_to_cpu(desc->duration); + data->desc[i].interval = le32_to_cpu(desc->interval); + + if (data->count[i] > 1 && + data->desc[i].interval < data->desc[i].duration) + continue; + + ieee80211_extend_noa_desc(data, tsf, i); + ret++; + } + + if (ret) + ieee80211_update_p2p_noa(data, tsf); + + return ret; +} +EXPORT_SYMBOL(ieee80211_parse_p2p_noa); -- cgit v1.2.3 From 567ffc3509b2d3f965a49a18631d3da7f9a96d4f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 18 Dec 2013 14:43:31 +0100 Subject: nl80211: support vendor-specific events In addition to vendor-specific commands, also support vendor-specific events. These must be registered with cfg80211 before they can be used. They're also advertised in nl80211 in the wiphy information so that userspace knows can be expected. The events themselves are sent on a new multicast group called "vendor". Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 65 +++++++++++++++++++++++++++-- include/uapi/linux/nl80211.h | 3 ++ net/wireless/nl80211.c | 98 +++++++++++++++++++++++++++++++++++--------- 3 files changed, 143 insertions(+), 23 deletions(-) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 80a10212b1b9..6d238a4331bd 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2824,6 +2824,8 @@ struct wiphy_vendor_command { * * @vendor_commands: array of vendor commands supported by the hardware * @n_vendor_commands: number of vendor commands + * @vendor_events: array of vendor events supported by the hardware + * @n_vendor_events: number of vendor events */ struct wiphy { /* assign these fields before you register the wiphy */ @@ -2936,7 +2938,8 @@ struct wiphy { const struct wiphy_coalesce_support *coalesce; const struct wiphy_vendor_command *vendor_commands; - int n_vendor_commands; + const struct nl80211_vendor_cmd_info *vendor_events; + int n_vendor_commands, n_vendor_events; char priv[0] __aligned(NETDEV_ALIGN); }; @@ -3907,6 +3910,14 @@ struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy, enum nl80211_attrs attr, int approxlen); +struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy, + enum nl80211_commands cmd, + enum nl80211_attrs attr, + int vendor_event_idx, + int approxlen, gfp_t gfp); + +void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp); + /** * cfg80211_vendor_cmd_alloc_reply_skb - allocate vendor command reply * @wiphy: the wiphy @@ -3951,6 +3962,44 @@ cfg80211_vendor_cmd_alloc_reply_skb(struct wiphy *wiphy, int approxlen) */ int cfg80211_vendor_cmd_reply(struct sk_buff *skb); +/** + * cfg80211_vendor_event_alloc - allocate vendor-specific event skb + * @wiphy: the wiphy + * @event_idx: index of the vendor event in the wiphy's vendor_events + * @approxlen: an upper bound of the length of the data that will + * be put into the skb + * @gfp: allocation flags + * + * This function allocates and pre-fills an skb for an event on the + * vendor-specific multicast group. + * + * When done filling the skb, call cfg80211_vendor_event() with the + * skb to send the event. + * + * Return: An allocated and pre-filled skb. %NULL if any errors happen. + */ +static inline struct sk_buff * +cfg80211_vendor_event_alloc(struct wiphy *wiphy, int approxlen, + int event_idx, gfp_t gfp) +{ + return __cfg80211_alloc_event_skb(wiphy, NL80211_CMD_VENDOR, + NL80211_ATTR_VENDOR_DATA, + event_idx, approxlen, gfp); +} + +/** + * cfg80211_vendor_event - send the event + * @skb: The skb, must have been allocated with cfg80211_vendor_event_alloc() + * @gfp: allocation flags + * + * This function sends the given @skb, which must have been allocated + * by cfg80211_vendor_event_alloc(), as an event. It always consumes it. + */ +static inline void cfg80211_vendor_event(struct sk_buff *skb, gfp_t gfp) +{ + __cfg80211_send_event_skb(skb, gfp); +} + #ifdef CONFIG_NL80211_TESTMODE /** * DOC: Test mode @@ -4031,8 +4080,13 @@ static inline int cfg80211_testmode_reply(struct sk_buff *skb) * * Return: An allocated and pre-filled skb. %NULL if any errors happen. */ -struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, - int approxlen, gfp_t gfp); +static inline struct sk_buff * +cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, int approxlen, gfp_t gfp) +{ + return __cfg80211_alloc_event_skb(wiphy, NL80211_CMD_TESTMODE, + NL80211_ATTR_TESTDATA, -1, + approxlen, gfp); +} /** * cfg80211_testmode_event - send the event @@ -4044,7 +4098,10 @@ struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, * by cfg80211_testmode_alloc_event_skb(), as an event. It always * consumes it. */ -void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp); +static inline void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp) +{ + __cfg80211_send_event_skb(skb, gfp); +} #define CFG80211_TESTMODE_CMD(cmd) .testmode_cmd = (cmd), #define CFG80211_TESTMODE_DUMP(cmd) .testmode_dump = (cmd), diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index e1307909ecf1..381184eb6c96 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1540,6 +1540,8 @@ enum nl80211_commands { * @NL80211_ATTR_VENDOR_SUBCMD: vendor sub-command * @NL80211_ATTR_VENDOR_DATA: data for the vendor command, if any; this * attribute is also used for vendor command feature advertisement + * @NL80211_ATTR_VENDOR_EVENTS: used for event list advertising in the wiphy + * info, containing a nested array of possible events * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -1865,6 +1867,7 @@ enum nl80211_attrs { NL80211_ATTR_VENDOR_ID, NL80211_ATTR_VENDOR_SUBCMD, NL80211_ATTR_VENDOR_DATA, + NL80211_ATTR_VENDOR_EVENTS, /* add attributes here, update the policy in nl80211.c */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 04681a46eda8..8a7ff041349b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -53,6 +53,7 @@ enum nl80211_multicast_groups { NL80211_MCGRP_SCAN, NL80211_MCGRP_REGULATORY, NL80211_MCGRP_MLME, + NL80211_MCGRP_VENDOR, NL80211_MCGRP_TESTMODE /* keep last - ifdef! */ }; @@ -61,6 +62,7 @@ static const struct genl_multicast_group nl80211_mcgrps[] = { [NL80211_MCGRP_SCAN] = { .name = "scan", }, [NL80211_MCGRP_REGULATORY] = { .name = "regulatory", }, [NL80211_MCGRP_MLME] = { .name = "mlme", }, + [NL80211_MCGRP_VENDOR] = { .name = "vendor", }, #ifdef CONFIG_NL80211_TESTMODE [NL80211_MCGRP_TESTMODE] = { .name = "testmode", } #endif @@ -1188,7 +1190,6 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, struct nlattr *nl_bands, *nl_band; struct nlattr *nl_freqs, *nl_freq; struct nlattr *nl_cmds; - struct nlattr *nl_vendor_cmds; enum ieee80211_band band; struct ieee80211_channel *chan; int i; @@ -1587,16 +1588,38 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, state->split_start++; break; case 11: - nl_vendor_cmds = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); - if (!nl_vendor_cmds) - goto nla_put_failure; + if (dev->wiphy.n_vendor_commands) { + const struct nl80211_vendor_cmd_info *info; + struct nlattr *nested; + + nested = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (!nested) + goto nla_put_failure; + + for (i = 0; i < dev->wiphy.n_vendor_commands; i++) { + info = &dev->wiphy.vendor_commands[i].info; + if (nla_put(msg, i + 1, sizeof(*info), info)) + goto nla_put_failure; + } + nla_nest_end(msg, nested); + } + + if (dev->wiphy.n_vendor_events) { + const struct nl80211_vendor_cmd_info *info; + struct nlattr *nested; - for (i = 0; i < dev->wiphy.n_vendor_commands; i++) - if (nla_put(msg, i + 1, - sizeof(struct nl80211_vendor_cmd_info), - &dev->wiphy.vendor_commands[i].info)) + nested = nla_nest_start(msg, + NL80211_ATTR_VENDOR_EVENTS); + if (!nested) goto nla_put_failure; - nla_nest_end(msg, nl_vendor_cmds); + + for (i = 0; i < dev->wiphy.n_vendor_events; i++) { + info = &dev->wiphy.vendor_events[i]; + if (nla_put(msg, i + 1, sizeof(*info), info)) + goto nla_put_failure; + } + nla_nest_end(msg, nested); + } /* done */ state->split_start = 0; @@ -6726,7 +6749,9 @@ static struct sk_buff * __cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev, int approxlen, u32 portid, u32 seq, enum nl80211_commands cmd, - enum nl80211_attrs attr, gfp_t gfp) + enum nl80211_attrs attr, + const struct nl80211_vendor_cmd_info *info, + gfp_t gfp) { struct sk_buff *skb; void *hdr; @@ -6744,6 +6769,16 @@ __cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev, if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx)) goto nla_put_failure; + + if (info) { + if (nla_put_u32(skb, NL80211_ATTR_VENDOR_ID, + info->vendor_id)) + goto nla_put_failure; + if (nla_put_u32(skb, NL80211_ATTR_VENDOR_SUBCMD, + info->subcmd)) + goto nla_put_failure; + } + data = nla_nest_start(skb, attr); ((void **)skb->cb)[0] = rdev; @@ -6884,29 +6919,54 @@ static int nl80211_testmode_dump(struct sk_buff *skb, return err; } -struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, - int approxlen, gfp_t gfp) +struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy, + enum nl80211_commands cmd, + enum nl80211_attrs attr, + int vendor_event_idx, + int approxlen, gfp_t gfp) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + const struct nl80211_vendor_cmd_info *info; + + switch (cmd) { + case NL80211_CMD_TESTMODE: + if (WARN_ON(vendor_event_idx != -1)) + return NULL; + info = NULL; + break; + case NL80211_CMD_VENDOR: + if (WARN_ON(vendor_event_idx < 0 || + vendor_event_idx >= wiphy->n_vendor_events)) + return NULL; + info = &wiphy->vendor_events[vendor_event_idx]; + break; + default: + WARN_ON(1); + return NULL; + } return __cfg80211_alloc_vendor_skb(rdev, approxlen, 0, 0, - NL80211_CMD_TESTMODE, - NL80211_ATTR_TESTDATA, gfp); + cmd, attr, info, gfp); } -EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb); +EXPORT_SYMBOL(__cfg80211_alloc_event_skb); -void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp) +void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp) { struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; void *hdr = ((void **)skb->cb)[1]; struct nlattr *data = ((void **)skb->cb)[2]; + enum nl80211_multicast_groups mcgrp = NL80211_MCGRP_TESTMODE; nla_nest_end(skb, data); genlmsg_end(skb, hdr); + + if (data->nla_type == NL80211_ATTR_VENDOR_DATA) + mcgrp = NL80211_MCGRP_VENDOR; + genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), skb, 0, - NL80211_MCGRP_TESTMODE, gfp); + mcgrp, gfp); } -EXPORT_SYMBOL(cfg80211_testmode_event); +EXPORT_SYMBOL(__cfg80211_send_event_skb); #endif static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) @@ -9039,7 +9099,7 @@ struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy, return __cfg80211_alloc_vendor_skb(rdev, approxlen, rdev->cur_cmd_info->snd_portid, rdev->cur_cmd_info->snd_seq, - cmd, attr, GFP_KERNEL); + cmd, attr, NULL, GFP_KERNEL); } EXPORT_SYMBOL(__cfg80211_alloc_reply_skb); -- cgit v1.2.3 From fa9ffc745610f31c6bc136d5a6a1782e00870e72 Mon Sep 17 00:00:00 2001 From: Kyeyoon Park Date: Mon, 16 Dec 2013 23:01:30 -0800 Subject: cfg80211: Add support for QoS mapping This allows QoS mapping from external networks to be implemented as defined in IEEE Std 802.11-2012, 10.24.9. APs can use this to advertise DSCP ranges and exceptions for mapping frames to a specific UP over Wi-Fi. The payload of the QoS Map Set element (IEEE Std 802.11-2012, 8.4.2.97) is sent to the driver through the new NL80211_ATTR_QOS_MAP attribute to configure the local behavior either on the AP (based on local configuration) or on a station (based on information received from the AP). Signed-off-by: Kyeyoon Park Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 2 +- drivers/net/wireless/mwifiex/main.c | 2 +- include/net/cfg80211.h | 53 +++++++++++++++++- include/uapi/linux/nl80211.h | 14 +++++ net/mac80211/wme.c | 2 +- net/wireless/ap.c | 1 + net/wireless/ibss.c | 2 + net/wireless/mesh.c | 1 + net/wireless/nl80211.c | 62 ++++++++++++++++++++++ net/wireless/rdev-ops.h | 15 ++++++ net/wireless/sme.c | 2 + net/wireless/trace.h | 40 ++++++++++++++ net/wireless/util.c | 19 ++++++- 13 files changed, 210 insertions(+), 5 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index e9bdfdb95d8f..4f80e12a1d4a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -1873,7 +1873,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) brcmf_dbg(DATA, "tx proto=0x%X\n", ntohs(eh->h_proto)); /* determine the priority */ if (!skb->priority) - skb->priority = cfg80211_classify8021d(skb); + skb->priority = cfg80211_classify8021d(skb, NULL); drvr->tx_multicast += !!multicast; if (pae) diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 6bf58aba51d2..2d6f5e1721cf 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -749,7 +749,7 @@ static struct net_device_stats *mwifiex_get_stats(struct net_device *dev) static u16 mwifiex_netdev_select_wmm_queue(struct net_device *dev, struct sk_buff *skb) { - skb->priority = cfg80211_classify8021d(skb); + skb->priority = cfg80211_classify8021d(skb, NULL); return mwifiex_1d_to_wmm_queue[skb->priority]; } diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 6d238a4331bd..56c597793d6d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1971,6 +1971,50 @@ struct cfg80211_mgmt_tx_params { bool dont_wait_for_ack; }; +/** + * struct cfg80211_dscp_exception - DSCP exception + * + * @dscp: DSCP value that does not adhere to the user priority range definition + * @up: user priority value to which the corresponding DSCP value belongs + */ +struct cfg80211_dscp_exception { + u8 dscp; + u8 up; +}; + +/** + * struct cfg80211_dscp_range - DSCP range definition for user priority + * + * @low: lowest DSCP value of this user priority range, inclusive + * @high: highest DSCP value of this user priority range, inclusive + */ +struct cfg80211_dscp_range { + u8 low; + u8 high; +}; + +/* QoS Map Set element length defined in IEEE Std 802.11-2012, 8.4.2.97 */ +#define IEEE80211_QOS_MAP_MAX_EX 21 +#define IEEE80211_QOS_MAP_LEN_MIN 16 +#define IEEE80211_QOS_MAP_LEN_MAX \ + (IEEE80211_QOS_MAP_LEN_MIN + 2 * IEEE80211_QOS_MAP_MAX_EX) + +/** + * struct cfg80211_qos_map - QoS Map Information + * + * This struct defines the Interworking QoS map setting for DSCP values + * + * @num_des: number of DSCP exceptions (0..21) + * @dscp_exception: optionally up to maximum of 21 DSCP exceptions from + * the user priority DSCP range definition + * @up: DSCP range definition for a particular user priority + */ +struct cfg80211_qos_map { + u8 num_des; + struct cfg80211_dscp_exception dscp_exception[IEEE80211_QOS_MAP_MAX_EX]; + struct cfg80211_dscp_range up[8]; +}; + /** * struct cfg80211_ops - backend description for wireless configuration * @@ -2213,6 +2257,8 @@ struct cfg80211_mgmt_tx_params { * @set_coalesce: Set coalesce parameters. * * @channel_switch: initiate channel-switch procedure (with CSA) + * + * @set_qos_map: Set QoS mapping information to the driver */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); @@ -2454,6 +2500,9 @@ struct cfg80211_ops { int (*channel_switch)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_csa_settings *params); + int (*set_qos_map)(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_qos_map *qos_map); }; /* @@ -3432,9 +3481,11 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, /** * cfg80211_classify8021d - determine the 802.1p/1d tag for a data frame * @skb: the data frame + * @qos_map: Interworking QoS mapping or %NULL if not in use * Return: The 802.1p/1d tag. */ -unsigned int cfg80211_classify8021d(struct sk_buff *skb); +unsigned int cfg80211_classify8021d(struct sk_buff *skb, + struct cfg80211_qos_map *qos_map); /** * cfg80211_find_ie - find information element in data diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 381184eb6c96..91054fd660e0 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -702,6 +702,12 @@ * (&struct nl80211_vendor_cmd_info) of the supported vendor commands. * This may also be sent as an event with the same attributes. * + * @NL80211_CMD_SET_QOS_MAP: Set Interworking QoS mapping for IP DSCP values. + * The QoS mapping information is included in %NL80211_ATTR_QOS_MAP. If + * that attribute is not included, QoS mapping is disabled. Since this + * QoS mapping is relevant for IP packets, it is only valid during an + * association. This is cleared on disassociation and AP restart. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -871,6 +877,8 @@ enum nl80211_commands { NL80211_CMD_VENDOR, + NL80211_CMD_SET_QOS_MAP, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1543,6 +1551,10 @@ enum nl80211_commands { * @NL80211_ATTR_VENDOR_EVENTS: used for event list advertising in the wiphy * info, containing a nested array of possible events * + * @NL80211_ATTR_QOS_MAP: IP DSCP mapping for Interworking QoS mapping. This + * data is in the format defined for the payload of the QoS Map Set element + * in IEEE Std 802.11-2012, 8.4.2.97. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1869,6 +1881,8 @@ enum nl80211_attrs { NL80211_ATTR_VENDOR_DATA, NL80211_ATTR_VENDOR_EVENTS, + NL80211_ATTR_QOS_MAP, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index afba19cb6f87..faa9d8e451f0 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -155,7 +155,7 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, /* use the data classifier to determine what 802.1d tag the * data frame has */ - skb->priority = cfg80211_classify8021d(skb); + skb->priority = cfg80211_classify8021d(skb, NULL); return ieee80211_downgrade_queue(sdata, skb); } diff --git a/net/wireless/ap.c b/net/wireless/ap.c index 324e8d851dc4..11ee4ed04f73 100644 --- a/net/wireless/ap.c +++ b/net/wireless/ap.c @@ -29,6 +29,7 @@ static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, wdev->beacon_interval = 0; wdev->channel = NULL; wdev->ssid_len = 0; + rdev_set_qos_map(rdev, dev, NULL); } return err; diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 730147ed8e65..f911c5f9f903 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -183,6 +183,8 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) kfree(wdev->connect_keys); wdev->connect_keys = NULL; + rdev_set_qos_map(rdev, dev, NULL); + /* * Delete all the keys ... pairwise keys can't really * exist any more anyway, but default keys might. diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 9c7a11ae7936..885862447b63 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -277,6 +277,7 @@ static int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, if (!err) { wdev->mesh_id_len = 0; wdev->channel = NULL; + rdev_set_qos_map(rdev, dev, NULL); } return err; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 8a7ff041349b..b4f40fe84a01 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -382,6 +382,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_VENDOR_ID] = { .type = NLA_U32 }, [NL80211_ATTR_VENDOR_SUBCMD] = { .type = NLA_U32 }, [NL80211_ATTR_VENDOR_DATA] = { .type = NLA_BINARY }, + [NL80211_ATTR_QOS_MAP] = { .type = NLA_BINARY, + .len = IEEE80211_QOS_MAP_LEN_MAX }, }; /* policy for the key attributes */ @@ -1456,6 +1458,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, if (dev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH) CMD(channel_switch, CHANNEL_SWITCH); } + CMD(set_qos_map, SET_QOS_MAP); #ifdef CONFIG_NL80211_TESTMODE CMD(testmode_cmd, TESTMODE); @@ -9121,6 +9124,57 @@ int cfg80211_vendor_cmd_reply(struct sk_buff *skb) EXPORT_SYMBOL_GPL(cfg80211_vendor_cmd_reply); +static int nl80211_set_qos_map(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct cfg80211_qos_map *qos_map = NULL; + struct net_device *dev = info->user_ptr[1]; + u8 *pos, len, num_des, des_len, des; + int ret; + + if (!rdev->ops->set_qos_map) + return -EOPNOTSUPP; + + if (info->attrs[NL80211_ATTR_QOS_MAP]) { + pos = nla_data(info->attrs[NL80211_ATTR_QOS_MAP]); + len = nla_len(info->attrs[NL80211_ATTR_QOS_MAP]); + + if (len % 2 || len < IEEE80211_QOS_MAP_LEN_MIN || + len > IEEE80211_QOS_MAP_LEN_MAX) + return -EINVAL; + + qos_map = kzalloc(sizeof(struct cfg80211_qos_map), GFP_KERNEL); + if (!qos_map) + return -ENOMEM; + + num_des = (len - IEEE80211_QOS_MAP_LEN_MIN) >> 1; + if (num_des) { + des_len = num_des * + sizeof(struct cfg80211_dscp_exception); + memcpy(qos_map->dscp_exception, pos, des_len); + qos_map->num_des = num_des; + for (des = 0; des < num_des; des++) { + if (qos_map->dscp_exception[des].up > 7) { + kfree(qos_map); + return -EINVAL; + } + } + pos += des_len; + } + memcpy(qos_map->up, pos, IEEE80211_QOS_MAP_LEN_MIN); + } + + wdev_lock(dev->ieee80211_ptr); + ret = nl80211_key_allowed(dev->ieee80211_ptr); + if (!ret) + ret = rdev_set_qos_map(rdev, dev, qos_map); + wdev_unlock(dev->ieee80211_ptr); + + kfree(qos_map); + return ret; +} + #define NL80211_FLAG_NEED_WIPHY 0x01 #define NL80211_FLAG_NEED_NETDEV 0x02 #define NL80211_FLAG_NEED_RTNL 0x04 @@ -9853,6 +9907,14 @@ static const struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_WIPHY | NL80211_FLAG_NEED_RTNL, }, + { + .cmd = NL80211_CMD_SET_QOS_MAP, + .doit = nl80211_set_qos_map, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, }; /* notification functions */ diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index a6c03ab14a0d..c8e225947adb 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -932,4 +932,19 @@ static inline int rdev_channel_switch(struct cfg80211_registered_device *rdev, return ret; } +static inline int rdev_set_qos_map(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_qos_map *qos_map) +{ + int ret = -EOPNOTSUPP; + + if (rdev->ops->set_qos_map) { + trace_rdev_set_qos_map(&rdev->wiphy, dev, qos_map); + ret = rdev->ops->set_qos_map(&rdev->wiphy, dev, qos_map); + trace_rdev_return_int(&rdev->wiphy, ret); + } + + return ret; +} + #endif /* __CFG80211_RDEV_OPS */ diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 65f800890d70..3f64202358f4 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -870,6 +870,8 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, for (i = 0; i < 6; i++) rdev_del_key(rdev, dev, i, false, NULL); + rdev_set_qos_map(rdev, dev, NULL); + #ifdef CONFIG_CFG80211_WEXT memset(&wrqu, 0, sizeof(wrqu)); wrqu.ap_addr.sa_family = ARPHRD_ETHER; diff --git a/net/wireless/trace.h b/net/wireless/trace.h index f7aa7a72d9bc..fbcc23edee54 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -186,6 +186,28 @@ #define BOOL_TO_STR(bo) (bo) ? "true" : "false" +#define QOS_MAP_ENTRY __field(u8, num_des) \ + __array(u8, dscp_exception, \ + 2 * IEEE80211_QOS_MAP_MAX_EX) \ + __array(u8, up, IEEE80211_QOS_MAP_LEN_MIN) +#define QOS_MAP_ASSIGN(qos_map) \ + do { \ + if ((qos_map)) { \ + __entry->num_des = (qos_map)->num_des; \ + memcpy(__entry->dscp_exception, \ + &(qos_map)->dscp_exception, \ + 2 * IEEE80211_QOS_MAP_MAX_EX); \ + memcpy(__entry->up, &(qos_map)->up, \ + IEEE80211_QOS_MAP_LEN_MIN); \ + } else { \ + __entry->num_des = 0; \ + memset(__entry->dscp_exception, 0, \ + 2 * IEEE80211_QOS_MAP_MAX_EX); \ + memset(__entry->up, 0, \ + IEEE80211_QOS_MAP_LEN_MIN); \ + } \ + } while (0) + /************************************************************* * rdev->ops traces * *************************************************************/ @@ -1875,6 +1897,24 @@ TRACE_EVENT(rdev_channel_switch, __entry->counter_offset_presp) ); +TRACE_EVENT(rdev_set_qos_map, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_qos_map *qos_map), + TP_ARGS(wiphy, netdev, qos_map), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + QOS_MAP_ENTRY + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + QOS_MAP_ASSIGN(qos_map); + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", num_des: %u", + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->num_des) +); + /************************************************************* * cfg80211 exported functions traces * *************************************************************/ diff --git a/net/wireless/util.c b/net/wireless/util.c index 935dea9485da..5618888853b2 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -689,7 +689,8 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, EXPORT_SYMBOL(ieee80211_amsdu_to_8023s); /* Given a data frame determine the 802.1p/1d tag to use. */ -unsigned int cfg80211_classify8021d(struct sk_buff *skb) +unsigned int cfg80211_classify8021d(struct sk_buff *skb, + struct cfg80211_qos_map *qos_map) { unsigned int dscp; unsigned char vlan_priority; @@ -720,6 +721,21 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb) return 0; } + if (qos_map) { + unsigned int i, tmp_dscp = dscp >> 2; + + for (i = 0; i < qos_map->num_des; i++) { + if (tmp_dscp == qos_map->dscp_exception[i].dscp) + return qos_map->dscp_exception[i].up; + } + + for (i = 0; i < 8; i++) { + if (tmp_dscp >= qos_map->up[i].low && + tmp_dscp <= qos_map->up[i].high) + return i; + } + } + return dscp >> 5; } EXPORT_SYMBOL(cfg80211_classify8021d); @@ -863,6 +879,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, dev->ieee80211_ptr->use_4addr = false; dev->ieee80211_ptr->mesh_id_up_len = 0; + rdev_set_qos_map(rdev, dev, NULL); switch (otype) { case NL80211_IFTYPE_AP: -- cgit v1.2.3 From b6835f9c5d3199e6fa17ffad023f4b7dbad4f1a3 Mon Sep 17 00:00:00 2001 From: Rashika Kheria Date: Thu, 19 Dec 2013 14:22:37 +0530 Subject: drivers: net: Include new header file in sbni.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create a new header file include/net/Space.h which contains prototype declaration of sbni_probe(). Include the new header file in drivers/net/Space.c and drivers/net/wan/sbni.c because they use this function. This eliminates the following warning in wan/sbni.c: drivers/net/wan/sbni.c:224:12: warning: no previous prototype for ‘sbni_probe’ [-Wmissing-prototypes] Signed-off-by: Rashika Kheria Reviewed-by: Josh Triplett Signed-off-by: David S. Miller --- drivers/net/Space.c | 29 +---------------------------- drivers/net/wan/sbni.c | 1 + include/net/Space.h | 31 +++++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 28 deletions(-) create mode 100644 include/net/Space.h (limited to 'include/net') diff --git a/drivers/net/Space.c b/drivers/net/Space.c index a7271e093845..67977f15af25 100644 --- a/drivers/net/Space.c +++ b/drivers/net/Space.c @@ -32,39 +32,12 @@ #include #include #include +#include /* A unified ethernet device probe. This is the easiest way to have every ethernet adaptor have the name "eth[0123...]". */ -extern struct net_device *hp100_probe(int unit); -extern struct net_device *ultra_probe(int unit); -extern struct net_device *wd_probe(int unit); -extern struct net_device *ne_probe(int unit); -extern struct net_device *fmv18x_probe(int unit); -extern struct net_device *i82596_probe(int unit); -extern struct net_device *ni65_probe(int unit); -extern struct net_device *sonic_probe(int unit); -extern struct net_device *smc_init(int unit); -extern struct net_device *atarilance_probe(int unit); -extern struct net_device *sun3lance_probe(int unit); -extern struct net_device *sun3_82586_probe(int unit); -extern struct net_device *apne_probe(int unit); -extern struct net_device *cs89x0_probe(int unit); -extern struct net_device *mvme147lance_probe(int unit); -extern struct net_device *tc515_probe(int unit); -extern struct net_device *lance_probe(int unit); -extern struct net_device *mac8390_probe(int unit); -extern struct net_device *mac89x0_probe(int unit); -extern struct net_device *cops_probe(int unit); -extern struct net_device *ltpc_probe(void); - -/* Fibre Channel adapters */ -extern int iph5526_probe(struct net_device *dev); - -/* SBNI adapters */ -extern int sbni_probe(int unit); - struct devprobe2 { struct net_device *(*probe)(int unit); int status; /* non-zero if autoprobe has failed */ diff --git a/drivers/net/wan/sbni.c b/drivers/net/wan/sbni.c index 388ddf60a66d..1b89ecf0959e 100644 --- a/drivers/net/wan/sbni.c +++ b/drivers/net/wan/sbni.c @@ -57,6 +57,7 @@ #include #include +#include #include #include diff --git a/include/net/Space.h b/include/net/Space.h new file mode 100644 index 000000000000..8a32771e4215 --- /dev/null +++ b/include/net/Space.h @@ -0,0 +1,31 @@ +/* A unified ethernet device probe. This is the easiest way to have every + * ethernet adaptor have the name "eth[0123...]". + */ + +struct net_device *hp100_probe(int unit); +struct net_device *ultra_probe(int unit); +struct net_device *wd_probe(int unit); +struct net_device *ne_probe(int unit); +struct net_device *fmv18x_probe(int unit); +struct net_device *i82596_probe(int unit); +struct net_device *ni65_probe(int unit); +struct net_device *sonic_probe(int unit); +struct net_device *smc_init(int unit); +struct net_device *atarilance_probe(int unit); +struct net_device *sun3lance_probe(int unit); +struct net_device *sun3_82586_probe(int unit); +struct net_device *apne_probe(int unit); +struct net_device *cs89x0_probe(int unit); +struct net_device *mvme147lance_probe(int unit); +struct net_device *tc515_probe(int unit); +struct net_device *lance_probe(int unit); +struct net_device *mac8390_probe(int unit); +struct net_device *mac89x0_probe(int unit); +struct net_device *cops_probe(int unit); +struct net_device *ltpc_probe(void); + +/* Fibre Channel adapters */ +int iph5526_probe(struct net_device *dev); + +/* SBNI adapters */ +int sbni_probe(int unit); -- cgit v1.2.3 From 7195cf72211d77108de8271a247ec2876ae60502 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Sat, 28 Dec 2013 11:07:18 -0800 Subject: arp: make arp_invalidate static Don't export arp_invalidate, only used in arp.c Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/net/arp.h | 1 - net/ipv4/arp.c | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'include/net') diff --git a/include/net/arp.h b/include/net/arp.h index 7509d9da4e36..73c49864076b 100644 --- a/include/net/arp.h +++ b/include/net/arp.h @@ -62,6 +62,5 @@ struct sk_buff *arp_create(int type, int ptype, __be32 dest_ip, const unsigned char *src_hw, const unsigned char *target_hw); void arp_xmit(struct sk_buff *skb); -int arp_invalidate(struct net_device *dev, __be32 ip); #endif /* _ARP_H */ diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index b67cf805910e..5bf408b466b1 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -1112,7 +1112,7 @@ static int arp_req_get(struct arpreq *r, struct net_device *dev) return err; } -int arp_invalidate(struct net_device *dev, __be32 ip) +static int arp_invalidate(struct net_device *dev, __be32 ip) { struct neighbour *neigh = neigh_lookup(&arp_tbl, &ip, dev); int err = -ENXIO; @@ -1127,7 +1127,6 @@ int arp_invalidate(struct net_device *dev, __be32 ip) return err; } -EXPORT_SYMBOL(arp_invalidate); static int arp_req_delete_public(struct net *net, struct arpreq *r, struct net_device *dev) -- cgit v1.2.3 From 068a6e183484b1c619025da502a6fbfa53c50991 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Sat, 28 Dec 2013 11:08:22 -0800 Subject: ipv4: remove unused function inetpeer_invalidate_family defined but never used Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/net/inetpeer.h | 1 - net/ipv4/inetpeer.c | 7 ------- 2 files changed, 8 deletions(-) (limited to 'include/net') diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h index f4e127af4e17..6efe73c79c52 100644 --- a/include/net/inetpeer.h +++ b/include/net/inetpeer.h @@ -163,7 +163,6 @@ void inet_putpeer(struct inet_peer *p); bool inet_peer_xrlim_allow(struct inet_peer *peer, int timeout); void inetpeer_invalidate_tree(struct inet_peer_base *); -void inetpeer_invalidate_family(int family); /* * temporary check to make sure we dont access rid, ip_id_count, tcp_ts, diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index d047c8851714..48f424465112 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -109,13 +109,6 @@ static inline void flush_check(struct inet_peer_base *base, int family) } } -void inetpeer_invalidate_family(int family) -{ - atomic_t *fp = inetpeer_seq_ptr(family); - - atomic_inc(fp); -} - #define PEER_MAXDEPTH 40 /* sufficient for about 2^27 nodes */ /* Exported for sysctl_net_ipv4. */ -- cgit v1.2.3 From ea074b3495a023c2cf969605d51991b2b9df9514 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Sat, 28 Dec 2013 11:11:42 -0800 Subject: ipv4: ping make local stuff static Don't export ping_table or ping_v4_sendmsg. Both are only used inside ping code. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/net/ping.h | 8 -------- net/ipv4/ping.c | 10 +++++++--- 2 files changed, 7 insertions(+), 11 deletions(-) (limited to 'include/net') diff --git a/include/net/ping.h b/include/net/ping.h index 90f48417b03d..76013653e08c 100644 --- a/include/net/ping.h +++ b/include/net/ping.h @@ -42,11 +42,6 @@ struct pingv6_ops { const struct net_device *dev, int strict); }; -struct ping_table { - struct hlist_nulls_head hash[PING_HTABLE_SIZE]; - rwlock_t lock; -}; - struct ping_iter_state { struct seq_net_private p; int bucket; @@ -54,7 +49,6 @@ struct ping_iter_state { }; extern struct proto ping_prot; -extern struct ping_table ping_table; #if IS_ENABLED(CONFIG_IPV6) extern struct pingv6_ops pingv6_ops; #endif @@ -81,8 +75,6 @@ int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len, int noblock, int flags, int *addr_len); int ping_common_sendmsg(int family, struct msghdr *msg, size_t len, void *user_icmph, size_t icmph_len); -int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len); int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len); int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb); diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 242e7f4ed6f4..cae5262a337c 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -53,8 +53,12 @@ #include #endif +struct ping_table { + struct hlist_nulls_head hash[PING_HTABLE_SIZE]; + rwlock_t lock; +}; -struct ping_table ping_table; +static struct ping_table ping_table; struct pingv6_ops pingv6_ops; EXPORT_SYMBOL_GPL(pingv6_ops); @@ -668,8 +672,8 @@ int ping_common_sendmsg(int family, struct msghdr *msg, size_t len, } EXPORT_SYMBOL_GPL(ping_common_sendmsg); -int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len) +static int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, + size_t len) { struct net *net = sock_net(sk); struct flowi4 fl4; -- cgit v1.2.3 From f7e56a76acf642d21a8e7bd587e270ae7addc4db Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Sun, 29 Dec 2013 11:39:51 -0800 Subject: tcp: make local functions static The following are only used in one file: tcp_connect_init tcp_set_rto Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/net/tcp.h | 3 --- net/ipv4/tcp_input.c | 2 +- net/ipv4/tcp_output.c | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) (limited to 'include/net') diff --git a/include/net/tcp.h b/include/net/tcp.h index 9cd62bc09055..56fc366da6d5 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -468,7 +468,6 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, struct tcp_fastopen_cookie *foc); int tcp_disconnect(struct sock *sk, int flags); -void tcp_connect_init(struct sock *sk); void tcp_finish_connect(struct sock *sk, struct sk_buff *skb); int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size); void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb); @@ -623,8 +622,6 @@ static inline u32 __tcp_set_rto(const struct tcp_sock *tp) return (tp->srtt >> 3) + tp->rttvar; } -void tcp_set_rto(struct sock *sk); - static inline void __tcp_fast_path_on(struct tcp_sock *tp, u32 snd_wnd) { tp->pred_flags = htonl((tp->tcp_header_len << 26) | diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index f75cf15b2c87..65cf90e063d5 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -766,7 +766,7 @@ static void tcp_update_pacing_rate(struct sock *sk) /* Calculate rto without backoff. This is the second half of Van Jacobson's * routine referred to above. */ -void tcp_set_rto(struct sock *sk) +static void tcp_set_rto(struct sock *sk) { const struct tcp_sock *tp = tcp_sk(sk); /* Old crap is replaced with new one. 8) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 9e7aec7ee67e..03d26b85eab8 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2777,7 +2777,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, EXPORT_SYMBOL(tcp_make_synack); /* Do all connect socket setups that can be done AF independent. */ -void tcp_connect_init(struct sock *sk) +static void tcp_connect_init(struct sock *sk) { const struct dst_entry *dst = __sk_dst_get(sk); struct tcp_sock *tp = tcp_sk(sk); -- cgit v1.2.3 From cc70d069e2b9cece683206c0f6a1d1484414e577 Mon Sep 17 00:00:00 2001 From: Eric Leblond Date: Sun, 29 Dec 2013 12:28:13 +0100 Subject: netfilter: REJECT: separate reusable code This patch prepares the addition of TCP reset support in the nft_reject module by moving reusable code into a header file. Signed-off-by: Eric Leblond Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/ipv4/nf_reject.h | 128 +++++++++++++++++++++++ include/net/netfilter/ipv6/nf_reject.h | 171 +++++++++++++++++++++++++++++++ net/ipv4/netfilter/ipt_REJECT.c | 140 ++------------------------ net/ipv6/netfilter/ip6t_REJECT.c | 179 ++------------------------------- 4 files changed, 317 insertions(+), 301 deletions(-) create mode 100644 include/net/netfilter/ipv4/nf_reject.h create mode 100644 include/net/netfilter/ipv6/nf_reject.h (limited to 'include/net') diff --git a/include/net/netfilter/ipv4/nf_reject.h b/include/net/netfilter/ipv4/nf_reject.h new file mode 100644 index 000000000000..931fbf812171 --- /dev/null +++ b/include/net/netfilter/ipv4/nf_reject.h @@ -0,0 +1,128 @@ +#ifndef _IPV4_NF_REJECT_H +#define _IPV4_NF_REJECT_H + +#include +#include +#include +#include + +static inline void nf_send_unreach(struct sk_buff *skb_in, int code) +{ + icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0); +} + +/* Send RST reply */ +static void nf_send_reset(struct sk_buff *oldskb, int hook) +{ + struct sk_buff *nskb; + const struct iphdr *oiph; + struct iphdr *niph; + const struct tcphdr *oth; + struct tcphdr _otcph, *tcph; + + /* IP header checks: fragment. */ + if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET)) + return; + + oth = skb_header_pointer(oldskb, ip_hdrlen(oldskb), + sizeof(_otcph), &_otcph); + if (oth == NULL) + return; + + /* No RST for RST. */ + if (oth->rst) + return; + + if (skb_rtable(oldskb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) + return; + + /* Check checksum */ + if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP)) + return; + oiph = ip_hdr(oldskb); + + nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) + + LL_MAX_HEADER, GFP_ATOMIC); + if (!nskb) + return; + + skb_reserve(nskb, LL_MAX_HEADER); + + skb_reset_network_header(nskb); + niph = (struct iphdr *)skb_put(nskb, sizeof(struct iphdr)); + niph->version = 4; + niph->ihl = sizeof(struct iphdr) / 4; + niph->tos = 0; + niph->id = 0; + niph->frag_off = htons(IP_DF); + niph->protocol = IPPROTO_TCP; + niph->check = 0; + niph->saddr = oiph->daddr; + niph->daddr = oiph->saddr; + + skb_reset_transport_header(nskb); + tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); + memset(tcph, 0, sizeof(*tcph)); + tcph->source = oth->dest; + tcph->dest = oth->source; + tcph->doff = sizeof(struct tcphdr) / 4; + + if (oth->ack) + tcph->seq = oth->ack_seq; + else { + tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin + + oldskb->len - ip_hdrlen(oldskb) - + (oth->doff << 2)); + tcph->ack = 1; + } + + tcph->rst = 1; + tcph->check = ~tcp_v4_check(sizeof(struct tcphdr), niph->saddr, + niph->daddr, 0); + nskb->ip_summed = CHECKSUM_PARTIAL; + nskb->csum_start = (unsigned char *)tcph - nskb->head; + nskb->csum_offset = offsetof(struct tcphdr, check); + + /* ip_route_me_harder expects skb->dst to be set */ + skb_dst_set_noref(nskb, skb_dst(oldskb)); + + nskb->protocol = htons(ETH_P_IP); + if (ip_route_me_harder(nskb, RTN_UNSPEC)) + goto free_nskb; + + niph->ttl = ip4_dst_hoplimit(skb_dst(nskb)); + + /* "Never happens" */ + if (nskb->len > dst_mtu(skb_dst(nskb))) + goto free_nskb; + + nf_ct_attach(nskb, oldskb); + +#ifdef CONFIG_BRIDGE_NETFILTER + /* If we use ip_local_out for bridged traffic, the MAC source on + * the RST will be ours, instead of the destination's. This confuses + * some routers/firewalls, and they drop the packet. So we need to + * build the eth header using the original destination's MAC as the + * source, and send the RST packet directly. + */ + if (oldskb->nf_bridge) { + struct ethhdr *oeth = eth_hdr(oldskb); + nskb->dev = oldskb->nf_bridge->physindev; + niph->tot_len = htons(nskb->len); + ip_send_check(niph); + if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol), + oeth->h_source, oeth->h_dest, nskb->len) < 0) + goto free_nskb; + dev_queue_xmit(nskb); + } else +#endif + ip_local_out(nskb); + + return; + + free_nskb: + kfree_skb(nskb); +} + + +#endif /* _IPV4_NF_REJECT_H */ diff --git a/include/net/netfilter/ipv6/nf_reject.h b/include/net/netfilter/ipv6/nf_reject.h new file mode 100644 index 000000000000..710d17ed70b4 --- /dev/null +++ b/include/net/netfilter/ipv6/nf_reject.h @@ -0,0 +1,171 @@ +#ifndef _IPV6_NF_REJECT_H +#define _IPV6_NF_REJECT_H + +#include +#include +#include +#include +#include + +static inline void +nf_send_unreach6(struct net *net, struct sk_buff *skb_in, unsigned char code, + unsigned int hooknum) +{ + if (hooknum == NF_INET_LOCAL_OUT && skb_in->dev == NULL) + skb_in->dev = net->loopback_dev; + + icmpv6_send(skb_in, ICMPV6_DEST_UNREACH, code, 0); +} + +/* Send RST reply */ +static void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook) +{ + struct sk_buff *nskb; + struct tcphdr otcph, *tcph; + unsigned int otcplen, hh_len; + int tcphoff, needs_ack; + const struct ipv6hdr *oip6h = ipv6_hdr(oldskb); + struct ipv6hdr *ip6h; +#define DEFAULT_TOS_VALUE 0x0U + const __u8 tclass = DEFAULT_TOS_VALUE; + struct dst_entry *dst = NULL; + u8 proto; + __be16 frag_off; + struct flowi6 fl6; + + if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) || + (!(ipv6_addr_type(&oip6h->daddr) & IPV6_ADDR_UNICAST))) { + pr_debug("addr is not unicast.\n"); + return; + } + + proto = oip6h->nexthdr; + tcphoff = ipv6_skip_exthdr(oldskb, ((u8*)(oip6h+1) - oldskb->data), &proto, &frag_off); + + if ((tcphoff < 0) || (tcphoff > oldskb->len)) { + pr_debug("Cannot get TCP header.\n"); + return; + } + + otcplen = oldskb->len - tcphoff; + + /* IP header checks: fragment, too short. */ + if (proto != IPPROTO_TCP || otcplen < sizeof(struct tcphdr)) { + pr_debug("proto(%d) != IPPROTO_TCP, " + "or too short. otcplen = %d\n", + proto, otcplen); + return; + } + + if (skb_copy_bits(oldskb, tcphoff, &otcph, sizeof(struct tcphdr))) + BUG(); + + /* No RST for RST. */ + if (otcph.rst) { + pr_debug("RST is set\n"); + return; + } + + /* Check checksum. */ + if (nf_ip6_checksum(oldskb, hook, tcphoff, IPPROTO_TCP)) { + pr_debug("TCP checksum is invalid\n"); + return; + } + + memset(&fl6, 0, sizeof(fl6)); + fl6.flowi6_proto = IPPROTO_TCP; + fl6.saddr = oip6h->daddr; + fl6.daddr = oip6h->saddr; + fl6.fl6_sport = otcph.dest; + fl6.fl6_dport = otcph.source; + security_skb_classify_flow(oldskb, flowi6_to_flowi(&fl6)); + dst = ip6_route_output(net, NULL, &fl6); + if (dst == NULL || dst->error) { + dst_release(dst); + return; + } + dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0); + if (IS_ERR(dst)) + return; + + hh_len = (dst->dev->hard_header_len + 15)&~15; + nskb = alloc_skb(hh_len + 15 + dst->header_len + sizeof(struct ipv6hdr) + + sizeof(struct tcphdr) + dst->trailer_len, + GFP_ATOMIC); + + if (!nskb) { + net_dbg_ratelimited("cannot alloc skb\n"); + dst_release(dst); + return; + } + + skb_dst_set(nskb, dst); + + skb_reserve(nskb, hh_len + dst->header_len); + + skb_put(nskb, sizeof(struct ipv6hdr)); + skb_reset_network_header(nskb); + ip6h = ipv6_hdr(nskb); + ip6_flow_hdr(ip6h, tclass, 0); + ip6h->hop_limit = ip6_dst_hoplimit(dst); + ip6h->nexthdr = IPPROTO_TCP; + ip6h->saddr = oip6h->daddr; + ip6h->daddr = oip6h->saddr; + + skb_reset_transport_header(nskb); + tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); + /* Truncate to length (no data) */ + tcph->doff = sizeof(struct tcphdr)/4; + tcph->source = otcph.dest; + tcph->dest = otcph.source; + + if (otcph.ack) { + needs_ack = 0; + tcph->seq = otcph.ack_seq; + tcph->ack_seq = 0; + } else { + needs_ack = 1; + tcph->ack_seq = htonl(ntohl(otcph.seq) + otcph.syn + otcph.fin + + otcplen - (otcph.doff<<2)); + tcph->seq = 0; + } + + /* Reset flags */ + ((u_int8_t *)tcph)[13] = 0; + tcph->rst = 1; + tcph->ack = needs_ack; + tcph->window = 0; + tcph->urg_ptr = 0; + tcph->check = 0; + + /* Adjust TCP checksum */ + tcph->check = csum_ipv6_magic(&ipv6_hdr(nskb)->saddr, + &ipv6_hdr(nskb)->daddr, + sizeof(struct tcphdr), IPPROTO_TCP, + csum_partial(tcph, + sizeof(struct tcphdr), 0)); + + nf_ct_attach(nskb, oldskb); + +#ifdef CONFIG_BRIDGE_NETFILTER + /* If we use ip6_local_out for bridged traffic, the MAC source on + * the RST will be ours, instead of the destination's. This confuses + * some routers/firewalls, and they drop the packet. So we need to + * build the eth header using the original destination's MAC as the + * source, and send the RST packet directly. + */ + if (oldskb->nf_bridge) { + struct ethhdr *oeth = eth_hdr(oldskb); + nskb->dev = oldskb->nf_bridge->physindev; + nskb->protocol = htons(ETH_P_IPV6); + ip6h->payload_len = htons(sizeof(struct tcphdr)); + if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol), + oeth->h_source, oeth->h_dest, nskb->len) < 0) + return; + dev_queue_xmit(nskb); + } else +#endif + ip6_local_out(nskb); +} + +#endif /* _IPV6_NF_REJECT_H */ diff --git a/net/ipv4/netfilter/ipt_REJECT.c b/net/ipv4/netfilter/ipt_REJECT.c index b969131ad1c1..5b6e0df4ccff 100644 --- a/net/ipv4/netfilter/ipt_REJECT.c +++ b/net/ipv4/netfilter/ipt_REJECT.c @@ -17,10 +17,6 @@ #include #include #include -#include -#include -#include -#include #include #include #include @@ -28,128 +24,12 @@ #include #endif +#include + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Netfilter Core Team "); MODULE_DESCRIPTION("Xtables: packet \"rejection\" target for IPv4"); -/* Send RST reply */ -static void send_reset(struct sk_buff *oldskb, int hook) -{ - struct sk_buff *nskb; - const struct iphdr *oiph; - struct iphdr *niph; - const struct tcphdr *oth; - struct tcphdr _otcph, *tcph; - - /* IP header checks: fragment. */ - if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET)) - return; - - oth = skb_header_pointer(oldskb, ip_hdrlen(oldskb), - sizeof(_otcph), &_otcph); - if (oth == NULL) - return; - - /* No RST for RST. */ - if (oth->rst) - return; - - if (skb_rtable(oldskb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) - return; - - /* Check checksum */ - if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP)) - return; - oiph = ip_hdr(oldskb); - - nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) + - LL_MAX_HEADER, GFP_ATOMIC); - if (!nskb) - return; - - skb_reserve(nskb, LL_MAX_HEADER); - - skb_reset_network_header(nskb); - niph = (struct iphdr *)skb_put(nskb, sizeof(struct iphdr)); - niph->version = 4; - niph->ihl = sizeof(struct iphdr) / 4; - niph->tos = 0; - niph->id = 0; - niph->frag_off = htons(IP_DF); - niph->protocol = IPPROTO_TCP; - niph->check = 0; - niph->saddr = oiph->daddr; - niph->daddr = oiph->saddr; - - skb_reset_transport_header(nskb); - tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); - memset(tcph, 0, sizeof(*tcph)); - tcph->source = oth->dest; - tcph->dest = oth->source; - tcph->doff = sizeof(struct tcphdr) / 4; - - if (oth->ack) - tcph->seq = oth->ack_seq; - else { - tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin + - oldskb->len - ip_hdrlen(oldskb) - - (oth->doff << 2)); - tcph->ack = 1; - } - - tcph->rst = 1; - tcph->check = ~tcp_v4_check(sizeof(struct tcphdr), niph->saddr, - niph->daddr, 0); - nskb->ip_summed = CHECKSUM_PARTIAL; - nskb->csum_start = (unsigned char *)tcph - nskb->head; - nskb->csum_offset = offsetof(struct tcphdr, check); - - /* ip_route_me_harder expects skb->dst to be set */ - skb_dst_set_noref(nskb, skb_dst(oldskb)); - - nskb->protocol = htons(ETH_P_IP); - if (ip_route_me_harder(nskb, RTN_UNSPEC)) - goto free_nskb; - - niph->ttl = ip4_dst_hoplimit(skb_dst(nskb)); - - /* "Never happens" */ - if (nskb->len > dst_mtu(skb_dst(nskb))) - goto free_nskb; - - nf_ct_attach(nskb, oldskb); - -#ifdef CONFIG_BRIDGE_NETFILTER - /* If we use ip_local_out for bridged traffic, the MAC source on - * the RST will be ours, instead of the destination's. This confuses - * some routers/firewalls, and they drop the packet. So we need to - * build the eth header using the original destination's MAC as the - * source, and send the RST packet directly. - */ - if (oldskb->nf_bridge) { - struct ethhdr *oeth = eth_hdr(oldskb); - nskb->dev = oldskb->nf_bridge->physindev; - niph->tot_len = htons(nskb->len); - ip_send_check(niph); - if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol), - oeth->h_source, oeth->h_dest, nskb->len) < 0) - goto free_nskb; - dev_queue_xmit(nskb); - } else -#endif - ip_local_out(nskb); - - return; - - free_nskb: - kfree_skb(nskb); -} - -static inline void send_unreach(struct sk_buff *skb_in, int code) -{ - icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0); -} - static unsigned int reject_tg(struct sk_buff *skb, const struct xt_action_param *par) { @@ -157,28 +37,28 @@ reject_tg(struct sk_buff *skb, const struct xt_action_param *par) switch (reject->with) { case IPT_ICMP_NET_UNREACHABLE: - send_unreach(skb, ICMP_NET_UNREACH); + nf_send_unreach(skb, ICMP_NET_UNREACH); break; case IPT_ICMP_HOST_UNREACHABLE: - send_unreach(skb, ICMP_HOST_UNREACH); + nf_send_unreach(skb, ICMP_HOST_UNREACH); break; case IPT_ICMP_PROT_UNREACHABLE: - send_unreach(skb, ICMP_PROT_UNREACH); + nf_send_unreach(skb, ICMP_PROT_UNREACH); break; case IPT_ICMP_PORT_UNREACHABLE: - send_unreach(skb, ICMP_PORT_UNREACH); + nf_send_unreach(skb, ICMP_PORT_UNREACH); break; case IPT_ICMP_NET_PROHIBITED: - send_unreach(skb, ICMP_NET_ANO); + nf_send_unreach(skb, ICMP_NET_ANO); break; case IPT_ICMP_HOST_PROHIBITED: - send_unreach(skb, ICMP_HOST_ANO); + nf_send_unreach(skb, ICMP_HOST_ANO); break; case IPT_ICMP_ADMIN_PROHIBITED: - send_unreach(skb, ICMP_PKT_FILTERED); + nf_send_unreach(skb, ICMP_PKT_FILTERED); break; case IPT_TCP_RESET: - send_reset(skb, par->hooknum); + nf_send_reset(skb, par->hooknum); case IPT_ICMP_ECHOREPLY: /* Doesn't happen. */ break; diff --git a/net/ipv6/netfilter/ip6t_REJECT.c b/net/ipv6/netfilter/ip6t_REJECT.c index da00a2ecde55..544b0a9da1b5 100644 --- a/net/ipv6/netfilter/ip6t_REJECT.c +++ b/net/ipv6/netfilter/ip6t_REJECT.c @@ -23,181 +23,18 @@ #include #include #include -#include -#include #include -#include -#include -#include #include #include #include #include +#include + MODULE_AUTHOR("Yasuyuki KOZAKAI "); MODULE_DESCRIPTION("Xtables: packet \"rejection\" target for IPv6"); MODULE_LICENSE("GPL"); -/* Send RST reply */ -static void send_reset(struct net *net, struct sk_buff *oldskb, int hook) -{ - struct sk_buff *nskb; - struct tcphdr otcph, *tcph; - unsigned int otcplen, hh_len; - int tcphoff, needs_ack; - const struct ipv6hdr *oip6h = ipv6_hdr(oldskb); - struct ipv6hdr *ip6h; -#define DEFAULT_TOS_VALUE 0x0U - const __u8 tclass = DEFAULT_TOS_VALUE; - struct dst_entry *dst = NULL; - u8 proto; - __be16 frag_off; - struct flowi6 fl6; - - if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) || - (!(ipv6_addr_type(&oip6h->daddr) & IPV6_ADDR_UNICAST))) { - pr_debug("addr is not unicast.\n"); - return; - } - - proto = oip6h->nexthdr; - tcphoff = ipv6_skip_exthdr(oldskb, ((u8*)(oip6h+1) - oldskb->data), &proto, &frag_off); - - if ((tcphoff < 0) || (tcphoff > oldskb->len)) { - pr_debug("Cannot get TCP header.\n"); - return; - } - - otcplen = oldskb->len - tcphoff; - - /* IP header checks: fragment, too short. */ - if (proto != IPPROTO_TCP || otcplen < sizeof(struct tcphdr)) { - pr_debug("proto(%d) != IPPROTO_TCP, " - "or too short. otcplen = %d\n", - proto, otcplen); - return; - } - - if (skb_copy_bits(oldskb, tcphoff, &otcph, sizeof(struct tcphdr))) - BUG(); - - /* No RST for RST. */ - if (otcph.rst) { - pr_debug("RST is set\n"); - return; - } - - /* Check checksum. */ - if (nf_ip6_checksum(oldskb, hook, tcphoff, IPPROTO_TCP)) { - pr_debug("TCP checksum is invalid\n"); - return; - } - - memset(&fl6, 0, sizeof(fl6)); - fl6.flowi6_proto = IPPROTO_TCP; - fl6.saddr = oip6h->daddr; - fl6.daddr = oip6h->saddr; - fl6.fl6_sport = otcph.dest; - fl6.fl6_dport = otcph.source; - security_skb_classify_flow(oldskb, flowi6_to_flowi(&fl6)); - dst = ip6_route_output(net, NULL, &fl6); - if (dst == NULL || dst->error) { - dst_release(dst); - return; - } - dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0); - if (IS_ERR(dst)) - return; - - hh_len = (dst->dev->hard_header_len + 15)&~15; - nskb = alloc_skb(hh_len + 15 + dst->header_len + sizeof(struct ipv6hdr) - + sizeof(struct tcphdr) + dst->trailer_len, - GFP_ATOMIC); - - if (!nskb) { - net_dbg_ratelimited("cannot alloc skb\n"); - dst_release(dst); - return; - } - - skb_dst_set(nskb, dst); - - skb_reserve(nskb, hh_len + dst->header_len); - - skb_put(nskb, sizeof(struct ipv6hdr)); - skb_reset_network_header(nskb); - ip6h = ipv6_hdr(nskb); - ip6_flow_hdr(ip6h, tclass, 0); - ip6h->hop_limit = ip6_dst_hoplimit(dst); - ip6h->nexthdr = IPPROTO_TCP; - ip6h->saddr = oip6h->daddr; - ip6h->daddr = oip6h->saddr; - - skb_reset_transport_header(nskb); - tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); - /* Truncate to length (no data) */ - tcph->doff = sizeof(struct tcphdr)/4; - tcph->source = otcph.dest; - tcph->dest = otcph.source; - - if (otcph.ack) { - needs_ack = 0; - tcph->seq = otcph.ack_seq; - tcph->ack_seq = 0; - } else { - needs_ack = 1; - tcph->ack_seq = htonl(ntohl(otcph.seq) + otcph.syn + otcph.fin - + otcplen - (otcph.doff<<2)); - tcph->seq = 0; - } - - /* Reset flags */ - ((u_int8_t *)tcph)[13] = 0; - tcph->rst = 1; - tcph->ack = needs_ack; - tcph->window = 0; - tcph->urg_ptr = 0; - tcph->check = 0; - - /* Adjust TCP checksum */ - tcph->check = csum_ipv6_magic(&ipv6_hdr(nskb)->saddr, - &ipv6_hdr(nskb)->daddr, - sizeof(struct tcphdr), IPPROTO_TCP, - csum_partial(tcph, - sizeof(struct tcphdr), 0)); - - nf_ct_attach(nskb, oldskb); - -#ifdef CONFIG_BRIDGE_NETFILTER - /* If we use ip6_local_out for bridged traffic, the MAC source on - * the RST will be ours, instead of the destination's. This confuses - * some routers/firewalls, and they drop the packet. So we need to - * build the eth header using the original destination's MAC as the - * source, and send the RST packet directly. - */ - if (oldskb->nf_bridge) { - struct ethhdr *oeth = eth_hdr(oldskb); - nskb->dev = oldskb->nf_bridge->physindev; - nskb->protocol = htons(ETH_P_IPV6); - ip6h->payload_len = htons(sizeof(struct tcphdr)); - if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol), - oeth->h_source, oeth->h_dest, nskb->len) < 0) - return; - dev_queue_xmit(nskb); - } else -#endif - ip6_local_out(nskb); -} - -static inline void -send_unreach(struct net *net, struct sk_buff *skb_in, unsigned char code, - unsigned int hooknum) -{ - if (hooknum == NF_INET_LOCAL_OUT && skb_in->dev == NULL) - skb_in->dev = net->loopback_dev; - - icmpv6_send(skb_in, ICMPV6_DEST_UNREACH, code, 0); -} static unsigned int reject_tg6(struct sk_buff *skb, const struct xt_action_param *par) @@ -208,25 +45,25 @@ reject_tg6(struct sk_buff *skb, const struct xt_action_param *par) pr_debug("%s: medium point\n", __func__); switch (reject->with) { case IP6T_ICMP6_NO_ROUTE: - send_unreach(net, skb, ICMPV6_NOROUTE, par->hooknum); + nf_send_unreach6(net, skb, ICMPV6_NOROUTE, par->hooknum); break; case IP6T_ICMP6_ADM_PROHIBITED: - send_unreach(net, skb, ICMPV6_ADM_PROHIBITED, par->hooknum); + nf_send_unreach6(net, skb, ICMPV6_ADM_PROHIBITED, par->hooknum); break; case IP6T_ICMP6_NOT_NEIGHBOUR: - send_unreach(net, skb, ICMPV6_NOT_NEIGHBOUR, par->hooknum); + nf_send_unreach6(net, skb, ICMPV6_NOT_NEIGHBOUR, par->hooknum); break; case IP6T_ICMP6_ADDR_UNREACH: - send_unreach(net, skb, ICMPV6_ADDR_UNREACH, par->hooknum); + nf_send_unreach6(net, skb, ICMPV6_ADDR_UNREACH, par->hooknum); break; case IP6T_ICMP6_PORT_UNREACH: - send_unreach(net, skb, ICMPV6_PORT_UNREACH, par->hooknum); + nf_send_unreach6(net, skb, ICMPV6_PORT_UNREACH, par->hooknum); break; case IP6T_ICMP6_ECHOREPLY: /* Do nothing */ break; case IP6T_TCP_RESET: - send_reset(net, skb, par->hooknum); + nf_send_reset6(net, skb, par->hooknum); break; default: net_info_ratelimited("case %u not handled yet\n", reject->with); -- cgit v1.2.3 From fe47755852d1f299b55a6e6594bb6e082ac103d4 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Sun, 22 Dec 2013 18:54:31 +0800 Subject: net: Allow setting sock flow hash without a sock This patch adds sock_rps_record_flow_hash and sock_rps_reset_flow_hash which take a hash value as an argument and sets the sock_flow_table accordingly. This allows the table to be populated in cases where flow is being tracked outside of a sock structure. sock_rps_record_flow and sock_rps_reset_flow call this function where the hash is taken from sk_rxhash. Signed-off-by: Tom Herbert Signed-off-by: Zhi Yong Wu Signed-off-by: David S. Miller --- include/net/sock.h | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'include/net') diff --git a/include/net/sock.h b/include/net/sock.h index 2ef3c3eca47a..8ee90add69d2 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -820,30 +820,40 @@ static inline int sk_backlog_rcv(struct sock *sk, struct sk_buff *skb) return sk->sk_backlog_rcv(sk, skb); } -static inline void sock_rps_record_flow(const struct sock *sk) +static inline void sock_rps_record_flow_hash(__u32 hash) { #ifdef CONFIG_RPS struct rps_sock_flow_table *sock_flow_table; rcu_read_lock(); sock_flow_table = rcu_dereference(rps_sock_flow_table); - rps_record_sock_flow(sock_flow_table, sk->sk_rxhash); + rps_record_sock_flow(sock_flow_table, hash); rcu_read_unlock(); #endif } -static inline void sock_rps_reset_flow(const struct sock *sk) +static inline void sock_rps_reset_flow_hash(__u32 hash) { #ifdef CONFIG_RPS struct rps_sock_flow_table *sock_flow_table; rcu_read_lock(); sock_flow_table = rcu_dereference(rps_sock_flow_table); - rps_reset_sock_flow(sock_flow_table, sk->sk_rxhash); + rps_reset_sock_flow(sock_flow_table, hash); rcu_read_unlock(); #endif } +static inline void sock_rps_record_flow(const struct sock *sk) +{ + sock_rps_record_flow_hash(sk->sk_rxhash); +} + +static inline void sock_rps_reset_flow(const struct sock *sk) +{ + sock_rps_reset_flow_hash(sk->sk_rxhash); +} + static inline void sock_rps_save_rxhash(struct sock *sk, const struct sk_buff *skb) { -- cgit v1.2.3 From c9d8ca0454a28d0f835138d7294ede4fc6d95572 Mon Sep 17 00:00:00 2001 From: Zhi Yong Wu Date: Wed, 1 Jan 2014 04:31:01 +0800 Subject: net, rps: fix build failure when CONFIG_RPS isn't set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In file included from net/socket.c:99:0: include/net/sock.h: In function ‘sock_rps_record_flow’: include/net/sock.h:849:30: error: ‘const struct sock’ has no member named ‘sk_rxhash’ include/net/sock.h: In function ‘sock_rps_reset_flow’: include/net/sock.h:854:29: error: ‘const struct sock’ has no member named ‘sk_rxhash’ Reported-by: Fengguang Wu Signed-off-by: Zhi Yong Wu Signed-off-by: David S. Miller --- include/net/sock.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include/net') diff --git a/include/net/sock.h b/include/net/sock.h index 8ee90add69d2..bd716b6996ff 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -846,12 +846,16 @@ static inline void sock_rps_reset_flow_hash(__u32 hash) static inline void sock_rps_record_flow(const struct sock *sk) { +#ifdef CONFIG_RPS sock_rps_record_flow_hash(sk->sk_rxhash); +#endif } static inline void sock_rps_reset_flow(const struct sock *sk) { +#ifdef CONFIG_RPS sock_rps_reset_flow_hash(sk->sk_rxhash); +#endif } static inline void sock_rps_save_rxhash(struct sock *sk, -- cgit v1.2.3 From 3678a9d86324e457d0ff9d898747ee7e787f4bb8 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Mon, 30 Dec 2013 10:41:32 -0800 Subject: netlink: cleanup rntl_af_register The function __rtnl_af_register is never called outside this code, and the return value is always 0. Signed-off-by: David S. Miller --- include/net/rtnetlink.h | 3 +-- net/bridge/br_netlink.c | 5 +---- net/core/rtnetlink.c | 22 ++-------------------- net/ipv6/addrconf.c | 5 +---- 4 files changed, 5 insertions(+), 30 deletions(-) (limited to 'include/net') diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index bb13a182fba6..8fb42070a2c1 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h @@ -115,10 +115,9 @@ struct rtnl_af_ops { const struct nlattr *attr); }; -int __rtnl_af_register(struct rtnl_af_ops *ops); void __rtnl_af_unregister(struct rtnl_af_ops *ops); -int rtnl_af_register(struct rtnl_af_ops *ops); +void rtnl_af_register(struct rtnl_af_ops *ops); void rtnl_af_unregister(struct rtnl_af_ops *ops); struct net *rtnl_link_get_net(struct net *src_net, struct nlattr *tb[]); diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 6526ac89bbfa..e74b6d530cb6 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -481,9 +481,7 @@ int __init br_netlink_init(void) int err; br_mdb_init(); - err = rtnl_af_register(&br_af_ops); - if (err) - goto out; + rtnl_af_register(&br_af_ops); err = rtnl_link_register(&br_link_ops); if (err) @@ -493,7 +491,6 @@ int __init br_netlink_init(void) out_af: rtnl_af_unregister(&br_af_ops); -out: br_mdb_uninit(); return err; } diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index cf67144d3e3c..e6e7d582f901 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -402,35 +402,17 @@ static const struct rtnl_af_ops *rtnl_af_lookup(const int family) return NULL; } -/** - * __rtnl_af_register - Register rtnl_af_ops with rtnetlink. - * @ops: struct rtnl_af_ops * to register - * - * The caller must hold the rtnl_mutex. - * - * Returns 0 on success or a negative error code. - */ -int __rtnl_af_register(struct rtnl_af_ops *ops) -{ - list_add_tail(&ops->list, &rtnl_af_ops); - return 0; -} -EXPORT_SYMBOL_GPL(__rtnl_af_register); - /** * rtnl_af_register - Register rtnl_af_ops with rtnetlink. * @ops: struct rtnl_af_ops * to register * * Returns 0 on success or a negative error code. */ -int rtnl_af_register(struct rtnl_af_ops *ops) +void rtnl_af_register(struct rtnl_af_ops *ops) { - int err; - rtnl_lock(); - err = __rtnl_af_register(ops); + list_add_tail(&ops->list, &rtnl_af_ops); rtnl_unlock(); - return err; } EXPORT_SYMBOL_GPL(rtnl_af_register); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 9418ca375132..6c1634507ec2 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -5207,9 +5207,7 @@ int __init addrconf_init(void) addrconf_verify(0); - err = rtnl_af_register(&inet6_ops); - if (err < 0) - goto errout_af; + rtnl_af_register(&inet6_ops); err = __rtnl_register(PF_INET6, RTM_GETLINK, NULL, inet6_dump_ifinfo, NULL); @@ -5233,7 +5231,6 @@ int __init addrconf_init(void) return 0; errout: rtnl_af_unregister(&inet6_ops); -errout_af: unregister_netdevice_notifier(&ipv6_dev_notf); errlo: unregister_pernet_subsys(&addrconf_ops); -- cgit v1.2.3 From e82435341ff08769b70400b72a7a8efda83c5014 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Sun, 29 Dec 2013 14:03:31 -0800 Subject: ipv6: namespace cleanups Running 'make namespacecheck' shows: net/ipv6/route.o ipv6_route_table_template rt6_bind_peer net/ipv6/icmp.o icmpv6_route_lookup ipv6_icmp_table_template This addresses some of those warnings by: * make icmpv6_route_lookup static * move inline's out of ip6_route.h since only used into route.c * move rt6_bind_peer into route.c Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/net/ip6_route.h | 20 -------------------- include/net/ipv6.h | 4 ---- net/ipv6/icmp.c | 8 +++++--- net/ipv6/route.c | 46 ++++++++++++++++++++++++++++++---------------- 4 files changed, 35 insertions(+), 43 deletions(-) (limited to 'include/net') diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index c2626ce1f2ad..1fb6cddbd448 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -51,26 +51,6 @@ static inline unsigned int rt6_flags2srcprefs(int flags) return (flags >> 3) & 7; } -void rt6_bind_peer(struct rt6_info *rt, int create); - -static inline struct inet_peer *__rt6_get_peer(struct rt6_info *rt, int create) -{ - if (rt6_has_peer(rt)) - return rt6_peer_ptr(rt); - - rt6_bind_peer(rt, create); - return (rt6_has_peer(rt) ? rt6_peer_ptr(rt) : NULL); -} - -static inline struct inet_peer *rt6_get_peer(struct rt6_info *rt) -{ - return __rt6_get_peer(rt, 0); -} - -static inline struct inet_peer *rt6_get_peer_create(struct rt6_info *rt) -{ - return __rt6_get_peer(rt, 1); -} void ip6_route_input(struct sk_buff *skb); diff --git a/include/net/ipv6.h b/include/net/ipv6.h index e600b89811aa..12079c65ea3e 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -267,9 +267,6 @@ void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info); int icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, struct icmp6hdr *thdr, int len); -struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *skb, - struct sock *sk, struct flowi6 *fl6); - int ip6_ra_control(struct sock *sk, int sel); int ipv6_parse_hopopts(struct sk_buff *skb); @@ -839,7 +836,6 @@ static inline int snmp6_unregister_dev(struct inet6_dev *idev) { return 0; } #ifdef CONFIG_SYSCTL extern struct ctl_table ipv6_route_table_template[]; -extern struct ctl_table ipv6_icmp_table_template[]; struct ctl_table *ipv6_icmp_sysctl_init(struct net *net); struct ctl_table *ipv6_route_sysctl_init(struct net *net); diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index eef8d945b362..5d420095190f 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -315,8 +315,10 @@ static void mip6_addr_swap(struct sk_buff *skb) static inline void mip6_addr_swap(struct sk_buff *skb) {} #endif -struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *skb, - struct sock *sk, struct flowi6 *fl6) +static struct dst_entry *icmpv6_route_lookup(struct net *net, + struct sk_buff *skb, + struct sock *sk, + struct flowi6 *fl6) { struct dst_entry *dst, *dst2; struct flowi6 fl2; @@ -984,7 +986,7 @@ int icmpv6_err_convert(u8 type, u8 code, int *err) EXPORT_SYMBOL(icmpv6_err_convert); #ifdef CONFIG_SYSCTL -struct ctl_table ipv6_icmp_table_template[] = { +static struct ctl_table ipv6_icmp_table_template[] = { { .procname = "ratelimit", .data = &init_net.ipv6.sysctl.icmpv6_time, diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 89b2735cecf5..b379d88d796a 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -104,6 +104,36 @@ static struct rt6_info *rt6_get_route_info(struct net *net, const struct in6_addr *gwaddr, int ifindex); #endif +static void rt6_bind_peer(struct rt6_info *rt, int create) +{ + struct inet_peer_base *base; + struct inet_peer *peer; + + base = inetpeer_base_ptr(rt->_rt6i_peer); + if (!base) + return; + + peer = inet_getpeer_v6(base, &rt->rt6i_dst.addr, create); + if (peer) { + if (!rt6_set_peer(rt, peer)) + inet_putpeer(peer); + } +} + +static struct inet_peer *__rt6_get_peer(struct rt6_info *rt, int create) +{ + if (rt6_has_peer(rt)) + return rt6_peer_ptr(rt); + + rt6_bind_peer(rt, create); + return (rt6_has_peer(rt) ? rt6_peer_ptr(rt) : NULL); +} + +static struct inet_peer *rt6_get_peer_create(struct rt6_info *rt) +{ + return __rt6_get_peer(rt, 1); +} + static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old) { struct rt6_info *rt = (struct rt6_info *) dst; @@ -312,22 +342,6 @@ static void ip6_dst_destroy(struct dst_entry *dst) } } -void rt6_bind_peer(struct rt6_info *rt, int create) -{ - struct inet_peer_base *base; - struct inet_peer *peer; - - base = inetpeer_base_ptr(rt->_rt6i_peer); - if (!base) - return; - - peer = inet_getpeer_v6(base, &rt->rt6i_dst.addr, create); - if (peer) { - if (!rt6_set_peer(rt, peer)) - inet_putpeer(peer); - } -} - static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, int how) { -- cgit v1.2.3 From 0c3584d58913a72a6b28a976f7c0bfd2afb65237 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Fri, 27 Dec 2013 16:32:38 +0800 Subject: ipv6: remove prune parameter for fib6_clean_all since the prune parameter for fib6_clean_all always is 0, remove it. Signed-off-by: Li RongQing Acked-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/net/ip6_fib.h | 2 +- net/ipv6/ip6_fib.c | 6 +++--- net/ipv6/route.c | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include/net') diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index 2182525e4d74..aca0c2709fd6 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -282,7 +282,7 @@ struct fib6_node *fib6_locate(struct fib6_node *root, const struct in6_addr *saddr, int src_len); void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *arg), - int prune, void *arg); + void *arg); int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info); diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 5550a8113a6d..075602fc6b6a 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -1530,7 +1530,7 @@ static void fib6_clean_tree(struct net *net, struct fib6_node *root, } void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *arg), - int prune, void *arg) + void *arg) { struct fib6_table *table; struct hlist_head *head; @@ -1542,7 +1542,7 @@ void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *arg), hlist_for_each_entry_rcu(table, head, tb6_hlist) { write_lock_bh(&table->tb6_lock); fib6_clean_tree(net, &table->tb6_root, - func, prune, arg); + func, 0, arg); write_unlock_bh(&table->tb6_lock); } } @@ -1636,7 +1636,7 @@ void fib6_run_gc(unsigned long expires, struct net *net, bool force) gc_args.more = icmp6_dst_gc(); - fib6_clean_all(net, fib6_age, 0, NULL); + fib6_clean_all(net, fib6_age, NULL); now = jiffies; net->ipv6.ip6_rt_last_gc = now; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index b379d88d796a..266f110cb6f7 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2258,7 +2258,7 @@ void rt6_remove_prefsrc(struct inet6_ifaddr *ifp) .net = net, .addr = &ifp->addr, }; - fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni); + fib6_clean_all(net, fib6_remove_prefsrc, &adni); } struct arg_dev_net { @@ -2285,7 +2285,7 @@ void rt6_ifdown(struct net *net, struct net_device *dev) .net = net, }; - fib6_clean_all(net, fib6_ifdown, 0, &adn); + fib6_clean_all(net, fib6_ifdown, &adn); icmp6_clean_all(fib6_ifdown, &adn); } @@ -2340,7 +2340,7 @@ void rt6_mtu_change(struct net_device *dev, unsigned int mtu) .mtu = mtu, }; - fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg); + fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg); } static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { -- cgit v1.2.3 From 9c75f4029cf494ae810a54e34ede9803cff60c7c Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Tue, 31 Dec 2013 11:54:00 -0800 Subject: sched action: make local function static No need to export functions only used in one file. Signed-off-by: Stephen Hemminger Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/net/act_api.h | 3 --- net/sched/act_api.c | 8 +++----- 2 files changed, 3 insertions(+), 8 deletions(-) (limited to 'include/net') diff --git a/include/net/act_api.h b/include/net/act_api.h index 77d5d8156efc..d34e1f4d897b 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -105,10 +105,7 @@ struct tcf_common *tcf_hash_lookup(u32 index, struct tcf_hashinfo *hinfo); void tcf_hash_destroy(struct tcf_common *p, struct tcf_hashinfo *hinfo); int tcf_hash_release(struct tcf_common *p, int bind, struct tcf_hashinfo *hinfo); -int tcf_generic_walker(struct sk_buff *skb, struct netlink_callback *cb, - int type, struct tc_action *a); u32 tcf_hash_new_index(u32 *idx_gen, struct tcf_hashinfo *hinfo); -int tcf_hash_search(struct tc_action *a, u32 index); struct tcf_common *tcf_hash_check(u32 index, struct tc_action *a, int bind, struct tcf_hashinfo *hinfo); struct tcf_common *tcf_hash_create(u32 index, struct nlattr *est, diff --git a/net/sched/act_api.c b/net/sched/act_api.c index dce2b6ecdbd8..6f103fd76c17 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -141,8 +141,8 @@ nla_put_failure: return -EINVAL; } -int tcf_generic_walker(struct sk_buff *skb, struct netlink_callback *cb, - int type, struct tc_action *a) +static int tcf_generic_walker(struct sk_buff *skb, struct netlink_callback *cb, + int type, struct tc_action *a) { struct tcf_hashinfo *hinfo = a->ops->hinfo; @@ -155,7 +155,6 @@ int tcf_generic_walker(struct sk_buff *skb, struct netlink_callback *cb, return -EINVAL; } } -EXPORT_SYMBOL(tcf_generic_walker); struct tcf_common *tcf_hash_lookup(u32 index, struct tcf_hashinfo *hinfo) { @@ -187,7 +186,7 @@ u32 tcf_hash_new_index(u32 *idx_gen, struct tcf_hashinfo *hinfo) } EXPORT_SYMBOL(tcf_hash_new_index); -int tcf_hash_search(struct tc_action *a, u32 index) +static int tcf_hash_search(struct tc_action *a, u32 index) { struct tcf_hashinfo *hinfo = a->ops->hinfo; struct tcf_common *p = tcf_hash_lookup(index, hinfo); @@ -198,7 +197,6 @@ int tcf_hash_search(struct tc_action *a, u32 index) } return 0; } -EXPORT_SYMBOL(tcf_hash_search); struct tcf_common *tcf_hash_check(u32 index, struct tc_action *a, int bind, struct tcf_hashinfo *hinfo) -- cgit v1.2.3 From c454997e68eb013510ba128283ad3b4aefeff630 Mon Sep 17 00:00:00 2001 From: Fan Du Date: Fri, 3 Jan 2014 11:18:32 +0800 Subject: {pktgen, xfrm} Introduce xfrm_state_lookup_byspi for pktgen Introduce xfrm_state_lookup_byspi to find user specified by custom from "pgset spi xxx". Using this scheme, any flow regardless its saddr/daddr could be transform by SA specified with configurable spi. Signed-off-by: Fan Du Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 2 ++ net/core/pktgen.c | 22 +++++++++++++++------- net/xfrm/xfrm_state.c | 22 ++++++++++++++++++++++ 3 files changed, 39 insertions(+), 7 deletions(-) (limited to 'include/net') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index b7635ef4d436..cd7c46ff6f1f 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1421,6 +1421,8 @@ struct xfrm_state *xfrm_stateonly_find(struct net *net, u32 mark, xfrm_address_t *saddr, unsigned short family, u8 mode, u8 proto, u32 reqid); +struct xfrm_state *xfrm_state_lookup_byspi(struct net *net, __be32 spi, + unsigned short family); int xfrm_state_check_expire(struct xfrm_state *x); void xfrm_state_insert(struct xfrm_state *x); int xfrm_state_add(struct xfrm_state *x); diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 628f7c572c6e..b553c36ea0ca 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -2247,13 +2247,21 @@ static void get_ipsec_sa(struct pktgen_dev *pkt_dev, int flow) struct xfrm_state *x = pkt_dev->flows[flow].x; struct pktgen_net *pn = net_generic(dev_net(pkt_dev->odev), pg_net_id); if (!x) { - /*slow path: we dont already have xfrm_state*/ - x = xfrm_stateonly_find(pn->net, DUMMY_MARK, - (xfrm_address_t *)&pkt_dev->cur_daddr, - (xfrm_address_t *)&pkt_dev->cur_saddr, - AF_INET, - pkt_dev->ipsmode, - pkt_dev->ipsproto, 0); + + if (pkt_dev->spi) { + /* We need as quick as possible to find the right SA + * Searching with minimum criteria to archieve this. + */ + x = xfrm_state_lookup_byspi(pn->net, htonl(pkt_dev->spi), AF_INET); + } else { + /* slow path: we dont already have xfrm_state */ + x = xfrm_stateonly_find(pn->net, DUMMY_MARK, + (xfrm_address_t *)&pkt_dev->cur_daddr, + (xfrm_address_t *)&pkt_dev->cur_saddr, + AF_INET, + pkt_dev->ipsmode, + pkt_dev->ipsproto, 0); + } if (x) { pkt_dev->flows[flow].x = x; set_pkt_overhead(pkt_dev); diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 300744094ad8..62181486ead8 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -915,6 +915,28 @@ xfrm_stateonly_find(struct net *net, u32 mark, } EXPORT_SYMBOL(xfrm_stateonly_find); +struct xfrm_state *xfrm_state_lookup_byspi(struct net *net, __be32 spi, + unsigned short family) +{ + struct xfrm_state *x; + struct xfrm_state_walk *w; + + spin_lock_bh(&net->xfrm.xfrm_state_lock); + list_for_each_entry(w, &net->xfrm.state_all, all) { + x = container_of(w, struct xfrm_state, km); + if (x->props.family != family || + x->id.spi != spi) + continue; + + spin_unlock_bh(&net->xfrm.xfrm_state_lock); + xfrm_state_hold(x); + return x; + } + spin_unlock_bh(&net->xfrm.xfrm_state_lock); + return NULL; +} +EXPORT_SYMBOL(xfrm_state_lookup_byspi); + static void __xfrm_state_insert(struct xfrm_state *x) { struct net *net = xs_net(x); -- cgit v1.2.3 From dcd93ed4cd1669b2c1510e801fe5f1132390761c Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Mon, 30 Dec 2013 17:16:08 -0800 Subject: netfilter: nf_conntrack: remove dead code The following code is not used in current upstream code. Some of this seems to be old hooks, other might be used by some out of tree module (which I don't care about breaking), and the need_ipv4_conntrack was used by old NAT code but no longer called. Signed-off-by: Stephen Hemminger Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/ipv4/nf_conntrack_ipv4.h | 2 -- include/net/netfilter/nf_conntrack_l3proto.h | 1 - net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 6 ------ net/netfilter/nf_conntrack_core.c | 15 --------------- net/netfilter/nf_conntrack_proto.c | 6 ------ 5 files changed, 30 deletions(-) (limited to 'include/net') diff --git a/include/net/netfilter/ipv4/nf_conntrack_ipv4.h b/include/net/netfilter/ipv4/nf_conntrack_ipv4.h index 6c3d12e2949f..981c327374da 100644 --- a/include/net/netfilter/ipv4/nf_conntrack_ipv4.h +++ b/include/net/netfilter/ipv4/nf_conntrack_ipv4.h @@ -19,6 +19,4 @@ extern struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp; int nf_conntrack_ipv4_compat_init(void); void nf_conntrack_ipv4_compat_fini(void); -void need_ipv4_conntrack(void); - #endif /*_NF_CONNTRACK_IPV4_H*/ diff --git a/include/net/netfilter/nf_conntrack_l3proto.h b/include/net/netfilter/nf_conntrack_l3proto.h index 3efab704b7eb..adc1fa3dd7ab 100644 --- a/include/net/netfilter/nf_conntrack_l3proto.h +++ b/include/net/netfilter/nf_conntrack_l3proto.h @@ -87,7 +87,6 @@ int nf_ct_l3proto_register(struct nf_conntrack_l3proto *proto); void nf_ct_l3proto_unregister(struct nf_conntrack_l3proto *proto); struct nf_conntrack_l3proto *nf_ct_l3proto_find_get(u_int16_t l3proto); -void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p); /* Existing built-in protocols */ extern struct nf_conntrack_l3proto nf_conntrack_l3proto_generic; diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index ecd8bec411c9..8127dc802865 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -548,9 +548,3 @@ static void __exit nf_conntrack_l3proto_ipv4_fini(void) module_init(nf_conntrack_l3proto_ipv4_init); module_exit(nf_conntrack_l3proto_ipv4_fini); - -void need_ipv4_conntrack(void) -{ - return; -} -EXPORT_SYMBOL_GPL(need_ipv4_conntrack); diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 43549eb7a7be..8824ed0ccc9c 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -60,12 +60,6 @@ int (*nfnetlink_parse_nat_setup_hook)(struct nf_conn *ct, const struct nlattr *attr) __read_mostly; EXPORT_SYMBOL_GPL(nfnetlink_parse_nat_setup_hook); -int (*nf_nat_seq_adjust_hook)(struct sk_buff *skb, - struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - unsigned int protoff); -EXPORT_SYMBOL_GPL(nf_nat_seq_adjust_hook); - DEFINE_SPINLOCK(nf_conntrack_lock); EXPORT_SYMBOL_GPL(nf_conntrack_lock); @@ -361,15 +355,6 @@ begin: return NULL; } -struct nf_conntrack_tuple_hash * -__nf_conntrack_find(struct net *net, u16 zone, - const struct nf_conntrack_tuple *tuple) -{ - return ____nf_conntrack_find(net, zone, tuple, - hash_conntrack_raw(tuple, zone)); -} -EXPORT_SYMBOL_GPL(__nf_conntrack_find); - /* Find a connection corresponding to a tuple. */ static struct nf_conntrack_tuple_hash * __nf_conntrack_find_get(struct net *net, u16 zone, diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index ce3004156eeb..b65d5864b6d9 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -92,12 +92,6 @@ nf_ct_l3proto_find_get(u_int16_t l3proto) } EXPORT_SYMBOL_GPL(nf_ct_l3proto_find_get); -void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p) -{ - module_put(p->me); -} -EXPORT_SYMBOL_GPL(nf_ct_l3proto_put); - int nf_ct_l3proto_try_module_get(unsigned short l3proto) { -- cgit v1.2.3 From fe1217c4f3f7d7cbf8efdd8dd5fdc7204a1d65a8 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sun, 29 Dec 2013 18:27:10 +0100 Subject: net: net_cls: move cgroupfs classid handling into core Zefan Li requested [1] to perform the following cleanup/refactoring: - Split cgroupfs classid handling into net core to better express a possible more generic use. - Disable module support for cgroupfs bits as the majority of other cgroupfs subsystems do not have that, and seems to be not wished from cgroup side. Zefan probably might want to follow-up for netprio later on. - By this, code can be further reduced which previously took care of functionality built when compiled as module. cgroupfs bits are being placed under net/core/netclassid_cgroup.c, so that we are consistent with {netclassid,netprio}_cgroup naming that is under net/core/ as suggested by Zefan. No change in functionality, but only code refactoring that is being done here. [1] http://patchwork.ozlabs.org/patch/304825/ Suggested-by: Li Zefan Signed-off-by: Daniel Borkmann Cc: Zefan Li Cc: Thomas Graf Cc: cgroups@vger.kernel.org Acked-by: Li Zefan Signed-off-by: Pablo Neira Ayuso --- include/linux/cgroup_subsys.h | 2 +- include/net/cls_cgroup.h | 40 +++++--------- net/Kconfig | 7 +++ net/core/Makefile | 1 + net/core/netclassid_cgroup.c | 120 ++++++++++++++++++++++++++++++++++++++++++ net/core/sock.c | 12 ----- net/sched/Kconfig | 1 + net/sched/cls_cgroup.c | 111 +------------------------------------- 8 files changed, 143 insertions(+), 151 deletions(-) create mode 100644 net/core/netclassid_cgroup.c (limited to 'include/net') diff --git a/include/linux/cgroup_subsys.h b/include/linux/cgroup_subsys.h index b613ffd402d1..58bf94de4b8e 100644 --- a/include/linux/cgroup_subsys.h +++ b/include/linux/cgroup_subsys.h @@ -31,7 +31,7 @@ SUBSYS(devices) SUBSYS(freezer) #endif -#if IS_SUBSYS_ENABLED(CONFIG_NET_CLS_CGROUP) +#if IS_SUBSYS_ENABLED(CONFIG_CGROUP_NET_CLASSID) SUBSYS(net_cls) #endif diff --git a/include/net/cls_cgroup.h b/include/net/cls_cgroup.h index 33d03b648646..9cf2d5ef38d9 100644 --- a/include/net/cls_cgroup.h +++ b/include/net/cls_cgroup.h @@ -16,17 +16,16 @@ #include #include #include +#include -#if IS_ENABLED(CONFIG_NET_CLS_CGROUP) -struct cgroup_cls_state -{ +#ifdef CONFIG_CGROUP_NET_CLASSID +struct cgroup_cls_state { struct cgroup_subsys_state css; u32 classid; }; -void sock_update_classid(struct sock *sk); +struct cgroup_cls_state *task_cls_state(struct task_struct *p); -#if IS_BUILTIN(CONFIG_NET_CLS_CGROUP) static inline u32 task_cls_classid(struct task_struct *p) { u32 classid; @@ -41,33 +40,18 @@ static inline u32 task_cls_classid(struct task_struct *p) return classid; } -#elif IS_MODULE(CONFIG_NET_CLS_CGROUP) -static inline u32 task_cls_classid(struct task_struct *p) -{ - struct cgroup_subsys_state *css; - u32 classid = 0; - - if (in_interrupt()) - return 0; - - rcu_read_lock(); - css = task_css(p, net_cls_subsys_id); - if (css) - classid = container_of(css, - struct cgroup_cls_state, css)->classid; - rcu_read_unlock(); - return classid; -} -#endif -#else /* !CGROUP_NET_CLS_CGROUP */ static inline void sock_update_classid(struct sock *sk) { -} + u32 classid; -static inline u32 task_cls_classid(struct task_struct *p) + classid = task_cls_classid(current); + if (classid != sk->sk_classid) + sk->sk_classid = classid; +} +#else /* !CONFIG_CGROUP_NET_CLASSID */ +static inline void sock_update_classid(struct sock *sk) { - return 0; } -#endif /* CGROUP_NET_CLS_CGROUP */ +#endif /* CONFIG_CGROUP_NET_CLASSID */ #endif /* _NET_CLS_CGROUP_H */ diff --git a/net/Kconfig b/net/Kconfig index d334678c0bd8..7da10b830d70 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -245,6 +245,13 @@ config NETPRIO_CGROUP Cgroup subsystem for use in assigning processes to network priorities on a per-interface basis +config CGROUP_NET_CLASSID + boolean "Network classid cgroup" + depends on CGROUPS + ---help--- + Cgroup subsystem for use as general purpose socket classid marker that is + being used in cls_cgroup and for netfilter matching. + config NET_RX_BUSY_POLL boolean default y diff --git a/net/core/Makefile b/net/core/Makefile index b33b996f5dd6..4839a2796964 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -22,3 +22,4 @@ obj-$(CONFIG_TRACEPOINTS) += net-traces.o obj-$(CONFIG_NET_DROP_MONITOR) += drop_monitor.o obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += timestamping.o obj-$(CONFIG_NETPRIO_CGROUP) += netprio_cgroup.o +obj-$(CONFIG_CGROUP_NET_CLASSID) += netclassid_cgroup.o diff --git a/net/core/netclassid_cgroup.c b/net/core/netclassid_cgroup.c new file mode 100644 index 000000000000..719efd541668 --- /dev/null +++ b/net/core/netclassid_cgroup.c @@ -0,0 +1,120 @@ +/* + * net/core/netclassid_cgroup.c Classid Cgroupfs Handling + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Thomas Graf + */ + +#include +#include +#include +#include +#include +#include + +static inline struct cgroup_cls_state *css_cls_state(struct cgroup_subsys_state *css) +{ + return css ? container_of(css, struct cgroup_cls_state, css) : NULL; +} + +struct cgroup_cls_state *task_cls_state(struct task_struct *p) +{ + return css_cls_state(task_css(p, net_cls_subsys_id)); +} +EXPORT_SYMBOL_GPL(task_cls_state); + +static struct cgroup_subsys_state * +cgrp_css_alloc(struct cgroup_subsys_state *parent_css) +{ + struct cgroup_cls_state *cs; + + cs = kzalloc(sizeof(*cs), GFP_KERNEL); + if (!cs) + return ERR_PTR(-ENOMEM); + + return &cs->css; +} + +static int cgrp_css_online(struct cgroup_subsys_state *css) +{ + struct cgroup_cls_state *cs = css_cls_state(css); + struct cgroup_cls_state *parent = css_cls_state(css_parent(css)); + + if (parent) + cs->classid = parent->classid; + + return 0; +} + +static void cgrp_css_free(struct cgroup_subsys_state *css) +{ + kfree(css_cls_state(css)); +} + +static int update_classid(const void *v, struct file *file, unsigned n) +{ + int err; + struct socket *sock = sock_from_file(file, &err); + + if (sock) + sock->sk->sk_classid = (u32)(unsigned long)v; + + return 0; +} + +static void cgrp_attach(struct cgroup_subsys_state *css, + struct cgroup_taskset *tset) +{ + struct cgroup_cls_state *cs = css_cls_state(css); + void *v = (void *)(unsigned long)cs->classid; + struct task_struct *p; + + cgroup_taskset_for_each(p, css, tset) { + task_lock(p); + iterate_fd(p->files, 0, update_classid, v); + task_unlock(p); + } +} + +static u64 read_classid(struct cgroup_subsys_state *css, struct cftype *cft) +{ + return css_cls_state(css)->classid; +} + +static int write_classid(struct cgroup_subsys_state *css, struct cftype *cft, + u64 value) +{ + css_cls_state(css)->classid = (u32) value; + + return 0; +} + +static struct cftype ss_files[] = { + { + .name = "classid", + .read_u64 = read_classid, + .write_u64 = write_classid, + }, + { } /* terminate */ +}; + +struct cgroup_subsys net_cls_subsys = { + .name = "net_cls", + .css_alloc = cgrp_css_alloc, + .css_online = cgrp_css_online, + .css_free = cgrp_css_free, + .attach = cgrp_attach, + .subsys_id = net_cls_subsys_id, + .base_cftypes = ss_files, + .module = THIS_MODULE, +}; + +static int __init init_netclassid_cgroup(void) +{ + return cgroup_load_subsys(&net_cls_subsys); +} +__initcall(init_netclassid_cgroup); diff --git a/net/core/sock.c b/net/core/sock.c index ab20ed9b0f31..3f150729fb15 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1308,18 +1308,6 @@ static void sk_prot_free(struct proto *prot, struct sock *sk) module_put(owner); } -#if IS_ENABLED(CONFIG_NET_CLS_CGROUP) -void sock_update_classid(struct sock *sk) -{ - u32 classid; - - classid = task_cls_classid(current); - if (classid != sk->sk_classid) - sk->sk_classid = classid; -} -EXPORT_SYMBOL(sock_update_classid); -#endif - #if IS_ENABLED(CONFIG_NETPRIO_CGROUP) void sock_update_netprioidx(struct sock *sk) { diff --git a/net/sched/Kconfig b/net/sched/Kconfig index ad1f1d819203..f711a471d0b7 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -435,6 +435,7 @@ config NET_CLS_FLOW config NET_CLS_CGROUP tristate "Control Group Classifier" select NET_CLS + select CGROUP_NET_CLASSID depends on CGROUPS ---help--- Say Y here if you want to classify packets based on the control diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index 16006c92c3fd..838fa40abad1 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c @@ -11,109 +11,13 @@ #include #include -#include -#include -#include #include -#include #include -#include #include #include #include #include -static inline struct cgroup_cls_state *css_cls_state(struct cgroup_subsys_state *css) -{ - return css ? container_of(css, struct cgroup_cls_state, css) : NULL; -} - -static inline struct cgroup_cls_state *task_cls_state(struct task_struct *p) -{ - return css_cls_state(task_css(p, net_cls_subsys_id)); -} - -static struct cgroup_subsys_state * -cgrp_css_alloc(struct cgroup_subsys_state *parent_css) -{ - struct cgroup_cls_state *cs; - - cs = kzalloc(sizeof(*cs), GFP_KERNEL); - if (!cs) - return ERR_PTR(-ENOMEM); - return &cs->css; -} - -static int cgrp_css_online(struct cgroup_subsys_state *css) -{ - struct cgroup_cls_state *cs = css_cls_state(css); - struct cgroup_cls_state *parent = css_cls_state(css_parent(css)); - - if (parent) - cs->classid = parent->classid; - return 0; -} - -static void cgrp_css_free(struct cgroup_subsys_state *css) -{ - kfree(css_cls_state(css)); -} - -static int update_classid(const void *v, struct file *file, unsigned n) -{ - int err; - struct socket *sock = sock_from_file(file, &err); - if (sock) - sock->sk->sk_classid = (u32)(unsigned long)v; - return 0; -} - -static void cgrp_attach(struct cgroup_subsys_state *css, - struct cgroup_taskset *tset) -{ - struct task_struct *p; - struct cgroup_cls_state *cs = css_cls_state(css); - void *v = (void *)(unsigned long)cs->classid; - - cgroup_taskset_for_each(p, css, tset) { - task_lock(p); - iterate_fd(p->files, 0, update_classid, v); - task_unlock(p); - } -} - -static u64 read_classid(struct cgroup_subsys_state *css, struct cftype *cft) -{ - return css_cls_state(css)->classid; -} - -static int write_classid(struct cgroup_subsys_state *css, struct cftype *cft, - u64 value) -{ - css_cls_state(css)->classid = (u32) value; - return 0; -} - -static struct cftype ss_files[] = { - { - .name = "classid", - .read_u64 = read_classid, - .write_u64 = write_classid, - }, - { } /* terminate */ -}; - -struct cgroup_subsys net_cls_subsys = { - .name = "net_cls", - .css_alloc = cgrp_css_alloc, - .css_online = cgrp_css_online, - .css_free = cgrp_css_free, - .attach = cgrp_attach, - .subsys_id = net_cls_subsys_id, - .base_cftypes = ss_files, - .module = THIS_MODULE, -}; - struct cls_cgroup_head { u32 handle; struct tcf_exts exts; @@ -309,25 +213,12 @@ static struct tcf_proto_ops cls_cgroup_ops __read_mostly = { static int __init init_cgroup_cls(void) { - int ret; - - ret = cgroup_load_subsys(&net_cls_subsys); - if (ret) - goto out; - - ret = register_tcf_proto_ops(&cls_cgroup_ops); - if (ret) - cgroup_unload_subsys(&net_cls_subsys); - -out: - return ret; + return register_tcf_proto_ops(&cls_cgroup_ops); } static void __exit exit_cgroup_cls(void) { unregister_tcf_proto_ops(&cls_cgroup_ops); - - cgroup_unload_subsys(&net_cls_subsys); } module_init(init_cgroup_cls); -- cgit v1.2.3 From 86f8515f9721fa171483f0fe0391968fbb949cc9 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sun, 29 Dec 2013 17:27:11 +0100 Subject: net: netprio: rename config to be more consistent with cgroup configs While we're at it and introduced CGROUP_NET_CLASSID, lets also make NETPRIO_CGROUP more consistent with the rest of cgroups and rename it into CONFIG_CGROUP_NET_PRIO so that for networking, we now have CONFIG_CGROUP_NET_{PRIO,CLASSID}. This not only makes the CONFIG option consistent among networking cgroups, but also among cgroups CONFIG conventions in general as the vast majority has a prefix of CONFIG_CGROUP_. Signed-off-by: Daniel Borkmann Cc: Zefan Li Cc: cgroups@vger.kernel.org Acked-by: Li Zefan Signed-off-by: Pablo Neira Ayuso --- include/linux/cgroup_subsys.h | 2 +- include/linux/netdevice.h | 2 +- include/net/netprio_cgroup.h | 18 ++++++------------ include/net/sock.h | 2 +- net/Kconfig | 4 ++-- net/core/Makefile | 2 +- net/core/dev.c | 2 +- net/core/sock.c | 2 +- 8 files changed, 14 insertions(+), 20 deletions(-) (limited to 'include/net') diff --git a/include/linux/cgroup_subsys.h b/include/linux/cgroup_subsys.h index 58bf94de4b8e..7b99d717411d 100644 --- a/include/linux/cgroup_subsys.h +++ b/include/linux/cgroup_subsys.h @@ -43,7 +43,7 @@ SUBSYS(blkio) SUBSYS(perf) #endif -#if IS_SUBSYS_ENABLED(CONFIG_NETPRIO_CGROUP) +#if IS_SUBSYS_ENABLED(CONFIG_CGROUP_NET_PRIO) SUBSYS(net_prio) #endif diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 5260d2eae2e6..45cf68194aa8 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1444,7 +1444,7 @@ struct net_device { /* max exchange id for FCoE LRO by ddp */ unsigned int fcoe_ddp_xid; #endif -#if IS_ENABLED(CONFIG_NETPRIO_CGROUP) +#if IS_ENABLED(CONFIG_CGROUP_NET_PRIO) struct netprio_map __rcu *priomap; #endif /* phy device may attach itself for hardware timestamping */ diff --git a/include/net/netprio_cgroup.h b/include/net/netprio_cgroup.h index 099d02782e22..dafc09f0fdbc 100644 --- a/include/net/netprio_cgroup.h +++ b/include/net/netprio_cgroup.h @@ -13,12 +13,12 @@ #ifndef _NETPRIO_CGROUP_H #define _NETPRIO_CGROUP_H + #include #include #include - -#if IS_ENABLED(CONFIG_NETPRIO_CGROUP) +#if IS_ENABLED(CONFIG_CGROUP_NET_PRIO) struct netprio_map { struct rcu_head rcu; u32 priomap_len; @@ -27,8 +27,7 @@ struct netprio_map { void sock_update_netprioidx(struct sock *sk); -#if IS_BUILTIN(CONFIG_NETPRIO_CGROUP) - +#if IS_BUILTIN(CONFIG_CGROUP_NET_PRIO) static inline u32 task_netprioidx(struct task_struct *p) { struct cgroup_subsys_state *css; @@ -40,9 +39,7 @@ static inline u32 task_netprioidx(struct task_struct *p) rcu_read_unlock(); return idx; } - -#elif IS_MODULE(CONFIG_NETPRIO_CGROUP) - +#elif IS_MODULE(CONFIG_CGROUP_NET_PRIO) static inline u32 task_netprioidx(struct task_struct *p) { struct cgroup_subsys_state *css; @@ -56,9 +53,7 @@ static inline u32 task_netprioidx(struct task_struct *p) return idx; } #endif - -#else /* !CONFIG_NETPRIO_CGROUP */ - +#else /* !CONFIG_CGROUP_NET_PRIO */ static inline u32 task_netprioidx(struct task_struct *p) { return 0; @@ -66,6 +61,5 @@ static inline u32 task_netprioidx(struct task_struct *p) #define sock_update_netprioidx(sk) -#endif /* CONFIG_NETPRIO_CGROUP */ - +#endif /* CONFIG_CGROUP_NET_PRIO */ #endif /* _NET_CLS_CGROUP_H */ diff --git a/include/net/sock.h b/include/net/sock.h index 2ef3c3eca47a..ef5e2be6eaf3 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -395,7 +395,7 @@ struct sock { unsigned short sk_ack_backlog; unsigned short sk_max_ack_backlog; __u32 sk_priority; -#if IS_ENABLED(CONFIG_NETPRIO_CGROUP) +#if IS_ENABLED(CONFIG_CGROUP_NET_PRIO) __u32 sk_cgrp_prioidx; #endif struct pid *sk_peer_pid; diff --git a/net/Kconfig b/net/Kconfig index 7da10b830d70..e411046a62e3 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -238,12 +238,12 @@ config XPS depends on SMP default y -config NETPRIO_CGROUP +config CGROUP_NET_PRIO tristate "Network priority cgroup" depends on CGROUPS ---help--- Cgroup subsystem for use in assigning processes to network priorities on - a per-interface basis + a per-interface basis. config CGROUP_NET_CLASSID boolean "Network classid cgroup" diff --git a/net/core/Makefile b/net/core/Makefile index 4839a2796964..9628c20acff6 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -21,5 +21,5 @@ obj-$(CONFIG_FIB_RULES) += fib_rules.o obj-$(CONFIG_TRACEPOINTS) += net-traces.o obj-$(CONFIG_NET_DROP_MONITOR) += drop_monitor.o obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += timestamping.o -obj-$(CONFIG_NETPRIO_CGROUP) += netprio_cgroup.o +obj-$(CONFIG_CGROUP_NET_PRIO) += netprio_cgroup.o obj-$(CONFIG_CGROUP_NET_CLASSID) += netclassid_cgroup.o diff --git a/net/core/dev.c b/net/core/dev.c index c95d664b2b42..888a79b2b8b9 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2747,7 +2747,7 @@ static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q, return rc; } -#if IS_ENABLED(CONFIG_NETPRIO_CGROUP) +#if IS_ENABLED(CONFIG_CGROUP_NET_PRIO) static void skb_update_prio(struct sk_buff *skb) { struct netprio_map *map = rcu_dereference_bh(skb->dev->priomap); diff --git a/net/core/sock.c b/net/core/sock.c index 3f150729fb15..a29735c9a05d 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1308,7 +1308,7 @@ static void sk_prot_free(struct proto *prot, struct sock *sk) module_put(owner); } -#if IS_ENABLED(CONFIG_NETPRIO_CGROUP) +#if IS_ENABLED(CONFIG_CGROUP_NET_PRIO) void sock_update_netprioidx(struct sock *sk) { if (in_interrupt()) -- cgit v1.2.3 From 7d442fab0a6777fd7612cfcada32ea859553d370 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Thu, 2 Jan 2014 11:48:26 -0800 Subject: ipv4: Cache dst in tunnels Avoid doing a route lookup on every packet being tunneled. In ip_tunnel.c cache the route returned from ip_route_output if the tunnel is "connected" so that all the rouitng parameters are taken from tunnel parms for a packet. Specifically, not NBMA tunnel and tos is from tunnel parms (not inner packet). Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/net/ip_tunnels.h | 3 ++ net/ipv4/ip_tunnel.c | 113 +++++++++++++++++++++++++++++++++++++---------- 2 files changed, 92 insertions(+), 24 deletions(-) (limited to 'include/net') diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index 732f8c6ae975..bde50fc5b4f0 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -54,6 +54,9 @@ struct ip_tunnel { int hlen; /* Precalculated header length */ int mlink; + struct dst_entry __rcu *dst_cache; + spinlock_t dst_lock; + struct ip_tunnel_parm parms; /* for SIT */ diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 90ff9570d7d4..27d756f8f870 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -68,6 +68,54 @@ static unsigned int ip_tunnel_hash(struct ip_tunnel_net *itn, IP_TNL_HASH_BITS); } +static inline void __tunnel_dst_set(struct ip_tunnel *t, struct dst_entry *dst) +{ + struct dst_entry *old_dst; + + if (dst && (dst->flags & DST_NOCACHE)) + dst = NULL; + + spin_lock_bh(&t->dst_lock); + old_dst = rcu_dereference_raw(t->dst_cache); + rcu_assign_pointer(t->dst_cache, dst); + dst_release(old_dst); + spin_unlock_bh(&t->dst_lock); +} + +static inline void tunnel_dst_set(struct ip_tunnel *t, struct dst_entry *dst) +{ + __tunnel_dst_set(t, dst); +} + +static inline void tunnel_dst_reset(struct ip_tunnel *t) +{ + tunnel_dst_set(t, NULL); +} + +static inline struct dst_entry *tunnel_dst_get(struct ip_tunnel *t) +{ + struct dst_entry *dst; + + rcu_read_lock(); + dst = rcu_dereference(t->dst_cache); + if (dst) + dst_hold(dst); + rcu_read_unlock(); + return dst; +} + +struct dst_entry *tunnel_dst_check(struct ip_tunnel *t, u32 cookie) +{ + struct dst_entry *dst = tunnel_dst_get(t); + + if (dst && dst->obsolete && dst->ops->check(dst, cookie) == NULL) { + tunnel_dst_reset(t); + return NULL; + } + + return dst; +} + /* Often modified stats are per cpu, other are shared (netdev->stats) */ struct rtnl_link_stats64 *ip_tunnel_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *tot) @@ -318,11 +366,10 @@ failed: return ERR_PTR(err); } -static inline struct rtable *ip_route_output_tunnel(struct net *net, - struct flowi4 *fl4, - int proto, - __be32 daddr, __be32 saddr, - __be32 key, __u8 tos, int oif) +static inline void init_tunnel_flow(struct flowi4 *fl4, + int proto, + __be32 daddr, __be32 saddr, + __be32 key, __u8 tos, int oif) { memset(fl4, 0, sizeof(*fl4)); fl4->flowi4_oif = oif; @@ -331,7 +378,6 @@ static inline struct rtable *ip_route_output_tunnel(struct net *net, fl4->flowi4_tos = tos; fl4->flowi4_proto = proto; fl4->fl4_gre_key = key; - return ip_route_output_key(net, fl4); } static int ip_tunnel_bind_dev(struct net_device *dev) @@ -350,14 +396,14 @@ static int ip_tunnel_bind_dev(struct net_device *dev) struct flowi4 fl4; struct rtable *rt; - rt = ip_route_output_tunnel(tunnel->net, &fl4, - tunnel->parms.iph.protocol, - iph->daddr, iph->saddr, - tunnel->parms.o_key, - RT_TOS(iph->tos), - tunnel->parms.link); + init_tunnel_flow(&fl4, iph->protocol, iph->daddr, + iph->saddr, tunnel->parms.o_key, + RT_TOS(iph->tos), tunnel->parms.link); + rt = ip_route_output_key(tunnel->net, &fl4); + if (!IS_ERR(rt)) { tdev = rt->dst.dev; + tunnel_dst_set(tunnel, dst_clone(&rt->dst)); ip_rt_put(rt); } if (dev->type != ARPHRD_ETHER) @@ -528,10 +574,11 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi4 fl4; u8 tos, ttl; __be16 df; - struct rtable *rt; /* Route to the other host */ + struct rtable *rt = NULL; /* Route to the other host */ unsigned int max_headroom; /* The extra header space needed */ __be32 dst; int err; + bool connected = true; inner_iph = (const struct iphdr *)skb_inner_network_header(skb); @@ -581,27 +628,39 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, #endif else goto tx_error; + + connected = false; } tos = tnl_params->tos; if (tos & 0x1) { tos &= ~0x1; - if (skb->protocol == htons(ETH_P_IP)) + if (skb->protocol == htons(ETH_P_IP)) { tos = inner_iph->tos; - else if (skb->protocol == htons(ETH_P_IPV6)) + connected = false; + } else if (skb->protocol == htons(ETH_P_IPV6)) { tos = ipv6_get_dsfield((const struct ipv6hdr *)inner_iph); + connected = false; + } } - rt = ip_route_output_tunnel(tunnel->net, &fl4, - protocol, - dst, tnl_params->saddr, - tunnel->parms.o_key, - RT_TOS(tos), - tunnel->parms.link); - if (IS_ERR(rt)) { - dev->stats.tx_carrier_errors++; - goto tx_error; + init_tunnel_flow(&fl4, protocol, dst, tnl_params->saddr, + tunnel->parms.o_key, RT_TOS(tos), tunnel->parms.link); + + if (connected) + rt = (struct rtable *)tunnel_dst_check(tunnel, 0); + + if (!rt) { + rt = ip_route_output_key(tunnel->net, &fl4); + + if (IS_ERR(rt)) { + dev->stats.tx_carrier_errors++; + goto tx_error; + } + if (connected) + tunnel_dst_set(tunnel, dst_clone(&rt->dst)); } + if (rt->dst.dev == dev) { ip_rt_put(rt); dev->stats.collisions++; @@ -696,6 +755,7 @@ static void ip_tunnel_update(struct ip_tunnel_net *itn, if (set_mtu) dev->mtu = mtu; } + tunnel_dst_reset(t); netdev_state_change(dev); } @@ -1001,6 +1061,9 @@ int ip_tunnel_init(struct net_device *dev) iph->version = 4; iph->ihl = 5; + tunnel->dst_cache = NULL; + spin_lock_init(&tunnel->dst_lock); + return 0; } EXPORT_SYMBOL_GPL(ip_tunnel_init); @@ -1015,6 +1078,8 @@ void ip_tunnel_uninit(struct net_device *dev) /* fb_tunnel_dev will be unregisted in net-exit call. */ if (itn->fb_tunnel_dev != dev) ip_tunnel_del(netdev_priv(dev)); + + tunnel_dst_reset(tunnel); } EXPORT_SYMBOL_GPL(ip_tunnel_uninit); -- cgit v1.2.3 From 9a4aa9af447f784f0a47313c8dcb79ac63442cf7 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Thu, 2 Jan 2014 11:48:33 -0800 Subject: ipv4: Use percpu Cache route in IP tunnels percpu route cache eliminates share of dst refcnt between CPUs. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/net/ip_tunnels.h | 8 ++++++-- net/ipv4/ip_tunnel.c | 44 ++++++++++++++++++++++++++++++++------------ 2 files changed, 38 insertions(+), 14 deletions(-) (limited to 'include/net') diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index bde50fc5b4f0..9e25b1bc31da 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -38,6 +38,11 @@ struct ip_tunnel_prl_entry { struct rcu_head rcu_head; }; +struct ip_tunnel_dst { + struct dst_entry __rcu *dst; + spinlock_t lock; +}; + struct ip_tunnel { struct ip_tunnel __rcu *next; struct hlist_node hash_node; @@ -54,8 +59,7 @@ struct ip_tunnel { int hlen; /* Precalculated header length */ int mlink; - struct dst_entry __rcu *dst_cache; - spinlock_t dst_lock; + struct ip_tunnel_dst __percpu *dst_cache; struct ip_tunnel_parm parms; diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 27d756f8f870..e2c9cff26eb5 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -68,23 +68,24 @@ static unsigned int ip_tunnel_hash(struct ip_tunnel_net *itn, IP_TNL_HASH_BITS); } -static inline void __tunnel_dst_set(struct ip_tunnel *t, struct dst_entry *dst) +static inline void __tunnel_dst_set(struct ip_tunnel_dst *idst, + struct dst_entry *dst) { struct dst_entry *old_dst; if (dst && (dst->flags & DST_NOCACHE)) dst = NULL; - spin_lock_bh(&t->dst_lock); - old_dst = rcu_dereference_raw(t->dst_cache); - rcu_assign_pointer(t->dst_cache, dst); + spin_lock_bh(&idst->lock); + old_dst = rcu_dereference(idst->dst); + rcu_assign_pointer(idst->dst, dst); dst_release(old_dst); - spin_unlock_bh(&t->dst_lock); + spin_unlock_bh(&idst->lock); } static inline void tunnel_dst_set(struct ip_tunnel *t, struct dst_entry *dst) { - __tunnel_dst_set(t, dst); + __tunnel_dst_set(this_cpu_ptr(t->dst_cache), dst); } static inline void tunnel_dst_reset(struct ip_tunnel *t) @@ -92,12 +93,20 @@ static inline void tunnel_dst_reset(struct ip_tunnel *t) tunnel_dst_set(t, NULL); } +static void tunnel_dst_reset_all(struct ip_tunnel *t) +{ + int i; + + for_each_possible_cpu(i) + __tunnel_dst_set(per_cpu_ptr(t->dst_cache, i), NULL); +} + static inline struct dst_entry *tunnel_dst_get(struct ip_tunnel *t) { struct dst_entry *dst; rcu_read_lock(); - dst = rcu_dereference(t->dst_cache); + dst = rcu_dereference(this_cpu_ptr(t->dst_cache)->dst); if (dst) dst_hold(dst); rcu_read_unlock(); @@ -755,7 +764,7 @@ static void ip_tunnel_update(struct ip_tunnel_net *itn, if (set_mtu) dev->mtu = mtu; } - tunnel_dst_reset(t); + tunnel_dst_reset_all(t); netdev_state_change(dev); } @@ -871,6 +880,7 @@ static void ip_tunnel_dev_free(struct net_device *dev) struct ip_tunnel *tunnel = netdev_priv(dev); gro_cells_destroy(&tunnel->gro_cells); + free_percpu(tunnel->dst_cache); free_percpu(dev->tstats); free_netdev(dev); } @@ -1049,8 +1059,21 @@ int ip_tunnel_init(struct net_device *dev) u64_stats_init(&ipt_stats->syncp); } + tunnel->dst_cache = alloc_percpu(struct ip_tunnel_dst); + if (!tunnel->dst_cache) { + free_percpu(dev->tstats); + return -ENOMEM; + } + + for_each_possible_cpu(i) { + struct ip_tunnel_dst *idst = per_cpu_ptr(tunnel->dst_cache, i); + idst-> dst = NULL; + spin_lock_init(&idst->lock); + } + err = gro_cells_init(&tunnel->gro_cells, dev); if (err) { + free_percpu(tunnel->dst_cache); free_percpu(dev->tstats); return err; } @@ -1061,9 +1084,6 @@ int ip_tunnel_init(struct net_device *dev) iph->version = 4; iph->ihl = 5; - tunnel->dst_cache = NULL; - spin_lock_init(&tunnel->dst_lock); - return 0; } EXPORT_SYMBOL_GPL(ip_tunnel_init); @@ -1079,7 +1099,7 @@ void ip_tunnel_uninit(struct net_device *dev) if (itn->fb_tunnel_dev != dev) ip_tunnel_del(netdev_priv(dev)); - tunnel_dst_reset(tunnel); + tunnel_dst_reset_all(tunnel); } EXPORT_SYMBOL_GPL(ip_tunnel_uninit); -- cgit v1.2.3 From 8f09898bf02fc24b7a525e9cfc78f38dcdf3a4eb Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 3 Jan 2014 09:17:14 -0800 Subject: socket: cleanups Namespace related cleaning * make cred_to_ucred static * remove unused sock_rmalloc function Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/linux/socket.h | 2 -- include/net/sock.h | 2 -- net/core/sock.c | 21 ++------------------- 3 files changed, 2 insertions(+), 23 deletions(-) (limited to 'include/net') diff --git a/include/linux/socket.h b/include/linux/socket.h index 445ef7519dc2..5d488a65ad20 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -305,8 +305,6 @@ struct ucred { /* IPX options */ #define IPX_TYPE 1 -extern void cred_to_ucred(struct pid *pid, const struct cred *cred, struct ucred *ucred); - extern int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov, int offset, int len); extern int csum_partial_copy_fromiovecend(unsigned char *kdata, diff --git a/include/net/sock.h b/include/net/sock.h index bd716b6996ff..8d9af66ccf2c 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1549,8 +1549,6 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority); struct sk_buff *sock_wmalloc(struct sock *sk, unsigned long size, int force, gfp_t priority); -struct sk_buff *sock_rmalloc(struct sock *sk, unsigned long size, int force, - gfp_t priority); void sock_wfree(struct sk_buff *skb); void skb_orphan_partial(struct sk_buff *skb); void sock_rfree(struct sk_buff *skb); diff --git a/net/core/sock.c b/net/core/sock.c index 5393b4b719d7..0358855576a8 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -925,8 +925,8 @@ set_rcvbuf: EXPORT_SYMBOL(sock_setsockopt); -void cred_to_ucred(struct pid *pid, const struct cred *cred, - struct ucred *ucred) +static void cred_to_ucred(struct pid *pid, const struct cred *cred, + struct ucred *ucred) { ucred->pid = pid_vnr(pid); ucred->uid = ucred->gid = -1; @@ -937,7 +937,6 @@ void cred_to_ucred(struct pid *pid, const struct cred *cred, ucred->gid = from_kgid_munged(current_ns, cred->egid); } } -EXPORT_SYMBOL_GPL(cred_to_ucred); int sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) @@ -1665,22 +1664,6 @@ struct sk_buff *sock_wmalloc(struct sock *sk, unsigned long size, int force, } EXPORT_SYMBOL(sock_wmalloc); -/* - * Allocate a skb from the socket's receive buffer. - */ -struct sk_buff *sock_rmalloc(struct sock *sk, unsigned long size, int force, - gfp_t priority) -{ - if (force || atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf) { - struct sk_buff *skb = alloc_skb(size, priority); - if (skb) { - skb_set_owner_r(skb, sk); - return skb; - } - } - return NULL; -} - /* * Allocate a memory block from the socket's option memory buffer. */ -- cgit v1.2.3 From 5e419e68a6450da279e8add304629774133576a9 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 3 Jan 2014 09:19:51 -0800 Subject: llc: make lock static The llc_sap_list_lock does not need to be global, only acquired in core. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/net/llc.h | 1 - net/llc/llc_core.c | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'include/net') diff --git a/include/net/llc.h b/include/net/llc.h index 68490cbc8a65..e8e61d4fb458 100644 --- a/include/net/llc.h +++ b/include/net/llc.h @@ -93,7 +93,6 @@ struct hlist_nulls_head *llc_sk_laddr_hash(struct llc_sap *sap, #define LLC_DEST_CONN 2 /* Type 2 goes here */ extern struct list_head llc_sap_list; -extern spinlock_t llc_sap_list_lock; int llc_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev); diff --git a/net/llc/llc_core.c b/net/llc/llc_core.c index e251c0493ac0..842851cef698 100644 --- a/net/llc/llc_core.c +++ b/net/llc/llc_core.c @@ -23,7 +23,7 @@ #include LIST_HEAD(llc_sap_list); -DEFINE_SPINLOCK(llc_sap_list_lock); +static DEFINE_SPINLOCK(llc_sap_list_lock); /** * llc_sap_alloc - allocates and initializes sap. @@ -159,7 +159,6 @@ module_init(llc_init); module_exit(llc_exit); EXPORT_SYMBOL(llc_sap_list); -EXPORT_SYMBOL(llc_sap_list_lock); EXPORT_SYMBOL(llc_sap_find); EXPORT_SYMBOL(llc_sap_open); EXPORT_SYMBOL(llc_sap_close); -- cgit v1.2.3 From 444fb98eed98f7292a83f9bf123d1c78f171327e Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Thu, 2 Jan 2014 11:58:12 +0100 Subject: NFC: digital: Add a note about asynchronous functions This explains how and why the timeout parameter must be handled by the driver implementation. Signed-off-by: Samuel Ortiz --- include/net/nfc/digital.h | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'include/net') diff --git a/include/net/nfc/digital.h b/include/net/nfc/digital.h index 36acecd5f06c..81af21e9bcd4 100644 --- a/include/net/nfc/digital.h +++ b/include/net/nfc/digital.h @@ -122,6 +122,16 @@ typedef void (*nfc_digital_cmd_complete_t)(struct nfc_digital_dev *ddev, * switch_rf to turn the radio on. A call to in|tg_configure_hw must turn * the device radio on. * @abort_cmd: Discard the last sent command. + * + * Notes: Asynchronous functions have a timeout parameter. It is the driver + * responsibility to call the digital stack back through the + * nfc_digital_cmd_complete_t callback when no RF respsonse has been + * received within the specified time (in milliseconds). In that case the + * driver must set the resp sk_buff to ERR_PTR(-ETIMEDOUT). + * Since the digital stack serializes commands to be sent, it's mandatory + * for the driver to handle the timeout correctly. Otherwise the stack + * would not be able to send new commands, waiting for the reply of the + * current one. */ struct nfc_digital_ops { int (*in_configure_hw)(struct nfc_digital_dev *ddev, int type, -- cgit v1.2.3 From f9f462faa02777f497eb25255683a94e0c054de6 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 3 Jan 2014 03:02:35 -0800 Subject: Bluetooth: Add quirk for disabling Delete Stored Link Key command Some controller pretend they support the Delete Stored Link Key command, but in reality they really don't support it. < HCI Command: Delete Stored Link Key (0x03|0x0012) plen 7 bdaddr 00:00:00:00:00:00 all 1 > HCI Event: Command Complete (0x0e) plen 4 Delete Stored Link Key (0x03|0x0012) ncmd 1 status 0x11 deleted 0 Error: Unsupported Feature or Parameter Value Not correctly supporting this command causes the controller setup to fail and will make a device not work. However sending the command for controller that handle stored link keys is important. This quirk allows a driver to disable the command if it knows that this command handling is broken. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 3 ++- net/bluetooth/hci_core.c | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 5dc3d9072650..66c1cd87bfe7 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -83,7 +83,8 @@ enum { HCI_QUIRK_RESET_ON_CLOSE, HCI_QUIRK_RAW_DEVICE, - HCI_QUIRK_FIXUP_BUFFER_SIZE + HCI_QUIRK_FIXUP_BUFFER_SIZE, + HCI_QUIRK_BROKEN_STORED_LINK_KEY, }; /* HCI device flags */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index b23d40385f18..5e8663c194c1 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1304,8 +1304,13 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt) * as supported send it. If not supported assume that the controller * does not have actual support for stored link keys which makes this * command redundant anyway. + * + * Some controllers indicate that they support handling deleting + * stored link keys, but they don't. The quirk lets a driver + * just disable this command. */ - if (hdev->commands[6] & 0x80) { + if (hdev->commands[6] & 0x80 && + !test_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks)) { struct hci_cp_delete_stored_link_key cp; bacpy(&cp.bdaddr, BDADDR_ANY); -- cgit v1.2.3 From 8f84985fec10de64a6b4cdfea45f2b0ab8f07c78 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Sat, 4 Jan 2014 13:57:59 +0800 Subject: net: unify the pcpu_tstats and br_cpu_netstats as one They are same, so unify them as one, pcpu_sw_netstats. Define pcpu_sw_netstat in netdevice.h, remove pcpu_tstats from if_tunnel and remove br_cpu_netstats from br_private.h Cc: Cong Wang Cc: Stephen Hemminger Signed-off-by: Li RongQing Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 11 ++++++----- include/linux/if_tunnel.h | 9 --------- include/linux/netdevice.h | 11 ++++++++++- include/net/ip6_tunnel.h | 2 +- include/net/ip_tunnels.h | 4 ++-- net/bridge/br_device.c | 10 +++++----- net/bridge/br_input.c | 2 +- net/bridge/br_private.h | 10 +--------- net/ipv4/ip_tunnel.c | 9 +++++---- net/ipv4/ip_vti.c | 2 +- net/ipv6/ip6_gre.c | 10 +++++----- net/ipv6/ip6_tunnel.c | 12 ++++++------ net/ipv6/ip6_vti.c | 10 +++++----- net/ipv6/sit.c | 10 +++++----- net/openvswitch/vport.c | 12 ++++++------ net/openvswitch/vport.h | 2 +- 16 files changed, 60 insertions(+), 66 deletions(-) (limited to 'include/net') diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index aef44aa44fe3..474a99ed0222 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -1081,7 +1081,7 @@ static void vxlan_rcv(struct vxlan_sock *vs, struct iphdr *oip = NULL; struct ipv6hdr *oip6 = NULL; struct vxlan_dev *vxlan; - struct pcpu_tstats *stats; + struct pcpu_sw_netstats *stats; union vxlan_addr saddr; __u32 vni; int err = 0; @@ -1587,11 +1587,12 @@ EXPORT_SYMBOL_GPL(vxlan_xmit_skb); static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan, struct vxlan_dev *dst_vxlan) { - struct pcpu_tstats *tx_stats = this_cpu_ptr(src_vxlan->dev->tstats); - struct pcpu_tstats *rx_stats = this_cpu_ptr(dst_vxlan->dev->tstats); + struct pcpu_sw_netstats *tx_stats, *rx_stats; union vxlan_addr loopback; union vxlan_addr *remote_ip = &dst_vxlan->default_dst.remote_ip; + tx_stats = this_cpu_ptr(src_vxlan->dev->tstats); + rx_stats = this_cpu_ptr(dst_vxlan->dev->tstats); skb->pkt_type = PACKET_HOST; skb->encapsulation = 0; skb->dev = dst_vxlan->dev; @@ -1897,12 +1898,12 @@ static int vxlan_init(struct net_device *dev) struct vxlan_sock *vs; int i; - dev->tstats = alloc_percpu(struct pcpu_tstats); + dev->tstats = alloc_percpu(struct pcpu_sw_netstats); if (!dev->tstats) return -ENOMEM; for_each_possible_cpu(i) { - struct pcpu_tstats *vxlan_stats; + struct pcpu_sw_netstats *vxlan_stats; vxlan_stats = per_cpu_ptr(dev->tstats, i); u64_stats_init(&vxlan_stats->syncp); } diff --git a/include/linux/if_tunnel.h b/include/linux/if_tunnel.h index f4e56ecd0b1a..712710bc0580 100644 --- a/include/linux/if_tunnel.h +++ b/include/linux/if_tunnel.h @@ -13,13 +13,4 @@ #define for_each_ip_tunnel_rcu(pos, start) \ for (pos = rcu_dereference(start); pos; pos = rcu_dereference(pos->next)) -/* often modified stats are per cpu, other are shared (netdev->stats) */ -struct pcpu_tstats { - u64 rx_packets; - u64 rx_bytes; - u64 tx_packets; - u64 tx_bytes; - struct u64_stats_sync syncp; -}; - #endif /* _IF_TUNNEL_H_ */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index bec60c481966..51c0fe258163 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1409,7 +1409,7 @@ struct net_device { union { void *ml_priv; struct pcpu_lstats __percpu *lstats; /* loopback stats */ - struct pcpu_tstats __percpu *tstats; /* tunnel stats */ + struct pcpu_sw_netstats __percpu *tstats; struct pcpu_dstats __percpu *dstats; /* dummy stats */ struct pcpu_vstats __percpu *vstats; /* veth stats */ }; @@ -1685,6 +1685,15 @@ struct packet_offload { struct list_head list; }; +/* often modified stats are per cpu, other are shared (netdev->stats) */ +struct pcpu_sw_netstats { + u64 rx_packets; + u64 rx_bytes; + u64 tx_packets; + u64 tx_bytes; + struct u64_stats_sync syncp; +}; + #include /* netdevice notifier chain. Please remember to update the rtnetlink diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h index 6d1549c4893c..a5593dab6af7 100644 --- a/include/net/ip6_tunnel.h +++ b/include/net/ip6_tunnel.h @@ -79,7 +79,7 @@ static inline void ip6tunnel_xmit(struct sk_buff *skb, struct net_device *dev) err = ip6_local_out(skb); if (net_xmit_eval(err) == 0) { - struct pcpu_tstats *tstats = this_cpu_ptr(dev->tstats); + struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats); u64_stats_update_begin(&tstats->syncp); tstats->tx_bytes += pkt_len; tstats->tx_packets++; diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index 9e25b1bc31da..cd729becbb07 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -162,10 +162,10 @@ struct sk_buff *iptunnel_handle_offloads(struct sk_buff *skb, bool gre_csum, static inline void iptunnel_xmit_stats(int err, struct net_device_stats *err_stats, - struct pcpu_tstats __percpu *stats) + struct pcpu_sw_netstats __percpu *stats) { if (err > 0) { - struct pcpu_tstats *tstats = this_cpu_ptr(stats); + struct pcpu_sw_netstats *tstats = this_cpu_ptr(stats); u64_stats_update_begin(&tstats->syncp); tstats->tx_bytes += err; diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index f00cfd2a0143..e4401a531afb 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -32,7 +32,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) const unsigned char *dest = skb->data; struct net_bridge_fdb_entry *dst; struct net_bridge_mdb_entry *mdst; - struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats); + struct pcpu_sw_netstats *brstats = this_cpu_ptr(br->stats); u16 vid = 0; rcu_read_lock(); @@ -90,12 +90,12 @@ static int br_dev_init(struct net_device *dev) struct net_bridge *br = netdev_priv(dev); int i; - br->stats = alloc_percpu(struct br_cpu_netstats); + br->stats = alloc_percpu(struct pcpu_sw_netstats); if (!br->stats) return -ENOMEM; for_each_possible_cpu(i) { - struct br_cpu_netstats *br_dev_stats; + struct pcpu_sw_netstats *br_dev_stats; br_dev_stats = per_cpu_ptr(br->stats, i); u64_stats_init(&br_dev_stats->syncp); } @@ -135,12 +135,12 @@ static struct rtnl_link_stats64 *br_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) { struct net_bridge *br = netdev_priv(dev); - struct br_cpu_netstats tmp, sum = { 0 }; + struct pcpu_sw_netstats tmp, sum = { 0 }; unsigned int cpu; for_each_possible_cpu(cpu) { unsigned int start; - const struct br_cpu_netstats *bstats + const struct pcpu_sw_netstats *bstats = per_cpu_ptr(br->stats, cpu); do { start = u64_stats_fetch_begin_bh(&bstats->syncp); diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 7e73c32e205d..bf8dc7d308d6 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -28,7 +28,7 @@ static int br_pass_frame_up(struct sk_buff *skb) { struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev; struct net_bridge *br = netdev_priv(brdev); - struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats); + struct pcpu_sw_netstats *brstats = this_cpu_ptr(br->stats); u64_stats_update_begin(&brstats->syncp); brstats->rx_packets++; diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 2e77d923c8ee..3733f152351c 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -210,21 +210,13 @@ static inline struct net_bridge_port *br_port_get_rtnl(const struct net_device * rtnl_dereference(dev->rx_handler_data) : NULL; } -struct br_cpu_netstats { - u64 rx_packets; - u64 rx_bytes; - u64 tx_packets; - u64 tx_bytes; - struct u64_stats_sync syncp; -}; - struct net_bridge { spinlock_t lock; struct list_head port_list; struct net_device *dev; - struct br_cpu_netstats __percpu *stats; + struct pcpu_sw_netstats __percpu *stats; spinlock_t hash_lock; struct hlist_head hash[BR_HASH_SIZE]; #ifdef CONFIG_BRIDGE_NETFILTER diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index e2c9cff26eb5..07a5ed374262 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -132,7 +132,8 @@ struct rtnl_link_stats64 *ip_tunnel_get_stats64(struct net_device *dev, int i; for_each_possible_cpu(i) { - const struct pcpu_tstats *tstats = per_cpu_ptr(dev->tstats, i); + const struct pcpu_sw_netstats *tstats = + per_cpu_ptr(dev->tstats, i); u64 rx_packets, rx_bytes, tx_packets, tx_bytes; unsigned int start; @@ -460,7 +461,7 @@ static struct ip_tunnel *ip_tunnel_create(struct net *net, int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb, const struct tnl_ptk_info *tpi, bool log_ecn_error) { - struct pcpu_tstats *tstats; + struct pcpu_sw_netstats *tstats; const struct iphdr *iph = ip_hdr(skb); int err; @@ -1049,12 +1050,12 @@ int ip_tunnel_init(struct net_device *dev) int i, err; dev->destructor = ip_tunnel_dev_free; - dev->tstats = alloc_percpu(struct pcpu_tstats); + dev->tstats = alloc_percpu(struct pcpu_sw_netstats); if (!dev->tstats) return -ENOMEM; for_each_possible_cpu(i) { - struct pcpu_tstats *ipt_stats; + struct pcpu_sw_netstats *ipt_stats; ipt_stats = per_cpu_ptr(dev->tstats, i); u64_stats_init(&ipt_stats->syncp); } diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index 52b802a0cd8c..0783200ad8d2 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -60,7 +60,7 @@ static int vti_rcv(struct sk_buff *skb) tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY, iph->saddr, iph->daddr, 0); if (tunnel != NULL) { - struct pcpu_tstats *tstats; + struct pcpu_sw_netstats *tstats; u32 oldmark = skb->mark; int ret; diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index e27fb78c61f2..e7a440dd5c0d 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -498,7 +498,7 @@ static int ip6gre_rcv(struct sk_buff *skb) &ipv6h->saddr, &ipv6h->daddr, key, gre_proto); if (tunnel) { - struct pcpu_tstats *tstats; + struct pcpu_sw_netstats *tstats; if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) goto drop; @@ -1265,12 +1265,12 @@ static int ip6gre_tunnel_init(struct net_device *dev) if (ipv6_addr_any(&tunnel->parms.raddr)) dev->header_ops = &ip6gre_header_ops; - dev->tstats = alloc_percpu(struct pcpu_tstats); + dev->tstats = alloc_percpu(struct pcpu_sw_netstats); if (!dev->tstats) return -ENOMEM; for_each_possible_cpu(i) { - struct pcpu_tstats *ip6gre_tunnel_stats; + struct pcpu_sw_netstats *ip6gre_tunnel_stats; ip6gre_tunnel_stats = per_cpu_ptr(dev->tstats, i); u64_stats_init(&ip6gre_tunnel_stats->syncp); } @@ -1466,12 +1466,12 @@ static int ip6gre_tap_init(struct net_device *dev) ip6gre_tnl_link_config(tunnel, 1); - dev->tstats = alloc_percpu(struct pcpu_tstats); + dev->tstats = alloc_percpu(struct pcpu_sw_netstats); if (!dev->tstats) return -ENOMEM; for_each_possible_cpu(i) { - struct pcpu_tstats *ip6gre_tap_stats; + struct pcpu_sw_netstats *ip6gre_tap_stats; ip6gre_tap_stats = per_cpu_ptr(dev->tstats, i); u64_stats_init(&ip6gre_tap_stats->syncp); } diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 8d7c9867a445..02894216a46d 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -102,11 +101,12 @@ struct ip6_tnl_net { static struct net_device_stats *ip6_get_stats(struct net_device *dev) { - struct pcpu_tstats sum = { 0 }; + struct pcpu_sw_netstats sum = { 0 }; int i; for_each_possible_cpu(i) { - const struct pcpu_tstats *tstats = per_cpu_ptr(dev->tstats, i); + const struct pcpu_sw_netstats *tstats = + per_cpu_ptr(dev->tstats, i); sum.rx_packets += tstats->rx_packets; sum.rx_bytes += tstats->rx_bytes; @@ -784,7 +784,7 @@ static int ip6_tnl_rcv(struct sk_buff *skb, __u16 protocol, if ((t = ip6_tnl_lookup(dev_net(skb->dev), &ipv6h->saddr, &ipv6h->daddr)) != NULL) { - struct pcpu_tstats *tstats; + struct pcpu_sw_netstats *tstats; if (t->parms.proto != ipproto && t->parms.proto != 0) { rcu_read_unlock(); @@ -1497,12 +1497,12 @@ ip6_tnl_dev_init_gen(struct net_device *dev) t->dev = dev; t->net = dev_net(dev); - dev->tstats = alloc_percpu(struct pcpu_tstats); + dev->tstats = alloc_percpu(struct pcpu_sw_netstats); if (!dev->tstats) return -ENOMEM; for_each_possible_cpu(i) { - struct pcpu_tstats *ip6_tnl_stats; + struct pcpu_sw_netstats *ip6_tnl_stats; ip6_tnl_stats = per_cpu_ptr(dev->tstats, i); u64_stats_init(&ip6_tnl_stats->syncp); } diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index ed94ba61dda0..da1d9e4d62ca 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -77,11 +76,12 @@ struct vti6_net { static struct net_device_stats *vti6_get_stats(struct net_device *dev) { - struct pcpu_tstats sum = { 0 }; + struct pcpu_sw_netstats sum = { 0 }; int i; for_each_possible_cpu(i) { - const struct pcpu_tstats *tstats = per_cpu_ptr(dev->tstats, i); + const struct pcpu_sw_netstats *tstats = + per_cpu_ptr(dev->tstats, i); sum.rx_packets += tstats->rx_packets; sum.rx_bytes += tstats->rx_bytes; @@ -312,7 +312,7 @@ static int vti6_rcv(struct sk_buff *skb) if ((t = vti6_tnl_lookup(dev_net(skb->dev), &ipv6h->saddr, &ipv6h->daddr)) != NULL) { - struct pcpu_tstats *tstats; + struct pcpu_sw_netstats *tstats; if (t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) { rcu_read_unlock(); @@ -753,7 +753,7 @@ static inline int vti6_dev_init_gen(struct net_device *dev) t->dev = dev; t->net = dev_net(dev); - dev->tstats = alloc_percpu(struct pcpu_tstats); + dev->tstats = alloc_percpu(struct pcpu_sw_netstats); if (!dev->tstats) return -ENOMEM; return 0; diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 366fbba3359a..9937b2616713 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -671,7 +671,7 @@ static int ipip6_rcv(struct sk_buff *skb) tunnel = ipip6_tunnel_lookup(dev_net(skb->dev), skb->dev, iph->saddr, iph->daddr); if (tunnel != NULL) { - struct pcpu_tstats *tstats; + struct pcpu_sw_netstats *tstats; if (tunnel->parms.iph.protocol != IPPROTO_IPV6 && tunnel->parms.iph.protocol != 0) @@ -1361,12 +1361,12 @@ static int ipip6_tunnel_init(struct net_device *dev) memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4); ipip6_tunnel_bind_dev(dev); - dev->tstats = alloc_percpu(struct pcpu_tstats); + dev->tstats = alloc_percpu(struct pcpu_sw_netstats); if (!dev->tstats) return -ENOMEM; for_each_possible_cpu(i) { - struct pcpu_tstats *ipip6_tunnel_stats; + struct pcpu_sw_netstats *ipip6_tunnel_stats; ipip6_tunnel_stats = per_cpu_ptr(dev->tstats, i); u64_stats_init(&ipip6_tunnel_stats->syncp); } @@ -1391,12 +1391,12 @@ static int __net_init ipip6_fb_tunnel_init(struct net_device *dev) iph->ihl = 5; iph->ttl = 64; - dev->tstats = alloc_percpu(struct pcpu_tstats); + dev->tstats = alloc_percpu(struct pcpu_sw_netstats); if (!dev->tstats) return -ENOMEM; for_each_possible_cpu(i) { - struct pcpu_tstats *ipip6_fb_stats; + struct pcpu_sw_netstats *ipip6_fb_stats; ipip6_fb_stats = per_cpu_ptr(dev->tstats, i); u64_stats_init(&ipip6_fb_stats->syncp); } diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index d830a95f03a4..f5275dd29cd9 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c @@ -136,14 +136,14 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops, vport->ops = ops; INIT_HLIST_NODE(&vport->dp_hash_node); - vport->percpu_stats = alloc_percpu(struct pcpu_tstats); + vport->percpu_stats = alloc_percpu(struct pcpu_sw_netstats); if (!vport->percpu_stats) { kfree(vport); return ERR_PTR(-ENOMEM); } for_each_possible_cpu(i) { - struct pcpu_tstats *vport_stats; + struct pcpu_sw_netstats *vport_stats; vport_stats = per_cpu_ptr(vport->percpu_stats, i); u64_stats_init(&vport_stats->syncp); } @@ -275,8 +275,8 @@ void ovs_vport_get_stats(struct vport *vport, struct ovs_vport_stats *stats) spin_unlock_bh(&vport->stats_lock); for_each_possible_cpu(i) { - const struct pcpu_tstats *percpu_stats; - struct pcpu_tstats local_stats; + const struct pcpu_sw_netstats *percpu_stats; + struct pcpu_sw_netstats local_stats; unsigned int start; percpu_stats = per_cpu_ptr(vport->percpu_stats, i); @@ -344,7 +344,7 @@ int ovs_vport_get_options(const struct vport *vport, struct sk_buff *skb) void ovs_vport_receive(struct vport *vport, struct sk_buff *skb, struct ovs_key_ipv4_tunnel *tun_key) { - struct pcpu_tstats *stats; + struct pcpu_sw_netstats *stats; stats = this_cpu_ptr(vport->percpu_stats); u64_stats_update_begin(&stats->syncp); @@ -370,7 +370,7 @@ int ovs_vport_send(struct vport *vport, struct sk_buff *skb) int sent = vport->ops->send(vport, skb); if (likely(sent > 0)) { - struct pcpu_tstats *stats; + struct pcpu_sw_netstats *stats; stats = this_cpu_ptr(vport->percpu_stats); diff --git a/net/openvswitch/vport.h b/net/openvswitch/vport.h index 1a9fbcec6e1b..bc97ef7fa2af 100644 --- a/net/openvswitch/vport.h +++ b/net/openvswitch/vport.h @@ -87,7 +87,7 @@ struct vport { struct hlist_node dp_hash_node; const struct vport_ops *ops; - struct pcpu_tstats __percpu *percpu_stats; + struct pcpu_sw_netstats __percpu *percpu_stats; spinlock_t stats_lock; struct vport_err_stats err_stats; -- cgit v1.2.3 From bb9b18fb55b03477fe5bdd3e97245d6d4d3dee4f Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Sat, 30 Nov 2013 13:21:30 +0100 Subject: genl: Add genlmsg_new_unicast() for unicast message allocation Allocates a new sk_buff large enough to cover the specified payload plus required Netlink headers. Will check receiving socket for memory mapped i/o capability and use it if enabled. Will fall back to non-mapped skb if message size exceeds the frame size of the ring. Signed-of-by: Thomas Graf Reviewed-by: Daniel Borkmann Signed-off-by: Jesse Gross --- include/net/genetlink.h | 4 ++++ net/netlink/genetlink.c | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+) (limited to 'include/net') diff --git a/include/net/genetlink.h b/include/net/genetlink.h index 1b177ed803b7..93695f0e22a5 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -73,6 +73,7 @@ struct genl_family { * @attrs: netlink attributes * @_net: network namespace * @user_ptr: user pointers + * @dst_sk: destination socket */ struct genl_info { u32 snd_seq; @@ -85,6 +86,7 @@ struct genl_info { struct net * _net; #endif void * user_ptr[2]; + struct sock * dst_sk; }; static inline struct net *genl_info_net(struct genl_info *info) @@ -177,6 +179,8 @@ void genl_notify(struct genl_family *family, struct sk_buff *skb, struct net *net, u32 portid, u32 group, struct nlmsghdr *nlh, gfp_t flags); +struct sk_buff *genlmsg_new_unicast(size_t payload, struct genl_info *info, + gfp_t flags); void *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, struct genl_family *family, int flags, u8 cmd); diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 4518a57aa5fe..85bf42e2a943 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -453,6 +453,26 @@ int genl_unregister_family(struct genl_family *family) } EXPORT_SYMBOL(genl_unregister_family); +/** + * genlmsg_new_unicast - Allocate generic netlink message for unicast + * @payload: size of the message payload + * @info: information on destination + * @flags: the type of memory to allocate + * + * Allocates a new sk_buff large enough to cover the specified payload + * plus required Netlink headers. Will check receiving socket for + * memory mapped i/o capability and use it if enabled. Will fall back + * to non-mapped skb if message size exceeds the frame size of the ring. + */ +struct sk_buff *genlmsg_new_unicast(size_t payload, struct genl_info *info, + gfp_t flags) +{ + size_t len = nlmsg_total_size(genlmsg_total_size(payload)); + + return netlink_alloc_skb(info->dst_sk, len, info->snd_portid, flags); +} +EXPORT_SYMBOL_GPL(genlmsg_new_unicast); + /** * genlmsg_put - Add generic netlink header to netlink message * @skb: socket buffer holding the message @@ -593,6 +613,7 @@ static int genl_family_rcv_msg(struct genl_family *family, info.genlhdr = nlmsg_data(nlh); info.userhdr = nlmsg_data(nlh) + GENL_HDRLEN; info.attrs = attrbuf; + info.dst_sk = skb->sk; genl_info_net_set(&info, net); memset(&info.user_ptr, 0, sizeof(info.user_ptr)); -- cgit v1.2.3 From 86e8586ed5beea15ce7c359f02a1084c2da93bc7 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 6 Jan 2014 12:58:17 -0800 Subject: NFC: NCI: Add setup handler Some drivers require special configuration while initializing. This patch adds setup handler for this custom configuration. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: Samuel Ortiz --- include/net/nfc/nci_core.h | 1 + net/nfc/nci/core.c | 3 +++ 2 files changed, 4 insertions(+) (limited to 'include/net') diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index 6126f1f992b4..5c6fadd63483 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -68,6 +68,7 @@ struct nci_ops { int (*open)(struct nci_dev *ndev); int (*close)(struct nci_dev *ndev); int (*send)(struct nci_dev *ndev, struct sk_buff *skb); + int (*setup)(struct nci_dev *ndev); }; #define NCI_MAX_SUPPORTED_RF_INTERFACES 4 diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 6fbb7bab46e7..29c1caf3e975 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -301,6 +301,9 @@ static int nci_open_device(struct nci_dev *ndev) rc = __nci_request(ndev, nci_reset_req, 0, msecs_to_jiffies(NCI_RESET_TIMEOUT)); + if (ndev->ops->setup(ndev)) + ndev->ops->setup(ndev); + if (!rc) { rc = __nci_request(ndev, nci_init_req, 0, msecs_to_jiffies(NCI_INIT_TIMEOUT)); -- cgit v1.2.3 From 22c15bf30b70ab2eae300f093ffc64e182620aba Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 6 Jan 2014 12:58:18 -0800 Subject: NFC: NCI: Add set_config API This API can be used by drivers to send their custom configuration using SET_CONFIG NCI command to the device. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: Samuel Ortiz --- include/net/nfc/nci_core.h | 1 + net/nfc/nci/core.c | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) (limited to 'include/net') diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index 5c6fadd63483..2b93b77b210c 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -155,6 +155,7 @@ void nci_free_device(struct nci_dev *ndev); int nci_register_device(struct nci_dev *ndev); void nci_unregister_device(struct nci_dev *ndev); int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb); +int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, __u8 *val); static inline struct sk_buff *nci_skb_alloc(struct nci_dev *ndev, unsigned int len, diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 29c1caf3e975..46bda010bf11 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -413,6 +413,22 @@ static int nci_dev_down(struct nfc_dev *nfc_dev) return nci_close_device(ndev); } +int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, __u8 *val) +{ + struct nci_set_config_param param; + + if (!val || !len) + return 0; + + param.id = id; + param.len = len; + param.val = val; + + return __nci_request(ndev, nci_set_config_req, (unsigned long)¶m, + msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); +} +EXPORT_SYMBOL(nci_set_config); + static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev) { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); -- cgit v1.2.3 From 438e38fadca2f6e57eeecc08326c8a95758594d4 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 6 Jan 2014 14:03:07 -0800 Subject: gre_offload: statically build GRE offloading support GRO/GSO layers can be enabled on a node, even if said node is only forwarding packets. This patch permits GSO (and upcoming GRO) support for GRE encapsulated packets, even if the host has no GRE tunnel setup. Signed-off-by: Eric Dumazet Cc: H.K. Jerry Chu Signed-off-by: David S. Miller --- include/net/gre.h | 3 --- net/ipv4/Makefile | 4 ++-- net/ipv4/gre_demux.c | 9 --------- net/ipv4/gre_offload.c | 7 +++++-- 4 files changed, 7 insertions(+), 16 deletions(-) (limited to 'include/net') diff --git a/include/net/gre.h b/include/net/gre.h index dcd9ae3270d3..70046a0b0b89 100644 --- a/include/net/gre.h +++ b/include/net/gre.h @@ -33,9 +33,6 @@ struct gre_cisco_protocol { int gre_cisco_register(struct gre_cisco_protocol *proto); int gre_cisco_unregister(struct gre_cisco_protocol *proto); -int gre_offload_init(void); -void gre_offload_exit(void); - void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi, int hdr_len); diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index 4b81e91c80fe..f8c49ce5b283 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -11,7 +11,7 @@ obj-y := route.o inetpeer.o protocol.o \ tcp_offload.o datagram.o raw.o udp.o udplite.o \ udp_offload.o arp.o icmp.o devinet.o af_inet.o igmp.o \ fib_frontend.o fib_semantics.o fib_trie.o \ - inet_fragment.o ping.o ip_tunnel_core.o + inet_fragment.o ping.o ip_tunnel_core.o gre_offload.o obj-$(CONFIG_NET_IP_TUNNEL) += ip_tunnel.o obj-$(CONFIG_SYSCTL) += sysctl_net_ipv4.o @@ -19,7 +19,7 @@ obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_IP_MULTIPLE_TABLES) += fib_rules.o obj-$(CONFIG_IP_MROUTE) += ipmr.o obj-$(CONFIG_NET_IPIP) += ipip.o -gre-y := gre_demux.o gre_offload.o +gre-y := gre_demux.o obj-$(CONFIG_NET_IPGRE_DEMUX) += gre.o obj-$(CONFIG_NET_IPGRE) += ip_gre.o obj-$(CONFIG_NET_IPVTI) += ip_vti.o diff --git a/net/ipv4/gre_demux.c b/net/ipv4/gre_demux.c index 5893e99e8299..1863422fb7d5 100644 --- a/net/ipv4/gre_demux.c +++ b/net/ipv4/gre_demux.c @@ -355,14 +355,7 @@ static int __init gre_init(void) goto err_gre; } - if (gre_offload_init()) { - pr_err("can't add protocol offload\n"); - goto err_gso; - } - return 0; -err_gso: - gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO); err_gre: inet_del_protocol(&net_gre_protocol, IPPROTO_GRE); err: @@ -371,8 +364,6 @@ err: static void __exit gre_exit(void) { - gre_offload_exit(); - gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO); inet_del_protocol(&net_gre_protocol, IPPROTO_GRE); } diff --git a/net/ipv4/gre_offload.c b/net/ipv4/gre_offload.c index 2cd02f32f99f..9138cfb10140 100644 --- a/net/ipv4/gre_offload.c +++ b/net/ipv4/gre_offload.c @@ -123,12 +123,15 @@ static const struct net_offload gre_offload = { }, }; -int __init gre_offload_init(void) +static int __init gre_offload_init(void) { return inet_add_offload(&gre_offload, IPPROTO_GRE); } -void __exit gre_offload_exit(void) +static void __exit gre_offload_exit(void) { inet_del_offload(&gre_offload, IPPROTO_GRE); } + +module_init(gre_offload_init); +module_exit(gre_offload_exit); -- cgit v1.2.3 From 509aba3b0d366b7f16a9a2eebac1156b25f5f622 Mon Sep 17 00:00:00 2001 From: FX Le Bail Date: Tue, 7 Jan 2014 14:57:27 +0100 Subject: IPv6: add the option to use anycast addresses as source addresses in echo reply This change allows to follow a recommandation of RFC4942. - Add "anycast_src_echo_reply" sysctl to control the use of anycast addresses as source addresses for ICMPv6 echo reply. This sysctl is false by default to preserve existing behavior. - Add inline check ipv6_anycast_destination(). - Use them in icmpv6_echo_reply(). Reference: RFC4942 - IPv6 Transition/Coexistence Security Considerations (http://tools.ietf.org/html/rfc4942#section-2.1.6) 2.1.6. Anycast Traffic Identification and Security [...] To avoid exposing knowledge about the internal structure of the network, it is recommended that anycast servers now take advantage of the ability to return responses with the anycast address as the source address if possible. Signed-off-by: Francois-Xavier Le Bail Acked-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 7 +++++++ include/net/ip6_route.h | 7 +++++++ include/net/netns/ipv6.h | 1 + net/ipv6/icmp.c | 4 +++- net/ipv6/sysctl_net_ipv6.c | 8 ++++++++ 5 files changed, 26 insertions(+), 1 deletion(-) (limited to 'include/net') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index d71afa8bd828..7373115407e4 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1094,6 +1094,13 @@ bindv6only - BOOLEAN Default: FALSE (as specified in RFC3493) +anycast_src_echo_reply - BOOLEAN + Controls the use of anycast addresses as source addresses for ICMPv6 + echo reply + TRUE: enabled + FALSE: disabled + Default: FALSE + IPv6 Fragmentation: ip6frag_high_thresh - INTEGER diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 1fb6cddbd448..017badb1aec7 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -152,6 +152,13 @@ static inline bool ipv6_unicast_destination(const struct sk_buff *skb) return rt->rt6i_flags & RTF_LOCAL; } +static inline bool ipv6_anycast_destination(const struct sk_buff *skb) +{ + struct rt6_info *rt = (struct rt6_info *) skb_dst(skb); + + return rt->rt6i_flags & RTF_ANYCAST; +} + int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)); static inline int ip6_skb_dst_mtu(struct sk_buff *skb) diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index 0fb2401197c5..76fc7d1dbfd3 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -73,6 +73,7 @@ struct netns_ipv6 { #endif atomic_t dev_addr_genid; atomic_t rt_genid; + int anycast_src_echo_reply; }; #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6) diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 5d420095190f..9a809a4b3d86 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -556,7 +556,9 @@ static void icmpv6_echo_reply(struct sk_buff *skb) saddr = &ipv6_hdr(skb)->daddr; - if (!ipv6_unicast_destination(skb)) + if (!ipv6_unicast_destination(skb) && + !(net->ipv6.anycast_src_echo_reply && + ipv6_anycast_destination(skb))) saddr = NULL; memcpy(&tmp_hdr, icmph, sizeof(tmp_hdr)); diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c index 107b2f1d90ae..6b6a2c83027e 100644 --- a/net/ipv6/sysctl_net_ipv6.c +++ b/net/ipv6/sysctl_net_ipv6.c @@ -24,6 +24,13 @@ static struct ctl_table ipv6_table_template[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "anycast_src_echo_reply", + .data = &init_net.ipv6.anycast_src_echo_reply, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { } }; @@ -51,6 +58,7 @@ static int __net_init ipv6_sysctl_net_init(struct net *net) if (!ipv6_table) goto out; ipv6_table[0].data = &net->ipv6.sysctl.bindv6only; + ipv6_table[1].data = &net->ipv6.anycast_src_echo_reply; ipv6_route_table = ipv6_route_sysctl_init(net); if (!ipv6_route_table) -- cgit v1.2.3 From 685328b296acc810541d2532957912690273c64a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 22:24:00 +0100 Subject: mac80211: remove channel_change_time This value is no longer used by mac80211, and practically no driver ever set it to a correct value anyway, so remove it. Signed-off-by: Johannes Berg --- drivers/net/wireless/adm8211.c | 1 - drivers/net/wireless/at76c50x-usb.c | 1 - drivers/net/wireless/ath/ath10k/mac.c | 1 - drivers/net/wireless/ath/ath5k/base.c | 1 - drivers/net/wireless/ath/ath9k/htc_drv_init.c | 1 - drivers/net/wireless/ath/ath9k/init.c | 1 - drivers/net/wireless/ath/carl9170/main.c | 12 ------------ drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c | 1 - drivers/net/wireless/cw1200/main.c | 1 - drivers/net/wireless/mac80211_hwsim.c | 1 - drivers/net/wireless/mwl8k.c | 2 -- drivers/net/wireless/p54/main.c | 1 - drivers/net/wireless/rtlwifi/base.c | 1 - drivers/net/wireless/ti/wl1251/main.c | 1 - drivers/net/wireless/ti/wlcore/main.c | 1 - drivers/staging/winbond/wbusb.c | 1 - include/net/mac80211.h | 3 --- 17 files changed, 31 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c index cfce83e1f273..54afde0463dd 100644 --- a/drivers/net/wireless/adm8211.c +++ b/drivers/net/wireless/adm8211.c @@ -1866,7 +1866,6 @@ static int adm8211_probe(struct pci_dev *pdev, dev->flags = IEEE80211_HW_SIGNAL_UNSPEC; dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); - dev->channel_change_time = 1000; dev->max_signal = 100; /* FIXME: find better value */ dev->queues = 1; /* ADM8211C supports more, maybe ADM8211B too */ diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c index 34c8a33cac06..8cfd1d6841b1 100644 --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -2112,7 +2112,6 @@ static struct at76_priv *at76_alloc_new_device(struct usb_device *udev) priv->pm_period = 0; /* unit us */ - priv->hw->channel_change_time = 100000; return priv; } diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index ce9ef3499ecb..747c8bd74980 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3704,7 +3704,6 @@ int ath10k_mac_register(struct ath10k *ar) ar->hw->vif_data_size = sizeof(struct ath10k_vif); - ar->hw->channel_change_time = 5000; ar->hw->max_listen_interval = ATH10K_MAX_HW_LISTEN_INTERVAL; ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 69f58b073e85..fe6878b4ecd0 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -2549,7 +2549,6 @@ ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops) hw->wiphy->available_antennas_rx = 0x3; hw->extra_tx_headroom = 2; - hw->channel_change_time = 5000; /* * Mark the device as detached to avoid processing diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index c3676bf1d6c4..bcdb50af56d3 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -748,7 +748,6 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; hw->queues = 4; - hw->channel_change_time = 5000; hw->max_listen_interval = 1; hw->vif_data_size = sizeof(struct ath9k_htc_vif); diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 609b7e13f3f0..d8787692c9d4 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -950,7 +950,6 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->queues = 4; hw->max_rates = 4; - hw->channel_change_time = 5000; hw->max_listen_interval = 1; hw->max_rate_tries = 10; hw->sta_data_size = sizeof(struct ath_node); diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 349fa22a921a..5ab68fddf7ba 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1968,18 +1968,6 @@ static int carl9170_parse_eeprom(struct ar9170 *ar) return -ENOMEM; ar->num_channels = chans; - /* - * I measured this, a bandswitch takes roughly - * 135 ms and a frequency switch about 80. - * - * FIXME: measure these values again once EEPROM settings - * are used, that will influence them! - */ - if (bands == 2) - ar->hw->channel_change_time = 135 * 1000; - else - ar->hw->channel_change_time = 80 * 1000; - regulatory->current_rd = le16_to_cpu(ar->eeprom.reg_domain[0]); /* second part of wiphy init */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index e71ce8c842a2..925034b80e9c 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -1071,7 +1071,6 @@ static int ieee_hw_init(struct ieee80211_hw *hw) hw->max_rates = 2; /* Primary rate and 1 fallback rate */ /* channel change time is dependent on chip and band */ - hw->channel_change_time = 7 * 1000; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_ADHOC); diff --git a/drivers/net/wireless/cw1200/main.c b/drivers/net/wireless/cw1200/main.c index 090f01577dd2..1be819dfac63 100644 --- a/drivers/net/wireless/cw1200/main.c +++ b/drivers/net/wireless/cw1200/main.c @@ -302,7 +302,6 @@ static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr, hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; - hw->channel_change_time = 1000; /* TODO: find actual value */ hw->queues = 4; priv->rts_threshold = -1; diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 9e7d7df03a47..59c65c37bccd 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -2341,7 +2341,6 @@ static int __init init_mac80211_hwsim(void) INIT_DELAYED_WORK(&data->roc_done, hw_roc_done); INIT_DELAYED_WORK(&data->hw_scan, hw_scan_work); - hw->channel_change_time = 1; hw->queues = 5; hw->offchannel_tx_hw_queue = 4; hw->wiphy->interface_modes = diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index b953ad621e0b..721c738a7dd8 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -5893,8 +5893,6 @@ static int mwl8k_firmware_load_success(struct mwl8k_priv *priv) hw->extra_tx_headroom -= priv->ap_fw ? REDUCED_TX_HEADROOM : 0; - hw->channel_change_time = 10; - hw->queues = MWL8K_TX_WMM_QUEUES; /* Set rssi values to dBm */ diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index 067e6f2fd050..b61fefad2205 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -757,7 +757,6 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len) BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_MESH_POINT); - dev->channel_change_time = 1000; /* TODO: find actual value */ priv->beacon_req_id = cpu_to_le32(0); priv->tx_stats[P54_QUEUE_BEACON].limit = 1; priv->tx_stats[P54_QUEUE_FWSCAN].limit = 1; diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c index fcf9b621918c..2cfb6d4514cd 100644 --- a/drivers/net/wireless/rtlwifi/base.c +++ b/drivers/net/wireless/rtlwifi/base.c @@ -353,7 +353,6 @@ static void _rtl_init_mac80211(struct ieee80211_hw *hw) /* TODO: Correct this value for our hw */ /* TODO: define these hard code value */ - hw->channel_change_time = 100; hw->max_listen_interval = 10; hw->max_rate_tries = 4; /* hw->max_rates = 1; */ diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c index 3291ffa95273..ee4ec4e5660d 100644 --- a/drivers/net/wireless/ti/wl1251/main.c +++ b/drivers/net/wireless/ti/wl1251/main.c @@ -1347,7 +1347,6 @@ int wl1251_init_ieee80211(struct wl1251 *wl) /* unit us */ /* FIXME: find a proper value */ - wl->hw->channel_change_time = 10000; wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index e9da47cead58..18a009e593c7 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5710,7 +5710,6 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) /* unit us */ /* FIXME: find a proper value */ - wl->hw->channel_change_time = 10000; wl->hw->max_listen_interval = wl->conf.conn.max_listen_interval; wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | diff --git a/drivers/staging/winbond/wbusb.c b/drivers/staging/winbond/wbusb.c index 07891a3e316e..0d29624416c3 100644 --- a/drivers/staging/winbond/wbusb.c +++ b/drivers/staging/winbond/wbusb.c @@ -788,7 +788,6 @@ static int wb35_probe(struct usb_interface *intf, dev->flags = IEEE80211_HW_SIGNAL_UNSPEC; dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); - dev->channel_change_time = 1000; dev->max_signal = 100; dev->queues = 1; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index f838af816b56..25b18877747f 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1616,8 +1616,6 @@ enum ieee80211_hw_flags { * @extra_beacon_tailroom: tailroom to reserve in each beacon tx skb. * Can be used by drivers to add extra IEs. * - * @channel_change_time: time (in microseconds) it takes to change channels. - * * @max_signal: Maximum value for signal (rssi) in RX information, used * only when @IEEE80211_HW_SIGNAL_UNSPEC or @IEEE80211_HW_SIGNAL_DB * @@ -1699,7 +1697,6 @@ struct ieee80211_hw { u32 flags; unsigned int extra_tx_headroom; unsigned int extra_beacon_tailroom; - int channel_change_time; int vif_data_size; int sta_data_size; int chanctx_data_size; -- cgit v1.2.3 From c9484874e7596d6c890e4130336f5379f6a59c5f Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Fri, 3 Jan 2014 12:16:14 +0000 Subject: netfilter: nf_tables: add hook ops to struct nft_pktinfo Multi-family tables need the AF from the hook ops. Add a pointer to the hook ops and replace usage of the hooknum member in struct nft_pktinfo. Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 5 +++-- net/netfilter/nf_tables_core.c | 2 +- net/netfilter/nft_log.c | 2 +- net/netfilter/nft_reject.c | 6 +++--- 4 files changed, 8 insertions(+), 7 deletions(-) (limited to 'include/net') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 5a91abfc0c30..c9e63167f9a2 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -13,7 +13,7 @@ struct nft_pktinfo { struct sk_buff *skb; const struct net_device *in; const struct net_device *out; - u8 hooknum; + const struct nf_hook_ops *ops; u8 nhoff; u8 thoff; /* for x_tables compatibility */ @@ -29,7 +29,8 @@ static inline void nft_set_pktinfo(struct nft_pktinfo *pkt, pkt->skb = skb; pkt->in = pkt->xt.in = in; pkt->out = pkt->xt.out = out; - pkt->hooknum = pkt->xt.hooknum = ops->hooknum; + pkt->ops = ops; + pkt->xt.hooknum = ops->hooknum; pkt->xt.family = ops->pf; } diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c index e8fcc343c2b9..5aae31772ff7 100644 --- a/net/netfilter/nf_tables_core.c +++ b/net/netfilter/nf_tables_core.c @@ -109,7 +109,7 @@ static inline void nft_trace_packet(const struct nft_pktinfo *pkt, { struct net *net = dev_net(pkt->in ? pkt->in : pkt->out); - nf_log_packet(net, pkt->xt.family, pkt->hooknum, pkt->skb, pkt->in, + nf_log_packet(net, pkt->xt.family, pkt->ops->hooknum, pkt->skb, pkt->in, pkt->out, &trace_loginfo, "TRACE: %s:%s:%s:%u ", chain->table->name, chain->name, comments[type], rulenum); diff --git a/net/netfilter/nft_log.c b/net/netfilter/nft_log.c index 57cad072a13e..5af790123ad8 100644 --- a/net/netfilter/nft_log.c +++ b/net/netfilter/nft_log.c @@ -33,7 +33,7 @@ static void nft_log_eval(const struct nft_expr *expr, const struct nft_log *priv = nft_expr_priv(expr); struct net *net = dev_net(pkt->in ? pkt->in : pkt->out); - nf_log_packet(net, priv->family, pkt->hooknum, pkt->skb, pkt->in, + nf_log_packet(net, priv->family, pkt->ops->hooknum, pkt->skb, pkt->in, pkt->out, &priv->loginfo, "%s", priv->prefix); } diff --git a/net/netfilter/nft_reject.c b/net/netfilter/nft_reject.c index 7ae63cdad959..5e204711d704 100644 --- a/net/netfilter/nft_reject.c +++ b/net/netfilter/nft_reject.c @@ -44,15 +44,15 @@ static void nft_reject_eval(const struct nft_expr *expr, #if IS_ENABLED(CONFIG_NF_TABLES_IPV6) else if (priv->family == NFPROTO_IPV6) nf_send_unreach6(net, pkt->skb, priv->icmp_code, - pkt->hooknum); + pkt->ops->hooknum); #endif break; case NFT_REJECT_TCP_RST: if (priv->family == NFPROTO_IPV4) - nf_send_reset(pkt->skb, pkt->hooknum); + nf_send_reset(pkt->skb, pkt->ops->hooknum); #if IS_ENABLED(CONFIG_NF_TABLES_IPV6) else if (priv->family == NFPROTO_IPV6) - nf_send_reset6(net, pkt->skb, pkt->hooknum); + nf_send_reset6(net, pkt->skb, pkt->ops->hooknum); #endif break; } -- cgit v1.2.3 From 115a60b173af0170e0db26b9a3fd6a911fba70a3 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Fri, 3 Jan 2014 12:16:15 +0000 Subject: netfilter: nf_tables: add support for multi family tables Add support to register chains to multiple hooks for different address families for mixed IPv4/IPv6 tables. Signed-off-by: Patrick McHardy --- include/net/netfilter/nf_tables.h | 9 +++++- net/bridge/netfilter/nf_tables_bridge.c | 1 + net/ipv4/netfilter/nf_tables_arp.c | 1 + net/ipv4/netfilter/nf_tables_ipv4.c | 1 + net/ipv6/netfilter/nf_tables_ipv6.c | 1 + net/netfilter/nf_tables_api.c | 49 +++++++++++++++++++-------------- net/netfilter/nft_compat.c | 8 +++--- 7 files changed, 45 insertions(+), 25 deletions(-) (limited to 'include/net') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index c9e63167f9a2..f066f252e5e5 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -422,6 +422,8 @@ struct nft_stats { u64 pkts; }; +#define NFT_HOOK_OPS_MAX 2 + /** * struct nft_base_chain - nf_tables base chain * @@ -432,7 +434,7 @@ struct nft_stats { * @chain: the chain */ struct nft_base_chain { - struct nf_hook_ops ops; + struct nf_hook_ops ops[NFT_HOOK_OPS_MAX]; enum nft_chain_type type; u8 policy; struct nft_stats __percpu *stats; @@ -476,6 +478,8 @@ struct nft_table { * @nhooks: number of hooks in this family * @owner: module owner * @tables: used internally + * @nops: number of hook ops in this family + * @hook_ops_init: initialization function for chain hook ops * @hooks: hookfn overrides for packet validation */ struct nft_af_info { @@ -484,6 +488,9 @@ struct nft_af_info { unsigned int nhooks; struct module *owner; struct list_head tables; + unsigned int nops; + void (*hook_ops_init)(struct nf_hook_ops *, + unsigned int); nf_hookfn *hooks[NF_MAX_HOOKS]; }; diff --git a/net/bridge/netfilter/nf_tables_bridge.c b/net/bridge/netfilter/nf_tables_bridge.c index c5fdd9a693be..003c1e9a8b0c 100644 --- a/net/bridge/netfilter/nf_tables_bridge.c +++ b/net/bridge/netfilter/nf_tables_bridge.c @@ -32,6 +32,7 @@ static struct nft_af_info nft_af_bridge __read_mostly = { .family = NFPROTO_BRIDGE, .nhooks = NF_BR_NUMHOOKS, .owner = THIS_MODULE, + .nops = 1, .hooks = { [NF_BR_LOCAL_IN] = nft_do_chain_bridge, [NF_BR_FORWARD] = nft_do_chain_bridge, diff --git a/net/ipv4/netfilter/nf_tables_arp.c b/net/ipv4/netfilter/nf_tables_arp.c index 31bb778cebb8..36d27fc7e859 100644 --- a/net/ipv4/netfilter/nf_tables_arp.c +++ b/net/ipv4/netfilter/nf_tables_arp.c @@ -32,6 +32,7 @@ static struct nft_af_info nft_af_arp __read_mostly = { .family = NFPROTO_ARP, .nhooks = NF_ARP_NUMHOOKS, .owner = THIS_MODULE, + .nops = 1, .hooks = { [NF_ARP_IN] = nft_do_chain_arp, [NF_ARP_OUT] = nft_do_chain_arp, diff --git a/net/ipv4/netfilter/nf_tables_ipv4.c b/net/ipv4/netfilter/nf_tables_ipv4.c index ed7e15a93410..177c3bceb7ca 100644 --- a/net/ipv4/netfilter/nf_tables_ipv4.c +++ b/net/ipv4/netfilter/nf_tables_ipv4.c @@ -52,6 +52,7 @@ static struct nft_af_info nft_af_ipv4 __read_mostly = { .family = NFPROTO_IPV4, .nhooks = NF_INET_NUMHOOKS, .owner = THIS_MODULE, + .nops = 1, .hooks = { [NF_INET_LOCAL_IN] = nft_do_chain_ipv4, [NF_INET_LOCAL_OUT] = nft_ipv4_output, diff --git a/net/ipv6/netfilter/nf_tables_ipv6.c b/net/ipv6/netfilter/nf_tables_ipv6.c index 54a2bcdc8a17..642280e44b65 100644 --- a/net/ipv6/netfilter/nf_tables_ipv6.c +++ b/net/ipv6/netfilter/nf_tables_ipv6.c @@ -51,6 +51,7 @@ static struct nft_af_info nft_af_ipv6 __read_mostly = { .family = NFPROTO_IPV6, .nhooks = NF_INET_NUMHOOKS, .owner = THIS_MODULE, + .nops = 1, .hooks = { [NF_INET_LOCAL_IN] = nft_do_chain_ipv6, [NF_INET_LOCAL_OUT] = nft_ipv6_output, diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index d568626bc0f9..572d88dd3e5f 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -307,7 +307,8 @@ err: return err; } -static int nf_tables_table_enable(struct nft_table *table) +static int nf_tables_table_enable(const struct nft_af_info *afi, + struct nft_table *table) { struct nft_chain *chain; int err, i = 0; @@ -316,7 +317,7 @@ static int nf_tables_table_enable(struct nft_table *table) if (!(chain->flags & NFT_BASE_CHAIN)) continue; - err = nf_register_hook(&nft_base_chain(chain)->ops); + err = nf_register_hooks(nft_base_chain(chain)->ops, afi->nops); if (err < 0) goto err; @@ -331,18 +332,20 @@ err: if (i-- <= 0) break; - nf_unregister_hook(&nft_base_chain(chain)->ops); + nf_unregister_hooks(nft_base_chain(chain)->ops, afi->nops); } return err; } -static int nf_tables_table_disable(struct nft_table *table) +static int nf_tables_table_disable(const struct nft_af_info *afi, + struct nft_table *table) { struct nft_chain *chain; list_for_each_entry(chain, &table->chains, list) { if (chain->flags & NFT_BASE_CHAIN) - nf_unregister_hook(&nft_base_chain(chain)->ops); + nf_unregister_hooks(nft_base_chain(chain)->ops, + afi->nops); } return 0; @@ -365,12 +368,12 @@ static int nf_tables_updtable(struct sock *nlsk, struct sk_buff *skb, if ((flags & NFT_TABLE_F_DORMANT) && !(table->flags & NFT_TABLE_F_DORMANT)) { - ret = nf_tables_table_disable(table); + ret = nf_tables_table_disable(afi, table); if (ret >= 0) table->flags |= NFT_TABLE_F_DORMANT; } else if (!(flags & NFT_TABLE_F_DORMANT) && table->flags & NFT_TABLE_F_DORMANT) { - ret = nf_tables_table_enable(table); + ret = nf_tables_table_enable(afi, table); if (ret >= 0) table->flags &= ~NFT_TABLE_F_DORMANT; } @@ -598,7 +601,7 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq, if (chain->flags & NFT_BASE_CHAIN) { const struct nft_base_chain *basechain = nft_base_chain(chain); - const struct nf_hook_ops *ops = &basechain->ops; + const struct nf_hook_ops *ops = &basechain->ops[0]; struct nlattr *nest; nest = nla_nest_start(skb, NFTA_CHAIN_HOOK); @@ -832,6 +835,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, struct net *net = sock_net(skb->sk); int family = nfmsg->nfgen_family; u64 handle = 0; + unsigned int i; int err; bool create; @@ -904,7 +908,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, if (nla[NFTA_CHAIN_HOOK]) { struct nf_hook_ops *ops; nf_hookfn *hookfn; - u32 hooknum; + u32 hooknum, priority; int type = NFT_CHAIN_T_DEFAULT; if (nla[NFTA_CHAIN_TYPE]) { @@ -926,6 +930,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, hooknum = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM])); if (hooknum >= afi->nhooks) return -EINVAL; + priority = ntohl(nla_get_be32(ha[NFTA_HOOK_PRIORITY])); if (!(chain_type[family][type]->hook_mask & (1 << hooknum))) return -EOPNOTSUPP; @@ -938,15 +943,19 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, basechain->type = type; chain = &basechain->chain; - ops = &basechain->ops; - ops->pf = family; - ops->owner = afi->owner; - ops->hooknum = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM])); - ops->priority = ntohl(nla_get_be32(ha[NFTA_HOOK_PRIORITY])); - ops->priv = chain; - ops->hook = afi->hooks[ops->hooknum]; - if (hookfn) - ops->hook = hookfn; + for (i = 0; i < afi->nops; i++) { + ops = &basechain->ops[i]; + ops->pf = family; + ops->owner = afi->owner; + ops->hooknum = hooknum; + ops->priority = priority; + ops->priv = chain; + ops->hook = afi->hooks[ops->hooknum]; + if (hookfn) + ops->hook = hookfn; + if (afi->hook_ops_init) + afi->hook_ops_init(ops, i); + } chain->flags |= NFT_BASE_CHAIN; @@ -993,7 +1002,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, if (!(table->flags & NFT_TABLE_F_DORMANT) && chain->flags & NFT_BASE_CHAIN) { - err = nf_register_hook(&nft_base_chain(chain)->ops); + err = nf_register_hooks(nft_base_chain(chain)->ops, afi->nops); if (err < 0) { free_percpu(basechain->stats); kfree(basechain); @@ -1052,7 +1061,7 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb, if (!(table->flags & NFT_TABLE_F_DORMANT) && chain->flags & NFT_BASE_CHAIN) - nf_unregister_hook(&nft_base_chain(chain)->ops); + nf_unregister_hooks(nft_base_chain(chain)->ops, afi->nops); nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_DELCHAIN, family); diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c index da0c1f4ada12..82cb8236f8a1 100644 --- a/net/netfilter/nft_compat.c +++ b/net/netfilter/nft_compat.c @@ -92,7 +92,7 @@ nft_target_set_tgchk_param(struct xt_tgchk_param *par, if (ctx->chain->flags & NFT_BASE_CHAIN) { const struct nft_base_chain *basechain = nft_base_chain(ctx->chain); - const struct nf_hook_ops *ops = &basechain->ops; + const struct nf_hook_ops *ops = &basechain->ops[0]; par->hook_mask = 1 << ops->hooknum; } @@ -253,7 +253,7 @@ static int nft_target_validate(const struct nft_ctx *ctx, if (ctx->chain->flags & NFT_BASE_CHAIN) { const struct nft_base_chain *basechain = nft_base_chain(ctx->chain); - const struct nf_hook_ops *ops = &basechain->ops; + const struct nf_hook_ops *ops = &basechain->ops[0]; hook_mask = 1 << ops->hooknum; if (hook_mask & target->hooks) @@ -323,7 +323,7 @@ nft_match_set_mtchk_param(struct xt_mtchk_param *par, const struct nft_ctx *ctx, if (ctx->chain->flags & NFT_BASE_CHAIN) { const struct nft_base_chain *basechain = nft_base_chain(ctx->chain); - const struct nf_hook_ops *ops = &basechain->ops; + const struct nf_hook_ops *ops = &basechain->ops[0]; par->hook_mask = 1 << ops->hooknum; } @@ -449,7 +449,7 @@ static int nft_match_validate(const struct nft_ctx *ctx, if (ctx->chain->flags & NFT_BASE_CHAIN) { const struct nft_base_chain *basechain = nft_base_chain(ctx->chain); - const struct nf_hook_ops *ops = &basechain->ops; + const struct nf_hook_ops *ops = &basechain->ops[0]; hook_mask = 1 << ops->hooknum; if (hook_mask & match->hooks) -- cgit v1.2.3 From 1d49144c0aaa61be4e3ccbef9cc5c40b0ec5f2fe Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Fri, 3 Jan 2014 12:16:16 +0000 Subject: netfilter: nf_tables: add "inet" table for IPv4/IPv6 This patch adds a new table family and a new filter chain that you can use to attach IPv4 and IPv6 rules. This should help to simplify rule-set maintainance in dual-stack setups. Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables_ipv4.h | 2 + include/net/netfilter/nf_tables_ipv6.h | 2 + include/net/netns/nftables.h | 1 + include/uapi/linux/netfilter.h | 1 + net/ipv4/netfilter/nf_tables_ipv4.c | 3 +- net/ipv6/netfilter/nf_tables_ipv6.c | 3 +- net/netfilter/Kconfig | 8 +++ net/netfilter/Makefile | 1 + net/netfilter/nf_tables_inet.c | 97 ++++++++++++++++++++++++++++++++++ 9 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 net/netfilter/nf_tables_inet.c (limited to 'include/net') diff --git a/include/net/netfilter/nf_tables_ipv4.h b/include/net/netfilter/nf_tables_ipv4.h index 1be1c2c197ee..f7b3a669aad3 100644 --- a/include/net/netfilter/nf_tables_ipv4.h +++ b/include/net/netfilter/nf_tables_ipv4.h @@ -20,4 +20,6 @@ nft_set_pktinfo_ipv4(struct nft_pktinfo *pkt, pkt->xt.fragoff = ntohs(ip->frag_off) & IP_OFFSET; } +extern struct nft_af_info nft_af_ipv4; + #endif diff --git a/include/net/netfilter/nf_tables_ipv6.h b/include/net/netfilter/nf_tables_ipv6.h index 4a9b88a65963..3d8ae489be0d 100644 --- a/include/net/netfilter/nf_tables_ipv6.h +++ b/include/net/netfilter/nf_tables_ipv6.h @@ -27,4 +27,6 @@ nft_set_pktinfo_ipv6(struct nft_pktinfo *pkt, return 0; } +extern struct nft_af_info nft_af_ipv6; + #endif diff --git a/include/net/netns/nftables.h b/include/net/netns/nftables.h index 15d056d534e3..26a394cb91a8 100644 --- a/include/net/netns/nftables.h +++ b/include/net/netns/nftables.h @@ -10,6 +10,7 @@ struct netns_nftables { struct list_head commit_list; struct nft_af_info *ipv4; struct nft_af_info *ipv6; + struct nft_af_info *inet; struct nft_af_info *arp; struct nft_af_info *bridge; u8 gencursor; diff --git a/include/uapi/linux/netfilter.h b/include/uapi/linux/netfilter.h index f7dc0ebeeba5..ef1b1f88ca18 100644 --- a/include/uapi/linux/netfilter.h +++ b/include/uapi/linux/netfilter.h @@ -53,6 +53,7 @@ enum nf_inet_hooks { enum { NFPROTO_UNSPEC = 0, + NFPROTO_INET = 1, NFPROTO_IPV4 = 2, NFPROTO_ARP = 3, NFPROTO_BRIDGE = 7, diff --git a/net/ipv4/netfilter/nf_tables_ipv4.c b/net/ipv4/netfilter/nf_tables_ipv4.c index 177c3bceb7ca..da927dc9f636 100644 --- a/net/ipv4/netfilter/nf_tables_ipv4.c +++ b/net/ipv4/netfilter/nf_tables_ipv4.c @@ -48,7 +48,7 @@ static unsigned int nft_ipv4_output(const struct nf_hook_ops *ops, return nft_do_chain_ipv4(ops, skb, in, out, okfn); } -static struct nft_af_info nft_af_ipv4 __read_mostly = { +struct nft_af_info nft_af_ipv4 __read_mostly = { .family = NFPROTO_IPV4, .nhooks = NF_INET_NUMHOOKS, .owner = THIS_MODULE, @@ -61,6 +61,7 @@ static struct nft_af_info nft_af_ipv4 __read_mostly = { [NF_INET_POST_ROUTING] = nft_do_chain_ipv4, }, }; +EXPORT_SYMBOL_GPL(nft_af_ipv4); static int nf_tables_ipv4_init_net(struct net *net) { diff --git a/net/ipv6/netfilter/nf_tables_ipv6.c b/net/ipv6/netfilter/nf_tables_ipv6.c index 642280e44b65..025e7f4e8ac5 100644 --- a/net/ipv6/netfilter/nf_tables_ipv6.c +++ b/net/ipv6/netfilter/nf_tables_ipv6.c @@ -47,7 +47,7 @@ static unsigned int nft_ipv6_output(const struct nf_hook_ops *ops, return nft_do_chain_ipv6(ops, skb, in, out, okfn); } -static struct nft_af_info nft_af_ipv6 __read_mostly = { +struct nft_af_info nft_af_ipv6 __read_mostly = { .family = NFPROTO_IPV6, .nhooks = NF_INET_NUMHOOKS, .owner = THIS_MODULE, @@ -60,6 +60,7 @@ static struct nft_af_info nft_af_ipv6 __read_mostly = { [NF_INET_POST_ROUTING] = nft_do_chain_ipv6, }, }; +EXPORT_SYMBOL_GPL(nft_af_ipv6); static int nf_tables_ipv6_init_net(struct net *net) { diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index c3b3b26c4c4e..37d2092705a7 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -428,6 +428,14 @@ config NF_TABLES To compile it as a module, choose M here. +config NF_TABLES_INET + depends on NF_TABLES + select NF_TABLES_IPV4 + select NF_TABLES_IPV6 + tristate "Netfilter nf_tables mixed IPv4/IPv6 tables support" + help + This option enables support for a mixed IPv4/IPv6 "inet" table. + config NFT_EXTHDR depends on NF_TABLES tristate "Netfilter nf_tables IPv6 exthdr module" diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 78b4e1c9c595..74c066109334 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -70,6 +70,7 @@ nf_tables-objs += nft_immediate.o nft_cmp.o nft_lookup.o nf_tables-objs += nft_bitwise.o nft_byteorder.o nft_payload.o obj-$(CONFIG_NF_TABLES) += nf_tables.o +obj-$(CONFIG_NF_TABLES_INET) += nf_tables_inet.o obj-$(CONFIG_NFT_COMPAT) += nft_compat.o obj-$(CONFIG_NFT_EXTHDR) += nft_exthdr.o obj-$(CONFIG_NFT_META) += nft_meta.o diff --git a/net/netfilter/nf_tables_inet.c b/net/netfilter/nf_tables_inet.c new file mode 100644 index 000000000000..ac0edcb549de --- /dev/null +++ b/net/netfilter/nf_tables_inet.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2012-2014 Patrick McHardy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void nft_inet_hook_ops_init(struct nf_hook_ops *ops, unsigned int n) +{ + struct nft_af_info *afi; + + if (n == 1) + afi = &nft_af_ipv4; + else + afi = &nft_af_ipv6; + + ops->pf = afi->family; + if (afi->hooks[ops->hooknum]) + ops->hook = afi->hooks[ops->hooknum]; +} + +static struct nft_af_info nft_af_inet __read_mostly = { + .family = NFPROTO_INET, + .nhooks = NF_INET_NUMHOOKS, + .owner = THIS_MODULE, + .nops = 2, + .hook_ops_init = nft_inet_hook_ops_init, +}; + +static int __net_init nf_tables_inet_init_net(struct net *net) +{ + net->nft.inet = kmalloc(sizeof(struct nft_af_info), GFP_KERNEL); + if (net->nft.inet == NULL) + return -ENOMEM; + memcpy(net->nft.inet, &nft_af_inet, sizeof(nft_af_inet)); + + if (nft_register_afinfo(net, net->nft.inet) < 0) + goto err; + + return 0; + +err: + kfree(net->nft.inet); + return -ENOMEM; +} + +static void __net_exit nf_tables_inet_exit_net(struct net *net) +{ + nft_unregister_afinfo(net->nft.inet); + kfree(net->nft.inet); +} + +static struct pernet_operations nf_tables_inet_net_ops = { + .init = nf_tables_inet_init_net, + .exit = nf_tables_inet_exit_net, +}; + +static struct nf_chain_type filter_inet = { + .family = NFPROTO_INET, + .name = "filter", + .type = NFT_CHAIN_T_DEFAULT, + .hook_mask = (1 << NF_INET_LOCAL_IN) | + (1 << NF_INET_LOCAL_OUT) | + (1 << NF_INET_FORWARD) | + (1 << NF_INET_PRE_ROUTING) | + (1 << NF_INET_POST_ROUTING), +}; + +static int __init nf_tables_inet_init(void) +{ + nft_register_chain_type(&filter_inet); + return register_pernet_subsys(&nf_tables_inet_net_ops); +} + +static void __exit nf_tables_inet_exit(void) +{ + unregister_pernet_subsys(&nf_tables_inet_net_ops); + nft_unregister_chain_type(&filter_inet); +} + +module_init(nf_tables_inet_init); +module_exit(nf_tables_inet_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy "); +MODULE_ALIAS_NFT_FAMILY(1); -- cgit v1.2.3 From 4566bf27069b7780e453cffb24ea5f5323059885 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Fri, 3 Jan 2014 12:16:18 +0000 Subject: netfilter: nft_meta: add l4proto support For L3-proto independant rules we need to get at the L4 protocol value directly. Add it to the nft_pktinfo struct and use the meta expression to retrieve it. Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 1 + include/net/netfilter/nf_tables_ipv4.h | 3 ++- include/net/netfilter/nf_tables_ipv6.h | 1 + include/uapi/linux/netfilter/nf_tables.h | 2 ++ net/netfilter/nft_meta.c | 4 ++++ 5 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include/net') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index f066f252e5e5..5d2b703efe1c 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -16,6 +16,7 @@ struct nft_pktinfo { const struct nf_hook_ops *ops; u8 nhoff; u8 thoff; + u8 tprot; /* for x_tables compatibility */ struct xt_action_param xt; }; diff --git a/include/net/netfilter/nf_tables_ipv4.h b/include/net/netfilter/nf_tables_ipv4.h index f7b3a669aad3..cba143fbd2e4 100644 --- a/include/net/netfilter/nf_tables_ipv4.h +++ b/include/net/netfilter/nf_tables_ipv4.h @@ -15,8 +15,9 @@ nft_set_pktinfo_ipv4(struct nft_pktinfo *pkt, nft_set_pktinfo(pkt, ops, skb, in, out); - pkt->xt.thoff = ip_hdrlen(pkt->skb); ip = ip_hdr(pkt->skb); + pkt->tprot = ip->protocol; + pkt->xt.thoff = ip_hdrlen(pkt->skb); pkt->xt.fragoff = ntohs(ip->frag_off) & IP_OFFSET; } diff --git a/include/net/netfilter/nf_tables_ipv6.h b/include/net/netfilter/nf_tables_ipv6.h index 3d8ae489be0d..74d976137658 100644 --- a/include/net/netfilter/nf_tables_ipv6.h +++ b/include/net/netfilter/nf_tables_ipv6.h @@ -21,6 +21,7 @@ nft_set_pktinfo_ipv6(struct nft_pktinfo *pkt, if (protohdr < 0) return -1; + pkt->tprot = protohdr; pkt->xt.thoff = thoff; pkt->xt.fragoff = frag_off; diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 10afbfc0e66a..448593c07120 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -532,6 +532,7 @@ enum nft_exthdr_attributes { * @NFT_META_RTCLASSID: realm value of packet's route (skb->dst->tclassid) * @NFT_META_SECMARK: packet secmark (skb->secmark) * @NFT_META_NFPROTO: netfilter protocol + * @NFT_META_L4PROTO: layer 4 protocol number */ enum nft_meta_keys { NFT_META_LEN, @@ -550,6 +551,7 @@ enum nft_meta_keys { NFT_META_RTCLASSID, NFT_META_SECMARK, NFT_META_NFPROTO, + NFT_META_L4PROTO, }; /** diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index 999d04688433..b43975a43309 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -46,6 +46,9 @@ static void nft_meta_get_eval(const struct nft_expr *expr, case NFT_META_NFPROTO: dest->data[0] = pkt->ops->pf; break; + case NFT_META_L4PROTO: + dest->data[0] = pkt->tprot; + break; case NFT_META_PRIORITY: dest->data[0] = skb->priority; break; @@ -185,6 +188,7 @@ static int nft_meta_init_validate_get(uint32_t key) case NFT_META_LEN: case NFT_META_PROTOCOL: case NFT_META_NFPROTO: + case NFT_META_L4PROTO: case NFT_META_PRIORITY: case NFT_META_MARK: case NFT_META_IIF: -- cgit v1.2.3 From bdfbec2d2d240e9c528caae9c743801629b60166 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Thu, 9 Jan 2014 11:37:23 +0200 Subject: cfg80211: Add a function to get the number of supported channels Add a utility function to get the number of channels supported by the device, and update the places in the code that need this data. Signed-off-by: Ilan Peer [replace another occurrence in libertas, fix kernel-doc, fix bugs] Signed-off-by: Johannes Berg --- drivers/net/wireless/libertas/cfg.c | 7 +------ include/net/cfg80211.h | 8 ++++++++ net/wireless/nl80211.c | 13 ++----------- net/wireless/scan.c | 7 ++----- net/wireless/sme.c | 13 +++---------- net/wireless/util.c | 13 +++++++++++++ 6 files changed, 29 insertions(+), 32 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 116f4aba08d6..32f75007a825 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -1268,14 +1268,9 @@ static struct cfg80211_scan_request * _new_connect_scan_req(struct wiphy *wiphy, struct cfg80211_connect_params *sme) { struct cfg80211_scan_request *creq = NULL; - int i, n_channels = 0; + int i, n_channels = ieee80211_get_num_supported_channels(wiphy); enum ieee80211_band band; - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (wiphy->bands[band]) - n_channels += wiphy->bands[band]->n_channels; - } - creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) + n_channels * sizeof(void *), GFP_ATOMIC); diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 56c597793d6d..b1f84b05c67e 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4640,6 +4640,14 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev, */ void cfg80211_crit_proto_stopped(struct wireless_dev *wdev, gfp_t gfp); +/** + * ieee80211_get_num_supported_channels - get number of channels device has + * @wiphy: the wiphy + * + * Return: the number of channels supported by the device. + */ +unsigned int ieee80211_get_num_supported_channels(struct wiphy *wiphy); + /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* wiphy_printk helpers, similar to dev_printk */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 20857126f742..d0afd82ebd77 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5285,12 +5285,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) goto unlock; } } else { - enum ieee80211_band band; - n_channels = 0; - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) - if (wiphy->bands[band]) - n_channels += wiphy->bands[band]->n_channels; + n_channels = ieee80211_get_num_supported_channels(wiphy); } if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) @@ -5498,11 +5493,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, if (!n_channels) return -EINVAL; } else { - n_channels = 0; - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) - if (wiphy->bands[band]) - n_channels += wiphy->bands[band]->n_channels; + n_channels = ieee80211_get_num_supported_channels(wiphy); } if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index a32d52a04c27..b528e31da2cf 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -1089,11 +1089,8 @@ int cfg80211_wext_siwscan(struct net_device *dev, /* Determine number of channels, needed to allocate creq */ if (wreq && wreq->num_channels) n_channels = wreq->num_channels; - else { - for (band = 0; band < IEEE80211_NUM_BANDS; band++) - if (wiphy->bands[band]) - n_channels += wiphy->bands[band]->n_channels; - } + else + n_channels = ieee80211_get_num_supported_channels(wiphy); creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) + n_channels * sizeof(void *), diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 3f64202358f4..c854f1ce22db 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -70,18 +70,11 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) if (rdev->scan_req) return -EBUSY; - if (wdev->conn->params.channel) { + if (wdev->conn->params.channel) n_channels = 1; - } else { - enum ieee80211_band band; - n_channels = 0; + else + n_channels = ieee80211_get_num_supported_channels(wdev->wiphy); - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (!wdev->wiphy->bands[band]) - continue; - n_channels += wdev->wiphy->bands[band]->n_channels; - } - } request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) + sizeof(request->channels[0]) * n_channels, GFP_KERNEL); diff --git a/net/wireless/util.c b/net/wireless/util.c index 329b0efb3ded..d39c37104ae2 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1481,6 +1481,19 @@ int ieee80211_get_ratemask(struct ieee80211_supported_band *sband, return 0; } +unsigned int ieee80211_get_num_supported_channels(struct wiphy *wiphy) +{ + enum ieee80211_band band; + unsigned int n_channels = 0; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) + if (wiphy->bands[band]) + n_channels += wiphy->bands[band]->n_channels; + + return n_channels; +} +EXPORT_SYMBOL(ieee80211_get_num_supported_channels); + /* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ /* Ethernet-II snap header (RFC1042 for most EtherTypes) */ const unsigned char rfc1042_header[] __aligned(2) = -- cgit v1.2.3 From baae3e62f31618d90e08fb886b4481e5d7b7f27c Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Thu, 9 Jan 2014 18:42:34 +0000 Subject: netfilter: nf_tables: fix chain type module reference handling The chain type module reference handling makes no sense at all: we take a reference immediately when the module is registered, preventing the module from ever being unloaded. Fix by taking a reference when we're actually creating a chain of the chain type and release the reference when destroying the chain. Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 2 +- net/netfilter/nf_tables_api.c | 45 +++++++++++++++++++++------------------ 2 files changed, 25 insertions(+), 22 deletions(-) (limited to 'include/net') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 5d2b703efe1c..e9b97862bf52 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -436,7 +436,7 @@ struct nft_stats { */ struct nft_base_chain { struct nf_hook_ops ops[NFT_HOOK_OPS_MAX]; - enum nft_chain_type type; + struct nf_chain_type *type; u8 policy; struct nft_stats __percpu *stats; struct nft_chain chain; diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 290472c0bf4f..d913fb0ab0aa 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -126,27 +126,29 @@ static inline u64 nf_tables_alloc_handle(struct nft_table *table) static struct nf_chain_type *chain_type[AF_MAX][NFT_CHAIN_T_MAX]; -static int __nf_tables_chain_type_lookup(int family, const struct nlattr *nla) +static struct nf_chain_type * +__nf_tables_chain_type_lookup(int family, const struct nlattr *nla) { int i; - for (i=0; iname)) - return i; + return chain_type[family][i]; } - return -1; + return NULL; } -static int nf_tables_chain_type_lookup(const struct nft_af_info *afi, - const struct nlattr *nla, - bool autoload) +static struct nf_chain_type * +nf_tables_chain_type_lookup(const struct nft_af_info *afi, + const struct nlattr *nla, + bool autoload) { - int type; + struct nf_chain_type *type; type = __nf_tables_chain_type_lookup(afi->family, nla); #ifdef CONFIG_MODULES - if (type < 0 && autoload) { + if (type == NULL && autoload) { nfnl_unlock(NFNL_SUBSYS_NFTABLES); request_module("nft-chain-%u-%*.s", afi->family, nla_len(nla)-1, (const char *)nla_data(nla)); @@ -478,10 +480,6 @@ int nft_register_chain_type(struct nf_chain_type *ctype) err = -EBUSY; goto out; } - - if (!try_module_get(ctype->me)) - goto out; - chain_type[ctype->family][ctype->type] = ctype; out: nfnl_unlock(NFNL_SUBSYS_NFTABLES); @@ -493,7 +491,6 @@ void nft_unregister_chain_type(struct nf_chain_type *ctype) { nfnl_lock(NFNL_SUBSYS_NFTABLES); chain_type[ctype->family][ctype->type] = NULL; - module_put(ctype->me); nfnl_unlock(NFNL_SUBSYS_NFTABLES); } EXPORT_SYMBOL_GPL(nft_unregister_chain_type); @@ -617,9 +614,8 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq, htonl(basechain->policy))) goto nla_put_failure; - if (nla_put_string(skb, NFTA_CHAIN_TYPE, - chain_type[ops->pf][nft_base_chain(chain)->type]->name)) - goto nla_put_failure; + if (nla_put_string(skb, NFTA_CHAIN_TYPE, basechain->type->name)) + goto nla_put_failure; if (nft_dump_stats(skb, nft_base_chain(chain)->stats)) goto nla_put_failure; @@ -900,16 +896,17 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, return -EOVERFLOW; if (nla[NFTA_CHAIN_HOOK]) { + struct nf_chain_type *type; struct nf_hook_ops *ops; nf_hookfn *hookfn; u32 hooknum, priority; - int type = NFT_CHAIN_T_DEFAULT; + type = chain_type[family][NFT_CHAIN_T_DEFAULT]; if (nla[NFTA_CHAIN_TYPE]) { type = nf_tables_chain_type_lookup(afi, nla[NFTA_CHAIN_TYPE], create); - if (type < 0) + if (type == NULL) return -ENOENT; } @@ -926,9 +923,11 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, return -EINVAL; priority = ntohl(nla_get_be32(ha[NFTA_HOOK_PRIORITY])); - if (!(chain_type[family][type]->hook_mask & (1 << hooknum))) + if (!(type->hook_mask & (1 << hooknum))) return -EOPNOTSUPP; - hookfn = chain_type[family][type]->fn[hooknum]; + if (!try_module_get(type->me)) + return -ENOENT; + hookfn = type->fn[hooknum]; basechain = kzalloc(sizeof(*basechain), GFP_KERNEL); if (basechain == NULL) @@ -938,6 +937,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, err = nf_tables_counters(basechain, nla[NFTA_CHAIN_COUNTERS]); if (err < 0) { + module_put(type->me); kfree(basechain); return err; } @@ -946,6 +946,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, newstats = alloc_percpu(struct nft_stats); if (newstats == NULL) { + module_put(type->me); kfree(basechain); return -ENOMEM; } @@ -987,6 +988,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, chain->flags & NFT_BASE_CHAIN) { err = nf_register_hooks(nft_base_chain(chain)->ops, afi->nops); if (err < 0) { + module_put(basechain->type->me); free_percpu(basechain->stats); kfree(basechain); return err; @@ -1007,6 +1009,7 @@ static void nf_tables_rcu_chain_destroy(struct rcu_head *head) BUG_ON(chain->use > 0); if (chain->flags & NFT_BASE_CHAIN) { + module_put(nft_base_chain(chain)->type->me); free_percpu(nft_base_chain(chain)->stats); kfree(nft_base_chain(chain)); } else -- cgit v1.2.3 From 2a37d755b885995443f11cdcaf1f9d4b5f246eab Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Thu, 9 Jan 2014 18:42:37 +0000 Subject: netfilter: nf_tables: constify chain type definitions and pointers Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 6 +++--- net/bridge/netfilter/nf_tables_bridge.c | 2 +- net/ipv4/netfilter/nf_tables_arp.c | 2 +- net/ipv4/netfilter/nf_tables_ipv4.c | 2 +- net/ipv4/netfilter/nft_chain_nat_ipv4.c | 2 +- net/ipv4/netfilter/nft_chain_route_ipv4.c | 2 +- net/ipv6/netfilter/nf_tables_ipv6.c | 2 +- net/ipv6/netfilter/nft_chain_nat_ipv6.c | 2 +- net/ipv6/netfilter/nft_chain_route_ipv6.c | 2 +- net/netfilter/nf_tables_api.c | 14 +++++++------- net/netfilter/nf_tables_inet.c | 2 +- 11 files changed, 19 insertions(+), 19 deletions(-) (limited to 'include/net') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index e9b97862bf52..d3f70530a59a 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -436,7 +436,7 @@ struct nft_stats { */ struct nft_base_chain { struct nf_hook_ops ops[NFT_HOOK_OPS_MAX]; - struct nf_chain_type *type; + const struct nf_chain_type *type; u8 policy; struct nft_stats __percpu *stats; struct nft_chain chain; @@ -507,8 +507,8 @@ struct nf_chain_type { int family; }; -int nft_register_chain_type(struct nf_chain_type *); -void nft_unregister_chain_type(struct nf_chain_type *); +int nft_register_chain_type(const struct nf_chain_type *); +void nft_unregister_chain_type(const struct nf_chain_type *); int nft_register_expr(struct nft_expr_type *); void nft_unregister_expr(struct nft_expr_type *); diff --git a/net/bridge/netfilter/nf_tables_bridge.c b/net/bridge/netfilter/nf_tables_bridge.c index f97222eb8235..283658d21825 100644 --- a/net/bridge/netfilter/nf_tables_bridge.c +++ b/net/bridge/netfilter/nf_tables_bridge.c @@ -68,7 +68,7 @@ static struct pernet_operations nf_tables_bridge_net_ops = { .exit = nf_tables_bridge_exit_net, }; -static struct nf_chain_type filter_bridge = { +static const struct nf_chain_type filter_bridge = { .family = NFPROTO_BRIDGE, .name = "filter", .type = NFT_CHAIN_T_DEFAULT, diff --git a/net/ipv4/netfilter/nf_tables_arp.c b/net/ipv4/netfilter/nf_tables_arp.c index 228df003f3cc..8af01a5e8f98 100644 --- a/net/ipv4/netfilter/nf_tables_arp.c +++ b/net/ipv4/netfilter/nf_tables_arp.c @@ -68,7 +68,7 @@ static struct pernet_operations nf_tables_arp_net_ops = { .exit = nf_tables_arp_exit_net, }; -static struct nf_chain_type filter_arp = { +static const struct nf_chain_type filter_arp = { .family = NFPROTO_ARP, .name = "filter", .type = NFT_CHAIN_T_DEFAULT, diff --git a/net/ipv4/netfilter/nf_tables_ipv4.c b/net/ipv4/netfilter/nf_tables_ipv4.c index d6fc1b4ace4e..cec7805de3e3 100644 --- a/net/ipv4/netfilter/nf_tables_ipv4.c +++ b/net/ipv4/netfilter/nf_tables_ipv4.c @@ -91,7 +91,7 @@ static struct pernet_operations nf_tables_ipv4_net_ops = { .exit = nf_tables_ipv4_exit_net, }; -static struct nf_chain_type filter_ipv4 = { +static const struct nf_chain_type filter_ipv4 = { .family = NFPROTO_IPV4, .name = "filter", .type = NFT_CHAIN_T_DEFAULT, diff --git a/net/ipv4/netfilter/nft_chain_nat_ipv4.c b/net/ipv4/netfilter/nft_chain_nat_ipv4.c index cf2c792cd971..9e535c2c2cd2 100644 --- a/net/ipv4/netfilter/nft_chain_nat_ipv4.c +++ b/net/ipv4/netfilter/nft_chain_nat_ipv4.c @@ -164,7 +164,7 @@ static unsigned int nf_nat_output(const struct nf_hook_ops *ops, return ret; } -static struct nf_chain_type nft_chain_nat_ipv4 = { +static const struct nf_chain_type nft_chain_nat_ipv4 = { .family = NFPROTO_IPV4, .name = "nat", .type = NFT_CHAIN_T_NAT, diff --git a/net/ipv4/netfilter/nft_chain_route_ipv4.c b/net/ipv4/netfilter/nft_chain_route_ipv4.c index 4e6bf9a3d7aa..2dd2eeaad15f 100644 --- a/net/ipv4/netfilter/nft_chain_route_ipv4.c +++ b/net/ipv4/netfilter/nft_chain_route_ipv4.c @@ -61,7 +61,7 @@ static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops, return ret; } -static struct nf_chain_type nft_chain_route_ipv4 = { +static const struct nf_chain_type nft_chain_route_ipv4 = { .family = NFPROTO_IPV4, .name = "route", .type = NFT_CHAIN_T_ROUTE, diff --git a/net/ipv6/netfilter/nf_tables_ipv6.c b/net/ipv6/netfilter/nf_tables_ipv6.c index a34027687ecd..758a32b0e2ff 100644 --- a/net/ipv6/netfilter/nf_tables_ipv6.c +++ b/net/ipv6/netfilter/nf_tables_ipv6.c @@ -90,7 +90,7 @@ static struct pernet_operations nf_tables_ipv6_net_ops = { .exit = nf_tables_ipv6_exit_net, }; -static struct nf_chain_type filter_ipv6 = { +static const struct nf_chain_type filter_ipv6 = { .family = NFPROTO_IPV6, .name = "filter", .type = NFT_CHAIN_T_DEFAULT, diff --git a/net/ipv6/netfilter/nft_chain_nat_ipv6.c b/net/ipv6/netfilter/nft_chain_nat_ipv6.c index e86dcd70dc76..efd1d57a610a 100644 --- a/net/ipv6/netfilter/nft_chain_nat_ipv6.c +++ b/net/ipv6/netfilter/nft_chain_nat_ipv6.c @@ -170,7 +170,7 @@ static unsigned int nf_nat_ipv6_output(const struct nf_hook_ops *ops, return ret; } -static struct nf_chain_type nft_chain_nat_ipv6 = { +static const struct nf_chain_type nft_chain_nat_ipv6 = { .family = NFPROTO_IPV6, .name = "nat", .type = NFT_CHAIN_T_NAT, diff --git a/net/ipv6/netfilter/nft_chain_route_ipv6.c b/net/ipv6/netfilter/nft_chain_route_ipv6.c index 3fe40f0456ad..3620f8851eba 100644 --- a/net/ipv6/netfilter/nft_chain_route_ipv6.c +++ b/net/ipv6/netfilter/nft_chain_route_ipv6.c @@ -59,7 +59,7 @@ static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops, return ret; } -static struct nf_chain_type nft_chain_route_ipv6 = { +static const struct nf_chain_type nft_chain_route_ipv6 = { .family = NFPROTO_IPV6, .name = "route", .type = NFT_CHAIN_T_ROUTE, diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 7d6a2264ae0a..acdd9d68d52f 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -124,9 +124,9 @@ static inline u64 nf_tables_alloc_handle(struct nft_table *table) return ++table->hgenerator; } -static struct nf_chain_type *chain_type[AF_MAX][NFT_CHAIN_T_MAX]; +static const struct nf_chain_type *chain_type[AF_MAX][NFT_CHAIN_T_MAX]; -static struct nf_chain_type * +static const struct nf_chain_type * __nf_tables_chain_type_lookup(int family, const struct nlattr *nla) { int i; @@ -139,12 +139,12 @@ __nf_tables_chain_type_lookup(int family, const struct nlattr *nla) return NULL; } -static struct nf_chain_type * +static const struct nf_chain_type * nf_tables_chain_type_lookup(const struct nft_af_info *afi, const struct nlattr *nla, bool autoload) { - struct nf_chain_type *type; + const struct nf_chain_type *type; type = __nf_tables_chain_type_lookup(afi->family, nla); if (type != NULL) @@ -475,7 +475,7 @@ static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb, return 0; } -int nft_register_chain_type(struct nf_chain_type *ctype) +int nft_register_chain_type(const struct nf_chain_type *ctype) { int err = 0; @@ -491,7 +491,7 @@ out: } EXPORT_SYMBOL_GPL(nft_register_chain_type); -void nft_unregister_chain_type(struct nf_chain_type *ctype) +void nft_unregister_chain_type(const struct nf_chain_type *ctype) { nfnl_lock(NFNL_SUBSYS_NFTABLES); chain_type[ctype->family][ctype->type] = NULL; @@ -900,7 +900,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, return -EOVERFLOW; if (nla[NFTA_CHAIN_HOOK]) { - struct nf_chain_type *type; + const struct nf_chain_type *type; struct nf_hook_ops *ops; nf_hookfn *hookfn; u32 hooknum, priority; diff --git a/net/netfilter/nf_tables_inet.c b/net/netfilter/nf_tables_inet.c index 280d3a2d5abc..ee29ba2829d0 100644 --- a/net/netfilter/nf_tables_inet.c +++ b/net/netfilter/nf_tables_inet.c @@ -66,7 +66,7 @@ static struct pernet_operations nf_tables_inet_net_ops = { .exit = nf_tables_inet_exit_net, }; -static struct nf_chain_type filter_inet = { +static const struct nf_chain_type filter_inet = { .family = NFPROTO_INET, .name = "filter", .type = NFT_CHAIN_T_DEFAULT, -- cgit v1.2.3 From fa2c1de0bbd98985f7f930205de97ae0d3e86c16 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Thu, 9 Jan 2014 18:42:38 +0000 Subject: netfilter: nf_tables: minor nf_chain_type cleanups Minor nf_chain_type cleanups: - reorder struct to plug a hoe - rename struct module member to "owner" for consistency - rename nf_hookfn array to "hooks" for consistency - reorder initializers for better readability Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 22 ++++++++++++++++------ net/bridge/netfilter/nf_tables_bridge.c | 4 ++-- net/ipv4/netfilter/nf_tables_arp.c | 4 ++-- net/ipv4/netfilter/nf_tables_ipv4.c | 4 ++-- net/ipv4/netfilter/nft_chain_nat_ipv4.c | 6 +++--- net/ipv4/netfilter/nft_chain_route_ipv4.c | 6 +++--- net/ipv6/netfilter/nf_tables_ipv6.c | 4 ++-- net/ipv6/netfilter/nft_chain_nat_ipv6.c | 6 +++--- net/ipv6/netfilter/nft_chain_route_ipv6.c | 6 +++--- net/netfilter/nf_tables_api.c | 12 ++++++------ net/netfilter/nf_tables_inet.c | 4 ++-- 11 files changed, 44 insertions(+), 34 deletions(-) (limited to 'include/net') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index d3f70530a59a..342236550ef9 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -498,13 +498,23 @@ struct nft_af_info { int nft_register_afinfo(struct net *, struct nft_af_info *); void nft_unregister_afinfo(struct nft_af_info *); +/** + * struct nf_chain_type - nf_tables chain type info + * + * @name: name of the type + * @type: numeric identifier + * @family: address family + * @owner: module owner + * @hook_mask: mask of valid hooks + * @hooks: hookfn overrides + */ struct nf_chain_type { - unsigned int hook_mask; - const char *name; - enum nft_chain_type type; - nf_hookfn *fn[NF_MAX_HOOKS]; - struct module *me; - int family; + const char *name; + enum nft_chain_type type; + int family; + struct module *owner; + unsigned int hook_mask; + nf_hookfn *hooks[NF_MAX_HOOKS]; }; int nft_register_chain_type(const struct nf_chain_type *); diff --git a/net/bridge/netfilter/nf_tables_bridge.c b/net/bridge/netfilter/nf_tables_bridge.c index 283658d21825..c83fab5f8736 100644 --- a/net/bridge/netfilter/nf_tables_bridge.c +++ b/net/bridge/netfilter/nf_tables_bridge.c @@ -69,10 +69,10 @@ static struct pernet_operations nf_tables_bridge_net_ops = { }; static const struct nf_chain_type filter_bridge = { - .family = NFPROTO_BRIDGE, .name = "filter", .type = NFT_CHAIN_T_DEFAULT, - .me = THIS_MODULE, + .family = NFPROTO_BRIDGE, + .owner = THIS_MODULE, .hook_mask = (1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | (1 << NF_BR_LOCAL_OUT), diff --git a/net/ipv4/netfilter/nf_tables_arp.c b/net/ipv4/netfilter/nf_tables_arp.c index 8af01a5e8f98..b90d16c332ab 100644 --- a/net/ipv4/netfilter/nf_tables_arp.c +++ b/net/ipv4/netfilter/nf_tables_arp.c @@ -69,10 +69,10 @@ static struct pernet_operations nf_tables_arp_net_ops = { }; static const struct nf_chain_type filter_arp = { - .family = NFPROTO_ARP, .name = "filter", .type = NFT_CHAIN_T_DEFAULT, - .me = THIS_MODULE, + .family = NFPROTO_ARP, + .owner = THIS_MODULE, .hook_mask = (1 << NF_ARP_IN) | (1 << NF_ARP_OUT) | (1 << NF_ARP_FORWARD), diff --git a/net/ipv4/netfilter/nf_tables_ipv4.c b/net/ipv4/netfilter/nf_tables_ipv4.c index cec7805de3e3..66679fd4b022 100644 --- a/net/ipv4/netfilter/nf_tables_ipv4.c +++ b/net/ipv4/netfilter/nf_tables_ipv4.c @@ -92,10 +92,10 @@ static struct pernet_operations nf_tables_ipv4_net_ops = { }; static const struct nf_chain_type filter_ipv4 = { - .family = NFPROTO_IPV4, .name = "filter", .type = NFT_CHAIN_T_DEFAULT, - .me = THIS_MODULE, + .family = NFPROTO_IPV4, + .owner = THIS_MODULE, .hook_mask = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_LOCAL_OUT) | (1 << NF_INET_FORWARD) | diff --git a/net/ipv4/netfilter/nft_chain_nat_ipv4.c b/net/ipv4/netfilter/nft_chain_nat_ipv4.c index 9e535c2c2cd2..208d60afaaa0 100644 --- a/net/ipv4/netfilter/nft_chain_nat_ipv4.c +++ b/net/ipv4/netfilter/nft_chain_nat_ipv4.c @@ -165,20 +165,20 @@ static unsigned int nf_nat_output(const struct nf_hook_ops *ops, } static const struct nf_chain_type nft_chain_nat_ipv4 = { - .family = NFPROTO_IPV4, .name = "nat", .type = NFT_CHAIN_T_NAT, + .family = NFPROTO_IPV4, + .owner = THIS_MODULE, .hook_mask = (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_POST_ROUTING) | (1 << NF_INET_LOCAL_OUT) | (1 << NF_INET_LOCAL_IN), - .fn = { + .hooks = { [NF_INET_PRE_ROUTING] = nf_nat_prerouting, [NF_INET_POST_ROUTING] = nf_nat_postrouting, [NF_INET_LOCAL_OUT] = nf_nat_output, [NF_INET_LOCAL_IN] = nf_nat_fn, }, - .me = THIS_MODULE, }; static int __init nft_chain_nat_init(void) diff --git a/net/ipv4/netfilter/nft_chain_route_ipv4.c b/net/ipv4/netfilter/nft_chain_route_ipv4.c index 2dd2eeaad15f..67db1bbde1c8 100644 --- a/net/ipv4/netfilter/nft_chain_route_ipv4.c +++ b/net/ipv4/netfilter/nft_chain_route_ipv4.c @@ -62,14 +62,14 @@ static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops, } static const struct nf_chain_type nft_chain_route_ipv4 = { - .family = NFPROTO_IPV4, .name = "route", .type = NFT_CHAIN_T_ROUTE, + .family = NFPROTO_IPV4, + .owner = THIS_MODULE, .hook_mask = (1 << NF_INET_LOCAL_OUT), - .fn = { + .hooks = { [NF_INET_LOCAL_OUT] = nf_route_table_hook, }, - .me = THIS_MODULE, }; static int __init nft_chain_route_init(void) diff --git a/net/ipv6/netfilter/nf_tables_ipv6.c b/net/ipv6/netfilter/nf_tables_ipv6.c index 758a32b0e2ff..859fca0432ff 100644 --- a/net/ipv6/netfilter/nf_tables_ipv6.c +++ b/net/ipv6/netfilter/nf_tables_ipv6.c @@ -91,10 +91,10 @@ static struct pernet_operations nf_tables_ipv6_net_ops = { }; static const struct nf_chain_type filter_ipv6 = { - .family = NFPROTO_IPV6, .name = "filter", .type = NFT_CHAIN_T_DEFAULT, - .me = THIS_MODULE, + .family = NFPROTO_IPV6, + .owner = THIS_MODULE, .hook_mask = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_LOCAL_OUT) | (1 << NF_INET_FORWARD) | diff --git a/net/ipv6/netfilter/nft_chain_nat_ipv6.c b/net/ipv6/netfilter/nft_chain_nat_ipv6.c index efd1d57a610a..9ed60ab833f5 100644 --- a/net/ipv6/netfilter/nft_chain_nat_ipv6.c +++ b/net/ipv6/netfilter/nft_chain_nat_ipv6.c @@ -171,20 +171,20 @@ static unsigned int nf_nat_ipv6_output(const struct nf_hook_ops *ops, } static const struct nf_chain_type nft_chain_nat_ipv6 = { - .family = NFPROTO_IPV6, .name = "nat", .type = NFT_CHAIN_T_NAT, + .family = NFPROTO_IPV6, + .owner = THIS_MODULE, .hook_mask = (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_POST_ROUTING) | (1 << NF_INET_LOCAL_OUT) | (1 << NF_INET_LOCAL_IN), - .fn = { + .hooks = { [NF_INET_PRE_ROUTING] = nf_nat_ipv6_prerouting, [NF_INET_POST_ROUTING] = nf_nat_ipv6_postrouting, [NF_INET_LOCAL_OUT] = nf_nat_ipv6_output, [NF_INET_LOCAL_IN] = nf_nat_ipv6_fn, }, - .me = THIS_MODULE, }; static int __init nft_chain_nat_ipv6_init(void) diff --git a/net/ipv6/netfilter/nft_chain_route_ipv6.c b/net/ipv6/netfilter/nft_chain_route_ipv6.c index 3620f8851eba..b2b7effa896b 100644 --- a/net/ipv6/netfilter/nft_chain_route_ipv6.c +++ b/net/ipv6/netfilter/nft_chain_route_ipv6.c @@ -60,14 +60,14 @@ static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops, } static const struct nf_chain_type nft_chain_route_ipv6 = { - .family = NFPROTO_IPV6, .name = "route", .type = NFT_CHAIN_T_ROUTE, + .family = NFPROTO_IPV6, + .owner = THIS_MODULE, .hook_mask = (1 << NF_INET_LOCAL_OUT), - .fn = { + .hooks = { [NF_INET_LOCAL_OUT] = nf_route_table_hook, }, - .me = THIS_MODULE, }; static int __init nft_chain_route_init(void) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index acdd9d68d52f..c8ca3b8762b4 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -929,9 +929,9 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, if (!(type->hook_mask & (1 << hooknum))) return -EOPNOTSUPP; - if (!try_module_get(type->me)) + if (!try_module_get(type->owner)) return -ENOENT; - hookfn = type->fn[hooknum]; + hookfn = type->hooks[hooknum]; basechain = kzalloc(sizeof(*basechain), GFP_KERNEL); if (basechain == NULL) @@ -941,7 +941,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, err = nf_tables_counters(basechain, nla[NFTA_CHAIN_COUNTERS]); if (err < 0) { - module_put(type->me); + module_put(type->owner); kfree(basechain); return err; } @@ -950,7 +950,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, newstats = alloc_percpu(struct nft_stats); if (newstats == NULL) { - module_put(type->me); + module_put(type->owner); kfree(basechain); return -ENOMEM; } @@ -992,7 +992,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, chain->flags & NFT_BASE_CHAIN) { err = nf_register_hooks(nft_base_chain(chain)->ops, afi->nops); if (err < 0) { - module_put(basechain->type->me); + module_put(basechain->type->owner); free_percpu(basechain->stats); kfree(basechain); return err; @@ -1013,7 +1013,7 @@ static void nf_tables_rcu_chain_destroy(struct rcu_head *head) BUG_ON(chain->use > 0); if (chain->flags & NFT_BASE_CHAIN) { - module_put(nft_base_chain(chain)->type->me); + module_put(nft_base_chain(chain)->type->owner); free_percpu(nft_base_chain(chain)->stats); kfree(nft_base_chain(chain)); } else diff --git a/net/netfilter/nf_tables_inet.c b/net/netfilter/nf_tables_inet.c index ee29ba2829d0..84478de179ea 100644 --- a/net/netfilter/nf_tables_inet.c +++ b/net/netfilter/nf_tables_inet.c @@ -67,10 +67,10 @@ static struct pernet_operations nf_tables_inet_net_ops = { }; static const struct nf_chain_type filter_inet = { - .family = NFPROTO_INET, .name = "filter", .type = NFT_CHAIN_T_DEFAULT, - .me = THIS_MODULE, + .family = NFPROTO_INET, + .owner = THIS_MODULE, .hook_mask = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_LOCAL_OUT) | (1 << NF_INET_FORWARD) | -- cgit v1.2.3 From 3876d22dba62ebf6582f33e1ef2160eeb95e1129 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Thu, 9 Jan 2014 18:42:43 +0000 Subject: netfilter: nf_tables: rename nft_do_chain_pktinfo() to nft_do_chain() We don't encode argument types into function names and since besides nft_do_chain() there are only AF-specific versions, there is no risk of confusion. Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 4 ++-- net/bridge/netfilter/nf_tables_bridge.c | 2 +- net/ipv4/netfilter/nf_tables_arp.c | 2 +- net/ipv4/netfilter/nf_tables_ipv4.c | 2 +- net/ipv4/netfilter/nft_chain_nat_ipv4.c | 2 +- net/ipv4/netfilter/nft_chain_route_ipv4.c | 2 +- net/ipv6/netfilter/nf_tables_ipv6.c | 2 +- net/ipv6/netfilter/nft_chain_nat_ipv6.c | 2 +- net/ipv6/netfilter/nft_chain_route_ipv6.c | 2 +- net/netfilter/nf_tables_core.c | 4 ++-- 10 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include/net') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 342236550ef9..57c8ff7955df 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -447,8 +447,8 @@ static inline struct nft_base_chain *nft_base_chain(const struct nft_chain *chai return container_of(chain, struct nft_base_chain, chain); } -unsigned int nft_do_chain_pktinfo(struct nft_pktinfo *pkt, - const struct nf_hook_ops *ops); +unsigned int nft_do_chain(struct nft_pktinfo *pkt, + const struct nf_hook_ops *ops); /** * struct nft_table - nf_tables table diff --git a/net/bridge/netfilter/nf_tables_bridge.c b/net/bridge/netfilter/nf_tables_bridge.c index c83fab5f8736..5bcc0d8b31f2 100644 --- a/net/bridge/netfilter/nf_tables_bridge.c +++ b/net/bridge/netfilter/nf_tables_bridge.c @@ -25,7 +25,7 @@ nft_do_chain_bridge(const struct nf_hook_ops *ops, nft_set_pktinfo(&pkt, ops, skb, in, out); - return nft_do_chain_pktinfo(&pkt, ops); + return nft_do_chain(&pkt, ops); } static struct nft_af_info nft_af_bridge __read_mostly = { diff --git a/net/ipv4/netfilter/nf_tables_arp.c b/net/ipv4/netfilter/nf_tables_arp.c index b90d16c332ab..19412a4063fb 100644 --- a/net/ipv4/netfilter/nf_tables_arp.c +++ b/net/ipv4/netfilter/nf_tables_arp.c @@ -25,7 +25,7 @@ nft_do_chain_arp(const struct nf_hook_ops *ops, nft_set_pktinfo(&pkt, ops, skb, in, out); - return nft_do_chain_pktinfo(&pkt, ops); + return nft_do_chain(&pkt, ops); } static struct nft_af_info nft_af_arp __read_mostly = { diff --git a/net/ipv4/netfilter/nf_tables_ipv4.c b/net/ipv4/netfilter/nf_tables_ipv4.c index 66679fd4b022..fec163a6ac38 100644 --- a/net/ipv4/netfilter/nf_tables_ipv4.c +++ b/net/ipv4/netfilter/nf_tables_ipv4.c @@ -28,7 +28,7 @@ static unsigned int nft_do_chain_ipv4(const struct nf_hook_ops *ops, nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out); - return nft_do_chain_pktinfo(&pkt, ops); + return nft_do_chain(&pkt, ops); } static unsigned int nft_ipv4_output(const struct nf_hook_ops *ops, diff --git a/net/ipv4/netfilter/nft_chain_nat_ipv4.c b/net/ipv4/netfilter/nft_chain_nat_ipv4.c index 208d60afaaa0..b5b256d45e67 100644 --- a/net/ipv4/netfilter/nft_chain_nat_ipv4.c +++ b/net/ipv4/netfilter/nft_chain_nat_ipv4.c @@ -75,7 +75,7 @@ static unsigned int nf_nat_fn(const struct nf_hook_ops *ops, nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out); - ret = nft_do_chain_pktinfo(&pkt, ops); + ret = nft_do_chain(&pkt, ops); if (ret != NF_ACCEPT) return ret; if (!nf_nat_initialized(ct, maniptype)) { diff --git a/net/ipv4/netfilter/nft_chain_route_ipv4.c b/net/ipv4/netfilter/nft_chain_route_ipv4.c index 67db1bbde1c8..125b66766c0a 100644 --- a/net/ipv4/netfilter/nft_chain_route_ipv4.c +++ b/net/ipv4/netfilter/nft_chain_route_ipv4.c @@ -47,7 +47,7 @@ static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops, daddr = iph->daddr; tos = iph->tos; - ret = nft_do_chain_pktinfo(&pkt, ops); + ret = nft_do_chain(&pkt, ops); if (ret != NF_DROP && ret != NF_QUEUE) { iph = ip_hdr(skb); diff --git a/net/ipv6/netfilter/nf_tables_ipv6.c b/net/ipv6/netfilter/nf_tables_ipv6.c index 859fca0432ff..59a43b474dca 100644 --- a/net/ipv6/netfilter/nf_tables_ipv6.c +++ b/net/ipv6/netfilter/nf_tables_ipv6.c @@ -28,7 +28,7 @@ static unsigned int nft_do_chain_ipv6(const struct nf_hook_ops *ops, if (nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out) < 0) return NF_DROP; - return nft_do_chain_pktinfo(&pkt, ops); + return nft_do_chain(&pkt, ops); } static unsigned int nft_ipv6_output(const struct nf_hook_ops *ops, diff --git a/net/ipv6/netfilter/nft_chain_nat_ipv6.c b/net/ipv6/netfilter/nft_chain_nat_ipv6.c index 9ed60ab833f5..9c3297a768fd 100644 --- a/net/ipv6/netfilter/nft_chain_nat_ipv6.c +++ b/net/ipv6/netfilter/nft_chain_nat_ipv6.c @@ -79,7 +79,7 @@ static unsigned int nf_nat_ipv6_fn(const struct nf_hook_ops *ops, nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out); - ret = nft_do_chain_pktinfo(&pkt, ops); + ret = nft_do_chain(&pkt, ops); if (ret != NF_ACCEPT) return ret; if (!nf_nat_initialized(ct, maniptype)) { diff --git a/net/ipv6/netfilter/nft_chain_route_ipv6.c b/net/ipv6/netfilter/nft_chain_route_ipv6.c index b2b7effa896b..42031299585e 100644 --- a/net/ipv6/netfilter/nft_chain_route_ipv6.c +++ b/net/ipv6/netfilter/nft_chain_route_ipv6.c @@ -47,7 +47,7 @@ static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops, /* flowlabel and prio (includes version, which shouldn't change either */ flowlabel = *((u32 *)ipv6_hdr(skb)); - ret = nft_do_chain_pktinfo(&pkt, ops); + ret = nft_do_chain(&pkt, ops); if (ret != NF_DROP && ret != NF_QUEUE && (memcmp(&ipv6_hdr(skb)->saddr, &saddr, sizeof(saddr)) || memcmp(&ipv6_hdr(skb)->daddr, &daddr, sizeof(daddr)) || diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c index 5aae31772ff7..0d879fcb8763 100644 --- a/net/netfilter/nf_tables_core.c +++ b/net/netfilter/nf_tables_core.c @@ -116,7 +116,7 @@ static inline void nft_trace_packet(const struct nft_pktinfo *pkt, } unsigned int -nft_do_chain_pktinfo(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops) +nft_do_chain(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops) { const struct nft_chain *chain = ops->priv; const struct nft_rule *rule; @@ -216,7 +216,7 @@ next_rule: return nft_base_chain(chain)->policy; } -EXPORT_SYMBOL_GPL(nft_do_chain_pktinfo); +EXPORT_SYMBOL_GPL(nft_do_chain); int __init nf_tables_core_module_init(void) { -- cgit v1.2.3 From b77cf4f8e1892e192ec52df5dd8c158b300fc496 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 9 Jan 2014 00:00:38 +0100 Subject: mac80211: handle MMPDUs at EOSP correctly If a uAPSD service period ends with an MMPDU, we currently just send that MMPDU, but it obviously won't get the EOSP bit set as it doesn't have a QoS header. This contradicts the standard, so add a QoS-nulldata frame after the MMPDU to properly terminate the service period with a frame that has EOSP set. Also fix a bug wrt. the TID for the MMPDU, it shouldn't be set to 0 unconditionally but use the actual TID that was assigned. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 5 ++++ net/mac80211/sta_info.c | 70 ++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 62 insertions(+), 13 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 25b18877747f..f4ab2fb4d50c 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2119,6 +2119,11 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb); * appropriately (only the last frame may have %IEEE80211_TX_STATUS_EOSP) * and also take care of the EOSP and MORE_DATA bits in the frame. * The driver may also use ieee80211_sta_eosp() in this case. + * + * Note that if the driver ever buffers frames other than QoS-data + * frames, it must take care to never send a non-QoS-data frame as + * the last frame in a service period, adding a QoS-nulldata frame + * after a non-QoS-data frame if needed. */ /** diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 93e2157a5b7b..decd30c1e290 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1153,7 +1153,8 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, int tid, - enum ieee80211_frame_release_type reason) + enum ieee80211_frame_release_type reason, + bool call_driver) { struct ieee80211_local *local = sdata->local; struct ieee80211_qos_hdr *nullfunc; @@ -1211,7 +1212,9 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, IEEE80211_TX_STATUS_EOSP | IEEE80211_TX_CTL_REQ_TX_STATUS; - drv_allow_buffered_frames(local, sta, BIT(tid), 1, reason, false); + if (call_driver) + drv_allow_buffered_frames(local, sta, BIT(tid), 1, + reason, false); skb->dev = sdata->dev; @@ -1334,12 +1337,13 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, /* This will evaluate to 1, 3, 5 or 7. */ tid = 7 - ((ffs(~ignored_acs) - 1) << 1); - ieee80211_send_null_response(sdata, sta, tid, reason); + ieee80211_send_null_response(sdata, sta, tid, reason, true); } else if (!driver_release_tids) { struct sk_buff_head pending; struct sk_buff *skb; int num = 0; u16 tids = 0; + bool need_null = false; skb_queue_head_init(&pending); @@ -1373,22 +1377,57 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, ieee80211_is_qos_nullfunc(hdr->frame_control)) qoshdr = ieee80211_get_qos_ctl(hdr); - /* end service period after last frame */ - if (skb_queue_empty(&frames)) { - if (reason == IEEE80211_FRAME_RELEASE_UAPSD && - qoshdr) - *qoshdr |= IEEE80211_QOS_CTL_EOSP; + tids |= BIT(skb->priority); + __skb_queue_tail(&pending, skb); + + /* end service period after last frame or add one */ + if (!skb_queue_empty(&frames)) + continue; + + if (reason != IEEE80211_FRAME_RELEASE_UAPSD) { + /* for PS-Poll, there's only one frame */ info->flags |= IEEE80211_TX_STATUS_EOSP | IEEE80211_TX_CTL_REQ_TX_STATUS; + break; } - if (qoshdr) - tids |= BIT(*qoshdr & IEEE80211_QOS_CTL_TID_MASK); - else - tids |= BIT(0); + /* For uAPSD, things are a bit more complicated. If the + * last frame has a QoS header (i.e. is a QoS-data or + * QoS-nulldata frame) then just set the EOSP bit there + * and be done. + * If the frame doesn't have a QoS header (which means + * it should be a bufferable MMPDU) then we can't set + * the EOSP bit in the QoS header; add a QoS-nulldata + * frame to the list to send it after the MMPDU. + * + * Note that this code is only in the mac80211-release + * code path, we assume that the driver will not buffer + * anything but QoS-data frames, or if it does, will + * create the QoS-nulldata frame by itself if needed. + * + * Cf. 802.11-2012 10.2.1.10 (c). + */ + if (qoshdr) { + *qoshdr |= IEEE80211_QOS_CTL_EOSP; - __skb_queue_tail(&pending, skb); + info->flags |= IEEE80211_TX_STATUS_EOSP | + IEEE80211_TX_CTL_REQ_TX_STATUS; + } else { + /* The standard isn't completely clear on this + * as it says the more-data bit should be set + * if there are more BUs. The QoS-Null frame + * we're about to send isn't buffered yet, we + * only create it below, but let's pretend it + * was buffered just in case some clients only + * expect more-data=0 when eosp=1. + */ + hdr->frame_control |= + cpu_to_le16(IEEE80211_FCTL_MOREDATA); + need_null = true; + num++; + } + break; } drv_allow_buffered_frames(local, sta, tids, num, @@ -1396,6 +1435,11 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, ieee80211_add_pending_skbs(local, &pending); + if (need_null) + ieee80211_send_null_response( + sdata, sta, find_highest_prio_tid(tids), + reason, false); + sta_info_recalc_tim(sta); } else { /* -- cgit v1.2.3 From f87c10a8aa1e82498c42d0335524d6ae7cf5a52b Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Thu, 9 Jan 2014 10:01:15 +0100 Subject: ipv4: introduce ip_dst_mtu_maybe_forward and protect forwarding path against pmtu spoofing While forwarding we should not use the protocol path mtu to calculate the mtu for a forwarded packet but instead use the interface mtu. We mark forwarded skbs in ip_forward with IPSKB_FORWARDED, which was introduced for multicast forwarding. But as it does not conflict with our usage in unicast code path it is perfect for reuse. I moved the functions ip_sk_accept_pmtu, ip_sk_use_pmtu and ip_skb_dst_mtu along with the new ip_dst_mtu_maybe_forward to net/ip.h to fix circular dependencies because of IPSKB_FORWARDED. Because someone might have written a software which does probe destinations manually and expects the kernel to honour those path mtus I introduced a new per-namespace "ip_forward_use_pmtu" knob so someone can disable this new behaviour. We also still use mtus which are locked on a route for forwarding. The reason for this change is, that path mtus information can be injected into the kernel via e.g. icmp_err protocol handler without verification of local sockets. As such, this could cause the IPv4 forwarding path to wrongfully emit fragmentation needed notifications or start to fragment packets along a path. Tunnel and ipsec output paths clear IPCB again, thus IPSKB_FORWARDED won't be set and further fragmentation logic will use the path mtu to determine the fragmentation size. They also recheck packet size with help of path mtu discovery and report appropriate errors. Cc: Eric Dumazet Cc: David Miller Cc: John Heffner Cc: Steffen Klassert Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 13 +++++++++++++ include/net/ip.h | 33 +++++++++++++++++++++++++++++++++ include/net/netns/ipv4.h | 1 + include/net/route.h | 19 +++---------------- net/ipv4/ip_forward.c | 7 +++++-- net/ipv4/ip_output.c | 8 +++++--- net/ipv4/route.c | 3 --- net/ipv4/sysctl_net_ipv4.c | 7 +++++++ 8 files changed, 67 insertions(+), 24 deletions(-) (limited to 'include/net') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 7373115407e4..0d71fa962d8a 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -32,6 +32,19 @@ ip_no_pmtu_disc - INTEGER min_pmtu - INTEGER default 552 - minimum discovered Path MTU +ip_forward_use_pmtu - BOOLEAN + By default we don't trust protocol path MTUs while forwarding + because they could be easily forged and can lead to unwanted + fragmentation by the router. + You only need to enable this if you have user-space software + which tries to discover path mtus by itself and depends on the + kernel honoring this information. This is normally not the + case. + Default: 0 (disabled) + Possible values: + 0 - disabled + 1 - enabled + route/max_size - INTEGER Maximum number of routes allowed in the kernel. Increase this when using large numbers of interfaces and/or routes. diff --git a/include/net/ip.h b/include/net/ip.h index 535664477c4a..0dab95c2e4d5 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -263,6 +263,39 @@ int ip_dont_fragment(struct sock *sk, struct dst_entry *dst) !(dst_metric_locked(dst, RTAX_MTU))); } +static inline bool ip_sk_accept_pmtu(const struct sock *sk) +{ + return inet_sk(sk)->pmtudisc != IP_PMTUDISC_INTERFACE; +} + +static inline bool ip_sk_use_pmtu(const struct sock *sk) +{ + return inet_sk(sk)->pmtudisc < IP_PMTUDISC_PROBE; +} + +static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst, + bool forwarding) +{ + struct net *net = dev_net(dst->dev); + + if (net->ipv4.sysctl_ip_fwd_use_pmtu || + dst_metric_locked(dst, RTAX_MTU) || + !forwarding) + return dst_mtu(dst); + + return min(dst->dev->mtu, IP_MAX_MTU); +} + +static inline unsigned int ip_skb_dst_mtu(const struct sk_buff *skb) +{ + if (!skb->sk || ip_sk_use_pmtu(skb->sk)) { + bool forwarding = IPCB(skb)->flags & IPSKB_FORWARDED; + return ip_dst_mtu_maybe_forward(skb_dst(skb), forwarding); + } else { + return min(skb_dst(skb)->dev->mtu, IP_MAX_MTU); + } +} + void __ip_select_ident(struct iphdr *iph, struct dst_entry *dst, int more); static inline void ip_select_ident(struct sk_buff *skb, struct dst_entry *dst, struct sock *sk) diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 929a668e91a9..80f500a29498 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -70,6 +70,7 @@ struct netns_ipv4 { int sysctl_tcp_ecn; int sysctl_ip_no_pmtu_disc; + int sysctl_ip_fwd_use_pmtu; kgid_t sysctl_ping_group_range[2]; diff --git a/include/net/route.h b/include/net/route.h index 638e3ebf76f3..9d1f423d5944 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -36,6 +36,9 @@ #include #include +/* IPv4 datagram length is stored into 16bit field (tot_len) */ +#define IP_MAX_MTU 0xFFFFU + #define RTO_ONLINK 0x01 #define RT_CONN_FLAGS(sk) (RT_TOS(inet_sk(sk)->tos) | sock_flag(sk, SOCK_LOCALROUTE)) @@ -311,20 +314,4 @@ static inline int ip4_dst_hoplimit(const struct dst_entry *dst) return hoplimit; } -static inline bool ip_sk_accept_pmtu(const struct sock *sk) -{ - return inet_sk(sk)->pmtudisc != IP_PMTUDISC_INTERFACE; -} - -static inline bool ip_sk_use_pmtu(const struct sock *sk) -{ - return inet_sk(sk)->pmtudisc < IP_PMTUDISC_PROBE; -} - -static inline int ip_skb_dst_mtu(const struct sk_buff *skb) -{ - return (!skb->sk || ip_sk_use_pmtu(skb->sk)) ? - dst_mtu(skb_dst(skb)) : skb_dst(skb)->dev->mtu; -} - #endif /* _ROUTE_H */ diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c index 694de3b7aebf..e9f1217a8afd 100644 --- a/net/ipv4/ip_forward.c +++ b/net/ipv4/ip_forward.c @@ -54,6 +54,7 @@ static int ip_forward_finish(struct sk_buff *skb) int ip_forward(struct sk_buff *skb) { + u32 mtu; struct iphdr *iph; /* Our header */ struct rtable *rt; /* Route we use */ struct ip_options *opt = &(IPCB(skb)->opt); @@ -88,11 +89,13 @@ int ip_forward(struct sk_buff *skb) if (opt->is_strictroute && rt->rt_uses_gateway) goto sr_failed; - if (unlikely(skb->len > dst_mtu(&rt->dst) && !skb_is_gso(skb) && + IPCB(skb)->flags |= IPSKB_FORWARDED; + mtu = ip_dst_mtu_maybe_forward(&rt->dst, true); + if (unlikely(skb->len > mtu && !skb_is_gso(skb) && (ip_hdr(skb)->frag_off & htons(IP_DF))) && !skb->local_df) { IP_INC_STATS(dev_net(rt->dst.dev), IPSTATS_MIB_FRAGFAILS); icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, - htonl(dst_mtu(&rt->dst))); + htonl(mtu)); goto drop; } diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index df184616493f..9a78804cfe9c 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -449,6 +449,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) __be16 not_last_frag; struct rtable *rt = skb_rtable(skb); int err = 0; + bool forwarding = IPCB(skb)->flags & IPSKB_FORWARDED; dev = rt->dst.dev; @@ -458,12 +459,13 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) iph = ip_hdr(skb); + mtu = ip_dst_mtu_maybe_forward(&rt->dst, forwarding); if (unlikely(((iph->frag_off & htons(IP_DF)) && !skb->local_df) || (IPCB(skb)->frag_max_size && - IPCB(skb)->frag_max_size > dst_mtu(&rt->dst)))) { + IPCB(skb)->frag_max_size > mtu))) { IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS); icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, - htonl(ip_skb_dst_mtu(skb))); + htonl(mtu)); kfree_skb(skb); return -EMSGSIZE; } @@ -473,7 +475,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) */ hlen = iph->ihl * 4; - mtu = dst_mtu(&rt->dst) - hlen; /* Size of data space */ + mtu = mtu - hlen; /* Size of data space */ #ifdef CONFIG_BRIDGE_NETFILTER if (skb->nf_bridge) mtu -= nf_bridge_mtu_reduction(skb); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index f8da28278014..25071b48921c 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -112,9 +112,6 @@ #define RT_FL_TOS(oldflp4) \ ((oldflp4)->flowi4_tos & (IPTOS_RT_MASK | RTO_ONLINK)) -/* IPv4 datagram length is stored into 16bit field (tot_len) */ -#define IP_MAX_MTU 0xFFFF - #define RT_GC_TIMEOUT (300*HZ) static int ip_rt_max_size; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 1d2480ac2bb6..44eba052b43d 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -831,6 +831,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "ip_forward_use_pmtu", + .data = &init_net.ipv4.sysctl_ip_fwd_use_pmtu, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { } }; -- cgit v1.2.3 From 8ed1dc44d3e9e8387a104b1ae8f92e9a3fbf1b1e Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Thu, 9 Jan 2014 10:01:17 +0100 Subject: ipv4: introduce hardened ip_no_pmtu_disc mode This new ip_no_pmtu_disc mode only allowes fragmentation-needed errors to be honored by protocols which do more stringent validation on the ICMP's packet payload. This knob is useful for people who e.g. want to run an unmodified DNS server in a namespace where they need to use pmtu for TCP connections (as they are used for zone transfers or fallback for requests) but don't want to use possibly spoofed UDP pmtu information. Currently the whitelisted protocols are TCP, SCTP and DCCP as they check if the returned packet is in the window or if the association is valid. Cc: Eric Dumazet Cc: David Miller Cc: John Heffner Suggested-by: Florian Weimer Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 13 ++++++++++++- include/net/protocol.h | 7 ++++++- net/dccp/ipv4.c | 1 + net/ipv4/af_inet.c | 1 + net/ipv4/icmp.c | 28 ++++++++++++++++++++++++---- net/sctp/protocol.c | 1 + 6 files changed, 45 insertions(+), 6 deletions(-) (limited to 'include/net') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 0d71fa962d8a..c97932c88ea3 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -26,7 +26,18 @@ ip_no_pmtu_disc - INTEGER discarded. Outgoing frames are handled the same as in mode 1, implicitly setting IP_PMTUDISC_DONT on every created socket. - Possible values: 0-2 + Mode 3 is a hardend pmtu discover mode. The kernel will only + accept fragmentation-needed errors if the underlying protocol + can verify them besides a plain socket lookup. Current + protocols for which pmtu events will be honored are TCP, SCTP + and DCCP as they verify e.g. the sequence number or the + association. This mode should not be enabled globally but is + only intended to secure e.g. name servers in namespaces where + TCP path mtu must still work but path MTU information of other + protocols should be discarded. If enabled globally this mode + could break other protocols. + + Possible values: 0-3 Default: FALSE min_pmtu - INTEGER diff --git a/include/net/protocol.h b/include/net/protocol.h index fbf7676c9a02..0e5f8665d7fb 100644 --- a/include/net/protocol.h +++ b/include/net/protocol.h @@ -43,7 +43,12 @@ struct net_protocol { int (*handler)(struct sk_buff *skb); void (*err_handler)(struct sk_buff *skb, u32 info); unsigned int no_policy:1, - netns_ok:1; + netns_ok:1, + /* does the protocol do more stringent + * icmp tag validation than simple + * socket lookup? + */ + icmp_strict_tag_validation:1; }; #if IS_ENABLED(CONFIG_IPV6) diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 88299c29101d..22b5d818b200 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -989,6 +989,7 @@ static const struct net_protocol dccp_v4_protocol = { .err_handler = dccp_v4_err, .no_policy = 1, .netns_ok = 1, + .icmp_strict_tag_validation = 1, }; static const struct proto_ops inet_dccp_ops = { diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 6268a4751e64..ecd2c3f245ce 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1545,6 +1545,7 @@ static const struct net_protocol tcp_protocol = { .err_handler = tcp_v4_err, .no_policy = 1, .netns_ok = 1, + .icmp_strict_tag_validation = 1, }; static const struct net_protocol udp_protocol = { diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index fb3c5637199d..0134663fdbce 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -668,6 +668,16 @@ static void icmp_socket_deliver(struct sk_buff *skb, u32 info) rcu_read_unlock(); } +static bool icmp_tag_validation(int proto) +{ + bool ok; + + rcu_read_lock(); + ok = rcu_dereference(inet_protos[proto])->icmp_strict_tag_validation; + rcu_read_unlock(); + return ok; +} + /* * Handle ICMP_DEST_UNREACH, ICMP_TIME_EXCEED, ICMP_QUENCH, and * ICMP_PARAMETERPROB. @@ -705,12 +715,22 @@ static void icmp_unreach(struct sk_buff *skb) case ICMP_PORT_UNREACH: break; case ICMP_FRAG_NEEDED: - if (net->ipv4.sysctl_ip_no_pmtu_disc == 2) { - goto out; - } else if (net->ipv4.sysctl_ip_no_pmtu_disc) { + /* for documentation of the ip_no_pmtu_disc + * values please see + * Documentation/networking/ip-sysctl.txt + */ + switch (net->ipv4.sysctl_ip_no_pmtu_disc) { + default: LIMIT_NETDEBUG(KERN_INFO pr_fmt("%pI4: fragmentation needed and DF set\n"), &iph->daddr); - } else { + break; + case 2: + goto out; + case 3: + if (!icmp_tag_validation(iph->protocol)) + goto out; + /* fall through */ + case 0: info = ntohs(icmph->un.frag.mtu); if (!info) goto out; diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 34b7726bcd7f..7c161084f241 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -1030,6 +1030,7 @@ static const struct net_protocol sctp_protocol = { .err_handler = sctp_v4_err, .no_policy = 1, .netns_ok = 1, + .icmp_strict_tag_validation = 1, }; /* IPv4 address related functions. */ -- cgit v1.2.3 From 4f7b91404cd5da3657a82b00394f4f5dfbff13d6 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Sat, 14 Dec 2013 20:09:06 +0100 Subject: cfg80211: make regulatory_hint() remove REGULATORY_CUSTOM_REG The REGULATORY_CUSTOM_REG can be used during early init with the goal of overriding the wiphy's default regulatory settings in case the alpha2 of the device is not known. In the case that the alpha2 becomes known lets avoid having drivers having to clear the REGULATORY_CUSTOM_REG flag by doing it for them when regulatory_hint() is used. Cc: Sujith Manoharan Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/regd.c | 2 -- include/net/regulatory.h | 4 ++++ net/wireless/reg.c | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index 56c3d05c6224..e5e905910db4 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -652,8 +652,6 @@ ath_regd_init_wiphy(struct ath_regulatory *reg, } wiphy_apply_custom_regulatory(wiphy, regd); - if (!ath_is_world_regd(reg)) - wiphy->regulatory_flags &= ~REGULATORY_CUSTOM_REG; ath_reg_apply_radar_flags(wiphy); ath_reg_apply_world_flags(wiphy, NL80211_REGDOM_SET_BY_DRIVER, reg); return 0; diff --git a/include/net/regulatory.h b/include/net/regulatory.h index c96a0b86f342..b07cdc9fa454 100644 --- a/include/net/regulatory.h +++ b/include/net/regulatory.h @@ -96,6 +96,10 @@ struct regulatory_request { * initiator is %REGDOM_SET_BY_CORE). Drivers that use * wiphy_apply_custom_regulatory() should have this flag set * or the regulatory core will set it for the wiphy. + * If you use regulatory_hint() *after* using + * wiphy_apply_custom_regulatory() the wireless core will + * clear the REGULATORY_CUSTOM_REG for your wiphy as it would be + * implied that the device somehow gained knowledge of its region. * @REGULATORY_STRICT_REG: tells us that the wiphy for this device * has regulatory domain that it wishes to be considered as the * superset for regulatory rules. After this device gets its regulatory diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 7d20d844ca60..9b897fca7487 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1853,6 +1853,8 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2) if (WARN_ON(!alpha2 || !wiphy)) return -EINVAL; + wiphy->regulatory_flags &= ~REGULATORY_CUSTOM_REG; + request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); if (!request) return -ENOMEM; -- cgit v1.2.3 From ddafd34f419546f1eb7c343178685f059c3cf127 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Thu, 9 Jan 2014 16:13:59 -0800 Subject: net_sched: act: move idx_gen into struct tcf_hashinfo There is no need to store the index separatedly since tcf_hashinfo is allocated statically too. Cc: Jamal Hadi Salim Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/net/act_api.h | 7 ++++--- net/sched/act_api.c | 10 +++++----- net/sched/act_csum.c | 3 +-- net/sched/act_gact.c | 3 +-- net/sched/act_ipt.c | 3 +-- net/sched/act_mirred.c | 3 +-- net/sched/act_nat.c | 3 +-- net/sched/act_pedit.c | 3 +-- net/sched/act_police.c | 3 +-- net/sched/act_simple.c | 3 +-- net/sched/act_skbedit.c | 3 +-- 11 files changed, 18 insertions(+), 26 deletions(-) (limited to 'include/net') diff --git a/include/net/act_api.h b/include/net/act_api.h index d34e1f4d897b..268f9e6babd7 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -39,6 +39,7 @@ struct tcf_hashinfo { struct hlist_head *htab; unsigned int hmask; spinlock_t lock; + u32 index; }; static inline unsigned int tcf_hash(u32 index, unsigned int hmask) @@ -51,6 +52,7 @@ static inline int tcf_hashinfo_init(struct tcf_hashinfo *hf, unsigned int mask) int i; spin_lock_init(&hf->lock); + hf->index = 0; hf->hmask = mask; hf->htab = kzalloc((mask + 1) * sizeof(struct hlist_head), GFP_KERNEL); @@ -105,13 +107,12 @@ struct tcf_common *tcf_hash_lookup(u32 index, struct tcf_hashinfo *hinfo); void tcf_hash_destroy(struct tcf_common *p, struct tcf_hashinfo *hinfo); int tcf_hash_release(struct tcf_common *p, int bind, struct tcf_hashinfo *hinfo); -u32 tcf_hash_new_index(u32 *idx_gen, struct tcf_hashinfo *hinfo); +u32 tcf_hash_new_index(struct tcf_hashinfo *hinfo); struct tcf_common *tcf_hash_check(u32 index, struct tc_action *a, int bind, struct tcf_hashinfo *hinfo); struct tcf_common *tcf_hash_create(u32 index, struct nlattr *est, struct tc_action *a, int size, - int bind, u32 *idx_gen, - struct tcf_hashinfo *hinfo); + int bind, struct tcf_hashinfo *hinfo); void tcf_hash_insert(struct tcf_common *p, struct tcf_hashinfo *hinfo); int tcf_register_action(struct tc_action_ops *a); diff --git a/net/sched/act_api.c b/net/sched/act_api.c index f63e1467cd3a..74ae3ff05747 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -173,16 +173,16 @@ struct tcf_common *tcf_hash_lookup(u32 index, struct tcf_hashinfo *hinfo) } EXPORT_SYMBOL(tcf_hash_lookup); -u32 tcf_hash_new_index(u32 *idx_gen, struct tcf_hashinfo *hinfo) +u32 tcf_hash_new_index(struct tcf_hashinfo *hinfo) { - u32 val = *idx_gen; + u32 val = hinfo->index; do { if (++val == 0) val = 1; } while (tcf_hash_lookup(val, hinfo)); - *idx_gen = val; + hinfo->index = val; return val; } EXPORT_SYMBOL(tcf_hash_new_index); @@ -215,7 +215,7 @@ EXPORT_SYMBOL(tcf_hash_check); struct tcf_common *tcf_hash_create(u32 index, struct nlattr *est, struct tc_action *a, int size, int bind, - u32 *idx_gen, struct tcf_hashinfo *hinfo) + struct tcf_hashinfo *hinfo) { struct tcf_common *p = kzalloc(size, GFP_KERNEL); @@ -227,7 +227,7 @@ struct tcf_common *tcf_hash_create(u32 index, struct nlattr *est, spin_lock_init(&p->tcfc_lock); INIT_HLIST_NODE(&p->tcfc_head); - p->tcfc_index = index ? index : tcf_hash_new_index(idx_gen, hinfo); + p->tcfc_index = index ? index : tcf_hash_new_index(hinfo); p->tcfc_tm.install = jiffies; p->tcfc_tm.lastuse = jiffies; if (est) { diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c index 8b1d65772a8d..ee28e1ccbbd5 100644 --- a/net/sched/act_csum.c +++ b/net/sched/act_csum.c @@ -37,7 +37,6 @@ #include #define CSUM_TAB_MASK 15 -static u32 csum_idx_gen; static struct tcf_hashinfo csum_hash_info; static const struct nla_policy csum_policy[TCA_CSUM_MAX + 1] = { @@ -67,7 +66,7 @@ static int tcf_csum_init(struct net *n, struct nlattr *nla, struct nlattr *est, pc = tcf_hash_check(parm->index, a, bind, &csum_hash_info); if (!pc) { pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind, - &csum_idx_gen, &csum_hash_info); + &csum_hash_info); if (IS_ERR(pc)) return PTR_ERR(pc); ret = ACT_P_CREATED; diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c index af5641c290fa..f26e6b890cc7 100644 --- a/net/sched/act_gact.c +++ b/net/sched/act_gact.c @@ -24,7 +24,6 @@ #include #define GACT_TAB_MASK 15 -static u32 gact_idx_gen; static struct tcf_hashinfo gact_hash_info; #ifdef CONFIG_GACT_PROB @@ -90,7 +89,7 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla, pc = tcf_hash_check(parm->index, a, bind, &gact_hash_info); if (!pc) { pc = tcf_hash_create(parm->index, est, a, sizeof(*gact), - bind, &gact_idx_gen, &gact_hash_info); + bind, &gact_hash_info); if (IS_ERR(pc)) return PTR_ERR(pc); ret = ACT_P_CREATED; diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index 242636950ea5..484bd19601e3 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -29,7 +29,6 @@ #define IPT_TAB_MASK 15 -static u32 ipt_idx_gen; static struct tcf_hashinfo ipt_hash_info; static int ipt_init_target(struct xt_entry_target *t, char *table, unsigned int hook) @@ -129,7 +128,7 @@ static int tcf_ipt_init(struct net *net, struct nlattr *nla, struct nlattr *est, pc = tcf_hash_check(index, a, bind, &ipt_hash_info); if (!pc) { pc = tcf_hash_create(index, est, a, sizeof(*ipt), bind, - &ipt_idx_gen, &ipt_hash_info); + &ipt_hash_info); if (IS_ERR(pc)) return PTR_ERR(pc); ret = ACT_P_CREATED; diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index 9dbb8cd64cb0..5d05b573a622 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -30,7 +30,6 @@ #include #define MIRRED_TAB_MASK 7 -static u32 mirred_idx_gen; static LIST_HEAD(mirred_list); static struct tcf_hashinfo mirred_hash_info; @@ -107,7 +106,7 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla, if (dev == NULL) return -EINVAL; pc = tcf_hash_create(parm->index, est, a, sizeof(*m), bind, - &mirred_idx_gen, &mirred_hash_info); + &mirred_hash_info); if (IS_ERR(pc)) return PTR_ERR(pc); ret = ACT_P_CREATED; diff --git a/net/sched/act_nat.c b/net/sched/act_nat.c index 584e65503edb..a49fa23b49dd 100644 --- a/net/sched/act_nat.c +++ b/net/sched/act_nat.c @@ -30,7 +30,6 @@ #define NAT_TAB_MASK 15 -static u32 nat_idx_gen; static struct tcf_hashinfo nat_hash_info; @@ -61,7 +60,7 @@ static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est, pc = tcf_hash_check(parm->index, a, bind, &nat_hash_info); if (!pc) { pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind, - &nat_idx_gen, &nat_hash_info); + &nat_hash_info); if (IS_ERR(pc)) return PTR_ERR(pc); ret = ACT_P_CREATED; diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index 729189341933..f361e4e3c314 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -24,7 +24,6 @@ #include #define PEDIT_TAB_MASK 15 -static u32 pedit_idx_gen; static struct tcf_hashinfo pedit_hash_info; @@ -63,7 +62,7 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, if (!parm->nkeys) return -EINVAL; pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind, - &pedit_idx_gen, &pedit_hash_info); + &pedit_hash_info); if (IS_ERR(pc)) return PTR_ERR(pc); p = to_pedit(pc); diff --git a/net/sched/act_police.c b/net/sched/act_police.c index 9295b86d5319..a719fdff575e 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -41,7 +41,6 @@ struct tcf_police { container_of(pc, struct tcf_police, common) #define POL_TAB_MASK 15 -static u32 police_idx_gen; static struct tcf_hashinfo police_hash_info; /* old policer structure from before tc actions */ @@ -251,7 +250,7 @@ override: police->tcfp_t_c = ktime_to_ns(ktime_get()); police->tcf_index = parm->index ? parm->index : - tcf_hash_new_index(&police_idx_gen, &police_hash_info); + tcf_hash_new_index(&police_hash_info); h = tcf_hash(police->tcf_index, POL_TAB_MASK); spin_lock_bh(&police_hash_info.lock); hlist_add_head(&police->tcf_head, &police_hash_info.htab[h]); diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c index b44491e3ec17..f7d5406c1fe2 100644 --- a/net/sched/act_simple.c +++ b/net/sched/act_simple.c @@ -25,7 +25,6 @@ #include #define SIMP_TAB_MASK 7 -static u32 simp_idx_gen; static struct tcf_hashinfo simp_hash_info; #define SIMP_MAX_DATA 32 @@ -118,7 +117,7 @@ static int tcf_simp_init(struct net *net, struct nlattr *nla, pc = tcf_hash_check(parm->index, a, bind, &simp_hash_info); if (!pc) { pc = tcf_hash_create(parm->index, est, a, sizeof(*d), bind, - &simp_idx_gen, &simp_hash_info); + &simp_hash_info); if (IS_ERR(pc)) return PTR_ERR(pc); diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c index 0fa1aad6e204..74af46127060 100644 --- a/net/sched/act_skbedit.c +++ b/net/sched/act_skbedit.c @@ -28,7 +28,6 @@ #include #define SKBEDIT_TAB_MASK 15 -static u32 skbedit_idx_gen; static struct tcf_hashinfo skbedit_hash_info; static int tcf_skbedit(struct sk_buff *skb, const struct tc_action *a, @@ -104,7 +103,7 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla, pc = tcf_hash_check(parm->index, a, bind, &skbedit_hash_info); if (!pc) { pc = tcf_hash_create(parm->index, est, a, sizeof(*d), bind, - &skbedit_idx_gen, &skbedit_hash_info); + &skbedit_hash_info); if (IS_ERR(pc)) return PTR_ERR(pc); -- cgit v1.2.3 From 832d1d5bfaefafa5aa40282f6765c6d996fe384e Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Thu, 9 Jan 2014 16:14:01 -0800 Subject: net_sched: add struct net pointer to tcf_proto_ops->dump It will be needed by the next patch. Cc: Jamal Hadi Salim Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/net/sch_generic.h | 2 +- net/sched/cls_api.c | 11 ++++++----- net/sched/cls_basic.c | 2 +- net/sched/cls_bpf.c | 2 +- net/sched/cls_cgroup.c | 2 +- net/sched/cls_flow.c | 2 +- net/sched/cls_fw.c | 2 +- net/sched/cls_route.c | 2 +- net/sched/cls_rsvp.h | 2 +- net/sched/cls_tcindex.c | 2 +- net/sched/cls_u32.c | 2 +- 11 files changed, 16 insertions(+), 15 deletions(-) (limited to 'include/net') diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 013d96dc6918..d062f81c692f 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -204,7 +204,7 @@ struct tcf_proto_ops { void (*walk)(struct tcf_proto*, struct tcf_walker *arg); /* rtnetlink specific */ - int (*dump)(struct tcf_proto*, unsigned long, + int (*dump)(struct net*, struct tcf_proto*, unsigned long, struct sk_buff *skb, struct tcmsg*); struct module *owner; diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index d8c42b1b88e5..29a30a14c315 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -340,7 +340,7 @@ errout: return err; } -static int tcf_fill_node(struct sk_buff *skb, struct tcf_proto *tp, +static int tcf_fill_node(struct net *net, struct sk_buff *skb, struct tcf_proto *tp, unsigned long fh, u32 portid, u32 seq, u16 flags, int event) { struct tcmsg *tcm; @@ -362,7 +362,7 @@ static int tcf_fill_node(struct sk_buff *skb, struct tcf_proto *tp, tcm->tcm_handle = fh; if (RTM_DELTFILTER != event) { tcm->tcm_handle = 0; - if (tp->ops->dump && tp->ops->dump(tp, fh, skb, tcm) < 0) + if (tp->ops->dump && tp->ops->dump(net, tp, fh, skb, tcm) < 0) goto nla_put_failure; } nlh->nlmsg_len = skb_tail_pointer(skb) - b; @@ -385,7 +385,7 @@ static int tfilter_notify(struct net *net, struct sk_buff *oskb, if (!skb) return -ENOBUFS; - if (tcf_fill_node(skb, tp, fh, portid, n->nlmsg_seq, 0, event) <= 0) { + if (tcf_fill_node(net, skb, tp, fh, portid, n->nlmsg_seq, 0, event) <= 0) { kfree_skb(skb); return -EINVAL; } @@ -404,8 +404,9 @@ static int tcf_node_dump(struct tcf_proto *tp, unsigned long n, struct tcf_walker *arg) { struct tcf_dump_args *a = (void *)arg; + struct net *net = sock_net(a->skb->sk); - return tcf_fill_node(a->skb, tp, n, NETLINK_CB(a->cb->skb).portid, + return tcf_fill_node(net, a->skb, tp, n, NETLINK_CB(a->cb->skb).portid, a->cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWTFILTER); } @@ -463,7 +464,7 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) if (t > s_t) memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0])); if (cb->args[1] == 0) { - if (tcf_fill_node(skb, tp, 0, NETLINK_CB(cb->skb).portid, + if (tcf_fill_node(net, skb, tp, 0, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWTFILTER) <= 0) break; diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c index b6552035d1f4..af6bea1a6a54 100644 --- a/net/sched/cls_basic.c +++ b/net/sched/cls_basic.c @@ -241,7 +241,7 @@ skip: } } -static int basic_dump(struct tcf_proto *tp, unsigned long fh, +static int basic_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, struct sk_buff *skb, struct tcmsg *t) { struct basic_filter *f = (struct basic_filter *) fh; diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index 00a5a585e5f1..8e3cf49118e3 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -295,7 +295,7 @@ errout: return ret; } -static int cls_bpf_dump(struct tcf_proto *tp, unsigned long fh, +static int cls_bpf_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, struct sk_buff *skb, struct tcmsg *tm) { struct cls_bpf_prog *prog = (struct cls_bpf_prog *) fh; diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index 8349fcdc50f3..8e2158ab551c 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c @@ -164,7 +164,7 @@ skip: arg->count++; } -static int cls_cgroup_dump(struct tcf_proto *tp, unsigned long fh, +static int cls_cgroup_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, struct sk_buff *skb, struct tcmsg *t) { struct cls_cgroup_head *head = tp->root; diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index dfd18a5c3e81..257029c54332 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -563,7 +563,7 @@ static void flow_put(struct tcf_proto *tp, unsigned long f) { } -static int flow_dump(struct tcf_proto *tp, unsigned long fh, +static int flow_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, struct sk_buff *skb, struct tcmsg *t) { struct flow_filter *f = (struct flow_filter *)fh; diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index 3f9cece13807..d605285af5bf 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -324,7 +324,7 @@ static void fw_walk(struct tcf_proto *tp, struct tcf_walker *arg) } } -static int fw_dump(struct tcf_proto *tp, unsigned long fh, +static int fw_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, struct sk_buff *skb, struct tcmsg *t) { struct fw_head *head = (struct fw_head *)tp->root; diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index 2473953a5948..c9136136727b 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -551,7 +551,7 @@ static void route4_walk(struct tcf_proto *tp, struct tcf_walker *arg) } } -static int route4_dump(struct tcf_proto *tp, unsigned long fh, +static int route4_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, struct sk_buff *skb, struct tcmsg *t) { struct route4_filter *f = (struct route4_filter *)fh; diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h index 4f25c2ac825b..19f8e5dfa8bd 100644 --- a/net/sched/cls_rsvp.h +++ b/net/sched/cls_rsvp.h @@ -594,7 +594,7 @@ static void rsvp_walk(struct tcf_proto *tp, struct tcf_walker *arg) } } -static int rsvp_dump(struct tcf_proto *tp, unsigned long fh, +static int rsvp_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, struct sk_buff *skb, struct tcmsg *t) { struct rsvp_filter *f = (struct rsvp_filter *)fh; diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c index ffad18791c93..f57535354243 100644 --- a/net/sched/cls_tcindex.c +++ b/net/sched/cls_tcindex.c @@ -422,7 +422,7 @@ static void tcindex_destroy(struct tcf_proto *tp) } -static int tcindex_dump(struct tcf_proto *tp, unsigned long fh, +static int tcindex_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, struct sk_buff *skb, struct tcmsg *t) { struct tcindex_data *p = PRIV(tp); diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 20f2fb79c747..e25411a80f9b 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -712,7 +712,7 @@ static void u32_walk(struct tcf_proto *tp, struct tcf_walker *arg) } } -static int u32_dump(struct tcf_proto *tp, unsigned long fh, +static int u32_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, struct sk_buff *skb, struct tcmsg *t) { struct tc_u_knode *n = (struct tc_u_knode *)fh; -- cgit v1.2.3 From 2519a602c273c5254781bc55b6e678a17e469a12 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Thu, 9 Jan 2014 16:14:02 -0800 Subject: net_sched: optimize tcf_match_indev() tcf_match_indev() is called in fast path, it is not wise to search for a netdev by ifindex and then compare by its name, just compare the ifindex. Also, dev->name could be changed by user-space, therefore the match would be always fail, but dev->ifindex could be consistent. BTW, this will also save some bytes from the core struct of u32. Cc: Jamal Hadi Salim Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/net/pkt_cls.h | 30 +++++++++++++++--------------- net/sched/cls_fw.c | 19 ++++++++++++------- net/sched/cls_u32.c | 19 ++++++++++++------- 3 files changed, 39 insertions(+), 29 deletions(-) (limited to 'include/net') diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 50ea07969c09..a2441fb1428f 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -338,27 +338,27 @@ static inline int tcf_valid_offset(const struct sk_buff *skb, #include static inline int -tcf_change_indev(struct tcf_proto *tp, char *indev, struct nlattr *indev_tlv) +tcf_change_indev(struct net *net, struct nlattr *indev_tlv) { + char indev[IFNAMSIZ]; + struct net_device *dev; + if (nla_strlcpy(indev, indev_tlv, IFNAMSIZ) >= IFNAMSIZ) return -EINVAL; - return 0; + dev = __dev_get_by_name(net, indev); + if (!dev) + return -ENODEV; + return dev->ifindex; } -static inline int -tcf_match_indev(struct sk_buff *skb, char *indev) +static inline bool +tcf_match_indev(struct sk_buff *skb, int ifindex) { - struct net_device *dev; - - if (indev[0]) { - if (!skb->skb_iif) - return 0; - dev = __dev_get_by_index(dev_net(skb->dev), skb->skb_iif); - if (!dev || strcmp(indev, dev->name)) - return 0; - } - - return 1; + if (!ifindex) + return true; + if (!skb->skb_iif) + return false; + return ifindex == skb->skb_iif; } #endif /* CONFIG_NET_CLS_IND */ diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index d605285af5bf..ca662aaa5eb4 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -41,7 +41,7 @@ struct fw_filter { u32 id; struct tcf_result res; #ifdef CONFIG_NET_CLS_IND - char indev[IFNAMSIZ]; + int ifindex; #endif /* CONFIG_NET_CLS_IND */ struct tcf_exts exts; }; @@ -86,7 +86,7 @@ static int fw_classify(struct sk_buff *skb, const struct tcf_proto *tp, if (f->id == id) { *res = f->res; #ifdef CONFIG_NET_CLS_IND - if (!tcf_match_indev(skb, f->indev)) + if (!tcf_match_indev(skb, f->ifindex)) continue; #endif /* CONFIG_NET_CLS_IND */ r = tcf_exts_exec(skb, &f->exts, res); @@ -207,9 +207,11 @@ fw_change_attrs(struct net *net, struct tcf_proto *tp, struct fw_filter *f, #ifdef CONFIG_NET_CLS_IND if (tb[TCA_FW_INDEV]) { - err = tcf_change_indev(tp, f->indev, tb[TCA_FW_INDEV]); - if (err < 0) + int ret; + ret = tcf_change_indev(net, tb[TCA_FW_INDEV]); + if (ret < 0) goto errout; + f->ifindex = ret; } #endif /* CONFIG_NET_CLS_IND */ @@ -348,9 +350,12 @@ static int fw_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, nla_put_u32(skb, TCA_FW_CLASSID, f->res.classid)) goto nla_put_failure; #ifdef CONFIG_NET_CLS_IND - if (strlen(f->indev) && - nla_put_string(skb, TCA_FW_INDEV, f->indev)) - goto nla_put_failure; + if (f->ifindex) { + struct net_device *dev; + dev = __dev_get_by_index(net, f->ifindex); + if (dev && nla_put_string(skb, TCA_FW_INDEV, dev->name)) + goto nla_put_failure; + } #endif /* CONFIG_NET_CLS_IND */ if (head->mask != 0xFFFFFFFF && nla_put_u32(skb, TCA_FW_MASK, head->mask)) diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index e25411a80f9b..f509b7907f8d 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -48,7 +48,7 @@ struct tc_u_knode { struct tc_u_hnode *ht_up; struct tcf_exts exts; #ifdef CONFIG_NET_CLS_IND - char indev[IFNAMSIZ]; + int ifindex; #endif u8 fshift; struct tcf_result res; @@ -152,7 +152,7 @@ check_terminal: *res = n->res; #ifdef CONFIG_NET_CLS_IND - if (!tcf_match_indev(skb, n->indev)) { + if (!tcf_match_indev(skb, n->ifindex)) { n = n->next; goto next_knode; } @@ -527,9 +527,11 @@ static int u32_set_parms(struct net *net, struct tcf_proto *tp, #ifdef CONFIG_NET_CLS_IND if (tb[TCA_U32_INDEV]) { - err = tcf_change_indev(tp, n->indev, tb[TCA_U32_INDEV]); - if (err < 0) + int ret; + ret = tcf_change_indev(net, tb[TCA_U32_INDEV]); + if (ret < 0) goto errout; + n->ifindex = ret; } #endif tcf_exts_change(tp, &n->exts, &e); @@ -760,9 +762,12 @@ static int u32_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, goto nla_put_failure; #ifdef CONFIG_NET_CLS_IND - if (strlen(n->indev) && - nla_put_string(skb, TCA_U32_INDEV, n->indev)) - goto nla_put_failure; + if (n->ifindex) { + struct net_device *dev; + dev = __dev_get_by_index(net, n->ifindex); + if (dev && nla_put_string(skb, TCA_U32_INDEV, dev->name)) + goto nla_put_failure; + } #endif #ifdef CONFIG_CLS_U32_PERF if (nla_put(skb, TCA_U32_PCNT, -- cgit v1.2.3 From 7eb8896df03d0f4a8b301cb177d7f31c0d70e112 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Thu, 9 Jan 2014 16:14:05 -0800 Subject: net_sched: act: remove struct tcf_act_hdr It is not necessary at all. Cc: Jamal Hadi Salim Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/net/act_api.h | 4 ---- net/sched/act_api.c | 16 ++++++++-------- 2 files changed, 8 insertions(+), 12 deletions(-) (limited to 'include/net') diff --git a/include/net/act_api.h b/include/net/act_api.h index 268f9e6babd7..e171387512b3 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -73,10 +73,6 @@ static inline void tcf_hashinfo_destroy(struct tcf_hashinfo *hf) #define ACT_P_CREATED 1 #define ACT_P_DELETED 1 -struct tcf_act_hdr { - struct tcf_common common; -}; - struct tc_action { void *priv; const struct tc_action_ops *ops; diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 178bf2eab117..35f89e9ce49c 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -556,9 +556,9 @@ int tcf_action_copy_stats(struct sk_buff *skb, struct tc_action *a, { int err = 0; struct gnet_dump d; - struct tcf_act_hdr *h = a->priv; + struct tcf_common *p = a->priv; - if (h == NULL) + if (p == NULL) goto errout; /* compat_mode being true specifies a call that is supposed @@ -567,20 +567,20 @@ int tcf_action_copy_stats(struct sk_buff *skb, struct tc_action *a, if (compat_mode) { if (a->type == TCA_OLD_COMPAT) err = gnet_stats_start_copy_compat(skb, 0, - TCA_STATS, TCA_XSTATS, &h->tcf_lock, &d); + TCA_STATS, TCA_XSTATS, &p->tcfc_lock, &d); else return 0; } else err = gnet_stats_start_copy(skb, TCA_ACT_STATS, - &h->tcf_lock, &d); + &p->tcfc_lock, &d); if (err < 0) goto errout; - if (gnet_stats_copy_basic(&d, &h->tcf_bstats) < 0 || - gnet_stats_copy_rate_est(&d, &h->tcf_bstats, - &h->tcf_rate_est) < 0 || - gnet_stats_copy_queue(&d, &h->tcf_qstats) < 0) + if (gnet_stats_copy_basic(&d, &p->tcfc_bstats) < 0 || + gnet_stats_copy_rate_est(&d, &p->tcfc_bstats, + &p->tcfc_rate_est) < 0 || + gnet_stats_copy_queue(&d, &p->tcfc_qstats) < 0) goto errout; if (gnet_stats_finish_copy(&d) < 0) -- cgit v1.2.3 From 6daaf0de2f3170206f57e7881adfbd8682cdd7fb Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Thu, 9 Jan 2014 22:31:11 -0800 Subject: sctp: make sctp_addto_chunk_fixed local Signed-off-by: Stephen Hemminger Acked-by: Neil Horman Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 1 - net/sctp/sm_make_chunk.c | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include/net') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index e9f732fda950..d992ca3145fe 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -649,7 +649,6 @@ int sctp_user_addto_chunk(struct sctp_chunk *chunk, int off, int len, struct iovec *data); void sctp_chunk_free(struct sctp_chunk *); void *sctp_addto_chunk(struct sctp_chunk *, int len, const void *data); -void *sctp_addto_chunk_fixed(struct sctp_chunk *, int len, const void *data); struct sctp_chunk *sctp_chunkify(struct sk_buff *, const struct sctp_association *, struct sock *); diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index e5f7cdb42a85..632090b961c3 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -78,6 +78,8 @@ static int sctp_process_param(struct sctp_association *asoc, gfp_t gfp); static void *sctp_addto_param(struct sctp_chunk *chunk, int len, const void *data); +static void *sctp_addto_chunk_fixed(struct sctp_chunk *, int len, + const void *data); /* Control chunk destructor */ static void sctp_control_release_owner(struct sk_buff *skb) @@ -1475,8 +1477,8 @@ void *sctp_addto_chunk(struct sctp_chunk *chunk, int len, const void *data) /* Append bytes to the end of a chunk. Returns NULL if there isn't sufficient * space in the chunk */ -void *sctp_addto_chunk_fixed(struct sctp_chunk *chunk, - int len, const void *data) +static void *sctp_addto_chunk_fixed(struct sctp_chunk *chunk, + int len, const void *data) { if (skb_tailroom(chunk->skb) >= len) return sctp_addto_chunk(chunk, len, data); -- cgit v1.2.3 From 72c1d3bdd5bf10a789608336ba0d61f1e44e4350 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Fri, 10 Jan 2014 16:09:45 -0800 Subject: ipv4: register igmp_notifier even when !CONFIG_PROC_FS We still need this notifier even when we don't config PROC_FS. It should be rare to have a kernel without PROC_FS, so just for completeness. Cc: Stephen Hemminger Cc: David S. Miller Cc: Patrick McHardy Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/net/ip.h | 2 +- net/ipv4/igmp.c | 8 ++++++-- net/ipv4/ip_output.c | 4 ++-- 3 files changed, 9 insertions(+), 5 deletions(-) (limited to 'include/net') diff --git a/include/net/ip.h b/include/net/ip.h index 0dab95c2e4d5..7876e3b96ce3 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -90,7 +90,7 @@ struct packet_type; struct rtable; struct sockaddr; -int igmp_mc_proc_init(void); +int igmp_mc_init(void); /* * Functions provided by ip.c diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 84c4329cbd30..bcbf33e58324 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -2762,6 +2762,7 @@ static struct pernet_operations igmp_net_ops = { .init = igmp_net_init, .exit = igmp_net_exit, }; +#endif static int igmp_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) @@ -2785,8 +2786,9 @@ static struct notifier_block igmp_notifier = { .notifier_call = igmp_netdev_event, }; -int __init igmp_mc_proc_init(void) +int __init igmp_mc_init(void) { +#if defined(CONFIG_PROC_FS) int err; err = register_pernet_subsys(&igmp_net_ops); @@ -2800,5 +2802,7 @@ int __init igmp_mc_proc_init(void) reg_notif_fail: unregister_pernet_subsys(&igmp_net_ops); return err; -} +#else + return register_netdevice_notifier(&igmp_notifier); #endif +} diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 9a78804cfe9c..8971780aec7c 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -1553,7 +1553,7 @@ void __init ip_init(void) ip_rt_init(); inet_initpeers(); -#if defined(CONFIG_IP_MULTICAST) && defined(CONFIG_PROC_FS) - igmp_mc_proc_init(); +#if defined(CONFIG_IP_MULTICAST) + igmp_mc_init(); #endif } -- cgit v1.2.3 From 63862b5bef7349dd1137e4c70702c67d77565785 Mon Sep 17 00:00:00 2001 From: Aruna-Hewapathirane Date: Sat, 11 Jan 2014 07:15:59 -0500 Subject: net: replace macros net_random and net_srandom with direct calls to prandom This patch removes the net_random and net_srandom macros and replaces them with direct calls to the prandom ones. As new commits only seem to use prandom_u32 there is no use to keep them around. This change makes it easier to grep for users of prandom_u32. Signed-off-by: Aruna-Hewapathirane Suggested-by: Hannes Frederic Sowa Acked-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- drivers/infiniband/core/cma.c | 2 +- include/linux/net.h | 3 --- include/net/red.h | 2 +- net/802/garp.c | 2 +- net/802/mrp.c | 2 +- net/core/neighbour.c | 5 +++-- net/core/stream.c | 2 +- net/ipv4/devinet.c | 2 +- net/ipv4/igmp.c | 6 +++--- net/ipv4/inet_connection_sock.c | 2 +- net/ipv4/udp.c | 2 +- net/ipv6/addrconf.c | 4 ++-- net/ipv6/ip6_flowlabel.c | 2 +- net/ipv6/mcast.c | 10 +++++----- net/netfilter/ipvs/ip_vs_conn.c | 2 +- net/netfilter/xt_statistic.c | 2 +- net/openvswitch/actions.c | 2 +- net/rds/bind.c | 2 +- net/sched/act_gact.c | 2 +- net/sched/sch_fq_codel.c | 2 +- net/sched/sch_hhf.c | 2 +- net/sched/sch_netem.c | 19 ++++++++++--------- net/sched/sch_pie.c | 2 +- net/sched/sch_sfb.c | 4 ++-- net/sched/sch_sfq.c | 6 +++--- net/sctp/socket.c | 2 +- net/sunrpc/cache.c | 2 +- net/sunrpc/xprt.c | 2 +- net/sunrpc/xprtsock.c | 2 +- net/xfrm/xfrm_state.c | 2 +- 30 files changed, 50 insertions(+), 51 deletions(-) (limited to 'include/net') diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 8e49db690f33..6c17f2dc0f09 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -2310,7 +2310,7 @@ static int cma_alloc_any_port(struct idr *ps, struct rdma_id_private *id_priv) inet_get_local_port_range(&init_net, &low, &high); remaining = (high - low) + 1; - rover = net_random() % remaining + low; + rover = prandom_u32() % remaining + low; retry: if (last_used_port != rover && !idr_find(ps, (unsigned short) rover)) { diff --git a/include/linux/net.h b/include/linux/net.h index 69be3e6079c8..94734a6259a4 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -245,9 +245,6 @@ do { \ #define net_dbg_ratelimited(fmt, ...) \ net_ratelimited_function(pr_debug, fmt, ##__VA_ARGS__) -#define net_random() prandom_u32() -#define net_srandom(seed) prandom_seed((__force u32)(seed)) - bool __net_get_random_once(void *buf, int nbytes, bool *done, struct static_key *done_key); diff --git a/include/net/red.h b/include/net/red.h index ef46058d35bf..168bb2f495f2 100644 --- a/include/net/red.h +++ b/include/net/red.h @@ -303,7 +303,7 @@ static inline unsigned long red_calc_qavg(const struct red_parms *p, static inline u32 red_random(const struct red_parms *p) { - return reciprocal_divide(net_random(), p->max_P_reciprocal); + return reciprocal_divide(prandom_u32(), p->max_P_reciprocal); } static inline int red_mark_probability(const struct red_parms *p, diff --git a/net/802/garp.c b/net/802/garp.c index 5d9630a0eb93..b38ee6dcba45 100644 --- a/net/802/garp.c +++ b/net/802/garp.c @@ -397,7 +397,7 @@ static void garp_join_timer_arm(struct garp_applicant *app) { unsigned long delay; - delay = (u64)msecs_to_jiffies(garp_join_time) * net_random() >> 32; + delay = (u64)msecs_to_jiffies(garp_join_time) * prandom_u32() >> 32; mod_timer(&app->join_timer, jiffies + delay); } diff --git a/net/802/mrp.c b/net/802/mrp.c index 3ed616215870..72db2785ef2c 100644 --- a/net/802/mrp.c +++ b/net/802/mrp.c @@ -583,7 +583,7 @@ static void mrp_join_timer_arm(struct mrp_applicant *app) { unsigned long delay; - delay = (u64)msecs_to_jiffies(mrp_join_time) * net_random() >> 32; + delay = (u64)msecs_to_jiffies(mrp_join_time) * prandom_u32() >> 32; mod_timer(&app->join_timer, jiffies + delay); } diff --git a/net/core/neighbour.c b/net/core/neighbour.c index ea97361f0e9b..e360d9e65c04 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -117,7 +117,7 @@ static void neigh_cleanup_and_release(struct neighbour *neigh) unsigned long neigh_rand_reach_time(unsigned long base) { - return base ? (net_random() % base) + (base >> 1) : 0; + return base ? (prandom_u32() % base) + (base >> 1) : 0; } EXPORT_SYMBOL(neigh_rand_reach_time); @@ -1415,7 +1415,8 @@ void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p, struct sk_buff *skb) { unsigned long now = jiffies; - unsigned long sched_next = now + (net_random() % + + unsigned long sched_next = now + (prandom_u32() % NEIGH_VAR(p, PROXY_DELAY)); if (tbl->proxy_queue.qlen > NEIGH_VAR(p, PROXY_QLEN)) { diff --git a/net/core/stream.c b/net/core/stream.c index 512f0a24269b..301c05f26060 100644 --- a/net/core/stream.c +++ b/net/core/stream.c @@ -122,7 +122,7 @@ int sk_stream_wait_memory(struct sock *sk, long *timeo_p) DEFINE_WAIT(wait); if (sk_stream_memory_free(sk)) - current_timeo = vm_wait = (net_random() % (HZ / 5)) + 2; + current_timeo = vm_wait = (prandom_u32() % (HZ / 5)) + 2; while (1) { set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags); diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 9809f7b69728..646023bd5449 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -464,7 +464,7 @@ static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, } if (!(ifa->ifa_flags & IFA_F_SECONDARY)) { - net_srandom(ifa->ifa_local); + prandom_seed((__force u32) ifa->ifa_local); ifap = last_primary; } diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index bcbf33e58324..97e4d1655d26 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -211,7 +211,7 @@ static void igmp_stop_timer(struct ip_mc_list *im) /* It must be called with locked im->lock */ static void igmp_start_timer(struct ip_mc_list *im, int max_delay) { - int tv = net_random() % max_delay; + int tv = prandom_u32() % max_delay; im->tm_running = 1; if (!mod_timer(&im->timer, jiffies+tv+2)) @@ -220,7 +220,7 @@ static void igmp_start_timer(struct ip_mc_list *im, int max_delay) static void igmp_gq_start_timer(struct in_device *in_dev) { - int tv = net_random() % in_dev->mr_maxdelay; + int tv = prandom_u32() % in_dev->mr_maxdelay; in_dev->mr_gq_running = 1; if (!mod_timer(&in_dev->mr_gq_timer, jiffies+tv+2)) @@ -229,7 +229,7 @@ static void igmp_gq_start_timer(struct in_device *in_dev) static void igmp_ifc_start_timer(struct in_device *in_dev, int delay) { - int tv = net_random() % delay; + int tv = prandom_u32() % delay; if (!mod_timer(&in_dev->mr_ifc_timer, jiffies+tv+2)) in_dev_hold(in_dev); diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index fc0e649cc002..0d1e2cb877ec 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -109,7 +109,7 @@ int inet_csk_get_port(struct sock *sk, unsigned short snum) again: inet_get_local_port_range(net, &low, &high); remaining = (high - low) + 1; - smallest_rover = rover = net_random() % remaining + low; + smallest_rover = rover = prandom_u32() % remaining + low; smallest_size = -1; do { diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 80f649fbee63..3d3141fd0580 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -223,7 +223,7 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum, inet_get_local_port_range(net, &low, &high); remaining = (high - low) + 1; - rand = net_random(); + rand = prandom_u32(); first = (((u64)rand * remaining) >> 32) + low; /* * force rand to be an odd multiple of UDP_HTABLE_SIZE diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index b0cd122e5fb4..5329cccf55dc 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -3104,7 +3104,7 @@ static void addrconf_dad_kick(struct inet6_ifaddr *ifp) if (ifp->flags & IFA_F_OPTIMISTIC) rand_num = 0; else - rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1); + rand_num = prandom_u32() % (idev->cnf.rtr_solicit_delay ? : 1); ifp->dad_probes = idev->cnf.dad_transmits; addrconf_mod_dad_timer(ifp, rand_num); @@ -3117,7 +3117,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp) addrconf_join_solict(dev, &ifp->addr); - net_srandom(ifp->addr.s6_addr32[3]); + prandom_seed((__force u32) ifp->addr.s6_addr32[3]); read_lock_bh(&idev->lock); spin_lock(&ifp->lock); diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index e7fb7106550f..cbc93517b455 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -210,7 +210,7 @@ static struct ip6_flowlabel *fl_intern(struct net *net, spin_lock_bh(&ip6_fl_lock); if (label == 0) { for (;;) { - fl->label = htonl(net_random())&IPV6_FLOWLABEL_MASK; + fl->label = htonl(prandom_u32())&IPV6_FLOWLABEL_MASK; if (fl->label) { lfl = __fl_lookup(net, fl->label); if (lfl == NULL) diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index d18f9f903db6..7ff82b3e54b6 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -999,7 +999,7 @@ bool ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group, static void mld_gq_start_timer(struct inet6_dev *idev) { - unsigned long tv = net_random() % idev->mc_maxdelay; + unsigned long tv = prandom_u32() % idev->mc_maxdelay; idev->mc_gq_running = 1; if (!mod_timer(&idev->mc_gq_timer, jiffies+tv+2)) @@ -1015,7 +1015,7 @@ static void mld_gq_stop_timer(struct inet6_dev *idev) static void mld_ifc_start_timer(struct inet6_dev *idev, unsigned long delay) { - unsigned long tv = net_random() % delay; + unsigned long tv = prandom_u32() % delay; if (!mod_timer(&idev->mc_ifc_timer, jiffies+tv+2)) in6_dev_hold(idev); @@ -1030,7 +1030,7 @@ static void mld_ifc_stop_timer(struct inet6_dev *idev) static void mld_dad_start_timer(struct inet6_dev *idev, unsigned long delay) { - unsigned long tv = net_random() % delay; + unsigned long tv = prandom_u32() % delay; if (!mod_timer(&idev->mc_dad_timer, jiffies+tv+2)) in6_dev_hold(idev); @@ -1061,7 +1061,7 @@ static void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime) } if (delay >= resptime) - delay = net_random() % resptime; + delay = prandom_u32() % resptime; ma->mca_timer.expires = jiffies + delay; if (!mod_timer(&ma->mca_timer, jiffies + delay)) @@ -2328,7 +2328,7 @@ static void igmp6_join_group(struct ifmcaddr6 *ma) igmp6_send(&ma->mca_addr, ma->idev->dev, ICMPV6_MGM_REPORT); - delay = net_random() % unsolicited_report_interval(ma->idev); + delay = prandom_u32() % unsolicited_report_interval(ma->idev); spin_lock_bh(&ma->mca_lock); if (del_timer(&ma->mca_timer)) { diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 4c8e5c0aa1ab..59a1a85bcb3e 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -1209,7 +1209,7 @@ void ip_vs_random_dropentry(struct net *net) * Randomly scan 1/32 of the whole table every second */ for (idx = 0; idx < (ip_vs_conn_tab_size>>5); idx++) { - unsigned int hash = net_random() & ip_vs_conn_tab_mask; + unsigned int hash = prandom_u32() & ip_vs_conn_tab_mask; hlist_for_each_entry_rcu(cp, &ip_vs_conn_tab[hash], c_list) { if (cp->flags & IP_VS_CONN_F_TEMPLATE) diff --git a/net/netfilter/xt_statistic.c b/net/netfilter/xt_statistic.c index 4fe4fb4276d0..11de55e7a868 100644 --- a/net/netfilter/xt_statistic.c +++ b/net/netfilter/xt_statistic.c @@ -37,7 +37,7 @@ statistic_mt(const struct sk_buff *skb, struct xt_action_param *par) switch (info->mode) { case XT_STATISTIC_MODE_RANDOM: - if ((net_random() & 0x7FFFFFFF) < info->u.random.probability) + if ((prandom_u32() & 0x7FFFFFFF) < info->u.random.probability) ret = !ret; break; case XT_STATISTIC_MODE_NTH: diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 716b7eebfe70..2c77e7b1a913 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -445,7 +445,7 @@ static int sample(struct datapath *dp, struct sk_buff *skb, a = nla_next(a, &rem)) { switch (nla_type(a)) { case OVS_SAMPLE_ATTR_PROBABILITY: - if (net_random() >= nla_get_u32(a)) + if (prandom_u32() >= nla_get_u32(a)) return 0; break; diff --git a/net/rds/bind.c b/net/rds/bind.c index b5ad65a0067e..a2e6562da751 100644 --- a/net/rds/bind.c +++ b/net/rds/bind.c @@ -117,7 +117,7 @@ static int rds_add_bound(struct rds_sock *rs, __be32 addr, __be16 *port) rover = be16_to_cpu(*port); last = rover; } else { - rover = max_t(u16, net_random(), 2); + rover = max_t(u16, prandom_u32(), 2); last = rover - 1; } diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c index f26e6b890cc7..31333073cd80 100644 --- a/net/sched/act_gact.c +++ b/net/sched/act_gact.c @@ -29,7 +29,7 @@ static struct tcf_hashinfo gact_hash_info; #ifdef CONFIG_GACT_PROB static int gact_net_rand(struct tcf_gact *gact) { - if (!gact->tcfg_pval || net_random() % gact->tcfg_pval) + if (!gact->tcfg_pval || prandom_u32() % gact->tcfg_pval) return gact->tcf_action; return gact->tcfg_paction; } diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c index 55786283a3df..ba5bc929eac7 100644 --- a/net/sched/sch_fq_codel.c +++ b/net/sched/sch_fq_codel.c @@ -390,7 +390,7 @@ static int fq_codel_init(struct Qdisc *sch, struct nlattr *opt) sch->limit = 10*1024; q->flows_cnt = 1024; q->quantum = psched_mtu(qdisc_dev(sch)); - q->perturbation = net_random(); + q->perturbation = prandom_u32(); INIT_LIST_HEAD(&q->new_flows); INIT_LIST_HEAD(&q->old_flows); codel_params_init(&q->cparams); diff --git a/net/sched/sch_hhf.c b/net/sched/sch_hhf.c index 1cf84a9e13aa..647680b1c625 100644 --- a/net/sched/sch_hhf.c +++ b/net/sched/sch_hhf.c @@ -607,7 +607,7 @@ static int hhf_init(struct Qdisc *sch, struct nlattr *opt) sch->limit = 1000; q->quantum = psched_mtu(qdisc_dev(sch)); - q->perturbation = net_random(); + q->perturbation = prandom_u32(); INIT_LIST_HEAD(&q->new_buckets); INIT_LIST_HEAD(&q->old_buckets); diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 090a4e3ecd0d..3019c10d6c56 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -169,7 +169,7 @@ static inline struct netem_skb_cb *netem_skb_cb(struct sk_buff *skb) static void init_crandom(struct crndstate *state, unsigned long rho) { state->rho = rho; - state->last = net_random(); + state->last = prandom_u32(); } /* get_crandom - correlated random number generator @@ -182,9 +182,9 @@ static u32 get_crandom(struct crndstate *state) unsigned long answer; if (state->rho == 0) /* no correlation */ - return net_random(); + return prandom_u32(); - value = net_random(); + value = prandom_u32(); rho = (u64)state->rho + 1; answer = (value * ((1ull<<32) - rho) + state->last * rho) >> 32; state->last = answer; @@ -198,7 +198,7 @@ static u32 get_crandom(struct crndstate *state) static bool loss_4state(struct netem_sched_data *q) { struct clgstate *clg = &q->clg; - u32 rnd = net_random(); + u32 rnd = prandom_u32(); /* * Makes a comparison between rnd and the transition @@ -264,15 +264,15 @@ static bool loss_gilb_ell(struct netem_sched_data *q) switch (clg->state) { case 1: - if (net_random() < clg->a1) + if (prandom_u32() < clg->a1) clg->state = 2; - if (net_random() < clg->a4) + if (prandom_u32() < clg->a4) return true; break; case 2: - if (net_random() < clg->a2) + if (prandom_u32() < clg->a2) clg->state = 1; - if (net_random() > clg->a3) + if (prandom_u32() > clg->a3) return true; } @@ -457,7 +457,8 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch) skb_checksum_help(skb))) return qdisc_drop(skb, sch); - skb->data[net_random() % skb_headlen(skb)] ^= 1<<(net_random() % 8); + skb->data[prandom_u32() % skb_headlen(skb)] ^= + 1<<(prandom_u32() % 8); } if (unlikely(skb_queue_len(&sch->q) >= sch->limit)) diff --git a/net/sched/sch_pie.c b/net/sched/sch_pie.c index fe65340c8eb4..a255d0200a59 100644 --- a/net/sched/sch_pie.c +++ b/net/sched/sch_pie.c @@ -122,7 +122,7 @@ static bool drop_early(struct Qdisc *sch, u32 packet_size) else local_prob = q->vars.prob; - rnd = net_random(); + rnd = prandom_u32(); if (rnd < local_prob) return true; diff --git a/net/sched/sch_sfb.c b/net/sched/sch_sfb.c index 30ea4674cabd..9b0f7093d970 100644 --- a/net/sched/sch_sfb.c +++ b/net/sched/sch_sfb.c @@ -220,7 +220,7 @@ static u32 sfb_compute_qlen(u32 *prob_r, u32 *avgpm_r, const struct sfb_sched_da static void sfb_init_perturbation(u32 slot, struct sfb_sched_data *q) { - q->bins[slot].perturbation = net_random(); + q->bins[slot].perturbation = prandom_u32(); } static void sfb_swap_slot(struct sfb_sched_data *q) @@ -381,7 +381,7 @@ static int sfb_enqueue(struct sk_buff *skb, struct Qdisc *sch) goto enqueue; } - r = net_random() & SFB_MAX_PROB; + r = prandom_u32() & SFB_MAX_PROB; if (unlikely(r < p_min)) { if (unlikely(p_min > SFB_MAX_PROB / 2)) { diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index 76f01e0258df..87317ff0b4ec 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -629,7 +629,7 @@ static void sfq_perturbation(unsigned long arg) spinlock_t *root_lock = qdisc_lock(qdisc_root_sleeping(sch)); spin_lock(root_lock); - q->perturbation = net_random(); + q->perturbation = prandom_u32(); if (!q->filter_list && q->tail) sfq_rehash(sch); spin_unlock(root_lock); @@ -698,7 +698,7 @@ static int sfq_change(struct Qdisc *sch, struct nlattr *opt) del_timer(&q->perturb_timer); if (q->perturb_period) { mod_timer(&q->perturb_timer, jiffies + q->perturb_period); - q->perturbation = net_random(); + q->perturbation = prandom_u32(); } sch_tree_unlock(sch); kfree(p); @@ -759,7 +759,7 @@ static int sfq_init(struct Qdisc *sch, struct nlattr *opt) q->quantum = psched_mtu(qdisc_dev(sch)); q->scaled_quantum = SFQ_ALLOT_SIZE(q->quantum); q->perturb_period = 0; - q->perturbation = net_random(); + q->perturbation = prandom_u32(); if (opt) { int err = sfq_change(sch, opt); diff --git a/net/sctp/socket.c b/net/sctp/socket.c index d32dae78a486..7cc62b922f12 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -5926,7 +5926,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) inet_get_local_port_range(sock_net(sk), &low, &high); remaining = (high - low) + 1; - rover = net_random() % remaining + low; + rover = prandom_u32() % remaining + low; do { rover++; diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index a72de074172d..e521d20e1970 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -619,7 +619,7 @@ static void cache_limit_defers(void) /* Consider removing either the first or the last */ if (cache_defer_cnt > DFR_MAX) { - if (net_random() & 1) + if (prandom_u32() & 1) discard = list_entry(cache_defer_list.next, struct cache_deferred_req, recent); else diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 04199bc8416f..1750048130a7 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -1188,7 +1188,7 @@ static inline __be32 xprt_alloc_xid(struct rpc_xprt *xprt) static inline void xprt_init_xid(struct rpc_xprt *xprt) { - xprt->xid = net_random(); + xprt->xid = prandom_u32(); } static void xprt_request_init(struct rpc_task *task, struct rpc_xprt *xprt) diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index dd9d295813cf..75b045e1cd50 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -1674,7 +1674,7 @@ static void xs_udp_timer(struct rpc_xprt *xprt, struct rpc_task *task) static unsigned short xs_get_random_port(void) { unsigned short range = xprt_max_resvport - xprt_min_resvport; - unsigned short rand = (unsigned short) net_random() % range; + unsigned short rand = (unsigned short) prandom_u32() % range; return rand + xprt_min_resvport; } diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 62181486ead8..8d11d28991a4 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -1565,7 +1565,7 @@ int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high) } else { u32 spi = 0; for (h = 0; h < high-low+1; h++) { - spi = low + net_random()%(high-low+1); + spi = low + prandom_u32()%(high-low+1); x0 = xfrm_state_lookup(net, mark, &x->id.daddr, htonl(spi), x->id.proto, x->props.family); if (x0 == NULL) { x->id.spi = htonl(spi); -- cgit v1.2.3 From ec35b61ea59aa6b1ecc8d8452b225e4677e32627 Mon Sep 17 00:00:00 2001 From: FX Le Bail Date: Mon, 13 Jan 2014 15:59:01 +0100 Subject: IPv6: move the anycast_src_echo_reply sysctl to netns_sysctl_ipv6 This change move anycast_src_echo_reply sysctl with other ipv6 sysctls. Suggested-by: Hannes Frederic Sowa Signed-off-by: Francois-Xavier Le Bail Acked-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/net/netns/ipv6.h | 2 +- net/ipv6/icmp.c | 2 +- net/ipv6/sysctl_net_ipv6.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include/net') diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index 76fc7d1dbfd3..592fecd2e59e 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -28,6 +28,7 @@ struct netns_sysctl_ipv6 { int ip6_rt_mtu_expires; int ip6_rt_min_advmss; int icmpv6_time; + int anycast_src_echo_reply; }; struct netns_ipv6 { @@ -73,7 +74,6 @@ struct netns_ipv6 { #endif atomic_t dev_addr_genid; atomic_t rt_genid; - int anycast_src_echo_reply; }; #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6) diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 8003b4930267..902405dc258c 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -559,7 +559,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) saddr = &ipv6_hdr(skb)->daddr; if (!ipv6_unicast_destination(skb) && - !(net->ipv6.anycast_src_echo_reply && + !(net->ipv6.sysctl.anycast_src_echo_reply && ipv6_anycast_destination(skb))) saddr = NULL; diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c index 6b6a2c83027e..b51b2688102c 100644 --- a/net/ipv6/sysctl_net_ipv6.c +++ b/net/ipv6/sysctl_net_ipv6.c @@ -26,7 +26,7 @@ static struct ctl_table ipv6_table_template[] = { }, { .procname = "anycast_src_echo_reply", - .data = &init_net.ipv6.anycast_src_echo_reply, + .data = &init_net.ipv6.sysctl.anycast_src_echo_reply, .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec @@ -58,7 +58,7 @@ static int __net_init ipv6_sysctl_net_init(struct net *net) if (!ipv6_table) goto out; ipv6_table[0].data = &net->ipv6.sysctl.bindv6only; - ipv6_table[1].data = &net->ipv6.anycast_src_echo_reply; + ipv6_table[1].data = &net->ipv6.sysctl.anycast_src_echo_reply; ipv6_route_table = ipv6_route_sysctl_init(net); if (!ipv6_route_table) -- cgit v1.2.3 From d76ed22b225c02228c643ae336f76e086fdc32f0 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Wed, 15 Jan 2014 17:03:30 +0800 Subject: ipv6: move IPV6_TCLASS_SHIFT into ipv6.h and define a helper Two places defined IPV6_TCLASS_SHIFT, so we should move it into ipv6.h, and use this macro as possible. And define ip6_tclass helper to return tclass Signed-off-by: Li RongQing Signed-off-by: David S. Miller --- include/net/ipv6.h | 5 +++++ net/ipv6/fib6_rules.c | 2 +- net/ipv6/ip6_gre.c | 2 -- net/ipv6/ip6_tunnel.c | 2 -- net/ipv6/ipv6_sockglue.c | 3 ++- 5 files changed, 8 insertions(+), 6 deletions(-) (limited to 'include/net') diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 12079c65ea3e..6d80f51897a5 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -239,6 +239,7 @@ struct ip6_flowlabel { #define IPV6_FLOWINFO_MASK cpu_to_be32(0x0FFFFFFF) #define IPV6_FLOWLABEL_MASK cpu_to_be32(0x000FFFFF) #define IPV6_TCLASS_MASK (IPV6_FLOWINFO_MASK & ~IPV6_FLOWLABEL_MASK) +#define IPV6_TCLASS_SHIFT 20 struct ipv6_fl_socklist { struct ipv6_fl_socklist __rcu *next; @@ -681,6 +682,10 @@ static inline __be32 ip6_flowlabel(const struct ipv6hdr *hdr) return *(__be32 *)hdr & IPV6_FLOWLABEL_MASK; } +static inline u8 ip6_tclass(__be32 flowinfo) +{ + return ntohl(flowinfo & IPV6_TCLASS_MASK) >> IPV6_TCLASS_SHIFT; +} /* * Prototypes exported by ipv6 */ diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c index 3fd0a578329e..b4d5e1d97c1b 100644 --- a/net/ipv6/fib6_rules.c +++ b/net/ipv6/fib6_rules.c @@ -169,7 +169,7 @@ static int fib6_rule_match(struct fib_rule *rule, struct flowi *fl, int flags) return 0; } - if (r->tclass && r->tclass != ((ntohl(fl6->flowlabel) >> 20) & 0xff)) + if (r->tclass && r->tclass != ip6_tclass(fl6->flowlabel)) return 0; return 1; diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index e7a440dd5c0d..f3ffb43f59c0 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -61,8 +61,6 @@ static bool log_ecn_error = true; module_param(log_ecn_error, bool, 0644); MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); -#define IPV6_TCLASS_SHIFT 20 - #define HASH_SIZE_SHIFT 5 #define HASH_SIZE (1 << HASH_SIZE_SHIFT) diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 1e5e2404f1af..5db8d310f9c0 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -69,8 +69,6 @@ MODULE_ALIAS_NETDEV("ip6tnl0"); #define IP6_TNL_TRACE(x...) do {;} while(0) #endif -#define IPV6_TCLASS_SHIFT 20 - #define HASH_SIZE_SHIFT 5 #define HASH_SIZE (1 << HASH_SIZE_SHIFT) diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index af0ecb94b3b4..2855b00ed49d 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -1019,7 +1019,8 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim); } if (np->rxopt.bits.rxtclass) { - int tclass = ntohl(np->rcv_flowinfo & IPV6_TCLASS_MASK) >> 20; + int tclass = (int)ip6_tclass(np->rcv_flowinfo); + put_cmsg(&msg, SOL_IPV6, IPV6_TCLASS, sizeof(tclass), &tclass); } if (np->rxopt.bits.rxoinfo) { -- cgit v1.2.3 From 89740ca74f5bb45d3e0dcb4aa9eab0ba9864bff5 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 9 Jan 2014 14:13:47 +0100 Subject: neigh: use NEIGH_VAR_INIT in ndo_neigh_setup functions. When ndo_neigh_setup is called, the bitfield used by NEIGH_VAR_SET is not initialized yet. This might cause confusion for the people who use NEIGH_VAR_SET in ndo_neigh_setup. So rather introduce NEIGH_VAR_INIT for usage in ndo_neigh_setup. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/neighbour.h | 5 +++++ net/802/hippi.c | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 4c09bd23b832..7277caf3743d 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -92,6 +92,11 @@ static inline void neigh_var_set(struct neigh_parms *p, int index, int val) } #define NEIGH_VAR(p, attr) ((p)->data[NEIGH_VAR_ ## attr]) + +/* In ndo_neigh_setup, NEIGH_VAR_INIT should be used. + * In other cases, NEIGH_VAR_SET should be used. + */ +#define NEIGH_VAR_INIT(p, attr, val) (NEIGH_VAR(p, attr) = val) #define NEIGH_VAR_SET(p, attr, val) neigh_var_set(p, NEIGH_VAR_ ## attr, val) static inline void neigh_parms_data_state_setall(struct neigh_parms *p) diff --git a/net/802/hippi.c b/net/802/hippi.c index a97a3bde77bb..5ff2a718ddca 100644 --- a/net/802/hippi.c +++ b/net/802/hippi.c @@ -172,14 +172,14 @@ EXPORT_SYMBOL(hippi_mac_addr); int hippi_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p) { /* Never send broadcast/multicast ARP messages */ - NEIGH_VAR_SET(p, MCAST_PROBES, 0); + NEIGH_VAR_INIT(p, MCAST_PROBES, 0); /* In IPv6 unicast probes are valid even on NBMA, * because they are encapsulated in normal IPv6 protocol. * Should be a generic flag. */ if (p->tbl->family != AF_INET6) - NEIGH_VAR_SET(p, UCAST_PROBES, 0); + NEIGH_VAR_INIT(p, UCAST_PROBES, 0); return 0; } EXPORT_SYMBOL(hippi_neigh_setup_dev); -- cgit v1.2.3 From 1d13a96c74fc4802a775189ddb58bc6469ffdaa3 Mon Sep 17 00:00:00 2001 From: Florent Fourcot Date: Thu, 16 Jan 2014 17:21:22 +0100 Subject: ipv6: tcp: fix flowlabel value in ACK messages send from TIME_WAIT This patch is following the commit b903d324bee262 (ipv6: tcp: fix TCLASS value in ACK messages sent from TIME_WAIT). For the same reason than tclass, we have to store the flow label in the inet_timewait_sock to provide consistency of flow label on the last ACK. Signed-off-by: Florent Fourcot Signed-off-by: David S. Miller --- include/net/inet_timewait_sock.h | 6 +++--- net/ipv4/tcp_minisocks.c | 1 + net/ipv6/tcp_ipv6.c | 17 +++++++++++------ 3 files changed, 15 insertions(+), 9 deletions(-) (limited to 'include/net') diff --git a/include/net/inet_timewait_sock.h b/include/net/inet_timewait_sock.h index 71c6e264e5b5..61474ea02152 100644 --- a/include/net/inet_timewait_sock.h +++ b/include/net/inet_timewait_sock.h @@ -133,9 +133,9 @@ struct inet_timewait_sock { /* And these are ours. */ unsigned int tw_ipv6only : 1, tw_transparent : 1, - tw_pad : 6, /* 6 bits hole */ - tw_tos : 8, - tw_pad2 : 16; /* 16 bits hole */ + tw_flowlabel : 20, + tw_pad : 2, /* 2 bits hole */ + tw_tos : 8; kmemcheck_bitfield_end(flags); u32 tw_ttd; struct inet_bind_bucket *tw_tb; diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 3aa9e3247991..7a436c517e44 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -297,6 +297,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) tw->tw_v6_daddr = sk->sk_v6_daddr; tw->tw_v6_rcv_saddr = sk->sk_v6_rcv_saddr; tw->tw_tclass = np->tclass; + tw->tw_flowlabel = np->flow_label >> 12; tw->tw_ipv6only = np->ipv6only; } #endif diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index ffd5fa8bdb15..b5512696e9ed 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -724,7 +724,8 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = { static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 tsval, u32 tsecr, - struct tcp_md5sig_key *key, int rst, u8 tclass) + struct tcp_md5sig_key *key, int rst, u8 tclass, + u32 label) { const struct tcphdr *th = tcp_hdr(skb); struct tcphdr *t1; @@ -786,6 +787,7 @@ static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, memset(&fl6, 0, sizeof(fl6)); fl6.daddr = ipv6_hdr(skb)->saddr; fl6.saddr = ipv6_hdr(skb)->daddr; + fl6.flowlabel = label; buff->ip_summed = CHECKSUM_PARTIAL; buff->csum = 0; @@ -871,7 +873,7 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) ack_seq = ntohl(th->seq) + th->syn + th->fin + skb->len - (th->doff << 2); - tcp_v6_send_response(skb, seq, ack_seq, 0, 0, 0, key, 1, 0); + tcp_v6_send_response(skb, seq, ack_seq, 0, 0, 0, key, 1, 0, 0); #ifdef CONFIG_TCP_MD5SIG release_sk1: @@ -884,9 +886,11 @@ release_sk1: static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 tsval, u32 tsecr, - struct tcp_md5sig_key *key, u8 tclass) + struct tcp_md5sig_key *key, u8 tclass, + u32 label) { - tcp_v6_send_response(skb, seq, ack, win, tsval, tsecr, key, 0, tclass); + tcp_v6_send_response(skb, seq, ack, win, tsval, tsecr, key, 0, tclass, + label); } static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb) @@ -898,7 +902,7 @@ static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb) tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale, tcp_time_stamp + tcptw->tw_ts_offset, tcptw->tw_ts_recent, tcp_twsk_md5_key(tcptw), - tw->tw_tclass); + tw->tw_tclass, (tw->tw_flowlabel << 12)); inet_twsk_put(tw); } @@ -908,7 +912,8 @@ static void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, { tcp_v6_send_ack(skb, tcp_rsk(req)->snt_isn + 1, tcp_rsk(req)->rcv_isn + 1, req->rcv_wnd, tcp_time_stamp, req->ts_recent, - tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr), 0); + tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr), + 0, 0); } -- cgit v1.2.3 From 6c7e7610ff6888ea15a901fbcb30c5d461816b34 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 16 Jan 2014 16:41:19 -0800 Subject: ipv4: fix a dst leak in tunnels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch : 1) Remove a dst leak if DST_NOCACHE was set on dst Fix this by holding a reference only if dst really cached. 2) Remove a lockdep warning in __tunnel_dst_set() This was reported by Cong Wang. 3) Remove usage of a spinlock where xchg() is enough 4) Remove some spurious inline keywords. Let compiler decide for us. Fixes: 7d442fab0a67 ("ipv4: Cache dst in tunnels") Signed-off-by: Eric Dumazet Cc: Cong Wang Cc: Tom Herbert Cc: Maciej Żenczykowski Signed-off-by: David S. Miller --- include/net/ip_tunnels.h | 1 - net/ipv4/ip_tunnel.c | 34 ++++++++++++++-------------------- 2 files changed, 14 insertions(+), 21 deletions(-) (limited to 'include/net') diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index cd729becbb07..48ed75c21260 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -40,7 +40,6 @@ struct ip_tunnel_prl_entry { struct ip_tunnel_dst { struct dst_entry __rcu *dst; - spinlock_t lock; }; struct ip_tunnel { diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index d3929a69f008..432c28ab3197 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -68,27 +68,27 @@ static unsigned int ip_tunnel_hash(struct ip_tunnel_net *itn, IP_TNL_HASH_BITS); } -static inline void __tunnel_dst_set(struct ip_tunnel_dst *idst, - struct dst_entry *dst) +static void __tunnel_dst_set(struct ip_tunnel_dst *idst, + struct dst_entry *dst) { struct dst_entry *old_dst; - if (dst && (dst->flags & DST_NOCACHE)) - dst = NULL; - - spin_lock_bh(&idst->lock); - old_dst = rcu_dereference(idst->dst); - rcu_assign_pointer(idst->dst, dst); + if (dst) { + if (dst->flags & DST_NOCACHE) + dst = NULL; + else + dst_clone(dst); + } + old_dst = xchg((__force struct dst_entry **)&idst->dst, dst); dst_release(old_dst); - spin_unlock_bh(&idst->lock); } -static inline void tunnel_dst_set(struct ip_tunnel *t, struct dst_entry *dst) +static void tunnel_dst_set(struct ip_tunnel *t, struct dst_entry *dst) { __tunnel_dst_set(this_cpu_ptr(t->dst_cache), dst); } -static inline void tunnel_dst_reset(struct ip_tunnel *t) +static void tunnel_dst_reset(struct ip_tunnel *t) { tunnel_dst_set(t, NULL); } @@ -101,7 +101,7 @@ static void tunnel_dst_reset_all(struct ip_tunnel *t) __tunnel_dst_set(per_cpu_ptr(t->dst_cache, i), NULL); } -static inline struct dst_entry *tunnel_dst_get(struct ip_tunnel *t) +static struct dst_entry *tunnel_dst_get(struct ip_tunnel *t) { struct dst_entry *dst; @@ -413,7 +413,7 @@ static int ip_tunnel_bind_dev(struct net_device *dev) if (!IS_ERR(rt)) { tdev = rt->dst.dev; - tunnel_dst_set(tunnel, dst_clone(&rt->dst)); + tunnel_dst_set(tunnel, &rt->dst); ip_rt_put(rt); } if (dev->type != ARPHRD_ETHER) @@ -668,7 +668,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, goto tx_error; } if (connected) - tunnel_dst_set(tunnel, dst_clone(&rt->dst)); + tunnel_dst_set(tunnel, &rt->dst); } if (rt->dst.dev == dev) { @@ -1066,12 +1066,6 @@ int ip_tunnel_init(struct net_device *dev) return -ENOMEM; } - for_each_possible_cpu(i) { - struct ip_tunnel_dst *idst = per_cpu_ptr(tunnel->dst_cache, i); - idst-> dst = NULL; - spin_lock_init(&idst->lock); - } - err = gro_cells_init(&tunnel->gro_cells, dev); if (err) { free_percpu(tunnel->dst_cache); -- cgit v1.2.3 From 46e5f401762c639e38eea350d335c0f54ec2442f Mon Sep 17 00:00:00 2001 From: Florent Fourcot Date: Fri, 17 Jan 2014 17:15:04 +0100 Subject: ipv6: add a flag to get the flow label used remotly This information is already available via IPV6_FLOWINFO of IPV6_2292PKTOPTIONS, and them a filtering to get the flow label information. But it is probably logical and easier for users to add this here, and to control both sent/received flow label values with the IPV6_FLOWLABEL_MGR option. Signed-off-by: Florent Fourcot Acked-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/net/ipv6.h | 3 ++- include/uapi/linux/in6.h | 1 + net/ipv6/ip6_flowlabel.c | 8 +++++++- net/ipv6/ipv6_sockglue.c | 5 ++++- 4 files changed, 14 insertions(+), 3 deletions(-) (limited to 'include/net') diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 6d80f51897a5..78d3d5124918 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -253,7 +253,8 @@ struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions *opt_space, struct ipv6_txoptions *fopt); void fl6_free_socklist(struct sock *sk); int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen); -int ipv6_flowlabel_opt_get(struct sock *sk, struct in6_flowlabel_req *freq); +int ipv6_flowlabel_opt_get(struct sock *sk, struct in6_flowlabel_req *freq, + int flags); int ip6_flowlabel_init(void); void ip6_flowlabel_cleanup(void); diff --git a/include/uapi/linux/in6.h b/include/uapi/linux/in6.h index 02c0cd685a27..633b93cac1ed 100644 --- a/include/uapi/linux/in6.h +++ b/include/uapi/linux/in6.h @@ -86,6 +86,7 @@ struct in6_flowlabel_req { #define IPV6_FL_F_CREATE 1 #define IPV6_FL_F_EXCL 2 #define IPV6_FL_F_REFLECT 4 +#define IPV6_FL_F_REMOTE 8 #define IPV6_FL_S_NONE 0 #define IPV6_FL_S_EXCL 1 diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index 55823f187446..01bf2524c72a 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -481,11 +481,17 @@ static inline void fl_link(struct ipv6_pinfo *np, struct ipv6_fl_socklist *sfl, spin_unlock_bh(&ip6_sk_fl_lock); } -int ipv6_flowlabel_opt_get(struct sock *sk, struct in6_flowlabel_req *freq) +int ipv6_flowlabel_opt_get(struct sock *sk, struct in6_flowlabel_req *freq, + int flags) { struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_fl_socklist *sfl; + if (flags & IPV6_FL_F_REMOTE) { + freq->flr_label = np->rcv_flowinfo & IPV6_FLOWLABEL_MASK; + return 0; + } + if (np->repflow) { freq->flr_label = np->flow_label; return 0; diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 2855b00ed49d..7024a874e901 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -1221,6 +1221,7 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, case IPV6_FLOWLABEL_MGR: { struct in6_flowlabel_req freq; + int flags; if (len < sizeof(freq)) return -EINVAL; @@ -1232,9 +1233,11 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, return -EINVAL; len = sizeof(freq); + flags = freq.flr_flags; + memset(&freq, 0, sizeof(freq)); - val = ipv6_flowlabel_opt_get(sk, &freq); + val = ipv6_flowlabel_opt_get(sk, &freq, flags); if (val < 0) return val; -- cgit v1.2.3 From 6444f72b4b74f627c51891101e93ba2b94078b0a Mon Sep 17 00:00:00 2001 From: Florent Fourcot Date: Fri, 17 Jan 2014 17:15:05 +0100 Subject: ipv6: add flowlabel_consistency sysctl With the introduction of IPV6_FL_F_REFLECT, there is no guarantee of flow label unicity. This patch introduces a new sysctl to protect the old behaviour, enable by default. Changelog of V3: * rename ip6_flowlabel_consistency to flowlabel_consistency * use net_info_ratelimited() * checkpatch cleanups Signed-off-by: Florent Fourcot Acked-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 8 ++++++++ include/net/netns/ipv6.h | 1 + net/ipv6/af_inet6.c | 1 + net/ipv6/ip6_flowlabel.c | 7 +++++++ net/ipv6/sysctl_net_ipv6.c | 8 ++++++++ 5 files changed, 25 insertions(+) (limited to 'include/net') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index c97932c88ea3..5de03740cdd5 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1118,6 +1118,14 @@ bindv6only - BOOLEAN Default: FALSE (as specified in RFC3493) +flowlabel_consistency - BOOLEAN + Protect the consistency (and unicity) of flow label. + You have to disable it to use IPV6_FL_F_REFLECT flag on the + flow label manager. + TRUE: enabled + FALSE: disabled + Default: TRUE + anycast_src_echo_reply - BOOLEAN Controls the use of anycast addresses as source addresses for ICMPv6 echo reply diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index 592fecd2e59e..21edaf1f7916 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -27,6 +27,7 @@ struct netns_sysctl_ipv6 { int ip6_rt_gc_elasticity; int ip6_rt_mtu_expires; int ip6_rt_min_advmss; + int flowlabel_consistency; int icmpv6_time; int anycast_src_echo_reply; }; diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index c921d5d38831..d935889f1008 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -775,6 +775,7 @@ static int __net_init inet6_net_init(struct net *net) net->ipv6.sysctl.bindv6only = 0; net->ipv6.sysctl.icmpv6_time = 1*HZ; + net->ipv6.sysctl.flowlabel_consistency = 1; atomic_set(&net->ipv6.rt_genid, 0); err = ipv6_init_mibs(net); diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index 01bf2524c72a..dfa41bb4e0dc 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -588,8 +588,15 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) case IPV6_FL_A_GET: if (freq.flr_flags & IPV6_FL_F_REFLECT) { + struct net *net = sock_net(sk); + if (net->ipv6.sysctl.flowlabel_consistency) { + net_info_ratelimited("Can not set IPV6_FL_F_REFLECT if flowlabel_consistency sysctl is enable\n"); + return -EPERM; + } + if (sk->sk_protocol != IPPROTO_TCP) return -ENOPROTOOPT; + np->repflow = 1; return 0; } diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c index b51b2688102c..7f405a168822 100644 --- a/net/ipv6/sysctl_net_ipv6.c +++ b/net/ipv6/sysctl_net_ipv6.c @@ -31,6 +31,13 @@ static struct ctl_table ipv6_table_template[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "flowlabel_consistency", + .data = &init_net.ipv6.sysctl.flowlabel_consistency, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { } }; @@ -59,6 +66,7 @@ static int __net_init ipv6_sysctl_net_init(struct net *net) goto out; ipv6_table[0].data = &net->ipv6.sysctl.bindv6only; ipv6_table[1].data = &net->ipv6.sysctl.anycast_src_echo_reply; + ipv6_table[2].data = &net->ipv6.sysctl.flowlabel_consistency; ipv6_route_table = ipv6_route_sysctl_init(net); if (!ipv6_route_table) -- cgit v1.2.3 From 4b261c75a99f29c93a0b6babfc180cdf566bd654 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Mon, 20 Jan 2014 03:43:08 +0100 Subject: ipv6: make IPV6_RECVPKTINFO work for ipv4 datagrams We currently don't report IPV6_RECVPKTINFO in cmsg access ancillary data for IPv4 datagrams on IPv6 sockets. This patch splits the ip6_datagram_recv_ctl into two functions, one which handles both protocol families, AF_INET and AF_INET6, while the ip6_datagram_recv_specific_ctl only handles IPv6 cmsg data. ip6_datagram_recv_*_ctl never reported back any errors, so we can make them return void. Also provide a helper for protocols which don't offer dual personality to further use ip6_datagram_recv_ctl, which is exported to modules. I needed to shuffle the code for ping around a bit to make it easier to implement dual personality for ping ipv6 sockets in future. Reported-by: Gert Doering Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/linux/ipv6.h | 3 +++ include/net/ip.h | 1 + include/net/ping.h | 8 ++++++-- include/net/transp_v6.h | 9 +++++++-- net/ipv4/ip_sockglue.c | 6 +++--- net/ipv4/ping.c | 7 ++++++- net/ipv6/datagram.c | 38 ++++++++++++++++++++++++++++++-------- net/ipv6/ipv6_sockglue.c | 4 +--- net/ipv6/ping.c | 10 ++++++---- net/ipv6/udp.c | 6 +++++- 10 files changed, 68 insertions(+), 24 deletions(-) (limited to 'include/net') diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 1084304fd75a..2faef339d8f2 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -284,6 +284,8 @@ static inline void inet_sk_copy_descendant(struct sock *sk_to, #define __ipv6_only_sock(sk) (inet6_sk(sk)->ipv6only) #define ipv6_only_sock(sk) ((sk)->sk_family == PF_INET6 && __ipv6_only_sock(sk)) +#define ipv6_sk_rxinfo(sk) ((sk)->sk_family == PF_INET6 && \ + inet6_sk(sk)->rxopt.bits.rxinfo) static inline const struct in6_addr *inet6_rcv_saddr(const struct sock *sk) { @@ -300,6 +302,7 @@ static inline int inet_v6_ipv6only(const struct sock *sk) #else #define __ipv6_only_sock(sk) 0 #define ipv6_only_sock(sk) 0 +#define ipv6_sk_rxinfo(sk) 0 static inline struct ipv6_pinfo * inet6_sk(const struct sock *__sk) { diff --git a/include/net/ip.h b/include/net/ip.h index 7876e3b96ce3..23be0fd37937 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -63,6 +63,7 @@ struct ipcm_cookie { }; #define IPCB(skb) ((struct inet_skb_parm*)((skb)->cb)) +#define PKTINFO_SKB_CB(skb) ((struct in_pktinfo *)((skb)->cb)) struct ip_ra_chain { struct ip_ra_chain __rcu *next; diff --git a/include/net/ping.h b/include/net/ping.h index 76013653e08c..026479b61a2d 100644 --- a/include/net/ping.h +++ b/include/net/ping.h @@ -33,8 +33,12 @@ struct pingv6_ops { int (*ipv6_recv_error)(struct sock *sk, struct msghdr *msg, int len, int *addr_len); - int (*ip6_datagram_recv_ctl)(struct sock *sk, struct msghdr *msg, - struct sk_buff *skb); + void (*ip6_datagram_recv_common_ctl)(struct sock *sk, + struct msghdr *msg, + struct sk_buff *skb); + void (*ip6_datagram_recv_specific_ctl)(struct sock *sk, + struct msghdr *msg, + struct sk_buff *skb); int (*icmpv6_err_convert)(u8 type, u8 code, int *err); void (*ipv6_icmp_error)(struct sock *sk, struct sk_buff *skb, int err, __be16 port, u32 info, u8 *payload); diff --git a/include/net/transp_v6.h b/include/net/transp_v6.h index 48660e50ae90..b927413dde86 100644 --- a/include/net/transp_v6.h +++ b/include/net/transp_v6.h @@ -32,8 +32,13 @@ void tcpv6_exit(void); int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len); -int ip6_datagram_recv_ctl(struct sock *sk, struct msghdr *msg, - struct sk_buff *skb); +/* this does all the common and the specific ctl work */ +void ip6_datagram_recv_ctl(struct sock *sk, struct msghdr *msg, + struct sk_buff *skb); +void ip6_datagram_recv_common_ctl(struct sock *sk, struct msghdr *msg, + struct sk_buff *skb); +void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg, + struct sk_buff *skb); int ip6_datagram_send_ctl(struct net *net, struct sock *sk, struct msghdr *msg, struct flowi6 *fl6, struct ipv6_txoptions *opt, diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 22f15eb1c260..580dd96666e0 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -56,7 +56,6 @@ /* * SOL_IP control messages. */ -#define PKTINFO_SKB_CB(__skb) ((struct in_pktinfo *)((__skb)->cb)) static void ip_cmsg_recv_pktinfo(struct msghdr *msg, struct sk_buff *skb) { @@ -1055,9 +1054,10 @@ e_inval: void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb) { struct in_pktinfo *pktinfo = PKTINFO_SKB_CB(skb); + bool prepare = (inet_sk(sk)->cmsg_flags & IP_CMSG_PKTINFO) || + ipv6_sk_rxinfo(sk); - if ((inet_sk(sk)->cmsg_flags & IP_CMSG_PKTINFO) && - skb_rtable(skb)) { + if (prepare && skb_rtable(skb)) { pktinfo->ipi_ifindex = inet_iif(skb); pktinfo->ipi_spec_dst.s_addr = fib_compute_spec_dst(skb); } else { diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index e09e8839d622..4a9e4266a0c3 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -906,7 +906,12 @@ int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, } if (inet6_sk(sk)->rxopt.all) - pingv6_ops.ip6_datagram_recv_ctl(sk, msg, skb); + pingv6_ops.ip6_datagram_recv_common_ctl(sk, msg, skb); + if (skb->protocol == htons(ETH_P_IPV6) && + inet6_sk(sk)->rxopt.all) + pingv6_ops.ip6_datagram_recv_specific_ctl(sk, msg, skb); + else if (skb->protocol == htons(ETH_P_IP) && isk->cmsg_flags) + ip_cmsg_recv(msg, skb); #endif } else { BUG(); diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index bce73cbd203a..cd8699bd2e6b 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -377,10 +377,12 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len) sin->sin6_family = AF_INET6; sin->sin6_flowinfo = 0; sin->sin6_port = 0; + if (np->rxopt.all) + ip6_datagram_recv_common_ctl(sk, msg, skb); if (skb->protocol == htons(ETH_P_IPV6)) { sin->sin6_addr = ipv6_hdr(skb)->saddr; if (np->rxopt.all) - ip6_datagram_recv_ctl(sk, msg, skb); + ip6_datagram_recv_specific_ctl(sk, msg, skb); sin->sin6_scope_id = ipv6_iface_scope_id(&sin->sin6_addr, IP6CB(skb)->iif); @@ -471,20 +473,34 @@ out: } -int ip6_datagram_recv_ctl(struct sock *sk, struct msghdr *msg, - struct sk_buff *skb) +void ip6_datagram_recv_common_ctl(struct sock *sk, struct msghdr *msg, + struct sk_buff *skb) { struct ipv6_pinfo *np = inet6_sk(sk); - struct inet6_skb_parm *opt = IP6CB(skb); - unsigned char *nh = skb_network_header(skb); + bool is_ipv6 = skb->protocol == htons(ETH_P_IPV6); if (np->rxopt.bits.rxinfo) { struct in6_pktinfo src_info; - src_info.ipi6_ifindex = opt->iif; - src_info.ipi6_addr = ipv6_hdr(skb)->daddr; + if (is_ipv6) { + src_info.ipi6_ifindex = IP6CB(skb)->iif; + src_info.ipi6_addr = ipv6_hdr(skb)->daddr; + } else { + src_info.ipi6_ifindex = + PKTINFO_SKB_CB(skb)->ipi_ifindex; + ipv6_addr_set_v4mapped(ip_hdr(skb)->daddr, + &src_info.ipi6_addr); + } put_cmsg(msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info); } +} + +void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg, + struct sk_buff *skb) +{ + struct ipv6_pinfo *np = inet6_sk(sk); + struct inet6_skb_parm *opt = IP6CB(skb); + unsigned char *nh = skb_network_header(skb); if (np->rxopt.bits.rxhlim) { int hlim = ipv6_hdr(skb)->hop_limit; @@ -602,7 +618,13 @@ int ip6_datagram_recv_ctl(struct sock *sk, struct msghdr *msg, put_cmsg(msg, SOL_IPV6, IPV6_ORIGDSTADDR, sizeof(sin6), &sin6); } } - return 0; +} + +void ip6_datagram_recv_ctl(struct sock *sk, struct msghdr *msg, + struct sk_buff *skb) +{ + ip6_datagram_recv_common_ctl(sk, msg, skb); + ip6_datagram_recv_specific_ctl(sk, msg, skb); } EXPORT_SYMBOL_GPL(ip6_datagram_recv_ctl); diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 7024a874e901..0a00f449de5e 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -1002,10 +1002,8 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, release_sock(sk); if (skb) { - int err = ip6_datagram_recv_ctl(sk, &msg, skb); + ip6_datagram_recv_ctl(sk, &msg, skb); kfree_skb(skb); - if (err) - return err; } else { if (np->rxopt.bits.rxinfo) { struct in6_pktinfo src_info; diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index cd71f3a540be..94a3d04c3200 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -62,10 +62,9 @@ static int dummy_ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, { return -EAFNOSUPPORT; } -static int dummy_ip6_datagram_recv_ctl(struct sock *sk, struct msghdr *msg, +static void dummy_ip6_datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) { - return -EAFNOSUPPORT; } static int dummy_icmpv6_err_convert(u8 type, u8 code, int *err) { @@ -254,7 +253,9 @@ int __init pingv6_init(void) return ret; #endif pingv6_ops.ipv6_recv_error = ipv6_recv_error; - pingv6_ops.ip6_datagram_recv_ctl = ip6_datagram_recv_ctl; + pingv6_ops.ip6_datagram_recv_common_ctl = ip6_datagram_recv_common_ctl; + pingv6_ops.ip6_datagram_recv_specific_ctl = + ip6_datagram_recv_specific_ctl; pingv6_ops.icmpv6_err_convert = icmpv6_err_convert; pingv6_ops.ipv6_icmp_error = ipv6_icmp_error; pingv6_ops.ipv6_chk_addr = ipv6_chk_addr; @@ -267,7 +268,8 @@ int __init pingv6_init(void) void pingv6_exit(void) { pingv6_ops.ipv6_recv_error = dummy_ipv6_recv_error; - pingv6_ops.ip6_datagram_recv_ctl = dummy_ip6_datagram_recv_ctl; + pingv6_ops.ip6_datagram_recv_common_ctl = dummy_ip6_datagram_recv_ctl; + pingv6_ops.ip6_datagram_recv_specific_ctl = dummy_ip6_datagram_recv_ctl; pingv6_ops.icmpv6_err_convert = dummy_icmpv6_err_convert; pingv6_ops.ipv6_icmp_error = dummy_ipv6_icmp_error; pingv6_ops.ipv6_chk_addr = dummy_ipv6_chk_addr; diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 233c3ab6aee4..1e586d92260e 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -477,12 +477,16 @@ try_again: } *addr_len = sizeof(*sin6); } + + if (np->rxopt.all) + ip6_datagram_recv_common_ctl(sk, msg, skb); + if (is_udp4) { if (inet->cmsg_flags) ip_cmsg_recv(msg, skb); } else { if (np->rxopt.all) - ip6_datagram_recv_ctl(sk, msg, skb); + ip6_datagram_recv_specific_ctl(sk, msg, skb); } err = copied; -- cgit v1.2.3 From 671314a5abb7abb8346cd3f16f75c1e55ed7667b Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Wed, 15 Jan 2014 15:49:30 -0800 Subject: net_sched: act: remove capab from struct tc_action_ops It is not actually implemented. Cc: Jamal Hadi Salim Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/net/act_api.h | 2 -- net/sched/act_csum.c | 1 - net/sched/act_gact.c | 1 - net/sched/act_ipt.c | 2 -- net/sched/act_mirred.c | 1 - net/sched/act_nat.c | 1 - net/sched/act_pedit.c | 1 - net/sched/act_police.c | 1 - net/sched/act_simple.c | 1 - net/sched/act_skbedit.c | 1 - 10 files changed, 12 deletions(-) (limited to 'include/net') diff --git a/include/net/act_api.h b/include/net/act_api.h index e171387512b3..8ed974665640 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -81,13 +81,11 @@ struct tc_action { struct list_head list; }; -#define TCA_CAP_NONE 0 struct tc_action_ops { struct list_head head; struct tcf_hashinfo *hinfo; char kind[IFNAMSIZ]; __u32 type; /* TBD to match kind */ - __u32 capab; /* capabilities includes 4 bit version */ struct module *owner; int (*act)(struct sk_buff *, const struct tc_action *, struct tcf_result *); int (*dump)(struct sk_buff *, struct tc_action *, int, int); diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c index ee28e1ccbbd5..9d5c1d343fe2 100644 --- a/net/sched/act_csum.c +++ b/net/sched/act_csum.c @@ -572,7 +572,6 @@ static struct tc_action_ops act_csum_ops = { .kind = "csum", .hinfo = &csum_hash_info, .type = TCA_ACT_CSUM, - .capab = TCA_CAP_NONE, .owner = THIS_MODULE, .act = tcf_csum, .dump = tcf_csum_dump, diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c index 31333073cd80..72c49de50616 100644 --- a/net/sched/act_gact.c +++ b/net/sched/act_gact.c @@ -194,7 +194,6 @@ static struct tc_action_ops act_gact_ops = { .kind = "gact", .hinfo = &gact_hash_info, .type = TCA_ACT_GACT, - .capab = TCA_CAP_NONE, .owner = THIS_MODULE, .act = tcf_gact, .dump = tcf_gact_dump, diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index bc9f498f6fef..67d701e0a2bd 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -287,7 +287,6 @@ static struct tc_action_ops act_ipt_ops = { .kind = "ipt", .hinfo = &ipt_hash_info, .type = TCA_ACT_IPT, - .capab = TCA_CAP_NONE, .owner = THIS_MODULE, .act = tcf_ipt, .dump = tcf_ipt_dump, @@ -299,7 +298,6 @@ static struct tc_action_ops act_xt_ops = { .kind = "xt", .hinfo = &ipt_hash_info, .type = TCA_ACT_XT, - .capab = TCA_CAP_NONE, .owner = THIS_MODULE, .act = tcf_ipt, .dump = tcf_ipt_dump, diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index 5d05b573a622..376234ee8514 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -257,7 +257,6 @@ static struct tc_action_ops act_mirred_ops = { .kind = "mirred", .hinfo = &mirred_hash_info, .type = TCA_ACT_MIRRED, - .capab = TCA_CAP_NONE, .owner = THIS_MODULE, .act = tcf_mirred, .dump = tcf_mirred_dump, diff --git a/net/sched/act_nat.c b/net/sched/act_nat.c index a49fa23b49dd..46e1aa36b147 100644 --- a/net/sched/act_nat.c +++ b/net/sched/act_nat.c @@ -296,7 +296,6 @@ static struct tc_action_ops act_nat_ops = { .kind = "nat", .hinfo = &nat_hash_info, .type = TCA_ACT_NAT, - .capab = TCA_CAP_NONE, .owner = THIS_MODULE, .act = tcf_nat, .dump = tcf_nat_dump, diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index f361e4e3c314..109265d7c14b 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -233,7 +233,6 @@ static struct tc_action_ops act_pedit_ops = { .kind = "pedit", .hinfo = &pedit_hash_info, .type = TCA_ACT_PEDIT, - .capab = TCA_CAP_NONE, .owner = THIS_MODULE, .act = tcf_pedit, .dump = tcf_pedit_dump, diff --git a/net/sched/act_police.c b/net/sched/act_police.c index 5ba467b6124a..85437ba5c64b 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -361,7 +361,6 @@ static struct tc_action_ops act_police_ops = { .kind = "police", .hinfo = &police_hash_info, .type = TCA_ID_POLICE, - .capab = TCA_CAP_NONE, .owner = THIS_MODULE, .act = tcf_act_police, .dump = tcf_act_police_dump, diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c index f7d5406c1fe2..92236daaac8d 100644 --- a/net/sched/act_simple.c +++ b/net/sched/act_simple.c @@ -190,7 +190,6 @@ static struct tc_action_ops act_simp_ops = { .kind = "simple", .hinfo = &simp_hash_info, .type = TCA_ACT_SIMP, - .capab = TCA_CAP_NONE, .owner = THIS_MODULE, .act = tcf_simp, .dump = tcf_simp_dump, diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c index 74af46127060..c36b5209bc15 100644 --- a/net/sched/act_skbedit.c +++ b/net/sched/act_skbedit.c @@ -189,7 +189,6 @@ static struct tc_action_ops act_skbedit_ops = { .kind = "skbedit", .hinfo = &skbedit_hash_info, .type = TCA_ACT_SKBEDIT, - .capab = TCA_CAP_NONE, .owner = THIS_MODULE, .act = tcf_skbedit, .dump = tcf_skbedit_dump, -- cgit v1.2.3 From c779f7af99f73abb7270dcaa4c29178ab5ef7472 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Fri, 17 Jan 2014 11:37:02 -0800 Subject: net_sched: act: fetch hinfo from a->ops->hinfo Every action ops has a pointer to hash info, so we don't need to hard-code it in each module. Cc: Jamal Hadi Salim Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/net/act_api.h | 4 ++-- net/sched/act_api.c | 23 +++++++++++------------ net/sched/act_csum.c | 9 ++++----- net/sched/act_gact.c | 11 +++++------ net/sched/act_ipt.c | 7 +++---- net/sched/act_mirred.c | 7 +++---- net/sched/act_nat.c | 9 ++++----- net/sched/act_pedit.c | 9 ++++----- net/sched/act_police.c | 18 ++++++++++-------- net/sched/act_simple.c | 7 +++---- net/sched/act_skbedit.c | 9 ++++----- 11 files changed, 53 insertions(+), 60 deletions(-) (limited to 'include/net') diff --git a/include/net/act_api.h b/include/net/act_api.h index 8ed974665640..152316b3f59a 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -103,10 +103,10 @@ int tcf_hash_release(struct tcf_common *p, int bind, struct tcf_hashinfo *hinfo); u32 tcf_hash_new_index(struct tcf_hashinfo *hinfo); struct tcf_common *tcf_hash_check(u32 index, struct tc_action *a, - int bind, struct tcf_hashinfo *hinfo); + int bind); struct tcf_common *tcf_hash_create(u32 index, struct nlattr *est, struct tc_action *a, int size, - int bind, struct tcf_hashinfo *hinfo); + int bind); void tcf_hash_insert(struct tcf_common *p, struct tcf_hashinfo *hinfo); int tcf_register_action(struct tc_action_ops *a); diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 35f89e9ce49c..b94825322d4d 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -62,8 +62,9 @@ int tcf_hash_release(struct tcf_common *p, int bind, EXPORT_SYMBOL(tcf_hash_release); static int tcf_dump_walker(struct sk_buff *skb, struct netlink_callback *cb, - struct tc_action *a, struct tcf_hashinfo *hinfo) + struct tc_action *a) { + struct tcf_hashinfo *hinfo = a->ops->hinfo; struct hlist_head *head; struct tcf_common *p; int err = 0, index = -1, i = 0, s_i = 0, n_i = 0; @@ -109,9 +110,9 @@ nla_put_failure: goto done; } -static int tcf_del_walker(struct sk_buff *skb, struct tc_action *a, - struct tcf_hashinfo *hinfo) +static int tcf_del_walker(struct sk_buff *skb, struct tc_action *a) { + struct tcf_hashinfo *hinfo = a->ops->hinfo; struct hlist_head *head; struct hlist_node *n; struct tcf_common *p; @@ -145,12 +146,10 @@ nla_put_failure: static int tcf_generic_walker(struct sk_buff *skb, struct netlink_callback *cb, int type, struct tc_action *a) { - struct tcf_hashinfo *hinfo = a->ops->hinfo; - if (type == RTM_DELACTION) { - return tcf_del_walker(skb, a, hinfo); + return tcf_del_walker(skb, a); } else if (type == RTM_GETACTION) { - return tcf_dump_walker(skb, cb, a, hinfo); + return tcf_dump_walker(skb, cb, a); } else { WARN(1, "tcf_generic_walker: unknown action %d\n", type); return -EINVAL; @@ -199,9 +198,9 @@ static int tcf_hash_search(struct tc_action *a, u32 index) return 0; } -struct tcf_common *tcf_hash_check(u32 index, struct tc_action *a, int bind, - struct tcf_hashinfo *hinfo) +struct tcf_common *tcf_hash_check(u32 index, struct tc_action *a, int bind) { + struct tcf_hashinfo *hinfo = a->ops->hinfo; struct tcf_common *p = NULL; if (index && (p = tcf_hash_lookup(index, hinfo)) != NULL) { if (bind) @@ -214,9 +213,9 @@ struct tcf_common *tcf_hash_check(u32 index, struct tc_action *a, int bind, EXPORT_SYMBOL(tcf_hash_check); struct tcf_common *tcf_hash_create(u32 index, struct nlattr *est, - struct tc_action *a, int size, int bind, - struct tcf_hashinfo *hinfo) + struct tc_action *a, int size, int bind) { + struct tcf_hashinfo *hinfo = a->ops->hinfo; struct tcf_common *p = kzalloc(size, GFP_KERNEL); if (unlikely(!p)) @@ -495,6 +494,7 @@ struct tc_action *tcf_action_init_1(struct net *net, struct nlattr *nla, if (a == NULL) goto err_mod; + a->ops = a_o; INIT_LIST_HEAD(&a->list); /* backward compatibility for policer */ if (name == NULL) @@ -510,7 +510,6 @@ struct tc_action *tcf_action_init_1(struct net *net, struct nlattr *nla, */ if (err != ACT_P_CREATED) module_put(a_o->owner); - a->ops = a_o; return a; diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c index 9d5c1d343fe2..2210187c45c2 100644 --- a/net/sched/act_csum.c +++ b/net/sched/act_csum.c @@ -63,17 +63,16 @@ static int tcf_csum_init(struct net *n, struct nlattr *nla, struct nlattr *est, return -EINVAL; parm = nla_data(tb[TCA_CSUM_PARMS]); - pc = tcf_hash_check(parm->index, a, bind, &csum_hash_info); + pc = tcf_hash_check(parm->index, a, bind); if (!pc) { - pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind, - &csum_hash_info); + pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind); if (IS_ERR(pc)) return PTR_ERR(pc); ret = ACT_P_CREATED; } else { if (bind)/* dont override defaults */ return 0; - tcf_hash_release(pc, bind, &csum_hash_info); + tcf_hash_release(pc, bind, a->ops->hinfo); if (!ovr) return -EEXIST; } @@ -85,7 +84,7 @@ static int tcf_csum_init(struct net *n, struct nlattr *nla, struct nlattr *est, spin_unlock_bh(&p->tcf_lock); if (ret == ACT_P_CREATED) - tcf_hash_insert(pc, &csum_hash_info); + tcf_hash_insert(pc, a->ops->hinfo); return ret; } diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c index 72c49de50616..a0eed30d5811 100644 --- a/net/sched/act_gact.c +++ b/net/sched/act_gact.c @@ -86,17 +86,16 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla, } #endif - pc = tcf_hash_check(parm->index, a, bind, &gact_hash_info); + pc = tcf_hash_check(parm->index, a, bind); if (!pc) { - pc = tcf_hash_create(parm->index, est, a, sizeof(*gact), - bind, &gact_hash_info); + pc = tcf_hash_create(parm->index, est, a, sizeof(*gact), bind); if (IS_ERR(pc)) return PTR_ERR(pc); ret = ACT_P_CREATED; } else { if (bind)/* dont override defaults */ return 0; - tcf_hash_release(pc, bind, &gact_hash_info); + tcf_hash_release(pc, bind, a->ops->hinfo); if (!ovr) return -EEXIST; } @@ -114,7 +113,7 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla, #endif spin_unlock_bh(&gact->tcf_lock); if (ret == ACT_P_CREATED) - tcf_hash_insert(pc, &gact_hash_info); + tcf_hash_insert(pc, a->ops->hinfo); return ret; } @@ -123,7 +122,7 @@ static int tcf_gact_cleanup(struct tc_action *a, int bind) struct tcf_gact *gact = a->priv; if (gact) - return tcf_hash_release(&gact->common, bind, &gact_hash_info); + return tcf_hash_release(&gact->common, bind, a->ops->hinfo); return 0; } diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index 67d701e0a2bd..0a6d62174027 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -125,10 +125,9 @@ static int tcf_ipt_init(struct net *net, struct nlattr *nla, struct nlattr *est, if (tb[TCA_IPT_INDEX] != NULL) index = nla_get_u32(tb[TCA_IPT_INDEX]); - pc = tcf_hash_check(index, a, bind, &ipt_hash_info); + pc = tcf_hash_check(index, a, bind); if (!pc) { - pc = tcf_hash_create(index, est, a, sizeof(*ipt), bind, - &ipt_hash_info); + pc = tcf_hash_create(index, est, a, sizeof(*ipt), bind); if (IS_ERR(pc)) return PTR_ERR(pc); ret = ACT_P_CREATED; @@ -171,7 +170,7 @@ static int tcf_ipt_init(struct net *net, struct nlattr *nla, struct nlattr *est, ipt->tcfi_hook = hook; spin_unlock_bh(&ipt->tcf_lock); if (ret == ACT_P_CREATED) - tcf_hash_insert(pc, &ipt_hash_info); + tcf_hash_insert(pc, a->ops->hinfo); return ret; err3: diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index 376234ee8514..0b2c6d39d396 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -101,12 +101,11 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla, dev = NULL; } - pc = tcf_hash_check(parm->index, a, bind, &mirred_hash_info); + pc = tcf_hash_check(parm->index, a, bind); if (!pc) { if (dev == NULL) return -EINVAL; - pc = tcf_hash_create(parm->index, est, a, sizeof(*m), bind, - &mirred_hash_info); + pc = tcf_hash_create(parm->index, est, a, sizeof(*m), bind); if (IS_ERR(pc)) return PTR_ERR(pc); ret = ACT_P_CREATED; @@ -132,7 +131,7 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla, spin_unlock_bh(&m->tcf_lock); if (ret == ACT_P_CREATED) { list_add(&m->tcfm_list, &mirred_list); - tcf_hash_insert(pc, &mirred_hash_info); + tcf_hash_insert(pc, a->ops->hinfo); } return ret; diff --git a/net/sched/act_nat.c b/net/sched/act_nat.c index 46e1aa36b147..81f0404bb335 100644 --- a/net/sched/act_nat.c +++ b/net/sched/act_nat.c @@ -57,17 +57,16 @@ static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est, return -EINVAL; parm = nla_data(tb[TCA_NAT_PARMS]); - pc = tcf_hash_check(parm->index, a, bind, &nat_hash_info); + pc = tcf_hash_check(parm->index, a, bind); if (!pc) { - pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind, - &nat_hash_info); + pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind); if (IS_ERR(pc)) return PTR_ERR(pc); ret = ACT_P_CREATED; } else { if (bind) return 0; - tcf_hash_release(pc, bind, &nat_hash_info); + tcf_hash_release(pc, bind, a->ops->hinfo); if (!ovr) return -EEXIST; } @@ -83,7 +82,7 @@ static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est, spin_unlock_bh(&p->tcf_lock); if (ret == ACT_P_CREATED) - tcf_hash_insert(pc, &nat_hash_info); + tcf_hash_insert(pc, a->ops->hinfo); return ret; } diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index 109265d7c14b..be3f0f6875bb 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -57,12 +57,11 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, if (nla_len(tb[TCA_PEDIT_PARMS]) < sizeof(*parm) + ksize) return -EINVAL; - pc = tcf_hash_check(parm->index, a, bind, &pedit_hash_info); + pc = tcf_hash_check(parm->index, a, bind); if (!pc) { if (!parm->nkeys) return -EINVAL; - pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind, - &pedit_hash_info); + pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind); if (IS_ERR(pc)) return PTR_ERR(pc); p = to_pedit(pc); @@ -77,7 +76,7 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, ret = ACT_P_CREATED; } else { p = to_pedit(pc); - tcf_hash_release(pc, bind, &pedit_hash_info); + tcf_hash_release(pc, bind, a->ops->hinfo); if (bind) return 0; if (!ovr) @@ -101,7 +100,7 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, memcpy(p->tcfp_keys, parm->keys, ksize); spin_unlock_bh(&p->tcf_lock); if (ret == ACT_P_CREATED) - tcf_hash_insert(pc, &pedit_hash_info); + tcf_hash_insert(pc, a->ops->hinfo); return ret; } diff --git a/net/sched/act_police.c b/net/sched/act_police.c index 85437ba5c64b..c7093896cf14 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -59,17 +59,18 @@ struct tc_police_compat { static int tcf_act_police_walker(struct sk_buff *skb, struct netlink_callback *cb, int type, struct tc_action *a) { + struct tcf_hashinfo *hinfo = a->ops->hinfo; struct hlist_head *head; struct tcf_common *p; int err = 0, index = -1, i = 0, s_i = 0, n_i = 0; struct nlattr *nest; - spin_lock_bh(&police_hash_info.lock); + spin_lock_bh(&hinfo->lock); s_i = cb->args[0]; for (i = 0; i < (POL_TAB_MASK + 1); i++) { - head = &police_hash_info.htab[tcf_hash(i, POL_TAB_MASK)]; + head = &hinfo->htab[tcf_hash(i, POL_TAB_MASK)]; hlist_for_each_entry_rcu(p, head, tcfc_head) { index++; @@ -94,7 +95,7 @@ static int tcf_act_police_walker(struct sk_buff *skb, struct netlink_callback *c } } done: - spin_unlock_bh(&police_hash_info.lock); + spin_unlock_bh(&hinfo->lock); if (n_i) cb->args[0] += n_i; return n_i; @@ -121,6 +122,7 @@ static int tcf_act_police_locate(struct net *net, struct nlattr *nla, struct tc_police *parm; struct tcf_police *police; struct qdisc_rate_table *R_tab = NULL, *P_tab = NULL; + struct tcf_hashinfo *hinfo = a->ops->hinfo; int size; if (nla == NULL) @@ -140,7 +142,7 @@ static int tcf_act_police_locate(struct net *net, struct nlattr *nla, if (parm->index) { struct tcf_common *pc; - pc = tcf_hash_lookup(parm->index, &police_hash_info); + pc = tcf_hash_lookup(parm->index, hinfo); if (pc != NULL) { a->priv = pc; police = to_police(pc); @@ -236,11 +238,11 @@ override: police->tcfp_t_c = ktime_to_ns(ktime_get()); police->tcf_index = parm->index ? parm->index : - tcf_hash_new_index(&police_hash_info); + tcf_hash_new_index(a->ops->hinfo); h = tcf_hash(police->tcf_index, POL_TAB_MASK); - spin_lock_bh(&police_hash_info.lock); - hlist_add_head(&police->tcf_head, &police_hash_info.htab[h]); - spin_unlock_bh(&police_hash_info.lock); + spin_lock_bh(&hinfo->lock); + hlist_add_head(&police->tcf_head, &hinfo->htab[h]); + spin_unlock_bh(&hinfo->lock); a->priv = police; return ret; diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c index 92236daaac8d..8ef2f1fcbfba 100644 --- a/net/sched/act_simple.c +++ b/net/sched/act_simple.c @@ -114,10 +114,9 @@ static int tcf_simp_init(struct net *net, struct nlattr *nla, parm = nla_data(tb[TCA_DEF_PARMS]); defdata = nla_data(tb[TCA_DEF_DATA]); - pc = tcf_hash_check(parm->index, a, bind, &simp_hash_info); + pc = tcf_hash_check(parm->index, a, bind); if (!pc) { - pc = tcf_hash_create(parm->index, est, a, sizeof(*d), bind, - &simp_hash_info); + pc = tcf_hash_create(parm->index, est, a, sizeof(*d), bind); if (IS_ERR(pc)) return PTR_ERR(pc); @@ -145,7 +144,7 @@ static int tcf_simp_init(struct net *net, struct nlattr *nla, } if (ret == ACT_P_CREATED) - tcf_hash_insert(pc, &simp_hash_info); + tcf_hash_insert(pc, a->ops->hinfo); return ret; } diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c index c36b5209bc15..98725080b5aa 100644 --- a/net/sched/act_skbedit.c +++ b/net/sched/act_skbedit.c @@ -100,10 +100,9 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla, parm = nla_data(tb[TCA_SKBEDIT_PARMS]); - pc = tcf_hash_check(parm->index, a, bind, &skbedit_hash_info); + pc = tcf_hash_check(parm->index, a, bind); if (!pc) { - pc = tcf_hash_create(parm->index, est, a, sizeof(*d), bind, - &skbedit_hash_info); + pc = tcf_hash_create(parm->index, est, a, sizeof(*d), bind); if (IS_ERR(pc)) return PTR_ERR(pc); @@ -113,7 +112,7 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla, d = to_skbedit(pc); if (bind) return 0; - tcf_hash_release(pc, bind, &skbedit_hash_info); + tcf_hash_release(pc, bind, a->ops->hinfo); if (!ovr) return -EEXIST; } @@ -133,7 +132,7 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla, spin_unlock_bh(&d->tcf_lock); if (ret == ACT_P_CREATED) - tcf_hash_insert(pc, &skbedit_hash_info); + tcf_hash_insert(pc, a->ops->hinfo); return ret; } -- cgit v1.2.3 From 6e6a50c254220acb19bd338ce433b1770e4a7a3c Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Fri, 17 Jan 2014 11:37:03 -0800 Subject: net_sched: act: export tcf_hash_search() instead of tcf_hash_lookup() So that we will not expose struct tcf_common to modules. Cc: Jamal Hadi Salim Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/net/act_api.h | 2 +- net/sched/act_api.c | 6 +++--- net/sched/act_police.c | 8 ++------ 3 files changed, 6 insertions(+), 10 deletions(-) (limited to 'include/net') diff --git a/include/net/act_api.h b/include/net/act_api.h index 152316b3f59a..788d8378e587 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -97,7 +97,7 @@ struct tc_action_ops { int (*walk)(struct sk_buff *, struct netlink_callback *, int, struct tc_action *); }; -struct tcf_common *tcf_hash_lookup(u32 index, struct tcf_hashinfo *hinfo); +int tcf_hash_search(struct tc_action *a, u32 index); void tcf_hash_destroy(struct tcf_common *p, struct tcf_hashinfo *hinfo); int tcf_hash_release(struct tcf_common *p, int bind, struct tcf_hashinfo *hinfo); diff --git a/net/sched/act_api.c b/net/sched/act_api.c index b94825322d4d..72bdc7166345 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -156,7 +156,7 @@ static int tcf_generic_walker(struct sk_buff *skb, struct netlink_callback *cb, } } -struct tcf_common *tcf_hash_lookup(u32 index, struct tcf_hashinfo *hinfo) +static struct tcf_common *tcf_hash_lookup(u32 index, struct tcf_hashinfo *hinfo) { struct tcf_common *p = NULL; struct hlist_head *head; @@ -170,7 +170,6 @@ struct tcf_common *tcf_hash_lookup(u32 index, struct tcf_hashinfo *hinfo) return p; } -EXPORT_SYMBOL(tcf_hash_lookup); u32 tcf_hash_new_index(struct tcf_hashinfo *hinfo) { @@ -186,7 +185,7 @@ u32 tcf_hash_new_index(struct tcf_hashinfo *hinfo) } EXPORT_SYMBOL(tcf_hash_new_index); -static int tcf_hash_search(struct tc_action *a, u32 index) +int tcf_hash_search(struct tc_action *a, u32 index) { struct tcf_hashinfo *hinfo = a->ops->hinfo; struct tcf_common *p = tcf_hash_lookup(index, hinfo); @@ -197,6 +196,7 @@ static int tcf_hash_search(struct tc_action *a, u32 index) } return 0; } +EXPORT_SYMBOL(tcf_hash_search); struct tcf_common *tcf_hash_check(u32 index, struct tc_action *a, int bind) { diff --git a/net/sched/act_police.c b/net/sched/act_police.c index c7093896cf14..1778209a332f 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -140,12 +140,8 @@ static int tcf_act_police_locate(struct net *net, struct nlattr *nla, parm = nla_data(tb[TCA_POLICE_TBF]); if (parm->index) { - struct tcf_common *pc; - - pc = tcf_hash_lookup(parm->index, hinfo); - if (pc != NULL) { - a->priv = pc; - police = to_police(pc); + if (tcf_hash_search(a, parm->index)) { + police = to_police(a->priv); if (bind) { police->tcf_bindcnt += 1; police->tcf_refcnt += 1; -- cgit v1.2.3 From 82b276cd2b0bacd58e7c307bf8856925a68c4d14 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Mon, 20 Jan 2014 05:16:39 +0100 Subject: ipv6: protect protocols not handling ipv4 from v4 connection/bind attempts Some ipv6 protocols cannot handle ipv4 addresses, so we must not allow connecting and binding to them. sendmsg logic does already check msg->name for this but must trust already connected sockets which could be set up for connection to ipv4 address family. Per-socket flag ipv6only is of no use here, as it is under users control by setsockopt. Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/net/ipv6.h | 2 ++ net/ipv4/ping.c | 3 +++ net/ipv6/datagram.c | 10 ++++++++++ net/ipv6/ping.c | 2 +- net/ipv6/raw.c | 6 +++++- net/l2tp/l2tp_ip6.c | 3 +++ 6 files changed, 24 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 78d3d5124918..4f541f11ce63 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -783,6 +783,8 @@ int compat_ipv6_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen); int ip6_datagram_connect(struct sock *sk, struct sockaddr *addr, int addr_len); +int ip6_datagram_connect_v6_only(struct sock *sk, struct sockaddr *addr, + int addr_len); int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len); diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 4a9e4266a0c3..2d11c094296e 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -320,6 +320,9 @@ static int ping_check_bind_addr(struct sock *sk, struct inet_sock *isk, if (addr_len < sizeof(*addr)) return -EINVAL; + if (addr->sin6_family != AF_INET6) + return -EINVAL; + pr_debug("ping_check_bind_addr(sk=%p,addr=%pI6c,port=%d)\n", sk, addr->sin6_addr.s6_addr, ntohs(addr->sin6_port)); diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index cd8699bd2e6b..2f5e2f154d21 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -205,6 +205,16 @@ out: } EXPORT_SYMBOL_GPL(ip6_datagram_connect); +int ip6_datagram_connect_v6_only(struct sock *sk, struct sockaddr *uaddr, + int addr_len) +{ + DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, uaddr); + if (sin6->sin6_family != AF_INET6) + return -EAFNOSUPPORT; + return ip6_datagram_connect(sk, uaddr, addr_len); +} +EXPORT_SYMBOL_GPL(ip6_datagram_connect_v6_only); + void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, __be16 port, u32 info, u8 *payload) { diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index 94a3d04c3200..fb9beb78f00b 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -31,7 +31,7 @@ struct proto pingv6_prot = { .owner = THIS_MODULE, .init = ping_init_sock, .close = ping_close, - .connect = ip6_datagram_connect, + .connect = ip6_datagram_connect_v6_only, .disconnect = udp_disconnect, .setsockopt = ipv6_setsockopt, .getsockopt = ipv6_getsockopt, diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index b4523117aeae..1f29996e368a 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -250,6 +250,10 @@ static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) if (addr_len < SIN6_LEN_RFC2133) return -EINVAL; + + if (addr->sin6_family != AF_INET6) + return -EINVAL; + addr_type = ipv6_addr_type(&addr->sin6_addr); /* Raw sockets are IPv6 only */ @@ -1209,7 +1213,7 @@ struct proto rawv6_prot = { .owner = THIS_MODULE, .close = rawv6_close, .destroy = raw6_destroy, - .connect = ip6_datagram_connect, + .connect = ip6_datagram_connect_v6_only, .disconnect = udp_disconnect, .ioctl = rawv6_ioctl, .init = rawv6_init_sk, diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c index aab5f74e91e6..7704ea9502fd 100644 --- a/net/l2tp/l2tp_ip6.c +++ b/net/l2tp/l2tp_ip6.c @@ -371,6 +371,9 @@ static int l2tp_ip6_connect(struct sock *sk, struct sockaddr *uaddr, if (addr_len < sizeof(*lsa)) return -EINVAL; + if (usin->sin6_family != AF_INET6) + return -EINVAL; + addr_type = ipv6_addr_type(&usin->sin6_addr); if (addr_type & IPV6_ADDR_MULTICAST) return -EINVAL; -- cgit v1.2.3 From b582ef0990d457f7ce8ccf827af51a575ca0b4a6 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Mon, 20 Jan 2014 13:59:19 +0200 Subject: net: Add GRO support for UDP encapsulating protocols Add GRO handlers for protocols that do UDP encapsulation, with the intent of being able to coalesce packets which encapsulate packets belonging to the same TCP session. For GRO purposes, the destination UDP port takes the role of the ether type field in the ethernet header or the next protocol in the IP header. The UDP GRO handler will only attempt to coalesce packets whose destination port is registered to have gro handler. Use a mark on the skb GRO CB data to disallow (flush) running the udp gro receive code twice on a packet. This solves the problem of udp encapsulated packets whose inner VM packet is udp and happen to carry a port which has registered offloads. Signed-off-by: Shlomo Pongratz Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- include/linux/netdevice.h | 10 +++- include/net/protocol.h | 3 + net/core/dev.c | 1 + net/ipv4/udp_offload.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 156 insertions(+), 1 deletion(-) (limited to 'include/net') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 83ce2aee65e6..c31022980e18 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1675,7 +1675,10 @@ struct napi_gro_cb { unsigned long age; /* Used in ipv6_gro_receive() */ - int proto; + u16 proto; + + /* Used in udp_gro_receive */ + u16 udp_mark; /* used to support CHECKSUM_COMPLETE for tunneling protocols */ __wsum csum; @@ -1714,6 +1717,11 @@ struct packet_offload { struct list_head list; }; +struct udp_offload { + __be16 port; + struct offload_callbacks callbacks; +}; + /* often modified stats are per cpu, other are shared (netdev->stats) */ struct pcpu_sw_netstats { u64 rx_packets; diff --git a/include/net/protocol.h b/include/net/protocol.h index 0e5f8665d7fb..a7e986b08147 100644 --- a/include/net/protocol.h +++ b/include/net/protocol.h @@ -108,6 +108,9 @@ int inet_del_offload(const struct net_offload *prot, unsigned char num); void inet_register_protosw(struct inet_protosw *p); void inet_unregister_protosw(struct inet_protosw *p); +int udp_add_offload(struct udp_offload *prot); +void udp_del_offload(struct udp_offload *prot); + #if IS_ENABLED(CONFIG_IPV6) int inet6_add_protocol(const struct inet6_protocol *prot, unsigned char num); int inet6_del_protocol(const struct inet6_protocol *prot, unsigned char num); diff --git a/net/core/dev.c b/net/core/dev.c index a578af589198..da92305c344f 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3893,6 +3893,7 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff NAPI_GRO_CB(skb)->same_flow = 0; NAPI_GRO_CB(skb)->flush = 0; NAPI_GRO_CB(skb)->free = 0; + NAPI_GRO_CB(skb)->udp_mark = 0; pp = ptype->callbacks.gro_receive(&napi->gro_list, skb); break; diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c index 79c62bdcd3c5..ee853c55deea 100644 --- a/net/ipv4/udp_offload.c +++ b/net/ipv4/udp_offload.c @@ -14,6 +14,15 @@ #include #include +static DEFINE_SPINLOCK(udp_offload_lock); +static struct udp_offload_priv *udp_offload_base __read_mostly; + +struct udp_offload_priv { + struct udp_offload *offload; + struct rcu_head rcu; + struct udp_offload_priv __rcu *next; +}; + static int udp4_ufo_send_check(struct sk_buff *skb) { if (!pskb_may_pull(skb, sizeof(struct udphdr))) @@ -89,10 +98,144 @@ out: return segs; } +int udp_add_offload(struct udp_offload *uo) +{ + struct udp_offload_priv **head = &udp_offload_base; + struct udp_offload_priv *new_offload = kzalloc(sizeof(*new_offload), GFP_KERNEL); + + if (!new_offload) + return -ENOMEM; + + new_offload->offload = uo; + + spin_lock(&udp_offload_lock); + rcu_assign_pointer(new_offload->next, rcu_dereference(*head)); + rcu_assign_pointer(*head, rcu_dereference(new_offload)); + spin_unlock(&udp_offload_lock); + + return 0; +} +EXPORT_SYMBOL(udp_add_offload); + +static void udp_offload_free_routine(struct rcu_head *head) +{ + struct udp_offload_priv *ou_priv = container_of(head, struct udp_offload_priv, rcu); + kfree(ou_priv); +} + +void udp_del_offload(struct udp_offload *uo) +{ + struct udp_offload_priv __rcu **head = &udp_offload_base; + struct udp_offload_priv *uo_priv; + + spin_lock(&udp_offload_lock); + + uo_priv = rcu_dereference(*head); + for (; uo_priv != NULL; + uo_priv = rcu_dereference(*head)) { + + if (uo_priv->offload == uo) { + rcu_assign_pointer(*head, rcu_dereference(uo_priv->next)); + goto unlock; + } + head = &uo_priv->next; + } + pr_warn("udp_del_offload: didn't find offload for port %d\n", htons(uo->port)); +unlock: + spin_unlock(&udp_offload_lock); + if (uo_priv != NULL) + call_rcu(&uo_priv->rcu, udp_offload_free_routine); +} +EXPORT_SYMBOL(udp_del_offload); + +static struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb) +{ + struct udp_offload_priv *uo_priv; + struct sk_buff *p, **pp = NULL; + struct udphdr *uh, *uh2; + unsigned int hlen, off; + int flush = 1; + + if (NAPI_GRO_CB(skb)->udp_mark || + (!skb->encapsulation && skb->ip_summed != CHECKSUM_COMPLETE)) + goto out; + + /* mark that this skb passed once through the udp gro layer */ + NAPI_GRO_CB(skb)->udp_mark = 1; + + off = skb_gro_offset(skb); + hlen = off + sizeof(*uh); + uh = skb_gro_header_fast(skb, off); + if (skb_gro_header_hard(skb, hlen)) { + uh = skb_gro_header_slow(skb, hlen, off); + if (unlikely(!uh)) + goto out; + } + + rcu_read_lock(); + uo_priv = rcu_dereference(udp_offload_base); + for (; uo_priv != NULL; uo_priv = rcu_dereference(uo_priv->next)) { + if (uo_priv->offload->port == uh->dest && + uo_priv->offload->callbacks.gro_receive) + goto unflush; + } + goto out_unlock; + +unflush: + flush = 0; + + for (p = *head; p; p = p->next) { + if (!NAPI_GRO_CB(p)->same_flow) + continue; + + uh2 = (struct udphdr *)(p->data + off); + if ((*(u32 *)&uh->source != *(u32 *)&uh2->source)) { + NAPI_GRO_CB(p)->same_flow = 0; + continue; + } + } + + skb_gro_pull(skb, sizeof(struct udphdr)); /* pull encapsulating udp header */ + pp = uo_priv->offload->callbacks.gro_receive(head, skb); + +out_unlock: + rcu_read_unlock(); +out: + NAPI_GRO_CB(skb)->flush |= flush; + return pp; +} + +static int udp_gro_complete(struct sk_buff *skb, int nhoff) +{ + struct udp_offload_priv *uo_priv; + __be16 newlen = htons(skb->len - nhoff); + struct udphdr *uh = (struct udphdr *)(skb->data + nhoff); + int err = -ENOSYS; + + uh->len = newlen; + + rcu_read_lock(); + + uo_priv = rcu_dereference(udp_offload_base); + for (; uo_priv != NULL; uo_priv = rcu_dereference(uo_priv->next)) { + if (uo_priv->offload->port == uh->dest && + uo_priv->offload->callbacks.gro_complete) + break; + } + + if (uo_priv != NULL) + err = uo_priv->offload->callbacks.gro_complete(skb, nhoff + sizeof(struct udphdr)); + + rcu_read_unlock(); + return err; +} + static const struct net_offload udpv4_offload = { .callbacks = { .gso_send_check = udp4_ufo_send_check, .gso_segment = udp4_ufo_fragment, + .gro_receive = udp_gro_receive, + .gro_complete = udp_gro_complete, }, }; -- cgit v1.2.3 From dc01e7d3447793fd9e4090aa9d50c549848b5a18 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Mon, 20 Jan 2014 13:59:21 +0200 Subject: net: Add GRO support for vxlan traffic Add GRO handlers for vxlann, by using the UDP GRO infrastructure. For single TCP session that goes through vxlan tunneling I got nice improvement from 6.8Gbs to 11.5Gbs --> UDP/VXLAN GRO disabled $ netperf -H 192.168.52.147 -c -C $ netperf -t TCP_STREAM -H 192.168.52.147 -c -C MIGRATED TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 192.168.52.147 () port 0 AF_INET Recv Send Send Utilization Service Demand Socket Socket Message Elapsed Send Recv Send Recv Size Size Size Time Throughput local remote local remote bytes bytes bytes secs. 10^6bits/s % S % S us/KB us/KB 87380 65536 65536 10.00 6799.75 12.54 24.79 0.604 1.195 --> UDP/VXLAN GRO enabled $ netperf -t TCP_STREAM -H 192.168.52.147 -c -C MIGRATED TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 192.168.52.147 () port 0 AF_INET Recv Send Send Utilization Service Demand Socket Socket Message Elapsed Send Recv Send Recv Size Size Size Time Throughput local remote local remote bytes bytes bytes secs. 10^6bits/s % S % S us/KB us/KB 87380 65536 65536 10.00 11562.72 24.90 20.34 0.706 0.577 Signed-off-by: Shlomo Pongratz Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++---- include/net/vxlan.h | 1 + 2 files changed, 111 insertions(+), 7 deletions(-) (limited to 'include/net') diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 0fa4b9108e82..942acc20d394 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -40,6 +40,7 @@ #include #include #include +#include #if IS_ENABLED(CONFIG_IPV6) #include #include @@ -554,13 +555,106 @@ static int vxlan_fdb_append(struct vxlan_fdb *f, return 1; } +static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, struct sk_buff *skb) +{ + struct sk_buff *p, **pp = NULL; + struct vxlanhdr *vh, *vh2; + struct ethhdr *eh, *eh2; + unsigned int hlen, off_vx, off_eth; + const struct packet_offload *ptype; + __be16 type; + int flush = 1; + + off_vx = skb_gro_offset(skb); + hlen = off_vx + sizeof(*vh); + vh = skb_gro_header_fast(skb, off_vx); + if (skb_gro_header_hard(skb, hlen)) { + vh = skb_gro_header_slow(skb, hlen, off_vx); + if (unlikely(!vh)) + goto out; + } + skb_gro_pull(skb, sizeof(struct vxlanhdr)); /* pull vxlan header */ + + off_eth = skb_gro_offset(skb); + hlen = off_eth + sizeof(*eh); + eh = skb_gro_header_fast(skb, off_eth); + if (skb_gro_header_hard(skb, hlen)) { + eh = skb_gro_header_slow(skb, hlen, off_eth); + if (unlikely(!eh)) + goto out; + } + + flush = 0; + + for (p = *head; p; p = p->next) { + if (!NAPI_GRO_CB(p)->same_flow) + continue; + + vh2 = (struct vxlanhdr *)(p->data + off_vx); + eh2 = (struct ethhdr *)(p->data + off_eth); + if (vh->vx_vni != vh2->vx_vni || compare_ether_header(eh, eh2)) { + NAPI_GRO_CB(p)->same_flow = 0; + continue; + } + goto found; + } + +found: + type = eh->h_proto; + + rcu_read_lock(); + ptype = gro_find_receive_by_type(type); + if (ptype == NULL) { + flush = 1; + goto out_unlock; + } + + skb_gro_pull(skb, sizeof(*eh)); /* pull inner eth header */ + pp = ptype->callbacks.gro_receive(head, skb); + +out_unlock: + rcu_read_unlock(); +out: + NAPI_GRO_CB(skb)->flush |= flush; + + return pp; +} + +static int vxlan_gro_complete(struct sk_buff *skb, int nhoff) +{ + struct ethhdr *eh; + struct packet_offload *ptype; + __be16 type; + int vxlan_len = sizeof(struct vxlanhdr) + sizeof(struct ethhdr); + int err = -ENOSYS; + + eh = (struct ethhdr *)(skb->data + nhoff + sizeof(struct vxlanhdr)); + type = eh->h_proto; + + rcu_read_lock(); + ptype = gro_find_complete_by_type(type); + if (ptype != NULL) + err = ptype->callbacks.gro_complete(skb, nhoff + vxlan_len); + + rcu_read_unlock(); + return err; +} + /* Notify netdevs that UDP port started listening */ -static void vxlan_notify_add_rx_port(struct sock *sk) +static void vxlan_notify_add_rx_port(struct vxlan_sock *vs) { struct net_device *dev; + struct sock *sk = vs->sock->sk; struct net *net = sock_net(sk); sa_family_t sa_family = sk->sk_family; __be16 port = inet_sk(sk)->inet_sport; + int err; + + if (sa_family == AF_INET) { + err = udp_add_offload(&vs->udp_offloads); + if (err) + pr_warn("vxlan: udp_add_offload failed with status %d\n", err); + } rcu_read_lock(); for_each_netdev_rcu(net, dev) { @@ -572,9 +666,10 @@ static void vxlan_notify_add_rx_port(struct sock *sk) } /* Notify netdevs that UDP port is no more listening */ -static void vxlan_notify_del_rx_port(struct sock *sk) +static void vxlan_notify_del_rx_port(struct vxlan_sock *vs) { struct net_device *dev; + struct sock *sk = vs->sock->sk; struct net *net = sock_net(sk); sa_family_t sa_family = sk->sk_family; __be16 port = inet_sk(sk)->inet_sport; @@ -586,6 +681,9 @@ static void vxlan_notify_del_rx_port(struct sock *sk) port); } rcu_read_unlock(); + + if (sa_family == AF_INET) + udp_del_offload(&vs->udp_offloads); } /* Add new entry to forwarding table -- assumes lock held */ @@ -963,7 +1061,7 @@ void vxlan_sock_release(struct vxlan_sock *vs) spin_lock(&vn->sock_lock); hlist_del_rcu(&vs->hlist); rcu_assign_sk_user_data(vs->sock->sk, NULL); - vxlan_notify_del_rx_port(sk); + vxlan_notify_del_rx_port(vs); spin_unlock(&vn->sock_lock); queue_work(vxlan_wq, &vs->del_work); @@ -1124,8 +1222,8 @@ static void vxlan_rcv(struct vxlan_sock *vs, * leave the CHECKSUM_UNNECESSARY, the device checksummed it * for us. Otherwise force the upper layers to verify it. */ - if (skb->ip_summed != CHECKSUM_UNNECESSARY || !skb->encapsulation || - !(vxlan->dev->features & NETIF_F_RXCSUM)) + if ((skb->ip_summed != CHECKSUM_UNNECESSARY && skb->ip_summed != CHECKSUM_PARTIAL) || + !skb->encapsulation || !(vxlan->dev->features & NETIF_F_RXCSUM)) skb->ip_summed = CHECKSUM_NONE; skb->encapsulation = 0; @@ -2303,7 +2401,7 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port, struct sock *sk; unsigned int h; - vs = kmalloc(sizeof(*vs), GFP_KERNEL); + vs = kzalloc(sizeof(*vs), GFP_KERNEL); if (!vs) return ERR_PTR(-ENOMEM); @@ -2328,9 +2426,14 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port, vs->data = data; rcu_assign_sk_user_data(vs->sock->sk, vs); + /* Initialize the vxlan udp offloads structure */ + vs->udp_offloads.port = port; + vs->udp_offloads.callbacks.gro_receive = vxlan_gro_receive; + vs->udp_offloads.callbacks.gro_complete = vxlan_gro_complete; + spin_lock(&vn->sock_lock); hlist_add_head_rcu(&vs->hlist, vs_head(net, port)); - vxlan_notify_add_rx_port(sk); + vxlan_notify_add_rx_port(vs); spin_unlock(&vn->sock_lock); /* Mark socket as an encapsulation socket. */ diff --git a/include/net/vxlan.h b/include/net/vxlan.h index 6b6d180fb91a..5deef1ae78c9 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -21,6 +21,7 @@ struct vxlan_sock { struct rcu_head rcu; struct hlist_head vni_list[VNI_HASH_SIZE]; atomic_t refcnt; + struct udp_offload udp_offloads; }; struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port, -- cgit v1.2.3 From 940287ee10e270b78fbda6c44eaa1def5fde897d Mon Sep 17 00:00:00 2001 From: wangweidong Date: Tue, 21 Jan 2014 15:44:06 +0800 Subject: sctp: remove macros sctp_spin_[un]lock_irqrestore Redefined spin_[un]lock_irqstore to sctp_spin_[un]lock_irqrestore for user space friendly code which we haven't use in years, so removing them. Signed-off-by: Wang Weidong Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'include/net') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 610a8c8738fa..b180a8fb68c5 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -171,9 +171,6 @@ extern struct kmem_cache *sctp_bucket_cachep __read_mostly; */ /* spin lock wrappers. */ -#define sctp_spin_lock_irqsave(lock, flags) spin_lock_irqsave(lock, flags) -#define sctp_spin_unlock_irqrestore(lock, flags) \ - spin_unlock_irqrestore(lock, flags) #define sctp_local_bh_disable() local_bh_disable() #define sctp_local_bh_enable() local_bh_enable() #define sctp_spin_lock(lock) spin_lock(lock) @@ -353,13 +350,13 @@ static inline void sctp_skb_list_tail(struct sk_buff_head *list, { unsigned long flags; - sctp_spin_lock_irqsave(&head->lock, flags); + spin_lock_irqsave(&head->lock, flags); sctp_spin_lock(&list->lock); skb_queue_splice_tail_init(list, head); sctp_spin_unlock(&list->lock); - sctp_spin_unlock_irqrestore(&head->lock, flags); + spin_unlock_irqrestore(&head->lock, flags); } /** -- cgit v1.2.3 From 79b91130a2679ea5063d49c021d97346a09eb0a5 Mon Sep 17 00:00:00 2001 From: wangweidong Date: Tue, 21 Jan 2014 15:44:07 +0800 Subject: sctp: remove macros sctp_local_bh_{disable|enable} Redefined local_bh_{disable|enable} to sctp_local_bh_{disable|enable} for user space friendly code which we haven't use in years, so removing them. Signed-off-by: Wang Weidong Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 2 -- net/sctp/endpointola.c | 4 ++-- net/sctp/input.c | 20 ++++++++++---------- net/sctp/proc.c | 12 ++++++------ net/sctp/socket.c | 16 ++++++++-------- 5 files changed, 26 insertions(+), 28 deletions(-) (limited to 'include/net') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index b180a8fb68c5..c17121afe182 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -171,8 +171,6 @@ extern struct kmem_cache *sctp_bucket_cachep __read_mostly; */ /* spin lock wrappers. */ -#define sctp_local_bh_disable() local_bh_disable() -#define sctp_local_bh_enable() local_bh_enable() #define sctp_spin_lock(lock) spin_lock(lock) #define sctp_spin_unlock(lock) spin_unlock(lock) #define sctp_write_lock(lock) write_lock(lock) diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index 6ffb6c1b13b7..8e5fdea05216 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c @@ -368,9 +368,9 @@ struct sctp_association *sctp_endpoint_lookup_assoc( { struct sctp_association *asoc; - sctp_local_bh_disable(); + local_bh_disable(); asoc = __sctp_endpoint_lookup_assoc(ep, paddr, transport); - sctp_local_bh_enable(); + local_bh_enable(); return asoc; } diff --git a/net/sctp/input.c b/net/sctp/input.c index 1f4eeb43fbd6..e22b326b3540 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -726,9 +726,9 @@ static void __sctp_hash_endpoint(struct sctp_endpoint *ep) /* Add an endpoint to the hash. Local BH-safe. */ void sctp_hash_endpoint(struct sctp_endpoint *ep) { - sctp_local_bh_disable(); + local_bh_disable(); __sctp_hash_endpoint(ep); - sctp_local_bh_enable(); + local_bh_enable(); } /* Remove endpoint from the hash table. */ @@ -752,9 +752,9 @@ static void __sctp_unhash_endpoint(struct sctp_endpoint *ep) /* Remove endpoint from the hash. Local BH-safe. */ void sctp_unhash_endpoint(struct sctp_endpoint *ep) { - sctp_local_bh_disable(); + local_bh_disable(); __sctp_unhash_endpoint(ep); - sctp_local_bh_enable(); + local_bh_enable(); } /* Look up an endpoint. */ @@ -809,9 +809,9 @@ void sctp_hash_established(struct sctp_association *asoc) if (asoc->temp) return; - sctp_local_bh_disable(); + local_bh_disable(); __sctp_hash_established(asoc); - sctp_local_bh_enable(); + local_bh_enable(); } /* Remove association from the hash table. */ @@ -839,9 +839,9 @@ void sctp_unhash_established(struct sctp_association *asoc) if (asoc->temp) return; - sctp_local_bh_disable(); + local_bh_disable(); __sctp_unhash_established(asoc); - sctp_local_bh_enable(); + local_bh_enable(); } /* Look up an association. */ @@ -891,9 +891,9 @@ struct sctp_association *sctp_lookup_association(struct net *net, { struct sctp_association *asoc; - sctp_local_bh_disable(); + local_bh_disable(); asoc = __sctp_lookup_association(net, laddr, paddr, transportp); - sctp_local_bh_enable(); + local_bh_enable(); return asoc; } diff --git a/net/sctp/proc.c b/net/sctp/proc.c index 63ba0bdc867a..0947f1e15eb8 100644 --- a/net/sctp/proc.c +++ b/net/sctp/proc.c @@ -218,7 +218,7 @@ static int sctp_eps_seq_show(struct seq_file *seq, void *v) return -ENOMEM; head = &sctp_ep_hashtable[hash]; - sctp_local_bh_disable(); + local_bh_disable(); read_lock(&head->lock); sctp_for_each_hentry(epb, &head->chain) { ep = sctp_ep(epb); @@ -235,7 +235,7 @@ static int sctp_eps_seq_show(struct seq_file *seq, void *v) seq_printf(seq, "\n"); } read_unlock(&head->lock); - sctp_local_bh_enable(); + local_bh_enable(); return 0; } @@ -326,7 +326,7 @@ static int sctp_assocs_seq_show(struct seq_file *seq, void *v) return -ENOMEM; head = &sctp_assoc_hashtable[hash]; - sctp_local_bh_disable(); + local_bh_disable(); read_lock(&head->lock); sctp_for_each_hentry(epb, &head->chain) { assoc = sctp_assoc(epb); @@ -362,7 +362,7 @@ static int sctp_assocs_seq_show(struct seq_file *seq, void *v) seq_printf(seq, "\n"); } read_unlock(&head->lock); - sctp_local_bh_enable(); + local_bh_enable(); return 0; } @@ -446,7 +446,7 @@ static int sctp_remaddr_seq_show(struct seq_file *seq, void *v) return -ENOMEM; head = &sctp_assoc_hashtable[hash]; - sctp_local_bh_disable(); + local_bh_disable(); read_lock(&head->lock); rcu_read_lock(); sctp_for_each_hentry(epb, &head->chain) { @@ -505,7 +505,7 @@ static int sctp_remaddr_seq_show(struct seq_file *seq, void *v) rcu_read_unlock(); read_unlock(&head->lock); - sctp_local_bh_enable(); + local_bh_enable(); return 0; diff --git a/net/sctp/socket.c b/net/sctp/socket.c index fd7337ad4c24..f2e00059efdb 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1510,7 +1510,7 @@ static void sctp_close(struct sock *sk, long timeout) /* Supposedly, no process has access to the socket, but * the net layers still may. */ - sctp_local_bh_disable(); + local_bh_disable(); sctp_bh_lock_sock(sk); /* Hold the sock, since sk_common_release() will put sock_put() @@ -1520,7 +1520,7 @@ static void sctp_close(struct sock *sk, long timeout) sk_common_release(sk); sctp_bh_unlock_sock(sk); - sctp_local_bh_enable(); + local_bh_enable(); sock_put(sk); @@ -5918,7 +5918,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) pr_debug("%s: begins, snum:%d\n", __func__, snum); - sctp_local_bh_disable(); + local_bh_disable(); if (snum == 0) { /* Search for an available port. */ @@ -6052,7 +6052,7 @@ fail_unlock: sctp_spin_unlock(&head->lock); fail: - sctp_local_bh_enable(); + local_bh_enable(); return ret; } @@ -6297,9 +6297,9 @@ static inline void __sctp_put_port(struct sock *sk) void sctp_put_port(struct sock *sk) { - sctp_local_bh_disable(); + local_bh_disable(); __sctp_put_port(sk); - sctp_local_bh_enable(); + local_bh_enable(); } /* @@ -6950,14 +6950,14 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, /* Hook this new socket in to the bind_hash list. */ head = &sctp_port_hashtable[sctp_phashfn(sock_net(oldsk), inet_sk(oldsk)->inet_num)]; - sctp_local_bh_disable(); + local_bh_disable(); sctp_spin_lock(&head->lock); pp = sctp_sk(oldsk)->bind_hash; sk_add_bind_node(newsk, &pp->owner); sctp_sk(newsk)->bind_hash = pp; inet_sk(newsk)->inet_num = inet_sk(oldsk)->inet_num; sctp_spin_unlock(&head->lock); - sctp_local_bh_enable(); + local_bh_enable(); /* Copy the bind_addr list from the original endpoint to the new * endpoint so that we can handle restarts properly -- cgit v1.2.3 From 3c8e43ba9fe93b2670decc119f82f072a8d2459f Mon Sep 17 00:00:00 2001 From: wangweidong Date: Tue, 21 Jan 2014 15:44:08 +0800 Subject: sctp: remove macros sctp_spin_[un]lock Redefined spin_[un]lock to sctp_spin_[un]lock for user space friendly code which we haven't use in years, so removing them. Signed-off-by: Wang Weidong Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 6 ++---- net/sctp/socket.c | 16 ++++++++-------- 2 files changed, 10 insertions(+), 12 deletions(-) (limited to 'include/net') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index c17121afe182..4f77e9811f9e 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -171,8 +171,6 @@ extern struct kmem_cache *sctp_bucket_cachep __read_mostly; */ /* spin lock wrappers. */ -#define sctp_spin_lock(lock) spin_lock(lock) -#define sctp_spin_unlock(lock) spin_unlock(lock) #define sctp_write_lock(lock) write_lock(lock) #define sctp_write_unlock(lock) write_unlock(lock) #define sctp_read_lock(lock) read_lock(lock) @@ -349,11 +347,11 @@ static inline void sctp_skb_list_tail(struct sk_buff_head *list, unsigned long flags; spin_lock_irqsave(&head->lock, flags); - sctp_spin_lock(&list->lock); + spin_lock(&list->lock); skb_queue_splice_tail_init(list, head); - sctp_spin_unlock(&list->lock); + spin_unlock(&list->lock); spin_unlock_irqrestore(&head->lock, flags); } diff --git a/net/sctp/socket.c b/net/sctp/socket.c index f2e00059efdb..98532cbd842e 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -5937,14 +5937,14 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) continue; index = sctp_phashfn(sock_net(sk), rover); head = &sctp_port_hashtable[index]; - sctp_spin_lock(&head->lock); + spin_lock(&head->lock); sctp_for_each_hentry(pp, &head->chain) if ((pp->port == rover) && net_eq(sock_net(sk), pp->net)) goto next; break; next: - sctp_spin_unlock(&head->lock); + spin_unlock(&head->lock); } while (--remaining > 0); /* Exhausted local port range during search? */ @@ -5965,7 +5965,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) * port iterator, pp being NULL. */ head = &sctp_port_hashtable[sctp_phashfn(sock_net(sk), snum)]; - sctp_spin_lock(&head->lock); + spin_lock(&head->lock); sctp_for_each_hentry(pp, &head->chain) { if ((pp->port == snum) && net_eq(pp->net, sock_net(sk))) goto pp_found; @@ -6049,7 +6049,7 @@ success: ret = 0; fail_unlock: - sctp_spin_unlock(&head->lock); + spin_unlock(&head->lock); fail: local_bh_enable(); @@ -6286,13 +6286,13 @@ static inline void __sctp_put_port(struct sock *sk) inet_sk(sk)->inet_num)]; struct sctp_bind_bucket *pp; - sctp_spin_lock(&head->lock); + spin_lock(&head->lock); pp = sctp_sk(sk)->bind_hash; __sk_del_bind_node(sk); sctp_sk(sk)->bind_hash = NULL; inet_sk(sk)->inet_num = 0; sctp_bucket_destroy(pp); - sctp_spin_unlock(&head->lock); + spin_unlock(&head->lock); } void sctp_put_port(struct sock *sk) @@ -6951,12 +6951,12 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, head = &sctp_port_hashtable[sctp_phashfn(sock_net(oldsk), inet_sk(oldsk)->inet_num)]; local_bh_disable(); - sctp_spin_lock(&head->lock); + spin_lock(&head->lock); pp = sctp_sk(oldsk)->bind_hash; sk_add_bind_node(newsk, &pp->owner); sctp_sk(newsk)->bind_hash = pp; inet_sk(newsk)->inet_num = inet_sk(oldsk)->inet_num; - sctp_spin_unlock(&head->lock); + spin_unlock(&head->lock); local_bh_enable(); /* Copy the bind_addr list from the original endpoint to the new -- cgit v1.2.3 From 387602dfdc3332f786f6195c74d53f9e8f7af402 Mon Sep 17 00:00:00 2001 From: wangweidong Date: Tue, 21 Jan 2014 15:44:09 +0800 Subject: sctp: remove macros sctp_write_[un]_lock Redefined write_[un]lock to sctp_write_[un]lock for user space friendly code which we haven't use in years, so removing them. Signed-off-by: Wang Weidong Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 2 -- net/sctp/input.c | 16 ++++++++-------- 2 files changed, 8 insertions(+), 10 deletions(-) (limited to 'include/net') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 4f77e9811f9e..73dcc0c82749 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -171,8 +171,6 @@ extern struct kmem_cache *sctp_bucket_cachep __read_mostly; */ /* spin lock wrappers. */ -#define sctp_write_lock(lock) write_lock(lock) -#define sctp_write_unlock(lock) write_unlock(lock) #define sctp_read_lock(lock) read_lock(lock) #define sctp_read_unlock(lock) read_unlock(lock) diff --git a/net/sctp/input.c b/net/sctp/input.c index e22b326b3540..94f7f44049a6 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -718,9 +718,9 @@ static void __sctp_hash_endpoint(struct sctp_endpoint *ep) epb->hashent = sctp_ep_hashfn(net, epb->bind_addr.port); head = &sctp_ep_hashtable[epb->hashent]; - sctp_write_lock(&head->lock); + write_lock(&head->lock); hlist_add_head(&epb->node, &head->chain); - sctp_write_unlock(&head->lock); + write_unlock(&head->lock); } /* Add an endpoint to the hash. Local BH-safe. */ @@ -744,9 +744,9 @@ static void __sctp_unhash_endpoint(struct sctp_endpoint *ep) head = &sctp_ep_hashtable[epb->hashent]; - sctp_write_lock(&head->lock); + write_lock(&head->lock); hlist_del_init(&epb->node); - sctp_write_unlock(&head->lock); + write_unlock(&head->lock); } /* Remove endpoint from the hash. Local BH-safe. */ @@ -798,9 +798,9 @@ static void __sctp_hash_established(struct sctp_association *asoc) head = &sctp_assoc_hashtable[epb->hashent]; - sctp_write_lock(&head->lock); + write_lock(&head->lock); hlist_add_head(&epb->node, &head->chain); - sctp_write_unlock(&head->lock); + write_unlock(&head->lock); } /* Add an association to the hash. Local BH-safe. */ @@ -828,9 +828,9 @@ static void __sctp_unhash_established(struct sctp_association *asoc) head = &sctp_assoc_hashtable[epb->hashent]; - sctp_write_lock(&head->lock); + write_lock(&head->lock); hlist_del_init(&epb->node); - sctp_write_unlock(&head->lock); + write_unlock(&head->lock); } /* Remove association from the hash table. Local BH-safe. */ -- cgit v1.2.3 From 1b0de194f11450d1c1e164bf2826fe80a38e1f62 Mon Sep 17 00:00:00 2001 From: wangweidong Date: Tue, 21 Jan 2014 15:44:10 +0800 Subject: sctp: remove macros sctp_read_[un]lock Redefined read_[un]lock to sctp_read_[un]lock for user space friendly code which we haven't use in years, and the macros we never used, so removing them. Signed-off-by: Wang Weidong Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'include/net') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 73dcc0c82749..572cd5a5292a 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -170,10 +170,6 @@ extern struct kmem_cache *sctp_bucket_cachep __read_mostly; * Section: Macros, externs, and inlines */ -/* spin lock wrappers. */ -#define sctp_read_lock(lock) read_lock(lock) -#define sctp_read_unlock(lock) read_unlock(lock) - /* sock lock wrappers. */ #define sctp_lock_sock(sk) lock_sock(sk) #define sctp_release_sock(sk) release_sock(sk) -- cgit v1.2.3 From 048ed4b6266144fdee55089c9eef55b0c1d42ba1 Mon Sep 17 00:00:00 2001 From: wangweidong Date: Tue, 21 Jan 2014 15:44:11 +0800 Subject: sctp: remove macros sctp_{lock|release}_sock Redefined {lock|release}_sock to sctp_{lock|release}_sock for user space friendly code which we haven't use in years, so removing them. Signed-off-by: Wang Weidong Signed-off-by: David S. Miller --- fs/dlm/lowcomms.c | 4 ++-- include/net/sctp/sctp.h | 2 -- net/sctp/socket.c | 62 ++++++++++++++++++++++++------------------------- 3 files changed, 33 insertions(+), 35 deletions(-) (limited to 'include/net') diff --git a/fs/dlm/lowcomms.c b/fs/dlm/lowcomms.c index d90909ec6aa6..ce53dffd2366 100644 --- a/fs/dlm/lowcomms.c +++ b/fs/dlm/lowcomms.c @@ -713,11 +713,11 @@ static void process_sctp_notification(struct connection *con, return; /* Peel off a new sock */ - sctp_lock_sock(con->sock->sk); + lock_sock(con->sock->sk); ret = sctp_do_peeloff(con->sock->sk, sn->sn_assoc_change.sac_assoc_id, &new_con->sock); - sctp_release_sock(con->sock->sk); + release_sock(con->sock->sk); if (ret < 0) { log_print("Can't peel off a socket for " "connection %d to node %d: err=%d", diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 572cd5a5292a..ec18d306bad7 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -171,8 +171,6 @@ extern struct kmem_cache *sctp_bucket_cachep __read_mostly; */ /* sock lock wrappers. */ -#define sctp_lock_sock(sk) lock_sock(sk) -#define sctp_release_sock(sk) release_sock(sk) #define sctp_bh_lock_sock(sk) bh_lock_sock(sk) #define sctp_bh_unlock_sock(sk) bh_unlock_sock(sk) diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 98532cbd842e..893aa56c91cc 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -272,7 +272,7 @@ static int sctp_bind(struct sock *sk, struct sockaddr *addr, int addr_len) { int retval = 0; - sctp_lock_sock(sk); + lock_sock(sk); pr_debug("%s: sk:%p, addr:%p, addr_len:%d\n", __func__, sk, addr, addr_len); @@ -284,7 +284,7 @@ static int sctp_bind(struct sock *sk, struct sockaddr *addr, int addr_len) else retval = -EINVAL; - sctp_release_sock(sk); + release_sock(sk); return retval; } @@ -1461,7 +1461,7 @@ static void sctp_close(struct sock *sk, long timeout) pr_debug("%s: sk:%p, timeout:%ld\n", __func__, sk, timeout); - sctp_lock_sock(sk); + lock_sock(sk); sk->sk_shutdown = SHUTDOWN_MASK; sk->sk_state = SCTP_SS_CLOSING; @@ -1505,7 +1505,7 @@ static void sctp_close(struct sock *sk, long timeout) sctp_wait_for_close(sk, timeout); /* This will run the backlog queue. */ - sctp_release_sock(sk); + release_sock(sk); /* Supposedly, no process has access to the socket, but * the net layers still may. @@ -1665,7 +1665,7 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, pr_debug("%s: about to look up association\n", __func__); - sctp_lock_sock(sk); + lock_sock(sk); /* If a msg_name has been specified, assume this is to be used. */ if (msg_name) { @@ -1949,7 +1949,7 @@ out_free: sctp_association_free(asoc); } out_unlock: - sctp_release_sock(sk); + release_sock(sk); out_nounlock: return sctp_error(sk, msg_flags, err); @@ -2035,7 +2035,7 @@ static int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, "addr_len:%p)\n", __func__, sk, msg, len, noblock, flags, addr_len); - sctp_lock_sock(sk); + lock_sock(sk); if (sctp_style(sk, TCP) && !sctp_sstate(sk, ESTABLISHED)) { err = -ENOTCONN; @@ -2119,7 +2119,7 @@ out_free: sctp_ulpevent_free(event); } out: - sctp_release_sock(sk); + release_sock(sk); return err; } @@ -3590,7 +3590,7 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname, goto out_nounlock; } - sctp_lock_sock(sk); + lock_sock(sk); switch (optname) { case SCTP_SOCKOPT_BINDX_ADD: @@ -3708,7 +3708,7 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname, break; } - sctp_release_sock(sk); + release_sock(sk); out_nounlock: return retval; @@ -3736,7 +3736,7 @@ static int sctp_connect(struct sock *sk, struct sockaddr *addr, int err = 0; struct sctp_af *af; - sctp_lock_sock(sk); + lock_sock(sk); pr_debug("%s: sk:%p, sockaddr:%p, addr_len:%d\n", __func__, sk, addr, addr_len); @@ -3752,7 +3752,7 @@ static int sctp_connect(struct sock *sk, struct sockaddr *addr, err = __sctp_connect(sk, addr, af->sockaddr_len, NULL); } - sctp_release_sock(sk); + release_sock(sk); return err; } @@ -3778,7 +3778,7 @@ static struct sock *sctp_accept(struct sock *sk, int flags, int *err) long timeo; int error = 0; - sctp_lock_sock(sk); + lock_sock(sk); sp = sctp_sk(sk); ep = sp->ep; @@ -3816,7 +3816,7 @@ static struct sock *sctp_accept(struct sock *sk, int flags, int *err) sctp_sock_migrate(sk, newsk, asoc, SCTP_SOCKET_TCP); out: - sctp_release_sock(sk); + release_sock(sk); *err = error; return newsk; } @@ -3826,7 +3826,7 @@ static int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg) { int rc = -ENOTCONN; - sctp_lock_sock(sk); + lock_sock(sk); /* * SEQPACKET-style sockets in LISTENING state are valid, for @@ -3856,7 +3856,7 @@ static int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg) break; } out: - sctp_release_sock(sk); + release_sock(sk); return rc; } @@ -5754,7 +5754,7 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname, if (get_user(len, optlen)) return -EFAULT; - sctp_lock_sock(sk); + lock_sock(sk); switch (optname) { case SCTP_STATUS: @@ -5878,7 +5878,7 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname, break; } - sctp_release_sock(sk); + release_sock(sk); return retval; } @@ -6144,7 +6144,7 @@ int sctp_inet_listen(struct socket *sock, int backlog) if (unlikely(backlog < 0)) return err; - sctp_lock_sock(sk); + lock_sock(sk); /* Peeled-off sockets are not allowed to listen(). */ if (sctp_style(sk, UDP_HIGH_BANDWIDTH)) @@ -6177,7 +6177,7 @@ int sctp_inet_listen(struct socket *sock, int backlog) err = 0; out: - sctp_release_sock(sk); + release_sock(sk); return err; } @@ -6474,9 +6474,9 @@ static int sctp_wait_for_packet(struct sock *sk, int *err, long *timeo_p) * does not fit in the user's buffer, but this seems to be the * only way to honor MSG_DONTWAIT realistically. */ - sctp_release_sock(sk); + release_sock(sk); *timeo_p = schedule_timeout(*timeo_p); - sctp_lock_sock(sk); + lock_sock(sk); ready: finish_wait(sk_sleep(sk), &wait); @@ -6659,10 +6659,10 @@ static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, /* Let another process have a go. Since we are going * to sleep anyway. */ - sctp_release_sock(sk); + release_sock(sk); current_timeo = schedule_timeout(current_timeo); BUG_ON(sk != asoc->base.sk); - sctp_lock_sock(sk); + lock_sock(sk); *timeo_p = current_timeo; } @@ -6767,9 +6767,9 @@ static int sctp_wait_for_connect(struct sctp_association *asoc, long *timeo_p) /* Let another process have a go. Since we are going * to sleep anyway. */ - sctp_release_sock(sk); + release_sock(sk); current_timeo = schedule_timeout(current_timeo); - sctp_lock_sock(sk); + lock_sock(sk); *timeo_p = current_timeo; } @@ -6812,9 +6812,9 @@ static int sctp_wait_for_accept(struct sock *sk, long timeo) TASK_INTERRUPTIBLE); if (list_empty(&ep->asocs)) { - sctp_release_sock(sk); + release_sock(sk); timeo = schedule_timeout(timeo); - sctp_lock_sock(sk); + lock_sock(sk); } err = -EINVAL; @@ -6847,9 +6847,9 @@ static void sctp_wait_for_close(struct sock *sk, long timeout) prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); if (list_empty(&sctp_sk(sk)->ep->asocs)) break; - sctp_release_sock(sk); + release_sock(sk); timeout = schedule_timeout(timeout); - sctp_lock_sock(sk); + lock_sock(sk); } while (!signal_pending(current) && timeout); finish_wait(sk_sleep(sk), &wait); @@ -7046,7 +7046,7 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, newsk->sk_shutdown |= RCV_SHUTDOWN; newsk->sk_state = SCTP_SS_ESTABLISHED; - sctp_release_sock(newsk); + release_sock(newsk); } -- cgit v1.2.3 From 5bc1d1b4a261a865cbde65b1561748df5b9c724b Mon Sep 17 00:00:00 2001 From: wangweidong Date: Tue, 21 Jan 2014 15:44:12 +0800 Subject: sctp: remove macros sctp_bh_[un]lock_sock Redefined bh_[un]lock_sock to sctp_bh[un]lock_sock for user space friendly code which we haven't use in years, so removing them. Signed-off-by: Wang Weidong Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 4 ---- net/sctp/input.c | 18 +++++++++--------- net/sctp/protocol.c | 4 ++-- net/sctp/sm_sideeffect.c | 16 ++++++++-------- net/sctp/socket.c | 4 ++-- 5 files changed, 21 insertions(+), 25 deletions(-) (limited to 'include/net') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index ec18d306bad7..a3353f45ef94 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -170,10 +170,6 @@ extern struct kmem_cache *sctp_bucket_cachep __read_mostly; * Section: Macros, externs, and inlines */ -/* sock lock wrappers. */ -#define sctp_bh_lock_sock(sk) bh_lock_sock(sk) -#define sctp_bh_unlock_sock(sk) bh_unlock_sock(sk) - /* SCTP SNMP MIB stats handlers */ #define SCTP_INC_STATS(net, field) SNMP_INC_STATS((net)->sctp.sctp_statistics, field) #define SCTP_INC_STATS_BH(net, field) SNMP_INC_STATS_BH((net)->sctp.sctp_statistics, field) diff --git a/net/sctp/input.c b/net/sctp/input.c index 94f7f44049a6..f2e2cbd2d750 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -238,7 +238,7 @@ int sctp_rcv(struct sk_buff *skb) * bottom halves on this lock, but a user may be in the lock too, * so check if it is busy. */ - sctp_bh_lock_sock(sk); + bh_lock_sock(sk); if (sk != rcvr->sk) { /* Our cached sk is different from the rcvr->sk. This is @@ -248,14 +248,14 @@ int sctp_rcv(struct sk_buff *skb) * be doing something with the new socket. Switch our veiw * of the current sk. */ - sctp_bh_unlock_sock(sk); + bh_unlock_sock(sk); sk = rcvr->sk; - sctp_bh_lock_sock(sk); + bh_lock_sock(sk); } if (sock_owned_by_user(sk)) { if (sctp_add_backlog(sk, skb)) { - sctp_bh_unlock_sock(sk); + bh_unlock_sock(sk); sctp_chunk_free(chunk); skb = NULL; /* sctp_chunk_free already freed the skb */ goto discard_release; @@ -266,7 +266,7 @@ int sctp_rcv(struct sk_buff *skb) sctp_inq_push(&chunk->rcvr->inqueue, chunk); } - sctp_bh_unlock_sock(sk); + bh_unlock_sock(sk); /* Release the asoc/ep ref we took in the lookup calls. */ if (asoc) @@ -327,7 +327,7 @@ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb) */ sk = rcvr->sk; - sctp_bh_lock_sock(sk); + bh_lock_sock(sk); if (sock_owned_by_user(sk)) { if (sk_add_backlog(sk, skb, sk->sk_rcvbuf)) @@ -337,7 +337,7 @@ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb) } else sctp_inq_push(inqueue, chunk); - sctp_bh_unlock_sock(sk); + bh_unlock_sock(sk); /* If the chunk was backloged again, don't drop refs */ if (backloged) @@ -522,7 +522,7 @@ struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *skb, goto out; } - sctp_bh_lock_sock(sk); + bh_lock_sock(sk); /* If too many ICMPs get dropped on busy * servers this needs to be solved differently. @@ -542,7 +542,7 @@ out: /* Common cleanup code for icmp/icmpv6 error handler. */ void sctp_err_finish(struct sock *sk, struct sctp_association *asoc) { - sctp_bh_unlock_sock(sk); + bh_unlock_sock(sk); sctp_association_put(asoc); } diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index d6934dc8dcb6..4e1d0fcb028e 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -634,10 +634,10 @@ static void sctp_addr_wq_timeout_handler(unsigned long arg) /* ignore bound-specific endpoints */ if (!sctp_is_ep_boundall(sk)) continue; - sctp_bh_lock_sock(sk); + bh_lock_sock(sk); if (sctp_asconf_mgmt(sp, addrw) < 0) pr_debug("%s: sctp_asconf_mgmt failed\n", __func__); - sctp_bh_unlock_sock(sk); + bh_unlock_sock(sk); } #if IS_ENABLED(CONFIG_IPV6) free_next: diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index ded6db66fb24..bd859154000e 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -248,7 +248,7 @@ void sctp_generate_t3_rtx_event(unsigned long peer) /* Check whether a task is in the sock. */ - sctp_bh_lock_sock(asoc->base.sk); + bh_lock_sock(asoc->base.sk); if (sock_owned_by_user(asoc->base.sk)) { pr_debug("%s: sock is busy\n", __func__); @@ -275,7 +275,7 @@ void sctp_generate_t3_rtx_event(unsigned long peer) asoc->base.sk->sk_err = -error; out_unlock: - sctp_bh_unlock_sock(asoc->base.sk); + bh_unlock_sock(asoc->base.sk); sctp_transport_put(transport); } @@ -288,7 +288,7 @@ static void sctp_generate_timeout_event(struct sctp_association *asoc, struct net *net = sock_net(asoc->base.sk); int error = 0; - sctp_bh_lock_sock(asoc->base.sk); + bh_lock_sock(asoc->base.sk); if (sock_owned_by_user(asoc->base.sk)) { pr_debug("%s: sock is busy: timer %d\n", __func__, timeout_type); @@ -315,7 +315,7 @@ static void sctp_generate_timeout_event(struct sctp_association *asoc, asoc->base.sk->sk_err = -error; out_unlock: - sctp_bh_unlock_sock(asoc->base.sk); + bh_unlock_sock(asoc->base.sk); sctp_association_put(asoc); } @@ -367,7 +367,7 @@ void sctp_generate_heartbeat_event(unsigned long data) struct sctp_association *asoc = transport->asoc; struct net *net = sock_net(asoc->base.sk); - sctp_bh_lock_sock(asoc->base.sk); + bh_lock_sock(asoc->base.sk); if (sock_owned_by_user(asoc->base.sk)) { pr_debug("%s: sock is busy\n", __func__); @@ -392,7 +392,7 @@ void sctp_generate_heartbeat_event(unsigned long data) asoc->base.sk->sk_err = -error; out_unlock: - sctp_bh_unlock_sock(asoc->base.sk); + bh_unlock_sock(asoc->base.sk); sctp_transport_put(transport); } @@ -405,7 +405,7 @@ void sctp_generate_proto_unreach_event(unsigned long data) struct sctp_association *asoc = transport->asoc; struct net *net = sock_net(asoc->base.sk); - sctp_bh_lock_sock(asoc->base.sk); + bh_lock_sock(asoc->base.sk); if (sock_owned_by_user(asoc->base.sk)) { pr_debug("%s: sock is busy\n", __func__); @@ -427,7 +427,7 @@ void sctp_generate_proto_unreach_event(unsigned long data) asoc->state, asoc->ep, asoc, transport, GFP_ATOMIC); out_unlock: - sctp_bh_unlock_sock(asoc->base.sk); + bh_unlock_sock(asoc->base.sk); sctp_association_put(asoc); } diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 893aa56c91cc..9e91d6e5df63 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1511,7 +1511,7 @@ static void sctp_close(struct sock *sk, long timeout) * the net layers still may. */ local_bh_disable(); - sctp_bh_lock_sock(sk); + bh_lock_sock(sk); /* Hold the sock, since sk_common_release() will put sock_put() * and we have just a little more cleanup. @@ -1519,7 +1519,7 @@ static void sctp_close(struct sock *sk, long timeout) sock_hold(sk); sk_common_release(sk); - sctp_bh_unlock_sock(sk); + bh_unlock_sock(sk); local_bh_enable(); sock_put(sk); -- cgit v1.2.3 From 89770b0a69ee0e0e5e99c722192d535115f73778 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 22 Jan 2014 02:29:40 +0100 Subject: net: introduce reciprocal_scale helper and convert users As David Laight suggests, we shouldn't necessarily call this reciprocal_divide() when users didn't requested a reciprocal_value(); lets keep the basic idea and call it reciprocal_scale(). More background information on this topic can be found in [1]. Joint work with Hannes Frederic Sowa. [1] http://homepage.cs.uiowa.edu/~jones/bcd/divide.html Suggested-by: David Laight Cc: Jakub Zawadzki Cc: Eric Dumazet Cc: linux-kernel@vger.kernel.org Signed-off-by: Hannes Frederic Sowa Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/kernel.h | 19 +++++++++++++++++++ include/net/codel.h | 4 +--- net/packet/af_packet.c | 3 +-- 3 files changed, 21 insertions(+), 5 deletions(-) (limited to 'include/net') diff --git a/include/linux/kernel.h b/include/linux/kernel.h index ecb87544cc5d..03d8a6b0e2e8 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -193,6 +193,25 @@ extern int _cond_resched(void); (__x < 0) ? -__x : __x; \ }) +/** + * reciprocal_scale - "scale" a value into range [0, ep_ro) + * @val: value + * @ep_ro: right open interval endpoint + * + * Perform a "reciprocal multiplication" in order to "scale" a value into + * range [0, ep_ro), where the upper interval endpoint is right-open. + * This is useful, e.g. for accessing a index of an array containing + * ep_ro elements, for example. Think of it as sort of modulus, only that + * the result isn't that of modulo. ;) Note that if initial input is a + * small value, then result will return 0. + * + * Return: a result based on val in interval [0, ep_ro). + */ +static inline u32 reciprocal_scale(u32 val, u32 ep_ro) +{ + return (u32)(((u64) val * ep_ro) >> 32); +} + #if defined(CONFIG_MMU) && \ (defined(CONFIG_PROVE_LOCKING) || defined(CONFIG_DEBUG_ATOMIC_SLEEP)) void might_fault(void); diff --git a/include/net/codel.h b/include/net/codel.h index 3b04ff5f6f8d..fe0eab32ce76 100644 --- a/include/net/codel.h +++ b/include/net/codel.h @@ -46,7 +46,6 @@ #include #include #include -#include /* Controlling Queue Delay (CoDel) algorithm * ========================================= @@ -211,10 +210,9 @@ static codel_time_t codel_control_law(codel_time_t t, codel_time_t interval, u32 rec_inv_sqrt) { - return t + reciprocal_divide(interval, rec_inv_sqrt << REC_INV_SQRT_SHIFT); + return t + reciprocal_scale(interval, rec_inv_sqrt << REC_INV_SQRT_SHIFT); } - static bool codel_should_drop(const struct sk_buff *skb, struct Qdisc *sch, struct codel_vars *vars, diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index df3cbdd585c7..97346162803d 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -88,7 +88,6 @@ #include #include #include -#include #include #ifdef CONFIG_INET #include @@ -1262,7 +1261,7 @@ static unsigned int fanout_demux_hash(struct packet_fanout *f, struct sk_buff *skb, unsigned int num) { - return reciprocal_divide(skb->rxhash, num); + return reciprocal_scale(skb->rxhash, num); } static unsigned int fanout_demux_lb(struct packet_fanout *f, -- cgit v1.2.3 From 809fa972fd90ff27225294b17a027e908b2d7b7a Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Wed, 22 Jan 2014 02:29:41 +0100 Subject: reciprocal_divide: update/correction of the algorithm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Jakub Zawadzki noticed that some divisions by reciprocal_divide() were not correct [1][2], which he could also show with BPF code after divisions are transformed into reciprocal_value() for runtime invariance which can be passed to reciprocal_divide() later on; reverse in BPF dump ended up with a different, off-by-one K in some situations. This has been fixed by Eric Dumazet in commit aee636c4809fa5 ("bpf: do not use reciprocal divide"). This follow-up patch improves reciprocal_value() and reciprocal_divide() to work in all cases by using Granlund and Montgomery method, so that also future use is safe and without any non-obvious side-effects. Known problems with the old implementation were that division by 1 always returned 0 and some off-by-ones when the dividend and divisor where very large. This seemed to not be problematic with its current users, as far as we can tell. Eric Dumazet checked for the slab usage, we cannot surely say so in the case of flex_array. Still, in order to fix that, we propose an extension from the original implementation from commit 6a2d7a955d8d resp. [3][4], by using the algorithm proposed in "Division by Invariant Integers Using Multiplication" [5], Torbjörn Granlund and Peter L. Montgomery, that is, pseudocode for q = n/d where q, n, d is in u32 universe: 1) Initialization: int l = ceil(log_2 d) uword m' = floor((1<<32)*((1<>32 q = (t+((n-t)>>sh_1))>>sh_2 The assembler implementation from Agner Fog [6] also helped a lot while implementing. We have tested the implementation on x86_64, ppc64, i686, s390x; on x86_64/haswell we're still half the latency compared to normal divide. Joint work with Daniel Borkmann. [1] http://www.wireshark.org/~darkjames/reciprocal-buggy.c [2] http://www.wireshark.org/~darkjames/set-and-dump-filter-k-bug.c [3] https://gmplib.org/~tege/division-paper.pdf [4] http://homepage.cs.uiowa.edu/~jones/bcd/divide.html [5] http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.1.2556 [6] http://www.agner.org/optimize/asmlib.zip Reported-by: Jakub Zawadzki Cc: Eric Dumazet Cc: Austin S Hemmelgarn Cc: linux-kernel@vger.kernel.org Cc: Jesse Gross Cc: Jamal Hadi Salim Cc: Stephen Hemminger Cc: Matt Mackall Cc: Pekka Enberg Cc: Christoph Lameter Cc: Andy Gospodarek Cc: Veaceslav Falico Cc: Jay Vosburgh Cc: Jakub Zawadzki Signed-off-by: Daniel Borkmann Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 24 ++++++++++++++++------- drivers/net/bonding/bond_netlink.c | 4 ---- drivers/net/bonding/bond_options.c | 15 ++++++++++----- drivers/net/bonding/bond_sysfs.c | 5 ----- drivers/net/bonding/bonding.h | 3 +++ include/linux/flex_array.h | 3 ++- include/linux/reciprocal_div.h | 39 ++++++++++++++++++++------------------ include/linux/slab_def.h | 4 +++- include/net/red.h | 3 ++- lib/flex_array.c | 7 ++++++- lib/reciprocal_div.c | 24 +++++++++++++++++++---- net/sched/sch_netem.c | 6 ++++-- 12 files changed, 88 insertions(+), 49 deletions(-) (limited to 'include/net') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 3220b488dd1e..f100bd958b88 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -79,7 +79,6 @@ #include #include #include -#include #include "bonding.h" #include "bond_3ad.h" #include "bond_alb.h" @@ -3596,8 +3595,9 @@ static void bond_xmit_slave_id(struct bonding *bond, struct sk_buff *skb, int sl */ static u32 bond_rr_gen_slave_id(struct bonding *bond) { - int packets_per_slave = bond->params.packets_per_slave; u32 slave_id; + struct reciprocal_value reciprocal_packets_per_slave; + int packets_per_slave = bond->params.packets_per_slave; switch (packets_per_slave) { case 0: @@ -3607,8 +3607,10 @@ static u32 bond_rr_gen_slave_id(struct bonding *bond) slave_id = bond->rr_tx_counter; break; default: + reciprocal_packets_per_slave = + bond->params.reciprocal_packets_per_slave; slave_id = reciprocal_divide(bond->rr_tx_counter, - packets_per_slave); + reciprocal_packets_per_slave); break; } bond->rr_tx_counter++; @@ -4343,10 +4345,18 @@ static int bond_check_params(struct bond_params *params) params->resend_igmp = resend_igmp; params->min_links = min_links; params->lp_interval = lp_interval; - if (packets_per_slave > 1) - params->packets_per_slave = reciprocal_value(packets_per_slave); - else - params->packets_per_slave = packets_per_slave; + params->packets_per_slave = packets_per_slave; + if (packets_per_slave > 0) { + params->reciprocal_packets_per_slave = + reciprocal_value(packets_per_slave); + } else { + /* reciprocal_packets_per_slave is unused if + * packets_per_slave is 0 or 1, just initialize it + */ + params->reciprocal_packets_per_slave = + (struct reciprocal_value) { 0 }; + } + if (primary) { strncpy(params->primary, primary, IFNAMSIZ); params->primary[IFNAMSIZ - 1] = 0; diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c index 21c648854a8c..e8526552790c 100644 --- a/drivers/net/bonding/bond_netlink.c +++ b/drivers/net/bonding/bond_netlink.c @@ -19,7 +19,6 @@ #include #include #include -#include #include "bonding.h" int bond_get_slave(struct net_device *slave_dev, struct sk_buff *skb) @@ -452,9 +451,6 @@ static int bond_fill_info(struct sk_buff *skb, goto nla_put_failure; packets_per_slave = bond->params.packets_per_slave; - if (packets_per_slave > 1) - packets_per_slave = reciprocal_value(packets_per_slave); - if (nla_put_u32(skb, IFLA_BOND_PACKETS_PER_SLAVE, packets_per_slave)) goto nla_put_failure; diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c index 945a6668da83..85e434886f2e 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c @@ -16,7 +16,6 @@ #include #include #include -#include #include "bonding.h" int bond_option_mode_set(struct bonding *bond, int mode) @@ -671,11 +670,17 @@ int bond_option_packets_per_slave_set(struct bonding *bond, pr_warn("%s: Warning: packets_per_slave has effect only in balance-rr mode\n", bond->dev->name); - if (packets_per_slave > 1) - bond->params.packets_per_slave = + bond->params.packets_per_slave = packets_per_slave; + if (packets_per_slave > 0) { + bond->params.reciprocal_packets_per_slave = reciprocal_value(packets_per_slave); - else - bond->params.packets_per_slave = packets_per_slave; + } else { + /* reciprocal_packets_per_slave is unused if + * packets_per_slave is 0 or 1, just initialize it + */ + bond->params.reciprocal_packets_per_slave = + (struct reciprocal_value) { 0 }; + } return 0; } diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index 011f163c2c67..c083e9a66ece 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c @@ -39,7 +39,6 @@ #include #include #include -#include #include "bonding.h" @@ -1374,10 +1373,6 @@ static ssize_t bonding_show_packets_per_slave(struct device *d, { struct bonding *bond = to_bond(d); unsigned int packets_per_slave = bond->params.packets_per_slave; - - if (packets_per_slave > 1) - packets_per_slave = reciprocal_value(packets_per_slave); - return sprintf(buf, "%u\n", packets_per_slave); } diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index 8a935f8f2b3c..0a616c41dc94 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h @@ -23,6 +23,8 @@ #include #include #include +#include + #include "bond_3ad.h" #include "bond_alb.h" @@ -171,6 +173,7 @@ struct bond_params { int resend_igmp; int lp_interval; int packets_per_slave; + struct reciprocal_value reciprocal_packets_per_slave; }; struct bond_parm_tbl { diff --git a/include/linux/flex_array.h b/include/linux/flex_array.h index 6843cf193a44..b6efb0c64408 100644 --- a/include/linux/flex_array.h +++ b/include/linux/flex_array.h @@ -2,6 +2,7 @@ #define _FLEX_ARRAY_H #include +#include #include #define FLEX_ARRAY_PART_SIZE PAGE_SIZE @@ -22,7 +23,7 @@ struct flex_array { int element_size; int total_nr_elements; int elems_per_part; - u32 reciprocal_elems; + struct reciprocal_value reciprocal_elems; struct flex_array_part *parts[]; }; /* diff --git a/include/linux/reciprocal_div.h b/include/linux/reciprocal_div.h index f9c90b33285b..8c5a3fb6c6c5 100644 --- a/include/linux/reciprocal_div.h +++ b/include/linux/reciprocal_div.h @@ -4,29 +4,32 @@ #include /* - * This file describes reciprocical division. + * This algorithm is based on the paper "Division by Invariant + * Integers Using Multiplication" by Torbjörn Granlund and Peter + * L. Montgomery. * - * This optimizes the (A/B) problem, when A and B are two u32 - * and B is a known value (but not known at compile time) + * The assembler implementation from Agner Fog, which this code is + * based on, can be found here: + * http://www.agner.org/optimize/asmlib.zip * - * The math principle used is : - * Let RECIPROCAL_VALUE(B) be (((1LL << 32) + (B - 1))/ B) - * Then A / B = (u32)(((u64)(A) * (R)) >> 32) - * - * This replaces a divide by a multiply (and a shift), and - * is generally less expensive in CPU cycles. + * This optimization for A/B is helpful if the divisor B is mostly + * runtime invariant. The reciprocal of B is calculated in the + * slow-path with reciprocal_value(). The fast-path can then just use + * a much faster multiplication operation with a variable dividend A + * to calculate the division A/B. */ -/* - * Computes the reciprocal value (R) for the value B of the divisor. - * Should not be called before each reciprocal_divide(), - * or else the performance is slower than a normal divide. - */ -extern u32 reciprocal_value(u32 B); +struct reciprocal_value { + u32 m; + u8 sh1, sh2; +}; +struct reciprocal_value reciprocal_value(u32 d); -static inline u32 reciprocal_divide(u32 A, u32 R) +static inline u32 reciprocal_divide(u32 a, struct reciprocal_value R) { - return (u32)(((u64)A * R) >> 32); + u32 t = (u32)(((u64)a * R.m) >> 32); + return (t + ((a - t) >> R.sh1)) >> R.sh2; } -#endif + +#endif /* _LINUX_RECIPROCAL_DIV_H */ diff --git a/include/linux/slab_def.h b/include/linux/slab_def.h index 09bfffb08a56..96e8abae19a9 100644 --- a/include/linux/slab_def.h +++ b/include/linux/slab_def.h @@ -1,6 +1,8 @@ #ifndef _LINUX_SLAB_DEF_H #define _LINUX_SLAB_DEF_H +#include + /* * Definitions unique to the original Linux SLAB allocator. */ @@ -12,7 +14,7 @@ struct kmem_cache { unsigned int shared; unsigned int size; - u32 reciprocal_buffer_size; + struct reciprocal_value reciprocal_buffer_size; /* 2) touched by every alloc & free from the backend */ unsigned int flags; /* constant flags */ diff --git a/include/net/red.h b/include/net/red.h index 168bb2f495f2..76e0b5f922c6 100644 --- a/include/net/red.h +++ b/include/net/red.h @@ -130,7 +130,8 @@ struct red_parms { u32 qth_max; /* Max avg length threshold: Wlog scaled */ u32 Scell_max; u32 max_P; /* probability, [0 .. 1.0] 32 scaled */ - u32 max_P_reciprocal; /* reciprocal_value(max_P / qth_delta) */ + /* reciprocal_value(max_P / qth_delta) */ + struct reciprocal_value max_P_reciprocal; u32 qth_delta; /* max_th - min_th */ u32 target_min; /* min_th + 0.4*(max_th - min_th) */ u32 target_max; /* min_th + 0.6*(max_th - min_th) */ diff --git a/lib/flex_array.c b/lib/flex_array.c index 6948a6692fc4..2eed22fa507c 100644 --- a/lib/flex_array.c +++ b/lib/flex_array.c @@ -90,8 +90,8 @@ struct flex_array *flex_array_alloc(int element_size, unsigned int total, { struct flex_array *ret; int elems_per_part = 0; - int reciprocal_elems = 0; int max_size = 0; + struct reciprocal_value reciprocal_elems = { 0 }; if (element_size) { elems_per_part = FLEX_ARRAY_ELEMENTS_PER_PART(element_size); @@ -119,6 +119,11 @@ EXPORT_SYMBOL(flex_array_alloc); static int fa_element_to_part_nr(struct flex_array *fa, unsigned int element_nr) { + /* + * if element_size == 0 we don't get here, so we never touch + * the zeroed fa->reciprocal_elems, which would yield invalid + * results + */ return reciprocal_divide(element_nr, fa->reciprocal_elems); } diff --git a/lib/reciprocal_div.c b/lib/reciprocal_div.c index 75510e94f7d0..464152410c51 100644 --- a/lib/reciprocal_div.c +++ b/lib/reciprocal_div.c @@ -1,11 +1,27 @@ +#include #include #include #include -u32 reciprocal_value(u32 k) +/* + * For a description of the algorithm please have a look at + * include/linux/reciprocal_div.h + */ + +struct reciprocal_value reciprocal_value(u32 d) { - u64 val = (1LL << 32) + (k - 1); - do_div(val, k); - return (u32)val; + struct reciprocal_value R; + u64 m; + int l; + + l = fls(d - 1); + m = ((1ULL << 32) * ((1ULL << l) - d)); + do_div(m, d); + ++m; + R.m = (u32)m; + R.sh1 = min(l, 1); + R.sh2 = max(l - 1, 0); + + return R; } EXPORT_SYMBOL(reciprocal_value); diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index a2bfc371b44a..de1059af6da1 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -91,7 +91,7 @@ struct netem_sched_data { u64 rate; s32 packet_overhead; u32 cell_size; - u32 cell_size_reciprocal; + struct reciprocal_value cell_size_reciprocal; s32 cell_overhead; struct crndstate { @@ -725,9 +725,11 @@ static void get_rate(struct Qdisc *sch, const struct nlattr *attr) q->rate = r->rate; q->packet_overhead = r->packet_overhead; q->cell_size = r->cell_size; + q->cell_overhead = r->cell_overhead; if (q->cell_size) q->cell_size_reciprocal = reciprocal_value(q->cell_size); - q->cell_overhead = r->cell_overhead; + else + q->cell_size_reciprocal = (struct reciprocal_value) { 0 }; } static int get_loss_clg(struct Qdisc *sch, const struct nlattr *attr) -- cgit v1.2.3 From 7c90cc2d40cab15adc78545edba8b5996bd4cade Mon Sep 17 00:00:00 2001 From: FX Le Bail Date: Wed, 22 Jan 2014 07:42:37 +0100 Subject: ipv6: enable anycast addresses as source addresses for datagrams This change allows to consider an anycast address valid as source address when given via an IPV6_PKTINFO or IPV6_2292PKTINFO ancillary data item. So, when sending a datagram with ancillary data, the unicast and anycast addresses are handled in the same way. - Adds ipv6_chk_acast_addr_src() to check if an anycast address is link-local on given interface or is global. - Uses it in ip6_datagram_send_ctl(). Signed-off-by: Francois-Xavier Le Bail Acked-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/net/addrconf.h | 5 +++-- net/ipv6/anycast.c | 11 +++++++++++ net/ipv6/datagram.c | 4 +++- 3 files changed, 17 insertions(+), 3 deletions(-) (limited to 'include/net') diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 66c4a44d8f5c..50e39a8822b4 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -205,8 +205,9 @@ void ipv6_sock_ac_close(struct sock *sk); int ipv6_dev_ac_inc(struct net_device *dev, const struct in6_addr *addr); int __ipv6_dev_ac_dec(struct inet6_dev *idev, const struct in6_addr *addr); bool ipv6_chk_acast_addr(struct net *net, struct net_device *dev, - const struct in6_addr *addr); - + const struct in6_addr *addr); +bool ipv6_chk_acast_addr_src(struct net *net, struct net_device *dev, + const struct in6_addr *addr); /* Device notifier */ int register_inet6addr_notifier(struct notifier_block *nb); diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c index 5a80f15a9de2..210183244689 100644 --- a/net/ipv6/anycast.c +++ b/net/ipv6/anycast.c @@ -383,6 +383,17 @@ bool ipv6_chk_acast_addr(struct net *net, struct net_device *dev, return found; } +/* check if this anycast address is link-local on given interface or + * is global + */ +bool ipv6_chk_acast_addr_src(struct net *net, struct net_device *dev, + const struct in6_addr *addr) +{ + return ipv6_chk_acast_addr(net, + (ipv6_addr_type(addr) & IPV6_ADDR_LINKLOCAL ? + dev : NULL), + addr); +} #ifdef CONFIG_PROC_FS struct ac6_iter_state { diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 2f5e2f154d21..c3bf2d2e519e 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -699,7 +699,9 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk, int strict = __ipv6_addr_src_scope(addr_type) <= IPV6_ADDR_SCOPE_LINKLOCAL; if (!(inet_sk(sk)->freebind || inet_sk(sk)->transparent) && !ipv6_chk_addr(net, &src_info->ipi6_addr, - strict ? dev : NULL, 0)) + strict ? dev : NULL, 0) && + !ipv6_chk_acast_addr_src(net, dev, + &src_info->ipi6_addr)) err = -EINVAL; else fl6->saddr = src_info->ipi6_addr; -- cgit v1.2.3 From ba7d49b1f0f8e5f24294a880ed576964059af5ef Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 22 Jan 2014 09:05:55 +0100 Subject: rtnetlink: provide api for getting and setting slave info Recent patch bonding: add netlink attributes to slave link dev (1d3ee88ae0d6) Introduced yet another device specific way to access slave information over rtnetlink. There is one already there for bridge. This patch introduces generic way to do this, for getting and setting info as well by extending link_ops. Later on, this new interface will be used for bridge ports as well. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/rtnetlink.h | 14 ++++ include/uapi/linux/if_link.h | 2 + net/core/rtnetlink.c | 158 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 154 insertions(+), 20 deletions(-) (limited to 'include/net') diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index 8fb42070a2c1..661e45d38051 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h @@ -79,6 +79,20 @@ struct rtnl_link_ops { const struct net_device *dev); unsigned int (*get_num_tx_queues)(void); unsigned int (*get_num_rx_queues)(void); + + int slave_maxtype; + const struct nla_policy *slave_policy; + int (*slave_validate)(struct nlattr *tb[], + struct nlattr *data[]); + int (*slave_changelink)(struct net_device *dev, + struct net_device *slave_dev, + struct nlattr *tb[], + struct nlattr *data[]); + size_t (*get_slave_size)(const struct net_device *dev, + const struct net_device *slave_dev); + int (*fill_slave_info)(struct sk_buff *skb, + const struct net_device *dev, + const struct net_device *slave_dev); }; int __rtnl_link_register(struct rtnl_link_ops *ops); diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 1f30b85ebf9d..b8fb352b58f6 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -241,6 +241,8 @@ enum { IFLA_INFO_KIND, IFLA_INFO_DATA, IFLA_INFO_XSTATS, + IFLA_INFO_SLAVE_KIND, + IFLA_INFO_SLAVE_DATA, __IFLA_INFO_MAX, }; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index cace14962234..a56bccf6629e 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -365,6 +365,22 @@ void rtnl_link_unregister(struct rtnl_link_ops *ops) } EXPORT_SYMBOL_GPL(rtnl_link_unregister); +static size_t rtnl_link_get_slave_info_data_size(const struct net_device *dev) +{ + struct net_device *master_dev; + const struct rtnl_link_ops *ops; + + master_dev = netdev_master_upper_dev_get((struct net_device *) dev); + if (!master_dev) + return 0; + ops = master_dev->rtnl_link_ops; + if (!ops->get_slave_size) + return 0; + /* IFLA_INFO_SLAVE_DATA + nested data */ + return nla_total_size(sizeof(struct nlattr)) + + ops->get_slave_size(master_dev, dev); +} + static size_t rtnl_link_get_size(const struct net_device *dev) { const struct rtnl_link_ops *ops = dev->rtnl_link_ops; @@ -385,6 +401,8 @@ static size_t rtnl_link_get_size(const struct net_device *dev) /* IFLA_INFO_XSTATS */ size += nla_total_size(ops->get_xstats_size(dev)); + size += rtnl_link_get_slave_info_data_size(dev); + return size; } @@ -459,40 +477,101 @@ static size_t rtnl_link_get_af_size(const struct net_device *dev) return size; } -static int rtnl_link_fill(struct sk_buff *skb, const struct net_device *dev) +static bool rtnl_have_link_slave_info(const struct net_device *dev) { - const struct rtnl_link_ops *ops = dev->rtnl_link_ops; - struct nlattr *linkinfo, *data; - int err = -EMSGSIZE; + struct net_device *master_dev; - linkinfo = nla_nest_start(skb, IFLA_LINKINFO); - if (linkinfo == NULL) - goto out; + master_dev = netdev_master_upper_dev_get((struct net_device *) dev); + if (master_dev && master_dev->rtnl_link_ops && + master_dev->rtnl_link_ops->fill_slave_info) + return true; + return false; +} + +static int rtnl_link_slave_info_fill(struct sk_buff *skb, + const struct net_device *dev) +{ + struct net_device *master_dev; + const struct rtnl_link_ops *ops; + struct nlattr *slave_data; + int err; + master_dev = netdev_master_upper_dev_get((struct net_device *) dev); + if (!master_dev) + return 0; + ops = master_dev->rtnl_link_ops; + if (!ops) + return 0; + if (nla_put_string(skb, IFLA_INFO_SLAVE_KIND, ops->kind) < 0) + return -EMSGSIZE; + if (ops->fill_slave_info) { + slave_data = nla_nest_start(skb, IFLA_INFO_SLAVE_DATA); + if (!slave_data) + return -EMSGSIZE; + err = ops->fill_slave_info(skb, master_dev, dev); + if (err < 0) + goto err_cancel_slave_data; + nla_nest_end(skb, slave_data); + } + return 0; + +err_cancel_slave_data: + nla_nest_cancel(skb, slave_data); + return err; +} + +static int rtnl_link_info_fill(struct sk_buff *skb, + const struct net_device *dev) +{ + const struct rtnl_link_ops *ops = dev->rtnl_link_ops; + struct nlattr *data; + int err; + + if (!ops) + return 0; if (nla_put_string(skb, IFLA_INFO_KIND, ops->kind) < 0) - goto err_cancel_link; + return -EMSGSIZE; if (ops->fill_xstats) { err = ops->fill_xstats(skb, dev); if (err < 0) - goto err_cancel_link; + return err; } if (ops->fill_info) { data = nla_nest_start(skb, IFLA_INFO_DATA); - if (data == NULL) { - err = -EMSGSIZE; - goto err_cancel_link; - } + if (data == NULL) + return -EMSGSIZE; err = ops->fill_info(skb, dev); if (err < 0) goto err_cancel_data; nla_nest_end(skb, data); } - - nla_nest_end(skb, linkinfo); return 0; err_cancel_data: nla_nest_cancel(skb, data); + return err; +} + +static int rtnl_link_fill(struct sk_buff *skb, const struct net_device *dev) +{ + struct nlattr *linkinfo; + int err = -EMSGSIZE; + + linkinfo = nla_nest_start(skb, IFLA_LINKINFO); + if (linkinfo == NULL) + goto out; + + err = rtnl_link_info_fill(skb, dev); + if (err < 0) + goto err_cancel_link; + + err = rtnl_link_slave_info_fill(skb, dev); + if (err < 0) + goto err_cancel_link; + + nla_nest_end(skb, linkinfo); + return 0; + err_cancel_link: nla_nest_cancel(skb, linkinfo); out: @@ -1052,10 +1131,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, if (rtnl_port_fill(skb, dev)) goto nla_put_failure; - if (rtnl_bond_slave_fill(skb, dev)) - goto nla_put_failure; - - if (dev->rtnl_link_ops) { + if (dev->rtnl_link_ops || rtnl_have_link_slave_info(dev)) { if (rtnl_link_fill(skb, dev) < 0) goto nla_put_failure; } @@ -1178,6 +1254,8 @@ EXPORT_SYMBOL(ifla_policy); static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = { [IFLA_INFO_KIND] = { .type = NLA_STRING }, [IFLA_INFO_DATA] = { .type = NLA_NESTED }, + [IFLA_INFO_SLAVE_KIND] = { .type = NLA_STRING }, + [IFLA_INFO_SLAVE_DATA] = { .type = NLA_NESTED }, }; static const struct nla_policy ifla_vfinfo_policy[IFLA_VF_INFO_MAX+1] = { @@ -1765,7 +1843,9 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh) { struct net *net = sock_net(skb->sk); const struct rtnl_link_ops *ops; + const struct rtnl_link_ops *m_ops = NULL; struct net_device *dev; + struct net_device *master_dev = NULL; struct ifinfomsg *ifm; char kind[MODULE_NAME_LEN]; char ifname[IFNAMSIZ]; @@ -1795,6 +1875,12 @@ replay: dev = NULL; } + if (dev) { + master_dev = netdev_master_upper_dev_get(dev); + if (master_dev) + m_ops = master_dev->rtnl_link_ops; + } + err = validate_linkmsg(dev, tb); if (err < 0) return err; @@ -1816,7 +1902,10 @@ replay: } if (1) { - struct nlattr *attr[ops ? ops->maxtype + 1 : 0], **data = NULL; + struct nlattr *attr[ops ? ops->maxtype + 1 : 0]; + struct nlattr *slave_attr[m_ops ? m_ops->slave_maxtype + 1 : 0]; + struct nlattr **data = NULL; + struct nlattr **slave_data = NULL; struct net *dest_net; if (ops) { @@ -1835,6 +1924,24 @@ replay: } } + if (m_ops) { + if (m_ops->slave_maxtype && + linkinfo[IFLA_INFO_SLAVE_DATA]) { + err = nla_parse_nested(slave_attr, + m_ops->slave_maxtype, + linkinfo[IFLA_INFO_SLAVE_DATA], + m_ops->slave_policy); + if (err < 0) + return err; + slave_data = slave_attr; + } + if (m_ops->slave_validate) { + err = m_ops->slave_validate(tb, slave_data); + if (err < 0) + return err; + } + } + if (dev) { int modified = 0; @@ -1854,6 +1961,17 @@ replay: modified = 1; } + if (linkinfo[IFLA_INFO_SLAVE_DATA]) { + if (!m_ops || !m_ops->slave_changelink) + return -EOPNOTSUPP; + + err = m_ops->slave_changelink(master_dev, dev, + tb, slave_data); + if (err < 0) + return err; + modified = 1; + } + return do_setlink(dev, ifm, tb, ifname, modified); } -- cgit v1.2.3