From 66e01cf99e0a9d0cbff21b0288c049654d5acf3e Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Mon, 13 Jan 2014 19:43:00 +0200 Subject: mac80211: only set CSA beacon when at least one beacon must be transmitted A beacon should never have a Channel Switch Announcement information element with a count of 0, because a count of 1 means switch just before the next beacon. So, if a count of 0 was valid in a beacon, it would have been transmitted in the next channel already, which is useless. A CSA count equal to zero is only meaningful in action frames or probe_responses. Fix the ieee80211_csa_is_complete() and ieee80211_update_csa() functions accordingly. With a CSA count of 0, we won't transmit any CSA beacons, because the switch will happen before the next TBTT. To avoid extra work and potential confusion in the drivers, complete the CSA immediately, instead of waiting for the driver to call ieee80211_csa_finish(). To keep things simpler, we also switch immediately when the CSA count is 1, while in theory we should delay the switch until just before the next TBTT. Additionally, move the ieee80211_csa_finish() function to cfg.c, where it makes more sense. Tested-by: Simon Wunderlich Acked-by: Simon Wunderlich Signed-off-by: Luciano Coelho Signed-off-by: Johannes Berg --- include/net/mac80211.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index f4ab2fb4d50c..df1004be7ba5 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2750,11 +2750,13 @@ enum ieee80211_roc_type { * @channel_switch_beacon: Starts a channel switch to a new channel. * Beacons are modified to include CSA or ECSA IEs before calling this * function. The corresponding count fields in these IEs must be - * decremented, and when they reach zero the driver must call + * decremented, and when they reach 1 the driver must call * ieee80211_csa_finish(). Drivers which use ieee80211_beacon_get() * get the csa counter decremented by mac80211, but must check if it is - * zero using ieee80211_csa_is_complete() after the beacon has been + * 1 using ieee80211_csa_is_complete() after the beacon has been * transmitted and then call ieee80211_csa_finish(). + * If the CSA count starts as zero or 1, this function will not be called, + * since there won't be any time to beacon before the switch anyway. * * @join_ibss: Join an IBSS (on an IBSS interface); this is called after all * information in bss_conf is set up and the beacon can be retrieved. A @@ -3452,13 +3454,13 @@ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, * @vif: &struct ieee80211_vif pointer from the add_interface callback. * * After a channel switch announcement was scheduled and the counter in this - * announcement hit zero, this function must be called by the driver to + * announcement hits 1, this function must be called by the driver to * notify mac80211 that the channel can be changed. */ void ieee80211_csa_finish(struct ieee80211_vif *vif); /** - * ieee80211_csa_is_complete - find out if counters reached zero + * ieee80211_csa_is_complete - find out if counters reached 1 * @vif: &struct ieee80211_vif pointer from the add_interface callback. * * This function returns whether the channel switch counters reached zero. -- cgit v1.2.3-71-gd317 From 1df4a51082df6e5b0b8eb70df81885b9b4c9e6ec Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Wed, 15 Jan 2014 00:00:47 +0200 Subject: cfg80211: Allow BSS hint to be provided for connect This clarifies the expected driver behavior on the older NL80211_ATTR_MAC and NL80211_ATTR_WIPHY_FREQ attributes and adds a new set of similar attributes with _HINT postfix to enable use of a recommendation of the initial BSS to choose. This can be helpful for some drivers that can avoid an additional full scan on connection request if the information is provided to them (user space tools like wpa_supplicant already has that information available based on earlier scans). In addition, this can be used to get more expected behavior for cases where a specific BSS should be picked first based on operations like Interworking network selection or WPS. These cases were already easily addressed with drivers that leave BSS selection to user space, but there was no convenient way to do this with drivers that take care of BSS selection internally without using the NL80211_ATTR_MAC which is not really desired since it is needed for other purposes to force the association to remain with the same BSS. Signed-off-by: Jouni Malinen [add const, fix policy] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 8 ++++++++ include/uapi/linux/nl80211.h | 20 ++++++++++++++++++-- net/wireless/nl80211.c | 13 +++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index b1f84b05c67e..572005981366 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1701,8 +1701,14 @@ struct cfg80211_ibss_params { * * @channel: The channel to use or %NULL if not specified (auto-select based * on scan results) + * @channel_hint: The channel of the recommended BSS for initial connection or + * %NULL if not specified * @bssid: The AP BSSID or %NULL if not specified (auto-select based on scan * results) + * @bssid_hint: The recommended AP BSSID for initial connection to the BSS or + * %NULL if not specified. Unlike the @bssid parameter, the driver is + * allowed to ignore this @bssid_hint if it has knowledge of a better BSS + * to use. * @ssid: SSID * @ssid_len: Length of ssid in octets * @auth_type: Authentication type (algorithm) @@ -1725,7 +1731,9 @@ struct cfg80211_ibss_params { */ struct cfg80211_connect_params { struct ieee80211_channel *channel; + struct ieee80211_channel *channel_hint; u8 *bssid; + const u8 *bssid_hint; u8 *ssid; size_t ssid_len; enum nl80211_auth_type auth_type; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 91054fd660e0..e57de3318068 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -418,8 +418,18 @@ * %NL80211_ATTR_SSID attribute, and can optionally specify the association * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP, * %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT, - * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE and - * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT. + * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, + * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, %NL80211_ATTR_MAC_HINT, and + * %NL80211_ATTR_WIPHY_FREQ_HINT. + * If included, %NL80211_ATTR_MAC and %NL80211_ATTR_WIPHY_FREQ are + * restrictions on BSS selection, i.e., they effectively prevent roaming + * within the ESS. %NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT + * can be included to provide a recommendation of the initial BSS while + * allowing the driver to roam to other BSSes within the ESS and also to + * ignore this recommendation if the indicated BSS is not ideal. Only one + * set of BSSID,frequency parameters is used (i.e., either the enforcing + * %NL80211_ATTR_MAC,%NL80211_ATTR_WIPHY_FREQ or the less strict + * %NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT). * Background scan period can optionally be * specified in %NL80211_ATTR_BG_SCAN_PERIOD, * if not specified default background scan configuration @@ -1555,6 +1565,9 @@ enum nl80211_commands { * 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_MAC_HINT: MAC address recommendation as initial BSS + * @NL80211_ATTR_WIPHY_FREQ_HINT: frequency of the recommended initial BSS + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1883,6 +1896,9 @@ enum nl80211_attrs { NL80211_ATTR_QOS_MAP, + NL80211_ATTR_MAC_HINT, + NL80211_ATTR_WIPHY_FREQ_HINT, + /* 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 7a742594916e..6e7d580ec645 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_DATA] = { .type = NLA_BINARY }, [NL80211_ATTR_QOS_MAP] = { .type = NLA_BINARY, .len = IEEE80211_QOS_MAP_LEN_MAX }, + [NL80211_ATTR_MAC_HINT] = { .len = ETH_ALEN }, + [NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 }, }; /* policy for the key attributes */ @@ -6984,6 +6986,9 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_MAC]) connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); + else if (info->attrs[NL80211_ATTR_MAC_HINT]) + connect.bssid_hint = + nla_data(info->attrs[NL80211_ATTR_MAC_HINT]); connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); @@ -7008,6 +7013,14 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) if (!connect.channel || connect.channel->flags & IEEE80211_CHAN_DISABLED) return -EINVAL; + } else if (info->attrs[NL80211_ATTR_WIPHY_FREQ_HINT]) { + connect.channel_hint = + ieee80211_get_channel(wiphy, + nla_get_u32( + info->attrs[NL80211_ATTR_WIPHY_FREQ_HINT])); + if (!connect.channel_hint || + connect.channel_hint->flags & IEEE80211_CHAN_DISABLED) + return -EINVAL; } if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) { -- cgit v1.2.3-71-gd317 From b43504cf75b8b8773ee70c90bcd691282e151b9a Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Wed, 15 Jan 2014 00:01:08 +0200 Subject: cfg80211: Advertise maximum associated STAs in AP mode This allows drivers to advertise the maximum number of associated stations they support in AP mode (including P2P GO). User space applications can use this for cleaner way of handling the limit (e.g., hostapd rejecting IEEE 802.11 authentication without manual configuration of the limit) or to figure out what type of use cases can be executed with multiple devices before trying and failing. Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 7 +++++++ include/uapi/linux/nl80211.h | 9 +++++++++ net/wireless/nl80211.c | 6 ++++++ 3 files changed, 22 insertions(+) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 572005981366..117bea0210be 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2883,6 +2883,11 @@ struct wiphy_vendor_command { * @n_vendor_commands: number of vendor commands * @vendor_events: array of vendor events supported by the hardware * @n_vendor_events: number of vendor events + * + * @max_ap_assoc_sta: maximum number of associated stations supported in AP mode + * (including P2P GO) or 0 to indicate no such limit is advertised. The + * driver is allowed to advertise a theoretical limit that it can reach in + * some cases, but may not always reach. */ struct wiphy { /* assign these fields before you register the wiphy */ @@ -2998,6 +3003,8 @@ struct wiphy { const struct nl80211_vendor_cmd_info *vendor_events; int n_vendor_commands, n_vendor_events; + u16 max_ap_assoc_sta; + char priv[0] __aligned(NETDEV_ALIGN); }; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index e57de3318068..9a86c8bf6da6 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1568,6 +1568,13 @@ enum nl80211_commands { * @NL80211_ATTR_MAC_HINT: MAC address recommendation as initial BSS * @NL80211_ATTR_WIPHY_FREQ_HINT: frequency of the recommended initial BSS * + * @NL80211_ATTR_MAX_AP_ASSOC_STA: Device attribute that indicates how many + * associated stations are supported in AP mode (including P2P GO); u32. + * Since drivers may not have a fixed limit on the maximum number (e.g., + * other concurrent operations may affect this), drivers are allowed to + * advertise values that cannot always be met. In such cases, an attempt + * to add a new station entry with @NL80211_CMD_NEW_STATION may fail. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1899,6 +1906,8 @@ enum nl80211_attrs { NL80211_ATTR_MAC_HINT, NL80211_ATTR_WIPHY_FREQ_HINT, + NL80211_ATTR_MAX_AP_ASSOC_STA, + /* 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 6e7d580ec645..b2ac1410b113 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1588,6 +1588,12 @@ 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; + + if (dev->wiphy.max_ap_assoc_sta && + nla_put_u32(msg, NL80211_ATTR_MAX_AP_ASSOC_STA, + dev->wiphy.max_ap_assoc_sta)) + goto nla_put_failure; + state->split_start++; break; case 11: -- cgit v1.2.3-71-gd317 From 664834dee63c55188093bb5f295283c7693003d6 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Wed, 15 Jan 2014 00:01:44 +0200 Subject: cfg80211: Clean up connect params and channel fetching Addition of the frequency hints showed up couple of places in cfg80211 where pointers could be marked const and a shared function could be used to fetch a valid channel. Signed-off-by: Jouni Malinen [fix mwifiex] Signed-off-by: Johannes Berg --- drivers/net/wireless/mwifiex/cfg80211.c | 5 ++-- include/net/cfg80211.h | 4 ++-- net/wireless/nl80211.c | 42 ++++++++++++++++++++------------- 3 files changed, 30 insertions(+), 21 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 8bfc07cd330e..f4cf9c9d40ec 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1583,8 +1583,9 @@ static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv) * the function notifies the CFG802.11 subsystem of the new BSS connection. */ static int -mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid, - u8 *bssid, int mode, struct ieee80211_channel *channel, +mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, + const u8 *ssid, const u8 *bssid, int mode, + struct ieee80211_channel *channel, struct cfg80211_connect_params *sme, bool privacy) { struct cfg80211_ssid req_ssid; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 117bea0210be..9237b26142a1 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1732,9 +1732,9 @@ struct cfg80211_ibss_params { struct cfg80211_connect_params { struct ieee80211_channel *channel; struct ieee80211_channel *channel_hint; - u8 *bssid; + const u8 *bssid; const u8 *bssid_hint; - u8 *ssid; + const u8 *ssid; size_t ssid_len; enum nl80211_auth_type auth_type; u8 *ie; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b2ac1410b113..09b6da8ffdfe 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -857,6 +857,19 @@ static int nl80211_key_allowed(struct wireless_dev *wdev) return 0; } +static struct ieee80211_channel *nl80211_get_valid_chan(struct wiphy *wiphy, + struct nlattr *tb) +{ + struct ieee80211_channel *chan; + + if (tb == NULL) + return NULL; + chan = ieee80211_get_channel(wiphy, nla_get_u32(tb)); + if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) + return NULL; + return chan; +} + static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes) { struct nlattr *nl_modes = nla_nest_start(msg, attr); @@ -6199,9 +6212,9 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) return -EOPNOTSUPP; bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - chan = ieee80211_get_channel(&rdev->wiphy, - nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); - if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) + chan = nl80211_get_valid_chan(&rdev->wiphy, + info->attrs[NL80211_ATTR_WIPHY_FREQ]); + if (!chan) return -EINVAL; ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); @@ -6354,9 +6367,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - chan = ieee80211_get_channel(&rdev->wiphy, - nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); - if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) + chan = nl80211_get_valid_chan(&rdev->wiphy, + info->attrs[NL80211_ATTR_WIPHY_FREQ]); + if (!chan) return -EINVAL; ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); @@ -7013,19 +7026,14 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) } if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { - connect.channel = - ieee80211_get_channel(wiphy, - nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); - if (!connect.channel || - connect.channel->flags & IEEE80211_CHAN_DISABLED) + connect.channel = nl80211_get_valid_chan( + wiphy, info->attrs[NL80211_ATTR_WIPHY_FREQ]); + if (!connect.channel) return -EINVAL; } else if (info->attrs[NL80211_ATTR_WIPHY_FREQ_HINT]) { - connect.channel_hint = - ieee80211_get_channel(wiphy, - nla_get_u32( - info->attrs[NL80211_ATTR_WIPHY_FREQ_HINT])); - if (!connect.channel_hint || - connect.channel_hint->flags & IEEE80211_CHAN_DISABLED) + connect.channel_hint = nl80211_get_valid_chan( + wiphy, info->attrs[NL80211_ATTR_WIPHY_FREQ_HINT]); + if (!connect.channel_hint) return -EINVAL; } -- cgit v1.2.3-71-gd317 From 4b5800fec6173765207abded99df3d692ed55691 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 15 Jan 2014 14:55:59 +0100 Subject: cfg80211: make connect ie param const This required liberally sprinkling 'const' over brcmfmac and mwifiex but seems like a useful thing to do since the pointer can't really be written. Signed-off-by: Johannes Berg --- drivers/net/wireless/brcm80211/brcmfmac/fwil.c | 5 ++- drivers/net/wireless/brcm80211/brcmfmac/fwil.h | 2 +- .../net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 46 +++++++++++----------- .../net/wireless/brcm80211/brcmfmac/wl_cfg80211.h | 3 +- drivers/net/wireless/mwifiex/main.h | 2 +- drivers/net/wireless/mwifiex/sta_ioctl.c | 2 +- include/net/cfg80211.h | 2 +- 7 files changed, 32 insertions(+), 30 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c index 22adbe311d20..59a5af5bf994 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c @@ -124,7 +124,8 @@ brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data) } static u32 -brcmf_create_iovar(char *name, char *data, u32 datalen, char *buf, u32 buflen) +brcmf_create_iovar(char *name, const char *data, u32 datalen, + char *buf, u32 buflen) { u32 len; @@ -144,7 +145,7 @@ brcmf_create_iovar(char *name, char *data, u32 datalen, char *buf, u32 buflen) s32 -brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, void *data, +brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data, u32 len) { struct brcmf_pub *drvr = ifp->drvr; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil.h index 77eae86e55c2..a30be683f4a1 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.h @@ -83,7 +83,7 @@ s32 brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len); s32 brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data); s32 brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data); -s32 brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, void *data, +s32 brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data, u32 len); s32 brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data, u32 len); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index d7718a5fa2f0..3d25c18340c5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -351,13 +351,11 @@ u16 channel_to_chanspec(struct brcmu_d11inf *d11inf, * triples, returning a pointer to the substring whose first element * matches tag */ -struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key) +const struct brcmf_tlv * +brcmf_parse_tlvs(const void *buf, int buflen, uint key) { - struct brcmf_tlv *elt; - int totlen; - - elt = (struct brcmf_tlv *)buf; - totlen = buflen; + const struct brcmf_tlv *elt = buf; + int totlen = buflen; /* find tagged parameter */ while (totlen >= TLV_HDR_LEN) { @@ -378,8 +376,8 @@ struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key) * not update the tlvs buffer pointer/length. */ static bool -brcmf_tlv_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len, - u8 *oui, u32 oui_len, u8 type) +brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len, + const u8 *oui, u32 oui_len, u8 type) { /* If the contents match the OUI and the type */ if (ie[TLV_LEN_OFF] >= oui_len + 1 && @@ -401,12 +399,12 @@ brcmf_tlv_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len, } static struct brcmf_vs_tlv * -brcmf_find_wpaie(u8 *parse, u32 len) +brcmf_find_wpaie(const u8 *parse, u32 len) { - struct brcmf_tlv *ie; + const struct brcmf_tlv *ie; while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) { - if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len, + if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len, WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE)) return (struct brcmf_vs_tlv *)ie; } @@ -414,9 +412,9 @@ brcmf_find_wpaie(u8 *parse, u32 len) } static struct brcmf_vs_tlv * -brcmf_find_wpsie(u8 *parse, u32 len) +brcmf_find_wpsie(const u8 *parse, u32 len) { - struct brcmf_tlv *ie; + const struct brcmf_tlv *ie; while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) { if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len, @@ -1562,9 +1560,9 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, struct ieee80211_channel *chan = sme->channel; struct brcmf_join_params join_params; size_t join_params_size; - struct brcmf_tlv *rsn_ie; - struct brcmf_vs_tlv *wpa_ie; - void *ie; + const struct brcmf_tlv *rsn_ie; + const struct brcmf_vs_tlv *wpa_ie; + const void *ie; u32 ie_len; struct brcmf_ext_join_params_le *ext_join_params; u16 chanspec; @@ -1591,7 +1589,8 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, ie_len = wpa_ie->len + TLV_HDR_LEN; } else { /* find the RSN_IE */ - rsn_ie = brcmf_parse_tlvs((u8 *)sme->ie, sme->ie_len, + rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie, + sme->ie_len, WLAN_EID_RSN); if (rsn_ie) { ie = rsn_ie; @@ -2455,7 +2454,7 @@ static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg, struct brcmf_cfg80211_profile *profile = ndev_to_prof(ifp->ndev); struct brcmf_bss_info_le *bi; struct brcmf_ssid *ssid; - struct brcmf_tlv *tim; + const struct brcmf_tlv *tim; u16 beacon_interval; u8 dtim_period; size_t ie_len; @@ -3220,8 +3219,9 @@ static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie) } static s32 -brcmf_configure_wpaie(struct net_device *ndev, struct brcmf_vs_tlv *wpa_ie, - bool is_rsn_ie) +brcmf_configure_wpaie(struct net_device *ndev, + const struct brcmf_vs_tlv *wpa_ie, + bool is_rsn_ie) { struct brcmf_if *ifp = netdev_priv(ndev); u32 auth = 0; /* d11 open authentication */ @@ -3707,11 +3707,11 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, s32 ie_offset; struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_tlv *ssid_ie; + const struct brcmf_tlv *ssid_ie; struct brcmf_ssid_le ssid_le; s32 err = -EPERM; - struct brcmf_tlv *rsn_ie; - struct brcmf_vs_tlv *wpa_ie; + const struct brcmf_tlv *rsn_ie; + const struct brcmf_vs_tlv *wpa_ie; struct brcmf_join_params join_params; enum nl80211_iftype dev_role; struct brcmf_fil_bss_enable_le bss_enable; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h index 2dc6a074e8ed..254feed2860e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h @@ -491,7 +491,8 @@ void brcmf_free_vif(struct brcmf_cfg80211_vif *vif); s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag, const u8 *vndr_ie_buf, u32 vndr_ie_len); s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif); -struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key); +const struct brcmf_tlv * +brcmf_parse_tlvs(const void *buf, int buflen, uint key); u16 channel_to_chanspec(struct brcmu_d11inf *d11inf, struct ieee80211_channel *ch); u32 wl_get_vif_state_all(struct brcmf_cfg80211_info *cfg, unsigned long state); diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index d8ad554ce39f..29d27d9b5ebe 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -1078,7 +1078,7 @@ int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp, const u8 *key, int key_len, u8 key_index, const u8 *mac_addr, int disable); -int mwifiex_set_gen_ie(struct mwifiex_private *priv, u8 *ie, int ie_len); +int mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len); int mwifiex_get_ver_ext(struct mwifiex_private *priv); diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index c5cb2ed19ec2..0bec94351f36 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -1391,7 +1391,7 @@ static int mwifiex_misc_ioctl_gen_ie(struct mwifiex_private *priv, * with requisite parameters and calls the IOCTL handler. */ int -mwifiex_set_gen_ie(struct mwifiex_private *priv, u8 *ie, int ie_len) +mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len) { struct mwifiex_ds_misc_gen_ie gen_ie; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 9237b26142a1..d10ba3a1bfa8 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1737,7 +1737,7 @@ struct cfg80211_connect_params { const u8 *ssid; size_t ssid_len; enum nl80211_auth_type auth_type; - u8 *ie; + const u8 *ie; size_t ie_len; bool privacy; enum nl80211_mfp mfp; -- cgit v1.2.3-71-gd317 From 0b9323f600a3e80a488e3bd14ddfa85b294e630d Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Wed, 8 Jan 2014 08:46:02 +0100 Subject: nl80211: add Guard Interval support for set_bitrate_mask Allow to force SGI, LGI. Mainly for test purpose. Signed-off-by: Janusz Dziedzic Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 1 + include/uapi/linux/nl80211.h | 8 ++++++++ net/wireless/nl80211.c | 7 +++++++ 3 files changed, 16 insertions(+) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index d10ba3a1bfa8..d5e57bf678a6 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1776,6 +1776,7 @@ struct cfg80211_bitrate_mask { u32 legacy; u8 ht_mcs[IEEE80211_HT_MCS_MASK_LEN]; u16 vht_mcs[NL80211_VHT_NSS_MAX]; + enum nl80211_txrate_gi gi; } control[IEEE80211_NUM_BANDS]; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 9a86c8bf6da6..53e56cf7c0fe 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3156,6 +3156,7 @@ enum nl80211_key_attributes { * in an array of MCS numbers. * @NL80211_TXRATE_VHT: VHT rates allowed for TX rate selection, * see &struct nl80211_txrate_vht + * @NL80211_TXRATE_GI: configure GI, see &enum nl80211_txrate_gi * @__NL80211_TXRATE_AFTER_LAST: internal * @NL80211_TXRATE_MAX: highest TX rate attribute */ @@ -3164,6 +3165,7 @@ enum nl80211_tx_rate_attributes { NL80211_TXRATE_LEGACY, NL80211_TXRATE_HT, NL80211_TXRATE_VHT, + NL80211_TXRATE_GI, /* keep last */ __NL80211_TXRATE_AFTER_LAST, @@ -3181,6 +3183,12 @@ struct nl80211_txrate_vht { __u16 mcs[NL80211_VHT_NSS_MAX]; }; +enum nl80211_txrate_gi { + NL80211_TXRATE_DEFAULT_GI, + NL80211_TXRATE_FORCE_SGI, + NL80211_TXRATE_FORCE_LGI, +}; + /** * enum nl80211_band - Frequency band * @NL80211_BAND_2GHZ: 2.4 GHz ISM band diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 09b6da8ffdfe..a3515ebbd32b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -7447,6 +7447,7 @@ static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = { [NL80211_TXRATE_HT] = { .type = NLA_BINARY, .len = NL80211_MAX_SUPP_HT_RATES }, [NL80211_TXRATE_VHT] = { .len = sizeof(struct nl80211_txrate_vht)}, + [NL80211_TXRATE_GI] = { .type = NLA_U8 }, }; static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, @@ -7527,6 +7528,12 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, mask.control[band].vht_mcs)) return -EINVAL; } + if (tb[NL80211_TXRATE_GI]) { + mask.control[band].gi = + nla_get_u8(tb[NL80211_TXRATE_GI]); + if (mask.control[band].gi > NL80211_TXRATE_FORCE_LGI) + return -EINVAL; + } if (mask.control[band].legacy == 0) { /* don't allow empty legacy rates if HT or VHT -- cgit v1.2.3-71-gd317 From 631ad703ba3a585e96acbfd2ac8c0f0fee1ad99b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 20 Jan 2014 23:29:34 +0100 Subject: mac80211: make rate control ops const Change the code to allow making all the rate control ops const, nothing ever needs to change them. Also change all drivers to make use of this and mark the ops const. Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath9k/rc.c | 2 +- drivers/net/wireless/iwlegacy/3945-rs.c | 2 +- drivers/net/wireless/iwlegacy/4965-rs.c | 2 +- drivers/net/wireless/iwlwifi/dvm/rs.c | 3 ++- drivers/net/wireless/iwlwifi/mvm/rs.c | 3 ++- drivers/net/wireless/rtlwifi/rc.c | 2 +- include/net/mac80211.h | 4 ++-- net/mac80211/rate.c | 16 ++++++++-------- net/mac80211/rate.h | 2 +- net/mac80211/rc80211_minstrel.c | 2 +- net/mac80211/rc80211_minstrel.h | 2 +- net/mac80211/rc80211_minstrel_ht.c | 2 +- net/mac80211/rc80211_pid_algo.c | 2 +- 13 files changed, 23 insertions(+), 21 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index d829bb62a3fc..1219532e908a 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -1466,7 +1466,7 @@ static void ath_rate_free_sta(void *priv, struct ieee80211_sta *sta, kfree(rate_priv); } -static struct rate_control_ops ath_rate_ops = { +static const struct rate_control_ops ath_rate_ops = { .module = NULL, .name = "ath9k_rate_control", .tx_status = ath_tx_status, diff --git a/drivers/net/wireless/iwlegacy/3945-rs.c b/drivers/net/wireless/iwlegacy/3945-rs.c index 9a45f6f626f6..7088c6a89455 100644 --- a/drivers/net/wireless/iwlegacy/3945-rs.c +++ b/drivers/net/wireless/iwlegacy/3945-rs.c @@ -891,7 +891,7 @@ il3945_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband, { } -static struct rate_control_ops rs_ops = { +static const struct rate_control_ops rs_ops = { .module = NULL, .name = RS_NAME, .tx_status = il3945_rs_tx_status, diff --git a/drivers/net/wireless/iwlegacy/4965-rs.c b/drivers/net/wireless/iwlegacy/4965-rs.c index 4d5e33259ca8..cdbfc1d30b98 100644 --- a/drivers/net/wireless/iwlegacy/4965-rs.c +++ b/drivers/net/wireless/iwlegacy/4965-rs.c @@ -2807,7 +2807,7 @@ il4965_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband, { } -static struct rate_control_ops rs_4965_ops = { +static const struct rate_control_ops rs_4965_ops = { .module = NULL, .name = IL4965_RS_NAME, .tx_status = il4965_rs_tx_status, diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.c b/drivers/net/wireless/iwlwifi/dvm/rs.c index 0977d93b529d..c4dded8d8091 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rs.c +++ b/drivers/net/wireless/iwlwifi/dvm/rs.c @@ -3319,7 +3319,8 @@ static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sba struct ieee80211_sta *sta, void *priv_sta) { } -static struct rate_control_ops rs_ops = { + +static const struct rate_control_ops rs_ops = { .module = NULL, .name = RS_NAME, .tx_status = rs_tx_status, diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 6abf74e1351f..22f1953880b6 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -2815,7 +2815,8 @@ static void rs_rate_init_stub(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta) { } -static struct rate_control_ops rs_mvm_ops = { + +static const struct rate_control_ops rs_mvm_ops = { .module = NULL, .name = RS_NAME, .tx_status = rs_tx_status, diff --git a/drivers/net/wireless/rtlwifi/rc.c b/drivers/net/wireless/rtlwifi/rc.c index a98acefb8c06..1503d9e5bc9f 100644 --- a/drivers/net/wireless/rtlwifi/rc.c +++ b/drivers/net/wireless/rtlwifi/rc.c @@ -260,7 +260,7 @@ static void rtl_rate_free_sta(void *rtlpriv, kfree(rate_priv); } -static struct rate_control_ops rtl_rate_ops = { +static const struct rate_control_ops rtl_rate_ops = { .module = NULL, .name = "rtl_rc", .alloc = rtl_rate_alloc, diff --git a/include/net/mac80211.h b/include/net/mac80211.h index df1004be7ba5..0c2676e2a1f8 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -4555,8 +4555,8 @@ int rate_control_set_rates(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, struct ieee80211_sta_rates *rates); -int ieee80211_rate_control_register(struct rate_control_ops *ops); -void ieee80211_rate_control_unregister(struct rate_control_ops *ops); +int ieee80211_rate_control_register(const struct rate_control_ops *ops); +void ieee80211_rate_control_unregister(const struct rate_control_ops *ops); static inline bool conf_is_ht20(struct ieee80211_conf *conf) diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 22b223f13c9f..255b59e616d0 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -18,7 +18,7 @@ struct rate_control_alg { struct list_head list; - struct rate_control_ops *ops; + const struct rate_control_ops *ops; }; static LIST_HEAD(rate_ctrl_algs); @@ -29,7 +29,7 @@ module_param(ieee80211_default_rc_algo, charp, 0644); MODULE_PARM_DESC(ieee80211_default_rc_algo, "Default rate control algorithm for mac80211 to use"); -int ieee80211_rate_control_register(struct rate_control_ops *ops) +int ieee80211_rate_control_register(const struct rate_control_ops *ops) { struct rate_control_alg *alg; @@ -60,7 +60,7 @@ int ieee80211_rate_control_register(struct rate_control_ops *ops) } EXPORT_SYMBOL(ieee80211_rate_control_register); -void ieee80211_rate_control_unregister(struct rate_control_ops *ops) +void ieee80211_rate_control_unregister(const struct rate_control_ops *ops) { struct rate_control_alg *alg; @@ -76,11 +76,11 @@ void ieee80211_rate_control_unregister(struct rate_control_ops *ops) } EXPORT_SYMBOL(ieee80211_rate_control_unregister); -static struct rate_control_ops * +static const struct rate_control_ops * ieee80211_try_rate_control_ops_get(const char *name) { struct rate_control_alg *alg; - struct rate_control_ops *ops = NULL; + const struct rate_control_ops *ops = NULL; if (!name) return NULL; @@ -98,10 +98,10 @@ ieee80211_try_rate_control_ops_get(const char *name) } /* Get the rate control algorithm. */ -static struct rate_control_ops * +static const struct rate_control_ops * ieee80211_rate_control_ops_get(const char *name) { - struct rate_control_ops *ops; + const struct rate_control_ops *ops; const char *alg_name; kparam_block_sysfs_write(ieee80211_default_rc_algo); @@ -127,7 +127,7 @@ ieee80211_rate_control_ops_get(const char *name) return ops; } -static void ieee80211_rate_control_ops_put(struct rate_control_ops *ops) +static void ieee80211_rate_control_ops_put(const struct rate_control_ops *ops) { module_put(ops->module); } diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h index b95e16c07081..9aa2a1190a86 100644 --- a/net/mac80211/rate.h +++ b/net/mac80211/rate.h @@ -21,7 +21,7 @@ struct rate_control_ref { struct ieee80211_local *local; - struct rate_control_ops *ops; + const struct rate_control_ops *ops; void *priv; }; diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index f3d88b0c054c..26fd94fa0aed 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -657,7 +657,7 @@ minstrel_free(void *priv) kfree(priv); } -struct rate_control_ops mac80211_minstrel = { +const struct rate_control_ops mac80211_minstrel = { .name = "minstrel", .tx_status = minstrel_tx_status, .get_rate = minstrel_get_rate, diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h index f4301f4b2e41..046d1bd598a8 100644 --- a/net/mac80211/rc80211_minstrel.h +++ b/net/mac80211/rc80211_minstrel.h @@ -123,7 +123,7 @@ struct minstrel_debugfs_info { char buf[]; }; -extern struct rate_control_ops mac80211_minstrel; +extern const struct rate_control_ops mac80211_minstrel; void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir); void minstrel_remove_sta_debugfs(void *priv, void *priv_sta); diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index c1b5b73c5b91..a6d6cc5c3db4 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -1031,7 +1031,7 @@ minstrel_ht_free(void *priv) mac80211_minstrel.free(priv); } -static struct rate_control_ops mac80211_minstrel_ht = { +static const struct rate_control_ops mac80211_minstrel_ht = { .name = "minstrel_ht", .tx_status = minstrel_ht_tx_status, .get_rate = minstrel_ht_get_rate, diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c index 958fad07b54c..d0da2a70fe68 100644 --- a/net/mac80211/rc80211_pid_algo.c +++ b/net/mac80211/rc80211_pid_algo.c @@ -452,7 +452,7 @@ static void rate_control_pid_free_sta(void *priv, struct ieee80211_sta *sta, kfree(priv_sta); } -static struct rate_control_ops mac80211_rcpid = { +static const struct rate_control_ops mac80211_rcpid = { .name = "pid", .tx_status = rate_control_pid_tx_status, .get_rate = rate_control_pid_get_rate, -- cgit v1.2.3-71-gd317 From cc01f9b55fe77831a3ef63c0c461ca76540cee88 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 22 Jan 2014 10:36:59 +0100 Subject: mac80211: remove module handling from rate control ops There's not a single rate control algorithm actually in a separate module where the module refcount would be required. Similarly, there's no specific rate control module. Therefore, all the module handling code in rate control is really just dead code, so remove it. Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath9k/rc.c | 1 - drivers/net/wireless/iwlegacy/3945-rs.c | 1 - drivers/net/wireless/iwlegacy/4965-rs.c | 1 - drivers/net/wireless/iwlwifi/dvm/rs.c | 1 - drivers/net/wireless/iwlwifi/mvm/rs.c | 1 - drivers/net/wireless/rtlwifi/rc.c | 1 - include/net/mac80211.h | 1 - net/mac80211/rate.c | 32 +++++++++----------------------- 8 files changed, 9 insertions(+), 30 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index 1219532e908a..7b5afee141da 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -1467,7 +1467,6 @@ static void ath_rate_free_sta(void *priv, struct ieee80211_sta *sta, } static const struct rate_control_ops ath_rate_ops = { - .module = NULL, .name = "ath9k_rate_control", .tx_status = ath_tx_status, .get_rate = ath_get_rate, diff --git a/drivers/net/wireless/iwlegacy/3945-rs.c b/drivers/net/wireless/iwlegacy/3945-rs.c index 7088c6a89455..76b0729ade17 100644 --- a/drivers/net/wireless/iwlegacy/3945-rs.c +++ b/drivers/net/wireless/iwlegacy/3945-rs.c @@ -892,7 +892,6 @@ il3945_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband, } static const struct rate_control_ops rs_ops = { - .module = NULL, .name = RS_NAME, .tx_status = il3945_rs_tx_status, .get_rate = il3945_rs_get_rate, diff --git a/drivers/net/wireless/iwlegacy/4965-rs.c b/drivers/net/wireless/iwlegacy/4965-rs.c index cdbfc1d30b98..eaaeea19d8c5 100644 --- a/drivers/net/wireless/iwlegacy/4965-rs.c +++ b/drivers/net/wireless/iwlegacy/4965-rs.c @@ -2808,7 +2808,6 @@ il4965_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband, } static const struct rate_control_ops rs_4965_ops = { - .module = NULL, .name = IL4965_RS_NAME, .tx_status = il4965_rs_tx_status, .get_rate = il4965_rs_get_rate, diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.c b/drivers/net/wireless/iwlwifi/dvm/rs.c index c4dded8d8091..592365ae46b6 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rs.c +++ b/drivers/net/wireless/iwlwifi/dvm/rs.c @@ -3321,7 +3321,6 @@ static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sba } static const struct rate_control_ops rs_ops = { - .module = NULL, .name = RS_NAME, .tx_status = rs_tx_status, .get_rate = rs_get_rate, diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 22f1953880b6..c49e3a4c63ed 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -2817,7 +2817,6 @@ static void rs_rate_init_stub(void *mvm_r, } static const struct rate_control_ops rs_mvm_ops = { - .module = NULL, .name = RS_NAME, .tx_status = rs_tx_status, .get_rate = rs_get_rate, diff --git a/drivers/net/wireless/rtlwifi/rc.c b/drivers/net/wireless/rtlwifi/rc.c index 1503d9e5bc9f..ee28a1a3d010 100644 --- a/drivers/net/wireless/rtlwifi/rc.c +++ b/drivers/net/wireless/rtlwifi/rc.c @@ -261,7 +261,6 @@ static void rtl_rate_free_sta(void *rtlpriv, } static const struct rate_control_ops rtl_rate_ops = { - .module = NULL, .name = "rtl_rc", .alloc = rtl_rate_alloc, .free = rtl_rate_free, diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 0c2676e2a1f8..f844770b7fd4 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -4453,7 +4453,6 @@ struct ieee80211_tx_rate_control { }; struct rate_control_ops { - struct module *module; const char *name; void *(*alloc)(struct ieee80211_hw *hw, struct dentry *debugfsdir); void (*free)(void *priv); diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 255b59e616d0..8fdadfd94ba8 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -10,8 +10,8 @@ #include #include -#include #include +#include #include "rate.h" #include "ieee80211_i.h" #include "debugfs.h" @@ -87,11 +87,10 @@ ieee80211_try_rate_control_ops_get(const char *name) mutex_lock(&rate_ctrl_mutex); list_for_each_entry(alg, &rate_ctrl_algs, list) { - if (!strcmp(alg->ops->name, name)) - if (try_module_get(alg->ops->module)) { - ops = alg->ops; - break; - } + if (!strcmp(alg->ops->name, name)) { + ops = alg->ops; + break; + } } mutex_unlock(&rate_ctrl_mutex); return ops; @@ -111,10 +110,6 @@ ieee80211_rate_control_ops_get(const char *name) alg_name = name; ops = ieee80211_try_rate_control_ops_get(alg_name); - if (!ops) { - request_module("rc80211_%s", alg_name); - ops = ieee80211_try_rate_control_ops_get(alg_name); - } if (!ops && name) /* try default if specific alg requested but not found */ ops = ieee80211_try_rate_control_ops_get(ieee80211_default_rc_algo); @@ -127,11 +122,6 @@ ieee80211_rate_control_ops_get(const char *name) return ops; } -static void ieee80211_rate_control_ops_put(const struct rate_control_ops *ops) -{ - module_put(ops->module); -} - #ifdef CONFIG_MAC80211_DEBUGFS static ssize_t rcname_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) @@ -158,11 +148,11 @@ static struct rate_control_ref *rate_control_alloc(const char *name, ref = kmalloc(sizeof(struct rate_control_ref), GFP_KERNEL); if (!ref) - goto fail_ref; + return NULL; ref->local = local; ref->ops = ieee80211_rate_control_ops_get(name); if (!ref->ops) - goto fail_ops; + goto free; #ifdef CONFIG_MAC80211_DEBUGFS debugfsdir = debugfs_create_dir("rc", local->hw.wiphy->debugfsdir); @@ -172,14 +162,11 @@ static struct rate_control_ref *rate_control_alloc(const char *name, ref->priv = ref->ops->alloc(&local->hw, debugfsdir); if (!ref->priv) - goto fail_priv; + goto free; return ref; -fail_priv: - ieee80211_rate_control_ops_put(ref->ops); -fail_ops: +free: kfree(ref); -fail_ref: return NULL; } @@ -192,7 +179,6 @@ static void rate_control_free(struct rate_control_ref *ctrl_ref) ctrl_ref->local->debugfs.rcdir = NULL; #endif - ieee80211_rate_control_ops_put(ctrl_ref->ops); kfree(ctrl_ref); } -- cgit v1.2.3-71-gd317 From ea73cbce4e1fd93113301532ad98041b119bc85a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 24 Jan 2014 10:53:53 +0100 Subject: nl80211: fix scheduled scan RSSI matchset attribute confusion The scheduled scan matchsets were intended to be a list of filters, with the found BSS having to pass at least one of them to be passed to the host. When the RSSI attribute was added, however, this was broken and currently wpa_supplicant adds that attribute in its own matchset; however, it doesn't intend that to mean that anything that passes the RSSI filter should be passed to the host, instead it wants it to mean that everything needs to also have higher RSSI. This is semantically problematic because we have a list of filters like [ SSID1, SSID2, SSID3, RSSI ] with no real indication which one should be OR'ed and which one AND'ed. To fix this, move the RSSI filter attribute into each matchset. As we need to stay backward compatible, treat a matchset with only the RSSI attribute as a "default RSSI filter" for all other matchsets, but only if there are other matchsets (an RSSI-only matchset by itself is still desirable.) To make driver implementation easier, keep a global min_rssi_thold for the entire request as well. The only affected driver is ath6kl. I found this when I looked into the code after Raja Mani submitted a patch fixing the n_match_sets calculation to disregard the RSSI, but that patch didn't address the semantic issue. Reported-by: Raja Mani Acked-by: Luciano Coelho Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 19 +++++--- drivers/net/wireless/iwlwifi/mvm/scan.c | 3 ++ include/net/cfg80211.h | 9 ++-- include/uapi/linux/nl80211.h | 10 ++++- net/wireless/nl80211.c | 70 ++++++++++++++++++++++++++---- 5 files changed, 92 insertions(+), 19 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index fd4c89df67e1..eba32f56850a 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -3256,6 +3256,15 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, struct ath6kl_vif *vif = netdev_priv(dev); u16 interval; int ret, rssi_thold; + int n_match_sets = request->n_match_sets; + + /* + * If there's a matchset w/o an SSID, then assume it's just for + * the RSSI (nothing else is currently supported) and ignore it. + * The device only supports a global RSSI filter that we set below. + */ + if (n_match_sets == 1 && !request->match_sets[0].ssid.ssid_len) + n_match_sets = 0; if (ar->state != ATH6KL_STATE_ON) return -EIO; @@ -3268,11 +3277,11 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, ret = ath6kl_set_probed_ssids(ar, vif, request->ssids, request->n_ssids, request->match_sets, - request->n_match_sets); + n_match_sets); if (ret < 0) return ret; - if (!request->n_match_sets) { + if (!n_match_sets) { ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, ALL_BSS_FILTER, 0); if (ret < 0) @@ -3286,12 +3295,12 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, if (test_bit(ATH6KL_FW_CAPABILITY_RSSI_SCAN_THOLD, ar->fw_capabilities)) { - if (request->rssi_thold <= NL80211_SCAN_RSSI_THOLD_OFF) + if (request->min_rssi_thold <= NL80211_SCAN_RSSI_THOLD_OFF) rssi_thold = 0; - else if (request->rssi_thold < -127) + else if (request->min_rssi_thold < -127) rssi_thold = -127; else - rssi_thold = request->rssi_thold; + rssi_thold = request->min_rssi_thold; ret = ath6kl_wmi_set_rssi_filter_cmd(ar->wmi, vif->fw_vif_idx, rssi_thold); diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index 0e0007960612..9674bfd978f1 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -595,6 +595,9 @@ static void iwl_scan_offload_build_ssid(struct cfg80211_sched_scan_request *req, * config match list. */ for (i = 0; i < req->n_match_sets && i < PROBE_OPTION_MAX; i++) { + /* skip empty SSID matchsets */ + if (!req->match_sets[i].ssid.ssid_len) + continue; scan->direct_scan[i].id = WLAN_EID_SSID; scan->direct_scan[i].len = req->match_sets[i].ssid.ssid_len; memcpy(scan->direct_scan[i].ssid, req->match_sets[i].ssid.ssid, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index d5e57bf678a6..009290e36d15 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1394,10 +1394,12 @@ struct cfg80211_scan_request { /** * struct cfg80211_match_set - sets of attributes to match * - * @ssid: SSID to be matched + * @ssid: SSID to be matched; may be zero-length for no match (RSSI only) + * @rssi_thold: don't report scan results below this threshold (in s32 dBm) */ struct cfg80211_match_set { struct cfg80211_ssid ssid; + s32 rssi_thold; }; /** @@ -1420,7 +1422,8 @@ struct cfg80211_match_set { * @dev: the interface * @scan_start: start time of the scheduled scan * @channels: channels to scan - * @rssi_thold: don't report scan results below this threshold (in s32 dBm) + * @min_rssi_thold: for drivers only supporting a single threshold, this + * contains the minimum over all matchsets */ struct cfg80211_sched_scan_request { struct cfg80211_ssid *ssids; @@ -1433,7 +1436,7 @@ struct cfg80211_sched_scan_request { u32 flags; struct cfg80211_match_set *match_sets; int n_match_sets; - s32 rssi_thold; + s32 min_rssi_thold; /* internal */ struct wiphy *wiphy; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 53e56cf7c0fe..474ce32e0797 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2467,9 +2467,15 @@ enum nl80211_reg_rule_attr { * enum nl80211_sched_scan_match_attr - scheduled scan match attributes * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching, - * only report BSS with matching SSID. + * only report BSS with matching SSID. * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a - * BSS in scan results. Filtering is turned off if not specified. + * BSS in scan results. Filtering is turned off if not specified. Note that + * if this attribute is in a match set of its own, then it is treated as + * the default value for all matchsets with an SSID, rather than being a + * matchset of its own without an RSSI filter. This is due to problems with + * how this API was implemented in the past. Also, due to the same problem, + * the only way to create a matchset with only an RSSI filter (with this + * attribute) is if there's only a single matchset with the RSSI attribute. * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter * attribute number currently defined * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 043bfbd58b56..20be186f7f77 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5467,6 +5467,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, enum ieee80211_band band; size_t ie_len; struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1]; + s32 default_match_rssi = NL80211_SCAN_RSSI_THOLD_OFF; if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || !rdev->ops->sched_scan_start) @@ -5501,11 +5502,40 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, if (n_ssids > wiphy->max_sched_scan_ssids) return -EINVAL; - if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) + /* + * First, count the number of 'real' matchsets. Due to an issue with + * the old implementation, matchsets containing only the RSSI attribute + * (NL80211_SCHED_SCAN_MATCH_ATTR_RSSI) are considered as the 'default' + * RSSI for all matchsets, rather than their own matchset for reporting + * all APs with a strong RSSI. This is needed to be compatible with + * older userspace that treated a matchset with only the RSSI as the + * global RSSI for all other matchsets - if there are other matchsets. + */ + if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) { nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH], - tmp) - n_match_sets++; + tmp) { + struct nlattr *rssi; + + err = nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX, + nla_data(attr), nla_len(attr), + nl80211_match_policy); + if (err) + return err; + /* add other standalone attributes here */ + if (tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]) { + n_match_sets++; + continue; + } + rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI]; + if (rssi) + default_match_rssi = nla_get_s32(rssi); + } + } + + /* However, if there's no other matchset, add the RSSI one */ + if (!n_match_sets && default_match_rssi != NL80211_SCAN_RSSI_THOLD_OFF) + n_match_sets = 1; if (n_match_sets > wiphy->max_match_sets) return -EINVAL; @@ -5633,6 +5663,15 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, goto out_free; ssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]; if (ssid) { + if (WARN_ON(i >= n_match_sets)) { + /* this indicates a programming error, + * the loop above should have verified + * things properly + */ + err = -EINVAL; + goto out_free; + } + if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) { err = -EINVAL; goto out_free; @@ -5641,15 +5680,28 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, nla_data(ssid), nla_len(ssid)); request->match_sets[i].ssid.ssid_len = nla_len(ssid); + /* special attribute - old implemenation w/a */ + request->match_sets[i].rssi_thold = + default_match_rssi; + rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI]; + if (rssi) + request->match_sets[i].rssi_thold = + nla_get_s32(rssi); } - rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI]; - if (rssi) - request->rssi_thold = nla_get_u32(rssi); - else - request->rssi_thold = - NL80211_SCAN_RSSI_THOLD_OFF; i++; } + + /* there was no other matchset, so the RSSI one is alone */ + if (i == 0) + request->match_sets[0].rssi_thold = default_match_rssi; + + request->min_rssi_thold = INT_MAX; + for (i = 0; i < n_match_sets; i++) + request->min_rssi_thold = + min(request->match_sets[i].rssi_thold, + request->min_rssi_thold); + } else { + request->min_rssi_thold = NL80211_SCAN_RSSI_THOLD_OFF; } if (info->attrs[NL80211_ATTR_IE]) { -- cgit v1.2.3-71-gd317 From fe94f3a4ffaa20c7470038c69ffc8e545ef5f90a Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Wed, 29 Jan 2014 17:53:43 +0100 Subject: cfg80211: fix channel configuration in IBSS join When receiving an IBSS_JOINED event select the BSS object based on the {bssid, channel} couple rather than the bssid only. With the current approach if another cell having the same BSSID (but using a different channel) exists then cfg80211 picks up the wrong BSS object. The result is a mismatching channel configuration between cfg80211 and the driver, that can lead to any sort of problem. The issue can be triggered by having an IBSS sitting on given channel and then asking the driver to create a new cell using the same BSSID but with a different frequency. By passing the channel to cfg80211_get_bss() we can solve this ambiguity and retrieve/create the correct BSS object. All the users of cfg80211_ibss_joined() have been changed accordingly. Moreover WARN when cfg80211_ibss_joined() gets a NULL channel as argument and remove a bogus call of the same function in ath6kl (it does not make sense to call cfg80211_ibss_joined() with a zero BSSID on ibss-leave). Cc: Kalle Valo Cc: Arend van Spriel Cc: Bing Zhao Cc: Jussi Kivilinna Cc: libertas-dev@lists.infradead.org Acked-by: Kalle Valo Signed-off-by: Antonio Quartulli [minor code cleanup in ath6kl] Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 8 ++------ .../net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 4 +++- drivers/net/wireless/libertas/cfg.c | 3 ++- drivers/net/wireless/mwifiex/cfg80211.c | 3 ++- drivers/net/wireless/rndis_wlan.c | 4 +++- include/net/cfg80211.h | 4 +++- net/mac80211/ibss.c | 2 +- net/wireless/core.h | 4 +++- net/wireless/ibss.c | 17 ++++++++++------ net/wireless/trace.h | 23 +++++++++++++++++----- net/wireless/util.c | 3 ++- 11 files changed, 50 insertions(+), 25 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index eba32f56850a..c2c6f4604958 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -790,7 +790,7 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, if (nw_type & ADHOC_NETWORK) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n", nw_type & ADHOC_CREATOR ? "creator" : "joiner"); - cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL); + cfg80211_ibss_joined(vif->ndev, bssid, chan, GFP_KERNEL); cfg80211_put_bss(ar->wiphy, bss); return; } @@ -861,13 +861,9 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason, } if (vif->nw_type & ADHOC_NETWORK) { - if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) { + if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: ath6k not in ibss mode\n", __func__); - return; - } - memset(bssid, 0, ETH_ALEN); - cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL); return; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 3d25c18340c5..1a80bf19cb89 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -4658,6 +4658,7 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, struct brcmf_cfg80211_info *cfg = ifp->drvr->config; struct net_device *ndev = ifp->ndev; struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; + struct ieee80211_channel *chan; s32 err = 0; if (ifp->vif->mode == WL_MODE_AP) { @@ -4665,9 +4666,10 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, } else if (brcmf_is_linkup(e)) { brcmf_dbg(CONN, "Linkup\n"); if (brcmf_is_ibssmode(ifp->vif)) { + chan = ieee80211_get_channel(cfg->wiphy, cfg->channel); memcpy(profile->bssid, e->addr, ETH_ALEN); wl_inform_ibss(cfg, ndev, e->addr); - cfg80211_ibss_joined(ndev, e->addr, GFP_KERNEL); + cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL); clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); set_bit(BRCMF_VIF_STATUS_CONNECTED, diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 32f75007a825..2d72a6b4b93e 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -1766,7 +1766,8 @@ static void lbs_join_post(struct lbs_private *priv, memcpy(priv->wdev->ssid, params->ssid, params->ssid_len); priv->wdev->ssid_len = params->ssid_len; - cfg80211_ibss_joined(priv->dev, bssid, GFP_KERNEL); + cfg80211_ibss_joined(priv->dev, bssid, params->chandef.chan, + GFP_KERNEL); /* TODO: consider doing this at MACREG_INT_CODE_LINK_SENSED time */ priv->connect_status = LBS_CONNECTED; diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index f4cf9c9d40ec..0948ebe8942e 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1882,7 +1882,8 @@ mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, params->privacy); done: if (!ret) { - cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, GFP_KERNEL); + cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, + params->chandef.chan, GFP_KERNEL); dev_dbg(priv->adapter->dev, "info: joined/created adhoc network with bssid" " %pM successfully\n", priv->cfg_bssid); diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 5028557aa18a..2e89a865a67d 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -2835,7 +2835,9 @@ static void rndis_wlan_do_link_up_work(struct usbnet *usbdev) bssid, req_ie, req_ie_len, resp_ie, resp_ie_len, GFP_KERNEL); } else if (priv->infra_mode == NDIS_80211_INFRA_ADHOC) - cfg80211_ibss_joined(usbdev->net, bssid, GFP_KERNEL); + cfg80211_ibss_joined(usbdev->net, bssid, + get_current_channel(usbdev, NULL), + GFP_KERNEL); kfree(info); diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 009290e36d15..c68201d78b90 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3895,6 +3895,7 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, * * @dev: network device * @bssid: the BSSID of the IBSS joined + * @channel: the channel of the IBSS joined * @gfp: allocation flags * * This function notifies cfg80211 that the device joined an IBSS or @@ -3904,7 +3905,8 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, * with the locally generated beacon -- this guarantees that there is * always a scan result for this IBSS. cfg80211 will handle the rest. */ -void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp); +void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, + struct ieee80211_channel *channel, gfp_t gfp); /** * cfg80211_notify_new_candidate - notify cfg80211 of a new mesh peer candidate diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 8e444476307a..9c84b75f3de8 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -382,7 +382,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, presp->head_len, 0, GFP_KERNEL); cfg80211_put_bss(local->hw.wiphy, bss); netif_carrier_on(sdata->dev); - cfg80211_ibss_joined(sdata->dev, ifibss->bssid, GFP_KERNEL); + cfg80211_ibss_joined(sdata->dev, ifibss->bssid, chan, GFP_KERNEL); } static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, diff --git a/net/wireless/core.h b/net/wireless/core.h index 37ec16d7bb1a..8a820f9c4a76 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -210,6 +210,7 @@ struct cfg80211_event { } dc; struct { u8 bssid[ETH_ALEN]; + struct ieee80211_channel *channel; } ij; }; }; @@ -257,7 +258,8 @@ int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, bool nowext); int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, bool nowext); -void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid); +void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, + struct ieee80211_channel *channel); int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev); diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index f911c5f9f903..e37e39c29dfb 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -14,7 +14,8 @@ #include "rdev-ops.h" -void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid) +void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, + struct ieee80211_channel *channel) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_bss *bss; @@ -28,8 +29,7 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid) if (!wdev->ssid_len) return; - bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, - wdev->ssid, wdev->ssid_len, + bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, NULL, 0, WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); if (WARN_ON(!bss)) @@ -54,21 +54,26 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid) #endif } -void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) +void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, + struct ieee80211_channel *channel, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct cfg80211_event *ev; unsigned long flags; - trace_cfg80211_ibss_joined(dev, bssid); + trace_cfg80211_ibss_joined(dev, bssid, channel); + + if (WARN_ON(!channel)) + return; ev = kzalloc(sizeof(*ev), gfp); if (!ev) return; ev->type = EVENT_IBSS_JOINED; - memcpy(ev->cr.bssid, bssid, ETH_ALEN); + memcpy(ev->ij.bssid, bssid, ETH_ALEN); + ev->ij.channel = channel; spin_lock_irqsave(&wdev->event_lock, flags); list_add_tail(&ev->list, &wdev->event_list); diff --git a/net/wireless/trace.h b/net/wireless/trace.h index fbcc23edee54..5eaeed59db07 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -2278,11 +2278,6 @@ DECLARE_EVENT_CLASS(cfg80211_rx_evt, TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT, NETDEV_PR_ARG, MAC_PR_ARG(addr)) ); -DEFINE_EVENT(cfg80211_rx_evt, cfg80211_ibss_joined, - TP_PROTO(struct net_device *netdev, const u8 *addr), - TP_ARGS(netdev, addr) -); - DEFINE_EVENT(cfg80211_rx_evt, cfg80211_rx_spurious_frame, TP_PROTO(struct net_device *netdev, const u8 *addr), TP_ARGS(netdev, addr) @@ -2293,6 +2288,24 @@ DEFINE_EVENT(cfg80211_rx_evt, cfg80211_rx_unexpected_4addr_frame, TP_ARGS(netdev, addr) ); +TRACE_EVENT(cfg80211_ibss_joined, + TP_PROTO(struct net_device *netdev, const u8 *bssid, + struct ieee80211_channel *channel), + TP_ARGS(netdev, bssid, channel), + TP_STRUCT__entry( + NETDEV_ENTRY + MAC_ENTRY(bssid) + CHAN_ENTRY + ), + TP_fast_assign( + NETDEV_ASSIGN; + MAC_ASSIGN(bssid, bssid); + CHAN_ASSIGN(channel); + ), + TP_printk(NETDEV_PR_FMT ", bssid: " MAC_PR_FMT ", " CHAN_PR_FMT, + NETDEV_PR_ARG, MAC_PR_ARG(bssid), CHAN_PR_ARG) +); + TRACE_EVENT(cfg80211_probe_status, TP_PROTO(struct net_device *netdev, const u8 *addr, u64 cookie, bool acked), diff --git a/net/wireless/util.c b/net/wireless/util.c index d39c37104ae2..7526a4d8aa16 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -820,7 +820,8 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev) ev->dc.reason, true); break; case EVENT_IBSS_JOINED: - __cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid); + __cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid, + ev->ij.channel); break; } wdev_unlock(wdev); -- cgit v1.2.3-71-gd317 From 9e0e29615a2077be852b1245b57c5b00fa609522 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 29 Jan 2014 14:22:27 +0100 Subject: cfg80211: consider existing DFS interfaces It was possible to break interface combinations in the following way: combo 1: iftype = AP, num_ifaces = 2, num_chans = 2, combo 2: iftype = AP, num_ifaces = 1, num_chans = 1, radar = HT20 With the above interface combinations it was possible to: step 1. start AP on DFS channel by matching combo 2 step 2. start AP on non-DFS channel by matching combo 1 This was possible beacuse (step 2) did not consider if other interfaces require radar detection. The patch changes how cfg80211 tracks channels - instead of channel itself now a complete chandef is stored. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 8 +++----- net/wireless/ap.c | 2 +- net/wireless/chan.c | 23 +++++++++++++++++++---- net/wireless/core.h | 3 ++- net/wireless/ibss.c | 2 ++ net/wireless/mesh.c | 6 +++--- net/wireless/mlme.c | 2 +- net/wireless/nl80211.c | 6 +++--- net/wireless/util.c | 2 +- 9 files changed, 35 insertions(+), 19 deletions(-) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index c68201d78b90..9f90554e88c4 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3146,8 +3146,8 @@ struct cfg80211_cached_keys; * @identifier: (private) Identifier used in nl80211 to identify this * wireless device if it has no netdev * @current_bss: (private) Used by the internal configuration code - * @channel: (private) Used by the internal configuration code to track - * the user-set AP, monitor and WDS channel + * @chandef: (private) Used by the internal configuration code to track + * the user-set channel definition. * @preset_chandef: (private) Used by the internal configuration code to * track the channel to be used for AP later * @bssid: (private) Used by the internal configuration code @@ -3211,9 +3211,7 @@ struct wireless_dev { struct cfg80211_internal_bss *current_bss; /* associated / joined */ struct cfg80211_chan_def preset_chandef; - - /* for AP and mesh channel tracking */ - struct ieee80211_channel *channel; + struct cfg80211_chan_def chandef; bool ibss_fixed; bool ibss_dfs_possible; diff --git a/net/wireless/ap.c b/net/wireless/ap.c index 4760d6554e62..68602be07cc1 100644 --- a/net/wireless/ap.c +++ b/net/wireless/ap.c @@ -27,7 +27,7 @@ static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, err = rdev_stop_ap(rdev, dev); if (!err) { wdev->beacon_interval = 0; - wdev->channel = NULL; + memset(&wdev->chandef, 0, sizeof(wdev->chandef)); wdev->ssid_len = 0; rdev_set_qos_map(rdev, dev, NULL); nl80211_send_ap_stopped(wdev); diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 78559b5bbd1f..f8ab7df1ab0d 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -642,7 +642,8 @@ int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, void cfg80211_get_chan_state(struct wireless_dev *wdev, struct ieee80211_channel **chan, - enum cfg80211_chan_mode *chanmode) + enum cfg80211_chan_mode *chanmode, + u8 *radar_detect) { *chan = NULL; *chanmode = CHAN_MODE_UNDEFINED; @@ -660,6 +661,11 @@ cfg80211_get_chan_state(struct wireless_dev *wdev, !wdev->ibss_dfs_possible) ? CHAN_MODE_SHARED : CHAN_MODE_EXCLUSIVE; + + /* consider worst-case - IBSS can try to return to the + * original user-specified channel as creator */ + if (wdev->ibss_dfs_possible) + *radar_detect |= BIT(wdev->chandef.width); return; } break; @@ -674,17 +680,26 @@ cfg80211_get_chan_state(struct wireless_dev *wdev, case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: if (wdev->cac_started) { - *chan = wdev->channel; + *chan = wdev->chandef.chan; *chanmode = CHAN_MODE_SHARED; + *radar_detect |= BIT(wdev->chandef.width); } else if (wdev->beacon_interval) { - *chan = wdev->channel; + *chan = wdev->chandef.chan; *chanmode = CHAN_MODE_SHARED; + + if (cfg80211_chandef_dfs_required(wdev->wiphy, + &wdev->chandef)) + *radar_detect |= BIT(wdev->chandef.width); } return; case NL80211_IFTYPE_MESH_POINT: if (wdev->mesh_id_len) { - *chan = wdev->channel; + *chan = wdev->chandef.chan; *chanmode = CHAN_MODE_SHARED; + + if (cfg80211_chandef_dfs_required(wdev->wiphy, + &wdev->chandef)) + *radar_detect |= BIT(wdev->chandef.width); } return; case NL80211_IFTYPE_MONITOR: diff --git a/net/wireless/core.h b/net/wireless/core.h index 8a820f9c4a76..9895ab16c051 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -443,7 +443,8 @@ static inline unsigned int elapsed_jiffies_msecs(unsigned long start) void cfg80211_get_chan_state(struct wireless_dev *wdev, struct ieee80211_channel **chan, - enum cfg80211_chan_mode *chanmode); + enum cfg80211_chan_mode *chanmode, + u8 *radar_detect); int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, struct cfg80211_chan_def *chandef); diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index e37e39c29dfb..1470b90e438f 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -122,6 +122,7 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, wdev->ibss_fixed = params->channel_fixed; wdev->ibss_dfs_possible = params->userspace_handles_dfs; + wdev->chandef = params->chandef; #ifdef CONFIG_CFG80211_WEXT wdev->wext.ibss.chandef = params->chandef; #endif @@ -205,6 +206,7 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) wdev->current_bss = NULL; wdev->ssid_len = 0; + memset(&wdev->chandef, 0, sizeof(wdev->chandef)); #ifdef CONFIG_CFG80211_WEXT if (!nowext) wdev->wext.ibss.ssid_len = 0; diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 885862447b63..d42a3fcb2f67 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -195,7 +195,7 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, if (!err) { memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len); wdev->mesh_id_len = setup->mesh_id_len; - wdev->channel = setup->chandef.chan; + wdev->chandef = setup->chandef; } return err; @@ -244,7 +244,7 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev, err = rdev_libertas_set_mesh_channel(rdev, wdev->netdev, chandef->chan); if (!err) - wdev->channel = chandef->chan; + wdev->chandef = *chandef; return err; } @@ -276,7 +276,7 @@ static int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, err = rdev_leave_mesh(rdev, dev); if (!err) { wdev->mesh_id_len = 0; - wdev->channel = NULL; + memset(&wdev->chandef, 0, sizeof(wdev->chandef)); rdev_set_qos_map(rdev, dev, NULL); } diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 52cca05044a8..d47c9d127b1e 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -772,7 +772,7 @@ void cfg80211_cac_event(struct net_device *netdev, if (WARN_ON(!wdev->cac_started)) return; - if (WARN_ON(!wdev->channel)) + if (WARN_ON(!wdev->chandef.chan)) return; switch (event) { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 0a186013728c..be091ddd43a4 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3281,7 +3281,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) if (!err) { wdev->preset_chandef = params.chandef; wdev->beacon_interval = params.beacon_interval; - wdev->channel = params.chandef.chan; + wdev->chandef = params.chandef; wdev->ssid_len = params.ssid_len; memcpy(wdev->ssid, params.ssid, wdev->ssid_len); } @@ -5797,7 +5797,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb, err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef); if (!err) { - wdev->channel = chandef.chan; + wdev->chandef = chandef; wdev->cac_started = true; wdev->cac_start_time = jiffies; } @@ -11215,7 +11215,7 @@ void cfg80211_ch_switch_notify(struct net_device *dev, wdev->iftype != NL80211_IFTYPE_MESH_POINT)) return; - wdev->channel = chandef->chan; + wdev->chandef = *chandef; wdev->preset_chandef = *chandef; nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL); } diff --git a/net/wireless/util.c b/net/wireless/util.c index 7526a4d8aa16..780b4546c9c7 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1357,7 +1357,7 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, */ mutex_lock_nested(&wdev_iter->mtx, 1); __acquire(wdev_iter->mtx); - cfg80211_get_chan_state(wdev_iter, &ch, &chmode); + cfg80211_get_chan_state(wdev_iter, &ch, &chmode, &radar_detect); wdev_unlock(wdev_iter); switch (chmode) { -- cgit v1.2.3-71-gd317 From 0059b2b142b9938118e1ed1ea630c527119425fe Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 5 Feb 2014 16:36:01 +0200 Subject: mac80211: remove unused radiotap vendor fields in ieee80211_rx_status The purpose of this housekeeping is to make some room for VHT flags. The radiotap vendor fields weren't in use. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/wcn36xx/txrx.c | 3 +- drivers/net/wireless/mac80211_hwsim.c | 26 ---------------- include/net/mac80211.h | 12 -------- net/mac80211/rx.c | 53 ++++----------------------------- 4 files changed, 6 insertions(+), 88 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/ath/wcn36xx/txrx.c b/drivers/net/wireless/ath/wcn36xx/txrx.c index b2b60e30caaf..6846f858ef62 100644 --- a/drivers/net/wireless/ath/wcn36xx/txrx.c +++ b/drivers/net/wireless/ath/wcn36xx/txrx.c @@ -57,8 +57,7 @@ int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb) RX_FLAG_MMIC_STRIPPED | RX_FLAG_DECRYPTED; - wcn36xx_dbg(WCN36XX_DBG_RX, "status.flags=%x status->vendor_radiotap_len=%x\n", - status.flag, status.vendor_radiotap_len); + wcn36xx_dbg(WCN36XX_DBG_RX, "status.flags=%x\n", status.flag); memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 6613489d1066..f7e3562542fe 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1062,32 +1062,6 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, ack = true; rx_status.mactime = now + data2->tsf_offset; -#if 0 - /* - * Don't enable this code by default as the OUI 00:00:00 - * is registered to Xerox so we shouldn't use it here, it - * might find its way into pcap files. - * Note that this code requires the headroom in the SKB - * that was allocated earlier. - */ - rx_status.vendor_radiotap_oui[0] = 0x00; - rx_status.vendor_radiotap_oui[1] = 0x00; - rx_status.vendor_radiotap_oui[2] = 0x00; - rx_status.vendor_radiotap_subns = 127; - /* - * Radiotap vendor namespaces can (and should) also be - * split into fields by using the standard radiotap - * presence bitmap mechanism. Use just BIT(0) here for - * the presence bitmap. - */ - rx_status.vendor_radiotap_bitmap = BIT(0); - /* We have 8 bytes of (dummy) data */ - rx_status.vendor_radiotap_len = 8; - /* For testing, also require it to be aligned */ - rx_status.vendor_radiotap_align = 8; - /* push the data */ - memcpy(skb_push(nskb, 8), "ABCDEFGH", 8); -#endif memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status)); ieee80211_rx_irqsafe(data2->hw, nskb); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index f844770b7fd4..452eb594dcef 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -906,21 +906,12 @@ enum mac80211_rx_flags { * @ampdu_reference: A-MPDU reference number, must be a different value for * each A-MPDU but the same for each subframe within one A-MPDU * @ampdu_delimiter_crc: A-MPDU delimiter CRC - * @vendor_radiotap_bitmap: radiotap vendor namespace presence bitmap - * @vendor_radiotap_len: radiotap vendor namespace length - * @vendor_radiotap_align: radiotap vendor namespace alignment. Note - * that the actual data must be at the start of the SKB data - * already. - * @vendor_radiotap_oui: radiotap vendor namespace OUI - * @vendor_radiotap_subns: radiotap vendor sub namespace */ struct ieee80211_rx_status { u64 mactime; u32 device_timestamp; u32 ampdu_reference; u32 flag; - u32 vendor_radiotap_bitmap; - u16 vendor_radiotap_len; u16 freq; u8 rate_idx; u8 vht_nss; @@ -931,9 +922,6 @@ struct ieee80211_rx_status { u8 chains; s8 chain_signal[IEEE80211_MAX_CHAINS]; u8 ampdu_delimiter_crc; - u8 vendor_radiotap_align; - u8 vendor_radiotap_oui[3]; - u8 vendor_radiotap_subns; }; /** diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 79a89fe9d616..b86330138d67 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -40,8 +40,6 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local, struct sk_buff *skb) { - struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); - if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) { if (likely(skb->len > FCS_LEN)) __pskb_trim(skb, skb->len - FCS_LEN); @@ -53,9 +51,6 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local, } } - if (status->vendor_radiotap_len) - __pskb_pull(skb, status->vendor_radiotap_len); - return skb; } @@ -64,14 +59,13 @@ static inline int should_drop_frame(struct sk_buff *skb, int present_fcs_len) struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr; - hdr = (void *)(skb->data + status->vendor_radiotap_len); + hdr = (void *)(skb->data); if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC | RX_FLAG_AMPDU_IS_ZEROLEN)) return 1; - if (unlikely(skb->len < 16 + present_fcs_len + - status->vendor_radiotap_len)) + if (unlikely(skb->len < 16 + present_fcs_len)) return 1; if (ieee80211_is_ctl(hdr->frame_control) && !ieee80211_is_pspoll(hdr->frame_control) && @@ -90,8 +84,6 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local, len = sizeof(struct ieee80211_radiotap_header) + 8; /* allocate extra bitmaps */ - if (status->vendor_radiotap_len) - len += 4; if (status->chains) len += 4 * hweight8(status->chains); @@ -127,18 +119,6 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local, len += 2 * hweight8(status->chains); } - if (status->vendor_radiotap_len) { - if (WARN_ON_ONCE(status->vendor_radiotap_align == 0)) - status->vendor_radiotap_align = 1; - /* align standard part of vendor namespace */ - len = ALIGN(len, 2); - /* allocate standard part of vendor namespace */ - len += 6; - /* align vendor-defined part */ - len = ALIGN(len, status->vendor_radiotap_align); - /* vendor-defined part is already in skb */ - } - return len; } @@ -172,7 +152,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, it_present = &rthdr->it_present; /* radiotap header, set always present flags */ - rthdr->it_len = cpu_to_le16(rtap_len + status->vendor_radiotap_len); + rthdr->it_len = cpu_to_le16(rtap_len); it_present_val = BIT(IEEE80211_RADIOTAP_FLAGS) | BIT(IEEE80211_RADIOTAP_CHANNEL) | BIT(IEEE80211_RADIOTAP_RX_FLAGS); @@ -190,14 +170,6 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, BIT(IEEE80211_RADIOTAP_DBM_ANTSIGNAL); } - if (status->vendor_radiotap_len) { - it_present_val |= BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE) | - BIT(IEEE80211_RADIOTAP_EXT); - put_unaligned_le32(it_present_val, it_present); - it_present++; - it_present_val = status->vendor_radiotap_bitmap; - } - put_unaligned_le32(it_present_val, it_present); pos = (void *)(it_present + 1); @@ -383,21 +355,6 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, *pos++ = status->chain_signal[chain]; *pos++ = chain; } - - if (status->vendor_radiotap_len) { - /* ensure 2 byte alignment for the vendor field as required */ - if ((pos - (u8 *)rthdr) & 1) - *pos++ = 0; - *pos++ = status->vendor_radiotap_oui[0]; - *pos++ = status->vendor_radiotap_oui[1]; - *pos++ = status->vendor_radiotap_oui[2]; - *pos++ = status->vendor_radiotap_subns; - put_unaligned_le16(status->vendor_radiotap_len, pos); - pos += 2; - /* align the actual payload as requested */ - while ((pos - (u8 *)rthdr) & (status->vendor_radiotap_align - 1)) - *pos++ = 0; - } } /* @@ -428,8 +385,8 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) present_fcs_len = FCS_LEN; - /* ensure hdr->frame_control and vendor radiotap data are in skb head */ - if (!pskb_may_pull(origskb, 2 + status->vendor_radiotap_len)) { + /* ensure hdr->frame_control is in skb head */ + if (!pskb_may_pull(origskb, 2)) { dev_kfree_skb(origskb); return NULL; } -- cgit v1.2.3-71-gd317 From 1b8d242adbea881658071efc31d2c0dcf8a44fb7 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 5 Feb 2014 16:37:11 +0200 Subject: mac80211: move VHT related RX_FLAG to another variable ieee80211_rx_status.flags is full. Define a new vht_flag variable to be able to set more VHT related flags and make room in flags. Signed-off-by: Emmanuel Grumbach Acked-by: Kalle Valo [ath10k] Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath10k/txrx.c | 4 ++-- drivers/net/wireless/iwlwifi/mvm/rx.c | 4 ++-- include/net/mac80211.h | 23 +++++++++++++++++------ net/mac80211/cfg.c | 6 +++--- net/mac80211/rx.c | 9 +++++---- net/mac80211/sta_info.h | 2 ++ net/mac80211/util.c | 6 +++--- 7 files changed, 34 insertions(+), 20 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c index 74f45fa6f428..27f20e0510f7 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.c +++ b/drivers/net/wireless/ath/ath10k/txrx.c @@ -204,7 +204,7 @@ static void process_rx_rates(struct ath10k *ar, struct htt_rx_info *info, break; /* 80MHZ */ case 2: - status->flag |= RX_FLAG_80MHZ; + status->vht_flag |= RX_VHT_FLAG_80MHZ; } status->flag |= RX_FLAG_VHT; @@ -266,7 +266,7 @@ void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info) status->flag & RX_FLAG_HT ? "ht" : "", status->flag & RX_FLAG_VHT ? "vht" : "", status->flag & RX_FLAG_40MHZ ? "40" : "", - status->flag & RX_FLAG_80MHZ ? "80" : "", + status->vht_flag & RX_VHT_FLAG_80MHZ ? "80" : "", status->flag & RX_FLAG_SHORT_GI ? "sgi " : "", status->rate_idx, status->vht_nss, diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c index a85b60f7e67e..c67d6375e622 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/iwlwifi/mvm/rx.c @@ -364,10 +364,10 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, rx_status.flag |= RX_FLAG_40MHZ; break; case RATE_MCS_CHAN_WIDTH_80: - rx_status.flag |= RX_FLAG_80MHZ; + rx_status.vht_flag |= RX_VHT_FLAG_80MHZ; break; case RATE_MCS_CHAN_WIDTH_160: - rx_status.flag |= RX_FLAG_160MHZ; + rx_status.vht_flag |= RX_VHT_FLAG_160MHZ; break; } if (rate_n_flags & RATE_MCS_SGI_MSK) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 452eb594dcef..a119da52665f 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -808,9 +808,6 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info) * @RX_FLAG_HT: HT MCS was used and rate_idx is MCS index * @RX_FLAG_VHT: VHT MCS was used and rate_index is MCS index * @RX_FLAG_40MHZ: HT40 (40 MHz) was used - * @RX_FLAG_80MHZ: 80 MHz was used - * @RX_FLAG_80P80MHZ: 80+80 MHz was used - * @RX_FLAG_160MHZ: 160 MHz was used * @RX_FLAG_SHORT_GI: Short guard interval was used * @RX_FLAG_NO_SIGNAL_VAL: The signal strength value is not present. * Valid only for data frames (mainly A-MPDU) @@ -866,9 +863,6 @@ enum mac80211_rx_flags { RX_FLAG_AMPDU_DELIM_CRC_KNOWN = BIT(20), RX_FLAG_MACTIME_END = BIT(21), RX_FLAG_VHT = BIT(22), - RX_FLAG_80MHZ = BIT(23), - RX_FLAG_80P80MHZ = BIT(24), - RX_FLAG_160MHZ = BIT(25), RX_FLAG_STBC_MASK = BIT(26) | BIT(27), RX_FLAG_10MHZ = BIT(28), RX_FLAG_5MHZ = BIT(29), @@ -877,6 +871,21 @@ enum mac80211_rx_flags { #define RX_FLAG_STBC_SHIFT 26 +/** + * enum mac80211_rx_vht_flags - receive VHT flags + * + * These flags are used with the @vht_flag member of + * &struct ieee80211_rx_status. + * @RX_VHT_FLAG_80MHZ: 80 MHz was used + * @RX_VHT_FLAG_80P80MHZ: 80+80 MHz was used + * @RX_VHT_FLAG_160MHZ: 160 MHz was used + */ +enum mac80211_rx_vht_flags { + RX_VHT_FLAG_80MHZ = BIT(0), + RX_VHT_FLAG_80P80MHZ = BIT(1), + RX_VHT_FLAG_160MHZ = BIT(2), +}; + /** * struct ieee80211_rx_status - receive status * @@ -902,6 +911,7 @@ enum mac80211_rx_flags { * HT or VHT is used (%RX_FLAG_HT/%RX_FLAG_VHT) * @vht_nss: number of streams (VHT only) * @flag: %RX_FLAG_* + * @vht_flag: %RX_VHT_FLAG_* * @rx_flags: internal RX flags for mac80211 * @ampdu_reference: A-MPDU reference number, must be a different value for * each A-MPDU but the same for each subframe within one A-MPDU @@ -913,6 +923,7 @@ struct ieee80211_rx_status { u32 ampdu_reference; u32 flag; u16 freq; + u8 vht_flag; u8 rate_idx; u8 vht_nss; u8 rx_flags; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 8192093f1e8b..6973ccdd230b 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -451,11 +451,11 @@ void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo) rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI) rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; - if (sta->last_rx_rate_flag & RX_FLAG_80MHZ) + if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_80MHZ) rinfo->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH; - if (sta->last_rx_rate_flag & RX_FLAG_80P80MHZ) + if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_80P80MHZ) rinfo->flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH; - if (sta->last_rx_rate_flag & RX_FLAG_160MHZ) + if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_160MHZ) rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH; } diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index b86330138d67..e81cab3ca157 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -321,7 +321,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT); /* known field - how to handle 80+80? */ - if (status->flag & RX_FLAG_80P80MHZ) + if (status->vht_flag & RX_VHT_FLAG_80P80MHZ) known &= ~IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH; put_unaligned_le16(known, pos); pos += 2; @@ -330,11 +330,11 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, *pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI; pos++; /* bandwidth */ - if (status->flag & RX_FLAG_80MHZ) + if (status->vht_flag & RX_VHT_FLAG_80MHZ) *pos++ = 4; - else if (status->flag & RX_FLAG_80P80MHZ) + else if (status->vht_flag & RX_VHT_FLAG_80P80MHZ) *pos++ = 0; /* marked not known above */ - else if (status->flag & RX_FLAG_160MHZ) + else if (status->vht_flag & RX_VHT_FLAG_160MHZ) *pos++ = 11; else if (status->flag & RX_FLAG_40MHZ) *pos++ = 1; @@ -1218,6 +1218,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) if (ieee80211_is_data(hdr->frame_control)) { sta->last_rx_rate_idx = status->rate_idx; sta->last_rx_rate_flag = status->flag; + sta->last_rx_rate_vht_flag = status->vht_flag; sta->last_rx_rate_vht_nss = status->vht_nss; } } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index d77ff7090630..d4d85de0d75d 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -261,6 +261,7 @@ struct ieee80211_tx_latency_stat { * "the" transmit rate * @last_rx_rate_idx: rx status rate index of the last data packet * @last_rx_rate_flag: rx status flag of the last data packet + * @last_rx_rate_vht_flag: rx status vht flag of the last data packet * @last_rx_rate_vht_nss: rx status nss of last data packet * @lock: used for locking all fields that require locking, see comments * in the header file. @@ -397,6 +398,7 @@ struct sta_info { struct ieee80211_tx_rate last_tx_rate; int last_rx_rate_idx; u32 last_rx_rate_flag; + u32 last_rx_rate_vht_flag; u8 last_rx_rate_vht_nss; u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1]; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index caa0cd4f1926..d842af5c8a95 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2298,11 +2298,11 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, ri.nss = status->vht_nss; if (status->flag & RX_FLAG_40MHZ) ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; - if (status->flag & RX_FLAG_80MHZ) + if (status->vht_flag & RX_VHT_FLAG_80MHZ) ri.flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH; - if (status->flag & RX_FLAG_80P80MHZ) + if (status->vht_flag & RX_VHT_FLAG_80P80MHZ) ri.flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH; - if (status->flag & RX_FLAG_160MHZ) + if (status->vht_flag & RX_VHT_FLAG_160MHZ) ri.flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH; if (status->flag & RX_FLAG_SHORT_GI) ri.flags |= RATE_INFO_FLAGS_SHORT_GI; -- cgit v1.2.3-71-gd317 From 63c361f5114d81db789f8f5671c76c228c35b021 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 5 Feb 2014 12:48:53 +0200 Subject: mac80211: propagate STBC / LDPC flags to radiotap This capabilities weren't propagated to the radiotap header. We don't set here the VHT_KNOWN / MCS_HAVE flag because not all the low level drivers will know how to properly flag the frames, hence the low level driver will be in charge of setting IEEE80211_RADIOTAP_MCS_HAVE_FEC, IEEE80211_RADIOTAP_MCS_HAVE_STBC and / or IEEE80211_RADIOTAP_VHT_KNOWN_STBC according to its capabilities. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- include/net/ieee80211_radiotap.h | 4 ++++ include/net/mac80211.h | 2 ++ net/mac80211/rx.c | 7 +++++++ 3 files changed, 13 insertions(+) (limited to 'include/net') diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h index 8b5b71433297..b0fd9476c538 100644 --- a/include/net/ieee80211_radiotap.h +++ b/include/net/ieee80211_radiotap.h @@ -316,6 +316,10 @@ enum ieee80211_radiotap_type { #define IEEE80211_RADIOTAP_VHT_FLAG_LDPC_EXTRA_OFDM_SYM 0x10 #define IEEE80211_RADIOTAP_VHT_FLAG_BEAMFORMED 0x20 +#define IEEE80211_RADIOTAP_CODING_LDPC_USER0 0x01 +#define IEEE80211_RADIOTAP_CODING_LDPC_USER1 0x02 +#define IEEE80211_RADIOTAP_CODING_LDPC_USER2 0x04 +#define IEEE80211_RADIOTAP_CODING_LDPC_USER3 0x08 /* helpers */ static inline int ieee80211_get_radiotap_len(unsigned char *data) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index a119da52665f..4f0f29dce0aa 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -827,6 +827,7 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info) * on this subframe * @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC * is stored in the @ampdu_delimiter_crc field) + * @RX_FLAG_LDPC: LDPC was used * @RX_FLAG_STBC_MASK: STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3 * @RX_FLAG_10MHZ: 10 MHz (half channel) was used * @RX_FLAG_5MHZ: 5 MHz (quarter channel) was used @@ -863,6 +864,7 @@ enum mac80211_rx_flags { RX_FLAG_AMPDU_DELIM_CRC_KNOWN = BIT(20), RX_FLAG_MACTIME_END = BIT(21), RX_FLAG_VHT = BIT(22), + RX_FLAG_LDPC = BIT(23), RX_FLAG_STBC_MASK = BIT(26) | BIT(27), RX_FLAG_10MHZ = BIT(28), RX_FLAG_5MHZ = BIT(29), diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index e81cab3ca157..593062109c50 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -279,6 +279,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, *pos |= IEEE80211_RADIOTAP_MCS_BW_40; if (status->flag & RX_FLAG_HT_GF) *pos |= IEEE80211_RADIOTAP_MCS_FMT_GF; + if (status->flag & RX_FLAG_LDPC) + *pos |= IEEE80211_RADIOTAP_MCS_FEC_LDPC; stbc = (status->flag & RX_FLAG_STBC_MASK) >> RX_FLAG_STBC_SHIFT; *pos |= stbc << IEEE80211_RADIOTAP_MCS_STBC_SHIFT; pos++; @@ -328,6 +330,9 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, /* flags */ if (status->flag & RX_FLAG_SHORT_GI) *pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI; + /* in VHT, STBC is binary */ + if (status->flag & RX_FLAG_STBC_MASK) + *pos |= IEEE80211_RADIOTAP_VHT_FLAG_STBC; pos++; /* bandwidth */ if (status->vht_flag & RX_VHT_FLAG_80MHZ) @@ -344,6 +349,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, *pos = (status->rate_idx << 4) | status->vht_nss; pos += 4; /* coding field */ + if (status->flag & RX_FLAG_LDPC) + *pos |= IEEE80211_RADIOTAP_CODING_LDPC_USER0; pos++; /* group ID */ pos++; -- cgit v1.2.3-71-gd317 From 448cd2e248732326632957e52ea9c44729affcb2 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 11 Feb 2014 12:30:18 +0200 Subject: mac80211: reset probe_send_count also in HW_CONNECTION_MONITOR case In case of beacon_loss with IEEE80211_HW_CONNECTION_MONITOR device, mac80211 probes the ap (and disconnects on timeout) but ignores the ack. If we already got an ack, there's no reason to continue disconnecting. this can help devices that supports IEEE80211_HW_CONNECTION_MONITOR only partially (e.g. take care of keep alives, but does not probe the ap. In case the device wants to disconnect without probing, it can just call ieee80211_connection_loss. Signed-off-by: Eliad Peller Signed-off-by: Johannes Berg --- include/net/mac80211.h | 2 -- net/mac80211/mlme.c | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 4f0f29dce0aa..4005c5b4e3b4 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1507,8 +1507,6 @@ struct ieee80211_tx_control { * @IEEE80211_HW_CONNECTION_MONITOR: * The hardware performs its own connection monitoring, including * periodic keep-alives to the AP and probing the AP on beacon loss. - * When this flag is set, signaling beacon-loss will cause an immediate - * change to disassociated state. * * @IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC: * This device needs to get data from beacon before association (i.e. diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 61604834b914..b9432b575444 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -131,13 +131,13 @@ void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata) if (unlikely(!sdata->u.mgd.associated)) return; + ifmgd->probe_send_count = 0; + if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) return; mod_timer(&sdata->u.mgd.conn_mon_timer, round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME)); - - ifmgd->probe_send_count = 0; } static int ecw2cw(int ecw) -- cgit v1.2.3-71-gd317 From ca925cf1534ebcec332c08719a7dee6ee1782ce4 Mon Sep 17 00:00:00 2001 From: Fan Du Date: Sat, 18 Jan 2014 09:55:27 +0800 Subject: flowcache: Make flow cache name space aware Inserting a entry into flowcache, or flushing flowcache should be based on per net scope. The reason to do so is flushing operation from fat netns crammed with flow entries will also making the slim netns with only a few flow cache entries go away in original implementation. Since flowcache is tightly coupled with IPsec, so it would be easier to put flow cache global parameters into xfrm namespace part. And one last thing needs to do is bumping flow cache genid, and flush flow cache should also be made in per net style. Signed-off-by: Fan Du Signed-off-by: Steffen Klassert --- include/net/flow.h | 5 +- include/net/flowcache.h | 25 ++++++++ include/net/netns/xfrm.h | 11 ++++ net/core/flow.c | 127 ++++++++++++++++++---------------------- net/xfrm/xfrm_policy.c | 7 ++- security/selinux/include/xfrm.h | 5 +- 6 files changed, 103 insertions(+), 77 deletions(-) create mode 100644 include/net/flowcache.h (limited to 'include/net') diff --git a/include/net/flow.h b/include/net/flow.h index d23e7fa2042e..bee3741e5a6f 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -218,9 +218,10 @@ struct flow_cache_object *flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir, flow_resolve_t resolver, void *ctx); +int flow_cache_init(struct net *net); -void flow_cache_flush(void); -void flow_cache_flush_deferred(void); +void flow_cache_flush(struct net *net); +void flow_cache_flush_deferred(struct net *net); extern atomic_t flow_cache_genid; #endif diff --git a/include/net/flowcache.h b/include/net/flowcache.h new file mode 100644 index 000000000000..c8f665ec6e0d --- /dev/null +++ b/include/net/flowcache.h @@ -0,0 +1,25 @@ +#ifndef _NET_FLOWCACHE_H +#define _NET_FLOWCACHE_H + +#include +#include +#include +#include + +struct flow_cache_percpu { + struct hlist_head *hash_table; + int hash_count; + u32 hash_rnd; + int hash_rnd_recalc; + struct tasklet_struct flush_tasklet; +}; + +struct flow_cache { + u32 hash_shift; + struct flow_cache_percpu __percpu *percpu; + struct notifier_block hotcpu_notifier; + int low_watermark; + int high_watermark; + struct timer_list rnd_timer; +}; +#endif /* _NET_FLOWCACHE_H */ diff --git a/include/net/netns/xfrm.h b/include/net/netns/xfrm.h index 1006a265beb3..52d0086d55d3 100644 --- a/include/net/netns/xfrm.h +++ b/include/net/netns/xfrm.h @@ -6,6 +6,7 @@ #include #include #include +#include struct ctl_table_header; @@ -61,6 +62,16 @@ struct netns_xfrm { spinlock_t xfrm_policy_sk_bundle_lock; rwlock_t xfrm_policy_lock; struct mutex xfrm_cfg_mutex; + + /* flow cache part */ + struct flow_cache flow_cache_global; + struct kmem_cache *flow_cachep; + atomic_t flow_cache_genid; + struct list_head flow_cache_gc_list; + spinlock_t flow_cache_gc_lock; + struct work_struct flow_cache_gc_work; + struct work_struct flow_cache_flush_work; + struct mutex flow_flush_sem; }; #endif diff --git a/net/core/flow.c b/net/core/flow.c index dfa602ceb8cd..344a184011fd 100644 --- a/net/core/flow.c +++ b/net/core/flow.c @@ -24,6 +24,7 @@ #include #include #include +#include struct flow_cache_entry { union { @@ -38,37 +39,12 @@ struct flow_cache_entry { struct flow_cache_object *object; }; -struct flow_cache_percpu { - struct hlist_head *hash_table; - int hash_count; - u32 hash_rnd; - int hash_rnd_recalc; - struct tasklet_struct flush_tasklet; -}; - struct flow_flush_info { struct flow_cache *cache; atomic_t cpuleft; struct completion completion; }; -struct flow_cache { - u32 hash_shift; - struct flow_cache_percpu __percpu *percpu; - struct notifier_block hotcpu_notifier; - int low_watermark; - int high_watermark; - struct timer_list rnd_timer; -}; - -atomic_t flow_cache_genid = ATOMIC_INIT(0); -EXPORT_SYMBOL(flow_cache_genid); -static struct flow_cache flow_cache_global; -static struct kmem_cache *flow_cachep __read_mostly; - -static DEFINE_SPINLOCK(flow_cache_gc_lock); -static LIST_HEAD(flow_cache_gc_list); - #define flow_cache_hash_size(cache) (1 << (cache)->hash_shift) #define FLOW_HASH_RND_PERIOD (10 * 60 * HZ) @@ -84,46 +60,50 @@ static void flow_cache_new_hashrnd(unsigned long arg) add_timer(&fc->rnd_timer); } -static int flow_entry_valid(struct flow_cache_entry *fle) +static int flow_entry_valid(struct flow_cache_entry *fle, + struct netns_xfrm *xfrm) { - if (atomic_read(&flow_cache_genid) != fle->genid) + if (atomic_read(&xfrm->flow_cache_genid) != fle->genid) return 0; if (fle->object && !fle->object->ops->check(fle->object)) return 0; return 1; } -static void flow_entry_kill(struct flow_cache_entry *fle) +static void flow_entry_kill(struct flow_cache_entry *fle, + struct netns_xfrm *xfrm) { if (fle->object) fle->object->ops->delete(fle->object); - kmem_cache_free(flow_cachep, fle); + kmem_cache_free(xfrm->flow_cachep, fle); } static void flow_cache_gc_task(struct work_struct *work) { struct list_head gc_list; struct flow_cache_entry *fce, *n; + struct netns_xfrm *xfrm = container_of(work, struct netns_xfrm, + flow_cache_gc_work); INIT_LIST_HEAD(&gc_list); - spin_lock_bh(&flow_cache_gc_lock); - list_splice_tail_init(&flow_cache_gc_list, &gc_list); - spin_unlock_bh(&flow_cache_gc_lock); + spin_lock_bh(&xfrm->flow_cache_gc_lock); + list_splice_tail_init(&xfrm->flow_cache_gc_list, &gc_list); + spin_unlock_bh(&xfrm->flow_cache_gc_lock); list_for_each_entry_safe(fce, n, &gc_list, u.gc_list) - flow_entry_kill(fce); + flow_entry_kill(fce, xfrm); } -static DECLARE_WORK(flow_cache_gc_work, flow_cache_gc_task); static void flow_cache_queue_garbage(struct flow_cache_percpu *fcp, - int deleted, struct list_head *gc_list) + int deleted, struct list_head *gc_list, + struct netns_xfrm *xfrm) { if (deleted) { fcp->hash_count -= deleted; - spin_lock_bh(&flow_cache_gc_lock); - list_splice_tail(gc_list, &flow_cache_gc_list); - spin_unlock_bh(&flow_cache_gc_lock); - schedule_work(&flow_cache_gc_work); + spin_lock_bh(&xfrm->flow_cache_gc_lock); + list_splice_tail(gc_list, &xfrm->flow_cache_gc_list); + spin_unlock_bh(&xfrm->flow_cache_gc_lock); + schedule_work(&xfrm->flow_cache_gc_work); } } @@ -135,6 +115,8 @@ static void __flow_cache_shrink(struct flow_cache *fc, struct hlist_node *tmp; LIST_HEAD(gc_list); int i, deleted = 0; + struct netns_xfrm *xfrm = container_of(fc, struct netns_xfrm, + flow_cache_global); for (i = 0; i < flow_cache_hash_size(fc); i++) { int saved = 0; @@ -142,7 +124,7 @@ static void __flow_cache_shrink(struct flow_cache *fc, hlist_for_each_entry_safe(fle, tmp, &fcp->hash_table[i], u.hlist) { if (saved < shrink_to && - flow_entry_valid(fle)) { + flow_entry_valid(fle, xfrm)) { saved++; } else { deleted++; @@ -152,7 +134,7 @@ static void __flow_cache_shrink(struct flow_cache *fc, } } - flow_cache_queue_garbage(fcp, deleted, &gc_list); + flow_cache_queue_garbage(fcp, deleted, &gc_list, xfrm); } static void flow_cache_shrink(struct flow_cache *fc, @@ -208,7 +190,7 @@ struct flow_cache_object * flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir, flow_resolve_t resolver, void *ctx) { - struct flow_cache *fc = &flow_cache_global; + struct flow_cache *fc = &net->xfrm.flow_cache_global; struct flow_cache_percpu *fcp; struct flow_cache_entry *fle, *tfle; struct flow_cache_object *flo; @@ -248,7 +230,7 @@ flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir, if (fcp->hash_count > fc->high_watermark) flow_cache_shrink(fc, fcp); - fle = kmem_cache_alloc(flow_cachep, GFP_ATOMIC); + fle = kmem_cache_alloc(net->xfrm.flow_cachep, GFP_ATOMIC); if (fle) { fle->net = net; fle->family = family; @@ -258,7 +240,7 @@ flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir, hlist_add_head(&fle->u.hlist, &fcp->hash_table[hash]); fcp->hash_count++; } - } else if (likely(fle->genid == atomic_read(&flow_cache_genid))) { + } else if (likely(fle->genid == atomic_read(&net->xfrm.flow_cache_genid))) { flo = fle->object; if (!flo) goto ret_object; @@ -279,7 +261,7 @@ nocache: } flo = resolver(net, key, family, dir, flo, ctx); if (fle) { - fle->genid = atomic_read(&flow_cache_genid); + fle->genid = atomic_read(&net->xfrm.flow_cache_genid); if (!IS_ERR(flo)) fle->object = flo; else @@ -303,12 +285,14 @@ static void flow_cache_flush_tasklet(unsigned long data) struct hlist_node *tmp; LIST_HEAD(gc_list); int i, deleted = 0; + struct netns_xfrm *xfrm = container_of(fc, struct netns_xfrm, + flow_cache_global); fcp = this_cpu_ptr(fc->percpu); for (i = 0; i < flow_cache_hash_size(fc); i++) { hlist_for_each_entry_safe(fle, tmp, &fcp->hash_table[i], u.hlist) { - if (flow_entry_valid(fle)) + if (flow_entry_valid(fle, xfrm)) continue; deleted++; @@ -317,7 +301,7 @@ static void flow_cache_flush_tasklet(unsigned long data) } } - flow_cache_queue_garbage(fcp, deleted, &gc_list); + flow_cache_queue_garbage(fcp, deleted, &gc_list, xfrm); if (atomic_dec_and_test(&info->cpuleft)) complete(&info->completion); @@ -351,10 +335,9 @@ static void flow_cache_flush_per_cpu(void *data) tasklet_schedule(tasklet); } -void flow_cache_flush(void) +void flow_cache_flush(struct net *net) { struct flow_flush_info info; - static DEFINE_MUTEX(flow_flush_sem); cpumask_var_t mask; int i, self; @@ -365,8 +348,8 @@ void flow_cache_flush(void) /* Don't want cpus going down or up during this. */ get_online_cpus(); - mutex_lock(&flow_flush_sem); - info.cache = &flow_cache_global; + mutex_lock(&net->xfrm.flow_flush_sem); + info.cache = &net->xfrm.flow_cache_global; for_each_online_cpu(i) if (!flow_cache_percpu_empty(info.cache, i)) cpumask_set_cpu(i, mask); @@ -386,21 +369,23 @@ void flow_cache_flush(void) wait_for_completion(&info.completion); done: - mutex_unlock(&flow_flush_sem); + mutex_unlock(&net->xfrm.flow_flush_sem); put_online_cpus(); free_cpumask_var(mask); } static void flow_cache_flush_task(struct work_struct *work) { - flow_cache_flush(); -} + struct netns_xfrm *xfrm = container_of(work, struct netns_xfrm, + flow_cache_gc_work); + struct net *net = container_of(xfrm, struct net, xfrm); -static DECLARE_WORK(flow_cache_flush_work, flow_cache_flush_task); + flow_cache_flush(net); +} -void flow_cache_flush_deferred(void) +void flow_cache_flush_deferred(struct net *net) { - schedule_work(&flow_cache_flush_work); + schedule_work(&net->xfrm.flow_cache_flush_work); } static int flow_cache_cpu_prepare(struct flow_cache *fc, int cpu) @@ -425,7 +410,8 @@ static int flow_cache_cpu(struct notifier_block *nfb, unsigned long action, void *hcpu) { - struct flow_cache *fc = container_of(nfb, struct flow_cache, hotcpu_notifier); + struct flow_cache *fc = container_of(nfb, struct flow_cache, + hotcpu_notifier); int res, cpu = (unsigned long) hcpu; struct flow_cache_percpu *fcp = per_cpu_ptr(fc->percpu, cpu); @@ -444,9 +430,20 @@ static int flow_cache_cpu(struct notifier_block *nfb, return NOTIFY_OK; } -static int __init flow_cache_init(struct flow_cache *fc) +int flow_cache_init(struct net *net) { int i; + struct flow_cache *fc = &net->xfrm.flow_cache_global; + + /* Initialize per-net flow cache global variables here */ + net->xfrm.flow_cachep = kmem_cache_create("flow_cache", + sizeof(struct flow_cache_entry), + 0, SLAB_PANIC, NULL); + spin_lock_init(&net->xfrm.flow_cache_gc_lock); + INIT_LIST_HEAD(&net->xfrm.flow_cache_gc_list); + INIT_WORK(&net->xfrm.flow_cache_gc_work, flow_cache_gc_task); + INIT_WORK(&net->xfrm.flow_cache_flush_work, flow_cache_flush_task); + mutex_init(&net->xfrm.flow_flush_sem); fc->hash_shift = 10; fc->low_watermark = 2 * flow_cache_hash_size(fc); @@ -484,14 +481,4 @@ err: return -ENOMEM; } - -static int __init flow_cache_init_global(void) -{ - flow_cachep = kmem_cache_create("flow_cache", - sizeof(struct flow_cache_entry), - 0, SLAB_PANIC, NULL); - - return flow_cache_init(&flow_cache_global); -} - -module_init(flow_cache_init_global); +EXPORT_SYMBOL(flow_cache_init); diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 4b98b25793c5..2232c6f26aff 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -661,7 +661,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl) hlist_add_head(&policy->bydst, chain); xfrm_pol_hold(policy); net->xfrm.policy_count[dir]++; - atomic_inc(&flow_cache_genid); + atomic_inc(&net->xfrm.flow_cache_genid); /* After previous checking, family can either be AF_INET or AF_INET6 */ if (policy->family == AF_INET) @@ -2567,14 +2567,14 @@ static void __xfrm_garbage_collect(struct net *net) void xfrm_garbage_collect(struct net *net) { - flow_cache_flush(); + flow_cache_flush(net); __xfrm_garbage_collect(net); } EXPORT_SYMBOL(xfrm_garbage_collect); static void xfrm_garbage_collect_deferred(struct net *net) { - flow_cache_flush_deferred(); + flow_cache_flush_deferred(net); __xfrm_garbage_collect(net); } @@ -2947,6 +2947,7 @@ static int __net_init xfrm_net_init(struct net *net) spin_lock_init(&net->xfrm.xfrm_policy_sk_bundle_lock); mutex_init(&net->xfrm.xfrm_cfg_mutex); + flow_cache_init(net); return 0; out_sysctl: diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h index 48c3cc94c168..dfe3fda7e5f7 100644 --- a/security/selinux/include/xfrm.h +++ b/security/selinux/include/xfrm.h @@ -45,10 +45,11 @@ static inline void selinux_xfrm_notify_policyload(void) { struct net *net; - atomic_inc(&flow_cache_genid); rtnl_lock(); - for_each_net(net) + for_each_net(net) { + atomic_inc(&net->xfrm.flow_cache_genid); rt_genid_bump_all(net); + } rtnl_unlock(); } #else -- cgit v1.2.3-71-gd317 From 06d181a8fd58031db9c114d920b40d8820380a6e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 4 Feb 2014 20:51:09 +0100 Subject: mac80211: add NAPI support back NAPI was originally added to mac80211 a long time ago (by John in commit 4e6cbfd09c66 in July 2010), but then removed years later (by Stanislaw in commit 30c97120c6c7 in February 2013). No driver ever used it, so that was fine. Now I'm adding support for NAPI to our driver, so add some code to mac80211 again to support NAPI. John was originally wrapping some (but not nearly all NAPI-related functions), but that doesn't scale very well with the number of functions that are there, some of which are even only inlines. Thus, instead of doing that, let the drivers manage the NAPI struct, except for napi_add() which is needed so mac80211 knows how to call napi_gro_receive(). Also remove some no longer needed definitions that were left when NAPI support was removed. Reviewed-by: Emmanuel Grumbach Reviewed-by: Eyal Shapira Signed-off-by: Johannes Berg --- include/net/mac80211.h | 34 +++++++++++++--------------------- net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/main.c | 12 ++++++++++++ net/mac80211/rx.c | 5 ++++- 4 files changed, 31 insertions(+), 22 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 4005c5b4e3b4..2d4d31212eed 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1642,10 +1642,6 @@ enum ieee80211_hw_flags { * the hw can report back. * @max_rate_tries: maximum number of tries for each stage * - * @napi_weight: weight used for NAPI polling. You must specify an - * appropriate value here if a napi_poll operation is provided - * by your driver. - * * @max_rx_aggregation_subframes: maximum buffer size (number of * sub-frames) to be used for A-MPDU block ack receiver * aggregation. @@ -1699,7 +1695,6 @@ struct ieee80211_hw { int vif_data_size; int sta_data_size; int chanctx_data_size; - int napi_weight; u16 queues; u16 max_listen_interval; s8 max_signal; @@ -2622,8 +2617,6 @@ enum ieee80211_roc_type { * callback. They must then call ieee80211_chswitch_done() to indicate * completion of the channel switch. * - * @napi_poll: Poll Rx queue for incoming data frames. - * * @set_antenna: Set antenna configuration (tx_ant, rx_ant) on the device. * Parameters are bitmaps of allowed antennas to use for TX/RX. Drivers may * reject TX/RX mask combinations they cannot support by returning -EINVAL @@ -2882,7 +2875,6 @@ struct ieee80211_ops { void (*flush)(struct ieee80211_hw *hw, u32 queues, bool drop); void (*channel_switch)(struct ieee80211_hw *hw, struct ieee80211_channel_switch *ch_switch); - int (*napi_poll)(struct ieee80211_hw *hw, int budget); int (*set_antenna)(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant); int (*get_antenna)(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant); @@ -3164,21 +3156,21 @@ void ieee80211_free_hw(struct ieee80211_hw *hw); */ void ieee80211_restart_hw(struct ieee80211_hw *hw); -/** ieee80211_napi_schedule - schedule NAPI poll - * - * Use this function to schedule NAPI polling on a device. - * - * @hw: the hardware to start polling - */ -void ieee80211_napi_schedule(struct ieee80211_hw *hw); - -/** ieee80211_napi_complete - complete NAPI polling - * - * Use this function to finish NAPI polling on a device. +/** + * ieee80211_napi_add - initialize mac80211 NAPI context + * @hw: the hardware to initialize the NAPI context on + * @napi: the NAPI context to initialize + * @napi_dev: dummy NAPI netdevice, here to not waste the space if the + * driver doesn't use NAPI + * @poll: poll function + * @weight: default weight * - * @hw: the hardware to stop polling + * See also netif_napi_add(). */ -void ieee80211_napi_complete(struct ieee80211_hw *hw); +void ieee80211_napi_add(struct ieee80211_hw *hw, struct napi_struct *napi, + struct net_device *napi_dev, + int (*poll)(struct napi_struct *, int), + int weight); /** * ieee80211_rx - receive frame diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 0014b5396ce5..8603dfb52b3a 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1242,6 +1242,8 @@ struct ieee80211_local { struct ieee80211_sub_if_data __rcu *p2p_sdata; + struct napi_struct *napi; + /* virtual monitor interface */ struct ieee80211_sub_if_data __rcu *monitor_sdata; struct cfg80211_chan_def monitor_chandef; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 1f7d8422d62d..b055f6a55c68 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1076,6 +1076,18 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) } EXPORT_SYMBOL(ieee80211_register_hw); +void ieee80211_napi_add(struct ieee80211_hw *hw, struct napi_struct *napi, + struct net_device *napi_dev, + int (*poll)(struct napi_struct *, int), + int weight) +{ + struct ieee80211_local *local = hw_to_local(hw); + + netif_napi_add(napi_dev, napi, poll, weight); + local->napi = napi; +} +EXPORT_SYMBOL_GPL(ieee80211_napi_add); + void ieee80211_unregister_hw(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 593062109c50..58e4b7052d17 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1954,7 +1954,10 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx) /* deliver to local stack */ skb->protocol = eth_type_trans(skb, dev); memset(skb->cb, 0, sizeof(skb->cb)); - netif_receive_skb(skb); + if (rx->local->napi) + napi_gro_receive(rx->local->napi, skb); + else + netif_receive_skb(skb); } if (xmit_skb) { -- cgit v1.2.3-71-gd317 From 86062033feb8a1692f7a3d570c652f1b4a4b4b52 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Tue, 11 Feb 2014 17:07:31 -0800 Subject: net_sched: act: hide struct tcf_common from API Now we can totally hide it from modules. tcf_hash_*() API's will operate on struct tc_action, modules don't need to care about the details. 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 +++++++-------- include/net/tc_act/tc_csum.h | 4 ++-- include/net/tc_act/tc_defact.h | 4 ++-- include/net/tc_act/tc_gact.h | 4 ++-- include/net/tc_act/tc_ipt.h | 4 ++-- include/net/tc_act/tc_mirred.h | 4 ++-- include/net/tc_act/tc_nat.h | 4 ++-- include/net/tc_act/tc_pedit.h | 4 ++-- include/net/tc_act/tc_skbedit.h | 4 ++-- net/sched/act_api.c | 43 ++++++++++++++++++++++++++++------------- net/sched/act_csum.c | 24 ++++++++--------------- net/sched/act_gact.c | 27 ++++++++------------------ net/sched/act_ipt.c | 39 ++++++++++++++----------------------- net/sched/act_mirred.c | 32 +++++++++++------------------- net/sched/act_nat.c | 25 ++++++++---------------- net/sched/act_pedit.c | 25 ++++++++++-------------- net/sched/act_police.c | 10 +--------- net/sched/act_simple.c | 39 +++++++++++++------------------------ net/sched/act_skbedit.c | 29 +++++++++------------------ 19 files changed, 135 insertions(+), 206 deletions(-) (limited to 'include/net') diff --git a/include/net/act_api.h b/include/net/act_api.h index 788d8378e587..24ae910609ce 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -98,16 +98,14 @@ struct tc_action_ops { }; 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); +void tcf_hash_destroy(struct tc_action *a); +int tcf_hash_release(struct tc_action *a, int bind); 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_common *tcf_hash_create(u32 index, struct nlattr *est, - struct tc_action *a, int size, - int bind); -void tcf_hash_insert(struct tcf_common *p, struct tcf_hashinfo *hinfo); +int tcf_hash_check(u32 index, struct tc_action *a, int bind); +int tcf_hash_create(u32 index, struct nlattr *est, struct tc_action *a, + int size, int bind); +void tcf_hash_cleanup(struct tc_action *a, struct nlattr *est); +void tcf_hash_insert(struct tc_action *a); int tcf_register_action(struct tc_action_ops *a); int tcf_unregister_action(struct tc_action_ops *a); diff --git a/include/net/tc_act/tc_csum.h b/include/net/tc_act/tc_csum.h index 9e8710be7a04..fa8f5fac65e9 100644 --- a/include/net/tc_act/tc_csum.h +++ b/include/net/tc_act/tc_csum.h @@ -9,7 +9,7 @@ struct tcf_csum { u32 update_flags; }; -#define to_tcf_csum(pc) \ - container_of(pc,struct tcf_csum,common) +#define to_tcf_csum(a) \ + container_of(a->priv,struct tcf_csum,common) #endif /* __NET_TC_CSUM_H */ diff --git a/include/net/tc_act/tc_defact.h b/include/net/tc_act/tc_defact.h index 65f024b80958..9763dcbb9bc3 100644 --- a/include/net/tc_act/tc_defact.h +++ b/include/net/tc_act/tc_defact.h @@ -8,7 +8,7 @@ struct tcf_defact { u32 tcfd_datalen; void *tcfd_defdata; }; -#define to_defact(pc) \ - container_of(pc, struct tcf_defact, common) +#define to_defact(a) \ + container_of(a->priv, struct tcf_defact, common) #endif /* __NET_TC_DEF_H */ diff --git a/include/net/tc_act/tc_gact.h b/include/net/tc_act/tc_gact.h index 9e3f6767b80e..9fc9b578908a 100644 --- a/include/net/tc_act/tc_gact.h +++ b/include/net/tc_act/tc_gact.h @@ -11,7 +11,7 @@ struct tcf_gact { int tcfg_paction; #endif }; -#define to_gact(pc) \ - container_of(pc, struct tcf_gact, common) +#define to_gact(a) \ + container_of(a->priv, struct tcf_gact, common) #endif /* __NET_TC_GACT_H */ diff --git a/include/net/tc_act/tc_ipt.h b/include/net/tc_act/tc_ipt.h index f7d25dfcc4b7..c0f4193f432c 100644 --- a/include/net/tc_act/tc_ipt.h +++ b/include/net/tc_act/tc_ipt.h @@ -11,7 +11,7 @@ struct tcf_ipt { char *tcfi_tname; struct xt_entry_target *tcfi_t; }; -#define to_ipt(pc) \ - container_of(pc, struct tcf_ipt, common) +#define to_ipt(a) \ + container_of(a->priv, struct tcf_ipt, common) #endif /* __NET_TC_IPT_H */ diff --git a/include/net/tc_act/tc_mirred.h b/include/net/tc_act/tc_mirred.h index cfe2943690ff..4dd77a1c106b 100644 --- a/include/net/tc_act/tc_mirred.h +++ b/include/net/tc_act/tc_mirred.h @@ -11,7 +11,7 @@ struct tcf_mirred { struct net_device *tcfm_dev; struct list_head tcfm_list; }; -#define to_mirred(pc) \ - container_of(pc, struct tcf_mirred, common) +#define to_mirred(a) \ + container_of(a->priv, struct tcf_mirred, common) #endif /* __NET_TC_MIR_H */ diff --git a/include/net/tc_act/tc_nat.h b/include/net/tc_act/tc_nat.h index 4a691f34d703..63d8e9ca9d99 100644 --- a/include/net/tc_act/tc_nat.h +++ b/include/net/tc_act/tc_nat.h @@ -13,9 +13,9 @@ struct tcf_nat { u32 flags; }; -static inline struct tcf_nat *to_tcf_nat(struct tcf_common *pc) +static inline struct tcf_nat *to_tcf_nat(struct tc_action *a) { - return container_of(pc, struct tcf_nat, common); + return container_of(a->priv, struct tcf_nat, common); } #endif /* __NET_TC_NAT_H */ diff --git a/include/net/tc_act/tc_pedit.h b/include/net/tc_act/tc_pedit.h index e6f6e15956f5..5b80998879c7 100644 --- a/include/net/tc_act/tc_pedit.h +++ b/include/net/tc_act/tc_pedit.h @@ -9,7 +9,7 @@ struct tcf_pedit { unsigned char tcfp_flags; struct tc_pedit_key *tcfp_keys; }; -#define to_pedit(pc) \ - container_of(pc, struct tcf_pedit, common) +#define to_pedit(a) \ + container_of(a->priv, struct tcf_pedit, common) #endif /* __NET_TC_PED_H */ diff --git a/include/net/tc_act/tc_skbedit.h b/include/net/tc_act/tc_skbedit.h index dd5d86fab030..0df9a0db4a8e 100644 --- a/include/net/tc_act/tc_skbedit.h +++ b/include/net/tc_act/tc_skbedit.h @@ -29,7 +29,7 @@ struct tcf_skbedit { u16 queue_mapping; /* XXX: 16-bit pad here? */ }; -#define to_skbedit(pc) \ - container_of(pc, struct tcf_skbedit, common) +#define to_skbedit(a) \ + container_of(a->priv, struct tcf_skbedit, common) #endif /* __NET_TC_SKBEDIT_H */ diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 72bdc7166345..4f2b807b3621 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -27,8 +27,11 @@ #include #include -void tcf_hash_destroy(struct tcf_common *p, struct tcf_hashinfo *hinfo) +void tcf_hash_destroy(struct tc_action *a) { + struct tcf_common *p = a->priv; + struct tcf_hashinfo *hinfo = a->ops->hinfo; + spin_lock_bh(&hinfo->lock); hlist_del(&p->tcfc_head); spin_unlock_bh(&hinfo->lock); @@ -42,9 +45,9 @@ void tcf_hash_destroy(struct tcf_common *p, struct tcf_hashinfo *hinfo) } EXPORT_SYMBOL(tcf_hash_destroy); -int tcf_hash_release(struct tcf_common *p, int bind, - struct tcf_hashinfo *hinfo) +int tcf_hash_release(struct tc_action *a, int bind) { + struct tcf_common *p = a->priv; int ret = 0; if (p) { @@ -53,7 +56,7 @@ int tcf_hash_release(struct tcf_common *p, int bind, p->tcfc_refcnt--; if (p->tcfc_bindcnt <= 0 && p->tcfc_refcnt <= 0) { - tcf_hash_destroy(p, hinfo); + tcf_hash_destroy(a); ret = 1; } } @@ -127,7 +130,8 @@ static int tcf_del_walker(struct sk_buff *skb, struct tc_action *a) for (i = 0; i < (hinfo->hmask + 1); i++) { 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)) { + a->priv = p; + if (ACT_P_DELETED == tcf_hash_release(a, 0)) { module_put(a->ops->owner); n_i++; } @@ -198,7 +202,7 @@ int tcf_hash_search(struct tc_action *a, u32 index) } EXPORT_SYMBOL(tcf_hash_search); -struct tcf_common *tcf_hash_check(u32 index, struct tc_action *a, int bind) +int tcf_hash_check(u32 index, struct tc_action *a, int bind) { struct tcf_hashinfo *hinfo = a->ops->hinfo; struct tcf_common *p = NULL; @@ -207,19 +211,30 @@ struct tcf_common *tcf_hash_check(u32 index, struct tc_action *a, int bind) p->tcfc_bindcnt++; p->tcfc_refcnt++; a->priv = p; + return 1; } - return p; + return 0; } EXPORT_SYMBOL(tcf_hash_check); -struct tcf_common *tcf_hash_create(u32 index, struct nlattr *est, - struct tc_action *a, int size, int bind) +void tcf_hash_cleanup(struct tc_action *a, struct nlattr *est) +{ + struct tcf_common *pc = a->priv; + if (est) + gen_kill_estimator(&pc->tcfc_bstats, + &pc->tcfc_rate_est); + kfree_rcu(pc, tcfc_rcu); +} +EXPORT_SYMBOL(tcf_hash_cleanup); + +int tcf_hash_create(u32 index, struct nlattr *est, 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)) - return ERR_PTR(-ENOMEM); + return -ENOMEM; p->tcfc_refcnt = 1; if (bind) p->tcfc_bindcnt = 1; @@ -234,17 +249,19 @@ struct tcf_common *tcf_hash_create(u32 index, struct nlattr *est, &p->tcfc_lock, est); if (err) { kfree(p); - return ERR_PTR(err); + return err; } } a->priv = (void *) p; - return p; + return 0; } EXPORT_SYMBOL(tcf_hash_create); -void tcf_hash_insert(struct tcf_common *p, struct tcf_hashinfo *hinfo) +void tcf_hash_insert(struct tc_action *a) { + struct tcf_common *p = a->priv; + struct tcf_hashinfo *hinfo = a->ops->hinfo; unsigned int h = tcf_hash(p->tcfc_index, hinfo->hmask); spin_lock_bh(&hinfo->lock); diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c index 2210187c45c2..f0f6e7a625d1 100644 --- a/net/sched/act_csum.c +++ b/net/sched/act_csum.c @@ -48,7 +48,6 @@ static int tcf_csum_init(struct net *n, struct nlattr *nla, struct nlattr *est, { struct nlattr *tb[TCA_CSUM_MAX + 1]; struct tc_csum *parm; - struct tcf_common *pc; struct tcf_csum *p; int ret = 0, err; @@ -63,38 +62,31 @@ 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); - if (!pc) { - pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind); - if (IS_ERR(pc)) - return PTR_ERR(pc); + if (!tcf_hash_check(parm->index, a, bind)) { + ret = tcf_hash_create(parm->index, est, a, sizeof(*p), bind); + if (ret) + return ret; ret = ACT_P_CREATED; } else { if (bind)/* dont override defaults */ return 0; - tcf_hash_release(pc, bind, a->ops->hinfo); + tcf_hash_release(a, bind); if (!ovr) return -EEXIST; } - p = to_tcf_csum(pc); + p = to_tcf_csum(a); spin_lock_bh(&p->tcf_lock); p->tcf_action = parm->action; p->update_flags = parm->update_flags; spin_unlock_bh(&p->tcf_lock); if (ret == ACT_P_CREATED) - tcf_hash_insert(pc, a->ops->hinfo); + tcf_hash_insert(a); return ret; } -static int tcf_csum_cleanup(struct tc_action *a, int bind) -{ - struct tcf_csum *p = a->priv; - return tcf_hash_release(&p->common, bind, &csum_hash_info); -} - /** * tcf_csum_skb_nextlayer - Get next layer pointer * @skb: sk_buff to use @@ -574,7 +566,7 @@ static struct tc_action_ops act_csum_ops = { .owner = THIS_MODULE, .act = tcf_csum, .dump = tcf_csum_dump, - .cleanup = tcf_csum_cleanup, + .cleanup = tcf_hash_release, .init = tcf_csum_init, }; diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c index a0eed30d5811..af6c0acd9bf1 100644 --- a/net/sched/act_gact.c +++ b/net/sched/act_gact.c @@ -57,7 +57,6 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla, struct nlattr *tb[TCA_GACT_MAX + 1]; struct tc_gact *parm; struct tcf_gact *gact; - struct tcf_common *pc; int ret = 0; int err; #ifdef CONFIG_GACT_PROB @@ -86,21 +85,20 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla, } #endif - pc = tcf_hash_check(parm->index, a, bind); - if (!pc) { - pc = tcf_hash_create(parm->index, est, a, sizeof(*gact), bind); - if (IS_ERR(pc)) - return PTR_ERR(pc); + if (!tcf_hash_check(parm->index, a, bind)) { + ret = tcf_hash_create(parm->index, est, a, sizeof(*gact), bind); + if (ret) + return ret; ret = ACT_P_CREATED; } else { if (bind)/* dont override defaults */ return 0; - tcf_hash_release(pc, bind, a->ops->hinfo); + tcf_hash_release(a, bind); if (!ovr) return -EEXIST; } - gact = to_gact(pc); + gact = to_gact(a); spin_lock_bh(&gact->tcf_lock); gact->tcf_action = parm->action; @@ -113,19 +111,10 @@ 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, a->ops->hinfo); + tcf_hash_insert(a); return ret; } -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, a->ops->hinfo); - return 0; -} - static int tcf_gact(struct sk_buff *skb, const struct tc_action *a, struct tcf_result *res) { @@ -196,7 +185,7 @@ static struct tc_action_ops act_gact_ops = { .owner = THIS_MODULE, .act = tcf_gact, .dump = tcf_gact_dump, - .cleanup = tcf_gact_cleanup, + .cleanup = tcf_hash_release, .init = tcf_gact_init, }; diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index 0a6d62174027..f5e69782d400 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -69,8 +69,9 @@ static void ipt_destroy_target(struct xt_entry_target *t) module_put(par.target->me); } -static int tcf_ipt_release(struct tcf_ipt *ipt, int bind) +static int tcf_ipt_release(struct tc_action *a, int bind) { + struct tcf_ipt *ipt = to_ipt(a); int ret = 0; if (ipt) { if (bind) @@ -80,7 +81,7 @@ static int tcf_ipt_release(struct tcf_ipt *ipt, int bind) ipt_destroy_target(ipt->tcfi_t); kfree(ipt->tcfi_tname); kfree(ipt->tcfi_t); - tcf_hash_destroy(&ipt->common, &ipt_hash_info); + tcf_hash_destroy(a); ret = ACT_P_DELETED; } } @@ -99,7 +100,6 @@ static int tcf_ipt_init(struct net *net, struct nlattr *nla, struct nlattr *est, { struct nlattr *tb[TCA_IPT_MAX + 1]; struct tcf_ipt *ipt; - struct tcf_common *pc; struct xt_entry_target *td, *t; char *tname; int ret = 0, err; @@ -125,21 +125,20 @@ 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); - if (!pc) { - pc = tcf_hash_create(index, est, a, sizeof(*ipt), bind); - if (IS_ERR(pc)) - return PTR_ERR(pc); + if (!tcf_hash_check(index, a, bind) ) { + ret = tcf_hash_create(index, est, a, sizeof(*ipt), bind); + if (ret) + return ret; ret = ACT_P_CREATED; } else { if (bind)/* dont override defaults */ return 0; - tcf_ipt_release(to_ipt(pc), bind); + tcf_ipt_release(a, bind); if (!ovr) return -EEXIST; } - ipt = to_ipt(pc); + ipt = to_ipt(a); hook = nla_get_u32(tb[TCA_IPT_HOOK]); @@ -170,7 +169,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, a->ops->hinfo); + tcf_hash_insert(a); return ret; err3: @@ -178,21 +177,11 @@ err3: err2: kfree(tname); err1: - if (ret == ACT_P_CREATED) { - if (est) - gen_kill_estimator(&pc->tcfc_bstats, - &pc->tcfc_rate_est); - kfree_rcu(pc, tcfc_rcu); - } + if (ret == ACT_P_CREATED) + tcf_hash_cleanup(a, est); return err; } -static int tcf_ipt_cleanup(struct tc_action *a, int bind) -{ - struct tcf_ipt *ipt = a->priv; - return tcf_ipt_release(ipt, bind); -} - static int tcf_ipt(struct sk_buff *skb, const struct tc_action *a, struct tcf_result *res) { @@ -289,7 +278,7 @@ static struct tc_action_ops act_ipt_ops = { .owner = THIS_MODULE, .act = tcf_ipt, .dump = tcf_ipt_dump, - .cleanup = tcf_ipt_cleanup, + .cleanup = tcf_ipt_release, .init = tcf_ipt_init, }; @@ -300,7 +289,7 @@ static struct tc_action_ops act_xt_ops = { .owner = THIS_MODULE, .act = tcf_ipt, .dump = tcf_ipt_dump, - .cleanup = tcf_ipt_cleanup, + .cleanup = tcf_ipt_release, .init = tcf_ipt_init, }; diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index 0b2c6d39d396..3edeecafba2f 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -33,8 +33,9 @@ static LIST_HEAD(mirred_list); static struct tcf_hashinfo mirred_hash_info; -static int tcf_mirred_release(struct tcf_mirred *m, int bind) +static int tcf_mirred_release(struct tc_action *a, int bind) { + struct tcf_mirred *m = to_mirred(a); if (m) { if (bind) m->tcf_bindcnt--; @@ -43,7 +44,7 @@ static int tcf_mirred_release(struct tcf_mirred *m, int bind) list_del(&m->tcfm_list); if (m->tcfm_dev) dev_put(m->tcfm_dev); - tcf_hash_destroy(&m->common, &mirred_hash_info); + tcf_hash_destroy(a); return 1; } } @@ -61,7 +62,6 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla, struct nlattr *tb[TCA_MIRRED_MAX + 1]; struct tc_mirred *parm; struct tcf_mirred *m; - struct tcf_common *pc; struct net_device *dev; int ret, ok_push = 0; @@ -101,21 +101,20 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla, dev = NULL; } - pc = tcf_hash_check(parm->index, a, bind); - if (!pc) { + if (!tcf_hash_check(parm->index, a, bind)) { if (dev == NULL) return -EINVAL; - pc = tcf_hash_create(parm->index, est, a, sizeof(*m), bind); - if (IS_ERR(pc)) - return PTR_ERR(pc); + ret = tcf_hash_create(parm->index, est, a, sizeof(*m), bind); + if (ret) + return ret; ret = ACT_P_CREATED; } else { if (!ovr) { - tcf_mirred_release(to_mirred(pc), bind); + tcf_mirred_release(a, bind); return -EEXIST; } } - m = to_mirred(pc); + m = to_mirred(a); spin_lock_bh(&m->tcf_lock); m->tcf_action = parm->action; @@ -131,21 +130,12 @@ 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, a->ops->hinfo); + tcf_hash_insert(a); } return ret; } -static int tcf_mirred_cleanup(struct tc_action *a, int bind) -{ - struct tcf_mirred *m = a->priv; - - if (m) - return tcf_mirred_release(m, bind); - return 0; -} - static int tcf_mirred(struct sk_buff *skb, const struct tc_action *a, struct tcf_result *res) { @@ -259,7 +249,7 @@ static struct tc_action_ops act_mirred_ops = { .owner = THIS_MODULE, .act = tcf_mirred, .dump = tcf_mirred_dump, - .cleanup = tcf_mirred_cleanup, + .cleanup = tcf_mirred_release, .init = tcf_mirred_init, }; diff --git a/net/sched/act_nat.c b/net/sched/act_nat.c index 81f0404bb335..ce9a3914ed4a 100644 --- a/net/sched/act_nat.c +++ b/net/sched/act_nat.c @@ -44,7 +44,6 @@ static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est, struct tc_nat *parm; int ret = 0, err; struct tcf_nat *p; - struct tcf_common *pc; if (nla == NULL) return -EINVAL; @@ -57,20 +56,19 @@ 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); - if (!pc) { - pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind); - if (IS_ERR(pc)) - return PTR_ERR(pc); + if (!tcf_hash_check(parm->index, a, bind)) { + ret = tcf_hash_create(parm->index, est, a, sizeof(*p), bind); + if (ret) + return ret; ret = ACT_P_CREATED; } else { if (bind) return 0; - tcf_hash_release(pc, bind, a->ops->hinfo); + tcf_hash_release(a, bind); if (!ovr) return -EEXIST; } - p = to_tcf_nat(pc); + p = to_tcf_nat(a); spin_lock_bh(&p->tcf_lock); p->old_addr = parm->old_addr; @@ -82,18 +80,11 @@ 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, a->ops->hinfo); + tcf_hash_insert(a); return ret; } -static int tcf_nat_cleanup(struct tc_action *a, int bind) -{ - struct tcf_nat *p = a->priv; - - return tcf_hash_release(&p->common, bind, &nat_hash_info); -} - static int tcf_nat(struct sk_buff *skb, const struct tc_action *a, struct tcf_result *res) { @@ -298,7 +289,7 @@ static struct tc_action_ops act_nat_ops = { .owner = THIS_MODULE, .act = tcf_nat, .dump = tcf_nat_dump, - .cleanup = tcf_nat_cleanup, + .cleanup = tcf_hash_release, .init = tcf_nat_init, }; diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index be3f0f6875bb..091ced38a376 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -39,7 +39,6 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, struct tc_pedit *parm; int ret = 0, err; struct tcf_pedit *p; - struct tcf_common *pc; struct tc_pedit_key *keys = NULL; int ksize; @@ -57,26 +56,22 @@ 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); - if (!pc) { + if (!tcf_hash_check(parm->index, a, bind)) { if (!parm->nkeys) return -EINVAL; - pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind); - if (IS_ERR(pc)) - return PTR_ERR(pc); - p = to_pedit(pc); + ret = tcf_hash_create(parm->index, est, a, sizeof(*p), bind); + if (ret) + return ret; + p = to_pedit(a); keys = kmalloc(ksize, GFP_KERNEL); if (keys == NULL) { - if (est) - gen_kill_estimator(&pc->tcfc_bstats, - &pc->tcfc_rate_est); - kfree_rcu(pc, tcfc_rcu); + tcf_hash_cleanup(a, est); return -ENOMEM; } ret = ACT_P_CREATED; } else { - p = to_pedit(pc); - tcf_hash_release(pc, bind, a->ops->hinfo); + p = to_pedit(a); + tcf_hash_release(a, bind); if (bind) return 0; if (!ovr) @@ -100,7 +95,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, a->ops->hinfo); + tcf_hash_insert(a); return ret; } @@ -110,7 +105,7 @@ static int tcf_pedit_cleanup(struct tc_action *a, int bind) if (p) { struct tc_pedit_key *keys = p->tcfp_keys; - if (tcf_hash_release(&p->common, bind, &pedit_hash_info)) { + if (tcf_hash_release(a, bind)) { kfree(keys); return 1; } diff --git a/net/sched/act_police.c b/net/sched/act_police.c index 1778209a332f..4695d02bd249 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -253,14 +253,6 @@ failure: return err; } -static int tcf_act_police_cleanup(struct tc_action *a, int bind) -{ - struct tcf_police *p = a->priv; - if (p) - return tcf_hash_release(&p->common, bind, &police_hash_info); - return 0; -} - static int tcf_act_police(struct sk_buff *skb, const struct tc_action *a, struct tcf_result *res) { @@ -362,7 +354,7 @@ static struct tc_action_ops act_police_ops = { .owner = THIS_MODULE, .act = tcf_act_police, .dump = tcf_act_police_dump, - .cleanup = tcf_act_police_cleanup, + .cleanup = tcf_hash_release, .init = tcf_act_police_locate, .walk = tcf_act_police_walker }; diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c index 8ef2f1fcbfba..11c2922bb900 100644 --- a/net/sched/act_simple.c +++ b/net/sched/act_simple.c @@ -47,8 +47,9 @@ static int tcf_simp(struct sk_buff *skb, const struct tc_action *a, return d->tcf_action; } -static int tcf_simp_release(struct tcf_defact *d, int bind) +static int tcf_simp_release(struct tc_action *a, int bind) { + struct tcf_defact *d = to_defact(a); int ret = 0; if (d) { if (bind) @@ -56,7 +57,7 @@ static int tcf_simp_release(struct tcf_defact *d, int bind) d->tcf_refcnt--; if (d->tcf_bindcnt <= 0 && d->tcf_refcnt <= 0) { kfree(d->tcfd_defdata); - tcf_hash_destroy(&d->common, &simp_hash_info); + tcf_hash_destroy(a); ret = 1; } } @@ -94,7 +95,6 @@ static int tcf_simp_init(struct net *net, struct nlattr *nla, struct nlattr *tb[TCA_DEF_MAX + 1]; struct tc_defact *parm; struct tcf_defact *d; - struct tcf_common *pc; char *defdata; int ret = 0, err; @@ -114,29 +114,25 @@ 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); - if (!pc) { - pc = tcf_hash_create(parm->index, est, a, sizeof(*d), bind); - if (IS_ERR(pc)) - return PTR_ERR(pc); + if (!tcf_hash_check(parm->index, a, bind)) { + ret = tcf_hash_create(parm->index, est, a, sizeof(*d), bind); + if (ret) + return ret; - d = to_defact(pc); + d = to_defact(a); ret = alloc_defdata(d, defdata); if (ret < 0) { - if (est) - gen_kill_estimator(&pc->tcfc_bstats, - &pc->tcfc_rate_est); - kfree_rcu(pc, tcfc_rcu); + tcf_hash_cleanup(a, est); return ret; } d->tcf_action = parm->action; ret = ACT_P_CREATED; } else { - d = to_defact(pc); + d = to_defact(a); if (bind) return 0; - tcf_simp_release(d, bind); + tcf_simp_release(a, bind); if (!ovr) return -EEXIST; @@ -144,19 +140,10 @@ static int tcf_simp_init(struct net *net, struct nlattr *nla, } if (ret == ACT_P_CREATED) - tcf_hash_insert(pc, a->ops->hinfo); + tcf_hash_insert(a); return ret; } -static int tcf_simp_cleanup(struct tc_action *a, int bind) -{ - struct tcf_defact *d = a->priv; - - if (d) - return tcf_simp_release(d, bind); - return 0; -} - static int tcf_simp_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) { @@ -192,7 +179,7 @@ static struct tc_action_ops act_simp_ops = { .owner = THIS_MODULE, .act = tcf_simp, .dump = tcf_simp_dump, - .cleanup = tcf_simp_cleanup, + .cleanup = tcf_simp_release, .init = tcf_simp_init, }; diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c index 98725080b5aa..71fd2d499109 100644 --- a/net/sched/act_skbedit.c +++ b/net/sched/act_skbedit.c @@ -65,7 +65,6 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla, struct nlattr *tb[TCA_SKBEDIT_MAX + 1]; struct tc_skbedit *parm; struct tcf_skbedit *d; - struct tcf_common *pc; u32 flags = 0, *priority = NULL, *mark = NULL; u16 *queue_mapping = NULL; int ret = 0, err; @@ -100,19 +99,18 @@ 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); - if (!pc) { - pc = tcf_hash_create(parm->index, est, a, sizeof(*d), bind); - if (IS_ERR(pc)) - return PTR_ERR(pc); + if (!tcf_hash_check(parm->index, a, bind)) { + ret = tcf_hash_create(parm->index, est, a, sizeof(*d), bind); + if (ret) + return ret; - d = to_skbedit(pc); + d = to_skbedit(a); ret = ACT_P_CREATED; } else { - d = to_skbedit(pc); + d = to_skbedit(a); if (bind) return 0; - tcf_hash_release(pc, bind, a->ops->hinfo); + tcf_hash_release(a, bind); if (!ovr) return -EEXIST; } @@ -132,19 +130,10 @@ 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, a->ops->hinfo); + tcf_hash_insert(a); return ret; } -static int tcf_skbedit_cleanup(struct tc_action *a, int bind) -{ - struct tcf_skbedit *d = a->priv; - - if (d) - return tcf_hash_release(&d->common, bind, &skbedit_hash_info); - return 0; -} - static int tcf_skbedit_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) { @@ -191,7 +180,7 @@ static struct tc_action_ops act_skbedit_ops = { .owner = THIS_MODULE, .act = tcf_skbedit, .dump = tcf_skbedit_dump, - .cleanup = tcf_skbedit_cleanup, + .cleanup = tcf_hash_release, .init = tcf_skbedit_init, }; -- cgit v1.2.3-71-gd317 From a5b5c958ffd1610545d6b4b8290aa9c5266d10fa Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Tue, 11 Feb 2014 17:07:32 -0800 Subject: net_sched: act: refactor cleanup ops For bindcnt and refcnt etc., they are common for all actions, not need to repeat such operations for their own, they can be unified now. Actions just need to do its specific cleanup if needed. 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 | 8 +++++--- net/sched/act_csum.c | 1 - net/sched/act_gact.c | 1 - net/sched/act_ipt.c | 21 +++++---------------- net/sched/act_mirred.c | 20 +++++--------------- net/sched/act_nat.c | 1 - net/sched/act_pedit.c | 13 +++---------- net/sched/act_police.c | 1 - net/sched/act_simple.c | 17 +++-------------- net/sched/act_skbedit.c | 1 - 11 files changed, 22 insertions(+), 64 deletions(-) (limited to 'include/net') diff --git a/include/net/act_api.h b/include/net/act_api.h index 24ae910609ce..3d22f42b6eec 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -89,7 +89,7 @@ struct tc_action_ops { 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); - int (*cleanup)(struct tc_action *, int bind); + void (*cleanup)(struct tc_action *, int bind); int (*lookup)(struct tc_action *, u32); int (*init)(struct net *net, struct nlattr *nla, struct nlattr *est, struct tc_action *act, int ovr, diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 4f2b807b3621..a5bf9351ce5c 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -56,6 +56,8 @@ int tcf_hash_release(struct tc_action *a, int bind) p->tcfc_refcnt--; if (p->tcfc_bindcnt <= 0 && p->tcfc_refcnt <= 0) { + if (a->ops->cleanup) + a->ops->cleanup(a, bind); tcf_hash_destroy(a); ret = 1; } @@ -277,8 +279,8 @@ int tcf_register_action(struct tc_action_ops *act) { struct tc_action_ops *a; - /* Must supply act, dump, cleanup and init */ - if (!act->act || !act->dump || !act->cleanup || !act->init) + /* Must supply act, dump and init */ + if (!act->act || !act->dump || !act->init) return -EINVAL; /* Supply defaults */ @@ -390,7 +392,7 @@ void tcf_action_destroy(struct list_head *actions, int bind) struct tc_action *a, *tmp; list_for_each_entry_safe(a, tmp, actions, list) { - if (a->ops->cleanup(a, bind) == ACT_P_DELETED) + if (tcf_hash_release(a, bind) == ACT_P_DELETED) module_put(a->ops->owner); list_del(&a->list); kfree(a); diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c index f0f6e7a625d1..8df3060e7ac1 100644 --- a/net/sched/act_csum.c +++ b/net/sched/act_csum.c @@ -566,7 +566,6 @@ static struct tc_action_ops act_csum_ops = { .owner = THIS_MODULE, .act = tcf_csum, .dump = tcf_csum_dump, - .cleanup = tcf_hash_release, .init = tcf_csum_init, }; diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c index af6c0acd9bf1..094a1b509d75 100644 --- a/net/sched/act_gact.c +++ b/net/sched/act_gact.c @@ -185,7 +185,6 @@ static struct tc_action_ops act_gact_ops = { .owner = THIS_MODULE, .act = tcf_gact, .dump = tcf_gact_dump, - .cleanup = tcf_hash_release, .init = tcf_gact_init, }; diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index f5e69782d400..71f29f1b5a20 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -69,23 +69,12 @@ static void ipt_destroy_target(struct xt_entry_target *t) module_put(par.target->me); } -static int tcf_ipt_release(struct tc_action *a, int bind) +static void tcf_ipt_release(struct tc_action *a, int bind) { struct tcf_ipt *ipt = to_ipt(a); - int ret = 0; - if (ipt) { - if (bind) - ipt->tcf_bindcnt--; - ipt->tcf_refcnt--; - if (ipt->tcf_bindcnt <= 0 && ipt->tcf_refcnt <= 0) { - ipt_destroy_target(ipt->tcfi_t); - kfree(ipt->tcfi_tname); - kfree(ipt->tcfi_t); - tcf_hash_destroy(a); - ret = ACT_P_DELETED; - } - } - return ret; + ipt_destroy_target(ipt->tcfi_t); + kfree(ipt->tcfi_tname); + kfree(ipt->tcfi_t); } static const struct nla_policy ipt_policy[TCA_IPT_MAX + 1] = { @@ -133,7 +122,7 @@ static int tcf_ipt_init(struct net *net, struct nlattr *nla, struct nlattr *est, } else { if (bind)/* dont override defaults */ return 0; - tcf_ipt_release(a, bind); + tcf_hash_release(a, bind); if (!ovr) return -EEXIST; diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index 3edeecafba2f..0f00eb96af84 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -33,22 +33,12 @@ static LIST_HEAD(mirred_list); static struct tcf_hashinfo mirred_hash_info; -static int tcf_mirred_release(struct tc_action *a, int bind) +static void tcf_mirred_release(struct tc_action *a, int bind) { struct tcf_mirred *m = to_mirred(a); - if (m) { - if (bind) - m->tcf_bindcnt--; - m->tcf_refcnt--; - if (!m->tcf_bindcnt && m->tcf_refcnt <= 0) { - list_del(&m->tcfm_list); - if (m->tcfm_dev) - dev_put(m->tcfm_dev); - tcf_hash_destroy(a); - return 1; - } - } - return 0; + list_del(&m->tcfm_list); + if (m->tcfm_dev) + dev_put(m->tcfm_dev); } static const struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = { @@ -110,7 +100,7 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla, ret = ACT_P_CREATED; } else { if (!ovr) { - tcf_mirred_release(a, bind); + tcf_hash_release(a, bind); return -EEXIST; } } diff --git a/net/sched/act_nat.c b/net/sched/act_nat.c index ce9a3914ed4a..9a3cb1d16d19 100644 --- a/net/sched/act_nat.c +++ b/net/sched/act_nat.c @@ -289,7 +289,6 @@ static struct tc_action_ops act_nat_ops = { .owner = THIS_MODULE, .act = tcf_nat, .dump = tcf_nat_dump, - .cleanup = tcf_hash_release, .init = tcf_nat_init, }; diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index 091ced38a376..8aa795b275f2 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -99,18 +99,11 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, return ret; } -static int tcf_pedit_cleanup(struct tc_action *a, int bind) +static void tcf_pedit_cleanup(struct tc_action *a, int bind) { struct tcf_pedit *p = a->priv; - - if (p) { - struct tc_pedit_key *keys = p->tcfp_keys; - if (tcf_hash_release(a, bind)) { - kfree(keys); - return 1; - } - } - return 0; + struct tc_pedit_key *keys = p->tcfp_keys; + kfree(keys); } static int tcf_pedit(struct sk_buff *skb, const struct tc_action *a, diff --git a/net/sched/act_police.c b/net/sched/act_police.c index 4695d02bd249..7ff7bef065bf 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -354,7 +354,6 @@ static struct tc_action_ops act_police_ops = { .owner = THIS_MODULE, .act = tcf_act_police, .dump = tcf_act_police_dump, - .cleanup = tcf_hash_release, .init = tcf_act_police_locate, .walk = tcf_act_police_walker }; diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c index 11c2922bb900..14b5e362a1d6 100644 --- a/net/sched/act_simple.c +++ b/net/sched/act_simple.c @@ -47,21 +47,10 @@ static int tcf_simp(struct sk_buff *skb, const struct tc_action *a, return d->tcf_action; } -static int tcf_simp_release(struct tc_action *a, int bind) +static void tcf_simp_release(struct tc_action *a, int bind) { struct tcf_defact *d = to_defact(a); - int ret = 0; - if (d) { - if (bind) - d->tcf_bindcnt--; - d->tcf_refcnt--; - if (d->tcf_bindcnt <= 0 && d->tcf_refcnt <= 0) { - kfree(d->tcfd_defdata); - tcf_hash_destroy(a); - ret = 1; - } - } - return ret; + kfree(d->tcfd_defdata); } static int alloc_defdata(struct tcf_defact *d, char *defdata) @@ -132,7 +121,7 @@ static int tcf_simp_init(struct net *net, struct nlattr *nla, if (bind) return 0; - tcf_simp_release(a, bind); + tcf_hash_release(a, bind); if (!ovr) return -EEXIST; diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c index 71fd2d499109..9f91928fcaeb 100644 --- a/net/sched/act_skbedit.c +++ b/net/sched/act_skbedit.c @@ -180,7 +180,6 @@ static struct tc_action_ops act_skbedit_ops = { .owner = THIS_MODULE, .act = tcf_skbedit, .dump = tcf_skbedit_dump, - .cleanup = tcf_hash_release, .init = tcf_skbedit_init, }; -- cgit v1.2.3-71-gd317 From 4f1e9d8949b438c7791993515fc164312e9080e2 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Tue, 11 Feb 2014 17:07:33 -0800 Subject: net_sched: act: move tcf_hashinfo_init() into tcf_register_action() 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 | 16 +++++++++++++++- net/sched/act_csum.c | 8 +------- net/sched/act_gact.c | 8 +------- net/sched/act_ipt.c | 14 +++----------- net/sched/act_mirred.c | 10 +--------- net/sched/act_nat.c | 9 +-------- net/sched/act_pedit.c | 9 +-------- net/sched/act_police.c | 13 ++----------- net/sched/act_simple.c | 14 ++------------ net/sched/act_skbedit.c | 8 +------- 11 files changed, 29 insertions(+), 82 deletions(-) (limited to 'include/net') diff --git a/include/net/act_api.h b/include/net/act_api.h index 3d22f42b6eec..969cac6344aa 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -107,7 +107,7 @@ int tcf_hash_create(u32 index, struct nlattr *est, struct tc_action *a, void tcf_hash_cleanup(struct tc_action *a, struct nlattr *est); void tcf_hash_insert(struct tc_action *a); -int tcf_register_action(struct tc_action_ops *a); +int tcf_register_action(struct tc_action_ops *a, unsigned int mask); int tcf_unregister_action(struct tc_action_ops *a); void tcf_action_destroy(struct list_head *actions, int bind); int tcf_action_exec(struct sk_buff *skb, const struct list_head *actions, diff --git a/net/sched/act_api.c b/net/sched/act_api.c index a5bf9351ce5c..c88d382d3b09 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -275,9 +275,10 @@ EXPORT_SYMBOL(tcf_hash_insert); static LIST_HEAD(act_base); static DEFINE_RWLOCK(act_mod_lock); -int tcf_register_action(struct tc_action_ops *act) +int tcf_register_action(struct tc_action_ops *act, unsigned int mask) { struct tc_action_ops *a; + int err; /* Must supply act, dump and init */ if (!act->act || !act->dump || !act->init) @@ -289,10 +290,21 @@ int tcf_register_action(struct tc_action_ops *act) if (!act->walk) act->walk = tcf_generic_walker; + act->hinfo = kmalloc(sizeof(struct tcf_hashinfo), GFP_KERNEL); + if (!act->hinfo) + return -ENOMEM; + err = tcf_hashinfo_init(act->hinfo, mask); + if (err) { + kfree(act->hinfo); + return err; + } + write_lock(&act_mod_lock); list_for_each_entry(a, &act_base, head) { if (act->type == a->type || (strcmp(act->kind, a->kind) == 0)) { write_unlock(&act_mod_lock); + tcf_hashinfo_destroy(act->hinfo); + kfree(act->hinfo); return -EEXIST; } } @@ -311,6 +323,8 @@ int tcf_unregister_action(struct tc_action_ops *act) list_for_each_entry(a, &act_base, head) { if (a == act) { list_del(&act->head); + tcf_hashinfo_destroy(act->hinfo); + kfree(act->hinfo); err = 0; break; } diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c index 8df3060e7ac1..edbf40dac709 100644 --- a/net/sched/act_csum.c +++ b/net/sched/act_csum.c @@ -37,7 +37,6 @@ #include #define CSUM_TAB_MASK 15 -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), }, @@ -561,7 +560,6 @@ nla_put_failure: static struct tc_action_ops act_csum_ops = { .kind = "csum", - .hinfo = &csum_hash_info, .type = TCA_ACT_CSUM, .owner = THIS_MODULE, .act = tcf_csum, @@ -574,11 +572,7 @@ MODULE_LICENSE("GPL"); static int __init csum_init_module(void) { - int err = tcf_hashinfo_init(&csum_hash_info, CSUM_TAB_MASK); - if (err) - return err; - - return tcf_register_action(&act_csum_ops); + return tcf_register_action(&act_csum_ops, CSUM_TAB_MASK); } static void __exit csum_cleanup_module(void) diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c index 094a1b509d75..d6bcbd9f7791 100644 --- a/net/sched/act_gact.c +++ b/net/sched/act_gact.c @@ -24,7 +24,6 @@ #include #define GACT_TAB_MASK 15 -static struct tcf_hashinfo gact_hash_info; #ifdef CONFIG_GACT_PROB static int gact_net_rand(struct tcf_gact *gact) @@ -180,7 +179,6 @@ nla_put_failure: static struct tc_action_ops act_gact_ops = { .kind = "gact", - .hinfo = &gact_hash_info, .type = TCA_ACT_GACT, .owner = THIS_MODULE, .act = tcf_gact, @@ -194,21 +192,17 @@ MODULE_LICENSE("GPL"); static int __init gact_init_module(void) { - int err = tcf_hashinfo_init(&gact_hash_info, GACT_TAB_MASK); - if (err) - return err; #ifdef CONFIG_GACT_PROB pr_info("GACT probability on\n"); #else pr_info("GACT probability NOT on\n"); #endif - return tcf_register_action(&act_gact_ops); + return tcf_register_action(&act_gact_ops, GACT_TAB_MASK); } 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 71f29f1b5a20..8a64a0734aee 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -29,7 +29,6 @@ #define IPT_TAB_MASK 15 -static struct tcf_hashinfo ipt_hash_info; static int ipt_init_target(struct xt_entry_target *t, char *table, unsigned int hook) { @@ -262,7 +261,6 @@ nla_put_failure: static struct tc_action_ops act_ipt_ops = { .kind = "ipt", - .hinfo = &ipt_hash_info, .type = TCA_ACT_IPT, .owner = THIS_MODULE, .act = tcf_ipt, @@ -273,7 +271,6 @@ static struct tc_action_ops act_ipt_ops = { static struct tc_action_ops act_xt_ops = { .kind = "xt", - .hinfo = &ipt_hash_info, .type = TCA_ACT_XT, .owner = THIS_MODULE, .act = tcf_ipt, @@ -289,20 +286,16 @@ MODULE_ALIAS("act_xt"); static int __init ipt_init_module(void) { - int ret1, ret2, err; - err = tcf_hashinfo_init(&ipt_hash_info, IPT_TAB_MASK); - if (err) - return err; + int ret1, ret2; - ret1 = tcf_register_action(&act_xt_ops); + ret1 = tcf_register_action(&act_xt_ops, IPT_TAB_MASK); if (ret1 < 0) printk("Failed to load xt action\n"); - ret2 = tcf_register_action(&act_ipt_ops); + ret2 = tcf_register_action(&act_ipt_ops, IPT_TAB_MASK); if (ret2 < 0) printk("Failed to load ipt action\n"); if (ret1 < 0 && ret2 < 0) { - tcf_hashinfo_destroy(&ipt_hash_info); return ret1; } else return 0; @@ -312,7 +305,6 @@ 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 0f00eb96af84..4f912c0e225b 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -31,7 +31,6 @@ #define MIRRED_TAB_MASK 7 static LIST_HEAD(mirred_list); -static struct tcf_hashinfo mirred_hash_info; static void tcf_mirred_release(struct tc_action *a, int bind) { @@ -234,7 +233,6 @@ static struct notifier_block mirred_device_notifier = { static struct tc_action_ops act_mirred_ops = { .kind = "mirred", - .hinfo = &mirred_hash_info, .type = TCA_ACT_MIRRED, .owner = THIS_MODULE, .act = tcf_mirred, @@ -253,19 +251,13 @@ static int __init mirred_init_module(void) if (err) return err; - err = tcf_hashinfo_init(&mirred_hash_info, MIRRED_TAB_MASK); - if (err) { - unregister_netdevice_notifier(&mirred_device_notifier); - return err; - } pr_info("Mirror/redirect action on\n"); - return tcf_register_action(&act_mirred_ops); + return tcf_register_action(&act_mirred_ops, MIRRED_TAB_MASK); } static void __exit mirred_cleanup_module(void) { tcf_unregister_action(&act_mirred_ops); - tcf_hashinfo_destroy(&mirred_hash_info); unregister_netdevice_notifier(&mirred_device_notifier); } diff --git a/net/sched/act_nat.c b/net/sched/act_nat.c index 9a3cb1d16d19..270a030d5fd0 100644 --- a/net/sched/act_nat.c +++ b/net/sched/act_nat.c @@ -31,8 +31,6 @@ #define NAT_TAB_MASK 15 -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) }, }; @@ -284,7 +282,6 @@ nla_put_failure: static struct tc_action_ops act_nat_ops = { .kind = "nat", - .hinfo = &nat_hash_info, .type = TCA_ACT_NAT, .owner = THIS_MODULE, .act = tcf_nat, @@ -297,16 +294,12 @@ MODULE_LICENSE("GPL"); static int __init nat_init_module(void) { - int err = tcf_hashinfo_init(&nat_hash_info, NAT_TAB_MASK); - if (err) - return err; - return tcf_register_action(&act_nat_ops); + return tcf_register_action(&act_nat_ops, NAT_TAB_MASK); } 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 8aa795b275f2..5f9bcb2e080b 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -25,8 +25,6 @@ #define PEDIT_TAB_MASK 15 -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) }, }; @@ -218,7 +216,6 @@ nla_put_failure: static struct tc_action_ops act_pedit_ops = { .kind = "pedit", - .hinfo = &pedit_hash_info, .type = TCA_ACT_PEDIT, .owner = THIS_MODULE, .act = tcf_pedit, @@ -233,15 +230,11 @@ MODULE_LICENSE("GPL"); static int __init pedit_init_module(void) { - int err = tcf_hashinfo_init(&pedit_hash_info, PEDIT_TAB_MASK); - if (err) - return err; - return tcf_register_action(&act_pedit_ops); + return tcf_register_action(&act_pedit_ops, PEDIT_TAB_MASK); } 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 7ff7bef065bf..0566e4606a4a 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 struct tcf_hashinfo police_hash_info; /* old policer structure from before tc actions */ struct tc_police_compat { @@ -234,7 +233,7 @@ override: police->tcfp_t_c = ktime_to_ns(ktime_get()); police->tcf_index = parm->index ? parm->index : - tcf_hash_new_index(a->ops->hinfo); + tcf_hash_new_index(hinfo); h = tcf_hash(police->tcf_index, POL_TAB_MASK); spin_lock_bh(&hinfo->lock); hlist_add_head(&police->tcf_head, &hinfo->htab[h]); @@ -349,7 +348,6 @@ MODULE_LICENSE("GPL"); static struct tc_action_ops act_police_ops = { .kind = "police", - .hinfo = &police_hash_info, .type = TCA_ID_POLICE, .owner = THIS_MODULE, .act = tcf_act_police, @@ -361,19 +359,12 @@ static struct tc_action_ops act_police_ops = { static int __init police_init_module(void) { - int err = tcf_hashinfo_init(&police_hash_info, POL_TAB_MASK); - if (err) - return err; - err = tcf_register_action(&act_police_ops); - if (err) - tcf_hashinfo_destroy(&police_hash_info); - return err; + return tcf_register_action(&act_police_ops, POL_TAB_MASK); } 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 14b5e362a1d6..992c2317ce88 100644 --- a/net/sched/act_simple.c +++ b/net/sched/act_simple.c @@ -25,7 +25,6 @@ #include #define SIMP_TAB_MASK 7 -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, @@ -163,7 +162,6 @@ nla_put_failure: static struct tc_action_ops act_simp_ops = { .kind = "simple", - .hinfo = &simp_hash_info, .type = TCA_ACT_SIMP, .owner = THIS_MODULE, .act = tcf_simp, @@ -178,23 +176,15 @@ MODULE_LICENSE("GPL"); static int __init simp_init_module(void) { - int err, ret; - err = tcf_hashinfo_init(&simp_hash_info, SIMP_TAB_MASK); - if (err) - return err; - - ret = tcf_register_action(&act_simp_ops); + int ret; + ret = tcf_register_action(&act_simp_ops, SIMP_TAB_MASK); 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 9f91928fcaeb..fcfeeaf838be 100644 --- a/net/sched/act_skbedit.c +++ b/net/sched/act_skbedit.c @@ -28,7 +28,6 @@ #include #define SKBEDIT_TAB_MASK 15 -static struct tcf_hashinfo skbedit_hash_info; static int tcf_skbedit(struct sk_buff *skb, const struct tc_action *a, struct tcf_result *res) @@ -175,7 +174,6 @@ nla_put_failure: static struct tc_action_ops act_skbedit_ops = { .kind = "skbedit", - .hinfo = &skbedit_hash_info, .type = TCA_ACT_SKBEDIT, .owner = THIS_MODULE, .act = tcf_skbedit, @@ -189,15 +187,11 @@ MODULE_LICENSE("GPL"); static int __init skbedit_init_module(void) { - int err = tcf_hashinfo_init(&skbedit_hash_info, SKBEDIT_TAB_MASK); - if (err) - return err; - return tcf_register_action(&act_skbedit_ops); + return tcf_register_action(&act_skbedit_ops, SKBEDIT_TAB_MASK); } static void __exit skbedit_cleanup_module(void) { - tcf_hashinfo_destroy(&skbedit_hash_info); tcf_unregister_action(&act_skbedit_ops); } -- cgit v1.2.3-71-gd317 From 55334a5db5cd32b207ac697cec3ec8e078f345d4 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Tue, 11 Feb 2014 17:07:34 -0800 Subject: net_sched: act: refuse to remove bound action outside When an action is bonnd to a filter, there is no point to remove it outside. Currently we just silently decrease the refcnt, we should reject this explicitly with EPERM. 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 | 26 ++++++++++++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) (limited to 'include/net') diff --git a/include/net/act_api.h b/include/net/act_api.h index 969cac6344aa..3ee4c92afd1b 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -109,7 +109,7 @@ void tcf_hash_insert(struct tc_action *a); int tcf_register_action(struct tc_action_ops *a, unsigned int mask); int tcf_unregister_action(struct tc_action_ops *a); -void tcf_action_destroy(struct list_head *actions, int bind); +int 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); int tcf_action_init(struct net *net, struct nlattr *nla, diff --git a/net/sched/act_api.c b/net/sched/act_api.c index c88d382d3b09..27e4c531ade1 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -53,6 +53,8 @@ int tcf_hash_release(struct tc_action *a, int bind) if (p) { if (bind) p->tcfc_bindcnt--; + else if (p->tcfc_bindcnt > 0) + return -EPERM; p->tcfc_refcnt--; if (p->tcfc_bindcnt <= 0 && p->tcfc_refcnt <= 0) { @@ -123,6 +125,7 @@ static int tcf_del_walker(struct sk_buff *skb, struct tc_action *a) struct tcf_common *p; struct nlattr *nest; int i = 0, n_i = 0; + int ret = -EINVAL; nest = nla_nest_start(skb, a->order); if (nest == NULL) @@ -133,10 +136,12 @@ static int tcf_del_walker(struct sk_buff *skb, struct tc_action *a) head = &hinfo->htab[tcf_hash(i, hinfo->hmask)]; hlist_for_each_entry_safe(p, n, head, tcfc_head) { a->priv = p; - if (ACT_P_DELETED == tcf_hash_release(a, 0)) { + ret = tcf_hash_release(a, 0); + if (ret == ACT_P_DELETED) { module_put(a->ops->owner); n_i++; - } + } else if (ret < 0) + goto nla_put_failure; } } if (nla_put_u32(skb, TCA_FCNT, n_i)) @@ -146,7 +151,7 @@ static int tcf_del_walker(struct sk_buff *skb, struct tc_action *a) return n_i; nla_put_failure: nla_nest_cancel(skb, nest); - return -EINVAL; + return ret; } static int tcf_generic_walker(struct sk_buff *skb, struct netlink_callback *cb, @@ -401,16 +406,21 @@ exec_done: } EXPORT_SYMBOL(tcf_action_exec); -void tcf_action_destroy(struct list_head *actions, int bind) +int tcf_action_destroy(struct list_head *actions, int bind) { struct tc_action *a, *tmp; + int ret = 0; list_for_each_entry_safe(a, tmp, actions, list) { - if (tcf_hash_release(a, bind) == ACT_P_DELETED) + ret = tcf_hash_release(a, bind); + if (ret == ACT_P_DELETED) module_put(a->ops->owner); + else if (ret < 0) + return ret; list_del(&a->list); kfree(a); } + return ret; } int @@ -838,7 +848,11 @@ tcf_del_notify(struct net *net, struct nlmsghdr *n, struct list_head *actions, } /* now do the delete */ - tcf_action_destroy(actions, 0); + ret = tcf_action_destroy(actions, 0); + if (ret < 0) { + kfree_skb(skb); + return ret; + } ret = rtnetlink_send(skb, net, portid, RTNLGRP_TC, n->nlmsg_flags & NLM_F_ECHO); -- cgit v1.2.3-71-gd317 From 0f24558e91563888d51e9be5b70981da920c37ac Mon Sep 17 00:00:00 2001 From: Horia Geanta Date: Wed, 12 Feb 2014 16:20:06 +0200 Subject: xfrm: avoid creating temporary SA when there are no listeners In the case when KMs have no listeners, km_query() will fail and temporary SAs are garbage collected immediately after their allocation. This causes strain on memory allocation, leading even to OOM since temporary SA alloc/free cycle is performed for every packet and garbage collection does not keep up the pace. The sane thing to do is to make sure we have audience before temporary SA allocation. Signed-off-by: Horia Geanta Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 15 +++++++++++++++ net/key/af_key.c | 19 +++++++++++++++++++ net/xfrm/xfrm_state.c | 31 +++++++++++++++++++++++++++++++ net/xfrm/xfrm_user.c | 6 ++++++ 4 files changed, 71 insertions(+) (limited to 'include/net') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index afa5730fb3bd..5313ccfdeedf 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -594,6 +594,7 @@ struct xfrm_mgr { const struct xfrm_migrate *m, int num_bundles, const struct xfrm_kmaddress *k); + bool (*is_alive)(const struct km_event *c); }; int xfrm_register_km(struct xfrm_mgr *km); @@ -1646,6 +1647,20 @@ static inline int xfrm_aevent_is_on(struct net *net) rcu_read_unlock(); return ret; } + +static inline int xfrm_acquire_is_on(struct net *net) +{ + struct sock *nlsk; + int ret = 0; + + rcu_read_lock(); + nlsk = rcu_dereference(net->xfrm.nlsk); + if (nlsk) + ret = netlink_has_listeners(nlsk, XFRMNLGRP_ACQUIRE); + rcu_read_unlock(); + + return ret; +} #endif static inline int xfrm_alg_len(const struct xfrm_algo *alg) diff --git a/net/key/af_key.c b/net/key/af_key.c index 1a04c1329362..e1c69d024197 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -3059,6 +3059,24 @@ static u32 get_acqseq(void) return res; } +static bool pfkey_is_alive(const struct km_event *c) +{ + struct netns_pfkey *net_pfkey = net_generic(c->net, pfkey_net_id); + struct sock *sk; + bool is_alive = false; + + rcu_read_lock(); + sk_for_each_rcu(sk, &net_pfkey->table) { + if (pfkey_sk(sk)->registered) { + is_alive = true; + break; + } + } + rcu_read_unlock(); + + return is_alive; +} + static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *xp) { struct sk_buff *skb; @@ -3784,6 +3802,7 @@ static struct xfrm_mgr pfkeyv2_mgr = .new_mapping = pfkey_send_new_mapping, .notify_policy = pfkey_send_policy_notify, .migrate = pfkey_send_migrate, + .is_alive = pfkey_is_alive, }; static int __net_init pfkey_net_init(struct net *net) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index a26b7aa79475..0bf12f665b9b 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -161,6 +161,7 @@ static DEFINE_SPINLOCK(xfrm_state_gc_lock); int __xfrm_state_delete(struct xfrm_state *x); int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol); +bool km_is_alive(const struct km_event *c); void km_state_expired(struct xfrm_state *x, int hard, u32 portid); static DEFINE_SPINLOCK(xfrm_type_lock); @@ -788,6 +789,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr, struct xfrm_state *best = NULL; u32 mark = pol->mark.v & pol->mark.m; unsigned short encap_family = tmpl->encap_family; + struct km_event c; to_put = NULL; @@ -832,6 +834,17 @@ found: error = -EEXIST; goto out; } + + c.net = net; + /* If the KMs have no listeners (yet...), avoid allocating an SA + * for each and every packet - garbage collection might not + * handle the flood. + */ + if (!km_is_alive(&c)) { + error = -ESRCH; + goto out; + } + x = xfrm_state_alloc(net); if (x == NULL) { error = -ENOMEM; @@ -1793,6 +1806,24 @@ int km_report(struct net *net, u8 proto, struct xfrm_selector *sel, xfrm_address } EXPORT_SYMBOL(km_report); +bool km_is_alive(const struct km_event *c) +{ + struct xfrm_mgr *km; + bool is_alive = false; + + rcu_read_lock(); + list_for_each_entry_rcu(km, &xfrm_km_list, list) { + if (km->is_alive && km->is_alive(c)) { + is_alive = true; + break; + } + } + rcu_read_unlock(); + + return is_alive; +} +EXPORT_SYMBOL(km_is_alive); + int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen) { int err; diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index ade9988f6e33..d7694f258294 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -2982,6 +2982,11 @@ static int xfrm_send_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MAPPING, GFP_ATOMIC); } +static bool xfrm_is_alive(const struct km_event *c) +{ + return (bool)xfrm_acquire_is_on(c->net); +} + static struct xfrm_mgr netlink_mgr = { .id = "netlink", .notify = xfrm_send_state_notify, @@ -2991,6 +2996,7 @@ static struct xfrm_mgr netlink_mgr = { .report = xfrm_send_report, .migrate = xfrm_send_migrate, .new_mapping = xfrm_send_mapping, + .is_alive = xfrm_is_alive, }; static int __net_init xfrm_user_net_init(struct net *net) -- cgit v1.2.3-71-gd317 From d5991585d0bc49dca4ff36d18447bb27ad1ccd73 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 10 Jan 2014 02:07:16 -0800 Subject: Bluetooth: Add LMP feature definitions for Secure Connections support The support for Secure Connections introduces two new controller features and one new host feature. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 4 ++++ include/net/bluetooth/hci_core.h | 3 +++ 2 files changed, 7 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 66c1cd87bfe7..cd40219d32aa 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -282,10 +282,14 @@ enum { #define LMP_SYNC_TRAIN 0x04 #define LMP_SYNC_SCAN 0x08 +#define LMP_SC 0x01 +#define LMP_PING 0x02 + /* Host features */ #define LMP_HOST_SSP 0x01 #define LMP_HOST_LE 0x02 #define LMP_HOST_LE_BREDR 0x04 +#define LMP_HOST_SC 0x08 /* Connection modes */ #define HCI_CM_ACTIVE 0x0000 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index f2f0cf5865c4..bb984d0626b7 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -803,9 +803,12 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #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) +#define lmp_sc_capable(dev) ((dev)->features[2][1] & LMP_SC) +#define lmp_ping_capable(dev) ((dev)->features[2][1] & LMP_PING) /* ----- Host capabilities ----- */ #define lmp_host_ssp_capable(dev) ((dev)->features[1][0] & LMP_HOST_SSP) +#define lmp_host_sc_capable(dev) ((dev)->features[1][0] & LMP_HOST_SC) #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)) -- cgit v1.2.3-71-gd317 From eb4b95c627258f0b5cee6c26c8e478dda6941e2b Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 10 Jan 2014 02:07:17 -0800 Subject: Bluetooth: Add HCI command definition for Secure Connections enabling The Secure Connections feature is optional and host stacks have to manually enable it. This add the HCI command definiton for reading and writing this setting. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index cd40219d32aa..2a35d273de2c 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -937,6 +937,17 @@ struct hci_rp_write_sync_train_params { __le16 sync_train_int; } __packed; +#define HCI_OP_READ_SC_SUPPORT 0x0c79 +struct hci_rp_read_sc_support { + __u8 status; + __u8 support; +} __packed; + +#define HCI_OP_WRITE_SC_SUPPORT 0x0c7a +struct hci_cp_write_sc_support { + __u8 support; +} __packed; + #define HCI_OP_READ_LOCAL_VERSION 0x1001 struct hci_rp_read_local_version { __u8 status; -- cgit v1.2.3-71-gd317 From e2f9913157133c3ffab4b835940927879d541b57 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 10 Jan 2014 02:07:18 -0800 Subject: Bluetooth: Add HCI command definition for extended OOB data The Secure Connections feature introduces the support for P-256 strength pairings (compared to P-192 with Secure Simple Pairing). This however means that for out-of-band pairing the hash and randomizer needs to be differentiated. Two new commands are introduced to handle the possible combinations of P-192 and P-256. This add the HCI command definition for both. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 2a35d273de2c..e4e94bfc5232 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -664,6 +664,15 @@ struct hci_rp_set_csb { #define HCI_OP_START_SYNC_TRAIN 0x0443 +#define HCI_OP_REMOTE_OOB_EXT_DATA_REPLY 0x0445 +struct hci_cp_remote_oob_ext_data_reply { + bdaddr_t bdaddr; + __u8 hash192[16]; + __u8 randomizer192[16]; + __u8 hash256[16]; + __u8 randomizer256[16]; +} __packed; + #define HCI_OP_SNIFF_MODE 0x0803 struct hci_cp_sniff_mode { __le16 handle; @@ -948,6 +957,15 @@ struct hci_cp_write_sc_support { __u8 support; } __packed; +#define HCI_OP_READ_LOCAL_OOB_EXT_DATA 0x0c7d +struct hci_rp_read_local_oob_ext_data { + __u8 status; + __u8 hash192[16]; + __u8 randomizer192[16]; + __u8 hash256[16]; + __u8 randomizer256[16]; +} __packed; + #define HCI_OP_READ_LOCAL_VERSION 0x1001 struct hci_rp_read_local_version { __u8 status; -- cgit v1.2.3-71-gd317 From 11015c7903c74350402f8753339c48bee0186e90 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 10 Jan 2014 02:07:19 -0800 Subject: Bluetooth: Add definitions for new link key types With the introduction of Secure Connections, the list of link key types got extended by P-256 versions of authenticated and unauthenticated link keys. To avoid any confusion the previous authenticated and unauthenticated link key types got ammended with a P912 postfix. And the two new keys have a P256 postfix now. Existing code using the previous definitions has been adjusted. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 6 ++++-- net/bluetooth/hci_conn.c | 4 ++-- net/bluetooth/hci_event.c | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index e4e94bfc5232..8d888bc432c6 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -331,9 +331,11 @@ enum { #define HCI_LK_LOCAL_UNIT 0x01 #define HCI_LK_REMOTE_UNIT 0x02 #define HCI_LK_DEBUG_COMBINATION 0x03 -#define HCI_LK_UNAUTH_COMBINATION 0x04 -#define HCI_LK_AUTH_COMBINATION 0x05 +#define HCI_LK_UNAUTH_COMBINATION_P192 0x04 +#define HCI_LK_AUTH_COMBINATION_P192 0x05 #define HCI_LK_CHANGED_COMBINATION 0x06 +#define HCI_LK_UNAUTH_COMBINATION_P256 0x07 +#define HCI_LK_AUTH_COMBINATION_P256 0x08 /* The spec doesn't define types for SMP keys, the _MASTER suffix is implied */ #define HCI_SMP_STK 0x80 #define HCI_SMP_STK_SLAVE 0x81 diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index ba5366c320da..251f22e32fbf 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -802,12 +802,12 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) /* An authenticated combination key has sufficient security for any security level. */ - if (conn->key_type == HCI_LK_AUTH_COMBINATION) + if (conn->key_type == HCI_LK_AUTH_COMBINATION_P192) goto encrypt; /* An unauthenticated combination key has sufficient security for security level 1 and 2. */ - if (conn->key_type == HCI_LK_UNAUTH_COMBINATION && + if (conn->key_type == HCI_LK_UNAUTH_COMBINATION_P192 && (sec_level == BT_SECURITY_MEDIUM || sec_level == BT_SECURITY_LOW)) goto encrypt; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index cfcce448957b..defa1252b534 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2633,7 +2633,7 @@ static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb) conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); if (conn) { - if (key->type == HCI_LK_UNAUTH_COMBINATION && + if (key->type == HCI_LK_UNAUTH_COMBINATION_P192 && conn->auth_type != 0xff && (conn->auth_type & 0x01)) { BT_DBG("%s ignoring unauthenticated key", hdev->name); goto not_found; -- cgit v1.2.3-71-gd317 From e98d2ce293a941d41b5c8435975ff25a1b858bf9 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 10 Jan 2014 02:07:22 -0800 Subject: Bluetooth: Add flags and setting for Secure Connections support The MGMT_SETTING_SECURE_CONN setting is used to track the support and status for Secure Connections from the management interface. For HCI based tracking HCI_SC_ENABLED flag is used. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 1 + include/net/bluetooth/mgmt.h | 1 + net/bluetooth/mgmt.c | 7 +++++++ 3 files changed, 9 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 8d888bc432c6..0253276e88e4 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -122,6 +122,7 @@ enum { HCI_LE_SCAN, HCI_SSP_ENABLED, + HCI_SC_ENABLED, HCI_HS_ENABLED, HCI_LE_ENABLED, HCI_ADVERTISING, diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 518c5c84e39a..4ec17dec62e0 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -94,6 +94,7 @@ struct mgmt_rp_read_index_list { #define MGMT_SETTING_HS 0x00000100 #define MGMT_SETTING_LE 0x00000200 #define MGMT_SETTING_ADVERTISING 0x00000400 +#define MGMT_SETTING_SECURE_CONN 0x00000800 #define MGMT_OP_READ_INFO 0x0004 #define MGMT_READ_INFO_SIZE 0 diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index a03ca3ca91bf..b00fa0253cba 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -79,6 +79,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_SET_BREDR, MGMT_OP_SET_STATIC_ADDRESS, MGMT_OP_SET_SCAN_PARAMS, + MGMT_OP_SET_SECURE_CONN, }; static const u16 mgmt_events[] = { @@ -376,6 +377,9 @@ static u32 get_supported_settings(struct hci_dev *hdev) settings |= MGMT_SETTING_SSP; settings |= MGMT_SETTING_HS; } + + if (lmp_sc_capable(hdev)) + settings |= MGMT_SETTING_SECURE_CONN; } if (lmp_le_capable(hdev)) { @@ -423,6 +427,9 @@ static u32 get_current_settings(struct hci_dev *hdev) if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) settings |= MGMT_SETTING_ADVERTISING; + if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) + settings |= MGMT_SETTING_SECURE_CONN; + return settings; } -- cgit v1.2.3-71-gd317 From eac83dc632a7afba72f7084266bc310219486253 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 10 Jan 2014 02:07:23 -0800 Subject: Bluetooth: Add management command for enabling Secure Connections The support for Secure Connections need to be explicitly enabled by userspace. This is required since only userspace that can handle the new link key types should enable support for Secure Connections. This command handling is similar to how Secure Simple Pairing enabling is done. It also tracks the case when Secure Connections support is enabled via raw HCI commands. This makes sure that the host features page is updated as well. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 1 + include/net/bluetooth/mgmt.h | 2 + net/bluetooth/hci_event.c | 32 ++++++++++++ net/bluetooth/mgmt.c | 106 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 141 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index bb984d0626b7..1eb55ec40ac0 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1125,6 +1125,7 @@ void mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status); void mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status); void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status); +void mgmt_sc_enable_complete(struct hci_dev *hdev, u8 enable, u8 status); void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class, u8 status); void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 4ec17dec62e0..8a2c78175997 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -370,6 +370,8 @@ struct mgmt_cp_set_scan_params { } __packed; #define MGMT_SET_SCAN_PARAMS_SIZE 4 +#define MGMT_OP_SET_SECURE_CONN 0x002D + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index b3c5396e0c1b..b6f0c241e236 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -461,6 +461,34 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) } } +static void hci_cc_write_sc_support(struct hci_dev *hdev, struct sk_buff *skb) +{ + u8 status = *((u8 *) skb->data); + struct hci_cp_write_sc_support *sent; + + BT_DBG("%s status 0x%2.2x", hdev->name, status); + + sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_SC_SUPPORT); + if (!sent) + return; + + if (!status) { + if (sent->support) + hdev->features[1][0] |= LMP_HOST_SC; + else + hdev->features[1][0] &= ~LMP_HOST_SC; + } + + if (test_bit(HCI_MGMT, &hdev->dev_flags)) + mgmt_sc_enable_complete(hdev, sent->support, status); + else if (!status) { + if (sent->support) + set_bit(HCI_SC_ENABLED, &hdev->dev_flags); + else + clear_bit(HCI_SC_ENABLED, &hdev->dev_flags); + } +} + static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_read_local_version *rp = (void *) skb->data; @@ -2147,6 +2175,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_write_ssp_mode(hdev, skb); break; + case HCI_OP_WRITE_SC_SUPPORT: + hci_cc_write_sc_support(hdev, skb); + break; + case HCI_OP_READ_LOCAL_VERSION: hci_cc_read_local_version(hdev, skb); break; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index b00fa0253cba..68a3c998d19c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -4006,6 +4006,79 @@ unlock: return err; } +static int set_secure_conn(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_mode *cp = data; + struct pending_cmd *cmd; + u8 status; + int err; + + BT_DBG("request for %s", hdev->name); + + status = mgmt_bredr_support(hdev); + if (status) + return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN, + status); + + if (!lmp_sc_capable(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN, + MGMT_STATUS_NOT_SUPPORTED); + + if (cp->val != 0x00 && cp->val != 0x01) + return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN, + MGMT_STATUS_INVALID_PARAMS); + + hci_dev_lock(hdev); + + if (!hdev_is_powered(hdev)) { + bool changed; + + if (cp->val) + changed = !test_and_set_bit(HCI_SC_ENABLED, + &hdev->dev_flags); + else + changed = test_and_clear_bit(HCI_SC_ENABLED, + &hdev->dev_flags); + + err = send_settings_rsp(sk, MGMT_OP_SET_SECURE_CONN, hdev); + if (err < 0) + goto failed; + + if (changed) + err = new_settings(hdev, sk); + + goto failed; + } + + if (mgmt_pending_find(MGMT_OP_SET_SECURE_CONN, hdev)) { + err = cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN, + MGMT_STATUS_BUSY); + goto failed; + } + + if (!!cp->val == test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) { + err = send_settings_rsp(sk, MGMT_OP_SET_SECURE_CONN, hdev); + goto failed; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_SET_SECURE_CONN, hdev, data, len); + if (!cmd) { + err = -ENOMEM; + goto failed; + } + + err = hci_send_cmd(hdev, HCI_OP_WRITE_SC_SUPPORT, 1, &cp->val); + if (err < 0) { + mgmt_pending_remove(cmd); + goto failed; + } + +failed: + hci_dev_unlock(hdev); + return err; +} + static bool ltk_is_valid(struct mgmt_ltk_info *key) { if (key->authenticated != 0x00 && key->authenticated != 0x01) @@ -4134,6 +4207,7 @@ static const struct mgmt_handler { { set_bredr, false, MGMT_SETTING_SIZE }, { set_static_address, false, MGMT_SET_STATIC_ADDRESS_SIZE }, { set_scan_params, false, MGMT_SET_SCAN_PARAMS_SIZE }, + { set_secure_conn, false, MGMT_SETTING_SIZE }, }; @@ -4917,6 +4991,38 @@ void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) hci_req_run(&req, NULL); } +void mgmt_sc_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) +{ + struct cmd_lookup match = { NULL, hdev }; + bool changed = false; + + if (status) { + u8 mgmt_err = mgmt_status(status); + + if (enable && test_and_clear_bit(HCI_SC_ENABLED, + &hdev->dev_flags)) + new_settings(hdev, NULL); + + mgmt_pending_foreach(MGMT_OP_SET_SECURE_CONN, hdev, + cmd_status_rsp, &mgmt_err); + return; + } + + if (enable) + changed = !test_and_set_bit(HCI_SC_ENABLED, &hdev->dev_flags); + else + changed = test_and_clear_bit(HCI_SC_ENABLED, &hdev->dev_flags); + + mgmt_pending_foreach(MGMT_OP_SET_SECURE_CONN, hdev, + settings_rsp, &match); + + if (changed) + new_settings(hdev, match.sk); + + if (match.sk) + sock_put(match.sk); +} + static void sk_lookup(struct pending_cmd *cmd, void *data) { struct cmd_lookup *match = data; -- cgit v1.2.3-71-gd317 From 4d2d27962642e23f88745b0430d47c3ff75afdd3 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 10 Jan 2014 02:07:26 -0800 Subject: Bluetooth: Add support for local OOB data with Secure Connections For Secure Connections support and the usage of out-of-band pairing, it is needed to read the P-256 hash and randomizer or P-192 hash and randomizer. This change will read P-192 data when Secure Connections is disabled and P-192 and P-256 data when it is enabled. The difference is between using HCI Read Local OOB Data and using the new HCI Read Local OOB Extended Data command. The first one has been introduced with Bluetooth 2.1 and returns only the P-192 data. < HCI Command: Read Local OOB Data (0x03|0x0057) plen 0 > HCI Event: Command Complete (0x0e) plen 36 Read Local OOB Data (0x03|0x0057) ncmd 1 Status: Success (0x00) Hash C from P-192: 975a59baa1c4eee391477cb410b23e6d Randomizer R with P-192: 9ee63b7dec411d3b467c5ae446df7f7d The second command has been introduced with Bluetooth 4.1 and will return P-192 and P-256 data. < HCI Command: Read Local OOB Extended Data (0x03|0x007d) plen 0 > HCI Event: Command Complete (0x0e) plen 68 Read Local OOB Extended Data (0x03|0x007d) ncmd 1 Status: Success (0x00) Hash C from P-192: 6489731804b156fa6355efb8124a1389 Randomizer R with P-192: 4781d5352fb215b2958222b3937b6026 Hash C from P-256: 69ef8a928b9d07fc149e630e74ecb991 Randomizer R with P-256: 4781d5352fb215b2958222b3937b6026 The change for the management interface is transparent and no change is required for existing userspace. The Secure Connections feature needs to be manually enabled. When it is disabled, then userspace only gets the P-192 returned and with Secure Connections enabled, userspace gets P-192 and P-256 in an extended structure. It is also acceptable to just ignore the P-256 data since it is not required to support them. The pairing with out-of-band credentials will still succeed. However then of course no Secure Connection will b established. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 5 +++-- include/net/bluetooth/mgmt.h | 6 ++++++ net/bluetooth/hci_event.c | 28 ++++++++++++++++++++++----- net/bluetooth/mgmt.c | 41 ++++++++++++++++++++++++++++++++-------- 4 files changed, 65 insertions(+), 15 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 1eb55ec40ac0..bd15eaa4c06e 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1129,8 +1129,9 @@ void mgmt_sc_enable_complete(struct hci_dev *hdev, u8 enable, u8 status); void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class, u8 status); void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status); -void mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, - u8 *randomizer, u8 status); +void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192, + u8 *randomizer192, u8 *hash256, + u8 *randomizer256, u8 status); void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8 ssp, u8 *eir, u16 eir_len); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 8a2c78175997..036ddc7dc7ed 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -295,6 +295,12 @@ struct mgmt_rp_read_local_oob_data { __u8 hash[16]; __u8 randomizer[16]; } __packed; +struct mgmt_rp_read_local_oob_ext_data { + __u8 hash192[16]; + __u8 randomizer192[16]; + __u8 hash256[16]; + __u8 randomizer256[16]; +} __packed; #define MGMT_OP_ADD_REMOTE_OOB_DATA 0x0021 struct mgmt_cp_add_remote_oob_data { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index b6f0c241e236..d5374d36e9fe 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -932,16 +932,30 @@ static void hci_cc_user_passkey_neg_reply(struct hci_dev *hdev, hci_dev_unlock(hdev); } -static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev, - struct sk_buff *skb) +static void hci_cc_read_local_oob_data(struct hci_dev *hdev, + struct sk_buff *skb) { struct hci_rp_read_local_oob_data *rp = (void *) skb->data; BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); hci_dev_lock(hdev); - mgmt_read_local_oob_data_reply_complete(hdev, rp->hash, - rp->randomizer, rp->status); + mgmt_read_local_oob_data_complete(hdev, rp->hash, rp->randomizer, + NULL, NULL, rp->status); + hci_dev_unlock(hdev); +} + +static void hci_cc_read_local_oob_ext_data(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_read_local_oob_ext_data *rp = (void *) skb->data; + + BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); + + hci_dev_lock(hdev); + mgmt_read_local_oob_data_complete(hdev, rp->hash192, rp->randomizer192, + rp->hash256, rp->randomizer256, + rp->status); hci_dev_unlock(hdev); } @@ -2248,7 +2262,11 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) break; case HCI_OP_READ_LOCAL_OOB_DATA: - hci_cc_read_local_oob_data_reply(hdev, skb); + hci_cc_read_local_oob_data(hdev, skb); + break; + + case HCI_OP_READ_LOCAL_OOB_EXT_DATA: + hci_cc_read_local_oob_ext_data(hdev, skb); break; case HCI_OP_LE_READ_BUFFER_SIZE: diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 9b162038acb7..a7d4ae679ab7 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3078,7 +3078,12 @@ static int read_local_oob_data(struct sock *sk, struct hci_dev *hdev, goto unlock; } - err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL); + if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) + err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_EXT_DATA, + 0, NULL); + else + err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL); + if (err < 0) mgmt_pending_remove(cmd); @@ -5077,8 +5082,9 @@ void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status) cmd ? cmd->sk : NULL); } -void mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, - u8 *randomizer, u8 status) +void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192, + u8 *randomizer192, u8 *hash256, + u8 *randomizer256, u8 status) { struct pending_cmd *cmd; @@ -5092,13 +5098,32 @@ void mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, mgmt_status(status)); } else { - struct mgmt_rp_read_local_oob_data rp; + if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags) && + hash256 && randomizer256) { + struct mgmt_rp_read_local_oob_ext_data rp; + + memcpy(rp.hash192, hash192, sizeof(rp.hash192)); + memcpy(rp.randomizer192, randomizer192, + sizeof(rp.randomizer192)); - memcpy(rp.hash, hash, sizeof(rp.hash)); - memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer)); + memcpy(rp.hash256, hash256, sizeof(rp.hash256)); + memcpy(rp.randomizer256, randomizer256, + sizeof(rp.randomizer256)); - cmd_complete(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, - 0, &rp, sizeof(rp)); + cmd_complete(cmd->sk, hdev->id, + MGMT_OP_READ_LOCAL_OOB_DATA, 0, + &rp, sizeof(rp)); + } else { + struct mgmt_rp_read_local_oob_data rp; + + memcpy(rp.hash, hash192, sizeof(rp.hash)); + memcpy(rp.randomizer, randomizer192, + sizeof(rp.randomizer)); + + cmd_complete(cmd->sk, hdev->id, + MGMT_OP_READ_LOCAL_OOB_DATA, 0, + &rp, sizeof(rp)); + } } mgmt_pending_remove(cmd); -- cgit v1.2.3-71-gd317 From 5afeac149ebc94485b750eb841d0f971ea9772cd Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 10 Jan 2014 02:07:27 -0800 Subject: Bluetooth: Add debugfs quirk for forcing Secure Connections support The Bluetooth 4.1 specification with Secure Connections support has just been released and controllers with this feature are still in an early stage. A handful of controllers have already support for it, but they do not always identify this feature correctly. This debugfs entry allows to tell the kernel that the controller can be treated as it would fully support Secure Connections. Using debugfs to force Secure Connections support of course does not make this feature magically appear in all controllers. This is a debug functionality for early adopters. Once the majority of controllers matures this quirk will be removed. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_core.c | 51 ++++++++++++++++++++++++++++++++++++++++++++- net/bluetooth/mgmt.c | 6 ++++-- 3 files changed, 55 insertions(+), 3 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 0253276e88e4..2bc19881e250 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -117,6 +117,7 @@ enum { HCI_SERVICE_CACHE, HCI_DEBUG_KEYS, HCI_DUT_MODE, + HCI_FORCE_SC, HCI_UNREGISTER, HCI_USER_CHANNEL, diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index b3b619a448b5..946631ffe802 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -415,6 +415,52 @@ static int ssp_debug_mode_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(ssp_debug_mode_fops, ssp_debug_mode_get, ssp_debug_mode_set, "%llu\n"); +static ssize_t force_sc_support_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct hci_dev *hdev = file->private_data; + char buf[3]; + + buf[0] = test_bit(HCI_FORCE_SC, &hdev->dev_flags) ? 'Y': 'N'; + buf[1] = '\n'; + buf[2] = '\0'; + return simple_read_from_buffer(user_buf, count, ppos, buf, 2); +} + +static ssize_t force_sc_support_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct hci_dev *hdev = file->private_data; + char buf[32]; + size_t buf_size = min(count, (sizeof(buf)-1)); + bool enable; + + if (test_bit(HCI_UP, &hdev->flags)) + return -EBUSY; + + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + buf[buf_size] = '\0'; + if (strtobool(buf, &enable)) + return -EINVAL; + + if (enable == test_bit(HCI_FORCE_SC, &hdev->dev_flags)) + return -EALREADY; + + change_bit(HCI_FORCE_SC, &hdev->dev_flags); + + return count; +} + +static const struct file_operations force_sc_support_fops = { + .open = simple_open, + .read = force_sc_support_read, + .write = force_sc_support_write, + .llseek = default_llseek, +}; + static int idle_timeout_set(void *data, u64 val) { struct hci_dev *hdev = data; @@ -1365,7 +1411,8 @@ static void hci_init4_req(struct hci_request *req, unsigned long opt) hci_req_add(req, HCI_OP_READ_SYNC_TRAIN_PARAMS, 0, NULL); /* Enable Secure Connections if supported and configured */ - if (lmp_sc_capable(hdev) && + if ((lmp_sc_capable(hdev) || + test_bit(HCI_FORCE_SC, &hdev->dev_flags)) && test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) { u8 support = 0x01; hci_req_add(req, HCI_OP_WRITE_SC_SUPPORT, @@ -1442,6 +1489,8 @@ static int __hci_init(struct hci_dev *hdev) hdev, &auto_accept_delay_fops); debugfs_create_file("ssp_debug_mode", 0644, hdev->debugfs, hdev, &ssp_debug_mode_fops); + debugfs_create_file("force_sc_support", 0644, hdev->debugfs, + hdev, &force_sc_support_fops); } if (lmp_sniff_capable(hdev)) { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index a7d4ae679ab7..bbe30c983492 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -378,7 +378,8 @@ static u32 get_supported_settings(struct hci_dev *hdev) settings |= MGMT_SETTING_HS; } - if (lmp_sc_capable(hdev)) + if (lmp_sc_capable(hdev) || + test_bit(HCI_FORCE_SC, &hdev->dev_flags)) settings |= MGMT_SETTING_SECURE_CONN; } @@ -4026,7 +4027,8 @@ static int set_secure_conn(struct sock *sk, struct hci_dev *hdev, return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN, status); - if (!lmp_sc_capable(hdev)) + if (!lmp_sc_capable(hdev) && + !test_bit(HCI_FORCE_SC, &hdev->dev_flags)) return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN, MGMT_STATUS_NOT_SUPPORTED); -- cgit v1.2.3-71-gd317 From 519ca9d017ab7eb4a15787bd8f2d867bebe375bc Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 10 Jan 2014 02:07:28 -0800 Subject: Bluetooth: Provide remote OOB data for Secure Connections When Secure Connections has been enabled it is possible to provide P-192 and/or P-256 data during the pairing process. The internal out-of-band credentials storage has been extended to also hold P-256 data. Initially the P-256 data will be empty and with Secure Connections enabled no P-256 data will be provided. This is according to the specification since it might be possible that the remote side did not provide either of the out-of-band credentials. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 6 ++++-- net/bluetooth/hci_core.c | 6 +++--- net/bluetooth/hci_event.c | 32 ++++++++++++++++++++++++-------- 3 files changed, 31 insertions(+), 13 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index bd15eaa4c06e..5948930f92e6 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -114,8 +114,10 @@ struct link_key { struct oob_data { struct list_head list; bdaddr_t bdaddr; - u8 hash[16]; - u8 randomizer[16]; + u8 hash192[16]; + u8 randomizer192[16]; + u8 hash256[16]; + u8 randomizer256[16]; }; #define HCI_MAX_SHORT_NAME_LENGTH 10 diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 946631ffe802..f13c0550f368 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2802,7 +2802,7 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, data = hci_find_remote_oob_data(hdev, bdaddr); if (!data) { - data = kmalloc(sizeof(*data), GFP_ATOMIC); + data = kzalloc(sizeof(*data), GFP_ATOMIC); if (!data) return -ENOMEM; @@ -2810,8 +2810,8 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, list_add(&data->list, &hdev->remote_oob_data); } - memcpy(data->hash, hash, sizeof(data->hash)); - memcpy(data->randomizer, randomizer, sizeof(data->randomizer)); + memcpy(data->hash192, hash, sizeof(data->hash192)); + memcpy(data->randomizer192, randomizer, sizeof(data->randomizer192)); BT_DBG("%s for %pMR", hdev->name, bdaddr); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index d5374d36e9fe..da1eca1c43db 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3391,20 +3391,36 @@ static void hci_remote_oob_data_request_evt(struct hci_dev *hdev, data = hci_find_remote_oob_data(hdev, &ev->bdaddr); if (data) { - struct hci_cp_remote_oob_data_reply cp; + if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) { + struct hci_cp_remote_oob_ext_data_reply cp; - bacpy(&cp.bdaddr, &ev->bdaddr); - memcpy(cp.hash, data->hash, sizeof(cp.hash)); - memcpy(cp.randomizer, data->randomizer, sizeof(cp.randomizer)); + bacpy(&cp.bdaddr, &ev->bdaddr); + memcpy(cp.hash192, data->hash192, sizeof(cp.hash192)); + memcpy(cp.randomizer192, data->randomizer192, + sizeof(cp.randomizer192)); + memcpy(cp.hash256, data->hash256, sizeof(cp.hash256)); + memcpy(cp.randomizer256, data->randomizer256, + sizeof(cp.randomizer256)); + + hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_EXT_DATA_REPLY, + sizeof(cp), &cp); + } else { + struct hci_cp_remote_oob_data_reply cp; - hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_REPLY, sizeof(cp), - &cp); + bacpy(&cp.bdaddr, &ev->bdaddr); + memcpy(cp.hash, data->hash192, sizeof(cp.hash)); + memcpy(cp.randomizer, data->randomizer192, + sizeof(cp.randomizer)); + + hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_REPLY, + sizeof(cp), &cp); + } } else { struct hci_cp_remote_oob_data_neg_reply cp; bacpy(&cp.bdaddr, &ev->bdaddr); - hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_NEG_REPLY, sizeof(cp), - &cp); + hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_NEG_REPLY, + sizeof(cp), &cp); } unlock: -- cgit v1.2.3-71-gd317 From 0798872ef1ad6433362faca1d16a31ad7ad72638 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 10 Jan 2014 02:07:29 -0800 Subject: Bluetooth: Add internal function for storing P-192 and P-256 data Add function to allow adding P-192 and P-256 data to the internal storage. This also fixes a few coding style issues from the previous helper functions for the out-of-band credentials storage. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 9 ++++++--- net/bluetooth/hci_core.c | 37 +++++++++++++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 7 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 5948930f92e6..66e96ebffe97 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -761,9 +761,12 @@ int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_remote_oob_data_clear(struct hci_dev *hdev); struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev, - bdaddr_t *bdaddr); -int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, - u8 *randomizer); + bdaddr_t *bdaddr); +int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 *hash, u8 *randomizer); +int hci_add_remote_oob_ext_data(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 *hash192, u8 *randomizer192, + u8 *hash256, u8 *randomizer256); int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr); void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index f13c0550f368..499ec1b1095d 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2794,15 +2794,14 @@ int hci_remote_oob_data_clear(struct hci_dev *hdev) return 0; } -int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, - u8 *randomizer) +int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 *hash, u8 *randomizer) { struct oob_data *data; data = hci_find_remote_oob_data(hdev, bdaddr); - if (!data) { - data = kzalloc(sizeof(*data), GFP_ATOMIC); + data = kmalloc(sizeof(*data), GFP_ATOMIC); if (!data) return -ENOMEM; @@ -2813,6 +2812,36 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, memcpy(data->hash192, hash, sizeof(data->hash192)); memcpy(data->randomizer192, randomizer, sizeof(data->randomizer192)); + memset(data->hash256, 0, sizeof(data->hash256)); + memset(data->randomizer256, 0, sizeof(data->randomizer256)); + + BT_DBG("%s for %pMR", hdev->name, bdaddr); + + return 0; +} + +int hci_add_remote_oob_ext_data(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 *hash192, u8 *randomizer192, + u8 *hash256, u8 *randomizer256) +{ + struct oob_data *data; + + data = hci_find_remote_oob_data(hdev, bdaddr); + if (!data) { + data = kmalloc(sizeof(*data), GFP_ATOMIC); + if (!data) + return -ENOMEM; + + bacpy(&data->bdaddr, bdaddr); + list_add(&data->list, &hdev->remote_oob_data); + } + + memcpy(data->hash192, hash192, sizeof(data->hash192)); + memcpy(data->randomizer192, randomizer192, sizeof(data->randomizer192)); + + memcpy(data->hash256, hash256, sizeof(data->hash256)); + memcpy(data->randomizer256, randomizer256, sizeof(data->randomizer256)); + BT_DBG("%s for %pMR", hdev->name, bdaddr); return 0; -- cgit v1.2.3-71-gd317 From ec1091131f9b53ea280247b5a01a617ce87d399e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 10 Jan 2014 02:07:30 -0800 Subject: Bluetooth: Add support for remote OOB input of P-256 data The current management interface only allows to provide the remote OOB input of P-192 data. This extends the command to also accept P-256 data as well. To make this backwards compatible, the userspace can decide to only provide P-192 data or the combined P-192 and P-256 data. It is also allowed to leave the P-192 data empty if userspace only has the remote P-256 data. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 8 ++++++++ net/bluetooth/mgmt.c | 45 +++++++++++++++++++++++++++++++++----------- 2 files changed, 42 insertions(+), 11 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 036ddc7dc7ed..e19049fb6c46 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -309,6 +309,14 @@ struct mgmt_cp_add_remote_oob_data { __u8 randomizer[16]; } __packed; #define MGMT_ADD_REMOTE_OOB_DATA_SIZE (MGMT_ADDR_INFO_SIZE + 32) +struct mgmt_cp_add_remote_oob_ext_data { + struct mgmt_addr_info addr; + __u8 hash192[16]; + __u8 randomizer192[16]; + __u8 hash256[16]; + __u8 randomizer256[16]; +} __packed; +#define MGMT_ADD_REMOTE_OOB_EXT_DATA_SIZE (MGMT_ADDR_INFO_SIZE + 64) #define MGMT_OP_REMOVE_REMOTE_OOB_DATA 0x0022 struct mgmt_cp_remove_remote_oob_data { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index bbe30c983492..4b6034fcc902 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3096,23 +3096,46 @@ unlock: static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { - struct mgmt_cp_add_remote_oob_data *cp = data; - u8 status; int err; BT_DBG("%s ", hdev->name); hci_dev_lock(hdev); - err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, cp->hash, - cp->randomizer); - if (err < 0) - status = MGMT_STATUS_FAILED; - else - status = MGMT_STATUS_SUCCESS; + if (len == MGMT_ADD_REMOTE_OOB_DATA_SIZE) { + struct mgmt_cp_add_remote_oob_data *cp = data; + u8 status; - err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, status, - &cp->addr, sizeof(cp->addr)); + err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, + cp->hash, cp->randomizer); + if (err < 0) + status = MGMT_STATUS_FAILED; + else + status = MGMT_STATUS_SUCCESS; + + err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, + status, &cp->addr, sizeof(cp->addr)); + } else if (len == MGMT_ADD_REMOTE_OOB_EXT_DATA_SIZE) { + struct mgmt_cp_add_remote_oob_ext_data *cp = data; + u8 status; + + err = hci_add_remote_oob_ext_data(hdev, &cp->addr.bdaddr, + cp->hash192, + cp->randomizer192, + cp->hash256, + cp->randomizer256); + if (err < 0) + status = MGMT_STATUS_FAILED; + else + status = MGMT_STATUS_SUCCESS; + + err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, + status, &cp->addr, sizeof(cp->addr)); + } else { + BT_ERR("add_remote_oob_data: invalid length of %u bytes", len); + err = cmd_status(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, + MGMT_STATUS_INVALID_PARAMS); + } hci_dev_unlock(hdev); return err; @@ -4202,7 +4225,7 @@ static const struct mgmt_handler { { user_passkey_reply, false, MGMT_USER_PASSKEY_REPLY_SIZE }, { user_passkey_neg_reply, false, MGMT_USER_PASSKEY_NEG_REPLY_SIZE }, { read_local_oob_data, false, MGMT_READ_LOCAL_OOB_DATA_SIZE }, - { add_remote_oob_data, false, MGMT_ADD_REMOTE_OOB_DATA_SIZE }, + { add_remote_oob_data, true, MGMT_ADD_REMOTE_OOB_DATA_SIZE }, { remove_remote_oob_data, false, MGMT_REMOVE_REMOTE_OOB_DATA_SIZE }, { start_discovery, false, MGMT_START_DISCOVERY_SIZE }, { stop_discovery, false, MGMT_STOP_DISCOVERY_SIZE }, -- cgit v1.2.3-71-gd317 From eb9a8f3fb6762a4e6ae0aa9e96532c9c544f400e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 15 Jan 2014 22:37:38 -0800 Subject: Bluetooth: Track Secure Connections support of remote devices It is important to know if Secure Connections support has been enabled for a given remote device. The information is provided in the remote host features page. So track this information and provide a simple helper function to extract the status. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 8 ++++++++ net/bluetooth/hci_event.c | 3 +++ 2 files changed, 11 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 66e96ebffe97..8d225e4ea2ce 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -448,6 +448,7 @@ enum { HCI_CONN_LE_SMP_PEND, HCI_CONN_MGMT_CONNECTED, HCI_CONN_SSP_ENABLED, + HCI_CONN_SC_ENABLED, HCI_CONN_POWER_SAVE, HCI_CONN_REMOTE_OOB, HCI_CONN_6LOWPAN, @@ -460,6 +461,13 @@ static inline bool hci_conn_ssp_enabled(struct hci_conn *conn) test_bit(HCI_CONN_SSP_ENABLED, &conn->flags); } +static inline bool hci_conn_sc_enabled(struct hci_conn *conn) +{ + struct hci_dev *hdev = conn->hdev; + return test_bit(HCI_SC_ENABLED, &hdev->dev_flags) && + test_bit(HCI_CONN_SC_ENABLED, &conn->flags); +} + static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c) { struct hci_conn_hash *h = &hdev->conn_hash; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index da1eca1c43db..8c44bbe19add 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2898,6 +2898,9 @@ static void hci_remote_ext_features_evt(struct hci_dev *hdev, * features do not indicate SSP support */ clear_bit(HCI_CONN_SSP_ENABLED, &conn->flags); } + + if (ev->features[0] & LMP_HOST_SC) + set_bit(HCI_CONN_SC_ENABLED, &conn->flags); } if (conn->state != BT_CONFIG) -- cgit v1.2.3-71-gd317 From 7b5a9241b780ea2f77e71647bc0d3c9708c18ef1 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 15 Jan 2014 22:37:39 -0800 Subject: Bluetooth: Introduce requirements for security level 4 The security level 4 is a new strong security requirement that is based around 128-bit equivalent strength for link and encryption keys required using FIPS approved algorithms. Which means that E0, SAFER+ and P-192 are not allowed. Only connections created with P-256 resulting from using Secure Connections support are allowed. This security level needs to be enforced when Secure Connection Only mode is enabled for a controller or a service requires FIPS compliant strong security. Currently it is not possible to enable either of these two cases. This patch just puts in the foundation for being able to handle security level 4 in the future. It should be noted that devices or services with security level 4 requirement can only communicate using Bluetooth 4.1 controllers with support for Secure Connections. There is no backward compatibilty if used with older hardware. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/bluetooth.h | 1 + include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_conn.c | 18 +++++++++++++----- 3 files changed, 15 insertions(+), 5 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index f4f9ee466791..904777c1cd24 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -65,6 +65,7 @@ struct bt_security { #define BT_SECURITY_LOW 1 #define BT_SECURITY_MEDIUM 2 #define BT_SECURITY_HIGH 3 +#define BT_SECURITY_FIPS 4 #define BT_DEFER_SETUP 7 diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 2bc19881e250..0064a9aa5df1 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -313,6 +313,7 @@ enum { #define HCI_LM_TRUSTED 0x0008 #define HCI_LM_RELIABLE 0x0010 #define HCI_LM_SECURE 0x0020 +#define HCI_LM_FIPS 0x0040 /* Authentication types */ #define HCI_AT_NO_BONDING 0x00 diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index cf96b3438a91..0266bd8e4913 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -800,10 +800,17 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) if (!(conn->link_mode & HCI_LM_AUTH)) goto auth; - /* An authenticated combination key has sufficient security for any - security level. */ - if (conn->key_type == HCI_LK_AUTH_COMBINATION_P192 || - conn->key_type == HCI_LK_AUTH_COMBINATION_P256) + /* An authenticated FIPS approved combination key has sufficient + * security for security level 4. */ + if (conn->key_type == HCI_LK_AUTH_COMBINATION_P256 && + sec_level == BT_SECURITY_FIPS) + goto encrypt; + + /* An authenticated combination key has sufficient security for + security level 3. */ + if ((conn->key_type == HCI_LK_AUTH_COMBINATION_P192 || + conn->key_type == HCI_LK_AUTH_COMBINATION_P256) && + sec_level == BT_SECURITY_HIGH) goto encrypt; /* An unauthenticated combination key has sufficient security for @@ -818,7 +825,8 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) is generated using maximum PIN code length (16). For pre 2.1 units. */ if (conn->key_type == HCI_LK_COMBINATION && - (sec_level != BT_SECURITY_HIGH || conn->pin_length == 16)) + (sec_level == BT_SECURITY_MEDIUM || sec_level == BT_SECURITY_LOW || + conn->pin_length == 16)) goto encrypt; auth: -- cgit v1.2.3-71-gd317 From 7d513e9243afd01df315db45ffe96a6e3688e612 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 15 Jan 2014 22:37:40 -0800 Subject: Bluetooth: Handle security level 4 for L2CAP connections With the introduction of security level 4, the L2CAP sockets need to be made aware of this new level. This change ensures that the pairing requirements are set correctly for these connections. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap_core.c | 11 ++++++++--- net/bluetooth/l2cap_sock.c | 10 ++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index dbc4a89984ca..c695083eee2b 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -91,6 +91,7 @@ struct l2cap_conninfo { #define L2CAP_LM_TRUSTED 0x0008 #define L2CAP_LM_RELIABLE 0x0010 #define L2CAP_LM_SECURE 0x0020 +#define L2CAP_LM_FIPS 0x0040 /* L2CAP command codes */ #define L2CAP_COMMAND_REJ 0x01 diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index b0ad2c752d73..3f0dd552cb2b 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -737,6 +737,7 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan) case L2CAP_CHAN_RAW: switch (chan->sec_level) { case BT_SECURITY_HIGH: + case BT_SECURITY_FIPS: return HCI_AT_DEDICATED_BONDING_MITM; case BT_SECURITY_MEDIUM: return HCI_AT_DEDICATED_BONDING; @@ -749,7 +750,8 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan) if (chan->sec_level == BT_SECURITY_LOW) chan->sec_level = BT_SECURITY_SDP; } - if (chan->sec_level == BT_SECURITY_HIGH) + if (chan->sec_level == BT_SECURITY_HIGH || + chan->sec_level == BT_SECURITY_FIPS) return HCI_AT_NO_BONDING_MITM; else return HCI_AT_NO_BONDING; @@ -759,7 +761,8 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan) if (chan->sec_level == BT_SECURITY_LOW) chan->sec_level = BT_SECURITY_SDP; - if (chan->sec_level == BT_SECURITY_HIGH) + if (chan->sec_level == BT_SECURITY_HIGH || + chan->sec_level == BT_SECURITY_FIPS) return HCI_AT_NO_BONDING_MITM; else return HCI_AT_NO_BONDING; @@ -768,6 +771,7 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan) default: switch (chan->sec_level) { case BT_SECURITY_HIGH: + case BT_SECURITY_FIPS: return HCI_AT_GENERAL_BONDING_MITM; case BT_SECURITY_MEDIUM: return HCI_AT_GENERAL_BONDING; @@ -7206,7 +7210,8 @@ static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt) if (encrypt == 0x00) { if (chan->sec_level == BT_SECURITY_MEDIUM) { __set_chan_timer(chan, L2CAP_ENC_TIMEOUT); - } else if (chan->sec_level == BT_SECURITY_HIGH) + } else if (chan->sec_level == BT_SECURITY_HIGH || + chan->sec_level == BT_SECURITY_FIPS) l2cap_chan_close(chan, ECONNREFUSED); } else { if (chan->sec_level == BT_SECURITY_MEDIUM) diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index d58f76bcebd1..fe086b4efc0c 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -432,6 +432,10 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE; break; + case BT_SECURITY_FIPS: + opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | + L2CAP_LM_SECURE | L2CAP_LM_FIPS; + break; default: opt = 0; break; @@ -445,6 +449,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, if (put_user(opt, (u32 __user *) optval)) err = -EFAULT; + break; case L2CAP_CONNINFO: @@ -699,6 +704,11 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, break; } + if (opt & L2CAP_LM_FIPS) { + err = -EINVAL; + break; + } + if (opt & L2CAP_LM_AUTH) chan->sec_level = BT_SECURITY_LOW; if (opt & L2CAP_LM_ENCRYPT) -- cgit v1.2.3-71-gd317 From 2c068e0b924c6fabd9a2ac59bc451b4b656cbae3 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 15 Jan 2014 22:37:41 -0800 Subject: Bluetooth: Handle security level 4 for RFCOMM connections With the introduction of security level 4, the RFCOMM sockets need to be made aware of this new level. This change ensures that the pairing requirements are set correctly for these connections. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/rfcomm.h | 1 + net/bluetooth/rfcomm/core.c | 4 +++- net/bluetooth/rfcomm/sock.c | 12 +++++++++++- 3 files changed, 15 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index 486213a1aed8..c312cfc4e922 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -295,6 +295,7 @@ struct rfcomm_conninfo { #define RFCOMM_LM_TRUSTED 0x0008 #define RFCOMM_LM_RELIABLE 0x0010 #define RFCOMM_LM_SECURE 0x0020 +#define RFCOMM_LM_FIPS 0x0040 #define rfcomm_pi(sk) ((struct rfcomm_pinfo *) sk) diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index facd8a79c038..ba115d472f7b 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -216,6 +216,7 @@ static int rfcomm_check_security(struct rfcomm_dlc *d) switch (d->sec_level) { case BT_SECURITY_HIGH: + case BT_SECURITY_FIPS: auth_type = HCI_AT_GENERAL_BONDING_MITM; break; case BT_SECURITY_MEDIUM: @@ -2085,7 +2086,8 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt) set_bit(RFCOMM_SEC_PENDING, &d->flags); rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); continue; - } else if (d->sec_level == BT_SECURITY_HIGH) { + } else if (d->sec_level == BT_SECURITY_HIGH || + d->sec_level == BT_SECURITY_FIPS) { set_bit(RFCOMM_ENC_DROP, &d->flags); continue; } diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 3c2d3e4aa2f5..fb8158af1f39 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -648,6 +648,11 @@ static int rfcomm_sock_setsockopt_old(struct socket *sock, int optname, char __u break; } + if (opt & RFCOMM_LM_FIPS) { + err = -EINVAL; + break; + } + if (opt & RFCOMM_LM_AUTH) rfcomm_pi(sk)->sec_level = BT_SECURITY_LOW; if (opt & RFCOMM_LM_ENCRYPT) @@ -762,7 +767,11 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u break; case BT_SECURITY_HIGH: opt = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | - RFCOMM_LM_SECURE; + RFCOMM_LM_SECURE; + break; + case BT_SECURITY_FIPS: + opt = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | + RFCOMM_LM_SECURE | RFCOMM_LM_FIPS; break; default: opt = 0; @@ -774,6 +783,7 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u if (put_user(opt, (u32 __user *) optval)) err = -EFAULT; + break; case RFCOMM_CONNINFO: -- cgit v1.2.3-71-gd317 From 134c2a89af22f500b1d7525d663fddda345ff01e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 15 Jan 2014 22:37:42 -0800 Subject: Bluetooth: Add debugfs entry to show Secure Connections Only mode For debugging purposes of Secure Connection Only support a simple debugfs entry is used to indicate if this mode is active or not. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_core.c | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 0064a9aa5df1..232c07804ca8 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -124,6 +124,7 @@ enum { HCI_LE_SCAN, HCI_SSP_ENABLED, HCI_SC_ENABLED, + HCI_SC_ONLY, HCI_HS_ENABLED, HCI_LE_ENABLED, HCI_ADVERTISING, diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 499ec1b1095d..369d30750417 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -461,6 +461,24 @@ static const struct file_operations force_sc_support_fops = { .llseek = default_llseek, }; +static ssize_t sc_only_mode_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct hci_dev *hdev = file->private_data; + char buf[3]; + + buf[0] = test_bit(HCI_SC_ONLY, &hdev->dev_flags) ? 'Y': 'N'; + buf[1] = '\n'; + buf[2] = '\0'; + return simple_read_from_buffer(user_buf, count, ppos, buf, 2); +} + +static const struct file_operations sc_only_mode_fops = { + .open = simple_open, + .read = sc_only_mode_read, + .llseek = default_llseek, +}; + static int idle_timeout_set(void *data, u64 val) { struct hci_dev *hdev = data; @@ -1491,6 +1509,8 @@ static int __hci_init(struct hci_dev *hdev) hdev, &ssp_debug_mode_fops); debugfs_create_file("force_sc_support", 0644, hdev->debugfs, hdev, &force_sc_support_fops); + debugfs_create_file("sc_only_mode", 0444, hdev->debugfs, + hdev, &sc_only_mode_fops); } if (lmp_sniff_capable(hdev)) { -- cgit v1.2.3-71-gd317 From 61a939c68ee033d43be3aa436d95eb8afdd16142 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 17 Jan 2014 20:45:11 +0200 Subject: Bluetooth: Queue incoming ACL data until BT_CONNECTED state is reached This patch adds a queue for incoming L2CAP data that's received before l2cap_connect_cfm is called and processes the data once l2cap_connect_cfm is called. This way we ensure that we have e.g. all remote features before processing L2CAP signaling data (which is very important for making the correct security decisions). The processing of the pending rx data needs to be done through queue_work since unlike l2cap_recv_acldata, l2cap_connect_cfm is called with the hci_dev lock held which could cause potential deadlocks. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 3 +++ net/bluetooth/l2cap_core.c | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index c695083eee2b..85cf40acc47e 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -624,6 +624,9 @@ struct l2cap_conn { __u32 rx_len; __u8 tx_ident; + struct sk_buff_head pending_rx; + struct work_struct pending_rx_work; + __u8 disc_reason; struct delayed_work security_timer; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 317a5737daf6..cd534599fbfa 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1550,6 +1550,8 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) } mutex_unlock(&conn->chan_lock); + + queue_work(hcon->hdev->workqueue, &conn->pending_rx_work); } /* Notify sockets that we cannot guaranty reliability anymore */ @@ -1675,6 +1677,9 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) kfree_skb(conn->rx_skb); + skb_queue_purge(&conn->pending_rx); + flush_work(&conn->pending_rx_work); + l2cap_unregister_all_users(conn); mutex_lock(&conn->chan_lock); @@ -6880,9 +6885,16 @@ drop: static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) { struct l2cap_hdr *lh = (void *) skb->data; + struct hci_conn *hcon = conn->hcon; u16 cid, len; __le16 psm; + if (hcon->state != BT_CONNECTED) { + BT_DBG("queueing pending rx skb"); + skb_queue_tail(&conn->pending_rx, skb); + return; + } + skb_pull(skb, L2CAP_HDR_SIZE); cid = __le16_to_cpu(lh->cid); len = __le16_to_cpu(lh->len); @@ -6928,6 +6940,18 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) } } +static void process_pending_rx(struct work_struct *work) +{ + struct l2cap_conn *conn = container_of(work, struct l2cap_conn, + pending_rx_work); + struct sk_buff *skb; + + BT_DBG(""); + + while ((skb = skb_dequeue(&conn->pending_rx))) + l2cap_recv_frame(conn, skb); +} + static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon) { struct l2cap_conn *conn = hcon->l2cap_data; @@ -6983,6 +7007,9 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon) else INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout); + skb_queue_head_init(&conn->pending_rx); + INIT_WORK(&conn->pending_rx_work, process_pending_rx); + conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM; return conn; -- cgit v1.2.3-71-gd317 From 2338a7e0440d646c194d421748ea36665e648384 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 24 Jan 2014 10:35:40 +0200 Subject: Bluetooth: Rename L2CAP_CHAN_CONN_FIX_A2MP to L2CAP_CHAN_FIXED There's no reason why A2MP should need or deserve its on channel type. Instead we should be able to group all fixed CID users under a single channel type and reuse as much code as possible for them. Where CID specific exceptions are needed the chan-scid value can be used. This patch renames the current A2MP channel type to a generic one and thereby paves the way to allow converting ATT and SMP (and any future fixed channel protocols) to use the new channel type. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 2 +- net/bluetooth/a2mp.c | 8 ++++++-- net/bluetooth/l2cap_core.c | 15 ++++++--------- 3 files changed, 13 insertions(+), 12 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 85cf40acc47e..ae482f41594a 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -651,7 +651,7 @@ struct l2cap_user { #define L2CAP_CHAN_RAW 1 #define L2CAP_CHAN_CONN_LESS 2 #define L2CAP_CHAN_CONN_ORIENTED 3 -#define L2CAP_CHAN_CONN_FIX_A2MP 4 +#define L2CAP_CHAN_FIXED 4 /* ----- L2CAP socket info ----- */ #define l2cap_pi(sk) ((struct l2cap_pinfo *) sk) diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index efcd108822c4..f986b9968bdb 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -235,7 +235,7 @@ static int a2mp_discover_rsp(struct amp_mgr *mgr, struct sk_buff *skb, BT_DBG("chan %p state %s", chan, state_to_string(chan->state)); - if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) + if (chan->scid == L2CAP_CID_A2MP) continue; l2cap_chan_lock(chan); @@ -726,7 +726,11 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked) BT_DBG("chan %p", chan); - chan->chan_type = L2CAP_CHAN_CONN_FIX_A2MP; + chan->chan_type = L2CAP_CHAN_FIXED; + chan->scid = L2CAP_CID_A2MP; + chan->dcid = L2CAP_CID_A2MP; + chan->omtu = L2CAP_A2MP_DEFAULT_MTU; + chan->imtu = L2CAP_A2MP_DEFAULT_MTU; chan->flush_to = L2CAP_DEFAULT_FLUSH_TO; chan->ops = &a2mp_chan_ops; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 138394ad3e51..cd28057d2903 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -519,11 +519,8 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) chan->omtu = L2CAP_DEFAULT_MTU; break; - case L2CAP_CHAN_CONN_FIX_A2MP: - chan->scid = L2CAP_CID_A2MP; - chan->dcid = L2CAP_CID_A2MP; - chan->omtu = L2CAP_A2MP_DEFAULT_MTU; - chan->imtu = L2CAP_A2MP_DEFAULT_MTU; + case L2CAP_CHAN_FIXED: + /* Caller will set CID and CID specific MTU values */ break; default: @@ -571,7 +568,7 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) chan->conn = NULL; - if (chan->chan_type != L2CAP_CHAN_CONN_FIX_A2MP) + if (chan->scid != L2CAP_CID_A2MP) hci_conn_drop(conn->hcon); if (mgr && mgr->bredr_chan == chan) @@ -1310,7 +1307,7 @@ static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err) __clear_ack_timer(chan); } - if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) { + if (chan->scid == L2CAP_CID_A2MP) { l2cap_state_change(chan, BT_DISCONN); return; } @@ -1508,7 +1505,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) l2cap_chan_lock(chan); - if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) { + if (chan->scid == L2CAP_CID_A2MP) { l2cap_chan_unlock(chan); continue; } @@ -7245,7 +7242,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) BT_DBG("chan %p scid 0x%4.4x state %s", chan, chan->scid, state_to_string(chan->state)); - if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) { + if (chan->scid == L2CAP_CID_A2MP) { l2cap_chan_unlock(chan); continue; } -- cgit v1.2.3-71-gd317 From 98a0b845c63cb74e90a72d1e864ea4be968bdd83 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 30 Jan 2014 19:40:00 -0800 Subject: Bluetooth: Fix differentiating stored master vs slave LTK types If LTK distribution happens in both directions we will have two LTKs for the same remote device: one which is used when we're connecting as master and another when we're connecting as slave. When looking up LTKs from the locally stored list we shouldn't blindly return the first match but also consider which type of key is in question. If we do not do this we may end up selecting an incorrect encryption key for a connection. This patch fixes the issue by always specifying to the LTK lookup functions whether we're looking for a master or a slave key. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 5 +++-- net/bluetooth/hci_core.c | 22 ++++++++++++++++++---- net/bluetooth/hci_event.c | 2 +- net/bluetooth/smp.c | 3 ++- 4 files changed, 24 insertions(+), 8 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 8d225e4ea2ce..378e2f32cfa0 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -757,12 +757,13 @@ int hci_link_keys_clear(struct hci_dev *hdev); struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len); -struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8]); +struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8], + bool master); int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type, int new_key, u8 authenticated, u8 tk[16], u8 enc_size, __le16 ediv, u8 rand[8]); struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 addr_type); + u8 addr_type, bool master); int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_smp_ltks_clear(struct hci_dev *hdev); int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 180473d965f6..d370b432aea6 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2605,7 +2605,16 @@ static bool hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn, return false; } -struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8]) +static bool ltk_type_master(u8 type) +{ + if (type == HCI_SMP_STK || type == HCI_SMP_LTK) + return true; + + return false; +} + +struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8], + bool master) { struct smp_ltk *k; @@ -2614,6 +2623,9 @@ struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8]) memcmp(rand, k->rand, sizeof(k->rand))) continue; + if (ltk_type_master(k->type) != master) + continue; + return k; } @@ -2621,13 +2633,14 @@ struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8]) } struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 addr_type) + u8 addr_type, bool master) { struct smp_ltk *k; list_for_each_entry(k, &hdev->long_term_keys, list) if (addr_type == k->bdaddr_type && - bacmp(bdaddr, &k->bdaddr) == 0) + bacmp(bdaddr, &k->bdaddr) == 0 && + ltk_type_master(k->type) == master) return k; return NULL; @@ -2691,8 +2704,9 @@ int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type, ediv, u8 rand[8]) { struct smp_ltk *key, *old_key; + bool master = ltk_type_master(type); - old_key = hci_find_ltk_by_addr(hdev, bdaddr, addr_type); + old_key = hci_find_ltk_by_addr(hdev, bdaddr, addr_type, master); if (old_key) key = old_key; else { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 8c44bbe19add..7bb8094a3ff2 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3650,7 +3650,7 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb) if (conn == NULL) goto not_found; - ltk = hci_find_ltk(hdev, ev->ediv, ev->random); + ltk = hci_find_ltk(hdev, ev->ediv, ev->random, conn->out); if (ltk == NULL) goto not_found; diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 9b1167007653..efe51ccdc615 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -699,7 +699,8 @@ static u8 smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level) struct smp_ltk *key; struct hci_conn *hcon = conn->hcon; - key = hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type); + key = hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type, + hcon->out); if (!key) return 0; -- cgit v1.2.3-71-gd317 From 1e406eefbe41467c00973939c2b61b37bf0e1323 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Thu, 30 Jan 2014 18:22:04 -0300 Subject: Bluetooth: Save connection interval parameters in hci_conn This patch creates two new fields in struct hci_conn to save the minimum and maximum connection interval values used to establish the connection this object represents. This change is required in order to know what parameters the connection is currently using. Signed-off-by: Andre Guedes Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_conn.c | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 378e2f32cfa0..b9676cc1a59d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -334,6 +334,8 @@ struct hci_conn { __u8 passkey_entered; __u16 disc_timeout; __u16 setting; + __u16 le_conn_min_interval; + __u16 le_conn_max_interval; unsigned long flags; __u8 remote_cap; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 0266bd8e4913..7f148c975736 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -558,8 +558,8 @@ static int hci_create_le_conn(struct hci_conn *conn) bacpy(&cp.peer_addr, &conn->dst); cp.peer_addr_type = conn->dst_type; cp.own_address_type = conn->src_type; - cp.conn_interval_min = cpu_to_le16(hdev->le_conn_min_interval); - cp.conn_interval_max = cpu_to_le16(hdev->le_conn_max_interval); + cp.conn_interval_min = cpu_to_le16(conn->le_conn_min_interval); + cp.conn_interval_max = cpu_to_le16(conn->le_conn_max_interval); cp.supervision_timeout = __constant_cpu_to_le16(0x002a); cp.min_ce_len = __constant_cpu_to_le16(0x0000); cp.max_ce_len = __constant_cpu_to_le16(0x0000); @@ -624,6 +624,8 @@ static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, conn->sec_level = BT_SECURITY_LOW; conn->pending_sec_level = sec_level; conn->auth_type = auth_type; + conn->le_conn_min_interval = hdev->le_conn_min_interval; + conn->le_conn_max_interval = hdev->le_conn_max_interval; err = hci_create_le_conn(conn); if (err) -- cgit v1.2.3-71-gd317 From 5c136e90a45e3d8e050b212f8f40f2d81f096879 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Thu, 30 Jan 2014 18:22:05 -0300 Subject: Bluetooth: Group list_head fields from strcut hci_dev together This patch groups the list_head fields from struct hci_dev together and removes empty lines between them. Signed-off-by: Andre Guedes Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b9676cc1a59d..2e1d184bd8d3 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -259,18 +259,15 @@ struct hci_dev { __u32 req_status; __u32 req_result; - struct list_head mgmt_pending; struct discovery_state discovery; struct hci_conn_hash conn_hash; - struct list_head blacklist; + struct list_head mgmt_pending; + struct list_head blacklist; struct list_head uuids; - struct list_head link_keys; - struct list_head long_term_keys; - struct list_head remote_oob_data; struct hci_dev_stats stat; -- cgit v1.2.3-71-gd317 From b1de97d8c06d9d8d38e85dc5b0cf3630372e702c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 31 Jan 2014 11:55:21 -0800 Subject: Bluetooth: Add management setting for use of debug keys When the controller has been enabled to allow usage of debug keys, then clearly identify that in the current settings information. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 1 + net/bluetooth/mgmt.c | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index e19049fb6c46..f87f5d784c3b 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -95,6 +95,7 @@ struct mgmt_rp_read_index_list { #define MGMT_SETTING_LE 0x00000200 #define MGMT_SETTING_ADVERTISING 0x00000400 #define MGMT_SETTING_SECURE_CONN 0x00000800 +#define MGMT_SETTING_DEBUG_KEYS 0x00001000 #define MGMT_OP_READ_INFO 0x0004 #define MGMT_READ_INFO_SIZE 0 diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 111b1296a2b8..91ffecd1727e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -364,6 +364,7 @@ static u32 get_supported_settings(struct hci_dev *hdev) settings |= MGMT_SETTING_POWERED; settings |= MGMT_SETTING_PAIRABLE; + settings |= MGMT_SETTING_DEBUG_KEYS; if (lmp_bredr_capable(hdev)) { settings |= MGMT_SETTING_CONNECTABLE; @@ -431,6 +432,9 @@ static u32 get_current_settings(struct hci_dev *hdev) if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) settings |= MGMT_SETTING_SECURE_CONN; + if (test_bit(HCI_DEBUG_KEYS, &hdev->dev_flags)) + settings |= MGMT_SETTING_DEBUG_KEYS; + return settings; } @@ -2207,6 +2211,7 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, { struct mgmt_cp_load_link_keys *cp = data; u16 key_count, expected_len; + bool changed; int i; BT_DBG("request for %s", hdev->name); @@ -2246,9 +2251,12 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, hci_link_keys_clear(hdev); if (cp->debug_keys) - set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags); + changed = !test_and_set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags); else - clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags); + changed = test_and_clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags); + + if (changed) + new_settings(hdev, NULL); for (i = 0; i < key_count; i++) { struct mgmt_link_key_info *key = &cp->keys[i]; -- cgit v1.2.3-71-gd317 From 4e39ac81366583486b857c88656409e56befefdf Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 31 Jan 2014 11:55:22 -0800 Subject: Bluetooth: Add management command to allow use of debug keys Originally allowing the use of debug keys was done via the Load Link Keys management command. However this is BR/EDR specific and to be flexible and allow extending this to LE as well, make this an independent command. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 2 ++ net/bluetooth/mgmt.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index f87f5d784c3b..dfab094fab73 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -387,6 +387,8 @@ struct mgmt_cp_set_scan_params { #define MGMT_OP_SET_SECURE_CONN 0x002D +#define MGMT_OP_SET_DEBUG_KEYS 0x002E + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 91ffecd1727e..70a3a7e917b7 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -80,6 +80,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_SET_STATIC_ADDRESS, MGMT_OP_SET_SCAN_PARAMS, MGMT_OP_SET_SECURE_CONN, + MGMT_OP_SET_DEBUG_KEYS, }; static const u16 mgmt_events[] = { @@ -4111,6 +4112,38 @@ failed: return err; } +static int set_debug_keys(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_mode *cp = data; + bool changed; + int err; + + BT_DBG("request for %s", hdev->name); + + if (cp->val != 0x00 && cp->val != 0x01) + return cmd_status(sk, hdev->id, MGMT_OP_SET_DEBUG_KEYS, + MGMT_STATUS_INVALID_PARAMS); + + hci_dev_lock(hdev); + + if (cp->val) + changed = !test_and_set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags); + else + changed = test_and_clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags); + + err = send_settings_rsp(sk, MGMT_OP_SET_DEBUG_KEYS, hdev); + if (err < 0) + goto unlock; + + if (changed) + err = new_settings(hdev, sk); + +unlock: + hci_dev_unlock(hdev); + return err; +} + static bool ltk_is_valid(struct mgmt_ltk_info *key) { if (key->authenticated != 0x00 && key->authenticated != 0x01) @@ -4240,6 +4273,7 @@ static const struct mgmt_handler { { set_static_address, false, MGMT_SET_STATIC_ADDRESS_SIZE }, { set_scan_params, false, MGMT_SET_SCAN_PARAMS_SIZE }, { set_secure_conn, false, MGMT_SETTING_SIZE }, + { set_debug_keys, false, MGMT_SETTING_SIZE }, }; -- cgit v1.2.3-71-gd317 From abf76bad8fb503fb21fb0eba854fa048c75ff123 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 31 Jan 2014 16:24:28 -0800 Subject: Bluetooth: Track the AES-CCM encryption status of LE and BR/EDR links When encryption for LE links has been enabled, it will always be use AES-CCM encryption. In case of BR/EDR Secure Connections, the link will also use AES-CCM encryption. In both cases track the AES-CCM status in the connection flags. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_event.c | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 2e1d184bd8d3..6854384b1f25 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -448,6 +448,7 @@ enum { HCI_CONN_MGMT_CONNECTED, HCI_CONN_SSP_ENABLED, HCI_CONN_SC_ENABLED, + HCI_CONN_AES_CCM, HCI_CONN_POWER_SAVE, HCI_CONN_REMOTE_OOB, HCI_CONN_6LOWPAN, diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 62aea2edc231..36c9a488ac56 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1997,8 +1997,14 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb) conn->link_mode |= HCI_LM_AUTH; conn->link_mode |= HCI_LM_ENCRYPT; conn->sec_level = conn->pending_sec_level; - } else + + if ((conn->type == ACL_LINK && ev->encrypt == 0x02) || + conn->type == LE_LINK) + set_bit(HCI_CONN_AES_CCM, &conn->flags); + } else { conn->link_mode &= ~HCI_LM_ENCRYPT; + clear_bit(HCI_CONN_AES_CCM, &conn->flags); + } } clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags); -- cgit v1.2.3-71-gd317 From d40f3eef0b9b70d15d5fd0031c0633d4a9ed78cd Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 31 Jan 2014 18:42:17 -0800 Subject: Bluetooth: Rename authentication to key_type in mgmt_ltk_info The field is not a boolean, it is actually a field for a key type. So name it properly. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 2 +- net/bluetooth/mgmt.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index dfab094fab73..4303fa90b7c1 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -182,7 +182,7 @@ struct mgmt_cp_load_link_keys { struct mgmt_ltk_info { struct mgmt_addr_info addr; - __u8 authenticated; + __u8 type; __u8 master; __u8 enc_size; __le16 ediv; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 8030eeb44382..8c94841072a8 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -4207,7 +4207,7 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, type = HCI_SMP_LTK_SLAVE; hci_add_ltk(hdev, &key->addr.bdaddr, addr_type, - type, 0, key->authenticated, key->val, + type, 0, key->type, key->val, key->enc_size, key->ediv, key->rand); } @@ -4648,7 +4648,7 @@ void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent) ev.store_hint = persistent; bacpy(&ev.key.addr.bdaddr, &key->bdaddr); ev.key.addr.type = link_to_bdaddr(LE_LINK, key->bdaddr_type); - ev.key.authenticated = key->authenticated; + ev.key.type = key->authenticated; ev.key.enc_size = key->enc_size; ev.key.ediv = key->ediv; -- cgit v1.2.3-71-gd317 From 03c515d7486b7a519728340d4b04baaad16bf806 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 31 Jan 2014 18:42:18 -0800 Subject: Bluetooth: Remove __packed from struct smp_ltk The struct smp_ltk does not need to be packed and so remove __packed. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 6854384b1f25..4e878780fa01 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -101,7 +101,7 @@ struct smp_ltk { __le16 ediv; u8 rand[8]; u8 val[16]; -} __packed; +}; struct link_key { struct list_head list; -- cgit v1.2.3-71-gd317 From 424ef94311512ef48a5464d173ef83862e4653cb Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 31 Jan 2014 19:02:30 -0800 Subject: Bluetooth: Add constants for LTK key types The LTK key types available right now are unauthenticated and authenticated ones. Provide two simple constants for it. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 232c07804ca8..352d3d7d06bb 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -346,6 +346,10 @@ enum { #define HCI_SMP_LTK 0x82 #define HCI_SMP_LTK_SLAVE 0x83 +/* Long Term Key types */ +#define HCI_LTK_UNAUTH 0x00 +#define HCI_LTK_AUTH 0x01 + /* ---- HCI Error Codes ---- */ #define HCI_ERROR_AUTH_FAILURE 0x05 #define HCI_ERROR_CONNECTION_TIMEOUT 0x08 -- cgit v1.2.3-71-gd317 From 15819a7065ac46eb804498bb7ccbba60d8f7d4d5 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Mon, 3 Feb 2014 13:56:18 -0300 Subject: Bluetooth: Introduce connection parameters list This patch adds to hdev the connection parameters list (hdev->le_ conn_params). The elements from this list (struct hci_conn_params) contains the connection parameters (for now, minimum and maximum connection interval) that should be used during the connection establishment. Moreover, this patch adds helper functions to manipulate hdev->le_ conn_params list. Some of these functions are also declared in hci_core.h since they will be used outside hci_core.c in upcoming patches. Signed-off-by: Andre Guedes Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 18 ++++++++++ net/bluetooth/hci_core.c | 77 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 4e878780fa01..92fa75fce29d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -269,6 +269,7 @@ struct hci_dev { struct list_head link_keys; struct list_head long_term_keys; struct list_head remote_oob_data; + struct list_head le_conn_params; struct hci_dev_stats stat; @@ -373,6 +374,16 @@ struct hci_chan { __u8 state; }; +struct hci_conn_params { + struct list_head list; + + bdaddr_t addr; + u8 addr_type; + + u16 conn_min_interval; + u16 conn_max_interval; +}; + extern struct list_head hci_dev_list; extern struct list_head hci_cb_list; extern rwlock_t hci_dev_list_lock; @@ -751,6 +762,13 @@ int hci_blacklist_clear(struct hci_dev *hdev); int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); +struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev, + bdaddr_t *addr, u8 addr_type); +void hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, + u16 conn_min_interval, u16 conn_max_interval); +void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); +void hci_conn_params_clear(struct hci_dev *hdev); + int hci_uuids_clear(struct hci_dev *hdev); int hci_link_keys_clear(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 7a44c8c1037a..e7746690d620 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2924,6 +2924,81 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) return mgmt_device_unblocked(hdev, bdaddr, type); } +/* This function requires the caller holds hdev->lock */ +struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev, + bdaddr_t *addr, u8 addr_type) +{ + struct hci_conn_params *params; + + list_for_each_entry(params, &hdev->le_conn_params, list) { + if (bacmp(¶ms->addr, addr) == 0 && + params->addr_type == addr_type) { + return params; + } + } + + return NULL; +} + +/* This function requires the caller holds hdev->lock */ +void hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, + u16 conn_min_interval, u16 conn_max_interval) +{ + struct hci_conn_params *params; + + params = hci_conn_params_lookup(hdev, addr, addr_type); + if (params) { + params->conn_min_interval = conn_min_interval; + params->conn_max_interval = conn_max_interval; + return; + } + + params = kzalloc(sizeof(*params), GFP_KERNEL); + if (!params) { + BT_ERR("Out of memory"); + return; + } + + bacpy(¶ms->addr, addr); + params->addr_type = addr_type; + params->conn_min_interval = conn_min_interval; + params->conn_max_interval = conn_max_interval; + + list_add(¶ms->list, &hdev->le_conn_params); + + BT_DBG("addr %pMR (type %u) conn_min_interval 0x%.4x " + "conn_max_interval 0x%.4x", addr, addr_type, conn_min_interval, + conn_max_interval); +} + +/* This function requires the caller holds hdev->lock */ +void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) +{ + struct hci_conn_params *params; + + params = hci_conn_params_lookup(hdev, addr, addr_type); + if (!params) + return; + + list_del(¶ms->list); + kfree(params); + + BT_DBG("addr %pMR (type %u)", addr, addr_type); +} + +/* This function requires the caller holds hdev->lock */ +void hci_conn_params_clear(struct hci_dev *hdev) +{ + struct hci_conn_params *params, *tmp; + + list_for_each_entry_safe(params, tmp, &hdev->le_conn_params, list) { + list_del(¶ms->list); + kfree(params); + } + + BT_DBG("All LE connection parameters were removed"); +} + static void inquiry_complete(struct hci_dev *hdev, u8 status) { if (status) { @@ -3034,6 +3109,7 @@ struct hci_dev *hci_alloc_dev(void) INIT_LIST_HEAD(&hdev->link_keys); INIT_LIST_HEAD(&hdev->long_term_keys); INIT_LIST_HEAD(&hdev->remote_oob_data); + INIT_LIST_HEAD(&hdev->le_conn_params); INIT_LIST_HEAD(&hdev->conn_hash.list); INIT_WORK(&hdev->rx_work, hci_rx_work); @@ -3219,6 +3295,7 @@ void hci_unregister_dev(struct hci_dev *hdev) hci_link_keys_clear(hdev); hci_smp_ltks_clear(hdev); hci_remote_oob_data_clear(hdev); + hci_conn_params_clear(hdev); hci_dev_unlock(hdev); hci_dev_put(hdev); -- cgit v1.2.3-71-gd317 From d85dad75566674ca8012715ac00a84ced3697972 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 13 Feb 2014 13:27:42 +0100 Subject: mac80211: remove erroneous comment about RX radiotap header There's no way the driver can pre-build the radiotap header, so remove the comment stating that it can. Reported-by: Jouni Malinen Signed-off-by: Johannes Berg --- include/net/mac80211.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 2d4d31212eed..a6bcc39e146e 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -66,10 +66,6 @@ * * Secondly, when the hardware handles fragmentation, the frame handed to * the driver from mac80211 is the MSDU, not the MPDU. - * - * Finally, for received frames, the driver is able to indicate that it has - * filled a radiotap header and put that in front of the frame; if it does - * not do so then mac80211 may add this under certain circumstances. */ /** -- cgit v1.2.3-71-gd317 From 45f7435968363816f8fc4c6abef692808534140d Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Wed, 12 Feb 2014 17:35:21 +0400 Subject: tcp: remove unused min_cwnd member of tcp_congestion_ops Commit 684bad110757 "tcp: use PRR to reduce cwin in CWR state" removed all calls to min_cwnd, so we can safely remove it. Also, remove tcp_reno_min_cwnd because it was only used for min_cwnd. Signed-off-by: Stanislav Fomichev Acked-by: Yuchung Cheng Signed-off-by: David S. Miller --- Documentation/networking/tcp.txt | 2 +- include/net/tcp.h | 3 --- net/ipv4/tcp_cong.c | 10 ---------- net/ipv4/tcp_highspeed.c | 1 - net/ipv4/tcp_hybla.c | 1 - net/ipv4/tcp_illinois.c | 1 - net/ipv4/tcp_lp.c | 1 - net/ipv4/tcp_scalable.c | 1 - net/ipv4/tcp_vegas.c | 1 - net/ipv4/tcp_westwood.c | 1 - net/ipv4/tcp_yeah.c | 1 - 11 files changed, 1 insertion(+), 22 deletions(-) (limited to 'include/net') diff --git a/Documentation/networking/tcp.txt b/Documentation/networking/tcp.txt index 7d11bb5dc30a..bdc4c0db51e1 100644 --- a/Documentation/networking/tcp.txt +++ b/Documentation/networking/tcp.txt @@ -30,7 +30,7 @@ A congestion control mechanism can be registered through functions in tcp_cong.c. The functions used by the congestion control mechanism are registered via passing a tcp_congestion_ops struct to tcp_register_congestion_control. As a minimum name, ssthresh, -cong_avoid, min_cwnd must be valid. +cong_avoid must be valid. Private data for a congestion control mechanism is stored in tp->ca_priv. tcp_ca(tp) returns a pointer to this space. This is preallocated space - it diff --git a/include/net/tcp.h b/include/net/tcp.h index 56fc366da6d5..1f820537741a 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -791,8 +791,6 @@ struct tcp_congestion_ops { /* return slow start threshold (required) */ u32 (*ssthresh)(struct sock *sk); - /* lower bound for congestion window (optional) */ - u32 (*min_cwnd)(const struct sock *sk); /* do new cwnd calculation (required) */ void (*cong_avoid)(struct sock *sk, u32 ack, u32 acked, u32 in_flight); /* call before changing ca_state (optional) */ @@ -827,7 +825,6 @@ void tcp_cong_avoid_ai(struct tcp_sock *tp, u32 w); extern struct tcp_congestion_ops tcp_init_congestion_ops; u32 tcp_reno_ssthresh(struct sock *sk); void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight); -u32 tcp_reno_min_cwnd(const struct sock *sk); extern struct tcp_congestion_ops tcp_reno; static inline void tcp_set_ca_state(struct sock *sk, const u8 ca_state) diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index ad37bf18ae4b..f49351edf97d 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c @@ -362,21 +362,12 @@ u32 tcp_reno_ssthresh(struct sock *sk) } EXPORT_SYMBOL_GPL(tcp_reno_ssthresh); -/* Lower bound on congestion window with halving. */ -u32 tcp_reno_min_cwnd(const struct sock *sk) -{ - const struct tcp_sock *tp = tcp_sk(sk); - return tp->snd_ssthresh/2; -} -EXPORT_SYMBOL_GPL(tcp_reno_min_cwnd); - struct tcp_congestion_ops tcp_reno = { .flags = TCP_CONG_NON_RESTRICTED, .name = "reno", .owner = THIS_MODULE, .ssthresh = tcp_reno_ssthresh, .cong_avoid = tcp_reno_cong_avoid, - .min_cwnd = tcp_reno_min_cwnd, }; /* Initial congestion control used (until SYN) @@ -388,6 +379,5 @@ struct tcp_congestion_ops tcp_init_congestion_ops = { .owner = THIS_MODULE, .ssthresh = tcp_reno_ssthresh, .cong_avoid = tcp_reno_cong_avoid, - .min_cwnd = tcp_reno_min_cwnd, }; EXPORT_SYMBOL_GPL(tcp_init_congestion_ops); diff --git a/net/ipv4/tcp_highspeed.c b/net/ipv4/tcp_highspeed.c index 8ed9305dfdf4..8b9e7bad77c0 100644 --- a/net/ipv4/tcp_highspeed.c +++ b/net/ipv4/tcp_highspeed.c @@ -162,7 +162,6 @@ static struct tcp_congestion_ops tcp_highspeed __read_mostly = { .init = hstcp_init, .ssthresh = hstcp_ssthresh, .cong_avoid = hstcp_cong_avoid, - .min_cwnd = tcp_reno_min_cwnd, .owner = THIS_MODULE, .name = "highspeed" diff --git a/net/ipv4/tcp_hybla.c b/net/ipv4/tcp_hybla.c index 478fe82611bf..2a1a9e2a4e51 100644 --- a/net/ipv4/tcp_hybla.c +++ b/net/ipv4/tcp_hybla.c @@ -166,7 +166,6 @@ static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 acked, static struct tcp_congestion_ops tcp_hybla __read_mostly = { .init = hybla_init, .ssthresh = tcp_reno_ssthresh, - .min_cwnd = tcp_reno_min_cwnd, .cong_avoid = hybla_cong_avoid, .set_state = hybla_state, diff --git a/net/ipv4/tcp_illinois.c b/net/ipv4/tcp_illinois.c index e498a62b8f97..be047c63ca10 100644 --- a/net/ipv4/tcp_illinois.c +++ b/net/ipv4/tcp_illinois.c @@ -328,7 +328,6 @@ static struct tcp_congestion_ops tcp_illinois __read_mostly = { .flags = TCP_CONG_RTT_STAMP, .init = tcp_illinois_init, .ssthresh = tcp_illinois_ssthresh, - .min_cwnd = tcp_reno_min_cwnd, .cong_avoid = tcp_illinois_cong_avoid, .set_state = tcp_illinois_state, .get_info = tcp_illinois_info, diff --git a/net/ipv4/tcp_lp.c b/net/ipv4/tcp_lp.c index 991d62a2f9bb..503798f2fcd6 100644 --- a/net/ipv4/tcp_lp.c +++ b/net/ipv4/tcp_lp.c @@ -319,7 +319,6 @@ static struct tcp_congestion_ops tcp_lp __read_mostly = { .init = tcp_lp_init, .ssthresh = tcp_reno_ssthresh, .cong_avoid = tcp_lp_cong_avoid, - .min_cwnd = tcp_reno_min_cwnd, .pkts_acked = tcp_lp_pkts_acked, .owner = THIS_MODULE, diff --git a/net/ipv4/tcp_scalable.c b/net/ipv4/tcp_scalable.c index 19ea6c2951f3..0ac50836da4d 100644 --- a/net/ipv4/tcp_scalable.c +++ b/net/ipv4/tcp_scalable.c @@ -39,7 +39,6 @@ static u32 tcp_scalable_ssthresh(struct sock *sk) static struct tcp_congestion_ops tcp_scalable __read_mostly = { .ssthresh = tcp_scalable_ssthresh, .cong_avoid = tcp_scalable_cong_avoid, - .min_cwnd = tcp_reno_min_cwnd, .owner = THIS_MODULE, .name = "scalable", diff --git a/net/ipv4/tcp_vegas.c b/net/ipv4/tcp_vegas.c index 06cae62bf208..a022c17c9cf1 100644 --- a/net/ipv4/tcp_vegas.c +++ b/net/ipv4/tcp_vegas.c @@ -310,7 +310,6 @@ static struct tcp_congestion_ops tcp_vegas __read_mostly = { .init = tcp_vegas_init, .ssthresh = tcp_reno_ssthresh, .cong_avoid = tcp_vegas_cong_avoid, - .min_cwnd = tcp_reno_min_cwnd, .pkts_acked = tcp_vegas_pkts_acked, .set_state = tcp_vegas_state, .cwnd_event = tcp_vegas_cwnd_event, diff --git a/net/ipv4/tcp_westwood.c b/net/ipv4/tcp_westwood.c index 76a1e23259e1..b94a04ae2ed5 100644 --- a/net/ipv4/tcp_westwood.c +++ b/net/ipv4/tcp_westwood.c @@ -276,7 +276,6 @@ static struct tcp_congestion_ops tcp_westwood __read_mostly = { .init = tcp_westwood_init, .ssthresh = tcp_reno_ssthresh, .cong_avoid = tcp_reno_cong_avoid, - .min_cwnd = tcp_westwood_bw_rttmin, .cwnd_event = tcp_westwood_event, .get_info = tcp_westwood_info, .pkts_acked = tcp_westwood_pkts_acked, diff --git a/net/ipv4/tcp_yeah.c b/net/ipv4/tcp_yeah.c index 1a8d271f994d..8eab02030ed0 100644 --- a/net/ipv4/tcp_yeah.c +++ b/net/ipv4/tcp_yeah.c @@ -231,7 +231,6 @@ static struct tcp_congestion_ops tcp_yeah __read_mostly = { .init = tcp_yeah_init, .ssthresh = tcp_yeah_ssthresh, .cong_avoid = tcp_yeah_cong_avoid, - .min_cwnd = tcp_reno_min_cwnd, .set_state = tcp_vegas_state, .cwnd_event = tcp_vegas_cwnd_event, .get_info = tcp_vegas_get_info, -- cgit v1.2.3-71-gd317 From 9b7655eafeeec9e74e97e9056e820ede8d18093e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 14 Feb 2014 07:40:51 +0200 Subject: Bluetooth: Enable LE L2CAP CoC support by default Now that the LE L2CAP Connection Oriented Channel support has undergone a decent amount of testing we can make it officially supported. This patch removes the enable_lecoc module parameter which was previously needed to enable support for LE L2CAP CoC. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 1 - net/bluetooth/l2cap_core.c | 11 ----------- net/bluetooth/l2cap_sock.c | 29 ----------------------------- 3 files changed, 41 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index ae482f41594a..13bec91785f4 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -857,7 +857,6 @@ 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_core.c b/net/bluetooth/l2cap_core.c index 66fbac91eaed..6e6b3a9c8e6d 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -5544,17 +5544,6 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn, { int err = 0; - if (!enable_lecoc) { - switch (cmd->code) { - case L2CAP_LE_CONN_REQ: - case L2CAP_LE_CONN_RSP: - case L2CAP_LE_CREDITS: - case L2CAP_DISCONN_REQ: - case L2CAP_DISCONN_RSP: - return -EINVAL; - } - } - switch (cmd->code) { case L2CAP_COMMAND_REJ: l2cap_le_command_rej(conn, cmd, cmd_len, data); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 27d3d6d48b6e..b247f9d27fed 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -36,8 +36,6 @@ #include "smp.h" -bool enable_lecoc; - static struct bt_sock_list l2cap_sk_list = { .lock = __RW_LOCK_UNLOCKED(l2cap_sk_list.lock) }; @@ -111,8 +109,6 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) } if (bdaddr_type_is_le(la.l2_bdaddr_type)) { - if (!enable_lecoc && la.l2_psm) - return -EINVAL; /* We only allow ATT user space socket */ if (la.l2_cid && la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT)) @@ -229,8 +225,6 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, return -EINVAL; if (bdaddr_type_is_le(la.l2_bdaddr_type)) { - if (!enable_lecoc && la.l2_psm) - return -EINVAL; /* We only allow ATT user space socket */ if (la.l2_cid && la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT)) @@ -578,11 +572,6 @@ static int l2cap_sock_getsockopt(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; @@ -598,11 +587,6 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, break; case BT_RCVMTU: - if (!enable_lecoc) { - err = -EPROTONOSUPPORT; - break; - } - if (!bdaddr_type_is_le(chan->src_type)) { err = -EINVAL; break; @@ -919,11 +903,6 @@ 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; @@ -936,11 +915,6 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, break; case BT_RCVMTU: - if (!enable_lecoc) { - err = -EPROTONOSUPPORT; - break; - } - if (!bdaddr_type_is_le(chan->src_type)) { err = -EINVAL; break; @@ -1643,6 +1617,3 @@ 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-71-gd317 From 1c64834e0624c61735308138e67cc3b527f41621 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sun, 9 Feb 2014 20:59:07 -0500 Subject: Bluetooth: Release rfcomm_dev only once No logic prevents an rfcomm_dev from being released multiple times. For example, if the rfcomm_dev ref count is large due to pending tx, then multiple RFCOMMRELEASEDEV ioctls may mistakenly release the rfcomm_dev too many times. Note that concurrent ioctls are not required to create this condition. Introduce RFCOMM_DEV_RELEASED status bit which guarantees the rfcomm_dev can only be released once. NB: Since the flags are exported to userspace, introduce the status field to track state for which userspace should not be aware. Signed-off-by: Peter Hurley Tested-By: Alexander Holler Signed-off-by: Marcel Holtmann --- include/net/bluetooth/rfcomm.h | 6 +++++- net/bluetooth/rfcomm/tty.c | 11 +++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index c312cfc4e922..b9759eb17cdd 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -324,11 +324,15 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, #define RFCOMMGETDEVINFO _IOR('R', 211, int) #define RFCOMMSTEALDLC _IOW('R', 220, int) +/* rfcomm_dev.flags bit definitions */ #define RFCOMM_REUSE_DLC 0 #define RFCOMM_RELEASE_ONHUP 1 #define RFCOMM_HANGUP_NOW 2 #define RFCOMM_TTY_ATTACHED 3 -#define RFCOMM_TTY_RELEASED 4 +#define RFCOMM_DEFUNCT_BIT4 4 /* don't reuse this bit - userspace visible */ + +/* rfcomm_dev.status bit definitions */ +#define RFCOMM_DEV_RELEASED 0 struct rfcomm_dev_req { s16 dev_id; diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index b385d9985656..d9d4bc89e638 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -51,6 +51,8 @@ struct rfcomm_dev { unsigned long flags; int err; + unsigned long status; /* don't export to userspace */ + bdaddr_t src; bdaddr_t dst; u8 channel; @@ -423,6 +425,12 @@ static int rfcomm_release_dev(void __user *arg) return -EPERM; } + /* only release once */ + if (test_and_set_bit(RFCOMM_DEV_RELEASED, &dev->status)) { + tty_port_put(&dev->port); + return -EALREADY; + } + if (req.flags & (1 << RFCOMM_HANGUP_NOW)) rfcomm_dlc_close(dev->dlc, 0); @@ -433,8 +441,7 @@ static int rfcomm_release_dev(void __user *arg) tty_kref_put(tty); } - if (!test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags) && - !test_and_set_bit(RFCOMM_TTY_RELEASED, &dev->flags)) + if (!test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) tty_port_put(&dev->port); tty_port_put(&dev->port); -- cgit v1.2.3-71-gd317 From 80ea73378af46b0023eb2f400d26c2a60248ffaa Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sun, 9 Feb 2014 20:59:08 -0500 Subject: Bluetooth: Fix unreleased rfcomm_dev reference When RFCOMM_RELEASE_ONHUP is set, the rfcomm tty driver 'takes over' the initial rfcomm_dev reference created by the RFCOMMCREATEDEV ioctl. The assumption is that the rfcomm tty driver will release the rfcomm_dev reference when the tty is freed (in rfcomm_tty_cleanup()). However, if the tty is never opened, the 'take over' never occurs, so when RFCOMMRELEASEDEV ioctl is called, the reference is not released. Track the state of the reference 'take over' so that the release is guaranteed by either the RFCOMMRELEASEDEV ioctl or the rfcomm tty driver. Note that the synchronous hangup in rfcomm_release_dev() ensures that rfcomm_tty_install() cannot race with the RFCOMMRELEASEDEV ioctl. Signed-off-by: Peter Hurley Tested-By: Alexander Holler Signed-off-by: Marcel Holtmann --- include/net/bluetooth/rfcomm.h | 1 + net/bluetooth/rfcomm/tty.c | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index b9759eb17cdd..0d69936831fa 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -333,6 +333,7 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, /* rfcomm_dev.status bit definitions */ #define RFCOMM_DEV_RELEASED 0 +#define RFCOMM_TTY_OWNED 1 struct rfcomm_dev_req { s16 dev_id; diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index d9d4bc89e638..bb570d95adca 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -441,7 +441,7 @@ static int rfcomm_release_dev(void __user *arg) tty_kref_put(tty); } - if (!test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) + if (!test_bit(RFCOMM_TTY_OWNED, &dev->status)) tty_port_put(&dev->port); tty_port_put(&dev->port); @@ -685,8 +685,10 @@ static int rfcomm_tty_install(struct tty_driver *driver, struct tty_struct *tty) * when the last process closes the tty. The behaviour is expected by * userspace. */ - if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) + if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) { + set_bit(RFCOMM_TTY_OWNED, &dev->status); tty_port_put(&dev->port); + } return 0; } -- cgit v1.2.3-71-gd317 From c10a848cea89a8f0418fa0efec33c4e8507aab4b Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sun, 9 Feb 2014 20:59:10 -0500 Subject: Bluetooth: Verify dlci not in use before rfcomm_dev create Only one session/channel combination may be in use at any one time. However, the failure does not occur until the tty is opened (in rfcomm_dlc_open()). Because these settings are actually bound at rfcomm device creation (via RFCOMMCREATEDEV ioctl), validate and fail before creating the rfcomm tty device. Signed-off-by: Peter Hurley Tested-By: Alexander Holler Signed-off-by: Marcel Holtmann --- include/net/bluetooth/rfcomm.h | 1 + net/bluetooth/rfcomm/core.c | 26 +++++++++++++++++++++++++- net/bluetooth/rfcomm/tty.c | 8 ++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) (limited to 'include/net') diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index 0d69936831fa..f8262a2783ec 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -241,6 +241,7 @@ int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb); int rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig); int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig); void rfcomm_dlc_accept(struct rfcomm_dlc *d); +struct rfcomm_dlc *rfcomm_dlc_exists(bdaddr_t *src, bdaddr_t *dst, u8 channel); #define rfcomm_dlc_lock(d) spin_lock(&d->lock) #define rfcomm_dlc_unlock(d) spin_unlock(&d->lock) diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index ba115d472f7b..b378bbb6f8a7 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -360,6 +360,11 @@ static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, u8 dlci) return NULL; } +static int rfcomm_check_channel(u8 channel) +{ + return channel < 1 || channel > 30; +} + static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel) { struct rfcomm_session *s; @@ -369,7 +374,7 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, BT_DBG("dlc %p state %ld %pMR -> %pMR channel %d", d, d->state, src, dst, channel); - if (channel < 1 || channel > 30) + if (rfcomm_check_channel(channel)) return -EINVAL; if (d->state != BT_OPEN && d->state != BT_CLOSED) @@ -514,6 +519,25 @@ no_session: return r; } +struct rfcomm_dlc *rfcomm_dlc_exists(bdaddr_t *src, bdaddr_t *dst, u8 channel) +{ + struct rfcomm_session *s; + struct rfcomm_dlc *dlc = NULL; + u8 dlci; + + if (rfcomm_check_channel(channel)) + return ERR_PTR(-EINVAL); + + rfcomm_lock(); + s = rfcomm_session_get(src, dst); + if (s) { + dlci = __dlci(!s->initiator, channel); + dlc = rfcomm_dlc_get(s, dlci); + } + rfcomm_unlock(); + return dlc; +} + int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb) { int len = skb->len; diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index 6ea08b05b53a..a58d693e1e61 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -385,6 +385,14 @@ static int rfcomm_create_dev(struct sock *sk, void __user *arg) dlc = rfcomm_pi(sk)->dlc; rfcomm_dlc_hold(dlc); } else { + /* Validate the channel is unused */ + dlc = rfcomm_dlc_exists(&req.src, &req.dst, req.channel); + if (IS_ERR(dlc)) + return PTR_ERR(dlc); + else if (dlc) { + rfcomm_dlc_put(dlc); + return -EBUSY; + } dlc = rfcomm_dlc_alloc(GFP_KERNEL); if (!dlc) return -ENOMEM; -- cgit v1.2.3-71-gd317 From 72e5108c6d637ea2f4c0e64b09621a79f363b664 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sun, 9 Feb 2014 20:59:22 -0500 Subject: Bluetooth: Don't fail RFCOMM tty writes The tty driver api design prefers no-fail writes if the driver write_room() method has previously indicated space is available to accept writes. Since this is trivially possible for the RFCOMM tty driver, do so. Introduce rfcomm_dlc_send_noerror(), which queues but does not schedule the krfcomm thread if the dlc is not yet connected (and thus does not error based on the connection state). The mtu size test is also unnecessary since the caller already chunks the written data into mtu size. Signed-off-by: Peter Hurley Tested-By: Alexander Holler Signed-off-by: Marcel Holtmann --- include/net/bluetooth/rfcomm.h | 1 + net/bluetooth/rfcomm/core.c | 14 ++++++++++++++ net/bluetooth/rfcomm/tty.c | 23 +++++++---------------- 3 files changed, 22 insertions(+), 16 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index f8262a2783ec..2611cc389d7d 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -238,6 +238,7 @@ int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel); int rfcomm_dlc_close(struct rfcomm_dlc *d, int reason); int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb); +void rfcomm_dlc_send_noerror(struct rfcomm_dlc *d, struct sk_buff *skb); int rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig); int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig); void rfcomm_dlc_accept(struct rfcomm_dlc *d); diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index b727cd97c5a2..21e15318937c 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -569,6 +569,20 @@ int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb) return len; } +void rfcomm_dlc_send_noerror(struct rfcomm_dlc *d, struct sk_buff *skb) +{ + int len = skb->len; + + BT_DBG("dlc %p mtu %d len %d", d, d->mtu, len); + + rfcomm_make_uih(skb, d->addr); + skb_queue_tail(&d->tx_queue, skb); + + if (d->state == BT_CONNECTED && + !test_bit(RFCOMM_TX_THROTTLED, &d->flags)) + rfcomm_schedule(); +} + void __rfcomm_dlc_throttle(struct rfcomm_dlc *d) { BT_DBG("dlc %p state %ld", d, d->state); diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index f6b9f0c4c29e..af775f35c019 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -374,14 +374,10 @@ static void rfcomm_set_owner_w(struct sk_buff *skb, struct rfcomm_dev *dev) static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, gfp_t priority) { - if (atomic_read(&dev->wmem_alloc) < rfcomm_room(dev->dlc)) { - struct sk_buff *skb = alloc_skb(size, priority); - if (skb) { - rfcomm_set_owner_w(skb, dev); - return skb; - } - } - return NULL; + struct sk_buff *skb = alloc_skb(size, priority); + if (skb) + rfcomm_set_owner_w(skb, dev); + return skb; } /* ---- Device IOCTLs ---- */ @@ -786,7 +782,7 @@ static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, in struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; struct rfcomm_dlc *dlc = dev->dlc; struct sk_buff *skb; - int err = 0, sent = 0, size; + int sent = 0, size; BT_DBG("tty %p count %d", tty, count); @@ -794,7 +790,6 @@ static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, in size = min_t(uint, count, dlc->mtu); skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_ATOMIC); - if (!skb) break; @@ -802,17 +797,13 @@ static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, in memcpy(skb_put(skb, size), buf + sent, size); - err = rfcomm_dlc_send(dlc, skb); - if (err < 0) { - kfree_skb(skb); - break; - } + rfcomm_dlc_send_noerror(dlc, skb); sent += size; count -= size; } - return sent ? sent : err; + return sent; } static int rfcomm_tty_write_room(struct tty_struct *tty) -- cgit v1.2.3-71-gd317 From e487e4dc2eb227c52fc71eae683181fa917163b8 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Tue, 14 Jan 2014 17:52:09 -0700 Subject: NFC: Add ISO/IEC 15693 header definitions Add the header definitions required by upcoming patches that add support for ISO/IEC 15693. Signed-off-by: Mark A. Greer Signed-off-by: Samuel Ortiz --- include/net/nfc/digital.h | 4 ++++ include/net/nfc/nfc.h | 3 +++ include/uapi/linux/nfc.h | 9 ++++++++- 3 files changed, 15 insertions(+), 1 deletion(-) (limited to 'include/net') diff --git a/include/net/nfc/digital.h b/include/net/nfc/digital.h index 81af21e9bcd4..1f0528d33500 100644 --- a/include/net/nfc/digital.h +++ b/include/net/nfc/digital.h @@ -35,6 +35,7 @@ enum { NFC_DIGITAL_RF_TECH_106A = 0, NFC_DIGITAL_RF_TECH_212F, NFC_DIGITAL_RF_TECH_424F, + NFC_DIGITAL_RF_TECH_ISO15693, NFC_DIGITAL_RF_TECH_LAST, }; @@ -57,6 +58,9 @@ enum { NFC_DIGITAL_FRAMING_NFCF_NFC_DEP, NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED, + NFC_DIGITAL_FRAMING_ISO15693_INVENTORY, + NFC_DIGITAL_FRAMING_ISO15693_TVT, /* Type V Tag (ISO/IEC 15693) */ + NFC_DIGITAL_FRAMING_LAST, }; diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index e80894bca1d0..2e8b40c16274 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -111,6 +111,9 @@ struct nfc_target { u8 sensf_res[NFC_SENSF_RES_MAXSIZE]; u8 hci_reader_gate; u8 logical_idx; + u8 is_iso15693; + u8 iso15693_dsfid; + u8 iso15693_uid[NFC_ISO15693_UID_MAXSIZE]; }; /** diff --git a/include/uapi/linux/nfc.h b/include/uapi/linux/nfc.h index 6ad6cc03ccd3..9789dc95b6a8 100644 --- a/include/uapi/linux/nfc.h +++ b/include/uapi/linux/nfc.h @@ -150,6 +150,8 @@ enum nfc_commands { * @NFC_ATTR_SE_TYPE: Secure element type (UICC or EMBEDDED) * @NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS: Firmware download operation status * @NFC_ATTR_APDU: Secure element APDU + * @NFC_ATTR_TARGET_ISO15693_DSFID: ISO 15693 Data Storage Format Identifier + * @NFC_ATTR_TARGET_ISO15693_UID: ISO 15693 Unique Identifier */ enum nfc_attrs { NFC_ATTR_UNSPEC, @@ -178,6 +180,8 @@ enum nfc_attrs { NFC_ATTR_SE_AID, NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS, NFC_ATTR_SE_APDU, + NFC_ATTR_TARGET_ISO15693_DSFID, + NFC_ATTR_TARGET_ISO15693_UID, /* private: internal use only */ __NFC_ATTR_AFTER_LAST }; @@ -200,6 +204,7 @@ enum nfc_sdp_attr { #define NFC_SENSF_RES_MAXSIZE 18 #define NFC_GB_MAXSIZE 48 #define NFC_FIRMWARE_NAME_MAXSIZE 32 +#define NFC_ISO15693_UID_MAXSIZE 8 /* NFC protocols */ #define NFC_PROTO_JEWEL 1 @@ -208,8 +213,9 @@ enum nfc_sdp_attr { #define NFC_PROTO_ISO14443 4 #define NFC_PROTO_NFC_DEP 5 #define NFC_PROTO_ISO14443_B 6 +#define NFC_PROTO_ISO15693 7 -#define NFC_PROTO_MAX 7 +#define NFC_PROTO_MAX 8 /* NFC communication modes */ #define NFC_COMM_ACTIVE 0 @@ -227,6 +233,7 @@ enum nfc_sdp_attr { #define NFC_PROTO_ISO14443_MASK (1 << NFC_PROTO_ISO14443) #define NFC_PROTO_NFC_DEP_MASK (1 << NFC_PROTO_NFC_DEP) #define NFC_PROTO_ISO14443_B_MASK (1 << NFC_PROTO_ISO14443_B) +#define NFC_PROTO_ISO15693_MASK (1 << NFC_PROTO_ISO15693) /* NFC Secure Elements */ #define NFC_SE_UICC 0x1 -- cgit v1.2.3-71-gd317 From 12e3d241e42956da168fd499347855af799f62fb Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Mon, 27 Jan 2014 00:31:31 +0100 Subject: NFC: digital: Add poll support for type 4A tag platform This adds support for ATS request and response handling for type 4A tag activation. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- include/net/nfc/digital.h | 3 ++ net/nfc/digital_core.c | 7 ++++ net/nfc/digital_technology.c | 81 ++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 89 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/nfc/digital.h b/include/net/nfc/digital.h index 1f0528d33500..b9699d7dd039 100644 --- a/include/net/nfc/digital.h +++ b/include/net/nfc/digital.h @@ -51,6 +51,7 @@ enum { NFC_DIGITAL_FRAMING_NFCA_T1T, NFC_DIGITAL_FRAMING_NFCA_T2T, + NFC_DIGITAL_FRAMING_NFCA_T4T, NFC_DIGITAL_FRAMING_NFCA_NFC_DEP, NFC_DIGITAL_FRAMING_NFCF, @@ -208,6 +209,8 @@ struct nfc_digital_dev { u8 curr_rf_tech; u8 curr_nfc_dep_pni; + u16 target_fsc; + int (*skb_check_crc)(struct sk_buff *skb); void (*skb_add_crc)(struct sk_buff *skb); }; diff --git a/net/nfc/digital_core.c b/net/nfc/digital_core.c index 48906ca60540..e1f240266adf 100644 --- a/net/nfc/digital_core.c +++ b/net/nfc/digital_core.c @@ -337,6 +337,11 @@ int digital_target_found(struct nfc_digital_dev *ddev, framing = NFC_DIGITAL_FRAMING_ISO15693_TVT; check_crc = digital_skb_check_crc_b; add_crc = digital_skb_add_crc_b; + + case NFC_PROTO_ISO14443: + framing = NFC_DIGITAL_FRAMING_NFCA_T4T; + check_crc = digital_skb_check_crc_a; + add_crc = digital_skb_add_crc_a; break; default: @@ -714,6 +719,8 @@ struct nfc_digital_dev *nfc_digital_allocate_device(struct nfc_digital_ops *ops, ddev->protocols |= NFC_PROTO_NFC_DEP_MASK; if (supported_protocols & NFC_PROTO_ISO15693_MASK) ddev->protocols |= NFC_PROTO_ISO15693_MASK; + if (supported_protocols & NFC_PROTO_ISO14443_MASK) + ddev->protocols |= NFC_PROTO_ISO14443_MASK; ddev->tx_headroom = tx_headroom + DIGITAL_MAX_HEADER_LEN; ddev->tx_tailroom = tx_tailroom + DIGITAL_CRC_LEN; diff --git a/net/nfc/digital_technology.c b/net/nfc/digital_technology.c index 97d3f602fc06..6649d9461dff 100644 --- a/net/nfc/digital_technology.c +++ b/net/nfc/digital_technology.c @@ -30,6 +30,7 @@ #define DIGITAL_SEL_RES_NFCID1_COMPLETE(sel_res) (!((sel_res) & 0x04)) #define DIGITAL_SEL_RES_IS_T2T(sel_res) (!((sel_res) & 0x60)) +#define DIGITAL_SEL_RES_IS_T4T(sel_res) ((sel_res) & 0x20) #define DIGITAL_SEL_RES_IS_NFC_DEP(sel_res) ((sel_res) & 0x40) #define DIGITAL_SENS_RES_IS_T1T(sens_res) (((sens_res) & 0x0C00) == 0x0C00) @@ -60,6 +61,16 @@ #define DIGITAL_ISO15693_RES_IS_VALID(flags) \ (!((flags) & DIGITAL_ISO15693_RES_FLAG_ERROR)) +static const u8 digital_ats_fsc[] = { + 16, 24, 32, 40, 48, 64, 96, 128, +}; + +#define DIGITAL_ATS_FSCI(t0) ((t0) & 0x0F) +#define DIGITAL_ATS_MAX_FSC 256 + +#define DIGITAL_RATS_BYTE1 0xE0 +#define DIGITAL_RATS_PARAM 0x80 + struct digital_sdd_res { u8 nfcid1[4]; u8 bcc; @@ -107,6 +118,63 @@ struct digital_iso15693_inv_res { static int digital_in_send_sdd_req(struct nfc_digital_dev *ddev, struct nfc_target *target); +static void digital_in_recv_ats(struct nfc_digital_dev *ddev, void *arg, + struct sk_buff *resp) +{ + struct nfc_target *target = arg; + u8 fsdi; + int rc; + + if (IS_ERR(resp)) { + rc = PTR_ERR(resp); + resp = NULL; + goto exit; + } + + if (resp->len < 2) { + rc = -EIO; + goto exit; + } + + fsdi = DIGITAL_ATS_FSCI(resp->data[1]); + if (fsdi >= 8) + ddev->target_fsc = DIGITAL_ATS_MAX_FSC; + else + ddev->target_fsc = digital_ats_fsc[fsdi]; + + ddev->curr_nfc_dep_pni = 0; + + rc = digital_target_found(ddev, target, NFC_PROTO_ISO14443); + +exit: + dev_kfree_skb(resp); + kfree(target); + + if (rc) + digital_poll_next_tech(ddev); +} + +static int digital_in_send_rats(struct nfc_digital_dev *ddev, + struct nfc_target *target) +{ + int rc; + struct sk_buff *skb; + + skb = digital_skb_alloc(ddev, 2); + if (!skb) + return -ENOMEM; + + *skb_put(skb, 1) = DIGITAL_RATS_BYTE1; + *skb_put(skb, 1) = DIGITAL_RATS_PARAM; + + rc = digital_in_send_cmd(ddev, skb, 30, digital_in_recv_ats, + target); + if (rc) + kfree_skb(skb); + + return rc; +} + static void digital_in_recv_sel_res(struct nfc_digital_dev *ddev, void *arg, struct sk_buff *resp) { @@ -144,8 +212,19 @@ static void digital_in_recv_sel_res(struct nfc_digital_dev *ddev, void *arg, goto exit_free_skb; } + target->sel_res = sel_res; + if (DIGITAL_SEL_RES_IS_T2T(sel_res)) { nfc_proto = NFC_PROTO_MIFARE; + } else if (DIGITAL_SEL_RES_IS_T4T(sel_res)) { + rc = digital_in_send_rats(ddev, target); + if (rc) + goto exit; + /* + * Skip target_found and don't free it for now. This will be + * done when receiving the ATS + */ + goto exit_free_skb; } else if (DIGITAL_SEL_RES_IS_NFC_DEP(sel_res)) { nfc_proto = NFC_PROTO_NFC_DEP; } else { @@ -153,8 +232,6 @@ static void digital_in_recv_sel_res(struct nfc_digital_dev *ddev, void *arg, goto exit; } - target->sel_res = sel_res; - rc = digital_target_found(ddev, target, nfc_proto); exit: -- cgit v1.2.3-71-gd317 From d3623099d3509fa68fa28235366049dd3156c63a Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Fri, 14 Feb 2014 15:30:36 +0100 Subject: ipsec: add support of limited SA dump The goal of this patch is to allow userland to dump only a part of SA by specifying a filter during the dump. The kernel is in charge to filter SA, this avoids to generate useless netlink traffic (it save also some cpu cycles). This is particularly useful when there is a big number of SA set on the system. Note that I removed the union in struct xfrm_state_walk to fix a problem on arm. struct netlink_callback->args is defined as a array of 6 long and the first long is used in xfrm code to flag the cb as initialized. Hence, we must have: sizeof(struct xfrm_state_walk) <= sizeof(long) * 5. With the union, it was false on arm (sizeof(struct xfrm_state_walk) was sizeof(long) * 7), due to the padding. In fact, whatever the arch is, this union seems useless, there will be always padding after it. Removing it will not increase the size of this struct (and reduce it on arm). Signed-off-by: Nicolas Dichtel Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 10 +++++----- include/uapi/linux/pfkeyv2.h | 15 ++++++++++++++- include/uapi/linux/xfrm.h | 10 ++++++++++ net/key/af_key.c | 19 ++++++++++++++++++- net/xfrm/xfrm_state.c | 25 ++++++++++++++++++++++++- net/xfrm/xfrm_user.c | 28 +++++++++++++++++++++++++++- 6 files changed, 98 insertions(+), 9 deletions(-) (limited to 'include/net') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 5313ccfdeedf..45332acac022 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -118,11 +118,10 @@ struct xfrm_state_walk { struct list_head all; u8 state; - union { - u8 dying; - u8 proto; - }; + u8 dying; + u8 proto; u32 seq; + struct xfrm_filter *filter; }; /* Full description of state of transformer. */ @@ -1406,7 +1405,8 @@ static inline void xfrm_sysctl_fini(struct net *net) } #endif -void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto); +void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto, + struct xfrm_filter *filter); 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, struct net *net); diff --git a/include/uapi/linux/pfkeyv2.h b/include/uapi/linux/pfkeyv2.h index 0b80c806631f..ada7f0171ccc 100644 --- a/include/uapi/linux/pfkeyv2.h +++ b/include/uapi/linux/pfkeyv2.h @@ -235,6 +235,18 @@ struct sadb_x_kmaddress { } __attribute__((packed)); /* sizeof(struct sadb_x_kmaddress) == 8 */ +/* To specify the SA dump filter */ +struct sadb_x_filter { + __u16 sadb_x_filter_len; + __u16 sadb_x_filter_exttype; + __u32 sadb_x_filter_saddr[4]; + __u32 sadb_x_filter_daddr[4]; + __u16 sadb_x_filter_family; + __u8 sadb_x_filter_splen; + __u8 sadb_x_filter_dplen; +} __attribute__((packed)); +/* sizeof(struct sadb_x_filter) == 40 */ + /* Message types */ #define SADB_RESERVED 0 #define SADB_GETSPI 1 @@ -358,7 +370,8 @@ struct sadb_x_kmaddress { #define SADB_X_EXT_SEC_CTX 24 /* Used with MIGRATE to pass @ to IKE for negotiation */ #define SADB_X_EXT_KMADDRESS 25 -#define SADB_EXT_MAX 25 +#define SADB_X_EXT_FILTER 26 +#define SADB_EXT_MAX 26 /* Identity Extension values */ #define SADB_IDENTTYPE_RESERVED 0 diff --git a/include/uapi/linux/xfrm.h b/include/uapi/linux/xfrm.h index a8cd6a4a2970..6550c679584f 100644 --- a/include/uapi/linux/xfrm.h +++ b/include/uapi/linux/xfrm.h @@ -298,6 +298,8 @@ enum xfrm_attr_type_t { XFRMA_TFCPAD, /* __u32 */ XFRMA_REPLAY_ESN_VAL, /* struct xfrm_replay_esn */ XFRMA_SA_EXTRA_FLAGS, /* __u32 */ + XFRMA_PROTO, /* __u8 */ + XFRMA_FILTER, /* struct xfrm_filter */ __XFRMA_MAX #define XFRMA_MAX (__XFRMA_MAX - 1) @@ -474,6 +476,14 @@ struct xfrm_user_mapping { __be16 new_sport; }; +struct xfrm_filter { + xfrm_address_t saddr; + xfrm_address_t daddr; + __u16 family; + __u8 splen; + __u8 dplen; +}; + #ifndef __KERNEL__ /* backwards compatibility for userspace */ #define XFRMGRP_ACQUIRE 1 diff --git a/net/key/af_key.c b/net/key/af_key.c index e1c69d024197..f0879c19f452 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -1798,6 +1798,7 @@ static void pfkey_dump_sa_done(struct pfkey_sock *pfk) static int pfkey_dump(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs) { u8 proto; + struct xfrm_filter *filter = NULL; struct pfkey_sock *pfk = pfkey_sk(sk); if (pfk->dump.dump != NULL) @@ -1807,11 +1808,27 @@ static int pfkey_dump(struct sock *sk, struct sk_buff *skb, const struct sadb_ms if (proto == 0) return -EINVAL; + if (ext_hdrs[SADB_X_EXT_FILTER - 1]) { + struct sadb_x_filter *xfilter = ext_hdrs[SADB_X_EXT_FILTER - 1]; + + filter = kmalloc(sizeof(*filter), GFP_KERNEL); + if (filter == NULL) + return -ENOMEM; + + memcpy(&filter->saddr, &xfilter->sadb_x_filter_saddr, + sizeof(xfrm_address_t)); + memcpy(&filter->daddr, &xfilter->sadb_x_filter_daddr, + sizeof(xfrm_address_t)); + filter->family = xfilter->sadb_x_filter_family; + filter->splen = xfilter->sadb_x_filter_splen; + filter->dplen = xfilter->sadb_x_filter_dplen; + } + pfk->dump.msg_version = hdr->sadb_msg_version; pfk->dump.msg_portid = hdr->sadb_msg_pid; pfk->dump.dump = pfkey_dump_sa; pfk->dump.done = pfkey_dump_sa_done; - xfrm_state_walk_init(&pfk->dump.u.state, proto); + xfrm_state_walk_init(&pfk->dump.u.state, proto, filter); return pfkey_do_dump(pfk); } diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 0bf12f665b9b..a750901ac3db 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -1603,6 +1603,23 @@ unlock: } EXPORT_SYMBOL(xfrm_alloc_spi); +static bool __xfrm_state_filter_match(struct xfrm_state *x, + struct xfrm_filter *filter) +{ + if (filter) { + if ((filter->family == AF_INET || + filter->family == AF_INET6) && + x->props.family != filter->family) + return false; + + return addr_match(&x->props.saddr, &filter->saddr, + filter->splen) && + addr_match(&x->id.daddr, &filter->daddr, + filter->dplen); + } + return true; +} + int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk, int (*func)(struct xfrm_state *, int, void*), void *data) @@ -1625,6 +1642,8 @@ int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk, state = container_of(x, struct xfrm_state, km); if (!xfrm_id_proto_match(state->id.proto, walk->proto)) continue; + if (!__xfrm_state_filter_match(state, walk->filter)) + continue; err = func(state, walk->seq, data); if (err) { list_move_tail(&walk->all, &x->all); @@ -1643,17 +1662,21 @@ out: } EXPORT_SYMBOL(xfrm_state_walk); -void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto) +void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto, + struct xfrm_filter *filter) { INIT_LIST_HEAD(&walk->all); walk->proto = proto; walk->state = XFRM_STATE_DEAD; walk->seq = 0; + walk->filter = filter; } EXPORT_SYMBOL(xfrm_state_walk_init); void xfrm_state_walk_done(struct xfrm_state_walk *walk, struct net *net) { + kfree(walk->filter); + if (list_empty(&walk->all)) return; diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index d7694f258294..023e5e7ea4c6 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -887,6 +887,7 @@ static int xfrm_dump_sa_done(struct netlink_callback *cb) return 0; } +static const struct nla_policy xfrma_policy[XFRMA_MAX+1]; static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb) { struct net *net = sock_net(skb->sk); @@ -902,8 +903,31 @@ static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb) info.nlmsg_flags = NLM_F_MULTI; if (!cb->args[0]) { + struct nlattr *attrs[XFRMA_MAX+1]; + struct xfrm_filter *filter = NULL; + u8 proto = 0; + int err; + cb->args[0] = 1; - xfrm_state_walk_init(walk, 0); + + err = nlmsg_parse(cb->nlh, 0, attrs, XFRMA_MAX, + xfrma_policy); + if (err < 0) + return err; + + if (attrs[XFRMA_FILTER]) { + filter = kmalloc(sizeof(*filter), GFP_KERNEL); + if (filter == NULL) + return -ENOMEM; + + memcpy(filter, nla_data(attrs[XFRMA_FILTER]), + sizeof(*filter)); + } + + if (attrs[XFRMA_PROTO]) + proto = nla_get_u8(attrs[XFRMA_PROTO]); + + xfrm_state_walk_init(walk, proto, filter); } (void) xfrm_state_walk(net, walk, dump_one_state, &info); @@ -2309,6 +2333,8 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = { [XFRMA_TFCPAD] = { .type = NLA_U32 }, [XFRMA_REPLAY_ESN_VAL] = { .len = sizeof(struct xfrm_replay_state_esn) }, [XFRMA_SA_EXTRA_FLAGS] = { .type = NLA_U32 }, + [XFRMA_PROTO] = { .type = NLA_U8 }, + [XFRMA_FILTER] = { .len = sizeof(struct xfrm_filter) }, }; static const struct xfrm_link { -- cgit v1.2.3-71-gd317 From 9b2777d6089bcd7fb035847f907280560fe233c8 Mon Sep 17 00:00:00 2001 From: Phoebe Buckheister Date: Mon, 17 Feb 2014 11:34:08 +0100 Subject: ieee802154: add TX power control to wpan_phy Replace the current u8 transmit_power in wpan_phy with s8 transmit_power. The u8 field contained the actual tx power and a tolerance field, which no physical radio every used. Adjust sysfs entries to keep compatibility with userspace, give tolerances of +-1dB statically there. This patch only adds support for this in the at86rf230 driver and the RF212 chip. Configuration calculation for RF212 is also somewhat basic, but does the job - the RF212 datasheet gives a large table with suggested values for combinations of TX power and page/channel, if this does not work well, we might have to copy the whole table. Signed-off-by: Phoebe Buckheister Signed-off-by: David S. Miller --- drivers/net/ieee802154/at86rf230.c | 25 +++++++++++++++++++ include/linux/nl802154.h | 4 ++++ include/net/mac802154.h | 5 ++++ include/net/wpan-phy.h | 6 +++-- net/ieee802154/ieee802154.h | 1 + net/ieee802154/netlink.c | 1 + net/ieee802154/nl-phy.c | 49 +++++++++++++++++++++++++++++++++++++- net/ieee802154/nl_policy.c | 2 ++ net/ieee802154/wpan-class.c | 4 +--- net/mac802154/ieee802154_dev.c | 11 +++++++++ 10 files changed, 102 insertions(+), 6 deletions(-) (limited to 'include/net') diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index bd1ef0b3bee9..9afb4b9d7c93 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -753,6 +753,30 @@ at86rf230_set_hw_addr_filt(struct ieee802154_dev *dev, return 0; } +static int +at86rf212_set_txpower(struct ieee802154_dev *dev, int db) +{ + struct at86rf230_local *lp = dev->priv; + int rc; + + /* typical maximum output is 5dBm with RG_PHY_TX_PWR 0x60, lower five + * bits decrease power in 1dB steps. 0x60 represents extra PA gain of + * 0dB. + * thus, supported values for db range from -26 to 5, for 31dB of + * reduction to 0dB of reduction. + */ + if (db > 5 || db < -26) + return -EINVAL; + + db = -(db - 5); + + rc = __at86rf230_write(lp, RG_PHY_TX_PWR, 0x60 | db); + if (rc) + return rc; + + return 0; +} + static struct ieee802154_ops at86rf230_ops = { .owner = THIS_MODULE, .xmit = at86rf230_xmit, @@ -771,6 +795,7 @@ static struct ieee802154_ops at86rf212_ops = { .start = at86rf230_start, .stop = at86rf230_stop, .set_hw_addr_filt = at86rf230_set_hw_addr_filt, + .set_txpower = at86rf212_set_txpower, }; static void at86rf230_irqwork(struct work_struct *work) diff --git a/include/linux/nl802154.h b/include/linux/nl802154.h index fd4f2d1cdf6c..625d19e0a1de 100644 --- a/include/linux/nl802154.h +++ b/include/linux/nl802154.h @@ -70,6 +70,8 @@ enum { IEEE802154_ATTR_PHY_NAME, IEEE802154_ATTR_DEV_TYPE, + IEEE802154_ATTR_TXPOWER, + __IEEE802154_ATTR_MAX, }; @@ -122,6 +124,8 @@ enum { IEEE802154_ADD_IFACE, IEEE802154_DEL_IFACE, + IEEE802154_SET_PHYPARAMS, + __IEEE802154_CMD_MAX, }; diff --git a/include/net/mac802154.h b/include/net/mac802154.h index 807d6b7a943f..8bd2785a663c 100644 --- a/include/net/mac802154.h +++ b/include/net/mac802154.h @@ -113,6 +113,10 @@ struct ieee802154_dev { * Set radio for listening on specific address. * Set the device for listening on specified address. * Returns either zero, or negative errno. + * + * set_txpower: + * Set radio transmit power in dB. Called with pib_lock held. + * Returns either zero, or negative errno. */ struct ieee802154_ops { struct module *owner; @@ -129,6 +133,7 @@ struct ieee802154_ops { unsigned long changed); int (*ieee_addr)(struct ieee802154_dev *dev, u8 addr[IEEE802154_ADDR_LEN]); + int (*set_txpower)(struct ieee802154_dev *dev, int db); }; /* Basic interface to register ieee802154 device */ diff --git a/include/net/wpan-phy.h b/include/net/wpan-phy.h index b52bda8d13b1..47fc0c1bc3c7 100644 --- a/include/net/wpan-phy.h +++ b/include/net/wpan-phy.h @@ -37,14 +37,14 @@ struct wpan_phy { struct mutex pib_lock; /* - * This is a PIB according to 802.15.4-2006. + * This is a PIB according to 802.15.4-2011. * We do not provide timing-related variables, as they * aren't used outside of driver */ u8 current_channel; u8 current_page; u32 channels_supported[32]; - u8 transmit_power; + s8 transmit_power; u8 cca_mode; struct device dev; @@ -54,6 +54,8 @@ struct wpan_phy { const char *name, int type); void (*del_iface)(struct wpan_phy *phy, struct net_device *dev); + int (*set_txpower)(struct wpan_phy *phy, int db); + char priv[0] __attribute__((__aligned__(NETDEV_ALIGN))); }; diff --git a/net/ieee802154/ieee802154.h b/net/ieee802154/ieee802154.h index cee4425b9956..6cbc8965be91 100644 --- a/net/ieee802154/ieee802154.h +++ b/net/ieee802154/ieee802154.h @@ -53,6 +53,7 @@ int ieee802154_list_phy(struct sk_buff *skb, struct genl_info *info); int ieee802154_dump_phy(struct sk_buff *skb, struct netlink_callback *cb); int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info); int ieee802154_del_iface(struct sk_buff *skb, struct genl_info *info); +int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info); enum ieee802154_mcgrp_ids { IEEE802154_COORD_MCGRP, diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c index 43f1b2bf469f..67c151bf4b91 100644 --- a/net/ieee802154/netlink.c +++ b/net/ieee802154/netlink.c @@ -115,6 +115,7 @@ static const struct genl_ops ieee8021154_ops[] = { ieee802154_dump_phy), IEEE802154_OP(IEEE802154_ADD_IFACE, ieee802154_add_iface), IEEE802154_OP(IEEE802154_DEL_IFACE, ieee802154_del_iface), + IEEE802154_OP(IEEE802154_SET_PHYPARAMS, ieee802154_set_phyparams), /* see nl-mac.c */ IEEE802154_OP(IEEE802154_ASSOCIATE_REQ, ieee802154_associate_req), IEEE802154_OP(IEEE802154_ASSOCIATE_RESP, ieee802154_associate_resp), diff --git a/net/ieee802154/nl-phy.c b/net/ieee802154/nl-phy.c index 89b265aea151..d3ee62fbae99 100644 --- a/net/ieee802154/nl-phy.c +++ b/net/ieee802154/nl-phy.c @@ -55,7 +55,8 @@ static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid, mutex_lock(&phy->pib_lock); if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) || nla_put_u8(msg, IEEE802154_ATTR_PAGE, phy->current_page) || - nla_put_u8(msg, IEEE802154_ATTR_CHANNEL, phy->current_channel)) + nla_put_u8(msg, IEEE802154_ATTR_CHANNEL, phy->current_channel) || + nla_put_s8(msg, IEEE802154_ATTR_TXPOWER, phy->transmit_power)) goto nla_put_failure; for (i = 0; i < 32; i++) { if (phy->channels_supported[i]) @@ -354,3 +355,49 @@ out_dev: return rc; } + +int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info) +{ + struct wpan_phy *phy; + const char *name; + int txpower; + int rc = -EINVAL; + + pr_debug("%s\n", __func__); + + if (!info->attrs[IEEE802154_ATTR_PHY_NAME]) + return -EINVAL; + + name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]); + if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0') + return -EINVAL; /* phy name should be null-terminated */ + + txpower = nla_get_s8(info->attrs[IEEE802154_ATTR_TXPOWER]); + + phy = wpan_phy_find(name); + if (!phy) + return -ENODEV; + + if (!phy->set_txpower) + goto out; + + mutex_lock(&phy->pib_lock); + + rc = phy->set_txpower(phy, txpower); + if (rc < 0) { + mutex_unlock(&phy->pib_lock); + goto out; + } + + phy->transmit_power = txpower; + + mutex_unlock(&phy->pib_lock); + + wpan_phy_put(phy); + + return 0; + +out: + wpan_phy_put(phy); + return rc; +} diff --git a/net/ieee802154/nl_policy.c b/net/ieee802154/nl_policy.c index 6adda4d46f95..90b1d0d2c14e 100644 --- a/net/ieee802154/nl_policy.c +++ b/net/ieee802154/nl_policy.c @@ -52,5 +52,7 @@ const struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = { [IEEE802154_ATTR_DURATION] = { .type = NLA_U8, }, [IEEE802154_ATTR_ED_LIST] = { .len = 27 }, [IEEE802154_ATTR_CHANNEL_PAGE_LIST] = { .len = 32 * 4, }, + + [IEEE802154_ATTR_TXPOWER] = { .type = NLA_S8, }, }; diff --git a/net/ieee802154/wpan-class.c b/net/ieee802154/wpan-class.c index 4dd37615a749..8d6f6704da84 100644 --- a/net/ieee802154/wpan-class.c +++ b/net/ieee802154/wpan-class.c @@ -44,9 +44,7 @@ static DEVICE_ATTR_RO(name); MASTER_SHOW(current_channel, "%d"); MASTER_SHOW(current_page, "%d"); -MASTER_SHOW_COMPLEX(transmit_power, "%d +- %d dB", - ((signed char) (phy->transmit_power << 2)) >> 2, - (phy->transmit_power >> 6) ? (phy->transmit_power >> 6) * 3 : 1); +MASTER_SHOW(transmit_power, "%d +- 1 dB"); MASTER_SHOW(cca_mode, "%d"); static ssize_t channels_supported_show(struct device *dev, diff --git a/net/mac802154/ieee802154_dev.c b/net/mac802154/ieee802154_dev.c index 52ae6646a411..9eb49e0886a4 100644 --- a/net/mac802154/ieee802154_dev.c +++ b/net/mac802154/ieee802154_dev.c @@ -165,6 +165,16 @@ err: return ERR_PTR(err); } +static int mac802154_set_txpower(struct wpan_phy *phy, int db) +{ + struct mac802154_priv *priv = wpan_phy_priv(phy); + + if (!priv->ops->set_txpower) + return -ENOTSUPP; + + return priv->ops->set_txpower(&priv->hw, db); +} + struct ieee802154_dev * ieee802154_alloc_device(size_t priv_data_len, struct ieee802154_ops *ops) { @@ -242,6 +252,7 @@ int ieee802154_register_device(struct ieee802154_dev *dev) priv->phy->add_iface = mac802154_add_iface; priv->phy->del_iface = mac802154_del_iface; + priv->phy->set_txpower = mac802154_set_txpower; rc = wpan_phy_register(priv->phy); if (rc < 0) -- cgit v1.2.3-71-gd317 From 84dda3c648fd55898064d76366b14f964cdc9d16 Mon Sep 17 00:00:00 2001 From: Phoebe Buckheister Date: Mon, 17 Feb 2014 11:34:10 +0100 Subject: ieee802154: add support for listen-before-talk in wpan_phy Listen-before-talk is an alternative to CSMA in uncoordinated networks and prescribed by european regulations if one wants to have a device with radio duty cycles above 10% (or less in some bands). Add a phy property to enable/disable LBT in the phy, including support in the at86rf230 driver for RF212 chips. Signed-off-by: Phoebe Buckheister Signed-off-by: David S. Miller --- drivers/net/ieee802154/at86rf230.c | 11 +++++++- include/linux/nl802154.h | 1 + include/net/mac802154.h | 6 ++++ include/net/wpan-phy.h | 3 ++ net/ieee802154/nl-phy.c | 58 ++++++++++++++++++++++++++++++-------- net/ieee802154/nl_policy.c | 1 + net/mac802154/ieee802154_dev.c | 11 ++++++++ 7 files changed, 78 insertions(+), 13 deletions(-) (limited to 'include/net') diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index 04a995dad593..3d40c2350261 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -152,7 +152,7 @@ static inline int is_rf212(struct at86rf230_local *local) #define SR_RESERVED_17_5 0x17, 0x08, 3 #define SR_AACK_UPLD_RES_FT 0x17, 0x10, 4 #define SR_AACK_FLTR_RES_FT 0x17, 0x20, 5 -#define SR_RESERVED_17_2 0x17, 0x40, 6 +#define SR_CSMA_LBT_MODE 0x17, 0x40, 6 #define SR_RESERVED_17_1 0x17, 0x80, 7 #define RG_FTN_CTRL (0x18) #define SR_RESERVED_18_2 0x18, 0x7f, 0 @@ -786,6 +786,14 @@ at86rf212_set_txpower(struct ieee802154_dev *dev, int db) return 0; } +static int +at86rf212_set_lbt(struct ieee802154_dev *dev, bool on) +{ + struct at86rf230_local *lp = dev->priv; + + return at86rf230_write_subreg(lp, SR_CSMA_LBT_MODE, on); +} + static struct ieee802154_ops at86rf230_ops = { .owner = THIS_MODULE, .xmit = at86rf230_xmit, @@ -805,6 +813,7 @@ static struct ieee802154_ops at86rf212_ops = { .stop = at86rf230_stop, .set_hw_addr_filt = at86rf230_set_hw_addr_filt, .set_txpower = at86rf212_set_txpower, + .set_lbt = at86rf212_set_lbt, }; static void at86rf230_irqwork(struct work_struct *work) diff --git a/include/linux/nl802154.h b/include/linux/nl802154.h index 625d19e0a1de..326baee227f7 100644 --- a/include/linux/nl802154.h +++ b/include/linux/nl802154.h @@ -71,6 +71,7 @@ enum { IEEE802154_ATTR_DEV_TYPE, IEEE802154_ATTR_TXPOWER, + IEEE802154_ATTR_LBT_ENABLED, __IEEE802154_ATTR_MAX, }; diff --git a/include/net/mac802154.h b/include/net/mac802154.h index 8bd2785a663c..521edcb0e586 100644 --- a/include/net/mac802154.h +++ b/include/net/mac802154.h @@ -117,6 +117,11 @@ struct ieee802154_dev { * set_txpower: * Set radio transmit power in dB. Called with pib_lock held. * Returns either zero, or negative errno. + * + * set_lbt + * Enables or disables listen before talk on the device. Called with + * pib_lock held. + * Returns either zero, or negative errno. */ struct ieee802154_ops { struct module *owner; @@ -134,6 +139,7 @@ struct ieee802154_ops { int (*ieee_addr)(struct ieee802154_dev *dev, u8 addr[IEEE802154_ADDR_LEN]); int (*set_txpower)(struct ieee802154_dev *dev, int db); + int (*set_lbt)(struct ieee802154_dev *dev, bool on); }; /* Basic interface to register ieee802154 device */ diff --git a/include/net/wpan-phy.h b/include/net/wpan-phy.h index 47fc0c1bc3c7..804e6c4f5f8a 100644 --- a/include/net/wpan-phy.h +++ b/include/net/wpan-phy.h @@ -47,6 +47,8 @@ struct wpan_phy { s8 transmit_power; u8 cca_mode; + bool lbt; + struct device dev; int idx; @@ -55,6 +57,7 @@ struct wpan_phy { void (*del_iface)(struct wpan_phy *phy, struct net_device *dev); int (*set_txpower)(struct wpan_phy *phy, int db); + int (*set_lbt)(struct wpan_phy *phy, bool on); char priv[0] __attribute__((__aligned__(NETDEV_ALIGN))); }; diff --git a/net/ieee802154/nl-phy.c b/net/ieee802154/nl-phy.c index d3ee62fbae99..f029310b0662 100644 --- a/net/ieee802154/nl-phy.c +++ b/net/ieee802154/nl-phy.c @@ -56,7 +56,8 @@ static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid, if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) || nla_put_u8(msg, IEEE802154_ATTR_PAGE, phy->current_page) || nla_put_u8(msg, IEEE802154_ATTR_CHANNEL, phy->current_channel) || - nla_put_s8(msg, IEEE802154_ATTR_TXPOWER, phy->transmit_power)) + nla_put_s8(msg, IEEE802154_ATTR_TXPOWER, phy->transmit_power) || + nla_put_u8(msg, IEEE802154_ATTR_LBT_ENABLED, phy->lbt)) goto nla_put_failure; for (i = 0; i < 32; i++) { if (phy->channels_supported[i]) @@ -356,40 +357,71 @@ out_dev: return rc; } +static int phy_set_txpower(struct wpan_phy *phy, struct genl_info *info) +{ + int txpower = nla_get_s8(info->attrs[IEEE802154_ATTR_TXPOWER]); + int rc; + + rc = phy->set_txpower(phy, txpower); + if (rc < 0) + return rc; + + phy->transmit_power = txpower; + + return 0; +} + +static int phy_set_lbt(struct wpan_phy *phy, struct genl_info *info) +{ + u8 on = !!nla_get_u8(info->attrs[IEEE802154_ATTR_LBT_ENABLED]); + int rc; + + rc = phy->set_lbt(phy, on); + if (rc < 0) + return rc; + + phy->lbt = on; + + return 0; +} + int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info) { struct wpan_phy *phy; const char *name; - int txpower; - int rc = -EINVAL; + int rc = -ENOTSUPP; pr_debug("%s\n", __func__); - if (!info->attrs[IEEE802154_ATTR_PHY_NAME]) + if (!info->attrs[IEEE802154_ATTR_PHY_NAME] && + !info->attrs[IEEE802154_ATTR_LBT_ENABLED]) return -EINVAL; name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]); if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0') return -EINVAL; /* phy name should be null-terminated */ - txpower = nla_get_s8(info->attrs[IEEE802154_ATTR_TXPOWER]); - phy = wpan_phy_find(name); if (!phy) return -ENODEV; - if (!phy->set_txpower) + if ((!phy->set_txpower && info->attrs[IEEE802154_ATTR_TXPOWER]) || + (!phy->set_lbt && info->attrs[IEEE802154_ATTR_LBT_ENABLED])) goto out; mutex_lock(&phy->pib_lock); - rc = phy->set_txpower(phy, txpower); - if (rc < 0) { - mutex_unlock(&phy->pib_lock); - goto out; + if (info->attrs[IEEE802154_ATTR_TXPOWER]) { + rc = phy_set_txpower(phy, info); + if (rc < 0) + goto error; } - phy->transmit_power = txpower; + if (info->attrs[IEEE802154_ATTR_LBT_ENABLED]) { + rc = phy_set_lbt(phy, info); + if (rc < 0) + goto error; + } mutex_unlock(&phy->pib_lock); @@ -397,6 +429,8 @@ int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info) return 0; +error: + mutex_unlock(&phy->pib_lock); out: wpan_phy_put(phy); return rc; diff --git a/net/ieee802154/nl_policy.c b/net/ieee802154/nl_policy.c index 90b1d0d2c14e..a09f6423a6e9 100644 --- a/net/ieee802154/nl_policy.c +++ b/net/ieee802154/nl_policy.c @@ -54,5 +54,6 @@ const struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = { [IEEE802154_ATTR_CHANNEL_PAGE_LIST] = { .len = 32 * 4, }, [IEEE802154_ATTR_TXPOWER] = { .type = NLA_S8, }, + [IEEE802154_ATTR_LBT_ENABLED] = { .type = NLA_U8, }, }; diff --git a/net/mac802154/ieee802154_dev.c b/net/mac802154/ieee802154_dev.c index 9eb49e0886a4..56338c8cfc33 100644 --- a/net/mac802154/ieee802154_dev.c +++ b/net/mac802154/ieee802154_dev.c @@ -175,6 +175,16 @@ static int mac802154_set_txpower(struct wpan_phy *phy, int db) return priv->ops->set_txpower(&priv->hw, db); } +static int mac802154_set_lbt(struct wpan_phy *phy, bool on) +{ + struct mac802154_priv *priv = wpan_phy_priv(phy); + + if (!priv->ops->set_lbt) + return -ENOTSUPP; + + return priv->ops->set_lbt(&priv->hw, on); +} + struct ieee802154_dev * ieee802154_alloc_device(size_t priv_data_len, struct ieee802154_ops *ops) { @@ -253,6 +263,7 @@ int ieee802154_register_device(struct ieee802154_dev *dev) priv->phy->add_iface = mac802154_add_iface; priv->phy->del_iface = mac802154_del_iface; priv->phy->set_txpower = mac802154_set_txpower; + priv->phy->set_lbt = mac802154_set_lbt; rc = wpan_phy_register(priv->phy); if (rc < 0) -- cgit v1.2.3-71-gd317 From ba08fea53a43e02b590d89224afdad976dece841 Mon Sep 17 00:00:00 2001 From: Phoebe Buckheister Date: Mon, 17 Feb 2014 11:34:11 +0100 Subject: ieee802154: add support for CCA mode in wpan phys The standard describes four modes of clear channel assesment: "energy above threshold", "carrier found", and the logical and/or of these two. Support for CCA mode setting is included in the at86rf230 driver, predicated for RF212 chips. Signed-off-by: Phoebe Buckheister Signed-off-by: David S. Miller --- drivers/net/ieee802154/at86rf230.c | 9 +++++++++ include/linux/nl802154.h | 1 + include/net/mac802154.h | 5 +++++ include/net/wpan-phy.h | 1 + net/ieee802154/nl-phy.c | 32 +++++++++++++++++++++++++++++--- net/ieee802154/nl_policy.c | 1 + net/mac802154/ieee802154_dev.c | 11 +++++++++++ 7 files changed, 57 insertions(+), 3 deletions(-) (limited to 'include/net') diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index 3d40c2350261..c60871aff333 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -794,6 +794,14 @@ at86rf212_set_lbt(struct ieee802154_dev *dev, bool on) return at86rf230_write_subreg(lp, SR_CSMA_LBT_MODE, on); } +static int +at86rf212_set_cca_mode(struct ieee802154_dev *dev, u8 mode) +{ + struct at86rf230_local *lp = dev->priv; + + return at86rf230_write_subreg(lp, SR_CCA_MODE, mode); +} + static struct ieee802154_ops at86rf230_ops = { .owner = THIS_MODULE, .xmit = at86rf230_xmit, @@ -814,6 +822,7 @@ static struct ieee802154_ops at86rf212_ops = { .set_hw_addr_filt = at86rf230_set_hw_addr_filt, .set_txpower = at86rf212_set_txpower, .set_lbt = at86rf212_set_lbt, + .set_cca_mode = at86rf212_set_cca_mode, }; static void at86rf230_irqwork(struct work_struct *work) diff --git a/include/linux/nl802154.h b/include/linux/nl802154.h index 326baee227f7..5edefc14bd83 100644 --- a/include/linux/nl802154.h +++ b/include/linux/nl802154.h @@ -72,6 +72,7 @@ enum { IEEE802154_ATTR_TXPOWER, IEEE802154_ATTR_LBT_ENABLED, + IEEE802154_ATTR_CCA_MODE, __IEEE802154_ATTR_MAX, }; diff --git a/include/net/mac802154.h b/include/net/mac802154.h index 521edcb0e586..1a98e5014e66 100644 --- a/include/net/mac802154.h +++ b/include/net/mac802154.h @@ -122,6 +122,10 @@ struct ieee802154_dev { * Enables or disables listen before talk on the device. Called with * pib_lock held. * Returns either zero, or negative errno. + * + * set_cca_mode + * Sets the CCA mode used by the device. Called with pib_lock held. + * Returns either zero, or negative errno. */ struct ieee802154_ops { struct module *owner; @@ -140,6 +144,7 @@ struct ieee802154_ops { u8 addr[IEEE802154_ADDR_LEN]); int (*set_txpower)(struct ieee802154_dev *dev, int db); int (*set_lbt)(struct ieee802154_dev *dev, bool on); + int (*set_cca_mode)(struct ieee802154_dev *dev, u8 mode); }; /* Basic interface to register ieee802154 device */ diff --git a/include/net/wpan-phy.h b/include/net/wpan-phy.h index 804e6c4f5f8a..03b59051972d 100644 --- a/include/net/wpan-phy.h +++ b/include/net/wpan-phy.h @@ -58,6 +58,7 @@ struct wpan_phy { int (*set_txpower)(struct wpan_phy *phy, int db); int (*set_lbt)(struct wpan_phy *phy, bool on); + int (*set_cca_mode)(struct wpan_phy *phy, u8 cca_mode); char priv[0] __attribute__((__aligned__(NETDEV_ALIGN))); }; diff --git a/net/ieee802154/nl-phy.c b/net/ieee802154/nl-phy.c index f029310b0662..36f58d633868 100644 --- a/net/ieee802154/nl-phy.c +++ b/net/ieee802154/nl-phy.c @@ -57,7 +57,8 @@ static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid, nla_put_u8(msg, IEEE802154_ATTR_PAGE, phy->current_page) || nla_put_u8(msg, IEEE802154_ATTR_CHANNEL, phy->current_channel) || nla_put_s8(msg, IEEE802154_ATTR_TXPOWER, phy->transmit_power) || - nla_put_u8(msg, IEEE802154_ATTR_LBT_ENABLED, phy->lbt)) + nla_put_u8(msg, IEEE802154_ATTR_LBT_ENABLED, phy->lbt) || + nla_put_u8(msg, IEEE802154_ATTR_CCA_MODE, phy->cca_mode)) goto nla_put_failure; for (i = 0; i < 32; i++) { if (phy->channels_supported[i]) @@ -385,6 +386,23 @@ static int phy_set_lbt(struct wpan_phy *phy, struct genl_info *info) return 0; } +static int phy_set_cca_mode(struct wpan_phy *phy, struct genl_info *info) +{ + u8 mode = nla_get_u8(info->attrs[IEEE802154_ATTR_CCA_MODE]); + int rc; + + if (mode > 3) + return -EINVAL; + + rc = phy->set_cca_mode(phy, mode); + if (rc < 0) + return rc; + + phy->cca_mode = mode; + + return 0; +} + int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info) { struct wpan_phy *phy; @@ -394,7 +412,8 @@ int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info) pr_debug("%s\n", __func__); if (!info->attrs[IEEE802154_ATTR_PHY_NAME] && - !info->attrs[IEEE802154_ATTR_LBT_ENABLED]) + !info->attrs[IEEE802154_ATTR_LBT_ENABLED] && + !info->attrs[IEEE802154_ATTR_CCA_MODE]) return -EINVAL; name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]); @@ -406,7 +425,8 @@ int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info) return -ENODEV; if ((!phy->set_txpower && info->attrs[IEEE802154_ATTR_TXPOWER]) || - (!phy->set_lbt && info->attrs[IEEE802154_ATTR_LBT_ENABLED])) + (!phy->set_lbt && info->attrs[IEEE802154_ATTR_LBT_ENABLED]) || + (!phy->set_cca_mode && info->attrs[IEEE802154_ATTR_CCA_MODE])) goto out; mutex_lock(&phy->pib_lock); @@ -423,6 +443,12 @@ int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info) goto error; } + if (info->attrs[IEEE802154_ATTR_CCA_MODE]) { + rc = phy_set_cca_mode(phy, info); + if (rc < 0) + goto error; + } + mutex_unlock(&phy->pib_lock); wpan_phy_put(phy); diff --git a/net/ieee802154/nl_policy.c b/net/ieee802154/nl_policy.c index a09f6423a6e9..d87c2c904110 100644 --- a/net/ieee802154/nl_policy.c +++ b/net/ieee802154/nl_policy.c @@ -55,5 +55,6 @@ const struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = { [IEEE802154_ATTR_TXPOWER] = { .type = NLA_S8, }, [IEEE802154_ATTR_LBT_ENABLED] = { .type = NLA_U8, }, + [IEEE802154_ATTR_CCA_MODE] = { .type = NLA_U8, }, }; diff --git a/net/mac802154/ieee802154_dev.c b/net/mac802154/ieee802154_dev.c index 56338c8cfc33..4965e4ce6b5b 100644 --- a/net/mac802154/ieee802154_dev.c +++ b/net/mac802154/ieee802154_dev.c @@ -185,6 +185,16 @@ static int mac802154_set_lbt(struct wpan_phy *phy, bool on) return priv->ops->set_lbt(&priv->hw, on); } +static int mac802154_set_cca_mode(struct wpan_phy *phy, u8 mode) +{ + struct mac802154_priv *priv = wpan_phy_priv(phy); + + if (!priv->ops->set_cca_mode) + return -ENOTSUPP; + + return priv->ops->set_cca_mode(&priv->hw, mode); +} + struct ieee802154_dev * ieee802154_alloc_device(size_t priv_data_len, struct ieee802154_ops *ops) { @@ -264,6 +274,7 @@ int ieee802154_register_device(struct ieee802154_dev *dev) priv->phy->del_iface = mac802154_del_iface; priv->phy->set_txpower = mac802154_set_txpower; priv->phy->set_lbt = mac802154_set_lbt; + priv->phy->set_cca_mode = mac802154_set_cca_mode; rc = wpan_phy_register(priv->phy); if (rc < 0) -- cgit v1.2.3-71-gd317 From 6ca001978dce0d50ebac01a38d6287f241a520c6 Mon Sep 17 00:00:00 2001 From: Phoebe Buckheister Date: Mon, 17 Feb 2014 11:34:12 +0100 Subject: ieee802154: add support for setting CCA energy detection levels Since three of the four clear channel assesment modes make use of energy detection, provide an API to set the energy detection threshold. Driver support for this is available in at86rf230 for the RF212 chips. Since for these chips the minimal energy detection threshold depends on page and channel used, add a field to struct at86rf230_local that stores the minimal threshold. Actual ED thresholds are configured as offsets from this value. For RF212, setting the ED threshold will not work before a channel/page has been set due to the dependency of energy detection in the chip and the actual channel/page selected. Signed-off-by: Phoebe Buckheister Signed-off-by: David S. Miller --- drivers/net/ieee802154/at86rf230.c | 26 ++++++++++++++++++++++++-- include/linux/nl802154.h | 1 + include/net/mac802154.h | 7 +++++++ include/net/wpan-phy.h | 2 ++ net/ieee802154/nl-phy.c | 30 +++++++++++++++++++++++++++--- net/ieee802154/nl_policy.c | 1 + net/mac802154/ieee802154_dev.c | 11 +++++++++++ 7 files changed, 73 insertions(+), 5 deletions(-) (limited to 'include/net') diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index c60871aff333..20596be61028 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -52,6 +52,8 @@ struct at86rf230_local { spinlock_t lock; bool irq_busy; bool is_tx; + + int rssi_base_val; }; static inline int is_rf212(struct at86rf230_local *local) @@ -580,6 +582,8 @@ at86rf230_stop(struct ieee802154_dev *dev) static int at86rf230_set_channel(struct at86rf230_local *lp, int page, int channel) { + lp->rssi_base_val = -91; + return at86rf230_write_subreg(lp, SR_CHANNEL, channel); } @@ -595,10 +599,13 @@ at86rf212_set_channel(struct at86rf230_local *lp, int page, int channel) if (rc < 0) return rc; - if (page == 0) + if (page == 0) { rc = at86rf230_write_subreg(lp, SR_BPSK_QPSK, 0); - else + lp->rssi_base_val = -100; + } else { rc = at86rf230_write_subreg(lp, SR_BPSK_QPSK, 1); + lp->rssi_base_val = -98; + } if (rc < 0) return rc; @@ -802,6 +809,20 @@ at86rf212_set_cca_mode(struct ieee802154_dev *dev, u8 mode) return at86rf230_write_subreg(lp, SR_CCA_MODE, mode); } +static int +at86rf212_set_cca_ed_level(struct ieee802154_dev *dev, s32 level) +{ + struct at86rf230_local *lp = dev->priv; + int desens_steps; + + if (level < lp->rssi_base_val || level > 30) + return -EINVAL; + + desens_steps = (level - lp->rssi_base_val) * 100 / 207; + + return at86rf230_write_subreg(lp, SR_CCA_ED_THRES, desens_steps); +} + static struct ieee802154_ops at86rf230_ops = { .owner = THIS_MODULE, .xmit = at86rf230_xmit, @@ -823,6 +844,7 @@ static struct ieee802154_ops at86rf212_ops = { .set_txpower = at86rf212_set_txpower, .set_lbt = at86rf212_set_lbt, .set_cca_mode = at86rf212_set_cca_mode, + .set_cca_ed_level = at86rf212_set_cca_ed_level, }; static void at86rf230_irqwork(struct work_struct *work) diff --git a/include/linux/nl802154.h b/include/linux/nl802154.h index 5edefc14bd83..0594a0ae71ba 100644 --- a/include/linux/nl802154.h +++ b/include/linux/nl802154.h @@ -73,6 +73,7 @@ enum { IEEE802154_ATTR_TXPOWER, IEEE802154_ATTR_LBT_ENABLED, IEEE802154_ATTR_CCA_MODE, + IEEE802154_ATTR_CCA_ED_LEVEL, __IEEE802154_ATTR_MAX, }; diff --git a/include/net/mac802154.h b/include/net/mac802154.h index 1a98e5014e66..15fe6bca80f0 100644 --- a/include/net/mac802154.h +++ b/include/net/mac802154.h @@ -126,6 +126,11 @@ struct ieee802154_dev { * set_cca_mode * Sets the CCA mode used by the device. Called with pib_lock held. * Returns either zero, or negative errno. + * + * set_cca_ed_level + * Sets the CCA energy detection threshold in dBm. Called with pib_lock + * held. + * Returns either zero, or negative errno. */ struct ieee802154_ops { struct module *owner; @@ -145,6 +150,8 @@ struct ieee802154_ops { int (*set_txpower)(struct ieee802154_dev *dev, int db); int (*set_lbt)(struct ieee802154_dev *dev, bool on); int (*set_cca_mode)(struct ieee802154_dev *dev, u8 mode); + int (*set_cca_ed_level)(struct ieee802154_dev *dev, + s32 level); }; /* Basic interface to register ieee802154 device */ diff --git a/include/net/wpan-phy.h b/include/net/wpan-phy.h index 03b59051972d..0b570ad5e5fa 100644 --- a/include/net/wpan-phy.h +++ b/include/net/wpan-phy.h @@ -48,6 +48,7 @@ struct wpan_phy { u8 cca_mode; bool lbt; + s32 cca_ed_level; struct device dev; int idx; @@ -59,6 +60,7 @@ struct wpan_phy { int (*set_txpower)(struct wpan_phy *phy, int db); int (*set_lbt)(struct wpan_phy *phy, bool on); int (*set_cca_mode)(struct wpan_phy *phy, u8 cca_mode); + int (*set_cca_ed_level)(struct wpan_phy *phy, int level); char priv[0] __attribute__((__aligned__(NETDEV_ALIGN))); }; diff --git a/net/ieee802154/nl-phy.c b/net/ieee802154/nl-phy.c index 36f58d633868..0af0d424dee0 100644 --- a/net/ieee802154/nl-phy.c +++ b/net/ieee802154/nl-phy.c @@ -58,7 +58,8 @@ static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid, nla_put_u8(msg, IEEE802154_ATTR_CHANNEL, phy->current_channel) || nla_put_s8(msg, IEEE802154_ATTR_TXPOWER, phy->transmit_power) || nla_put_u8(msg, IEEE802154_ATTR_LBT_ENABLED, phy->lbt) || - nla_put_u8(msg, IEEE802154_ATTR_CCA_MODE, phy->cca_mode)) + nla_put_u8(msg, IEEE802154_ATTR_CCA_MODE, phy->cca_mode) || + nla_put_s32(msg, IEEE802154_ATTR_CCA_ED_LEVEL, phy->cca_ed_level)) goto nla_put_failure; for (i = 0; i < 32; i++) { if (phy->channels_supported[i]) @@ -403,6 +404,20 @@ static int phy_set_cca_mode(struct wpan_phy *phy, struct genl_info *info) return 0; } +static int phy_set_cca_ed_level(struct wpan_phy *phy, struct genl_info *info) +{ + s32 level = nla_get_s32(info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]); + int rc; + + rc = phy->set_cca_ed_level(phy, level); + if (rc < 0) + return rc; + + phy->cca_ed_level = level; + + return 0; +} + int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info) { struct wpan_phy *phy; @@ -413,7 +428,8 @@ int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info) if (!info->attrs[IEEE802154_ATTR_PHY_NAME] && !info->attrs[IEEE802154_ATTR_LBT_ENABLED] && - !info->attrs[IEEE802154_ATTR_CCA_MODE]) + !info->attrs[IEEE802154_ATTR_CCA_MODE] && + !info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]) return -EINVAL; name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]); @@ -426,7 +442,9 @@ int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info) if ((!phy->set_txpower && info->attrs[IEEE802154_ATTR_TXPOWER]) || (!phy->set_lbt && info->attrs[IEEE802154_ATTR_LBT_ENABLED]) || - (!phy->set_cca_mode && info->attrs[IEEE802154_ATTR_CCA_MODE])) + (!phy->set_cca_mode && info->attrs[IEEE802154_ATTR_CCA_MODE]) || + (!phy->set_cca_ed_level && + info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL])) goto out; mutex_lock(&phy->pib_lock); @@ -449,6 +467,12 @@ int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info) goto error; } + if (info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]) { + rc = phy_set_cca_ed_level(phy, info); + if (rc < 0) + goto error; + } + mutex_unlock(&phy->pib_lock); wpan_phy_put(phy); diff --git a/net/ieee802154/nl_policy.c b/net/ieee802154/nl_policy.c index d87c2c904110..55b5616295ff 100644 --- a/net/ieee802154/nl_policy.c +++ b/net/ieee802154/nl_policy.c @@ -56,5 +56,6 @@ const struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = { [IEEE802154_ATTR_TXPOWER] = { .type = NLA_S8, }, [IEEE802154_ATTR_LBT_ENABLED] = { .type = NLA_U8, }, [IEEE802154_ATTR_CCA_MODE] = { .type = NLA_U8, }, + [IEEE802154_ATTR_CCA_ED_LEVEL] = { .type = NLA_S32, }, }; diff --git a/net/mac802154/ieee802154_dev.c b/net/mac802154/ieee802154_dev.c index 4965e4ce6b5b..4707f36546d3 100644 --- a/net/mac802154/ieee802154_dev.c +++ b/net/mac802154/ieee802154_dev.c @@ -195,6 +195,16 @@ static int mac802154_set_cca_mode(struct wpan_phy *phy, u8 mode) return priv->ops->set_cca_mode(&priv->hw, mode); } +static int mac802154_set_cca_ed_level(struct wpan_phy *phy, s32 level) +{ + struct mac802154_priv *priv = wpan_phy_priv(phy); + + if (!priv->ops->set_cca_ed_level) + return -ENOTSUPP; + + return priv->ops->set_cca_ed_level(&priv->hw, level); +} + struct ieee802154_dev * ieee802154_alloc_device(size_t priv_data_len, struct ieee802154_ops *ops) { @@ -275,6 +285,7 @@ int ieee802154_register_device(struct ieee802154_dev *dev) priv->phy->set_txpower = mac802154_set_txpower; priv->phy->set_lbt = mac802154_set_lbt; priv->phy->set_cca_mode = mac802154_set_cca_mode; + priv->phy->set_cca_ed_level = mac802154_set_cca_ed_level; rc = wpan_phy_register(priv->phy); if (rc < 0) -- cgit v1.2.3-71-gd317 From 4244db1b0b7bc9ab7b67d8c1c38de6cf15bc87a8 Mon Sep 17 00:00:00 2001 From: Phoebe Buckheister Date: Mon, 17 Feb 2014 11:34:14 +0100 Subject: ieee802154: add netlink APIs for smartMAC configuration Introduce new netlink attributes for SET_PHY_ATTRS: * CSMA minimal backoff exponent * CSMA maximal backoff exponent * CSMA retry limit * frame retransmission limit The CSMA attributes shall correspond to minBE, maxBE and maxCSMABackoffs of 802.15.4, respectively. The frame retransmission shall correspond to maxFrameRetries of 802.15.4, unless given as -1: then the old behaviour of the stack shall apply. For RF2xy, the old behaviour is to not do channel sensing at all and simply send *right now*, which is not intended behaviour for most applications and actually prohibited for some channel/page combinations. For all values except frame retransmission limit, the defaults of 802.15.4 apply. Frame retransmission limits are set to -1 to indicate backward-compatible behaviour. Signed-off-by: Phoebe Buckheister Signed-off-by: David S. Miller --- include/linux/nl802154.h | 5 +++ include/net/mac802154.h | 12 ++++++++ include/net/wpan-phy.h | 7 +++++ net/ieee802154/nl-phy.c | 69 ++++++++++++++++++++++++++++++++++++++++-- net/ieee802154/nl_policy.c | 5 +++ net/ieee802154/wpan-class.c | 6 ++++ net/mac802154/ieee802154_dev.c | 23 ++++++++++++++ 7 files changed, 125 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/linux/nl802154.h b/include/linux/nl802154.h index 0594a0ae71ba..e110b8c266f5 100644 --- a/include/linux/nl802154.h +++ b/include/linux/nl802154.h @@ -74,6 +74,11 @@ enum { IEEE802154_ATTR_LBT_ENABLED, IEEE802154_ATTR_CCA_MODE, IEEE802154_ATTR_CCA_ED_LEVEL, + IEEE802154_ATTR_CSMA_RETRIES, + IEEE802154_ATTR_CSMA_MIN_BE, + IEEE802154_ATTR_CSMA_MAX_BE, + + IEEE802154_ATTR_FRAME_RETRIES, __IEEE802154_ATTR_MAX, }; diff --git a/include/net/mac802154.h b/include/net/mac802154.h index 15fe6bca80f0..8ca3d04e7558 100644 --- a/include/net/mac802154.h +++ b/include/net/mac802154.h @@ -131,6 +131,14 @@ struct ieee802154_dev { * Sets the CCA energy detection threshold in dBm. Called with pib_lock * held. * Returns either zero, or negative errno. + * + * set_csma_params + * Sets the CSMA parameter set for the PHY. Called with pib_lock held. + * Returns either zero, or negative errno. + * + * set_frame_retries + * Sets the retransmission attempt limit. Called with pib_lock held. + * Returns either zero, or negative errno. */ struct ieee802154_ops { struct module *owner; @@ -152,6 +160,10 @@ struct ieee802154_ops { int (*set_cca_mode)(struct ieee802154_dev *dev, u8 mode); int (*set_cca_ed_level)(struct ieee802154_dev *dev, s32 level); + int (*set_csma_params)(struct ieee802154_dev *dev, + u8 min_be, u8 max_be, u8 retries); + int (*set_frame_retries)(struct ieee802154_dev *dev, + s8 retries); }; /* Basic interface to register ieee802154 device */ diff --git a/include/net/wpan-phy.h b/include/net/wpan-phy.h index 0b570ad5e5fa..10ab0fc6d4f7 100644 --- a/include/net/wpan-phy.h +++ b/include/net/wpan-phy.h @@ -46,6 +46,10 @@ struct wpan_phy { u32 channels_supported[32]; s8 transmit_power; u8 cca_mode; + u8 min_be; + u8 max_be; + u8 csma_retries; + s8 frame_retries; bool lbt; s32 cca_ed_level; @@ -61,6 +65,9 @@ struct wpan_phy { int (*set_lbt)(struct wpan_phy *phy, bool on); int (*set_cca_mode)(struct wpan_phy *phy, u8 cca_mode); int (*set_cca_ed_level)(struct wpan_phy *phy, int level); + int (*set_csma_params)(struct wpan_phy *phy, u8 min_be, u8 max_be, + u8 retries); + int (*set_frame_retries)(struct wpan_phy *phy, s8 retries); char priv[0] __attribute__((__aligned__(NETDEV_ALIGN))); }; diff --git a/net/ieee802154/nl-phy.c b/net/ieee802154/nl-phy.c index 0af0d424dee0..c9dfd6f59e34 100644 --- a/net/ieee802154/nl-phy.c +++ b/net/ieee802154/nl-phy.c @@ -59,7 +59,11 @@ static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid, nla_put_s8(msg, IEEE802154_ATTR_TXPOWER, phy->transmit_power) || nla_put_u8(msg, IEEE802154_ATTR_LBT_ENABLED, phy->lbt) || nla_put_u8(msg, IEEE802154_ATTR_CCA_MODE, phy->cca_mode) || - nla_put_s32(msg, IEEE802154_ATTR_CCA_ED_LEVEL, phy->cca_ed_level)) + nla_put_s32(msg, IEEE802154_ATTR_CCA_ED_LEVEL, phy->cca_ed_level) || + nla_put_u8(msg, IEEE802154_ATTR_CSMA_RETRIES, phy->csma_retries) || + nla_put_u8(msg, IEEE802154_ATTR_CSMA_MIN_BE, phy->min_be) || + nla_put_u8(msg, IEEE802154_ATTR_CSMA_MAX_BE, phy->max_be) || + nla_put_s8(msg, IEEE802154_ATTR_FRAME_RETRIES, phy->frame_retries)) goto nla_put_failure; for (i = 0; i < 32; i++) { if (phy->channels_supported[i]) @@ -418,6 +422,49 @@ static int phy_set_cca_ed_level(struct wpan_phy *phy, struct genl_info *info) return 0; } +static int phy_set_csma_params(struct wpan_phy *phy, struct genl_info *info) +{ + int rc; + u8 min_be = phy->min_be; + u8 max_be = phy->max_be; + u8 retries = phy->csma_retries; + + if (info->attrs[IEEE802154_ATTR_CSMA_RETRIES]) + retries = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_RETRIES]); + if (info->attrs[IEEE802154_ATTR_CSMA_MIN_BE]) + min_be = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_MIN_BE]); + if (info->attrs[IEEE802154_ATTR_CSMA_MAX_BE]) + max_be = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_MAX_BE]); + + if (retries > 5 || max_be > 8 || min_be > max_be || + retries < -1 || retries > 7) + return -EINVAL; + + rc = phy->set_csma_params(phy, min_be, max_be, retries); + if (rc < 0) + return rc; + + phy->min_be = min_be; + phy->max_be = max_be; + phy->csma_retries = retries; + + return 0; +} + +static int phy_set_frame_retries(struct wpan_phy *phy, struct genl_info *info) +{ + s8 retries = nla_get_s8(info->attrs[IEEE802154_ATTR_FRAME_RETRIES]); + int rc; + + rc = phy->set_frame_retries(phy, retries); + if (rc < 0) + return rc; + + phy->frame_retries = retries; + + return 0; +} + int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info) { struct wpan_phy *phy; @@ -429,7 +476,11 @@ int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info) if (!info->attrs[IEEE802154_ATTR_PHY_NAME] && !info->attrs[IEEE802154_ATTR_LBT_ENABLED] && !info->attrs[IEEE802154_ATTR_CCA_MODE] && - !info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]) + !info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL] && + !info->attrs[IEEE802154_ATTR_CSMA_RETRIES] && + !info->attrs[IEEE802154_ATTR_CSMA_MIN_BE] && + !info->attrs[IEEE802154_ATTR_CSMA_MAX_BE] && + !info->attrs[IEEE802154_ATTR_FRAME_RETRIES]) return -EINVAL; name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]); @@ -473,6 +524,20 @@ int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info) goto error; } + if (info->attrs[IEEE802154_ATTR_CSMA_RETRIES] || + info->attrs[IEEE802154_ATTR_CSMA_MIN_BE] || + info->attrs[IEEE802154_ATTR_CSMA_MAX_BE]) { + rc = phy_set_csma_params(phy, info); + if (rc < 0) + goto error; + } + + if (info->attrs[IEEE802154_ATTR_FRAME_RETRIES]) { + rc = phy_set_frame_retries(phy, info); + if (rc < 0) + goto error; + } + mutex_unlock(&phy->pib_lock); wpan_phy_put(phy); diff --git a/net/ieee802154/nl_policy.c b/net/ieee802154/nl_policy.c index 55b5616295ff..fd7be5e45cef 100644 --- a/net/ieee802154/nl_policy.c +++ b/net/ieee802154/nl_policy.c @@ -57,5 +57,10 @@ const struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = { [IEEE802154_ATTR_LBT_ENABLED] = { .type = NLA_U8, }, [IEEE802154_ATTR_CCA_MODE] = { .type = NLA_U8, }, [IEEE802154_ATTR_CCA_ED_LEVEL] = { .type = NLA_S32, }, + [IEEE802154_ATTR_CSMA_RETRIES] = { .type = NLA_U8, }, + [IEEE802154_ATTR_CSMA_MIN_BE] = { .type = NLA_U8, }, + [IEEE802154_ATTR_CSMA_MAX_BE] = { .type = NLA_U8, }, + + [IEEE802154_ATTR_FRAME_RETRIES] = { .type = NLA_S8, }, }; diff --git a/net/ieee802154/wpan-class.c b/net/ieee802154/wpan-class.c index 8d6f6704da84..edd0962d55f9 100644 --- a/net/ieee802154/wpan-class.c +++ b/net/ieee802154/wpan-class.c @@ -169,6 +169,12 @@ struct wpan_phy *wpan_phy_alloc(size_t priv_size) phy->current_channel = -1; /* not initialised */ phy->current_page = 0; /* for compatibility */ + /* defaults per 802.15.4-2011 */ + phy->min_be = 3; + phy->max_be = 5; + phy->csma_retries = 4; + phy->frame_retries = -1; /* for compatibility, actual default is 3 */ + return phy; out: diff --git a/net/mac802154/ieee802154_dev.c b/net/mac802154/ieee802154_dev.c index 4707f36546d3..b75bb01e5c6b 100644 --- a/net/mac802154/ieee802154_dev.c +++ b/net/mac802154/ieee802154_dev.c @@ -205,6 +205,27 @@ static int mac802154_set_cca_ed_level(struct wpan_phy *phy, s32 level) return priv->ops->set_cca_ed_level(&priv->hw, level); } +static int mac802154_set_csma_params(struct wpan_phy *phy, u8 min_be, + u8 max_be, u8 retries) +{ + struct mac802154_priv *priv = wpan_phy_priv(phy); + + if (!priv->ops->set_csma_params) + return -ENOTSUPP; + + return priv->ops->set_csma_params(&priv->hw, min_be, max_be, retries); +} + +static int mac802154_set_frame_retries(struct wpan_phy *phy, s8 retries) +{ + struct mac802154_priv *priv = wpan_phy_priv(phy); + + if (!priv->ops->set_frame_retries) + return -ENOTSUPP; + + return priv->ops->set_frame_retries(&priv->hw, retries); +} + struct ieee802154_dev * ieee802154_alloc_device(size_t priv_data_len, struct ieee802154_ops *ops) { @@ -286,6 +307,8 @@ int ieee802154_register_device(struct ieee802154_dev *dev) priv->phy->set_lbt = mac802154_set_lbt; priv->phy->set_cca_mode = mac802154_set_cca_mode; priv->phy->set_cca_ed_level = mac802154_set_cca_ed_level; + priv->phy->set_csma_params = mac802154_set_csma_params; + priv->phy->set_frame_retries = mac802154_set_frame_retries; rc = wpan_phy_register(priv->phy); if (rc < 0) -- cgit v1.2.3-71-gd317 From 99780a7b639e73d8f8f291fa1a981db883aec47f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 18 Feb 2014 10:40:07 +0200 Subject: Bluetooth: Add AES crypto context for each HCI device Previously the crypto context has only been available for LE SMP sessions, but now that we'll need to perform operations also during discovery it makes sense to have this context part of the hci_dev struct. Later, the context can be removed from the SMP context. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 92fa75fce29d..b344890b18f5 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -259,6 +259,7 @@ struct hci_dev { __u32 req_status; __u32 req_result; + struct crypto_blkcipher *tfm_aes; struct discovery_state discovery; struct hci_conn_hash conn_hash; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index b40d52446f8f..df25af5502ef 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -3205,9 +3206,18 @@ int hci_register_dev(struct hci_dev *hdev) dev_set_name(&hdev->dev, "%s", hdev->name); + hdev->tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, + CRYPTO_ALG_ASYNC); + if (IS_ERR(hdev->tfm_aes)) { + BT_ERR("Unable to create crypto context"); + error = PTR_ERR(hdev->tfm_aes); + hdev->tfm_aes = NULL; + goto err_wqueue; + } + error = device_add(&hdev->dev); if (error < 0) - goto err_wqueue; + goto err_tfm; hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev, RFKILL_TYPE_BLUETOOTH, &hci_rfkill_ops, @@ -3243,6 +3253,8 @@ int hci_register_dev(struct hci_dev *hdev) return id; +err_tfm: + crypto_free_blkcipher(hdev->tfm_aes); err_wqueue: destroy_workqueue(hdev->workqueue); destroy_workqueue(hdev->req_workqueue); @@ -3293,6 +3305,9 @@ void hci_unregister_dev(struct hci_dev *hdev) rfkill_destroy(hdev->rfkill); } + if (hdev->tfm_aes) + crypto_free_blkcipher(hdev->tfm_aes); + device_del(&hdev->dev); debugfs_remove_recursive(hdev->debugfs); -- cgit v1.2.3-71-gd317 From 970c4e46037ab8ece3940afc9fcf916d3ed7e003 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 18 Feb 2014 10:19:33 +0200 Subject: Bluetooth: Add basic IRK management support This patch adds the initial IRK storage and management functions to the HCI core. This includes storing a list of IRKs per HCI device and the ability to add, remove and lookup entries in that list. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 16 +++++++++ net/bluetooth/hci_core.c | 70 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b344890b18f5..eac422337582 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -103,6 +103,14 @@ struct smp_ltk { u8 val[16]; }; +struct smp_irk { + struct list_head list; + bdaddr_t rpa; + bdaddr_t bdaddr; + u8 addr_type; + u8 val[16]; +}; + struct link_key { struct list_head list; bdaddr_t bdaddr; @@ -269,6 +277,7 @@ struct hci_dev { struct list_head uuids; struct list_head link_keys; struct list_head long_term_keys; + struct list_head identity_resolving_keys; struct list_head remote_oob_data; struct list_head le_conn_params; @@ -787,6 +796,13 @@ int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_smp_ltks_clear(struct hci_dev *hdev); int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); +struct smp_irk *hci_find_irk_by_rpa(struct hci_dev *hdev, bdaddr_t *rpa); +struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 addr_type); +int hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, + u8 val[16], bdaddr_t *rpa); +void hci_smp_irks_clear(struct hci_dev *hdev); + int hci_remote_oob_data_clear(struct hci_dev *hdev); struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index df25af5502ef..59a76b2566eb 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -35,6 +35,8 @@ #include #include +#include "smp.h" + static void hci_rx_work(struct work_struct *work); static void hci_cmd_work(struct work_struct *work); static void hci_tx_work(struct work_struct *work); @@ -2544,6 +2546,16 @@ int hci_smp_ltks_clear(struct hci_dev *hdev) return 0; } +void hci_smp_irks_clear(struct hci_dev *hdev) +{ + struct smp_irk *k, *tmp; + + list_for_each_entry_safe(k, tmp, &hdev->identity_resolving_keys, list) { + list_del(&k->list); + kfree(k); + } +} + struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct link_key *k; @@ -2632,6 +2644,39 @@ struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, return NULL; } +struct smp_irk *hci_find_irk_by_rpa(struct hci_dev *hdev, bdaddr_t *rpa) +{ + struct smp_irk *irk; + + list_for_each_entry(irk, &hdev->identity_resolving_keys, list) { + if (!bacmp(&irk->rpa, rpa)) + return irk; + } + + list_for_each_entry(irk, &hdev->identity_resolving_keys, list) { + if (smp_irk_matches(hdev->tfm_aes, irk->val, rpa)) { + bacpy(&irk->rpa, rpa); + return irk; + } + } + + return NULL; +} + +struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 addr_type) +{ + struct smp_irk *irk; + + list_for_each_entry(irk, &hdev->identity_resolving_keys, list) { + if (addr_type == irk->addr_type && + bacmp(bdaddr, &irk->bdaddr) == 0) + return irk; + } + + return NULL; +} + int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len) { @@ -2726,6 +2771,29 @@ int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type, return 0; } +int hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, + u8 val[16], bdaddr_t *rpa) +{ + struct smp_irk *irk; + + irk = hci_find_irk_by_addr(hdev, bdaddr, addr_type); + if (!irk) { + irk = kzalloc(sizeof(*irk), GFP_KERNEL); + if (!irk) + return -ENOMEM; + + bacpy(&irk->bdaddr, bdaddr); + irk->addr_type = addr_type; + + list_add(&irk->list, &hdev->identity_resolving_keys); + } + + memcpy(irk->val, val, 16); + bacpy(&irk->rpa, rpa); + + return 0; +} + int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct link_key *key; @@ -3120,6 +3188,7 @@ struct hci_dev *hci_alloc_dev(void) INIT_LIST_HEAD(&hdev->uuids); INIT_LIST_HEAD(&hdev->link_keys); INIT_LIST_HEAD(&hdev->long_term_keys); + INIT_LIST_HEAD(&hdev->identity_resolving_keys); INIT_LIST_HEAD(&hdev->remote_oob_data); INIT_LIST_HEAD(&hdev->le_conn_params); INIT_LIST_HEAD(&hdev->conn_hash.list); @@ -3320,6 +3389,7 @@ void hci_unregister_dev(struct hci_dev *hdev) hci_uuids_clear(hdev); hci_link_keys_clear(hdev); hci_smp_ltks_clear(hdev); + hci_smp_irks_clear(hdev); hci_remote_oob_data_clear(hdev); hci_conn_params_clear(hdev); hci_dev_unlock(hdev); -- cgit v1.2.3-71-gd317 From 301cb2d85eb89140eaff8c22066a4bfd774960a3 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 18 Feb 2014 10:19:34 +0200 Subject: Bluetooth: Add hci_bdaddr_is_rpa convenience function When implementing support for Resolvable Private Addresses (RPAs) we'll need to in several places be able to identify such addresses. This patch adds a simple convenience function to do the identification of the address type. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index eac422337582..86ea4bab9e77 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1071,6 +1071,17 @@ static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type) return false; } +static inline bool hci_bdaddr_is_rpa(bdaddr_t *bdaddr, u8 addr_type) +{ + if (addr_type != 0x01) + return false; + + if ((bdaddr->b[5] & 0xc0) == 0x40) + return true; + + return false; +} + int hci_register_cb(struct hci_cb *hcb); int hci_unregister_cb(struct hci_cb *hcb); -- cgit v1.2.3-71-gd317 From 41edf1601af3b25461d91e73834dc89510bca8e5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 18 Feb 2014 10:19:35 +0200 Subject: Bluetooth: Implement mgmt_load_irks command This patch implements the Load IRKs command for the management interface. The command is used to load the kernel with the initial set of IRKs. It also sets a HCI_RPA_RESOLVING flag to indicate that we can start requesting devices to distribute their IRK to us. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 1 + include/net/bluetooth/mgmt.h | 12 +++++++ net/bluetooth/mgmt.c | 79 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 352d3d7d06bb..d3a8fff50f69 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -125,6 +125,7 @@ enum { HCI_SSP_ENABLED, HCI_SC_ENABLED, HCI_SC_ONLY, + HCI_RPA_RESOLVING, HCI_HS_ENABLED, HCI_LE_ENABLED, HCI_ADVERTISING, diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 4303fa90b7c1..e4fa13e559e2 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -389,6 +389,18 @@ struct mgmt_cp_set_scan_params { #define MGMT_OP_SET_DEBUG_KEYS 0x002E +struct mgmt_irk_info { + struct mgmt_addr_info addr; + __u8 val[16]; +} __packed; + +#define MGMT_OP_LOAD_IRKS 0x0030 +struct mgmt_cp_load_irks { + __le16 irk_count; + struct mgmt_irk_info irks[0]; +} __packed; +#define MGMT_LOAD_IRKS_SIZE 2 + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 70bef3d5db57..782e2bb10881 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -81,6 +81,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_SET_SCAN_PARAMS, MGMT_OP_SET_SECURE_CONN, MGMT_OP_SET_DEBUG_KEYS, + MGMT_OP_LOAD_IRKS, }; static const u16 mgmt_events[] = { @@ -4158,6 +4159,82 @@ unlock: return err; } +static bool irk_is_valid(struct mgmt_irk_info *irk) +{ + switch (irk->addr.type) { + case BDADDR_LE_PUBLIC: + return true; + + case BDADDR_LE_RANDOM: + /* Two most significant bits shall be set */ + if ((irk->addr.bdaddr.b[5] & 0xc0) != 0xc0) + return false; + return true; + } + + return false; +} + +static int load_irks(struct sock *sk, struct hci_dev *hdev, void *cp_data, + u16 len) +{ + struct mgmt_cp_load_irks *cp = cp_data; + u16 irk_count, expected_len; + int i, err; + + BT_DBG("request for %s", hdev->name); + + if (!lmp_le_capable(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_LOAD_IRKS, + MGMT_STATUS_NOT_SUPPORTED); + + irk_count = __le16_to_cpu(cp->irk_count); + + expected_len = sizeof(*cp) + irk_count * sizeof(struct mgmt_irk_info); + if (expected_len != len) { + BT_ERR("load_irks: expected %u bytes, got %u bytes", + len, expected_len); + return cmd_status(sk, hdev->id, MGMT_OP_LOAD_IRKS, + MGMT_STATUS_INVALID_PARAMS); + } + + BT_DBG("%s irk_count %u", hdev->name, irk_count); + + for (i = 0; i < irk_count; i++) { + struct mgmt_irk_info *key = &cp->irks[i]; + + if (!irk_is_valid(key)) + return cmd_status(sk, hdev->id, + MGMT_OP_LOAD_IRKS, + MGMT_STATUS_INVALID_PARAMS); + } + + hci_dev_lock(hdev); + + hci_smp_irks_clear(hdev); + + for (i = 0; i < irk_count; i++) { + struct mgmt_irk_info *irk = &cp->irks[i]; + u8 addr_type; + + if (irk->addr.type == BDADDR_LE_PUBLIC) + addr_type = ADDR_LE_DEV_PUBLIC; + else + addr_type = ADDR_LE_DEV_RANDOM; + + hci_add_irk(hdev, &irk->addr.bdaddr, addr_type, irk->val, + BDADDR_ANY); + } + + set_bit(HCI_RPA_RESOLVING, &hdev->dev_flags); + + err = cmd_complete(sk, hdev->id, MGMT_OP_LOAD_IRKS, 0, NULL, 0); + + hci_dev_unlock(hdev); + + return err; +} + static bool ltk_is_valid(struct mgmt_ltk_info *key) { if (key->master != 0x00 && key->master != 0x01) @@ -4296,6 +4373,8 @@ static const struct mgmt_handler { { set_scan_params, false, MGMT_SET_SCAN_PARAMS_SIZE }, { set_secure_conn, false, MGMT_SETTING_SIZE }, { set_debug_keys, false, MGMT_SETTING_SIZE }, + { }, + { load_irks, true, MGMT_LOAD_IRKS_SIZE }, }; -- cgit v1.2.3-71-gd317 From e0b2b27e622da0ba8a3d253b985d3d8f174b4313 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 18 Feb 2014 17:14:31 +0200 Subject: Bluetooth: Fix missing address type check for removing LTKs When removing Long Term Keys we should also be checking that the given address type (public vs random) matches. This patch updates the hci_remove_ltk function to take an extra parameter and uses it for address type matching. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_core.c | 4 ++-- net/bluetooth/mgmt.c | 14 +++++++++++--- 3 files changed, 14 insertions(+), 6 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 86ea4bab9e77..ab94abdeb3c1 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -792,7 +792,7 @@ int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type, __le16 ediv, u8 rand[8]); struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, bool master); -int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr); +int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type); int hci_smp_ltks_clear(struct hci_dev *hdev); int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 59a76b2566eb..957c8f4cc4c7 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2810,12 +2810,12 @@ int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) return 0; } -int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr) +int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type) { struct smp_ltk *k, *tmp; list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) { - if (bacmp(bdaddr, &k->bdaddr)) + if (bacmp(bdaddr, &k->bdaddr) || k->bdaddr_type != bdaddr_type) continue; BT_DBG("%s removing %pMR", hdev->name, bdaddr); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 782e2bb10881..473f8687b28b 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2318,10 +2318,18 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data, goto unlock; } - if (cp->addr.type == BDADDR_BREDR) + if (cp->addr.type == BDADDR_BREDR) { err = hci_remove_link_key(hdev, &cp->addr.bdaddr); - else - err = hci_remove_ltk(hdev, &cp->addr.bdaddr); + } else { + u8 addr_type; + + if (cp->addr.type == BDADDR_LE_PUBLIC) + addr_type = ADDR_LE_DEV_PUBLIC; + else + addr_type = ADDR_LE_DEV_RANDOM; + + err = hci_remove_ltk(hdev, &cp->addr.bdaddr, addr_type); + } if (err < 0) { err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, -- cgit v1.2.3-71-gd317 From 35f7498a87794ca531335f7c782e5b9495fec6d6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 18 Feb 2014 17:14:32 +0200 Subject: Bluetooth: Remove return values from functions that don't need them There are many functions that never fail but still declare an integer return value for no reason. This patch converts these functions to use a void return value to avoid any confusion of whether they can fail or not. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 10 +++++----- net/bluetooth/hci_core.c | 26 +++++++++----------------- net/bluetooth/mgmt.c | 2 +- 3 files changed, 15 insertions(+), 23 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ab94abdeb3c1..964a7888ad0c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -768,7 +768,7 @@ int hci_inquiry(void __user *arg); struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); -int hci_blacklist_clear(struct hci_dev *hdev); +void hci_blacklist_clear(struct hci_dev *hdev); int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); @@ -779,9 +779,9 @@ void hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); void hci_conn_params_clear(struct hci_dev *hdev); -int hci_uuids_clear(struct hci_dev *hdev); +void hci_uuids_clear(struct hci_dev *hdev); -int hci_link_keys_clear(struct hci_dev *hdev); +void hci_link_keys_clear(struct hci_dev *hdev); struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len); @@ -793,7 +793,7 @@ int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type, struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, bool master); int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type); -int hci_smp_ltks_clear(struct hci_dev *hdev); +void hci_smp_ltks_clear(struct hci_dev *hdev); int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); struct smp_irk *hci_find_irk_by_rpa(struct hci_dev *hdev, bdaddr_t *rpa); @@ -803,7 +803,7 @@ int hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 val[16], bdaddr_t *rpa); void hci_smp_irks_clear(struct hci_dev *hdev); -int hci_remote_oob_data_clear(struct hci_dev *hdev); +void hci_remote_oob_data_clear(struct hci_dev *hdev); struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 957c8f4cc4c7..fd5bb4086613 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2506,7 +2506,7 @@ static void hci_discov_off(struct work_struct *work) mgmt_discoverable_timeout(hdev); } -int hci_uuids_clear(struct hci_dev *hdev) +void hci_uuids_clear(struct hci_dev *hdev) { struct bt_uuid *uuid, *tmp; @@ -2514,11 +2514,9 @@ int hci_uuids_clear(struct hci_dev *hdev) list_del(&uuid->list); kfree(uuid); } - - return 0; } -int hci_link_keys_clear(struct hci_dev *hdev) +void hci_link_keys_clear(struct hci_dev *hdev) { struct list_head *p, *n; @@ -2530,11 +2528,9 @@ int hci_link_keys_clear(struct hci_dev *hdev) list_del(p); kfree(key); } - - return 0; } -int hci_smp_ltks_clear(struct hci_dev *hdev) +void hci_smp_ltks_clear(struct hci_dev *hdev) { struct smp_ltk *k, *tmp; @@ -2542,8 +2538,6 @@ int hci_smp_ltks_clear(struct hci_dev *hdev) list_del(&k->list); kfree(k); } - - return 0; } void hci_smp_irks_clear(struct hci_dev *hdev) @@ -2873,7 +2867,7 @@ int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr) return 0; } -int hci_remote_oob_data_clear(struct hci_dev *hdev) +void hci_remote_oob_data_clear(struct hci_dev *hdev) { struct oob_data *data, *n; @@ -2881,8 +2875,6 @@ int hci_remote_oob_data_clear(struct hci_dev *hdev) list_del(&data->list); kfree(data); } - - return 0; } int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, @@ -2951,7 +2943,7 @@ struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, return NULL; } -int hci_blacklist_clear(struct hci_dev *hdev) +void hci_blacklist_clear(struct hci_dev *hdev) { struct list_head *p, *n; @@ -2961,8 +2953,6 @@ int hci_blacklist_clear(struct hci_dev *hdev) list_del(p); kfree(b); } - - return 0; } int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) @@ -2991,8 +2981,10 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) { struct bdaddr_list *entry; - if (!bacmp(bdaddr, BDADDR_ANY)) - return hci_blacklist_clear(hdev); + if (!bacmp(bdaddr, BDADDR_ANY)) { + hci_blacklist_clear(hdev); + return 0; + } entry = hci_blacklist_lookup(hdev, bdaddr, type); if (!entry) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 473f8687b28b..fbb76a0de580 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2073,7 +2073,7 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data, } if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) { - err = hci_uuids_clear(hdev); + hci_uuids_clear(hdev); if (enable_service_cache(hdev)) { err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_UUID, -- cgit v1.2.3-71-gd317 From a7ec73386ce2a8ab351ee8ab6a1e5475f72617dc Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 18 Feb 2014 17:14:35 +0200 Subject: Bluetooth: Fix removing any IRKs when unpairing devices When mgmt_unpair_device is called we should also remove any associated IRKs. This patch adds a hci_remove_irk convenience function and ensures that it's called when mgmt_unpair_device is called. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 15 +++++++++++++++ net/bluetooth/mgmt.c | 2 ++ 3 files changed, 18 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 964a7888ad0c..ac468de11cb7 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -801,6 +801,7 @@ struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type); int hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 val[16], bdaddr_t *rpa); +void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type); void hci_smp_irks_clear(struct hci_dev *hdev); void hci_remote_oob_data_clear(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 69b7145bfce2..cdba4709f012 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2823,6 +2823,21 @@ int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type) return removed ? 0 : -ENOENT; } +void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type) +{ + struct smp_irk *k, *tmp; + + list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) { + if (bacmp(bdaddr, &k->bdaddr) || k->addr_type != addr_type) + continue; + + BT_DBG("%s removing %pMR", hdev->name, bdaddr); + + list_del(&k->list); + kfree(k); + } +} + /* HCI command timer function */ static void hci_cmd_timeout(unsigned long arg) { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index fbb76a0de580..90aac905a98b 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2328,6 +2328,8 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data, else addr_type = ADDR_LE_DEV_RANDOM; + hci_remove_irk(hdev, &cp->addr.bdaddr, addr_type); + err = hci_remove_ltk(hdev, &cp->addr.bdaddr, addr_type); } -- cgit v1.2.3-71-gd317 From 2426f3a5945ce1dfdb04aaf26748a987be49ff7c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 18 Feb 2014 17:14:36 +0200 Subject: Bluetooth: Add convenience function for fetching IRKs There are many situations where we need to check if an LE address is an RPA and if so try to look up the IRK for it. To simplify such cases this patch adds a convenience function for the job. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ac468de11cb7..4461c0051228 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1083,6 +1083,15 @@ static inline bool hci_bdaddr_is_rpa(bdaddr_t *bdaddr, u8 addr_type) return false; } +static inline struct smp_irk *hci_get_irk(struct hci_dev *hdev, + bdaddr_t *bdaddr, u8 addr_type) +{ + if (!hci_bdaddr_is_rpa(bdaddr, addr_type)) + return NULL; + + return hci_find_irk_by_rpa(hdev, bdaddr); +} + int hci_register_cb(struct hci_cb *hcb); int hci_unregister_cb(struct hci_cb *hcb); -- cgit v1.2.3-71-gd317 From 387a33e304caeeabf0c2439607fa6e726666bdf0 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 18 Feb 2014 21:41:33 +0200 Subject: Bluetooth: Fix updating Identity Address in L2CAP channels When we receive a remote identity address during SMP key distribution we should ensure that any associated L2CAP channel instances get their address information correspondingly updated (so that e.g. doing getpeername on associated sockets returns the correct address). This patch adds a new L2CAP core function l2cap_conn_update_id_addr() which is used to iterate through all L2CAP channels associated with a connection and update their address information. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap_core.c | 17 +++++++++++++++++ net/bluetooth/smp.c | 2 ++ 3 files changed, 20 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 13bec91785f4..4abdcb220e3a 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -881,6 +881,7 @@ 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); void l2cap_chan_del(struct l2cap_chan *chan, int err); +void l2cap_conn_update_id_addr(struct hci_conn *hcon); void l2cap_send_conn_req(struct l2cap_chan *chan); void l2cap_move_start(struct l2cap_chan *chan); void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan, diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 6e6b3a9c8e6d..c3bda6445f3d 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -609,6 +609,23 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) return; } +void l2cap_conn_update_id_addr(struct hci_conn *hcon) +{ + struct l2cap_conn *conn = hcon->l2cap_data; + struct l2cap_chan *chan; + + mutex_lock(&conn->chan_lock); + + list_for_each_entry(chan, &conn->chan_l, list) { + l2cap_chan_lock(chan); + bacpy(&chan->dst, &hcon->dst); + chan->dst_type = bdaddr_type(hcon, hcon->dst_type); + l2cap_chan_unlock(chan); + } + + mutex_unlock(&conn->chan_lock); +} + static void l2cap_chan_le_connect_reject(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index af29afed0cca..b6a2a8942b2d 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -991,6 +991,8 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn, bacpy(&hcon->dst, &smp->id_addr); hcon->dst_type = smp->id_addr_type; + l2cap_conn_update_id_addr(hcon); + smp_distribute_keys(conn, 1); return 0; -- cgit v1.2.3-71-gd317 From f4a407bef20c0e63fcd910a9404418522abff4ab Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 18 Feb 2014 21:41:34 +0200 Subject: Bluetooth: Wait for SMP key distribution completion when pairing When we initiate pairing through mgmt_pair_device the code has so far been waiting for a successful HCI Encrypt Change event in order to respond to the mgmt command. However, putting privacy into the play we actually want the key distribution to be complete before replying so that we can include the Identity Address in the mgmt response. This patch updates the various hci_conn callbacks for LE in mgmt.c to only respond in the case of failure, and adds a new mgmt_smp_complete function that the SMP code will call once key distribution has been completed. Since the smp_chan_destroy function that's used to indicate completion and clean up the SMP context can be called from various places, including outside of smp.c, the easiest way to track failure vs success is a new flag that we set once key distribution has been successfully completed. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/mgmt.c | 25 +++++++++++++++++++------ net/bluetooth/smp.c | 5 +++++ net/bluetooth/smp.h | 1 + 4 files changed, 26 insertions(+), 6 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 4461c0051228..64c4e3f0a515 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1212,6 +1212,7 @@ int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent); void mgmt_reenable_advertising(struct hci_dev *hdev); +void mgmt_smp_complete(struct hci_conn *conn, bool complete); /* HCI info for socket */ #define hci_pi(sk) ((struct hci_pinfo *) sk) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 90aac905a98b..24a85fe76cd8 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2655,6 +2655,16 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status) mgmt_pending_remove(cmd); } +void mgmt_smp_complete(struct hci_conn *conn, bool complete) +{ + u8 status = complete ? MGMT_STATUS_SUCCESS : MGMT_STATUS_FAILED; + struct pending_cmd *cmd; + + cmd = find_pairing(conn); + if (cmd) + pairing_complete(cmd, status); +} + static void pairing_complete_cb(struct hci_conn *conn, u8 status) { struct pending_cmd *cmd; @@ -2668,7 +2678,7 @@ static void pairing_complete_cb(struct hci_conn *conn, u8 status) pairing_complete(cmd, mgmt_status(status)); } -static void le_connect_complete_cb(struct hci_conn *conn, u8 status) +static void le_pairing_complete_cb(struct hci_conn *conn, u8 status) { struct pending_cmd *cmd; @@ -2755,13 +2765,16 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, } /* For LE, just connecting isn't a proof that the pairing finished */ - if (cp->addr.type == BDADDR_BREDR) + if (cp->addr.type == BDADDR_BREDR) { conn->connect_cfm_cb = pairing_complete_cb; - else - conn->connect_cfm_cb = le_connect_complete_cb; + conn->security_cfm_cb = pairing_complete_cb; + conn->disconn_cfm_cb = pairing_complete_cb; + } else { + conn->connect_cfm_cb = le_pairing_complete_cb; + conn->security_cfm_cb = le_pairing_complete_cb; + conn->disconn_cfm_cb = le_pairing_complete_cb; + } - conn->security_cfm_cb = pairing_complete_cb; - conn->disconn_cfm_cb = pairing_complete_cb; conn->io_capability = cp->io_cap; cmd->user_data = conn; diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index b6a2a8942b2d..27eebca260fa 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -565,9 +565,13 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn) void smp_chan_destroy(struct l2cap_conn *conn) { struct smp_chan *smp = conn->smp_chan; + bool complete; BUG_ON(!smp); + complete = test_bit(SMP_FLAG_COMPLETE, &smp->smp_flags); + mgmt_smp_complete(conn->hcon, complete); + kfree(smp); conn->smp_chan = NULL; conn->hcon->smp_conn = NULL; @@ -1187,6 +1191,7 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) if (conn->hcon->out || force || !(rsp->init_key_dist & 0x07)) { clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags); cancel_delayed_work_sync(&conn->security_timer); + set_bit(SMP_FLAG_COMPLETE, &smp->smp_flags); smp_chan_destroy(conn); } diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h index 8f54c9b152de..675fd3b21d2c 100644 --- a/net/bluetooth/smp.h +++ b/net/bluetooth/smp.h @@ -118,6 +118,7 @@ struct smp_cmd_security_req { #define SMP_FLAG_TK_VALID 1 #define SMP_FLAG_CFM_PENDING 2 #define SMP_FLAG_MITM_AUTH 3 +#define SMP_FLAG_COMPLETE 4 struct smp_chan { struct l2cap_conn *conn; -- cgit v1.2.3-71-gd317 From f7b12606b5de323a2bb5ca1696558efde8f25441 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 18 Feb 2014 20:53:18 +0100 Subject: rtnl: make ifla_policy static The only place this is used outside rtnetlink.c is veth. So provide wrapper function for this usage. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/veth.c | 8 ++-- include/net/rtnetlink.h | 2 +- net/core/rtnetlink.c | 107 +++++++++++++++++++++++++----------------------- 3 files changed, 61 insertions(+), 56 deletions(-) (limited to 'include/net') diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 91c33c1d3c9c..34b52638e12d 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -323,10 +324,9 @@ static int veth_newlink(struct net *src_net, struct net_device *dev, nla_peer = data[VETH_INFO_PEER]; ifmp = nla_data(nla_peer); - err = nla_parse(peer_tb, IFLA_MAX, - nla_data(nla_peer) + sizeof(struct ifinfomsg), - nla_len(nla_peer) - sizeof(struct ifinfomsg), - ifla_policy); + err = rtnl_nla_parse_ifla(peer_tb, + nla_data(nla_peer) + sizeof(struct ifinfomsg), + nla_len(nla_peer) - sizeof(struct ifinfomsg)); if (err < 0) return err; diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index 661e45d38051..72240e5ac2c4 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h @@ -140,7 +140,7 @@ struct net_device *rtnl_create_link(struct net *net, char *ifname, struct nlattr *tb[]); int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm); -extern const struct nla_policy ifla_policy[IFLA_MAX+1]; +int rtnl_nla_parse_ifla(struct nlattr **tb, const struct nlattr *head, int len); #define MODULE_ALIAS_RTNL_LINK(kind) MODULE_ALIAS("rtnl-link-" kind) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 048dc8d183aa..7b2ad564b303 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1121,56 +1121,7 @@ nla_put_failure: return -EMSGSIZE; } -static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) -{ - struct net *net = sock_net(skb->sk); - int h, s_h; - int idx = 0, s_idx; - struct net_device *dev; - struct hlist_head *head; - struct nlattr *tb[IFLA_MAX+1]; - u32 ext_filter_mask = 0; - - s_h = cb->args[0]; - s_idx = cb->args[1]; - - rcu_read_lock(); - cb->seq = net->dev_base_seq; - - if (nlmsg_parse(cb->nlh, sizeof(struct ifinfomsg), tb, IFLA_MAX, - ifla_policy) >= 0) { - - if (tb[IFLA_EXT_MASK]) - ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]); - } - - for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { - idx = 0; - head = &net->dev_index_head[h]; - hlist_for_each_entry_rcu(dev, head, index_hlist) { - if (idx < s_idx) - goto cont; - if (rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK, - NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, 0, - NLM_F_MULTI, - ext_filter_mask) <= 0) - goto out; - - nl_dump_check_consistent(cb, nlmsg_hdr(skb)); -cont: - idx++; - } - } -out: - rcu_read_unlock(); - cb->args[1] = idx; - cb->args[0] = h; - - return skb->len; -} - -const struct nla_policy ifla_policy[IFLA_MAX+1] = { +static const struct nla_policy ifla_policy[IFLA_MAX+1] = { [IFLA_IFNAME] = { .type = NLA_STRING, .len = IFNAMSIZ-1 }, [IFLA_ADDRESS] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN }, [IFLA_BROADCAST] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN }, @@ -1197,7 +1148,6 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = { [IFLA_NUM_RX_QUEUES] = { .type = NLA_U32 }, [IFLA_PHYS_PORT_ID] = { .type = NLA_BINARY, .len = MAX_PHYS_PORT_ID_LEN }, }; -EXPORT_SYMBOL(ifla_policy); static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = { [IFLA_INFO_KIND] = { .type = NLA_STRING }, @@ -1235,6 +1185,61 @@ static const struct nla_policy ifla_port_policy[IFLA_PORT_MAX+1] = { [IFLA_PORT_RESPONSE] = { .type = NLA_U16, }, }; +static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct net *net = sock_net(skb->sk); + int h, s_h; + int idx = 0, s_idx; + struct net_device *dev; + struct hlist_head *head; + struct nlattr *tb[IFLA_MAX+1]; + u32 ext_filter_mask = 0; + + s_h = cb->args[0]; + s_idx = cb->args[1]; + + rcu_read_lock(); + cb->seq = net->dev_base_seq; + + if (nlmsg_parse(cb->nlh, sizeof(struct ifinfomsg), tb, IFLA_MAX, + ifla_policy) >= 0) { + + if (tb[IFLA_EXT_MASK]) + ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]); + } + + for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { + idx = 0; + head = &net->dev_index_head[h]; + hlist_for_each_entry_rcu(dev, head, index_hlist) { + if (idx < s_idx) + goto cont; + if (rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK, + NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, 0, + NLM_F_MULTI, + ext_filter_mask) <= 0) + goto out; + + nl_dump_check_consistent(cb, nlmsg_hdr(skb)); +cont: + idx++; + } + } +out: + rcu_read_unlock(); + cb->args[1] = idx; + cb->args[0] = h; + + return skb->len; +} + +int rtnl_nla_parse_ifla(struct nlattr **tb, const struct nlattr *head, int len) +{ + return nla_parse(tb, IFLA_MAX, head, len, ifla_policy); +} +EXPORT_SYMBOL(rtnl_nla_parse_ifla); + struct net *rtnl_link_get_net(struct net *src_net, struct nlattr *tb[]) { struct net *net; -- cgit v1.2.3-71-gd317 From 1a1ccc96abb2ed9b8fbb71018e64b97324caef53 Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Wed, 19 Feb 2014 10:07:34 +0100 Subject: xfrm: Remove caching of xfrm_policy_sk_bundles We currently cache socket policy bundles at xfrm_policy_sk_bundles. These cached bundles are never used. Instead we create and cache a new one whenever xfrm_lookup() is called on a socket policy. Most protocols cache the used routes to the socket, so let's remove the unused caching of socket policy bundles in xfrm. Signed-off-by: Steffen Klassert --- include/net/netns/xfrm.h | 1 - net/xfrm/xfrm_policy.c | 28 ---------------------------- 2 files changed, 29 deletions(-) (limited to 'include/net') diff --git a/include/net/netns/xfrm.h b/include/net/netns/xfrm.h index 52d0086d55d3..51f0dce7b643 100644 --- a/include/net/netns/xfrm.h +++ b/include/net/netns/xfrm.h @@ -59,7 +59,6 @@ struct netns_xfrm { 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; diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 2232c6f26aff..bb3669d973a7 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -39,8 +39,6 @@ #define XFRM_QUEUE_TMO_MAX ((unsigned)(60*HZ)) #define XFRM_MAX_QUEUE_LEN 100 -static struct dst_entry *xfrm_policy_sk_bundles; - static DEFINE_SPINLOCK(xfrm_policy_afinfo_lock); static struct xfrm_policy_afinfo __rcu *xfrm_policy_afinfo[NPROTO] __read_mostly; @@ -2109,13 +2107,6 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig, goto no_transform; } - dst_hold(&xdst->u.dst); - - 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(&net->xfrm.xfrm_policy_sk_bundle_lock); - route = xdst->route; } } @@ -2549,33 +2540,15 @@ static struct dst_entry *xfrm_negative_advice(struct dst_entry *dst) return dst; } -static void __xfrm_garbage_collect(struct net *net) -{ - struct dst_entry *head, *next; - - spin_lock_bh(&net->xfrm.xfrm_policy_sk_bundle_lock); - head = xfrm_policy_sk_bundles; - xfrm_policy_sk_bundles = NULL; - spin_unlock_bh(&net->xfrm.xfrm_policy_sk_bundle_lock); - - while (head) { - next = head->next; - dst_free(head); - head = next; - } -} - void xfrm_garbage_collect(struct net *net) { flow_cache_flush(net); - __xfrm_garbage_collect(net); } EXPORT_SYMBOL(xfrm_garbage_collect); static void xfrm_garbage_collect_deferred(struct net *net) { flow_cache_flush_deferred(net); - __xfrm_garbage_collect(net); } static void xfrm_init_pmtu(struct dst_entry *dst) @@ -2944,7 +2917,6 @@ static int __net_init xfrm_net_init(struct net *net) /* 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); flow_cache_init(net); -- cgit v1.2.3-71-gd317 From d2bf2f34cc1a8304a5dab0d42e7a2ae58ede94cd Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 18 Feb 2014 15:25:32 +0100 Subject: netfilter: nft_ct: labels get support This also adds NF_CT_LABELS_MAX_SIZE so it can be re-used as BUILD_BUG_ON in nft_ct. At this time, nft doesn't yet support writing to the label area; when this changes the label->words handling needs to be moved out of xt_connlabel.c into nf_conntrack_labels.c. Also removes a useless run-time check: words cannot grow beyond 4 (32 bit) or 2 (64bit) since xt_connlabel enforces a maximum of 128 labels. Signed-off-by: Florian Westphal Acked-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_labels.h | 4 +++- include/uapi/linux/netfilter/nf_tables.h | 1 + net/netfilter/nf_conntrack_netlink.c | 5 ++--- net/netfilter/nft_ct.c | 24 ++++++++++++++++++++++++ 4 files changed, 30 insertions(+), 4 deletions(-) (limited to 'include/net') diff --git a/include/net/netfilter/nf_conntrack_labels.h b/include/net/netfilter/nf_conntrack_labels.h index c985695283b3..dec6336bf850 100644 --- a/include/net/netfilter/nf_conntrack_labels.h +++ b/include/net/netfilter/nf_conntrack_labels.h @@ -7,6 +7,8 @@ #include +#define NF_CT_LABELS_MAX_SIZE ((XT_CONNLABEL_MAXBIT + 1) / BITS_PER_BYTE) + struct nf_conn_labels { u8 words; unsigned long bits[]; @@ -29,7 +31,7 @@ static inline struct nf_conn_labels *nf_ct_labels_ext_add(struct nf_conn *ct) u8 words; words = ACCESS_ONCE(net->ct.label_words); - if (words == 0 || WARN_ON_ONCE(words > 8)) + if (words == 0) return NULL; cl_ext = nf_ct_ext_add_length(ct, NF_CT_EXT_LABELS, diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 83c985a6170b..c84c452c62a7 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -601,6 +601,7 @@ enum nft_ct_keys { NFT_CT_PROTOCOL, NFT_CT_PROTO_SRC, NFT_CT_PROTO_DST, + NFT_CT_LABELS, }; /** diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index bb322d0beb48..47e9369997ef 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -966,7 +966,6 @@ ctnetlink_parse_help(const struct nlattr *attr, char **helper_name, return 0; } -#define __CTA_LABELS_MAX_LENGTH ((XT_CONNLABEL_MAXBIT + 1) / BITS_PER_BYTE) static const struct nla_policy ct_nla_policy[CTA_MAX+1] = { [CTA_TUPLE_ORIG] = { .type = NLA_NESTED }, [CTA_TUPLE_REPLY] = { .type = NLA_NESTED }, @@ -984,9 +983,9 @@ static const struct nla_policy ct_nla_policy[CTA_MAX+1] = { [CTA_ZONE] = { .type = NLA_U16 }, [CTA_MARK_MASK] = { .type = NLA_U32 }, [CTA_LABELS] = { .type = NLA_BINARY, - .len = __CTA_LABELS_MAX_LENGTH }, + .len = NF_CT_LABELS_MAX_SIZE }, [CTA_LABELS_MASK] = { .type = NLA_BINARY, - .len = __CTA_LABELS_MAX_LENGTH }, + .len = NF_CT_LABELS_MAX_SIZE }, }; static int diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index 46e275403838..e59b08f9ccbd 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -19,6 +19,7 @@ #include #include #include +#include struct nft_ct { enum nft_ct_keys key:8; @@ -97,6 +98,26 @@ static void nft_ct_get_eval(const struct nft_expr *expr, goto err; strncpy((char *)dest->data, helper->name, sizeof(dest->data)); return; +#ifdef CONFIG_NF_CONNTRACK_LABELS + case NFT_CT_LABELS: { + struct nf_conn_labels *labels = nf_ct_labels_find(ct); + unsigned int size; + + if (!labels) { + memset(dest->data, 0, sizeof(dest->data)); + return; + } + + BUILD_BUG_ON(NF_CT_LABELS_MAX_SIZE > sizeof(dest->data)); + size = labels->words * sizeof(long); + + memcpy(dest->data, labels->bits, size); + if (size < sizeof(dest->data)) + memset(((char *) dest->data) + size, 0, + sizeof(dest->data) - size); + return; + } +#endif } tuple = &ct->tuplehash[priv->dir].tuple; @@ -220,6 +241,9 @@ static int nft_ct_init_validate_get(const struct nft_expr *expr, #endif #ifdef CONFIG_NF_CONNTRACK_SECMARK case NFT_CT_SECMARK: +#endif +#ifdef CONFIG_NF_CONNTRACK_LABELS + case NFT_CT_LABELS: #endif case NFT_CT_EXPIRATION: case NFT_CT_HELPER: -- cgit v1.2.3-71-gd317 From ca9142b8825c159897cc4a246ea27585c82add4b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 19 Feb 2014 14:57:44 +0200 Subject: Bluetooth: Return added key when adding LTKs and IRKs The SMP code will need to postpone the mgmt event emission for the IRK and LTKs. To avoid extra lookups at the end of the key distribution simply return the added value from the add_ltk and add_irk functions. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 11 ++++++----- net/bluetooth/hci_core.c | 21 +++++++++++---------- 2 files changed, 17 insertions(+), 15 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 64c4e3f0a515..5366dc9e25eb 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -787,9 +787,10 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len); struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8], bool master); -int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type, - int new_key, u8 authenticated, u8 tk[16], u8 enc_size, - __le16 ediv, u8 rand[8]); +struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 addr_type, u8 type, int new_key, + u8 authenticated, u8 tk[16], u8 enc_size, + __le16 ediv, u8 rand[8]); struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, bool master); int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type); @@ -799,8 +800,8 @@ int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); struct smp_irk *hci_find_irk_by_rpa(struct hci_dev *hdev, bdaddr_t *rpa); struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type); -int hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, - u8 val[16], bdaddr_t *rpa); +struct smp_irk *hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 addr_type, u8 val[16], bdaddr_t *rpa); void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type); void hci_smp_irks_clear(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 7e679e085506..e23c718d668b 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2761,9 +2761,10 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, return 0; } -int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type, - int new_key, u8 authenticated, u8 tk[16], u8 enc_size, __le16 - ediv, u8 rand[8]) +struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 addr_type, u8 type, int new_key, + u8 authenticated, u8 tk[16], u8 enc_size, + __le16 ediv, u8 rand[8]) { struct smp_ltk *key, *old_key; bool master = ltk_type_master(type); @@ -2775,7 +2776,7 @@ int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type, else { key = kzalloc(sizeof(*key), GFP_KERNEL); if (!key) - return -ENOMEM; + return NULL; list_add(&key->list, &hdev->long_term_keys); } @@ -2789,7 +2790,7 @@ int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type, memcpy(key->rand, rand, sizeof(key->rand)); if (!new_key) - return 0; + return key; if (addr_type == ADDR_LE_DEV_RANDOM && (bdaddr->b[5] & 0xc0) != 0xc0) persistent = 0; @@ -2799,11 +2800,11 @@ int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type, if (type == HCI_SMP_LTK || type == HCI_SMP_LTK_SLAVE) mgmt_new_ltk(hdev, key, persistent); - return 0; + return key; } -int hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, - u8 val[16], bdaddr_t *rpa) +struct smp_irk *hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 addr_type, u8 val[16], bdaddr_t *rpa) { struct smp_irk *irk; @@ -2811,7 +2812,7 @@ int hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, if (!irk) { irk = kzalloc(sizeof(*irk), GFP_KERNEL); if (!irk) - return -ENOMEM; + return NULL; bacpy(&irk->bdaddr, bdaddr); irk->addr_type = addr_type; @@ -2822,7 +2823,7 @@ int hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, memcpy(irk->val, val, 16); bacpy(&irk->rpa, rpa); - return 0; + return irk; } int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) -- cgit v1.2.3-71-gd317 From ba74b666b5e581ef3d4912af73774fab48c03198 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 19 Feb 2014 14:57:45 +0200 Subject: Bluetooth: Move New LTK store hint evaluation into mgmt_new_ltk It's simpler (one less if-statement) to just evaluate the appropriate value for store_hint in the mgmt_new_ltk function than to pass a boolean parameter to the function. Furthermore, this simplifies moving the mgmt event emission out from hci_add_ltk in subsequent patches. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_core.c | 8 +------- net/bluetooth/mgmt.c | 9 +++++++-- 3 files changed, 9 insertions(+), 10 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 5366dc9e25eb..8ca95e5e3765 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1211,7 +1211,7 @@ void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, void mgmt_discovering(struct hci_dev *hdev, u8 discovering); int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); -void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent); +void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key); void mgmt_reenable_advertising(struct hci_dev *hdev); void mgmt_smp_complete(struct hci_conn *conn, bool complete); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index e23c718d668b..60c875267c19 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2768,7 +2768,6 @@ struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, { struct smp_ltk *key, *old_key; bool master = ltk_type_master(type); - u8 persistent; old_key = hci_find_ltk_by_addr(hdev, bdaddr, addr_type, master); if (old_key) @@ -2792,13 +2791,8 @@ struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, if (!new_key) return key; - if (addr_type == ADDR_LE_DEV_RANDOM && (bdaddr->b[5] & 0xc0) != 0xc0) - persistent = 0; - else - persistent = 1; - if (type == HCI_SMP_LTK || type == HCI_SMP_LTK_SLAVE) - mgmt_new_ltk(hdev, key, persistent); + mgmt_new_ltk(hdev, key); return key; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 747cb9bbc331..ad51da1b6dc2 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -4765,13 +4765,18 @@ void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL); } -void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent) +void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key) { struct mgmt_ev_new_long_term_key ev; memset(&ev, 0, sizeof(ev)); - ev.store_hint = persistent; + if (key->bdaddr_type == ADDR_LE_DEV_RANDOM && + (key->bdaddr.b[5] & 0xc0) != 0xc0) + ev.store_hint = 0x00; + else + ev.store_hint = 0x01; + bacpy(&ev.key.addr.bdaddr, &key->bdaddr); ev.key.addr.type = link_to_bdaddr(LE_LINK, key->bdaddr_type); ev.key.type = key->authenticated; -- cgit v1.2.3-71-gd317 From 35d702719d6464a9de2bf98d536c6e054f0a8f7e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 19 Feb 2014 14:57:47 +0200 Subject: Bluetooth: Move SMP LTK notification after key distribution This patch moves the SMP Long Term Key notification over mgmt from the hci_add_ltk function to smp.c when both sides have completed their key distribution. This way we are also able to update the identity address into the mgmt_new_ltk event. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 5 ++--- net/bluetooth/hci_core.c | 11 ++--------- net/bluetooth/mgmt.c | 6 +++--- net/bluetooth/smp.c | 29 ++++++++++++++++++++++++----- 4 files changed, 31 insertions(+), 20 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 8ca95e5e3765..59ae04c2684f 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -788,9 +788,8 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8], bool master); struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 addr_type, u8 type, int new_key, - u8 authenticated, u8 tk[16], u8 enc_size, - __le16 ediv, u8 rand[8]); + u8 addr_type, u8 type, u8 authenticated, + u8 tk[16], u8 enc_size, __le16 ediv, u8 rand[8]); struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, bool master); int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 60c875267c19..3711c7626cb2 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2762,9 +2762,8 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, } struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 addr_type, u8 type, int new_key, - u8 authenticated, u8 tk[16], u8 enc_size, - __le16 ediv, u8 rand[8]) + u8 addr_type, u8 type, u8 authenticated, + u8 tk[16], u8 enc_size, __le16 ediv, u8 rand[8]) { struct smp_ltk *key, *old_key; bool master = ltk_type_master(type); @@ -2788,12 +2787,6 @@ struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, key->type = type; memcpy(key->rand, rand, sizeof(key->rand)); - if (!new_key) - return key; - - if (type == HCI_SMP_LTK || type == HCI_SMP_LTK_SLAVE) - mgmt_new_ltk(hdev, key); - return key; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index ad51da1b6dc2..bcfc6da67a5c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -4330,9 +4330,9 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, else type = HCI_SMP_LTK_SLAVE; - hci_add_ltk(hdev, &key->addr.bdaddr, addr_type, - type, 0, key->type, key->val, - key->enc_size, key->ediv, key->rand); + hci_add_ltk(hdev, &key->addr.bdaddr, addr_type, type, + key->type, key->val, key->enc_size, key->ediv, + key->rand); } err = cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, 0, diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index eaac54be91b1..f05c1b71d99a 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -532,7 +532,7 @@ static void random_work(struct work_struct *work) SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size); hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, - HCI_SMP_STK_SLAVE, 0, 0, stk, smp->enc_key_size, + HCI_SMP_STK_SLAVE, 0, stk, smp->enc_key_size, ediv, rand); } @@ -931,7 +931,7 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb) hci_dev_lock(hdev); authenticated = (hcon->sec_level == BT_SECURITY_HIGH); - ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, HCI_SMP_LTK, 1, + ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, HCI_SMP_LTK, authenticated, smp->tk, smp->enc_key_size, rp->ediv, rp->rand); smp->ltk = ltk; @@ -1106,6 +1106,25 @@ done: return err; } +static void smp_notify_keys(struct l2cap_conn *conn) +{ + struct smp_chan *smp = conn->smp_chan; + struct hci_conn *hcon = conn->hcon; + struct hci_dev *hdev = hcon->hdev; + + if (smp->ltk) { + smp->ltk->bdaddr_type = hcon->dst_type; + bacpy(&smp->ltk->bdaddr, &hcon->dst); + mgmt_new_ltk(hdev, smp->ltk); + } + + if (smp->slave_ltk) { + smp->slave_ltk->bdaddr_type = hcon->dst_type; + bacpy(&smp->slave_ltk->bdaddr, &hcon->dst); + mgmt_new_ltk(hdev, smp->slave_ltk); + } +} + int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) { struct smp_cmd_pairing *req, *rsp; @@ -1151,9 +1170,8 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) authenticated = hcon->sec_level == BT_SECURITY_HIGH; ltk = hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, - HCI_SMP_LTK_SLAVE, 1, authenticated, - enc.ltk, smp->enc_key_size, ediv, - ident.rand); + HCI_SMP_LTK_SLAVE, authenticated, enc.ltk, + smp->enc_key_size, ediv, ident.rand); smp->slave_ltk = ltk; ident.ediv = ediv; @@ -1197,6 +1215,7 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags); cancel_delayed_work_sync(&conn->security_timer); set_bit(SMP_FLAG_COMPLETE, &smp->smp_flags); + smp_notify_keys(conn); smp_chan_destroy(conn); } -- cgit v1.2.3-71-gd317 From 95fbac8a8e459262c580ee4172e4713cdc60929b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 19 Feb 2014 15:18:31 +0200 Subject: Bluetooth: Add support for sending New IRK event This patch adds the necessary helper function to send the New IRK mgmt event and makes sure that the function is called at when SMP key distribution has completed. The event is sent before the New LTK event so user space knows which remote device to associate with the keys. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + include/net/bluetooth/mgmt.h | 7 +++++++ net/bluetooth/mgmt.c | 15 +++++++++++++++ net/bluetooth/smp.c | 3 +++ 4 files changed, 26 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 59ae04c2684f..3be2905010cd 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1211,6 +1211,7 @@ void mgmt_discovering(struct hci_dev *hdev, u8 discovering); int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key); +void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk); void mgmt_reenable_advertising(struct hci_dev *hdev); void mgmt_smp_complete(struct hci_conn *conn, bool complete); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index e4fa13e559e2..2e46251e8aec 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -536,3 +536,10 @@ struct mgmt_ev_passkey_notify { __le32 passkey; __u8 entered; } __packed; + +#define MGMT_EV_NEW_IRK 0x0018 +struct mgmt_ev_new_irk { + __u8 store_hint; + bdaddr_t rpa; + struct mgmt_irk_info irk; +} __packed; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index bcfc6da67a5c..1daa837da091 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -4792,6 +4792,21 @@ void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key) mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, &ev, sizeof(ev), NULL); } +void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk) +{ + struct mgmt_ev_new_irk ev; + + memset(&ev, 0, sizeof(ev)); + + ev.store_hint = 0x01; + bacpy(&ev.rpa, &irk->rpa); + bacpy(&ev.irk.addr.bdaddr, &irk->bdaddr); + ev.irk.addr.type = link_to_bdaddr(LE_LINK, irk->addr_type); + memcpy(ev.irk.val, irk->val, sizeof(irk->val)); + + mgmt_event(MGMT_EV_NEW_IRK, hdev, &ev, sizeof(ev), NULL); +} + static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data, u8 data_len) { diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index f05c1b71d99a..f06068072bdd 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -1112,6 +1112,9 @@ static void smp_notify_keys(struct l2cap_conn *conn) struct hci_conn *hcon = conn->hcon; struct hci_dev *hdev = hcon->hdev; + if (smp->remote_irk) + mgmt_new_irk(hdev, smp->remote_irk); + if (smp->ltk) { smp->ltk->bdaddr_type = hcon->dst_type; bacpy(&smp->ltk->bdaddr, &hcon->dst); -- cgit v1.2.3-71-gd317 From c8e6ad0829a723a74cd2fea9996a3392d2579a18 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Tue, 18 Feb 2014 21:38:08 +0100 Subject: ipv6: honor IPV6_PKTINFO with v4 mapped addresses on sendmsg In case we decide in udp6_sendmsg to send the packet down the ipv4 udp_sendmsg path because the destination is either of family AF_INET or the destination is an ipv4 mapped ipv6 address, we don't honor the maybe specified ipv4 mapped ipv6 address in IPV6_PKTINFO. We simply can check for this option in ip_cmsg_send because no calls to ipv6 module functions are needed to do so. Reported-by: Gert Doering Cc: Tore Anderson Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/net/ip.h | 3 ++- net/ipv4/ip_sockglue.c | 19 ++++++++++++++++++- net/ipv4/ping.c | 2 +- net/ipv4/raw.c | 2 +- net/ipv4/udp.c | 3 ++- 5 files changed, 24 insertions(+), 5 deletions(-) (limited to 'include/net') diff --git a/include/net/ip.h b/include/net/ip.h index 23be0fd37937..4aa781b7f609 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -489,7 +489,8 @@ int ip_options_rcv_srr(struct sk_buff *skb); void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb); void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb); -int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc); +int ip_cmsg_send(struct net *net, struct msghdr *msg, + struct ipcm_cookie *ipc, bool allow_ipv6); int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval, unsigned int optlen); int ip_getsockopt(struct sock *sk, int level, int optname, char __user *optval, diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 580dd96666e0..0968b28c4cf3 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -186,7 +186,8 @@ void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb) } EXPORT_SYMBOL(ip_cmsg_recv); -int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc) +int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc, + bool allow_ipv6) { int err, val; struct cmsghdr *cmsg; @@ -194,6 +195,22 @@ int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc) for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { if (!CMSG_OK(msg, cmsg)) return -EINVAL; +#if defined(CONFIG_IPV6) + if (allow_ipv6 && + cmsg->cmsg_level == SOL_IPV6 && + cmsg->cmsg_type == IPV6_PKTINFO) { + struct in6_pktinfo *src_info; + + if (cmsg->cmsg_len < CMSG_LEN(sizeof(*src_info))) + return -EINVAL; + src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg); + if (!ipv6_addr_v4mapped(&src_info->ipi6_addr)) + return -EINVAL; + ipc->oif = src_info->ipi6_ifindex; + ipc->addr = src_info->ipi6_addr.s6_addr32[3]; + continue; + } +#endif if (cmsg->cmsg_level != SOL_IP) continue; switch (cmsg->cmsg_type) { diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 2d11c094296e..f4b19e5dde54 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -727,7 +727,7 @@ static int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m sock_tx_timestamp(sk, &ipc.tx_flags); if (msg->msg_controllen) { - err = ip_cmsg_send(sock_net(sk), msg, &ipc); + err = ip_cmsg_send(sock_net(sk), msg, &ipc, false); if (err) return err; if (ipc.opt) diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index c04518f4850a..a9dbe58bdfe7 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -524,7 +524,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, ipc.oif = sk->sk_bound_dev_if; if (msg->msg_controllen) { - err = ip_cmsg_send(sock_net(sk), msg, &ipc); + err = ip_cmsg_send(sock_net(sk), msg, &ipc, false); if (err) goto out; if (ipc.opt) diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 77bd16fa9f34..4468e1adc094 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -931,7 +931,8 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, sock_tx_timestamp(sk, &ipc.tx_flags); if (msg->msg_controllen) { - err = ip_cmsg_send(sock_net(sk), msg, &ipc); + err = ip_cmsg_send(sock_net(sk), msg, &ipc, + sk->sk_family == AF_INET6); if (err) return err; if (ipc.opt) -- cgit v1.2.3-71-gd317 From b32bba6ced5696593a6bae5fdc69dc79c0a97ef5 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 19 Feb 2014 19:31:26 -0800 Subject: Bluetooth: Replace own_address_type with force_static_address debugfs The own_address_type debugfs option does not providing enough flexibity for interacting with the upcoming LE privacy support. What really is needed is an option to force using the static address compared to the public address. The new force_static_address debugfs option does exactly that. In addition it is also only available when the controller does actually have a public address. For single mode LE only controllers this option will not be available. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_core.c | 95 +++++++++++++++++++++++++++++---------------- 2 files changed, 63 insertions(+), 33 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index d3a8fff50f69..fe4b06bfc150 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -118,6 +118,7 @@ enum { HCI_DEBUG_KEYS, HCI_DUT_MODE, HCI_FORCE_SC, + HCI_FORCE_STATIC_ADDR, HCI_UNREGISTER, HCI_USER_CHANNEL, diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 3711c7626cb2..b25a36c3064b 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -571,33 +571,52 @@ static const struct file_operations static_address_fops = { .release = single_release, }; -static int own_address_type_set(void *data, u64 val) +static ssize_t force_static_address_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { - struct hci_dev *hdev = data; - - if (val != 0 && val != 1) - return -EINVAL; - - hci_dev_lock(hdev); - hdev->own_addr_type = val; - hci_dev_unlock(hdev); + struct hci_dev *hdev = file->private_data; + char buf[3]; - return 0; + buf[0] = test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags) ? 'Y': 'N'; + buf[1] = '\n'; + buf[2] = '\0'; + return simple_read_from_buffer(user_buf, count, ppos, buf, 2); } -static int own_address_type_get(void *data, u64 *val) +static ssize_t force_static_address_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) { - struct hci_dev *hdev = data; + struct hci_dev *hdev = file->private_data; + char buf[32]; + size_t buf_size = min(count, (sizeof(buf)-1)); + bool enable; - hci_dev_lock(hdev); - *val = hdev->own_addr_type; - hci_dev_unlock(hdev); + if (test_bit(HCI_UP, &hdev->flags)) + return -EBUSY; - return 0; + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + buf[buf_size] = '\0'; + if (strtobool(buf, &enable)) + return -EINVAL; + + if (enable == test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags)) + return -EALREADY; + + change_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags); + + return count; } -DEFINE_SIMPLE_ATTRIBUTE(own_address_type_fops, own_address_type_get, - own_address_type_set, "%llu\n"); +static const struct file_operations force_static_address_fops = { + .open = simple_open, + .read = force_static_address_read, + .write = force_static_address_write, + .llseek = default_llseek, +}; static int identity_resolving_keys_show(struct seq_file *f, void *ptr) { @@ -1406,17 +1425,19 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt) hci_setup_link_policy(req); if (lmp_le_capable(hdev)) { - if (test_bit(HCI_SETUP, &hdev->dev_flags)) { - /* If the controller has a public BD_ADDR, then - * by default use that one. If this is a LE only - * controller without a public address, default - * to the random address. - */ - if (bacmp(&hdev->bdaddr, BDADDR_ANY)) - hdev->own_addr_type = ADDR_LE_DEV_PUBLIC; - else - hdev->own_addr_type = ADDR_LE_DEV_RANDOM; - } + /* If the controller has a public BD_ADDR, then by default + * use that one. If this is a LE only controller without + * a public address, default to the random address. + * + * For debugging purposes it is possible to force + * controllers with a public address to use the + * random address instead. + */ + if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags) || + !bacmp(&hdev->bdaddr, BDADDR_ANY)) + hdev->own_addr_type = ADDR_LE_DEV_RANDOM; + else + hdev->own_addr_type = ADDR_LE_DEV_PUBLIC; hci_set_le_support(req); } @@ -1536,12 +1557,20 @@ static int __hci_init(struct hci_dev *hdev) } if (lmp_le_capable(hdev)) { + debugfs_create_file("static_address", 0444, hdev->debugfs, + hdev, &static_address_fops); + + /* For controllers with a public address, provide a debug + * option to force the usage of the configured static + * address. By default the public address is used. + */ + if (bacmp(&hdev->bdaddr, BDADDR_ANY)) + debugfs_create_file("force_static_address", 0644, + hdev->debugfs, hdev, + &force_static_address_fops); + debugfs_create_u8("white_list_size", 0444, hdev->debugfs, &hdev->le_white_list_size); - debugfs_create_file("static_address", 0444, hdev->debugfs, - hdev, &static_address_fops); - debugfs_create_file("own_address_type", 0644, hdev->debugfs, - hdev, &own_address_type_fops); debugfs_create_file("identity_resolving_keys", 0400, hdev->debugfs, hdev, &identity_resolving_keys_fops); -- cgit v1.2.3-71-gd317 From 7a4cd51dec96b42d899ed7b2207c9ef810534451 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 19 Feb 2014 19:52:13 -0800 Subject: Bluetooth: Track the current configured random address For Bluetooth controllers with LE support, track the value of the currently configured random address. It is important to know what the current random address is to avoid unneeded attempts to set a new address. This will become important when introducing the LE privacy support in the future. In addition expose the current configured random address via debugfs for debugging purposes. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 26 ++++++++++++++++++++++++++ net/bluetooth/hci_event.c | 24 ++++++++++++++++++++++++ 3 files changed, 51 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3be2905010cd..3a8e22e9b25d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -151,6 +151,7 @@ struct hci_dev { __u8 bus; __u8 dev_type; bdaddr_t bdaddr; + bdaddr_t random_addr; bdaddr_t static_addr; __u8 own_addr_type; __u8 dev_name[HCI_MAX_NAME_LENGTH]; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index b25a36c3064b..877330b4876f 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -548,6 +548,29 @@ static int sniff_max_interval_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(sniff_max_interval_fops, sniff_max_interval_get, sniff_max_interval_set, "%llu\n"); +static int random_address_show(struct seq_file *f, void *p) +{ + struct hci_dev *hdev = f->private; + + hci_dev_lock(hdev); + seq_printf(f, "%pMR\n", &hdev->random_addr); + hci_dev_unlock(hdev); + + return 0; +} + +static int random_address_open(struct inode *inode, struct file *file) +{ + return single_open(file, random_address_show, inode->i_private); +} + +static const struct file_operations random_address_fops = { + .open = random_address_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static int static_address_show(struct seq_file *f, void *p) { struct hci_dev *hdev = f->private; @@ -1557,6 +1580,8 @@ static int __hci_init(struct hci_dev *hdev) } if (lmp_le_capable(hdev)) { + debugfs_create_file("random_address", 0444, hdev->debugfs, + hdev, &random_address_fops); debugfs_create_file("static_address", 0444, hdev->debugfs, hdev, &static_address_fops); @@ -2205,6 +2230,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) memset(hdev->eir, 0, sizeof(hdev->eir)); memset(hdev->dev_class, 0, sizeof(hdev->dev_class)); + bacpy(&hdev->random_addr, BDADDR_ANY); hci_req_unlock(hdev); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 7228fa100b1f..4327b129d38e 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -959,6 +959,26 @@ static void hci_cc_read_local_oob_ext_data(struct hci_dev *hdev, hci_dev_unlock(hdev); } + +static void hci_cc_le_set_random_addr(struct hci_dev *hdev, struct sk_buff *skb) +{ + __u8 status = *((__u8 *) skb->data); + bdaddr_t *sent; + + BT_DBG("%s status 0x%2.2x", hdev->name, status); + + sent = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_RANDOM_ADDR); + if (!sent) + return; + + hci_dev_lock(hdev); + + if (!status) + bacpy(&hdev->random_addr, sent); + + hci_dev_unlock(hdev); +} + static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb) { __u8 *sent, status = *((__u8 *) skb->data); @@ -2308,6 +2328,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_user_passkey_neg_reply(hdev, skb); break; + case HCI_OP_LE_SET_RANDOM_ADDR: + hci_cc_le_set_random_addr(hdev, skb); + break; + case HCI_OP_LE_SET_ADV_ENABLE: hci_cc_le_set_adv_enable(hdev, skb); break; -- cgit v1.2.3-71-gd317 From df942e7ba70cd0a7aa9e0432b8a6a328de2c5574 Mon Sep 17 00:00:00 2001 From: Sunil Dutt Undekari Date: Thu, 20 Feb 2014 16:22:09 +0530 Subject: cfg80211: Pass TDLS peer capability information in tdls_mgmt While framing the TDLS Setup Confirmation frame, the driver needs to know if the TDLS peer is VHT/HT/WMM capable and thus shall construct the VHT/HT operation / WMM parameter elements accordingly. Supplicant determines if the TDLS peer is VHT/HT/WMM capable based on the presence of the respective IEs in the received TDLS Setup Response frame. The host driver should not need to parse the received TDLS Response frame and thus, should be able to rely on the supplicant to indicate the capability of the peer through additional flags while transmitting the TDLS Setup Confirmation frame through tdls_mgmt operations. Signed-off-by: Sunil Dutt Undekari Signed-off-by: Johannes Berg --- drivers/net/wireless/mwifiex/cfg80211.c | 4 ++-- include/net/cfg80211.h | 3 ++- include/uapi/linux/nl80211.h | 21 +++++++++++++++++++++ net/mac80211/cfg.c | 4 ++-- net/wireless/nl80211.c | 7 ++++++- net/wireless/rdev-ops.h | 9 ++++++--- net/wireless/trace.h | 12 ++++++++---- 7 files changed, 47 insertions(+), 13 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 436ba437a4ba..6948a97af839 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -2600,8 +2600,8 @@ static int mwifiex_cfg80211_set_coalesce(struct wiphy *wiphy, static int mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, u8 *peer, u8 action_code, u8 dialog_token, - u16 status_code, const u8 *extra_ies, - size_t extra_ies_len) + u16 status_code, u32 peer_capability, + const u8 *extra_ies, size_t extra_ies_len) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); int ret; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 9f90554e88c4..c89a5b5bd103 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2465,7 +2465,8 @@ struct cfg80211_ops { int (*tdls_mgmt)(struct wiphy *wiphy, struct net_device *dev, u8 *peer, u8 action_code, u8 dialog_token, - u16 status_code, const u8 *buf, size_t len); + u16 status_code, u32 peer_capability, + const u8 *buf, size_t len); int (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev, u8 *peer, enum nl80211_tdls_operation oper); diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index ba1f7625625c..47d7087513e0 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1575,6 +1575,9 @@ enum nl80211_commands { * advertise values that cannot always be met. In such cases, an attempt * to add a new station entry with @NL80211_CMD_NEW_STATION may fail. * + * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32. + * As specified in the &enum nl80211_tdls_peer_capability. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1908,6 +1911,8 @@ enum nl80211_attrs { NL80211_ATTR_MAX_AP_ASSOC_STA, + NL80211_ATTR_TDLS_PEER_CAPABILITY, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -4074,4 +4079,20 @@ struct nl80211_vendor_cmd_info { __u32 subcmd; }; +/** + * enum nl80211_tdls_peer_capability - TDLS peer flags. + * + * Used by tdls_mgmt() to determine which conditional elements need + * to be added to TDLS Setup frames. + * + * @NL80211_TDLS_PEER_HT: TDLS peer is HT capable. + * @NL80211_TDLS_PEER_VHT: TDLS peer is VHT capable. + * @NL80211_TDLS_PEER_WMM: TDLS peer is WMM capable. + */ +enum nl80211_tdls_peer_capability { + NL80211_TDLS_PEER_HT = 1<<0, + NL80211_TDLS_PEER_VHT = 1<<1, + NL80211_TDLS_PEER_WMM = 1<<2, +}; + #endif /* __LINUX_NL80211_H */ diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 3849fd07a321..1acb29109b45 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -3644,8 +3644,8 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, u8 *peer, u8 action_code, u8 dialog_token, - u16 status_code, const u8 *extra_ies, - size_t extra_ies_len) + u16 status_code, u32 peer_capability, + const u8 *extra_ies, size_t extra_ies_len) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 058aa0e1a462..be836098d342 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -384,6 +384,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { .len = IEEE80211_QOS_MAP_LEN_MAX }, [NL80211_ATTR_MAC_HINT] = { .len = ETH_ALEN }, [NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 }, + [NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 }, }; /* policy for the key attributes */ @@ -7269,6 +7270,7 @@ static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info) struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; u8 action_code, dialog_token; + u32 peer_capability = 0; u16 status_code; u8 *peer; @@ -7287,9 +7289,12 @@ static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info) action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]); status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]); dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]); + if (info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY]) + peer_capability = + nla_get_u32(info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY]); return rdev_tdls_mgmt(rdev, dev, peer, action_code, - dialog_token, status_code, + dialog_token, status_code, peer_capability, nla_data(info->attrs[NL80211_ATTR_IE]), nla_len(info->attrs[NL80211_ATTR_IE])); } diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index c8e225947adb..74d97d33c938 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -769,13 +769,16 @@ static inline int rdev_set_rekey_data(struct cfg80211_registered_device *rdev, static inline int rdev_tdls_mgmt(struct cfg80211_registered_device *rdev, struct net_device *dev, u8 *peer, u8 action_code, u8 dialog_token, - u16 status_code, const u8 *buf, size_t len) + u16 status_code, u32 peer_capability, + const u8 *buf, size_t len) { int ret; trace_rdev_tdls_mgmt(&rdev->wiphy, dev, peer, action_code, - dialog_token, status_code, buf, len); + dialog_token, status_code, peer_capability, + buf, len); ret = rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code, - dialog_token, status_code, buf, len); + dialog_token, status_code, peer_capability, + buf, len); trace_rdev_return_int(&rdev->wiphy, ret); return ret; } diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 5eaeed59db07..aabccf13e07b 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1468,9 +1468,10 @@ TRACE_EVENT(rdev_sched_scan_start, TRACE_EVENT(rdev_tdls_mgmt, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *peer, u8 action_code, u8 dialog_token, - u16 status_code, const u8 *buf, size_t len), + u16 status_code, u32 peer_capability, + const u8 *buf, size_t len), TP_ARGS(wiphy, netdev, peer, action_code, dialog_token, status_code, - buf, len), + peer_capability, buf, len), TP_STRUCT__entry( WIPHY_ENTRY NETDEV_ENTRY @@ -1478,6 +1479,7 @@ TRACE_EVENT(rdev_tdls_mgmt, __field(u8, action_code) __field(u8, dialog_token) __field(u16, status_code) + __field(u32, peer_capability) __dynamic_array(u8, buf, len) ), TP_fast_assign( @@ -1487,13 +1489,15 @@ TRACE_EVENT(rdev_tdls_mgmt, __entry->action_code = action_code; __entry->dialog_token = dialog_token; __entry->status_code = status_code; + __entry->peer_capability = peer_capability; memcpy(__get_dynamic_array(buf), buf, len); ), TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ", action_code: %u, " - "dialog_token: %u, status_code: %u, buf: %#.2x ", + "dialog_token: %u, status_code: %u, peer_capability: %u buf: %#.2x ", WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->action_code, __entry->dialog_token, - __entry->status_code, ((u8 *)__get_dynamic_array(buf))[0]) + __entry->status_code, __entry->peer_capability, + ((u8 *)__get_dynamic_array(buf))[0]) ); TRACE_EVENT(rdev_dump_survey, -- cgit v1.2.3-71-gd317 From d9b8396a52b4e857263eeb9e1eba474ea11c19bf Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 13 Feb 2014 17:16:10 +0100 Subject: cfg80211: document sched_scan_stop synchronous behaviour Due to userspace assumptions, the sched_scan_stop operation must be synchronous, i.e. once it returns a new scheduled scan must be able to start immediately. Document this in the API. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 7 ++++++- include/uapi/linux/nl80211.h | 5 +++-- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index c89a5b5bd103..7c9fe4b05927 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2206,7 +2206,12 @@ struct cfg80211_qos_map { * @set_cqm_txe_config: Configure connection quality monitor TX error * thresholds. * @sched_scan_start: Tell the driver to start a scheduled scan. - * @sched_scan_stop: Tell the driver to stop an ongoing scheduled scan. + * @sched_scan_stop: Tell the driver to stop an ongoing scheduled scan. This + * call must stop the scheduled scan and be ready for starting a new one + * before it returns, i.e. @sched_scan_start may be called immediately + * after that again and should not fail in that case. The driver should + * not call cfg80211_sched_scan_stopped() for a requested stop (when this + * method returns 0.) * * @mgmt_frame_register: Notify driver that a management frame type was * registered. Note that this callback may not sleep, and cannot run diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 47d7087513e0..81481cff1dc1 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -303,8 +303,9 @@ * passed, all channels allowed for the current regulatory domain * are used. Extra IEs can also be passed from the userspace by * using the %NL80211_ATTR_IE attribute. - * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT - * if scheduled scan is not running. + * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT if + * scheduled scan is not running. The caller may assume that as soon + * as the call returns, it is safe to start a new scheduled scan again. * @NL80211_CMD_SCHED_SCAN_RESULTS: indicates that there are scheduled scan * results available. * @NL80211_CMD_SCHED_SCAN_STOPPED: indicates that the scheduled scan has -- cgit v1.2.3-71-gd317 From 37e3308cb2b6933019d9d9c2045877d6d68d9c5a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 17 Feb 2014 10:48:17 +0100 Subject: mac80211: allow driver to return error from sched_scan_stop In order to solve races with sched_scan_stop, it is necessary for the driver to be able to return an error to propagate that to cfg80211 so it doesn't send an event. Reviewed-by: Alexander Bondar Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 6 ++++-- drivers/net/wireless/ti/wlcore/main.c | 6 ++++-- include/net/mac80211.h | 3 ++- net/mac80211/driver-ops.h | 12 ++++++++---- net/mac80211/scan.c | 2 +- 5 files changed, 19 insertions(+), 10 deletions(-) (limited to 'include/net') diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index beaf8140abbf..7492fc0f2766 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -1746,14 +1746,16 @@ out: return ret; } -static void iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) +static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); mutex_lock(&mvm->mutex); iwl_mvm_sched_scan_stop(mvm); mutex_unlock(&mvm->mutex); + + return 0; } static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 7aae5b3a0c2c..4175a57ac9f5 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -3668,8 +3668,8 @@ out: return ret; } -static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) +static int wl1271_op_sched_scan_stop(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); @@ -3691,6 +3691,8 @@ static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw, wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); + + return 0; } static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index a6bcc39e146e..86faa413b37d 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2460,6 +2460,7 @@ enum ieee80211_roc_type { * This process will continue until sched_scan_stop is called. * * @sched_scan_stop: Tell the hardware to stop an ongoing scheduled scan. + * In this case, ieee80211_sched_scan_stopped() must not be called. * * @sw_scan_start: Notifier function that is called just before a software scan * is started. Can be NULL, if the driver doesn't need this notification. @@ -2807,7 +2808,7 @@ struct ieee80211_ops { struct ieee80211_vif *vif, struct cfg80211_sched_scan_request *req, struct ieee80211_sched_scan_ies *ies); - void (*sched_scan_stop)(struct ieee80211_hw *hw, + int (*sched_scan_stop)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); void (*sw_scan_start)(struct ieee80211_hw *hw); void (*sw_scan_complete)(struct ieee80211_hw *hw); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index ef8b385eff04..fc689f5d971e 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -354,16 +354,20 @@ drv_sched_scan_start(struct ieee80211_local *local, return ret; } -static inline void drv_sched_scan_stop(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata) +static inline int drv_sched_scan_stop(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) { + int ret; + might_sleep(); check_sdata_in_driver(sdata); trace_drv_sched_scan_stop(local, sdata); - local->ops->sched_scan_stop(&local->hw, &sdata->vif); - trace_drv_return_void(local); + ret = local->ops->sched_scan_stop(&local->hw, &sdata->vif); + trace_drv_return_int(local, ret); + + return ret; } static inline void drv_sw_scan_start(struct ieee80211_local *local) diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index b211e412511f..836f500dfbf3 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -1056,7 +1056,7 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata) local->sched_scan_req = NULL; if (rcu_access_pointer(local->sched_scan_sdata)) - drv_sched_scan_stop(local, sdata); + ret = drv_sched_scan_stop(local, sdata); out: mutex_unlock(&local->mtx); -- cgit v1.2.3-71-gd317 From 3f959d46a60c20eedf6f228e49d820c5922ec68f Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 20 Feb 2014 11:55:56 -0800 Subject: Bluetooth: Provide option for changing LE advertising channel map For testing purposes it is useful to provide an option to change the advertising channel map. So add a debugfs option to allow this. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 31 +++++++++++++++++++++++++++++++ net/bluetooth/mgmt.c | 2 +- 3 files changed, 33 insertions(+), 1 deletion(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3a8e22e9b25d..c0fcc041fbb5 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -178,6 +178,7 @@ struct hci_dev { __u16 page_scan_interval; __u16 page_scan_window; __u8 page_scan_type; + __u8 le_adv_channel_map; __u16 le_scan_interval; __u16 le_scan_window; __u16 le_conn_min_interval; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 877330b4876f..67192867c998 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -757,6 +757,34 @@ static int conn_max_interval_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(conn_max_interval_fops, conn_max_interval_get, conn_max_interval_set, "%llu\n"); +static int adv_channel_map_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + if (val < 0x01 || val > 0x07) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->le_adv_channel_map = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int adv_channel_map_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->le_adv_channel_map; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(adv_channel_map_fops, adv_channel_map_get, + adv_channel_map_set, "%llu\n"); + static ssize_t lowpan_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -1605,6 +1633,8 @@ static int __hci_init(struct hci_dev *hdev) hdev, &conn_min_interval_fops); debugfs_create_file("conn_max_interval", 0644, hdev->debugfs, hdev, &conn_max_interval_fops); + debugfs_create_file("adv_channel_map", 0644, hdev->debugfs, + hdev, &adv_channel_map_fops); debugfs_create_file("6lowpan", 0644, hdev->debugfs, hdev, &lowpan_debugfs_fops); } @@ -3264,6 +3294,7 @@ struct hci_dev *hci_alloc_dev(void) hdev->sniff_max_interval = 800; hdev->sniff_min_interval = 80; + hdev->le_adv_channel_map = 0x07; hdev->le_scan_interval = 0x0060; hdev->le_scan_window = 0x0030; hdev->le_conn_min_interval = 0x0028; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 5f5e388716ec..12fa6399c796 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1374,7 +1374,7 @@ static void enable_advertising(struct hci_request *req) cp.max_interval = __constant_cpu_to_le16(0x0800); cp.type = get_adv_type(hdev); cp.own_address_type = hdev->own_addr_type; - cp.channel_map = 0x07; + cp.channel_map = hdev->le_adv_channel_map; hci_req_add(req, HCI_OP_LE_SET_ADV_PARAM, sizeof(cp), &cp); -- cgit v1.2.3-71-gd317 From 0fcf8ac5acb60839ada695b069362761f1f2da71 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 20 Feb 2014 16:45:33 +0200 Subject: cfg80211: docbook: fix small formatting error docbook (or one of its friends) gets confused with semi-colons in the argument descriptions, causing it to think that the semi-colon is marking a new section in the description of addr_mask in wiphy struct. Prevent this by using hyphens instead of semi-colons in the mask example. Signed-off-by: Luciano Coelho Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 7c9fe4b05927..b36a822b9028 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2796,7 +2796,7 @@ struct wiphy_vendor_command { * @perm_addr: permanent MAC address of this device * @addr_mask: If the device supports multiple MAC addresses by masking, * set this to a mask with variable bits set to 1, e.g. if the last - * four bits are variable then set it to 00:...:00:0f. The actual + * four bits are variable then set it to 00-00-00-00-00-0f. The actual * variable bits shall be determined by the interfaces added, with * interfaces not matching the mask being rejected to be brought up. * @n_addresses: number of addresses in @addresses. -- cgit v1.2.3-71-gd317 From b80edbc177800623dd07240e19e69c7b16ee5cba Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 20 Feb 2014 16:45:34 +0200 Subject: cfg80211: docbook: add interface combinations documentation Add the ieee80211_iface_limit and the ieee80211_iface_combination structures to docbook. Reformat the examples of combinations slightly, so it looks a bit better on docbook. Signed-off-by: Luciano Coelho Signed-off-by: Johannes Berg --- Documentation/DocBook/80211.tmpl | 2 ++ include/net/cfg80211.h | 12 ++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'include/net') diff --git a/Documentation/DocBook/80211.tmpl b/Documentation/DocBook/80211.tmpl index 46ad6faee9ab..044b76436e83 100644 --- a/Documentation/DocBook/80211.tmpl +++ b/Documentation/DocBook/80211.tmpl @@ -98,6 +98,8 @@ !Finclude/net/cfg80211.h priv_to_wiphy !Finclude/net/cfg80211.h set_wiphy_dev !Finclude/net/cfg80211.h wdev_priv +!Finclude/net/cfg80211.h ieee80211_iface_limit +!Finclude/net/cfg80211.h ieee80211_iface_combination Actions and configuration diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index b36a822b9028..8c9ba44fb7cf 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2616,9 +2616,12 @@ struct ieee80211_iface_limit { * only in special cases. * @radar_detect_widths: bitmap of channel widths supported for radar detection * - * These examples can be expressed as follows: + * With this structure the driver can describe which interface + * combinations it supports concurrently. * - * Allow #STA <= 1, #AP <= 1, matching BI, channels = 1, 2 total: + * Examples: + * + * 1. Allow #STA <= 1, #AP <= 1, matching BI, channels = 1, 2 total: * * struct ieee80211_iface_limit limits1[] = { * { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), }, @@ -2632,7 +2635,7 @@ struct ieee80211_iface_limit { * }; * * - * Allow #{AP, P2P-GO} <= 8, channels = 1, 8 total: + * 2. Allow #{AP, P2P-GO} <= 8, channels = 1, 8 total: * * struct ieee80211_iface_limit limits2[] = { * { .max = 8, .types = BIT(NL80211_IFTYPE_AP) | @@ -2646,7 +2649,8 @@ struct ieee80211_iface_limit { * }; * * - * Allow #STA <= 1, #{P2P-client,P2P-GO} <= 3 on two channels, 4 total. + * 3. Allow #STA <= 1, #{P2P-client,P2P-GO} <= 3 on two channels, 4 total. + * * This allows for an infrastructure connection and three P2P connections. * * struct ieee80211_iface_limit limits3[] = { -- cgit v1.2.3-71-gd317 From 863efaf224d24705c0ffdc59f2a0ec68f2d85b4f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 22 Feb 2014 19:06:32 +0200 Subject: Bluetooth: Add initial code for distributing local IRK This code adds a HCI_PRIVACY flag to track whether Privacy support is enabled (meaning we have a local IRK) and makes sure the IRK is distributed during SMP key distribution in case this flag is set. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 1 + include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/smp.c | 6 ++++-- 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index fe4b06bfc150..5ff885ff29df 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -126,6 +126,7 @@ enum { HCI_SSP_ENABLED, HCI_SC_ENABLED, HCI_SC_ONLY, + HCI_PRIVACY, HCI_RPA_RESOLVING, HCI_HS_ENABLED, HCI_LE_ENABLED, diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index c0fcc041fbb5..68bbcabdd9fd 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -303,6 +303,8 @@ struct hci_dev { __u8 scan_rsp_data[HCI_MAX_AD_LENGTH]; __u8 scan_rsp_data_len; + __u8 irk[16]; + int (*open)(struct hci_dev *hdev); int (*close)(struct hci_dev *hdev); int (*flush)(struct hci_dev *hdev); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 6355a460e9d0..8ef50c790b96 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -265,6 +265,9 @@ static void build_pairing_cmd(struct l2cap_conn *conn, if (test_bit(HCI_RPA_RESOLVING, &hdev->dev_flags)) remote_dist |= SMP_DIST_ID_KEY; + if (test_bit(HCI_PRIVACY, &hdev->dev_flags)) + local_dist |= SMP_DIST_ID_KEY; + if (rsp == NULL) { req->io_capability = conn->hcon->io_capability; req->oob_flag = SMP_OOB_NOT_PRESENT; @@ -1189,8 +1192,7 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) struct smp_cmd_ident_addr_info addrinfo; struct smp_cmd_ident_info idinfo; - /* Send a dummy key */ - get_random_bytes(idinfo.irk, sizeof(idinfo.irk)); + memcpy(idinfo.irk, hdev->irk, sizeof(idinfo.irk)); smp_send_cmd(conn, SMP_CMD_IDENT_INFO, sizeof(idinfo), &idinfo); -- cgit v1.2.3-71-gd317 From 755a900fcde16c66223a85259859a3b534b6c64c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 22 Feb 2014 19:06:34 +0200 Subject: Bluetooth: Add mgmt defines for privacy This patch adds basic mgmt defines for enabling privacy. This includes a new setting flag as well as the Set Privacy command. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 2e46251e8aec..62d560624e3d 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -96,6 +96,7 @@ struct mgmt_rp_read_index_list { #define MGMT_SETTING_ADVERTISING 0x00000400 #define MGMT_SETTING_SECURE_CONN 0x00000800 #define MGMT_SETTING_DEBUG_KEYS 0x00001000 +#define MGMT_SETTING_PRIVACY 0x00002000 #define MGMT_OP_READ_INFO 0x0004 #define MGMT_READ_INFO_SIZE 0 @@ -389,6 +390,13 @@ struct mgmt_cp_set_scan_params { #define MGMT_OP_SET_DEBUG_KEYS 0x002E +#define MGMT_OP_SET_PRIVACY 0x002F +struct mgmt_cp_set_privacy { + __u8 privacy; + __u8 irk[16]; +} __packed; +#define MGMT_SET_PRIVACY_SIZE 17 + struct mgmt_irk_info { struct mgmt_addr_info addr; __u8 val[16]; -- cgit v1.2.3-71-gd317 From d6bfd59caef7e543c7786af9664309dd1a7f6396 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 23 Feb 2014 19:42:20 +0200 Subject: Bluetooth: Add timer for regenerating local RPA This patch adds a timer for updating the local RPA periodically. The default timeout is set to 15 minutes. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 1 + include/net/bluetooth/hci_core.h | 5 +++++ net/bluetooth/hci_core.c | 4 ++++ net/bluetooth/mgmt.c | 27 +++++++++++++++++++++++++++ 4 files changed, 37 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 5ff885ff29df..1bb45a47a78a 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -127,6 +127,7 @@ enum { HCI_SC_ENABLED, HCI_SC_ONLY, HCI_PRIVACY, + HCI_RPA_EXPIRED, HCI_RPA_RESOLVING, HCI_HS_ENABLED, HCI_LE_ENABLED, diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 68bbcabdd9fd..6415514e4f17 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -130,6 +130,9 @@ struct oob_data { #define HCI_MAX_SHORT_NAME_LENGTH 10 +/* Default LE RPA expiry time, 15 minutes */ +#define HCI_DEFAULT_RPA_TIMEOUT (15 * 60) + struct amp_assoc { __u16 len; __u16 offset; @@ -304,6 +307,8 @@ struct hci_dev { __u8 scan_rsp_data_len; __u8 irk[16]; + __u32 rpa_timeout; + struct delayed_work rpa_expired; int (*open)(struct hci_dev *hdev); int (*close)(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 964aa8deb009..92d35811b61e 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2102,6 +2102,7 @@ static int hci_dev_do_open(struct hci_dev *hdev) if (!ret) { hci_dev_hold(hdev); + set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags); set_bit(HCI_UP, &hdev->flags); hci_notify(hdev, HCI_DEV_UP); if (!test_bit(HCI_SETUP, &hdev->dev_flags) && @@ -2199,6 +2200,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) cancel_delayed_work(&hdev->service_cache); cancel_delayed_work_sync(&hdev->le_scan_disable); + cancel_delayed_work_sync(&hdev->rpa_expired); hci_dev_lock(hdev); hci_inquiry_cache_flush(hdev); @@ -3300,6 +3302,8 @@ struct hci_dev *hci_alloc_dev(void) hdev->le_conn_min_interval = 0x0028; hdev->le_conn_max_interval = 0x0038; + hdev->rpa_timeout = HCI_DEFAULT_RPA_TIMEOUT; + mutex_init(&hdev->lock); mutex_init(&hdev->req_lock); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 301b18a1c6a0..4522da18d8e5 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -881,12 +881,39 @@ static void service_cache_off(struct work_struct *work) hci_req_run(&req, NULL); } +static void rpa_expired(struct work_struct *work) +{ + struct hci_dev *hdev = container_of(work, struct hci_dev, + rpa_expired.work); + struct hci_request req; + + BT_DBG(""); + + set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags); + + if (!test_bit(HCI_ADVERTISING, &hdev->dev_flags) || + hci_conn_num(hdev, LE_LINK) > 0) + return; + + /* The generation of a new RPA and programming it into the + * controller happens in the enable_advertising() function. + */ + + hci_req_init(&req, hdev); + + disable_advertising(&req); + enable_advertising(&req); + + hci_req_run(&req, NULL); +} + static void mgmt_init_hdev(struct sock *sk, struct hci_dev *hdev) { if (test_and_set_bit(HCI_MGMT, &hdev->dev_flags)) return; INIT_DELAYED_WORK(&hdev->service_cache, service_cache_off); + INIT_DELAYED_WORK(&hdev->rpa_expired, rpa_expired); /* Non-mgmt controlled devices get this bit set * implicitly so that pairing works for them, however -- cgit v1.2.3-71-gd317 From ebd3a74765377b7528bb372aab2890638790301d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 23 Feb 2014 19:42:21 +0200 Subject: Bluetooth: Add hci_update_random_address() convenience function This patch adds a convenience function for updating the local random address which is needed before advertising, scanning and initiating LE connections. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_core.c | 55 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 6415514e4f17..2506963c7a06 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1257,6 +1257,8 @@ void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8], __u8 ltk[16]); +int hci_update_random_address(struct hci_request *req, u8 *own_addr_type); + #define SCO_AIRMODE_MASK 0x0003 #define SCO_AIRMODE_CVSD 0x0000 #define SCO_AIRMODE_TRANSP 0x0003 diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 92d35811b61e..7bc67b4e47a7 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3276,6 +3276,61 @@ static void le_scan_disable_work(struct work_struct *work) BT_ERR("Disable LE scanning request failed: err %d", err); } +int hci_update_random_address(struct hci_request *req, u8 *own_addr_type) +{ + struct hci_dev *hdev = req->hdev; + int err; + + /* If privacy is enabled use a resolvable private address. If + * the current RPA has expired or there's something else than an + * RPA currently in use regenerate a new one. + */ + if (test_bit(HCI_PRIVACY, &hdev->dev_flags)) { + bdaddr_t rpa; + int to; + + *own_addr_type = ADDR_LE_DEV_RANDOM; + + if (!test_and_clear_bit(HCI_RPA_EXPIRED, &hdev->dev_flags) && + hci_bdaddr_is_rpa(&hdev->random_addr, ADDR_LE_DEV_RANDOM)) + return 0; + + err = smp_generate_rpa(hdev->tfm_aes, hdev->irk, &rpa); + if (err < 0) { + BT_ERR("%s failed to generate new RPA", hdev->name); + return err; + } + + hci_req_add(req, HCI_OP_LE_SET_RANDOM_ADDR, 6, &rpa); + + to = msecs_to_jiffies(hdev->rpa_timeout * 1000); + queue_delayed_work(hdev->workqueue, &hdev->rpa_expired, to); + + return 0; + } + + /* If forcing static address is in use or there is no public + * address use the static address as random address (but skip + * the HCI command if the current random address is already the + * static one. + */ + if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags) || + !bacmp(&hdev->bdaddr, BDADDR_ANY)) { + *own_addr_type = ADDR_LE_DEV_RANDOM; + if (bacmp(&hdev->static_addr, &hdev->random_addr)) + hci_req_add(req, HCI_OP_LE_SET_RANDOM_ADDR, 6, + &hdev->static_addr); + return 0; + } + + /* Neither privacy nor static address is being used so use a + * public address. + */ + *own_addr_type = ADDR_LE_DEV_PUBLIC; + + return 0; +} + /* Alloc HCI device */ struct hci_dev *hci_alloc_dev(void) { -- cgit v1.2.3-71-gd317 From 7bf32048b1af87942d311ef1620995ffc89c07d8 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 23 Feb 2014 19:42:29 +0200 Subject: Bluetooth: Remove unneeded hdev->own_addr_type Now that the identity address type is always looked up for all successful connections, the hdev->own_addr_type variable has become completely unnecessary. Simply remove it. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 - net/bluetooth/hci_core.c | 17 +---------------- 2 files changed, 1 insertion(+), 17 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 2506963c7a06..43b6d1131c4d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -156,7 +156,6 @@ struct hci_dev { bdaddr_t bdaddr; bdaddr_t random_addr; bdaddr_t static_addr; - __u8 own_addr_type; __u8 dev_name[HCI_MAX_NAME_LENGTH]; __u8 short_name[HCI_MAX_SHORT_NAME_LENGTH]; __u8 eir[HCI_MAX_EIR_LENGTH]; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 629919be071c..1651de959d9c 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1506,23 +1506,8 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt) if (hdev->commands[5] & 0x10) hci_setup_link_policy(req); - if (lmp_le_capable(hdev)) { - /* If the controller has a public BD_ADDR, then by default - * use that one. If this is a LE only controller without - * a public address, default to the random address. - * - * For debugging purposes it is possible to force - * controllers with a public address to use the - * random address instead. - */ - if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags) || - !bacmp(&hdev->bdaddr, BDADDR_ANY)) - hdev->own_addr_type = ADDR_LE_DEV_RANDOM; - else - hdev->own_addr_type = ADDR_LE_DEV_PUBLIC; - + if (lmp_le_capable(hdev)) hci_set_le_support(req); - } /* Read features beyond page 1 if available */ for (p = 2; p < HCI_MAX_PAGES && p <= hdev->max_page; p++) { -- cgit v1.2.3-71-gd317 From 94b1fc92cd7cf550460ffd4bcc08c2707564aa49 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 23 Feb 2014 20:25:54 -0800 Subject: Bluetooth: Use unresolvable private address for active scanning When running active scanning during LE discovery, do not reveal the own identity to the peer devices. In case LE privacy has been enabled, then a resolvable private address is used. If the LE privacy option is off, then use an unresolvable private address. The public address or static random address is never used in active scanning anymore. This ensures that scan request are send using a random address. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 3 ++- net/bluetooth/hci_conn.c | 5 ++++- net/bluetooth/hci_core.c | 18 +++++++++++++++++- net/bluetooth/mgmt.c | 8 ++++++-- 4 files changed, 29 insertions(+), 5 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 43b6d1131c4d..0ee9cd11b3ef 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1256,7 +1256,8 @@ void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8], __u8 ltk[16]); -int hci_update_random_address(struct hci_request *req, u8 *own_addr_type); +int hci_update_random_address(struct hci_request *req, bool require_privacy, + u8 *own_addr_type); #define SCO_AIRMODE_MASK 0x0003 #define SCO_AIRMODE_CVSD 0x0000 diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index a1efa1c62de8..3d6b1cf07d23 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -563,7 +563,10 @@ static int hci_create_le_conn(struct hci_conn *conn) memset(&cp, 0, sizeof(cp)); - err = hci_update_random_address(&req, &own_addr_type); + /* Update random address, but set require_privacy to false so + * that we never connect with an unresolvable address. + */ + err = hci_update_random_address(&req, false, &own_addr_type); if (err < 0) return err; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 80462a126ebd..31e68ade309d 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3332,7 +3332,8 @@ static void le_scan_disable_work(struct work_struct *work) BT_ERR("Disable LE scanning request failed: err %d", err); } -int hci_update_random_address(struct hci_request *req, u8 *own_addr_type) +int hci_update_random_address(struct hci_request *req, bool require_privacy, + u8 *own_addr_type) { struct hci_dev *hdev = req->hdev; int err; @@ -3365,6 +3366,21 @@ int hci_update_random_address(struct hci_request *req, u8 *own_addr_type) return 0; } + /* In case of required privacy without resolvable private address, + * use an unresolvable private address. This is useful for active + * scanning and non-connectable advertising. + */ + if (require_privacy) { + bdaddr_t urpa; + + get_random_bytes(&urpa, 6); + urpa.b[5] &= 0x3f; /* Clear two most significant bits */ + + *own_addr_type = ADDR_LE_DEV_RANDOM; + hci_req_add(req, HCI_OP_LE_SET_RANDOM_ADDR, 6, &urpa); + return 0; + } + /* If forcing static address is in use or there is no public * address use the static address as random address (but skip * the HCI command if the current random address is already the diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 37305facf4d6..5d309d4ab527 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -844,7 +844,7 @@ static void enable_advertising(struct hci_request *req) memset(&cp, 0, sizeof(cp)); - if (hci_update_random_address(req, &own_addr_type) < 0) + if (hci_update_random_address(req, false, &own_addr_type) < 0) return; cp.min_interval = __constant_cpu_to_le16(0x0800); @@ -3389,7 +3389,11 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, memset(¶m_cp, 0, sizeof(param_cp)); - err = hci_update_random_address(&req, &own_addr_type); + /* All active scans will be done with either a resolvable + * private address (when privacy feature has been enabled) + * or unresolvable private address. + */ + err = hci_update_random_address(&req, true, &own_addr_type); if (err < 0) { err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, MGMT_STATUS_FAILED); -- cgit v1.2.3-71-gd317 From 2b5224dca5a9257a3df8cc9f93978ecb3757b9c2 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 23 Feb 2014 20:39:22 -0800 Subject: Bluetooth: Store current RPA and update it if needed The RPA needs to be stored to know which is the current one. Otherwise it is impossible to ensure that always the correct RPA can be programmed into the controller when it is needed. Current code checks if the address in the controller is a RPA, but that can potentially lead to using a RPA that can not be resolved with the IRK that has been distributed. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 11 +++++------ 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 0ee9cd11b3ef..fb3b677ff8a6 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -308,6 +308,7 @@ struct hci_dev { __u8 irk[16]; __u32 rpa_timeout; struct delayed_work rpa_expired; + bdaddr_t rpa; int (*open)(struct hci_dev *hdev); int (*close)(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 31e68ade309d..9f1c3d7d1d74 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3339,26 +3339,25 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy, int err; /* If privacy is enabled use a resolvable private address. If - * the current RPA has expired or there's something else than an - * RPA currently in use regenerate a new one. + * current RPA has expired or there is something else than + * the current RPA in use, then generate a new one. */ if (test_bit(HCI_PRIVACY, &hdev->dev_flags)) { - bdaddr_t rpa; int to; *own_addr_type = ADDR_LE_DEV_RANDOM; if (!test_and_clear_bit(HCI_RPA_EXPIRED, &hdev->dev_flags) && - hci_bdaddr_is_rpa(&hdev->random_addr, ADDR_LE_DEV_RANDOM)) + !bacmp(&hdev->random_addr, &hdev->rpa)) return 0; - err = smp_generate_rpa(hdev->tfm_aes, hdev->irk, &rpa); + err = smp_generate_rpa(hdev->tfm_aes, hdev->irk, &hdev->rpa); if (err < 0) { BT_ERR("%s failed to generate new RPA", hdev->name); return err; } - hci_req_add(req, HCI_OP_LE_SET_RANDOM_ADDR, 6, &rpa); + hci_req_add(req, HCI_OP_LE_SET_RANDOM_ADDR, 6, &hdev->rpa); to = msecs_to_jiffies(hdev->rpa_timeout * 1000); queue_delayed_work(hdev->workqueue, &hdev->rpa_expired, to); -- cgit v1.2.3-71-gd317 From f4f07505005932ca5f6c8003323bd38dbd0c769c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 24 Feb 2014 14:52:16 +0200 Subject: Bluetooth: Add convenience function for getting total connection count This patch adds a convenience function to return the number of connections in the conn_hash list. This will be useful once we update the power off procedure to disconnect any open connections. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index fb3b677ff8a6..d2d756753714 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -561,6 +561,13 @@ static inline unsigned int hci_conn_num(struct hci_dev *hdev, __u8 type) } } +static inline unsigned int hci_conn_count(struct hci_dev *hdev) +{ + struct hci_conn_hash *c = &hdev->conn_hash; + + return c->acl_num + c->amp_num + c->sco_num + c->le_num; +} + static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev, __u16 handle) { -- cgit v1.2.3-71-gd317 From 778b235a3be0588da1909f7ef75b4bc3dbc09dfc Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 24 Feb 2014 14:52:17 +0200 Subject: Bluetooth: Move HCI_ADVERTISING handling into mgmt.c We'll soon need to make decisions on toggling the HCI_ADVERTISING flag based on pending mgmt_set_powered commands. Therefore, move the handling from hci_event.c into mgmt.c. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_event.c | 8 ++------ net/bluetooth/mgmt.c | 8 ++++++++ 3 files changed, 11 insertions(+), 6 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index d2d756753714..6ff882e727d4 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1174,6 +1174,7 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered); void mgmt_discoverable_timeout(struct hci_dev *hdev); void mgmt_discoverable(struct hci_dev *hdev, u8 discoverable); void mgmt_connectable(struct hci_dev *hdev, u8 connectable); +void mgmt_advertising(struct hci_dev *hdev, u8 advertising); void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status); void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, bool persistent); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 064d619344b3..dea465ba276b 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -991,12 +991,8 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_lock(hdev); - if (!status) { - if (*sent) - set_bit(HCI_ADVERTISING, &hdev->dev_flags); - else - clear_bit(HCI_ADVERTISING, &hdev->dev_flags); - } + if (!status) + mgmt_advertising(hdev, *sent); hci_dev_unlock(hdev); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 9865e523df20..d39e57e9fed6 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -4832,6 +4832,14 @@ void mgmt_connectable(struct hci_dev *hdev, u8 connectable) new_settings(hdev, NULL); } +void mgmt_advertising(struct hci_dev *hdev, u8 advertising) +{ + if (advertising) + set_bit(HCI_ADVERTISING, &hdev->dev_flags); + else + clear_bit(HCI_ADVERTISING, &hdev->dev_flags); +} + void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status) { u8 mgmt_err = mgmt_status(status); -- cgit v1.2.3-71-gd317 From 12d4a3b2ccb3ac2bd56e7c216d6e7f44730006f3 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 24 Feb 2014 14:52:18 +0200 Subject: Bluetooth: Move check for MGMT_CONNECTED flag into mgmt.c Once mgmt_set_powered(off) starts doing disconnections we'll need to care about any disconnections in mgmt.c and not just those with the MGMT_CONNECTED flag set. Therefore, move the check into mgmt.c from hci_event.c. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 3 ++- net/bluetooth/hci_event.c | 7 ++++--- net/bluetooth/mgmt.c | 6 +++++- 3 files changed, 11 insertions(+), 5 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 6ff882e727d4..269c8201a362 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1182,7 +1182,8 @@ void mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u32 flags, u8 *name, u8 name_len, u8 *dev_class); void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 reason); + u8 link_type, u8 addr_type, u8 reason, + bool mgmt_connected); void mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status); void mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index dea465ba276b..877cee844b9e 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1842,6 +1842,7 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) struct hci_ev_disconn_complete *ev = (void *) skb->data; u8 reason = hci_to_mgmt_reason(ev->reason); struct hci_conn *conn; + bool mgmt_connected; u8 type; BT_DBG("%s status 0x%2.2x", hdev->name, ev->status); @@ -1860,9 +1861,9 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) conn->state = BT_CLOSED; - if (test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) - mgmt_device_disconnected(hdev, &conn->dst, conn->type, - conn->dst_type, reason); + mgmt_connected = test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags); + mgmt_device_disconnected(hdev, &conn->dst, conn->type, conn->dst_type, + reason, mgmt_connected); if (conn->type == ACL_LINK && conn->flush_key) hci_remove_link_key(hdev, &conn->dst); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d39e57e9fed6..bdc831b3bb97 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5012,11 +5012,15 @@ static void unpair_device_rsp(struct pending_cmd *cmd, void *data) } void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 reason) + u8 link_type, u8 addr_type, u8 reason, + bool mgmt_connected) { struct mgmt_ev_device_disconnected ev; struct sock *sk = NULL; + if (!mgmt_connected) + return; + if (link_type != ACL_LINK && link_type != LE_LINK) return; -- cgit v1.2.3-71-gd317 From 3328715e6c1fcb10cd86b0f3212d18290b7e4463 Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Fri, 21 Feb 2014 08:41:08 +0100 Subject: xfrm4: Add IPsec protocol multiplexer This patch add an IPsec protocol multiplexer. With this it is possible to add alternative protocol handlers as needed for IPsec virtual tunnel interfaces. Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 31 +++++- net/ipv4/Makefile | 2 +- net/ipv4/xfrm4_input.c | 9 -- net/ipv4/xfrm4_protocol.c | 268 ++++++++++++++++++++++++++++++++++++++++++++++ net/xfrm/xfrm_input.c | 17 +-- 5 files changed, 310 insertions(+), 17 deletions(-) create mode 100644 net/ipv4/xfrm4_protocol.c (limited to 'include/net') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 45332acac022..345a15084557 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1347,6 +1347,18 @@ struct xfrm_algo_desc { struct sadb_alg desc; }; +/* XFRM protocol handlers. */ +struct xfrm4_protocol { + int (*handler)(struct sk_buff *skb); + int (*input_handler)(struct sk_buff *skb, int nexthdr, __be32 spi, + int encap_type); + int (*cb_handler)(struct sk_buff *skb, int err); + int (*err_handler)(struct sk_buff *skb, u32 info); + + struct xfrm4_protocol __rcu *next; + int priority; +}; + /* XFRM tunnel handlers. */ struct xfrm_tunnel { int (*handler)(struct sk_buff *skb); @@ -1498,13 +1510,18 @@ int xfrm4_rcv(struct sk_buff *skb); static inline int xfrm4_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi) { - return xfrm4_rcv_encap(skb, nexthdr, spi, 0); + XFRM_SPI_SKB_CB(skb)->family = AF_INET; + XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr); + return xfrm_input(skb, nexthdr, spi, 0); } int xfrm4_extract_output(struct xfrm_state *x, struct sk_buff *skb); int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb); int xfrm4_output(struct sk_buff *skb); int xfrm4_output_finish(struct sk_buff *skb); +int xfrm4_rcv_cb(struct sk_buff *skb, u8 protocol, int err); +int xfrm4_protocol_register(struct xfrm4_protocol *handler, unsigned char protocol); +int xfrm4_protocol_deregister(struct xfrm4_protocol *handler, unsigned char protocol); int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family); int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family); void xfrm4_local_error(struct sk_buff *skb, u32 mtu); @@ -1752,4 +1769,16 @@ static inline int xfrm_mark_put(struct sk_buff *skb, const struct xfrm_mark *m) return ret; } +static inline int xfrm_rcv_cb(struct sk_buff *skb, unsigned int family, + u8 protocol, int err) +{ + switch(family) { +#ifdef CONFIG_INET + case AF_INET: + return xfrm4_rcv_cb(skb, protocol, err); +#endif + } + return 0; +} + #endif /* _NET_XFRM_H */ diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index f8c49ce5b283..f032688d20d3 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -55,4 +55,4 @@ obj-$(CONFIG_MEMCG_KMEM) += tcp_memcontrol.o obj-$(CONFIG_NETLABEL) += cipso_ipv4.o obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \ - xfrm4_output.o + xfrm4_output.o xfrm4_protocol.o diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c index 1f12c8b45864..aac6197b7a71 100644 --- a/net/ipv4/xfrm4_input.c +++ b/net/ipv4/xfrm4_input.c @@ -37,15 +37,6 @@ drop: return NET_RX_DROP; } -int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, - int encap_type) -{ - XFRM_SPI_SKB_CB(skb)->family = AF_INET; - XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr); - return xfrm_input(skb, nexthdr, spi, encap_type); -} -EXPORT_SYMBOL(xfrm4_rcv_encap); - int xfrm4_transport_finish(struct sk_buff *skb, int async) { struct iphdr *iph = ip_hdr(skb); diff --git a/net/ipv4/xfrm4_protocol.c b/net/ipv4/xfrm4_protocol.c new file mode 100644 index 000000000000..862a26c2014f --- /dev/null +++ b/net/ipv4/xfrm4_protocol.c @@ -0,0 +1,268 @@ +/* xfrm4_protocol.c - Generic xfrm protocol multiplexer. + * + * Copyright (C) 2013 secunet Security Networks AG + * + * Author: + * Steffen Klassert + * + * Based on: + * net/ipv4/tunnel4.c + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +static struct xfrm4_protocol __rcu *esp4_handlers __read_mostly; +static struct xfrm4_protocol __rcu *ah4_handlers __read_mostly; +static struct xfrm4_protocol __rcu *ipcomp4_handlers __read_mostly; +static DEFINE_MUTEX(xfrm4_protocol_mutex); + +static inline struct xfrm4_protocol __rcu **proto_handlers(u8 protocol) +{ + switch (protocol) { + case IPPROTO_ESP: + return &esp4_handlers; + case IPPROTO_AH: + return &ah4_handlers; + case IPPROTO_COMP: + return &ipcomp4_handlers; + } + + return NULL; +} + +#define for_each_protocol_rcu(head, handler) \ + for (handler = rcu_dereference(head); \ + handler != NULL; \ + handler = rcu_dereference(handler->next)) \ + +int xfrm4_rcv_cb(struct sk_buff *skb, u8 protocol, int err) +{ + int ret; + struct xfrm4_protocol *handler; + + for_each_protocol_rcu(*proto_handlers(protocol), handler) + if ((ret = handler->cb_handler(skb, err)) <= 0) + return ret; + + return 0; +} +EXPORT_SYMBOL(xfrm4_rcv_cb); + +int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, + int encap_type) +{ + int ret; + struct xfrm4_protocol *handler; + + XFRM_SPI_SKB_CB(skb)->family = AF_INET; + XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr); + + for_each_protocol_rcu(*proto_handlers(nexthdr), handler) + if ((ret = handler->input_handler(skb, nexthdr, spi, encap_type)) != -EINVAL) + return ret; + + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); + + kfree_skb(skb); + return 0; +} +EXPORT_SYMBOL(xfrm4_rcv_encap); + +static int xfrm4_esp_rcv(struct sk_buff *skb) +{ + int ret; + struct xfrm4_protocol *handler; + + for_each_protocol_rcu(esp4_handlers, handler) + if ((ret = handler->handler(skb)) != -EINVAL) + return ret; + + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); + + kfree_skb(skb); + return 0; +} + +static void xfrm4_esp_err(struct sk_buff *skb, u32 info) +{ + struct xfrm4_protocol *handler; + + for_each_protocol_rcu(esp4_handlers, handler) + if (!handler->err_handler(skb, info)) + break; +} + +static int xfrm4_ah_rcv(struct sk_buff *skb) +{ + int ret; + struct xfrm4_protocol *handler; + + for_each_protocol_rcu(ah4_handlers, handler) + if ((ret = handler->handler(skb)) != -EINVAL) + return ret;; + + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); + + kfree_skb(skb); + return 0; +} + +static void xfrm4_ah_err(struct sk_buff *skb, u32 info) +{ + struct xfrm4_protocol *handler; + + for_each_protocol_rcu(ah4_handlers, handler) + if (!handler->err_handler(skb, info)) + break; +} + +static int xfrm4_ipcomp_rcv(struct sk_buff *skb) +{ + int ret; + struct xfrm4_protocol *handler; + + for_each_protocol_rcu(ipcomp4_handlers, handler) + if ((ret = handler->handler(skb)) != -EINVAL) + return ret; + + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); + + kfree_skb(skb); + return 0; +} + +static void xfrm4_ipcomp_err(struct sk_buff *skb, u32 info) +{ + struct xfrm4_protocol *handler; + + for_each_protocol_rcu(ipcomp4_handlers, handler) + if (!handler->err_handler(skb, info)) + break; +} + +static const struct net_protocol esp4_protocol = { + .handler = xfrm4_esp_rcv, + .err_handler = xfrm4_esp_err, + .no_policy = 1, + .netns_ok = 1, +}; + +static const struct net_protocol ah4_protocol = { + .handler = xfrm4_ah_rcv, + .err_handler = xfrm4_ah_err, + .no_policy = 1, + .netns_ok = 1, +}; + +static const struct net_protocol ipcomp4_protocol = { + .handler = xfrm4_ipcomp_rcv, + .err_handler = xfrm4_ipcomp_err, + .no_policy = 1, + .netns_ok = 1, +}; + +static inline const struct net_protocol *netproto(unsigned char protocol) +{ + switch (protocol) { + case IPPROTO_ESP: + return &esp4_protocol; + case IPPROTO_AH: + return &ah4_protocol; + case IPPROTO_COMP: + return &ipcomp4_protocol; + } + + return NULL; +} + +int xfrm4_protocol_register(struct xfrm4_protocol *handler, + unsigned char protocol) +{ + struct xfrm4_protocol __rcu **pprev; + struct xfrm4_protocol *t; + bool add_netproto = false; + + int ret = -EEXIST; + int priority = handler->priority; + + mutex_lock(&xfrm4_protocol_mutex); + + if (!rcu_dereference_protected(*proto_handlers(protocol), + lockdep_is_held(&xfrm4_protocol_mutex))) + add_netproto = true; + + for (pprev = proto_handlers(protocol); + (t = rcu_dereference_protected(*pprev, + lockdep_is_held(&xfrm4_protocol_mutex))) != NULL; + pprev = &t->next) { + if (t->priority < priority) + break; + if (t->priority == priority) + goto err; + } + + handler->next = *pprev; + rcu_assign_pointer(*pprev, handler); + + ret = 0; + +err: + mutex_unlock(&xfrm4_protocol_mutex); + + if (add_netproto) { + if (inet_add_protocol(netproto(protocol), protocol)) { + pr_err("%s: can't add protocol\n", __func__); + ret = -EAGAIN; + } + } + + return ret; +} +EXPORT_SYMBOL(xfrm4_protocol_register); + +int xfrm4_protocol_deregister(struct xfrm4_protocol *handler, + unsigned char protocol) +{ + struct xfrm4_protocol __rcu **pprev; + struct xfrm4_protocol *t; + int ret = -ENOENT; + + mutex_lock(&xfrm4_protocol_mutex); + + for (pprev = proto_handlers(protocol); + (t = rcu_dereference_protected(*pprev, + lockdep_is_held(&xfrm4_protocol_mutex))) != NULL; + pprev = &t->next) { + if (t == handler) { + *pprev = handler->next; + ret = 0; + break; + } + } + + if (!rcu_dereference_protected(*proto_handlers(protocol), + lockdep_is_held(&xfrm4_protocol_mutex))) { + if (inet_del_protocol(netproto(protocol), protocol) < 0) { + pr_err("%s: can't remove protocol\n", __func__); + ret = -EAGAIN; + } + } + + mutex_unlock(&xfrm4_protocol_mutex); + + synchronize_net(); + + return ret; +} +EXPORT_SYMBOL(xfrm4_protocol_deregister); diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index 6c7ac016ce3a..99e3a9e5285e 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c @@ -108,7 +108,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) int err; __be32 seq; __be32 seq_hi; - struct xfrm_state *x; + struct xfrm_state *x = NULL; xfrm_address_t *daddr; struct xfrm_mode *inner_mode; unsigned int family; @@ -120,9 +120,14 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) async = 1; x = xfrm_input_state(skb); seq = XFRM_SKB_CB(skb)->seq.input.low; + family = x->outer_mode->afinfo->family; goto resume; } + daddr = (xfrm_address_t *)(skb_network_header(skb) + + XFRM_SPI_SKB_CB(skb)->daddroff); + family = XFRM_SPI_SKB_CB(skb)->family; + /* Allocate new secpath or COW existing one. */ if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) { struct sec_path *sp; @@ -137,10 +142,6 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) skb->sp = sp; } - daddr = (xfrm_address_t *)(skb_network_header(skb) + - XFRM_SPI_SKB_CB(skb)->daddroff); - family = XFRM_SPI_SKB_CB(skb)->family; - seq = 0; if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR); @@ -201,7 +202,6 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) if (nexthdr == -EINPROGRESS) return 0; - resume: spin_lock(&x->lock); if (nexthdr <= 0) { @@ -263,6 +263,10 @@ resume: } } while (!err); + err = xfrm_rcv_cb(skb, family, x->type->proto, 0); + if (err) + goto drop; + nf_reset(skb); if (decaps) { @@ -276,6 +280,7 @@ resume: drop_unlock: spin_unlock(&x->lock); drop: + xfrm_rcv_cb(skb, family, x && x->type ? x->type->proto : nexthdr, -1); kfree_skb(skb); return 0; } -- cgit v1.2.3-71-gd317 From 70be6c91c86596ad2b60c73587880b47df170a41 Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Fri, 21 Feb 2014 08:41:09 +0100 Subject: xfrm: Add xfrm_tunnel_skb_cb to the skb common buffer IPsec vti_rcv needs to remind the tunnel pointer to check it later at the vti_rcv_cb callback. So add this pointer to the IPsec common buffer, initialize it and check it to avoid transport state matching of a tunneled packet. Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 50 +++++++++++++++++++++++++++++++++++------------ net/ipv4/xfrm4_protocol.c | 7 +++++++ net/xfrm/xfrm_input.c | 5 +++++ 3 files changed, 50 insertions(+), 12 deletions(-) (limited to 'include/net') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 345a15084557..33112599fa47 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -599,16 +599,27 @@ struct xfrm_mgr { int xfrm_register_km(struct xfrm_mgr *km); int xfrm_unregister_km(struct xfrm_mgr *km); +struct xfrm_tunnel_skb_cb { + union { + struct inet_skb_parm h4; + struct inet6_skb_parm h6; + } header; + + union { + struct ip_tunnel *ip4; + struct ip6_tnl *ip6; + } tunnel; +}; + +#define XFRM_TUNNEL_SKB_CB(__skb) ((struct xfrm_tunnel_skb_cb *)&((__skb)->cb[0])) + /* * This structure is used for the duration where packets are being * transformed by IPsec. As soon as the packet leaves IPsec the * area beyond the generic IP part may be overwritten. */ struct xfrm_skb_cb { - union { - struct inet_skb_parm h4; - struct inet6_skb_parm h6; - } header; + struct xfrm_tunnel_skb_cb header; /* Sequence number for replay protection. */ union { @@ -630,10 +641,7 @@ struct xfrm_skb_cb { * to transmit header information to the mode input/output functions. */ struct xfrm_mode_skb_cb { - union { - struct inet_skb_parm h4; - struct inet6_skb_parm h6; - } header; + struct xfrm_tunnel_skb_cb header; /* Copied from header for IPv4, always set to zero and DF for IPv6. */ __be16 id; @@ -665,10 +673,7 @@ struct xfrm_mode_skb_cb { * related information. */ struct xfrm_spi_skb_cb { - union { - struct inet_skb_parm h4; - struct inet6_skb_parm h6; - } header; + struct xfrm_tunnel_skb_cb header; unsigned int daddroff; unsigned int family; @@ -1510,6 +1515,7 @@ int xfrm4_rcv(struct sk_buff *skb); static inline int xfrm4_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi) { + XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL; XFRM_SPI_SKB_CB(skb)->family = AF_INET; XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr); return xfrm_input(skb, nexthdr, spi, 0); @@ -1781,4 +1787,24 @@ static inline int xfrm_rcv_cb(struct sk_buff *skb, unsigned int family, return 0; } +static inline int xfrm_tunnel_check(struct sk_buff *skb, struct xfrm_state *x, + unsigned int family) +{ + bool tunnel = false; + + switch(family) { + case AF_INET: + if (XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4) + tunnel = true; + break; + case AF_INET6: + if (XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6) + tunnel = true; + break; + } + if (tunnel && !(x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL)) + return -EINVAL; + + return 0; +} #endif /* _NET_XFRM_H */ diff --git a/net/ipv4/xfrm4_protocol.c b/net/ipv4/xfrm4_protocol.c index 862a26c2014f..cdc09efca442 100644 --- a/net/ipv4/xfrm4_protocol.c +++ b/net/ipv4/xfrm4_protocol.c @@ -65,6 +65,7 @@ int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, int ret; struct xfrm4_protocol *handler; + XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL; XFRM_SPI_SKB_CB(skb)->family = AF_INET; XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr); @@ -84,6 +85,8 @@ static int xfrm4_esp_rcv(struct sk_buff *skb) int ret; struct xfrm4_protocol *handler; + XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL; + for_each_protocol_rcu(esp4_handlers, handler) if ((ret = handler->handler(skb)) != -EINVAL) return ret; @@ -108,6 +111,8 @@ static int xfrm4_ah_rcv(struct sk_buff *skb) int ret; struct xfrm4_protocol *handler; + XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL; + for_each_protocol_rcu(ah4_handlers, handler) if ((ret = handler->handler(skb)) != -EINVAL) return ret;; @@ -132,6 +137,8 @@ static int xfrm4_ipcomp_rcv(struct sk_buff *skb) int ret; struct xfrm4_protocol *handler; + XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL; + for_each_protocol_rcu(ipcomp4_handlers, handler) if ((ret = handler->handler(skb)) != -EINVAL) return ret; diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index 99e3a9e5285e..4218164f4f5e 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c @@ -163,6 +163,11 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) skb->sp->xvec[skb->sp->len++] = x; + if (xfrm_tunnel_check(skb, x, family)) { + XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR); + goto drop; + } + spin_lock(&x->lock); if (unlikely(x->km.state == XFRM_STATE_ACQ)) { XFRM_INC_STATS(net, LINUX_MIB_XFRMACQUIREERROR); -- cgit v1.2.3-71-gd317 From 9994bb8e1e05833e627d54147acb64952efcb713 Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Fri, 21 Feb 2014 08:41:10 +0100 Subject: xfrm4: Remove xfrm_tunnel_notifier This was used from vti and is replaced by the IPsec protocol multiplexer hooks. It is now unused, so remove it. Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 2 -- net/ipv4/xfrm4_mode_tunnel.c | 68 -------------------------------------------- 2 files changed, 70 deletions(-) (limited to 'include/net') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 33112599fa47..8b925288a8bc 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1531,8 +1531,6 @@ int xfrm4_protocol_deregister(struct xfrm4_protocol *handler, unsigned char prot int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family); int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family); void xfrm4_local_error(struct sk_buff *skb, u32 mtu); -int xfrm4_mode_tunnel_input_register(struct xfrm_tunnel_notifier *handler); -int xfrm4_mode_tunnel_input_deregister(struct xfrm_tunnel_notifier *handler); int xfrm6_mode_tunnel_input_register(struct xfrm_tunnel_notifier *handler); int xfrm6_mode_tunnel_input_deregister(struct xfrm_tunnel_notifier *handler); int xfrm6_extract_header(struct sk_buff *skb); diff --git a/net/ipv4/xfrm4_mode_tunnel.c b/net/ipv4/xfrm4_mode_tunnel.c index 31b18152528f..05f2b484954f 100644 --- a/net/ipv4/xfrm4_mode_tunnel.c +++ b/net/ipv4/xfrm4_mode_tunnel.c @@ -15,65 +15,6 @@ #include #include -/* Informational hook. The decap is still done here. */ -static struct xfrm_tunnel_notifier __rcu *rcv_notify_handlers __read_mostly; -static DEFINE_MUTEX(xfrm4_mode_tunnel_input_mutex); - -int xfrm4_mode_tunnel_input_register(struct xfrm_tunnel_notifier *handler) -{ - struct xfrm_tunnel_notifier __rcu **pprev; - struct xfrm_tunnel_notifier *t; - int ret = -EEXIST; - int priority = handler->priority; - - mutex_lock(&xfrm4_mode_tunnel_input_mutex); - - for (pprev = &rcv_notify_handlers; - (t = rcu_dereference_protected(*pprev, - lockdep_is_held(&xfrm4_mode_tunnel_input_mutex))) != NULL; - pprev = &t->next) { - if (t->priority > priority) - break; - if (t->priority == priority) - goto err; - - } - - handler->next = *pprev; - rcu_assign_pointer(*pprev, handler); - - ret = 0; - -err: - mutex_unlock(&xfrm4_mode_tunnel_input_mutex); - return ret; -} -EXPORT_SYMBOL_GPL(xfrm4_mode_tunnel_input_register); - -int xfrm4_mode_tunnel_input_deregister(struct xfrm_tunnel_notifier *handler) -{ - struct xfrm_tunnel_notifier __rcu **pprev; - struct xfrm_tunnel_notifier *t; - int ret = -ENOENT; - - mutex_lock(&xfrm4_mode_tunnel_input_mutex); - for (pprev = &rcv_notify_handlers; - (t = rcu_dereference_protected(*pprev, - lockdep_is_held(&xfrm4_mode_tunnel_input_mutex))) != NULL; - pprev = &t->next) { - if (t == handler) { - *pprev = handler->next; - ret = 0; - break; - } - } - mutex_unlock(&xfrm4_mode_tunnel_input_mutex); - synchronize_net(); - - return ret; -} -EXPORT_SYMBOL_GPL(xfrm4_mode_tunnel_input_deregister); - static inline void ipip_ecn_decapsulate(struct sk_buff *skb) { struct iphdr *inner_iph = ipip_hdr(skb); @@ -127,14 +68,8 @@ static int xfrm4_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb) return 0; } -#define for_each_input_rcu(head, handler) \ - for (handler = rcu_dereference(head); \ - handler != NULL; \ - handler = rcu_dereference(handler->next)) - static int xfrm4_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb) { - struct xfrm_tunnel_notifier *handler; int err = -EINVAL; if (XFRM_MODE_SKB_CB(skb)->protocol != IPPROTO_IPIP) @@ -143,9 +78,6 @@ static int xfrm4_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb) if (!pskb_may_pull(skb, sizeof(struct iphdr))) goto out; - for_each_input_rcu(rcv_notify_handlers, handler) - handler->handler(skb); - err = skb_unclone(skb, GFP_ATOMIC); if (err) goto out; -- cgit v1.2.3-71-gd317 From 67a8fc27cca06e185c1ab39baaccd2103f6f9f51 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 18 Feb 2014 18:06:49 +0000 Subject: netfilter: nf_tables: add nft_dereference() macro Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 4 ++++ net/netfilter/nf_tables_api.c | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index e7e14ffe0f6a..81abd61500f4 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -521,6 +522,9 @@ 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 *); +#define nft_dereference(p) \ + nfnl_dereference(p, NFNL_SUBSYS_NFTABLES) + #define MODULE_ALIAS_NFT_FAMILY(family) \ MODULE_ALIAS("nft-afinfo-" __stringify(family)) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index adce01e8bb57..4b7e14ddd2b2 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -794,9 +794,8 @@ nf_tables_counters(struct nft_base_chain *chain, const struct nlattr *attr) stats->pkts = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS])); if (chain->stats) { - /* nfnl_lock is held, add some nfnl function for this, later */ struct nft_stats __percpu *oldstats = - rcu_dereference_protected(chain->stats, 1); + nft_dereference(chain->stats); rcu_assign_pointer(chain->stats, newstats); synchronize_rcu(); -- cgit v1.2.3-71-gd317 From 089027e57cfa79337feffdd7252c8ba0be352afa Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 21 Feb 2014 19:46:12 +0100 Subject: cfg80211: regulatory: allow getting DFS CAC time from userspace Introduce DFS CAC time as a regd param, configured per REG_RULE and set per channel in cfg80211. DFS CAC time is close connected with regulatory database configuration. Instead of using hardcoded values, get DFS CAC time form regulatory database. Pass DFS CAC time to user mode (mainly for iw reg get, iw list, iw info). Allow setting DFS CAC time via CRDA. Add support for internal regulatory database. Signed-off-by: Janusz Dziedzic [rewrap commit log] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ include/net/regulatory.h | 21 +++++++++++++-------- include/uapi/linux/nl80211.h | 6 ++++++ net/wireless/genregdb.awk | 8 +++++++- net/wireless/nl80211.c | 13 ++++++++++++- net/wireless/reg.c | 32 ++++++++++++++++++++++++++------ 6 files changed, 66 insertions(+), 16 deletions(-) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 8c9ba44fb7cf..bfa9a0c7b2d7 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -151,6 +151,7 @@ enum ieee80211_channel_flags { * @dfs_state: current state of this channel. Only relevant if radar is required * on this channel. * @dfs_state_entered: timestamp (jiffies) when the dfs state was entered. + * @dfs_cac_ms: DFS CAC time in milliseconds, this is valid for DFS channels. */ struct ieee80211_channel { enum ieee80211_band band; @@ -165,6 +166,7 @@ struct ieee80211_channel { int orig_mag, orig_mpwr; enum nl80211_dfs_state dfs_state; unsigned long dfs_state_entered; + unsigned int dfs_cac_ms; }; /** diff --git a/include/net/regulatory.h b/include/net/regulatory.h index b07cdc9fa454..75fc1f5a948d 100644 --- a/include/net/regulatory.h +++ b/include/net/regulatory.h @@ -155,6 +155,7 @@ struct ieee80211_reg_rule { struct ieee80211_freq_range freq_range; struct ieee80211_power_rule power_rule; u32 flags; + u32 dfs_cac_ms; }; struct ieee80211_regdomain { @@ -172,14 +173,18 @@ struct ieee80211_regdomain { #define DBM_TO_MBM(gain) ((gain) * 100) #define MBM_TO_DBM(gain) ((gain) / 100) -#define REG_RULE(start, end, bw, gain, eirp, reg_flags) \ -{ \ - .freq_range.start_freq_khz = MHZ_TO_KHZ(start), \ - .freq_range.end_freq_khz = MHZ_TO_KHZ(end), \ - .freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw), \ - .power_rule.max_antenna_gain = DBI_TO_MBI(gain),\ - .power_rule.max_eirp = DBM_TO_MBM(eirp), \ - .flags = reg_flags, \ +#define REG_RULE_EXT(start, end, bw, gain, eirp, dfs_cac, reg_flags) \ +{ \ + .freq_range.start_freq_khz = MHZ_TO_KHZ(start), \ + .freq_range.end_freq_khz = MHZ_TO_KHZ(end), \ + .freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw), \ + .power_rule.max_antenna_gain = DBI_TO_MBI(gain), \ + .power_rule.max_eirp = DBM_TO_MBM(eirp), \ + .flags = reg_flags, \ + .dfs_cac_ms = dfs_cac, \ } +#define REG_RULE(start, end, bw, gain, eirp, reg_flags) \ + REG_RULE_EXT(start, end, bw, gain, eirp, 0, reg_flags) + #endif diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index ff72cab3cd3a..1ba9d626aa83 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2335,6 +2335,7 @@ enum nl80211_band_attr { * @NL80211_FREQUENCY_ATTR_NO_160MHZ: any 160 MHz (but not 80+80) channel * using this channel as the primary or any of the secondary channels * isn't possible + * @NL80211_FREQUENCY_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds. * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number * currently defined * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use @@ -2353,6 +2354,7 @@ enum nl80211_frequency_attr { NL80211_FREQUENCY_ATTR_NO_HT40_PLUS, NL80211_FREQUENCY_ATTR_NO_80MHZ, NL80211_FREQUENCY_ATTR_NO_160MHZ, + NL80211_FREQUENCY_ATTR_DFS_CAC_TIME, /* keep last */ __NL80211_FREQUENCY_ATTR_AFTER_LAST, @@ -2449,6 +2451,8 @@ enum nl80211_reg_type { * If you don't have one then don't send this. * @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for * a given frequency range. The value is in mBm (100 * dBm). + * @NL80211_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds. + * If not present or 0 default CAC time will be used. * @NL80211_REG_RULE_ATTR_MAX: highest regulatory rule attribute number * currently defined * @__NL80211_REG_RULE_ATTR_AFTER_LAST: internal use @@ -2464,6 +2468,8 @@ enum nl80211_reg_rule_attr { NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN, NL80211_ATTR_POWER_RULE_MAX_EIRP, + NL80211_ATTR_DFS_CAC_TIME, + /* keep last */ __NL80211_REG_RULE_ATTR_AFTER_LAST, NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1 diff --git a/net/wireless/genregdb.awk b/net/wireless/genregdb.awk index fdfd3f063a9b..b35da8dc85de 100644 --- a/net/wireless/genregdb.awk +++ b/net/wireless/genregdb.awk @@ -66,6 +66,7 @@ function parse_reg_rule() units = $8 sub(/\)/, "", units) sub(/,/, "", units) + dfs_cac = $9 if (units == "mW") { if (power == 100) { power = 20 @@ -78,7 +79,12 @@ function parse_reg_rule() } else { print "Unknown power value in database!" } + } else { + dfs_cac = $8 } + sub(/,/, "", dfs_cac) + sub(/\(/, "", dfs_cac) + sub(/\)/, "", dfs_cac) flagstr = "" for (i=8; i<=NF; i++) flagstr = flagstr $i @@ -111,7 +117,7 @@ function parse_reg_rule() } flags = flags "0" - printf "\t\tREG_RULE(%d, %d, %d, %d, %d, %s),\n", start, end, bw, gain, power, flags + printf "\t\tREG_RULE_EXT(%d, %d, %d, %d, %d, %d, %s),\n", start, end, bw, gain, power, dfs_cac, flags rules++ } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 2c38b28a85b9..9f7ebf94a050 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -593,6 +593,10 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_TIME, time)) goto nla_put_failure; + if (nla_put_u32(msg, + NL80211_FREQUENCY_ATTR_DFS_CAC_TIME, + chan->dfs_cac_ms)) + goto nla_put_failure; } } @@ -4614,6 +4618,7 @@ static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 }, [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 }, [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 }, + [NL80211_ATTR_DFS_CAC_TIME] = { .type = NLA_U32 }, }; static int parse_reg_rule(struct nlattr *tb[], @@ -4649,6 +4654,10 @@ static int parse_reg_rule(struct nlattr *tb[], power_rule->max_antenna_gain = nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]); + if (tb[NL80211_ATTR_DFS_CAC_TIME]) + reg_rule->dfs_cac_ms = + nla_get_u32(tb[NL80211_ATTR_DFS_CAC_TIME]); + return 0; } @@ -5136,7 +5145,9 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN, power_rule->max_antenna_gain) || nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP, - power_rule->max_eirp)) + power_rule->max_eirp) || + nla_put_u32(msg, NL80211_ATTR_DFS_CAC_TIME, + reg_rule->dfs_cac_ms)) goto nla_put_failure_rcu; nla_nest_end(msg, nl_reg_rule); diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 651404c22de9..b95e9cf139c0 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -756,6 +756,9 @@ static int reg_rules_intersect(const struct ieee80211_regdomain *rd1, power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain, power_rule2->max_antenna_gain); + intersected_rule->dfs_cac_ms = max(rule1->dfs_cac_ms, + rule2->dfs_cac_ms); + if (!is_valid_reg_rule(intersected_rule)) return -EINVAL; @@ -1078,6 +1081,14 @@ static void handle_channel(struct wiphy *wiphy, min_t(int, chan->orig_mag, MBI_TO_DBI(power_rule->max_antenna_gain)); chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp); + + if (chan->flags & IEEE80211_CHAN_RADAR) { + if (reg_rule->dfs_cac_ms) + chan->dfs_cac_ms = reg_rule->dfs_cac_ms; + else + chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS; + } + if (chan->orig_mpwr) { /* * Devices that use REGULATORY_COUNTRY_IE_FOLLOW_POWER @@ -2256,9 +2267,9 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd) const struct ieee80211_reg_rule *reg_rule = NULL; const struct ieee80211_freq_range *freq_range = NULL; const struct ieee80211_power_rule *power_rule = NULL; - char bw[32]; + char bw[32], cac_time[32]; - pr_info(" (start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp)\n"); + pr_info(" (start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp), (dfs_cac_time)\n"); for (i = 0; i < rd->n_reg_rules; i++) { reg_rule = &rd->reg_rules[i]; @@ -2273,23 +2284,32 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd) snprintf(bw, sizeof(bw), "%d KHz", freq_range->max_bandwidth_khz); + if (reg_rule->flags & NL80211_RRF_DFS) + scnprintf(cac_time, sizeof(cac_time), "%u s", + reg_rule->dfs_cac_ms/1000); + else + scnprintf(cac_time, sizeof(cac_time), "N/A"); + + /* * There may not be documentation for max antenna gain * in certain regions */ if (power_rule->max_antenna_gain) - pr_info(" (%d KHz - %d KHz @ %s), (%d mBi, %d mBm)\n", + pr_info(" (%d KHz - %d KHz @ %s), (%d mBi, %d mBm), (%s)\n", freq_range->start_freq_khz, freq_range->end_freq_khz, bw, power_rule->max_antenna_gain, - power_rule->max_eirp); + power_rule->max_eirp, + cac_time); else - pr_info(" (%d KHz - %d KHz @ %s), (N/A, %d mBm)\n", + pr_info(" (%d KHz - %d KHz @ %s), (N/A, %d mBm), (%s)\n", freq_range->start_freq_khz, freq_range->end_freq_khz, bw, - power_rule->max_eirp); + power_rule->max_eirp, + cac_time); } } -- cgit v1.2.3-71-gd317 From 31559f35c5724976fd975e5d7e90cdb693b8dd27 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 21 Feb 2014 19:46:13 +0100 Subject: cfg80211: DFS get CAC time from regulatory database Send Channel Availability Check time as a parameter of start_radar_detection() callback. Get CAC time from regulatory database. Signed-off-by: Janusz Dziedzic Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 5 ++++- net/mac80211/cfg.c | 8 ++++---- net/wireless/chan.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++ net/wireless/core.h | 3 +++ net/wireless/mlme.c | 2 +- net/wireless/nl80211.c | 9 +++++++- 6 files changed, 76 insertions(+), 7 deletions(-) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index bfa9a0c7b2d7..ff3af16eba21 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2505,7 +2505,8 @@ struct cfg80211_ops { int (*start_radar_detection)(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_chan_def *chandef); + struct cfg80211_chan_def *chandef, + u32 cac_time_ms); int (*update_ft_ies)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_update_ft_ies_params *ftie); int (*crit_proto_start)(struct wiphy *wiphy, @@ -3182,6 +3183,7 @@ struct cfg80211_cached_keys; * @p2p_started: true if this is a P2P Device that has been started * @cac_started: true if DFS channel availability check has been started * @cac_start_time: timestamp (jiffies) when the dfs state was entered. + * @cac_time_ms: CAC time in ms * @ps: powersave mode is enabled * @ps_timeout: dynamic powersave timeout * @ap_unexpected_nlportid: (private) netlink port ID of application @@ -3237,6 +3239,7 @@ struct wireless_dev { bool cac_started; unsigned long cac_start_time; + unsigned int cac_time_ms; #ifdef CONFIG_CFG80211_WEXT /* wext data */ diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 1acb29109b45..80534f524fd6 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2914,11 +2914,11 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy, static int ieee80211_start_radar_detection(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_chan_def *chandef) + struct cfg80211_chan_def *chandef, + u32 cac_time_ms) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; - unsigned long timeout; int err; mutex_lock(&local->mtx); @@ -2937,9 +2937,9 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy, if (err) goto out_unlock; - timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS); ieee80211_queue_delayed_work(&sdata->local->hw, - &sdata->dfs_cac_timer_work, timeout); + &sdata->dfs_cac_timer_work, + msecs_to_jiffies(cac_time_ms)); out_unlock: mutex_unlock(&local->mtx); diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 5946450c5406..8659d5cee2a6 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -490,6 +490,62 @@ static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy, return r; } +static unsigned int cfg80211_get_chans_dfs_cac_time(struct wiphy *wiphy, + u32 center_freq, + u32 bandwidth) +{ + struct ieee80211_channel *c; + u32 start_freq, end_freq, freq; + unsigned int dfs_cac_ms = 0; + + start_freq = cfg80211_get_start_freq(center_freq, bandwidth); + end_freq = cfg80211_get_end_freq(center_freq, bandwidth); + + for (freq = start_freq; freq <= end_freq; freq += 20) { + c = ieee80211_get_channel(wiphy, freq); + if (!c) + return 0; + + if (c->flags & IEEE80211_CHAN_DISABLED) + return 0; + + if (!(c->flags & IEEE80211_CHAN_RADAR)) + continue; + + if (c->dfs_cac_ms > dfs_cac_ms) + dfs_cac_ms = c->dfs_cac_ms; + } + + return dfs_cac_ms; +} + +unsigned int +cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy, + const struct cfg80211_chan_def *chandef) +{ + int width; + unsigned int t1 = 0, t2 = 0; + + if (WARN_ON(!cfg80211_chandef_valid(chandef))) + return 0; + + width = cfg80211_chandef_get_width(chandef); + if (width < 0) + return 0; + + t1 = cfg80211_get_chans_dfs_cac_time(wiphy, + chandef->center_freq1, + width); + + if (!chandef->center_freq2) + return t1; + + t2 = cfg80211_get_chans_dfs_cac_time(wiphy, + chandef->center_freq2, + width); + + return max(t1, t2); +} static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, u32 center_freq, u32 bandwidth, diff --git a/net/wireless/core.h b/net/wireless/core.h index 40683004d523..64fde38c1a7e 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -402,6 +402,9 @@ void cfg80211_set_dfs_state(struct wiphy *wiphy, void cfg80211_dfs_channels_update_work(struct work_struct *work); +unsigned int +cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy, + const struct cfg80211_chan_def *chandef); static inline int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index d47c9d127b1e..c52ff59a3e96 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -778,7 +778,7 @@ void cfg80211_cac_event(struct net_device *netdev, switch (event) { case NL80211_RADAR_CAC_FINISHED: timeout = wdev->cac_start_time + - msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS); + msecs_to_jiffies(wdev->cac_time_ms); WARN_ON(!time_after_eq(jiffies, timeout)); cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE); break; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 9f7ebf94a050..8fa02a3fa7f7 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5779,6 +5779,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb, struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_chan_def chandef; enum nl80211_dfs_regions dfs_region; + unsigned int cac_time_ms; int err; dfs_region = reg_get_dfs_region(wdev->wiphy); @@ -5814,11 +5815,17 @@ static int nl80211_start_radar_detection(struct sk_buff *skb, if (err) return err; - err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef); + cac_time_ms = cfg80211_chandef_dfs_cac_time(&rdev->wiphy, &chandef); + if (WARN_ON(!cac_time_ms)) + cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS; + + err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef, + cac_time_ms); if (!err) { wdev->chandef = chandef; wdev->cac_started = true; wdev->cac_start_time = jiffies; + wdev->cac_time_ms = cac_time_ms; } return err; } -- cgit v1.2.3-71-gd317 From 1b346576359c72bee34b1476b4fc63d77d37b314 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Wed, 26 Feb 2014 01:20:42 +0100 Subject: ipv4: yet another new IP_MTU_DISCOVER option IP_PMTUDISC_OMIT IP_PMTUDISC_INTERFACE has a design error: because it does not allow the generation of fragments if the interface mtu is exceeded, it is very hard to make use of this option in already deployed name server software for which I introduced this option. This patch adds yet another new IP_MTU_DISCOVER option to not honor any path mtu information and not accepting new icmp notifications destined for the socket this option is enabled on. But we allow outgoing fragmentation in case the packet size exceeds the outgoing interface mtu. As such this new option can be used as a drop-in replacement for IP_PMTUDISC_DONT, which is currently in use by most name server software making the adoption of this option very smooth and easy. The original advantage of IP_PMTUDISC_INTERFACE is still maintained: ignoring incoming path MTU updates and not honoring discovered path MTUs in the output path. Fixes: 482fc6094afad5 ("ipv4: introduce new IP_MTU_DISCOVER mode IP_PMTUDISC_INTERFACE") Cc: Florian Weimer Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/net/ip.h | 9 ++++++++- include/uapi/linux/in.h | 4 ++++ net/ipv4/ip_output.c | 9 +++------ net/ipv4/ip_sockglue.c | 2 +- 4 files changed, 16 insertions(+), 8 deletions(-) (limited to 'include/net') diff --git a/include/net/ip.h b/include/net/ip.h index 4aa781b7f609..b885d75cede4 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -266,7 +266,8 @@ int ip_dont_fragment(struct sock *sk, struct dst_entry *dst) static inline bool ip_sk_accept_pmtu(const struct sock *sk) { - return inet_sk(sk)->pmtudisc != IP_PMTUDISC_INTERFACE; + return inet_sk(sk)->pmtudisc != IP_PMTUDISC_INTERFACE && + inet_sk(sk)->pmtudisc != IP_PMTUDISC_OMIT; } static inline bool ip_sk_use_pmtu(const struct sock *sk) @@ -274,6 +275,12 @@ static inline bool ip_sk_use_pmtu(const struct sock *sk) return inet_sk(sk)->pmtudisc < IP_PMTUDISC_PROBE; } +static inline bool ip_sk_local_df(const struct sock *sk) +{ + return inet_sk(sk)->pmtudisc < IP_PMTUDISC_DO || + inet_sk(sk)->pmtudisc == IP_PMTUDISC_OMIT; +} + static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst, bool forwarding) { diff --git a/include/uapi/linux/in.h b/include/uapi/linux/in.h index 393c5de09d42..c33a65e3d62c 100644 --- a/include/uapi/linux/in.h +++ b/include/uapi/linux/in.h @@ -120,6 +120,10 @@ struct in_addr { * this socket to prevent accepting spoofed ones. */ #define IP_PMTUDISC_INTERFACE 4 +/* weaker version of IP_PMTUDISC_INTERFACE, which allos packets to get + * fragmented if they exeed the interface mtu + */ +#define IP_PMTUDISC_OMIT 5 #define IP_MULTICAST_IF 32 #define IP_MULTICAST_TTL 33 diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 22aa11971ed1..e85445b2b102 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -824,8 +824,7 @@ static int __ip_append_data(struct sock *sk, fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0); maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen; - maxnonfragsize = (inet->pmtudisc >= IP_PMTUDISC_DO) ? - mtu : 0xFFFF; + maxnonfragsize = ip_sk_local_df(sk) ? 0xFFFF : mtu; if (cork->length + length > maxnonfragsize - fragheaderlen) { ip_local_error(sk, EMSGSIZE, fl4->daddr, inet->inet_dport, @@ -1148,8 +1147,7 @@ ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page, fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0); maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen; - maxnonfragsize = (inet->pmtudisc >= IP_PMTUDISC_DO) ? - mtu : 0xFFFF; + maxnonfragsize = ip_sk_local_df(sk) ? 0xFFFF : mtu; if (cork->length + size > maxnonfragsize - fragheaderlen) { ip_local_error(sk, EMSGSIZE, fl4->daddr, inet->inet_dport, @@ -1310,8 +1308,7 @@ struct sk_buff *__ip_make_skb(struct sock *sk, * to fragment the frame generated here. No matter, what transforms * how transforms change size of the packet, it will come out. */ - if (inet->pmtudisc < IP_PMTUDISC_DO) - skb->local_df = 1; + skb->local_df = ip_sk_local_df(sk); /* DF bit is set when we want to see DF on outgoing frames. * If local_df is set too, we still allow to fragment this frame diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 0968b28c4cf3..64741b938632 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -643,7 +643,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, inet->nodefrag = val ? 1 : 0; break; case IP_MTU_DISCOVER: - if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_INTERFACE) + if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_OMIT) goto e_inval; inet->pmtudisc = val; break; -- cgit v1.2.3-71-gd317 From 0b95227a7ba7e69f795757cd7c839eff0615f2d1 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Wed, 26 Feb 2014 01:20:43 +0100 Subject: ipv6: yet another new IPV6_MTU_DISCOVER option IPV6_PMTUDISC_OMIT This option has the same semantic as IP_PMTUDISC_OMIT for IPv4 which got recently introduced. It doesn't honor the path mtu discovered by the host but in contrary to IPV6_PMTUDISC_INTERFACE allows the generation of fragments if the packet size exceeds the MTU of the outgoing interface MTU. Fixes: 93b36cf3425b9b ("ipv6: support IPV6_PMTU_INTERFACE on sockets") Cc: Florian Weimer Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/net/ip6_route.h | 9 ++++++++- include/uapi/linux/in6.h | 4 ++++ net/ipv6/ip6_output.c | 9 +++++---- net/ipv6/ipv6_sockglue.c | 2 +- 4 files changed, 18 insertions(+), 6 deletions(-) (limited to 'include/net') diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 017badb1aec7..00e3f12cb2f9 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -171,7 +171,14 @@ static inline int ip6_skb_dst_mtu(struct sk_buff *skb) static inline bool ip6_sk_accept_pmtu(const struct sock *sk) { - return inet6_sk(sk)->pmtudisc != IPV6_PMTUDISC_INTERFACE; + return inet6_sk(sk)->pmtudisc != IPV6_PMTUDISC_INTERFACE && + inet6_sk(sk)->pmtudisc != IPV6_PMTUDISC_OMIT; +} + +static inline bool ip6_sk_local_df(const struct sock *sk) +{ + return inet6_sk(sk)->pmtudisc < IPV6_PMTUDISC_DO || + inet6_sk(sk)->pmtudisc == IPV6_PMTUDISC_OMIT; } static inline struct in6_addr *rt6_nexthop(struct rt6_info *rt) diff --git a/include/uapi/linux/in6.h b/include/uapi/linux/in6.h index e9a1d2d973b6..0d8e0f0342dc 100644 --- a/include/uapi/linux/in6.h +++ b/include/uapi/linux/in6.h @@ -185,6 +185,10 @@ struct in6_flowlabel_req { * also see comments on IP_PMTUDISC_INTERFACE */ #define IPV6_PMTUDISC_INTERFACE 4 +/* weaker version of IPV6_PMTUDISC_INTERFACE, which allows packets to + * get fragmented if they exceed the interface mtu + */ +#define IPV6_PMTUDISC_OMIT 5 /* Flowlabel */ #define IPV6_FLOWLABEL_MGR 32 diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 070a2fae2375..be1b7f5a3a54 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1234,8 +1234,10 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, sizeof(struct frag_hdr) : 0) + rt->rt6i_nfheader_len; - maxnonfragsize = (np->pmtudisc >= IPV6_PMTUDISC_DO) ? - mtu : sizeof(struct ipv6hdr) + IPV6_MAXPLEN; + if (ip6_sk_local_df(sk)) + maxnonfragsize = sizeof(struct ipv6hdr) + IPV6_MAXPLEN; + else + maxnonfragsize = mtu; /* dontfrag active */ if ((cork->length + length > mtu - headersize) && dontfrag && @@ -1543,8 +1545,7 @@ int ip6_push_pending_frames(struct sock *sk) } /* Allow local fragmentation. */ - if (np->pmtudisc < IPV6_PMTUDISC_DO) - skb->local_df = 1; + skb->local_df = ip6_sk_local_df(sk); *final_dst = fl6->daddr; __skb_pull(skb, skb_network_header_len(skb)); diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 0a00f449de5e..edb58aff4ae7 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 < IPV6_PMTUDISC_DONT || val > IPV6_PMTUDISC_INTERFACE) + if (val < IPV6_PMTUDISC_DONT || val > IPV6_PMTUDISC_OMIT) goto e_inval; np->pmtudisc = val; retv = 0; -- cgit v1.2.3-71-gd317 From 740b0f1841f6e39085b711d41db9ffb07198682b Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 26 Feb 2014 14:02:48 -0800 Subject: tcp: switch rtt estimations to usec resolution Upcoming congestion controls for TCP require usec resolution for RTT estimations. Millisecond resolution is simply not enough these days. FQ/pacing in DC environments also require this change for finer control and removal of bimodal behavior due to the current hack in tcp_update_pacing_rate() for 'small rtt' TCP_CONG_RTT_STAMP is no longer needed. As Julian Anastasov pointed out, we need to keep user compatibility : tcp_metrics used to export RTT and RTTVAR in msec resolution, so we added RTT_US and RTTVAR_US. An iproute2 patch is needed to use the new attributes if provided by the kernel. In this example ss command displays a srtt of 32 usecs (10Gbit link) lpk51:~# ./ss -i dst lpk52 Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port tcp ESTAB 0 1 10.246.11.51:42959 10.246.11.52:64614 cubic wscale:6,6 rto:201 rtt:0.032/0.001 ato:40 mss:1448 cwnd:10 send 3620.0Mbps pacing_rate 7240.0Mbps unacked:1 rcv_rtt:993 rcv_space:29559 Updated iproute2 ip command displays : lpk51:~# ./ip tcp_metrics | grep 10.246.11.52 10.246.11.52 age 561.914sec cwnd 10 rtt 274us rttvar 213us source 10.246.11.51 Old binary displays : lpk51:~# ip tcp_metrics | grep 10.246.11.52 10.246.11.52 age 561.914sec cwnd 10 rtt 250us rttvar 125us source 10.246.11.51 With help from Julian Anastasov, Stephen Hemminger and Yuchung Cheng Signed-off-by: Eric Dumazet Acked-by: Neal Cardwell Cc: Stephen Hemminger Cc: Yuchung Cheng Cc: Larry Brakmo Cc: Julian Anastasov Signed-off-by: David S. Miller --- include/linux/tcp.h | 8 +- include/net/tcp.h | 10 ++- include/uapi/linux/tcp_metrics.h | 7 +- net/ipv4/tcp.c | 8 +- net/ipv4/tcp_cubic.c | 4 - net/ipv4/tcp_hybla.c | 12 +-- net/ipv4/tcp_illinois.c | 1 - net/ipv4/tcp_input.c | 183 +++++++++++++++++++-------------------- net/ipv4/tcp_ipv4.c | 2 +- net/ipv4/tcp_lp.c | 1 - net/ipv4/tcp_metrics.c | 83 ++++++++++-------- net/ipv4/tcp_minisocks.c | 4 +- net/ipv4/tcp_output.c | 15 ++-- net/ipv4/tcp_probe.c | 2 +- net/ipv4/tcp_vegas.c | 1 - net/ipv4/tcp_veno.c | 1 - net/ipv4/tcp_yeah.c | 1 - 17 files changed, 174 insertions(+), 169 deletions(-) (limited to 'include/net') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 4ad0706d40eb..239946868142 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -201,10 +201,10 @@ struct tcp_sock { u32 tlp_high_seq; /* snd_nxt at the time of TLP retransmit. */ /* RTT measurement */ - u32 srtt; /* smoothed round trip time << 3 */ - u32 mdev; /* medium deviation */ - u32 mdev_max; /* maximal mdev for the last rtt period */ - u32 rttvar; /* smoothed mdev_max */ + u32 srtt_us; /* smoothed round trip time << 3 in usecs */ + u32 mdev_us; /* medium deviation */ + u32 mdev_max_us; /* maximal mdev for the last rtt period */ + u32 rttvar_us; /* smoothed mdev_max */ u32 rtt_seq; /* sequence number to update rttvar */ u32 packets_out; /* Packets which are "in flight" */ diff --git a/include/net/tcp.h b/include/net/tcp.h index 1f820537741a..93eab0b9da60 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -478,7 +479,6 @@ int __cookie_v4_check(const struct iphdr *iph, const struct tcphdr *th, struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, struct ip_options *opt); #ifdef CONFIG_SYN_COOKIES -#include /* Syncookies use a monotonic timer which increments every 64 seconds. * This counter is used both as a hash input and partially encoded into @@ -619,7 +619,7 @@ static inline void tcp_bound_rto(const struct sock *sk) static inline u32 __tcp_set_rto(const struct tcp_sock *tp) { - return (tp->srtt >> 3) + tp->rttvar; + return usecs_to_jiffies((tp->srtt_us >> 3) + tp->rttvar_us); } static inline void __tcp_fast_path_on(struct tcp_sock *tp, u32 snd_wnd) @@ -656,6 +656,11 @@ static inline u32 tcp_rto_min(struct sock *sk) return rto_min; } +static inline u32 tcp_rto_min_us(struct sock *sk) +{ + return jiffies_to_usecs(tcp_rto_min(sk)); +} + /* Compute the actual receive window we are currently advertising. * Rcv_nxt can be after the window if our peer push more data * than the offered window. @@ -778,7 +783,6 @@ enum tcp_ca_event { #define TCP_CA_BUF_MAX (TCP_CA_NAME_MAX*TCP_CA_MAX) #define TCP_CONG_NON_RESTRICTED 0x1 -#define TCP_CONG_RTT_STAMP 0x2 struct tcp_congestion_ops { struct list_head list; diff --git a/include/uapi/linux/tcp_metrics.h b/include/uapi/linux/tcp_metrics.h index 54a37b13f2c4..93533926035c 100644 --- a/include/uapi/linux/tcp_metrics.h +++ b/include/uapi/linux/tcp_metrics.h @@ -11,12 +11,15 @@ #define TCP_METRICS_GENL_VERSION 0x1 enum tcp_metric_index { - TCP_METRIC_RTT, - TCP_METRIC_RTTVAR, + TCP_METRIC_RTT, /* in ms units */ + TCP_METRIC_RTTVAR, /* in ms units */ TCP_METRIC_SSTHRESH, TCP_METRIC_CWND, TCP_METRIC_REORDERING, + TCP_METRIC_RTT_US, /* in usec units */ + TCP_METRIC_RTTVAR_US, /* in usec units */ + /* Always last. */ __TCP_METRIC_MAX, }; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index bed379c7abcd..7374905b3701 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -387,7 +387,7 @@ void tcp_init_sock(struct sock *sk) INIT_LIST_HEAD(&tp->tsq_node); icsk->icsk_rto = TCP_TIMEOUT_INIT; - tp->mdev = TCP_TIMEOUT_INIT; + tp->mdev_us = jiffies_to_usecs(TCP_TIMEOUT_INIT); /* So many TCP implementations out there (incorrectly) count the * initial SYN frame in their delayed-ACK and congestion control @@ -2339,7 +2339,7 @@ int tcp_disconnect(struct sock *sk, int flags) sk->sk_shutdown = 0; sock_reset_flag(sk, SOCK_DONE); - tp->srtt = 0; + tp->srtt_us = 0; if ((tp->write_seq += tp->max_window + 2) == 0) tp->write_seq = 1; icsk->icsk_backoff = 0; @@ -2783,8 +2783,8 @@ void tcp_get_info(const struct sock *sk, struct tcp_info *info) info->tcpi_pmtu = icsk->icsk_pmtu_cookie; info->tcpi_rcv_ssthresh = tp->rcv_ssthresh; - info->tcpi_rtt = jiffies_to_usecs(tp->srtt)>>3; - info->tcpi_rttvar = jiffies_to_usecs(tp->mdev)>>2; + info->tcpi_rtt = tp->srtt_us >> 3; + info->tcpi_rttvar = tp->mdev_us >> 2; info->tcpi_snd_ssthresh = tp->snd_ssthresh; info->tcpi_snd_cwnd = tp->snd_cwnd; info->tcpi_advmss = tp->advmss; diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index 828e4c3ffbaf..8bf224516ba2 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -476,10 +476,6 @@ static int __init cubictcp_register(void) /* divide by bic_scale and by constant Srtt (100ms) */ do_div(cube_factor, bic_scale * 10); - /* hystart needs ms clock resolution */ - if (hystart && HZ < 1000) - cubictcp.flags |= TCP_CONG_RTT_STAMP; - return tcp_register_congestion_control(&cubictcp); } diff --git a/net/ipv4/tcp_hybla.c b/net/ipv4/tcp_hybla.c index 2a1a9e2a4e51..a15a799bf768 100644 --- a/net/ipv4/tcp_hybla.c +++ b/net/ipv4/tcp_hybla.c @@ -21,7 +21,7 @@ struct hybla { u32 rho2; /* Rho * Rho, integer part */ u32 rho_3ls; /* Rho parameter, <<3 */ u32 rho2_7ls; /* Rho^2, <<7 */ - u32 minrtt; /* Minimum smoothed round trip time value seen */ + u32 minrtt_us; /* Minimum smoothed round trip time value seen */ }; /* Hybla reference round trip time (default= 1/40 sec = 25 ms), in ms */ @@ -35,7 +35,9 @@ static inline void hybla_recalc_param (struct sock *sk) { struct hybla *ca = inet_csk_ca(sk); - ca->rho_3ls = max_t(u32, tcp_sk(sk)->srtt / msecs_to_jiffies(rtt0), 8); + ca->rho_3ls = max_t(u32, + tcp_sk(sk)->srtt_us / (rtt0 * USEC_PER_MSEC), + 8U); ca->rho = ca->rho_3ls >> 3; ca->rho2_7ls = (ca->rho_3ls * ca->rho_3ls) << 1; ca->rho2 = ca->rho2_7ls >> 7; @@ -59,7 +61,7 @@ static void hybla_init(struct sock *sk) hybla_recalc_param(sk); /* set minimum rtt as this is the 1st ever seen */ - ca->minrtt = tp->srtt; + ca->minrtt_us = tp->srtt_us; tp->snd_cwnd = ca->rho; } @@ -94,9 +96,9 @@ static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 acked, int is_slowstart = 0; /* Recalculate rho only if this srtt is the lowest */ - if (tp->srtt < ca->minrtt){ + if (tp->srtt_us < ca->minrtt_us) { hybla_recalc_param(sk); - ca->minrtt = tp->srtt; + ca->minrtt_us = tp->srtt_us; } if (!tcp_is_cwnd_limited(sk, in_flight)) diff --git a/net/ipv4/tcp_illinois.c b/net/ipv4/tcp_illinois.c index be047c63ca10..863d105e3015 100644 --- a/net/ipv4/tcp_illinois.c +++ b/net/ipv4/tcp_illinois.c @@ -325,7 +325,6 @@ static void tcp_illinois_info(struct sock *sk, u32 ext, } static struct tcp_congestion_ops tcp_illinois __read_mostly = { - .flags = TCP_CONG_RTT_STAMP, .init = tcp_illinois_init, .ssthresh = tcp_illinois_ssthresh, .cong_avoid = tcp_illinois_cong_avoid, diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 227cba79fa6b..23a41d978fad 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -667,11 +667,11 @@ static void tcp_event_data_recv(struct sock *sk, struct sk_buff *skb) * To save cycles in the RFC 1323 implementation it was better to break * it up into three procedures. -- erics */ -static void tcp_rtt_estimator(struct sock *sk, const __u32 mrtt) +static void tcp_rtt_estimator(struct sock *sk, long mrtt_us) { struct tcp_sock *tp = tcp_sk(sk); - long m = mrtt; /* RTT */ - u32 srtt = tp->srtt; + long m = mrtt_us; /* RTT */ + u32 srtt = tp->srtt_us; /* The following amusing code comes from Jacobson's * article in SIGCOMM '88. Note that rtt and mdev @@ -694,7 +694,7 @@ static void tcp_rtt_estimator(struct sock *sk, const __u32 mrtt) srtt += m; /* rtt = 7/8 rtt + 1/8 new */ if (m < 0) { m = -m; /* m is now abs(error) */ - m -= (tp->mdev >> 2); /* similar update on mdev */ + m -= (tp->mdev_us >> 2); /* similar update on mdev */ /* This is similar to one of Eifel findings. * Eifel blocks mdev updates when rtt decreases. * This solution is a bit different: we use finer gain @@ -706,28 +706,29 @@ static void tcp_rtt_estimator(struct sock *sk, const __u32 mrtt) if (m > 0) m >>= 3; } else { - m -= (tp->mdev >> 2); /* similar update on mdev */ + m -= (tp->mdev_us >> 2); /* similar update on mdev */ } - tp->mdev += m; /* mdev = 3/4 mdev + 1/4 new */ - if (tp->mdev > tp->mdev_max) { - tp->mdev_max = tp->mdev; - if (tp->mdev_max > tp->rttvar) - tp->rttvar = tp->mdev_max; + tp->mdev_us += m; /* mdev = 3/4 mdev + 1/4 new */ + if (tp->mdev_us > tp->mdev_max_us) { + tp->mdev_max_us = tp->mdev_us; + if (tp->mdev_max_us > tp->rttvar_us) + tp->rttvar_us = tp->mdev_max_us; } if (after(tp->snd_una, tp->rtt_seq)) { - if (tp->mdev_max < tp->rttvar) - tp->rttvar -= (tp->rttvar - tp->mdev_max) >> 2; + if (tp->mdev_max_us < tp->rttvar_us) + tp->rttvar_us -= (tp->rttvar_us - tp->mdev_max_us) >> 2; tp->rtt_seq = tp->snd_nxt; - tp->mdev_max = tcp_rto_min(sk); + tp->mdev_max_us = tcp_rto_min_us(sk); } } else { /* no previous measure. */ srtt = m << 3; /* take the measured time to be rtt */ - tp->mdev = m << 1; /* make sure rto = 3*rtt */ - tp->mdev_max = tp->rttvar = max(tp->mdev, tcp_rto_min(sk)); + tp->mdev_us = m << 1; /* make sure rto = 3*rtt */ + tp->rttvar_us = max(tp->mdev_us, tcp_rto_min_us(sk)); + tp->mdev_max_us = tp->rttvar_us; tp->rtt_seq = tp->snd_nxt; } - tp->srtt = max(1U, srtt); + tp->srtt_us = max(1U, srtt); } /* Set the sk_pacing_rate to allow proper sizing of TSO packets. @@ -742,20 +743,12 @@ static void tcp_update_pacing_rate(struct sock *sk) u64 rate; /* set sk_pacing_rate to 200 % of current rate (mss * cwnd / srtt) */ - rate = (u64)tp->mss_cache * 2 * (HZ << 3); + rate = (u64)tp->mss_cache * 2 * (USEC_PER_SEC << 3); rate *= max(tp->snd_cwnd, tp->packets_out); - /* Correction for small srtt and scheduling constraints. - * For small rtt, consider noise is too high, and use - * the minimal value (srtt = 1 -> 125 us for HZ=1000) - * - * We probably need usec resolution in the future. - * Note: This also takes care of possible srtt=0 case, - * when tcp_rtt_estimator() was not yet called. - */ - if (tp->srtt > 8 + 2) - do_div(rate, tp->srtt); + if (likely(tp->srtt_us)) + do_div(rate, tp->srtt_us); /* ACCESS_ONCE() is needed because sch_fq fetches sk_pacing_rate * without any lock. We want to make sure compiler wont store @@ -1122,10 +1115,10 @@ static bool tcp_check_dsack(struct sock *sk, const struct sk_buff *ack_skb, } struct tcp_sacktag_state { - int reord; - int fack_count; - int flag; - s32 rtt; /* RTT measured by SACKing never-retransmitted data */ + int reord; + int fack_count; + long rtt_us; /* RTT measured by SACKing never-retransmitted data */ + int flag; }; /* Check if skb is fully within the SACK block. In presence of GSO skbs, @@ -1186,7 +1179,8 @@ static int tcp_match_skb_to_sack(struct sock *sk, struct sk_buff *skb, static u8 tcp_sacktag_one(struct sock *sk, struct tcp_sacktag_state *state, u8 sacked, u32 start_seq, u32 end_seq, - int dup_sack, int pcount, u32 xmit_time) + int dup_sack, int pcount, + const struct skb_mstamp *xmit_time) { struct tcp_sock *tp = tcp_sk(sk); int fack_count = state->fack_count; @@ -1227,8 +1221,13 @@ static u8 tcp_sacktag_one(struct sock *sk, if (!after(end_seq, tp->high_seq)) state->flag |= FLAG_ORIG_SACK_ACKED; /* Pick the earliest sequence sacked for RTT */ - if (state->rtt < 0) - state->rtt = tcp_time_stamp - xmit_time; + if (state->rtt_us < 0) { + struct skb_mstamp now; + + skb_mstamp_get(&now); + state->rtt_us = skb_mstamp_us_delta(&now, + xmit_time); + } } if (sacked & TCPCB_LOST) { @@ -1287,7 +1286,7 @@ static bool tcp_shifted_skb(struct sock *sk, struct sk_buff *skb, */ tcp_sacktag_one(sk, state, TCP_SKB_CB(skb)->sacked, start_seq, end_seq, dup_sack, pcount, - TCP_SKB_CB(skb)->when); + &skb->skb_mstamp); if (skb == tp->lost_skb_hint) tp->lost_cnt_hint += pcount; @@ -1565,7 +1564,7 @@ static struct sk_buff *tcp_sacktag_walk(struct sk_buff *skb, struct sock *sk, TCP_SKB_CB(skb)->end_seq, dup_sack, tcp_skb_pcount(skb), - TCP_SKB_CB(skb)->when); + &skb->skb_mstamp); if (!before(TCP_SKB_CB(skb)->seq, tcp_highest_sack_seq(tp))) @@ -1622,7 +1621,7 @@ static int tcp_sack_cache_ok(const struct tcp_sock *tp, const struct tcp_sack_bl static int tcp_sacktag_write_queue(struct sock *sk, const struct sk_buff *ack_skb, - u32 prior_snd_una, s32 *sack_rtt) + u32 prior_snd_una, long *sack_rtt_us) { struct tcp_sock *tp = tcp_sk(sk); const unsigned char *ptr = (skb_transport_header(ack_skb) + @@ -1640,7 +1639,7 @@ tcp_sacktag_write_queue(struct sock *sk, const struct sk_buff *ack_skb, state.flag = 0; state.reord = tp->packets_out; - state.rtt = -1; + state.rtt_us = -1L; if (!tp->sacked_out) { if (WARN_ON(tp->fackets_out)) @@ -1824,7 +1823,7 @@ out: WARN_ON((int)tp->retrans_out < 0); WARN_ON((int)tcp_packets_in_flight(tp) < 0); #endif - *sack_rtt = state.rtt; + *sack_rtt_us = state.rtt_us; return state.flag; } @@ -2034,10 +2033,12 @@ static bool tcp_pause_early_retransmit(struct sock *sk, int flag) * available, or RTO is scheduled to fire first. */ if (sysctl_tcp_early_retrans < 2 || sysctl_tcp_early_retrans > 3 || - (flag & FLAG_ECE) || !tp->srtt) + (flag & FLAG_ECE) || !tp->srtt_us) return false; - delay = max_t(unsigned long, (tp->srtt >> 5), msecs_to_jiffies(2)); + delay = max(usecs_to_jiffies(tp->srtt_us >> 5), + msecs_to_jiffies(2)); + if (!time_after(inet_csk(sk)->icsk_timeout, (jiffies + delay))) return false; @@ -2884,7 +2885,7 @@ static void tcp_fastretrans_alert(struct sock *sk, const int acked, } static inline bool tcp_ack_update_rtt(struct sock *sk, const int flag, - s32 seq_rtt, s32 sack_rtt) + long seq_rtt_us, long sack_rtt_us) { const struct tcp_sock *tp = tcp_sk(sk); @@ -2894,10 +2895,10 @@ static inline bool tcp_ack_update_rtt(struct sock *sk, const int flag, * is acked (RFC6298). */ if (flag & FLAG_RETRANS_DATA_ACKED) - seq_rtt = -1; + seq_rtt_us = -1L; - if (seq_rtt < 0) - seq_rtt = sack_rtt; + if (seq_rtt_us < 0) + seq_rtt_us = sack_rtt_us; /* RTTM Rule: A TSecr value received in a segment is used to * update the averaged RTT measurement only if the segment @@ -2905,14 +2906,14 @@ static inline bool tcp_ack_update_rtt(struct sock *sk, const int flag, * left edge of the send window. * See draft-ietf-tcplw-high-performance-00, section 3.3. */ - if (seq_rtt < 0 && tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr && + if (seq_rtt_us < 0 && tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr && flag & FLAG_ACKED) - seq_rtt = tcp_time_stamp - tp->rx_opt.rcv_tsecr; + seq_rtt_us = jiffies_to_usecs(tcp_time_stamp - tp->rx_opt.rcv_tsecr); - if (seq_rtt < 0) + if (seq_rtt_us < 0) return false; - tcp_rtt_estimator(sk, seq_rtt); + tcp_rtt_estimator(sk, seq_rtt_us); tcp_set_rto(sk); /* RFC6298: only reset backoff on valid RTT measurement. */ @@ -2924,16 +2925,16 @@ static inline bool tcp_ack_update_rtt(struct sock *sk, const int flag, static void tcp_synack_rtt_meas(struct sock *sk, const u32 synack_stamp) { struct tcp_sock *tp = tcp_sk(sk); - s32 seq_rtt = -1; + long seq_rtt_us = -1L; if (synack_stamp && !tp->total_retrans) - seq_rtt = tcp_time_stamp - synack_stamp; + seq_rtt_us = jiffies_to_usecs(tcp_time_stamp - synack_stamp); /* If the ACK acks both the SYNACK and the (Fast Open'd) data packets * sent in SYN_RECV, SYNACK RTT is the smooth RTT computed in tcp_ack() */ - if (!tp->srtt) - tcp_ack_update_rtt(sk, FLAG_SYN_ACKED, seq_rtt, -1); + if (!tp->srtt_us) + tcp_ack_update_rtt(sk, FLAG_SYN_ACKED, seq_rtt_us, -1L); } static void tcp_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight) @@ -3022,26 +3023,27 @@ static u32 tcp_tso_acked(struct sock *sk, struct sk_buff *skb) * arrived at the other end. */ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets, - u32 prior_snd_una, s32 sack_rtt) + u32 prior_snd_una, long sack_rtt_us) { - struct tcp_sock *tp = tcp_sk(sk); const struct inet_connection_sock *icsk = inet_csk(sk); - struct sk_buff *skb; - u32 now = tcp_time_stamp; + struct skb_mstamp first_ackt, last_ackt, now; + struct tcp_sock *tp = tcp_sk(sk); + u32 prior_sacked = tp->sacked_out; + u32 reord = tp->packets_out; bool fully_acked = true; - int flag = 0; + long ca_seq_rtt_us = -1L; + long seq_rtt_us = -1L; + struct sk_buff *skb; u32 pkts_acked = 0; - u32 reord = tp->packets_out; - u32 prior_sacked = tp->sacked_out; - s32 seq_rtt = -1; - s32 ca_seq_rtt = -1; - ktime_t last_ackt = net_invalid_timestamp(); bool rtt_update; + int flag = 0; + + first_ackt.v64 = 0; while ((skb = tcp_write_queue_head(sk)) && skb != tcp_send_head(sk)) { struct tcp_skb_cb *scb = TCP_SKB_CB(skb); - u32 acked_pcount; u8 sacked = scb->sacked; + u32 acked_pcount; /* Determine how many packets and what bytes were acked, tso and else */ if (after(scb->end_seq, tp->snd_una)) { @@ -3063,11 +3065,10 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets, tp->retrans_out -= acked_pcount; flag |= FLAG_RETRANS_DATA_ACKED; } else { - ca_seq_rtt = now - scb->when; - last_ackt = skb->tstamp; - if (seq_rtt < 0) { - seq_rtt = ca_seq_rtt; - } + last_ackt = skb->skb_mstamp; + if (!first_ackt.v64) + first_ackt = last_ackt; + if (!(sacked & TCPCB_SACKED_ACKED)) reord = min(pkts_acked, reord); if (!after(scb->end_seq, tp->high_seq)) @@ -3113,7 +3114,13 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets, if (skb && (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED)) flag |= FLAG_SACK_RENEGING; - rtt_update = tcp_ack_update_rtt(sk, flag, seq_rtt, sack_rtt); + skb_mstamp_get(&now); + if (first_ackt.v64) { + seq_rtt_us = skb_mstamp_us_delta(&now, &first_ackt); + ca_seq_rtt_us = skb_mstamp_us_delta(&now, &last_ackt); + } + + rtt_update = tcp_ack_update_rtt(sk, flag, seq_rtt_us, sack_rtt_us); if (flag & FLAG_ACKED) { const struct tcp_congestion_ops *ca_ops @@ -3141,25 +3148,11 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets, tp->fackets_out -= min(pkts_acked, tp->fackets_out); - if (ca_ops->pkts_acked) { - s32 rtt_us = -1; - - /* Is the ACK triggering packet unambiguous? */ - if (!(flag & FLAG_RETRANS_DATA_ACKED)) { - /* High resolution needed and available? */ - if (ca_ops->flags & TCP_CONG_RTT_STAMP && - !ktime_equal(last_ackt, - net_invalid_timestamp())) - rtt_us = ktime_us_delta(ktime_get_real(), - last_ackt); - else if (ca_seq_rtt >= 0) - rtt_us = jiffies_to_usecs(ca_seq_rtt); - } + if (ca_ops->pkts_acked) + ca_ops->pkts_acked(sk, pkts_acked, ca_seq_rtt_us); - ca_ops->pkts_acked(sk, pkts_acked, rtt_us); - } - } else if (skb && rtt_update && sack_rtt >= 0 && - sack_rtt > (s32)(now - TCP_SKB_CB(skb)->when)) { + } else if (skb && rtt_update && sack_rtt_us >= 0 && + sack_rtt_us > skb_mstamp_us_delta(&now, &skb->skb_mstamp)) { /* Do not re-arm RTO if the sack RTT is measured from data sent * after when the head was last (re)transmitted. Otherwise the * timeout may continue to extend in loss recovery. @@ -3369,12 +3362,12 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) u32 ack_seq = TCP_SKB_CB(skb)->seq; u32 ack = TCP_SKB_CB(skb)->ack_seq; bool is_dupack = false; - u32 prior_in_flight, prior_cwnd = tp->snd_cwnd, prior_rtt = tp->srtt; + u32 prior_in_flight; u32 prior_fackets; int prior_packets = tp->packets_out; const int prior_unsacked = tp->packets_out - tp->sacked_out; int acked = 0; /* Number of packets newly acked */ - s32 sack_rtt = -1; + long sack_rtt_us = -1L; /* If the ack is older than previous acks * then we can probably ignore it. @@ -3432,7 +3425,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) if (TCP_SKB_CB(skb)->sacked) flag |= tcp_sacktag_write_queue(sk, skb, prior_snd_una, - &sack_rtt); + &sack_rtt_us); if (TCP_ECN_rcv_ecn_echo(tp, tcp_hdr(skb))) flag |= FLAG_ECE; @@ -3451,7 +3444,8 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) /* See if we can take anything off of the retransmit queue. */ acked = tp->packets_out; - flag |= tcp_clean_rtx_queue(sk, prior_fackets, prior_snd_una, sack_rtt); + flag |= tcp_clean_rtx_queue(sk, prior_fackets, prior_snd_una, + sack_rtt_us); acked -= tp->packets_out; /* Advance cwnd if state allows */ @@ -3474,8 +3468,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) if (icsk->icsk_pending == ICSK_TIME_RETRANS) tcp_schedule_loss_probe(sk); - if (tp->srtt != prior_rtt || tp->snd_cwnd != prior_cwnd) - tcp_update_pacing_rate(sk); + tcp_update_pacing_rate(sk); return 1; no_queue: @@ -3504,7 +3497,7 @@ old_ack: */ if (TCP_SKB_CB(skb)->sacked) { flag |= tcp_sacktag_write_queue(sk, skb, prior_snd_una, - &sack_rtt); + &sack_rtt_us); tcp_fastretrans_alert(sk, acked, prior_unsacked, is_dupack, flag); } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 3cf976510497..17c0fb172fba 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -435,7 +435,7 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) break; icsk->icsk_backoff--; - inet_csk(sk)->icsk_rto = (tp->srtt ? __tcp_set_rto(tp) : + inet_csk(sk)->icsk_rto = (tp->srtt_us ? __tcp_set_rto(tp) : TCP_TIMEOUT_INIT) << icsk->icsk_backoff; tcp_bound_rto(sk); diff --git a/net/ipv4/tcp_lp.c b/net/ipv4/tcp_lp.c index 503798f2fcd6..c9aecae31327 100644 --- a/net/ipv4/tcp_lp.c +++ b/net/ipv4/tcp_lp.c @@ -315,7 +315,6 @@ static void tcp_lp_pkts_acked(struct sock *sk, u32 num_acked, s32 rtt_us) } static struct tcp_congestion_ops tcp_lp __read_mostly = { - .flags = TCP_CONG_RTT_STAMP, .init = tcp_lp_init, .ssthresh = tcp_reno_ssthresh, .cong_avoid = tcp_lp_cong_avoid, diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index d547075d8300..dcaf72f10216 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -33,6 +33,11 @@ struct tcp_fastopen_metrics { struct tcp_fastopen_cookie cookie; }; +/* TCP_METRIC_MAX includes 2 extra fields for userspace compatibility + * Kernel only stores RTT and RTTVAR in usec resolution + */ +#define TCP_METRIC_MAX_KERNEL (TCP_METRIC_MAX - 2) + struct tcp_metrics_block { struct tcp_metrics_block __rcu *tcpm_next; struct inetpeer_addr tcpm_saddr; @@ -41,7 +46,7 @@ struct tcp_metrics_block { u32 tcpm_ts; u32 tcpm_ts_stamp; u32 tcpm_lock; - u32 tcpm_vals[TCP_METRIC_MAX + 1]; + u32 tcpm_vals[TCP_METRIC_MAX_KERNEL + 1]; struct tcp_fastopen_metrics tcpm_fastopen; struct rcu_head rcu_head; @@ -59,12 +64,6 @@ static u32 tcp_metric_get(struct tcp_metrics_block *tm, return tm->tcpm_vals[idx]; } -static u32 tcp_metric_get_jiffies(struct tcp_metrics_block *tm, - enum tcp_metric_index idx) -{ - return msecs_to_jiffies(tm->tcpm_vals[idx]); -} - static void tcp_metric_set(struct tcp_metrics_block *tm, enum tcp_metric_index idx, u32 val) @@ -72,13 +71,6 @@ static void tcp_metric_set(struct tcp_metrics_block *tm, tm->tcpm_vals[idx] = val; } -static void tcp_metric_set_msecs(struct tcp_metrics_block *tm, - enum tcp_metric_index idx, - u32 val) -{ - tm->tcpm_vals[idx] = jiffies_to_msecs(val); -} - static bool addr_same(const struct inetpeer_addr *a, const struct inetpeer_addr *b) { @@ -101,9 +93,11 @@ struct tcpm_hash_bucket { static DEFINE_SPINLOCK(tcp_metrics_lock); -static void tcpm_suck_dst(struct tcp_metrics_block *tm, struct dst_entry *dst, +static void tcpm_suck_dst(struct tcp_metrics_block *tm, + const struct dst_entry *dst, bool fastopen_clear) { + u32 msval; u32 val; tm->tcpm_stamp = jiffies; @@ -121,8 +115,11 @@ static void tcpm_suck_dst(struct tcp_metrics_block *tm, struct dst_entry *dst, val |= 1 << TCP_METRIC_REORDERING; tm->tcpm_lock = val; - tm->tcpm_vals[TCP_METRIC_RTT] = dst_metric_raw(dst, RTAX_RTT); - tm->tcpm_vals[TCP_METRIC_RTTVAR] = dst_metric_raw(dst, RTAX_RTTVAR); + msval = dst_metric_raw(dst, RTAX_RTT); + tm->tcpm_vals[TCP_METRIC_RTT] = msval * USEC_PER_MSEC; + + msval = dst_metric_raw(dst, RTAX_RTTVAR); + tm->tcpm_vals[TCP_METRIC_RTTVAR] = msval * USEC_PER_MSEC; tm->tcpm_vals[TCP_METRIC_SSTHRESH] = dst_metric_raw(dst, RTAX_SSTHRESH); tm->tcpm_vals[TCP_METRIC_CWND] = dst_metric_raw(dst, RTAX_CWND); tm->tcpm_vals[TCP_METRIC_REORDERING] = dst_metric_raw(dst, RTAX_REORDERING); @@ -384,7 +381,7 @@ void tcp_update_metrics(struct sock *sk) dst_confirm(dst); rcu_read_lock(); - if (icsk->icsk_backoff || !tp->srtt) { + if (icsk->icsk_backoff || !tp->srtt_us) { /* This session failed to estimate rtt. Why? * Probably, no packets returned in time. Reset our * results. @@ -399,8 +396,8 @@ void tcp_update_metrics(struct sock *sk) if (!tm) goto out_unlock; - rtt = tcp_metric_get_jiffies(tm, TCP_METRIC_RTT); - m = rtt - tp->srtt; + rtt = tcp_metric_get(tm, TCP_METRIC_RTT); + m = rtt - tp->srtt_us; /* If newly calculated rtt larger than stored one, store new * one. Otherwise, use EWMA. Remember, rtt overestimation is @@ -408,10 +405,10 @@ void tcp_update_metrics(struct sock *sk) */ if (!tcp_metric_locked(tm, TCP_METRIC_RTT)) { if (m <= 0) - rtt = tp->srtt; + rtt = tp->srtt_us; else rtt -= (m >> 3); - tcp_metric_set_msecs(tm, TCP_METRIC_RTT, rtt); + tcp_metric_set(tm, TCP_METRIC_RTT, rtt); } if (!tcp_metric_locked(tm, TCP_METRIC_RTTVAR)) { @@ -422,16 +419,16 @@ void tcp_update_metrics(struct sock *sk) /* Scale deviation to rttvar fixed point */ m >>= 1; - if (m < tp->mdev) - m = tp->mdev; + if (m < tp->mdev_us) + m = tp->mdev_us; - var = tcp_metric_get_jiffies(tm, TCP_METRIC_RTTVAR); + var = tcp_metric_get(tm, TCP_METRIC_RTTVAR); if (m >= var) var = m; else var -= (var - m) >> 2; - tcp_metric_set_msecs(tm, TCP_METRIC_RTTVAR, var); + tcp_metric_set(tm, TCP_METRIC_RTTVAR, var); } if (tcp_in_initial_slowstart(tp)) { @@ -528,7 +525,7 @@ void tcp_init_metrics(struct sock *sk) tp->reordering = val; } - crtt = tcp_metric_get_jiffies(tm, TCP_METRIC_RTT); + crtt = tcp_metric_get(tm, TCP_METRIC_RTT); rcu_read_unlock(); reset: /* The initial RTT measurement from the SYN/SYN-ACK is not ideal @@ -551,18 +548,20 @@ reset: * to low value, and then abruptly stops to do it and starts to delay * ACKs, wait for troubles. */ - if (crtt > tp->srtt) { + if (crtt > tp->srtt_us) { /* Set RTO like tcp_rtt_estimator(), but from cached RTT. */ - crtt >>= 3; + crtt /= 8 * USEC_PER_MSEC; inet_csk(sk)->icsk_rto = crtt + max(2 * crtt, tcp_rto_min(sk)); - } else if (tp->srtt == 0) { + } else if (tp->srtt_us == 0) { /* RFC6298: 5.7 We've failed to get a valid RTT sample from * 3WHS. This is most likely due to retransmission, * including spurious one. Reset the RTO back to 3secs * from the more aggressive 1sec to avoid more spurious * retransmission. */ - tp->mdev = tp->mdev_max = tp->rttvar = TCP_TIMEOUT_FALLBACK; + tp->rttvar_us = jiffies_to_usecs(TCP_TIMEOUT_FALLBACK); + tp->mdev_us = tp->mdev_max_us = tp->rttvar_us; + inet_csk(sk)->icsk_rto = TCP_TIMEOUT_FALLBACK; } /* Cut cwnd down to 1 per RFC5681 if SYN or SYN-ACK has been @@ -809,10 +808,26 @@ static int tcp_metrics_fill_info(struct sk_buff *msg, nest = nla_nest_start(msg, TCP_METRICS_ATTR_VALS); if (!nest) goto nla_put_failure; - for (i = 0; i < TCP_METRIC_MAX + 1; i++) { - if (!tm->tcpm_vals[i]) + for (i = 0; i < TCP_METRIC_MAX_KERNEL + 1; i++) { + u32 val = tm->tcpm_vals[i]; + + if (!val) continue; - if (nla_put_u32(msg, i + 1, tm->tcpm_vals[i]) < 0) + if (i == TCP_METRIC_RTT) { + if (nla_put_u32(msg, TCP_METRIC_RTT_US + 1, + val) < 0) + goto nla_put_failure; + n++; + val = max(val / 1000, 1U); + } + if (i == TCP_METRIC_RTTVAR) { + if (nla_put_u32(msg, TCP_METRIC_RTTVAR_US + 1, + val) < 0) + goto nla_put_failure; + n++; + val = max(val / 1000, 1U); + } + if (nla_put_u32(msg, i + 1, val) < 0) goto nla_put_failure; n++; } diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 7a436c517e44..ca788ada5bd3 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -398,8 +398,8 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, tcp_init_wl(newtp, treq->rcv_isn); - newtp->srtt = 0; - newtp->mdev = TCP_TIMEOUT_INIT; + newtp->srtt_us = 0; + newtp->mdev_us = jiffies_to_usecs(TCP_TIMEOUT_INIT); newicsk->icsk_rto = TCP_TIMEOUT_INIT; newtp->packets_out = 0; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index c5eadec001c1..bf38b1fb63ab 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -866,11 +866,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, if (clone_it) { const struct sk_buff *fclone = skb + 1; - /* If congestion control is doing timestamping, we must - * take such a timestamp before we potentially clone/copy. - */ - if (icsk->icsk_ca_ops->flags & TCP_CONG_RTT_STAMP) - __net_timestamp(skb); + skb_mstamp_get(&skb->skb_mstamp); if (unlikely(skb->fclone == SKB_FCLONE_ORIG && fclone->fclone == SKB_FCLONE_CLONE)) @@ -1974,7 +1970,7 @@ bool tcp_schedule_loss_probe(struct sock *sk) struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); u32 timeout, tlp_time_stamp, rto_time_stamp; - u32 rtt = tp->srtt >> 3; + u32 rtt = usecs_to_jiffies(tp->srtt_us >> 3); if (WARN_ON(icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS)) return false; @@ -1996,7 +1992,7 @@ bool tcp_schedule_loss_probe(struct sock *sk) /* Schedule a loss probe in 2*RTT for SACK capable connections * in Open state, that are either limited by cwnd or application. */ - if (sysctl_tcp_early_retrans < 3 || !tp->srtt || !tp->packets_out || + if (sysctl_tcp_early_retrans < 3 || !tp->srtt_us || !tp->packets_out || !tcp_is_sack(tp) || inet_csk(sk)->icsk_ca_state != TCP_CA_Open) return false; @@ -3050,8 +3046,9 @@ void tcp_send_delayed_ack(struct sock *sk) * Do not use inet_csk(sk)->icsk_rto here, use results of rtt measurements * directly. */ - if (tp->srtt) { - int rtt = max(tp->srtt >> 3, TCP_DELACK_MIN); + if (tp->srtt_us) { + int rtt = max_t(int, usecs_to_jiffies(tp->srtt_us >> 3), + TCP_DELACK_MIN); if (rtt < max_ato) max_ato = rtt; diff --git a/net/ipv4/tcp_probe.c b/net/ipv4/tcp_probe.c index 1f2d37613c9e..3b66610d4156 100644 --- a/net/ipv4/tcp_probe.c +++ b/net/ipv4/tcp_probe.c @@ -154,7 +154,7 @@ static void jtcp_rcv_established(struct sock *sk, struct sk_buff *skb, p->snd_wnd = tp->snd_wnd; p->rcv_wnd = tp->rcv_wnd; p->ssthresh = tcp_current_ssthresh(sk); - p->srtt = tp->srtt >> 3; + p->srtt = tp->srtt_us >> 3; tcp_probe.head = (tcp_probe.head + 1) & (bufsize - 1); } diff --git a/net/ipv4/tcp_vegas.c b/net/ipv4/tcp_vegas.c index a022c17c9cf1..48539fff6357 100644 --- a/net/ipv4/tcp_vegas.c +++ b/net/ipv4/tcp_vegas.c @@ -306,7 +306,6 @@ void tcp_vegas_get_info(struct sock *sk, u32 ext, struct sk_buff *skb) EXPORT_SYMBOL_GPL(tcp_vegas_get_info); static struct tcp_congestion_ops tcp_vegas __read_mostly = { - .flags = TCP_CONG_RTT_STAMP, .init = tcp_vegas_init, .ssthresh = tcp_reno_ssthresh, .cong_avoid = tcp_vegas_cong_avoid, diff --git a/net/ipv4/tcp_veno.c b/net/ipv4/tcp_veno.c index 326475a94865..1b8e28fcd7e1 100644 --- a/net/ipv4/tcp_veno.c +++ b/net/ipv4/tcp_veno.c @@ -203,7 +203,6 @@ static u32 tcp_veno_ssthresh(struct sock *sk) } static struct tcp_congestion_ops tcp_veno __read_mostly = { - .flags = TCP_CONG_RTT_STAMP, .init = tcp_veno_init, .ssthresh = tcp_veno_ssthresh, .cong_avoid = tcp_veno_cong_avoid, diff --git a/net/ipv4/tcp_yeah.c b/net/ipv4/tcp_yeah.c index 8eab02030ed0..5ede0e727945 100644 --- a/net/ipv4/tcp_yeah.c +++ b/net/ipv4/tcp_yeah.c @@ -227,7 +227,6 @@ static u32 tcp_yeah_ssthresh(struct sock *sk) { } static struct tcp_congestion_ops tcp_yeah __read_mostly = { - .flags = TCP_CONG_RTT_STAMP, .init = tcp_yeah_init, .ssthresh = tcp_yeah_ssthresh, .cong_avoid = tcp_yeah_cong_avoid, -- cgit v1.2.3-71-gd317 From b1efcc2870687ec3e3c51fa72210b8e4fa465df8 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Wed, 26 Feb 2014 20:21:40 -0300 Subject: Bluetooth: Create hci_req_add_le_scan_disable helper This patch moves stop LE scanning duplicate code to one single place and reuses it. This will avoid more duplicate code in upcoming patches. Signed-off-by: Andre Guedes Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_core.c | 14 ++++++++++---- net/bluetooth/mgmt.c | 12 ++---------- 3 files changed, 14 insertions(+), 14 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 269c8201a362..bef65d0a14f0 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1129,6 +1129,8 @@ void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen, const void *param, u8 event); void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status); +void hci_req_add_le_scan_disable(struct hci_request *req); + struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen, const void *param, u32 timeout); struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen, diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 669c76ec659a..9a078cf81d3f 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3318,7 +3318,6 @@ static void le_scan_disable_work(struct work_struct *work) { struct hci_dev *hdev = container_of(work, struct hci_dev, le_scan_disable.work); - struct hci_cp_le_set_scan_enable cp; struct hci_request req; int err; @@ -3326,9 +3325,7 @@ static void le_scan_disable_work(struct work_struct *work) hci_req_init(&req, hdev); - memset(&cp, 0, sizeof(cp)); - cp.enable = LE_SCAN_DISABLE; - hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp); + hci_req_add_le_scan_disable(&req); err = hci_req_run(&req, le_scan_disable_work_complete); if (err) @@ -4872,3 +4869,12 @@ static void hci_cmd_work(struct work_struct *work) } } } + +void hci_req_add_le_scan_disable(struct hci_request *req) +{ + struct hci_cp_le_set_scan_enable cp; + + memset(&cp, 0, sizeof(cp)); + cp.enable = LE_SCAN_DISABLE; + hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp); +} diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d6e269287cfc..cfcaf97c998b 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1052,11 +1052,7 @@ static int clean_up_hci_state(struct hci_dev *hdev) disable_advertising(&req); if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) { - struct hci_cp_le_set_scan_enable cp; - - memset(&cp, 0, sizeof(cp)); - cp.enable = LE_SCAN_DISABLE; - hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp); + hci_req_add_le_scan_disable(&req); } list_for_each_entry(conn, &hdev->conn_hash.list, list) { @@ -3527,7 +3523,6 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data, struct hci_cp_remote_name_req_cancel cp; struct inquiry_entry *e; struct hci_request req; - struct hci_cp_le_set_scan_enable enable_cp; int err; BT_DBG("%s", hdev->name); @@ -3563,10 +3558,7 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data, } else { cancel_delayed_work(&hdev->le_scan_disable); - memset(&enable_cp, 0, sizeof(enable_cp)); - enable_cp.enable = LE_SCAN_DISABLE; - hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, - sizeof(enable_cp), &enable_cp); + hci_req_add_le_scan_disable(&req); } break; -- cgit v1.2.3-71-gd317 From 06c053fb54c10be49ef30fc9b6b01e42cc9a1b61 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Wed, 26 Feb 2014 20:21:41 -0300 Subject: Bluetooth: Declare le_conn_failed in hci_core.h This patch adds the "hci_" prefix to le_conn_failed() helper and declares it in hci_core.h so it can be reused in hci_event.c. Signed-off-by: Andre Guedes Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_conn.c | 4 ++-- net/bluetooth/hci_event.c | 6 +----- 3 files changed, 5 insertions(+), 7 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index bef65d0a14f0..4253bdfc2f81 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -653,6 +653,8 @@ int hci_conn_switch_role(struct hci_conn *conn, __u8 role); void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active); +void hci_le_conn_failed(struct hci_conn *conn, u8 status); + /* * hci_conn_get() and hci_conn_put() are used to control the life-time of an * "hci_conn" object. They do not guarantee that the hci_conn object is running, diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 3d6b1cf07d23..dc8aad946426 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -515,7 +515,7 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src) EXPORT_SYMBOL(hci_get_route); /* This function requires the caller holds hdev->lock */ -static void le_conn_failed(struct hci_conn *conn, u8 status) +void hci_le_conn_failed(struct hci_conn *conn, u8 status) { struct hci_dev *hdev = conn->hdev; @@ -545,7 +545,7 @@ static void create_le_conn_complete(struct hci_dev *hdev, u8 status) if (!conn) goto done; - le_conn_failed(conn, status); + hci_le_conn_failed(conn, status); done: hci_dev_unlock(hdev); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 877cee844b9e..eaa69650b1e5 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3658,11 +3658,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) } if (ev->status) { - mgmt_connect_failed(hdev, &conn->dst, conn->type, - conn->dst_type, ev->status); - hci_proto_connect_cfm(conn, ev->status); - conn->state = BT_CLOSED; - hci_conn_del(conn); + hci_le_conn_failed(conn, ev->status); goto unlock; } -- cgit v1.2.3-71-gd317 From 2acf3d9066b36e1b05db42bfe43152eee07a5e9e Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Wed, 26 Feb 2014 20:21:42 -0300 Subject: Bluetooth: Stop scanning on LE connection Some LE controllers don't support scanning and creating a connection at the same time. So we should always stop scanning in order to establish the connection. Since we may prematurely stop the discovery procedure in favor of the connection establishment, we should also cancel hdev->le_scan_ disable delayed work and set the discovery state to DISCOVERY_STOPPED. This change does a small improvement since it is not mandatory the user stops scanning before connecting anymore. Moreover, this change is required by upcoming LE auto connection mechanism in order to work properly with controllers that don't support background scanning and connection establishment at the same time. In future, we might want to do a small optimization by checking if controller is able to scan and connect at the same time. For now, we want the simplest approach so we always stop scanning (even if the controller is able to carry out both operations). Signed-off-by: Andre Guedes Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_conn.c | 92 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 91 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 1bb45a47a78a..c3834d3aecbb 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -356,6 +356,7 @@ enum { /* ---- HCI Error Codes ---- */ #define HCI_ERROR_AUTH_FAILURE 0x05 +#define HCI_ERROR_MEMORY_EXCEEDED 0x07 #define HCI_ERROR_CONNECTION_TIMEOUT 0x08 #define HCI_ERROR_REJ_BAD_ADDR 0x0f #define HCI_ERROR_REMOTE_USER_TERM 0x13 diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index dc8aad946426..2b8bfda3ea35 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -594,12 +594,86 @@ static int hci_create_le_conn(struct hci_conn *conn) return 0; } +static void hci_req_add_le_create_conn(struct hci_request *req, + struct hci_conn *conn) +{ + struct hci_cp_le_create_conn cp; + struct hci_dev *hdev = conn->hdev; + u8 own_addr_type; + + memset(&cp, 0, sizeof(cp)); + + /* Update random address, but set require_privacy to false so + * that we never connect with an unresolvable address. + */ + if (hci_update_random_address(req, false, &own_addr_type)) + return; + + /* Save the address type used for this connnection attempt so we able + * to retrieve this information if we need it. + */ + conn->src_type = own_addr_type; + + cp.scan_interval = cpu_to_le16(hdev->le_scan_interval); + cp.scan_window = cpu_to_le16(hdev->le_scan_window); + bacpy(&cp.peer_addr, &conn->dst); + cp.peer_addr_type = conn->dst_type; + cp.own_address_type = own_addr_type; + cp.conn_interval_min = cpu_to_le16(conn->le_conn_min_interval); + cp.conn_interval_max = cpu_to_le16(conn->le_conn_max_interval); + cp.supervision_timeout = __constant_cpu_to_le16(0x002a); + cp.min_ce_len = __constant_cpu_to_le16(0x0000); + cp.max_ce_len = __constant_cpu_to_le16(0x0000); + + hci_req_add(req, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp); +} + +static void stop_scan_complete(struct hci_dev *hdev, u8 status) +{ + struct hci_request req; + struct hci_conn *conn; + int err; + + conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT); + if (!conn) + return; + + if (status) { + BT_DBG("HCI request failed to stop scanning: status 0x%2.2x", + status); + + hci_dev_lock(hdev); + hci_le_conn_failed(conn, status); + hci_dev_unlock(hdev); + return; + } + + /* Since we may have prematurely stopped discovery procedure, we should + * update discovery state. + */ + cancel_delayed_work(&hdev->le_scan_disable); + hci_discovery_set_state(hdev, DISCOVERY_STOPPED); + + hci_req_init(&req, hdev); + + hci_req_add_le_create_conn(&req, conn); + + err = hci_req_run(&req, create_le_conn_complete); + if (err) { + hci_dev_lock(hdev); + hci_le_conn_failed(conn, HCI_ERROR_MEMORY_EXCEEDED); + hci_dev_unlock(hdev); + return; + } +} + static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, u8 dst_type, u8 sec_level, u8 auth_type) { struct hci_conn_params *params; struct hci_conn *conn; struct smp_irk *irk; + struct hci_request req; int err; if (test_bit(HCI_ADVERTISING, &hdev->flags)) @@ -675,9 +749,23 @@ static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, conn->le_conn_max_interval = hdev->le_conn_max_interval; } - err = hci_create_le_conn(conn); - if (err) + hci_req_init(&req, hdev); + + /* If controller is scanning, we stop it since some controllers are + * not able to scan and connect at the same time. + */ + if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) { + hci_req_add_le_scan_disable(&req); + err = hci_req_run(&req, stop_scan_complete); + } else { + hci_req_add_le_create_conn(&req, conn); + err = hci_req_run(&req, create_le_conn_complete); + } + + if (err) { + hci_conn_del(conn); return ERR_PTR(err); + } done: hci_conn_hold(conn); -- cgit v1.2.3-71-gd317 From 04a6c5898e8cbb46313b7d425001b701f0fa4e3d Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Wed, 26 Feb 2014 20:21:44 -0300 Subject: Bluetooth: Refactor HCI connection code hci_connect() is a very simple and useless wrapper of hci_connect_acl and hci_connect_le functions. Addtionally, all places where hci_connect is called the link type value is passed explicitly. This way, we can safely delete hci_connect, declare hci_connect_acl and hci_connect_le in hci_core.h and call them directly. No functionality is changed by this patch. Signed-off-by: Andre Guedes Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 6 ++++-- net/bluetooth/hci_conn.c | 24 ++++-------------------- net/bluetooth/l2cap_core.c | 7 +++---- net/bluetooth/mgmt.c | 8 ++++---- 4 files changed, 15 insertions(+), 30 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 4253bdfc2f81..20bdb2eafeea 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -641,8 +641,10 @@ void hci_chan_del(struct hci_chan *chan); void hci_chan_list_flush(struct hci_conn *conn); struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle); -struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, - __u8 dst_type, __u8 sec_level, __u8 auth_type); +struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, + u8 dst_type, u8 sec_level, u8 auth_type); +struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst, + u8 sec_level, u8 auth_type); struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst, __u16 setting); int hci_conn_check_link_mode(struct hci_conn *conn); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 296b8ee42451..5c392aaed5a9 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -624,8 +624,8 @@ static void stop_scan_complete(struct hci_dev *hdev, u8 status) } } -static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, - u8 dst_type, u8 sec_level, u8 auth_type) +struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, + u8 dst_type, u8 sec_level, u8 auth_type) { struct hci_conn_params *params; struct hci_conn *conn; @@ -729,8 +729,8 @@ done: return conn; } -static struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst, - u8 sec_level, u8 auth_type) +struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst, + u8 sec_level, u8 auth_type) { struct hci_conn *acl; @@ -799,22 +799,6 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst, return sco; } -/* Create SCO, ACL or LE connection. */ -struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, - __u8 dst_type, __u8 sec_level, __u8 auth_type) -{ - BT_DBG("%s dst %pMR type 0x%x", hdev->name, dst, type); - - switch (type) { - case LE_LINK: - return hci_connect_le(hdev, dst, dst_type, sec_level, auth_type); - case ACL_LINK: - return hci_connect_acl(hdev, dst, sec_level, auth_type); - } - - return ERR_PTR(-EINVAL); -} - /* Check link security requirement */ int hci_conn_check_link_mode(struct hci_conn *conn) { diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index d8d990215158..ab5e2bd113ed 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -7109,11 +7109,10 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, auth_type = l2cap_get_auth_type(chan); if (bdaddr_type_is_le(dst_type)) - hcon = hci_connect(hdev, LE_LINK, dst, dst_type, - chan->sec_level, auth_type); + hcon = hci_connect_le(hdev, dst, dst_type, chan->sec_level, + auth_type); else - hcon = hci_connect(hdev, ACL_LINK, dst, dst_type, - chan->sec_level, auth_type); + hcon = hci_connect_acl(hdev, dst, chan->sec_level, auth_type); if (IS_ERR(hcon)) { err = PTR_ERR(hcon); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index cfcaf97c998b..9fc7c1d9fcbb 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2816,11 +2816,11 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, auth_type = HCI_AT_DEDICATED_BONDING_MITM; if (cp->addr.type == BDADDR_BREDR) - conn = hci_connect(hdev, ACL_LINK, &cp->addr.bdaddr, - cp->addr.type, sec_level, auth_type); + conn = hci_connect_acl(hdev, &cp->addr.bdaddr, sec_level, + auth_type); else - conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr, - cp->addr.type, sec_level, auth_type); + conn = hci_connect_le(hdev, &cp->addr.bdaddr, cp->addr.type, + sec_level, auth_type); if (IS_ERR(conn)) { int status; -- cgit v1.2.3-71-gd317 From 77a77a30ae893a63467c51e45de18d0bdfa612e4 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Wed, 26 Feb 2014 20:21:46 -0300 Subject: Bluetooth: Introduce hdev->pend_le_conn list This patch introduces the hdev->pend_le_conn list which holds the device addresses the kernel should autonomously connect. It also introduces some helper functions to manipulate the list. The list and helper functions will be used by the next patch which implements the LE auto connection infrastructure. Signed-off-by: Andre Guedes Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 7 +++++ net/bluetooth/hci_core.c | 68 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 20bdb2eafeea..e08405d02649 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -284,6 +284,7 @@ struct hci_dev { struct list_head identity_resolving_keys; struct list_head remote_oob_data; struct list_head le_conn_params; + struct list_head pend_le_conns; struct hci_dev_stats stat; @@ -799,6 +800,12 @@ void hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); void hci_conn_params_clear(struct hci_dev *hdev); +struct bdaddr_list *hci_pend_le_conn_lookup(struct hci_dev *hdev, + bdaddr_t *addr, u8 addr_type); +void hci_pend_le_conn_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); +void hci_pend_le_conn_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); +void hci_pend_le_conns_clear(struct hci_dev *hdev); + void hci_uuids_clear(struct hci_dev *hdev); void hci_link_keys_clear(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 9a078cf81d3f..142ecd846ccd 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3259,6 +3259,72 @@ void hci_conn_params_clear(struct hci_dev *hdev) BT_DBG("All LE connection parameters were removed"); } +/* This function requires the caller holds hdev->lock */ +struct bdaddr_list *hci_pend_le_conn_lookup(struct hci_dev *hdev, + bdaddr_t *addr, u8 addr_type) +{ + struct bdaddr_list *entry; + + list_for_each_entry(entry, &hdev->pend_le_conns, list) { + if (bacmp(&entry->bdaddr, addr) == 0 && + entry->bdaddr_type == addr_type) + return entry; + } + + return NULL; +} + +/* This function requires the caller holds hdev->lock */ +void hci_pend_le_conn_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) +{ + struct bdaddr_list *entry; + + entry = hci_pend_le_conn_lookup(hdev, addr, addr_type); + if (entry) + return; + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + BT_ERR("Out of memory"); + return; + } + + bacpy(&entry->bdaddr, addr); + entry->bdaddr_type = addr_type; + + list_add(&entry->list, &hdev->pend_le_conns); + + BT_DBG("addr %pMR (type %u)", addr, addr_type); +} + +/* This function requires the caller holds hdev->lock */ +void hci_pend_le_conn_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) +{ + struct bdaddr_list *entry; + + entry = hci_pend_le_conn_lookup(hdev, addr, addr_type); + if (!entry) + return; + + list_del(&entry->list); + kfree(entry); + + BT_DBG("addr %pMR (type %u)", addr, addr_type); +} + +/* This function requires the caller holds hdev->lock */ +void hci_pend_le_conns_clear(struct hci_dev *hdev) +{ + struct bdaddr_list *entry, *tmp; + + list_for_each_entry_safe(entry, tmp, &hdev->pend_le_conns, list) { + list_del(&entry->list); + kfree(entry); + } + + BT_DBG("All LE pending connections cleared"); +} + static void inquiry_complete(struct hci_dev *hdev, u8 status) { if (status) { @@ -3441,6 +3507,7 @@ struct hci_dev *hci_alloc_dev(void) INIT_LIST_HEAD(&hdev->identity_resolving_keys); INIT_LIST_HEAD(&hdev->remote_oob_data); INIT_LIST_HEAD(&hdev->le_conn_params); + INIT_LIST_HEAD(&hdev->pend_le_conns); INIT_LIST_HEAD(&hdev->conn_hash.list); INIT_WORK(&hdev->rx_work, hci_rx_work); @@ -3642,6 +3709,7 @@ void hci_unregister_dev(struct hci_dev *hdev) hci_smp_irks_clear(hdev); hci_remote_oob_data_clear(hdev); hci_conn_params_clear(hdev); + hci_pend_le_conns_clear(hdev); hci_dev_unlock(hdev); hci_dev_put(hdev); -- cgit v1.2.3-71-gd317 From a4790dbd43d1617b09d57e96494fde5a4b01980a Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Wed, 26 Feb 2014 20:21:47 -0300 Subject: Bluetooth: Introduce LE auto connection infrastructure This patch introduces the LE auto connection infrastructure which will be used to implement the LE auto connection options. In summary, the auto connection mechanism works as follows: Once the first pending LE connection is created, the background scanning is started. When the target device is found in range, the kernel autonomously starts the connection attempt. If connection is established successfully, that pending LE connection is deleted and the background is stopped. To achieve that, this patch introduces the hci_update_background_scan() which controls the background scanning state. This function starts or stops the background scanning based on the hdev->pend_le_conns list. If there is no pending LE connection, the background scanning is stopped. Otherwise, we start the background scanning. Then, every time a pending LE connection is added we call hci_update_ background_scan() so the background scanning is started (in case it is not already running). Likewise, every time a pending LE connection is deleted we call hci_update_background_scan() so the background scanning is stopped (in case this was the last pending LE connection) or it is started again (in case we have more pending LE connections). Finally, we also call hci_update_background_scan() in hci_le_conn_failed() so the background scan is restarted in case the connection establishment fails. This way the background scanning keeps running until all pending LE connection are established. At this point, resolvable addresses are not support by this infrastructure. The proper support is added in upcoming patches. Signed-off-by: Andre Guedes Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 + net/bluetooth/hci_conn.c | 5 +++ net/bluetooth/hci_core.c | 94 +++++++++++++++++++++++++++++++++++++++- net/bluetooth/hci_event.c | 38 ++++++++++++++++ 4 files changed, 137 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index e08405d02649..617cf495a449 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -806,6 +806,8 @@ void hci_pend_le_conn_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); void hci_pend_le_conn_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); void hci_pend_le_conns_clear(struct hci_dev *hdev); +void hci_update_background_scan(struct hci_dev *hdev); + void hci_uuids_clear(struct hci_dev *hdev); void hci_link_keys_clear(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 46b27133740f..7d6f05e3cae8 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -527,6 +527,11 @@ void hci_le_conn_failed(struct hci_conn *conn, u8 status) hci_proto_connect_cfm(conn, status); hci_conn_del(conn); + + /* Since we may have temporarily stopped the background scanning in + * favor of connection establishment, we should restart it. + */ + hci_update_background_scan(hdev); } static void create_le_conn_complete(struct hci_dev *hdev, u8 status) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 142ecd846ccd..9a08f341f0a4 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3281,7 +3281,7 @@ void hci_pend_le_conn_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) entry = hci_pend_le_conn_lookup(hdev, addr, addr_type); if (entry) - return; + goto done; entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (!entry) { @@ -3295,6 +3295,9 @@ void hci_pend_le_conn_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) list_add(&entry->list, &hdev->pend_le_conns); BT_DBG("addr %pMR (type %u)", addr, addr_type); + +done: + hci_update_background_scan(hdev); } /* This function requires the caller holds hdev->lock */ @@ -3304,12 +3307,15 @@ void hci_pend_le_conn_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) entry = hci_pend_le_conn_lookup(hdev, addr, addr_type); if (!entry) - return; + goto done; list_del(&entry->list); kfree(entry); BT_DBG("addr %pMR (type %u)", addr, addr_type); + +done: + hci_update_background_scan(hdev); } /* This function requires the caller holds hdev->lock */ @@ -4946,3 +4952,87 @@ void hci_req_add_le_scan_disable(struct hci_request *req) cp.enable = LE_SCAN_DISABLE; hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp); } + +static void update_background_scan_complete(struct hci_dev *hdev, u8 status) +{ + if (status) + BT_DBG("HCI request failed to update background scanning: " + "status 0x%2.2x", status); +} + +/* This function controls the background scanning based on hdev->pend_le_conns + * list. If there are pending LE connection we start the background scanning, + * otherwise we stop it. + * + * This function requires the caller holds hdev->lock. + */ +void hci_update_background_scan(struct hci_dev *hdev) +{ + struct hci_cp_le_set_scan_param param_cp; + struct hci_cp_le_set_scan_enable enable_cp; + struct hci_request req; + struct hci_conn *conn; + int err; + + hci_req_init(&req, hdev); + + if (list_empty(&hdev->pend_le_conns)) { + /* If there is no pending LE connections, we should stop + * the background scanning. + */ + + /* If controller is not scanning we are done. */ + if (!test_bit(HCI_LE_SCAN, &hdev->dev_flags)) + return; + + hci_req_add_le_scan_disable(&req); + + BT_DBG("%s stopping background scanning", hdev->name); + } else { + u8 own_addr_type; + + /* If there is at least one pending LE connection, we should + * keep the background scan running. + */ + + /* If controller is already scanning we are done. */ + if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) + return; + + /* If controller is connecting, we should not start scanning + * since some controllers are not able to scan and connect at + * the same time. + */ + conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT); + if (conn) + return; + + /* Set require_privacy to true to avoid identification from + * unknown peer devices. Since this is passive scanning, no + * SCAN_REQ using the local identity should be sent. Mandating + * privacy is just an extra precaution. + */ + if (hci_update_random_address(&req, true, &own_addr_type)) + return; + + memset(¶m_cp, 0, sizeof(param_cp)); + param_cp.type = LE_SCAN_PASSIVE; + param_cp.interval = cpu_to_le16(hdev->le_scan_interval); + param_cp.window = cpu_to_le16(hdev->le_scan_window); + param_cp.own_address_type = own_addr_type; + hci_req_add(&req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp), + ¶m_cp); + + memset(&enable_cp, 0, sizeof(enable_cp)); + enable_cp.enable = LE_SCAN_ENABLE; + enable_cp.filter_dup = LE_SCAN_FILTER_DUP_DISABLE; + hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp), + &enable_cp); + + BT_DBG("%s starting background scanning", hdev->name); + } + + err = hci_req_run(&req, update_background_scan_complete); + if (err) + BT_ERR("Failed to run HCI request: err %d", err); +} diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index eaa69650b1e5..b6631d7e2ddf 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3677,25 +3677,63 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_proto_connect_cfm(conn, ev->status); + hci_pend_le_conn_del(hdev, &conn->dst, conn->dst_type); + unlock: hci_dev_unlock(hdev); } +/* This function requires the caller holds hdev->lock */ +static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, + u8 addr_type) +{ + struct hci_conn *conn; + + if (!hci_pend_le_conn_lookup(hdev, addr, addr_type)) + return; + + conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW, + HCI_AT_NO_BONDING); + if (!IS_ERR(conn)) + return; + + switch (PTR_ERR(conn)) { + case -EBUSY: + /* If hci_connect() returns -EBUSY it means there is already + * an LE connection attempt going on. Since controllers don't + * support more than one connection attempt at the time, we + * don't consider this an error case. + */ + break; + default: + BT_DBG("Failed to connect: err %ld", PTR_ERR(conn)); + } +} + static void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb) { u8 num_reports = skb->data[0]; void *ptr = &skb->data[1]; s8 rssi; + hci_dev_lock(hdev); + while (num_reports--) { struct hci_ev_le_advertising_info *ev = ptr; + if (ev->evt_type == LE_ADV_IND || + ev->evt_type == LE_ADV_DIRECT_IND) + check_pending_le_conn(hdev, &ev->bdaddr, + ev->bdaddr_type); + rssi = ev->data[ev->length]; mgmt_device_found(hdev, &ev->bdaddr, LE_LINK, ev->bdaddr_type, NULL, rssi, 0, 1, ev->data, ev->length); ptr += sizeof(*ev) + ev->length + 1; } + + hci_dev_unlock(hdev); } static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb) -- cgit v1.2.3-71-gd317 From 9fcb18ef3acb51e54b6bca6d2d803676ac86813d Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Wed, 26 Feb 2014 20:21:48 -0300 Subject: Bluetooth: Introduce LE auto connect options This patch introduces the LE auto connection options: HCI_AUTO_CONN_ ALWAYS and HCI_AUTO_CONN_LINK_LOSS. Their working mechanism are described as follows: The HCI_AUTO_CONN_ALWAYS option configures the kernel to always re- establish the connection, no matter the reason the connection was terminated. This feature is required by some LE profiles such as HID over GATT, Health Thermometer and Blood Pressure. These profiles require the host autonomously connect to the device as soon as it enters in connectable mode (start advertising) so the device is able to delivery notifications or indications. The BT_AUTO_CONN_LINK_LOSS option configures the kernel to re- establish the connection in case the connection was terminated due to a link loss. This feature is required by the majority of LE profiles such as Proximity, Find Me, Cycling Speed and Cadence and Time. Signed-off-by: Andre Guedes Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 9 ++++++++- net/bluetooth/hci_core.c | 11 +++++++---- net/bluetooth/hci_event.c | 18 ++++++++++++++++++ 3 files changed, 33 insertions(+), 5 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 617cf495a449..b159810f67a6 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -402,6 +402,12 @@ struct hci_conn_params { u16 conn_min_interval; u16 conn_max_interval; + + enum { + HCI_AUTO_CONN_DISABLED, + HCI_AUTO_CONN_ALWAYS, + HCI_AUTO_CONN_LINK_LOSS, + } auto_connect; }; extern struct list_head hci_dev_list; @@ -796,7 +802,8 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); void hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, - u16 conn_min_interval, u16 conn_max_interval); + u8 auto_connect, u16 conn_min_interval, + u16 conn_max_interval); void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); void hci_conn_params_clear(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 9a08f341f0a4..f4224dc58e4d 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3202,7 +3202,8 @@ struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev, /* This function requires the caller holds hdev->lock */ void hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, - u16 conn_min_interval, u16 conn_max_interval) + u8 auto_connect, u16 conn_min_interval, + u16 conn_max_interval) { struct hci_conn_params *params; @@ -3210,6 +3211,7 @@ void hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, if (params) { params->conn_min_interval = conn_min_interval; params->conn_max_interval = conn_max_interval; + params->auto_connect = auto_connect; return; } @@ -3223,12 +3225,13 @@ void hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, params->addr_type = addr_type; params->conn_min_interval = conn_min_interval; params->conn_max_interval = conn_max_interval; + params->auto_connect = auto_connect; list_add(¶ms->list, &hdev->le_conn_params); - BT_DBG("addr %pMR (type %u) conn_min_interval 0x%.4x " - "conn_max_interval 0x%.4x", addr, addr_type, conn_min_interval, - conn_max_interval); + BT_DBG("addr %pMR (type %u) auto_connect %u conn_min_interval 0x%.4x " + "conn_max_interval 0x%.4x", addr, addr_type, auto_connect, + conn_min_interval, conn_max_interval); } /* This function requires the caller holds hdev->lock */ diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index b6631d7e2ddf..46da8b6f4368 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1841,6 +1841,7 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_disconn_complete *ev = (void *) skb->data; u8 reason = hci_to_mgmt_reason(ev->reason); + struct hci_conn_params *params; struct hci_conn *conn; bool mgmt_connected; u8 type; @@ -1868,6 +1869,23 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) if (conn->type == ACL_LINK && conn->flush_key) hci_remove_link_key(hdev, &conn->dst); + params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); + if (params) { + switch (params->auto_connect) { + case HCI_AUTO_CONN_LINK_LOSS: + if (ev->reason != HCI_ERROR_CONNECTION_TIMEOUT) + break; + /* Fall through */ + + case HCI_AUTO_CONN_ALWAYS: + hci_pend_le_conn_add(hdev, &conn->dst, conn->dst_type); + break; + + default: + break; + } + } + type = conn->type; hci_proto_disconn_cfm(conn, ev->reason); -- cgit v1.2.3-71-gd317 From a9b0a04c2aac1e6e41e254221926bdce75321f55 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Wed, 26 Feb 2014 20:21:52 -0300 Subject: Bluetooth: Connection parameters and resolvable address We should only accept connection parameters from identity addresses (public or random static). Thus, we should check the address type in hci_conn_params_add(). Additionally, since the IRK is removed during unpair, we should also remove the connection parameters from that device. Signed-off-by: Andre Guedes Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 6 +++--- net/bluetooth/hci_core.c | 25 +++++++++++++++++++++---- net/bluetooth/mgmt.c | 2 ++ 3 files changed, 26 insertions(+), 7 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b159810f67a6..4b192d0fa76e 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -801,9 +801,9 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); -void hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, - u8 auto_connect, u16 conn_min_interval, - u16 conn_max_interval); +int hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, + u8 auto_connect, u16 conn_min_interval, + u16 conn_max_interval); void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); void hci_conn_params_clear(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 9470a9c14324..6d83ca040970 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3220,13 +3220,28 @@ static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type) return true; } +static bool is_identity_address(bdaddr_t *addr, u8 addr_type) +{ + if (addr_type == ADDR_LE_DEV_PUBLIC) + return true; + + /* Check for Random Static address type */ + if ((addr->b[5] & 0xc0) == 0xc0) + return true; + + return false; +} + /* This function requires the caller holds hdev->lock */ -void hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, - u8 auto_connect, u16 conn_min_interval, - u16 conn_max_interval) +int hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, + u8 auto_connect, u16 conn_min_interval, + u16 conn_max_interval) { struct hci_conn_params *params; + if (!is_identity_address(addr, addr_type)) + return -EINVAL; + params = hci_conn_params_lookup(hdev, addr, addr_type); if (params) goto update; @@ -3234,7 +3249,7 @@ void hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, params = kzalloc(sizeof(*params), GFP_KERNEL); if (!params) { BT_ERR("Out of memory"); - return; + return -ENOMEM; } bacpy(¶ms->addr, addr); @@ -3261,6 +3276,8 @@ update: BT_DBG("addr %pMR (type %u) auto_connect %u conn_min_interval 0x%.4x " "conn_max_interval 0x%.4x", addr, addr_type, auto_connect, conn_min_interval, conn_max_interval); + + return 0; } /* This function requires the caller holds hdev->lock */ diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f878267ba6ab..2e6564e47ded 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2416,6 +2416,8 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data, hci_remove_irk(hdev, &cp->addr.bdaddr, addr_type); + hci_conn_params_del(hdev, &cp->addr.bdaddr, addr_type); + err = hci_remove_ltk(hdev, &cp->addr.bdaddr, addr_type); } -- cgit v1.2.3-71-gd317 From 8ef30fd3d1f08f9ffdf2495907f50f44f2101cd3 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Wed, 26 Feb 2014 20:21:55 -0300 Subject: Bluetooth: Create hci_req_add_le_passive_scan helper This patches creates the public hci_req_add_le_passive_scan helper so it can be re-used outside hci_core.c in the next patch. Signed-off-by: Andre Guedes Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 56 ++++++++++++++++++++++------------------ 2 files changed, 32 insertions(+), 25 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 4b192d0fa76e..79a75edc62d0 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1150,6 +1150,7 @@ void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen, void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status); void hci_req_add_le_scan_disable(struct hci_request *req); +void hci_req_add_le_passive_scan(struct hci_request *req); struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen, const void *param, u32 timeout); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 0b96f20238d8..bbd085d32d78 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -5115,6 +5115,36 @@ void hci_req_add_le_scan_disable(struct hci_request *req) hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp); } +void hci_req_add_le_passive_scan(struct hci_request *req) +{ + struct hci_cp_le_set_scan_param param_cp; + struct hci_cp_le_set_scan_enable enable_cp; + struct hci_dev *hdev = req->hdev; + u8 own_addr_type; + + /* Set require_privacy to true to avoid identification from + * unknown peer devices. Since this is passive scanning, no + * SCAN_REQ using the local identity should be sent. Mandating + * privacy is just an extra precaution. + */ + if (hci_update_random_address(req, true, &own_addr_type)) + return; + + memset(¶m_cp, 0, sizeof(param_cp)); + param_cp.type = LE_SCAN_PASSIVE; + param_cp.interval = cpu_to_le16(hdev->le_scan_interval); + param_cp.window = cpu_to_le16(hdev->le_scan_window); + param_cp.own_address_type = own_addr_type; + hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp), + ¶m_cp); + + memset(&enable_cp, 0, sizeof(enable_cp)); + enable_cp.enable = LE_SCAN_ENABLE; + enable_cp.filter_dup = LE_SCAN_FILTER_DUP_DISABLE; + hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp), + &enable_cp); +} + static void update_background_scan_complete(struct hci_dev *hdev, u8 status) { if (status) @@ -5130,8 +5160,6 @@ static void update_background_scan_complete(struct hci_dev *hdev, u8 status) */ void hci_update_background_scan(struct hci_dev *hdev) { - struct hci_cp_le_set_scan_param param_cp; - struct hci_cp_le_set_scan_enable enable_cp; struct hci_request req; struct hci_conn *conn; int err; @@ -5151,8 +5179,6 @@ void hci_update_background_scan(struct hci_dev *hdev) BT_DBG("%s stopping background scanning", hdev->name); } else { - u8 own_addr_type; - /* If there is at least one pending LE connection, we should * keep the background scan running. */ @@ -5169,27 +5195,7 @@ void hci_update_background_scan(struct hci_dev *hdev) if (conn) return; - /* Set require_privacy to true to avoid identification from - * unknown peer devices. Since this is passive scanning, no - * SCAN_REQ using the local identity should be sent. Mandating - * privacy is just an extra precaution. - */ - if (hci_update_random_address(&req, true, &own_addr_type)) - return; - - memset(¶m_cp, 0, sizeof(param_cp)); - param_cp.type = LE_SCAN_PASSIVE; - param_cp.interval = cpu_to_le16(hdev->le_scan_interval); - param_cp.window = cpu_to_le16(hdev->le_scan_window); - param_cp.own_address_type = own_addr_type; - hci_req_add(&req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp), - ¶m_cp); - - memset(&enable_cp, 0, sizeof(enable_cp)); - enable_cp.enable = LE_SCAN_ENABLE; - enable_cp.filter_dup = LE_SCAN_FILTER_DUP_DISABLE; - hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp), - &enable_cp); + hci_req_add_le_passive_scan(&req); BT_DBG("%s starting background scanning", hdev->name); } -- cgit v1.2.3-71-gd317 From 0768b3b3d228c5acf2075f40f3d25cda30011d4f Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 19 Feb 2014 17:27:06 +0100 Subject: netfilter: nf_tables: add optional user data area to rules This allows us to store user comment strings, but it could be also used to store any kind of information that the user application needs to link to the rule. Scratch 8 bits for the new ulen field that indicates the length the user data area. 4 bits from the handle (so it's 42 bits long, according to Patrick, it would last 139 years with 1000 new rules per second) and 4 bits from dlen (so the expression data area is 4K, which seems sufficient by now even considering the compatibility layer). Signed-off-by: Pablo Neira Ayuso Acked-by: Patrick McHardy --- include/net/netfilter/nf_tables.h | 11 +++++++++-- include/uapi/linux/netfilter/nf_tables.h | 5 ++++- net/netfilter/nf_tables_api.c | 17 +++++++++++++++-- 3 files changed, 28 insertions(+), 5 deletions(-) (limited to 'include/net') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 81abd61500f4..5af56da6d6c6 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -326,13 +326,15 @@ static inline void *nft_expr_priv(const struct nft_expr *expr) * @handle: rule handle * @genmask: generation mask * @dlen: length of expression data + * @ulen: length of user data (used for comments) * @data: expression data */ struct nft_rule { struct list_head list; - u64 handle:46, + u64 handle:42, genmask:2, - dlen:16; + dlen:12, + ulen:8; unsigned char data[] __attribute__((aligned(__alignof__(struct nft_expr)))); }; @@ -371,6 +373,11 @@ static inline struct nft_expr *nft_expr_last(const struct nft_rule *rule) return (struct nft_expr *)&rule->data[rule->dlen]; } +static inline void *nft_userdata(const struct nft_rule *rule) +{ + return (void *)&rule->data[rule->dlen]; +} + /* * The last pointer isn't really necessary, but the compiler isn't able to * determine that the result of nft_expr_last() is always the same since it diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index c84c452c62a7..c88ccbfda5f1 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -1,7 +1,8 @@ #ifndef _LINUX_NF_TABLES_H #define _LINUX_NF_TABLES_H -#define NFT_CHAIN_MAXNAMELEN 32 +#define NFT_CHAIN_MAXNAMELEN 32 +#define NFT_USERDATA_MAXLEN 256 enum nft_registers { NFT_REG_VERDICT, @@ -156,6 +157,7 @@ enum nft_chain_attributes { * @NFTA_RULE_EXPRESSIONS: list of expressions (NLA_NESTED: nft_expr_attributes) * @NFTA_RULE_COMPAT: compatibility specifications of the rule (NLA_NESTED: nft_rule_compat_attributes) * @NFTA_RULE_POSITION: numeric handle of the previous rule (NLA_U64) + * @NFTA_RULE_USERDATA: user data (NLA_BINARY, NFT_USERDATA_MAXLEN) */ enum nft_rule_attributes { NFTA_RULE_UNSPEC, @@ -165,6 +167,7 @@ enum nft_rule_attributes { NFTA_RULE_EXPRESSIONS, NFTA_RULE_COMPAT, NFTA_RULE_POSITION, + NFTA_RULE_USERDATA, __NFTA_RULE_MAX }; #define NFTA_RULE_MAX (__NFTA_RULE_MAX - 1) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 0b5634094cb0..f25d0110fe95 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -1295,6 +1295,8 @@ static const struct nla_policy nft_rule_policy[NFTA_RULE_MAX + 1] = { [NFTA_RULE_EXPRESSIONS] = { .type = NLA_NESTED }, [NFTA_RULE_COMPAT] = { .type = NLA_NESTED }, [NFTA_RULE_POSITION] = { .type = NLA_U64 }, + [NFTA_RULE_USERDATA] = { .type = NLA_BINARY, + .len = NFT_USERDATA_MAXLEN }, }; static int nf_tables_fill_rule_info(struct sk_buff *skb, u32 portid, u32 seq, @@ -1347,6 +1349,10 @@ static int nf_tables_fill_rule_info(struct sk_buff *skb, u32 portid, u32 seq, } nla_nest_end(skb, list); + if (rule->ulen && + nla_put(skb, NFTA_RULE_USERDATA, rule->ulen, nft_userdata(rule))) + goto nla_put_failure; + return nlmsg_end(skb, nlh); nla_put_failure: @@ -1583,7 +1589,7 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, struct nft_expr *expr; struct nft_ctx ctx; struct nlattr *tmp; - unsigned int size, i, n; + unsigned int size, i, n, ulen = 0; int err, rem; bool create; u64 handle, pos_handle; @@ -1649,8 +1655,11 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, } } + if (nla[NFTA_RULE_USERDATA]) + ulen = nla_len(nla[NFTA_RULE_USERDATA]); + err = -ENOMEM; - rule = kzalloc(sizeof(*rule) + size, GFP_KERNEL); + rule = kzalloc(sizeof(*rule) + size + ulen, GFP_KERNEL); if (rule == NULL) goto err1; @@ -1658,6 +1667,10 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, rule->handle = handle; rule->dlen = size; + rule->ulen = ulen; + + if (ulen) + nla_memcpy(nft_userdata(rule), nla[NFTA_RULE_USERDATA], ulen); expr = nft_expr_first(rule); for (i = 0; i < n; i++) { -- cgit v1.2.3-71-gd317 From 56ed2cb88c7370d5aa88c92a2a0b1cb92c0979b9 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 27 Feb 2014 14:05:40 +0200 Subject: Bluetooth: Add tracking of advertising address type To know the real source address for incoming connections (needed e.g. for SMP) we should store the own_address_type parameter that was used for the last HCI_LE_Write_Advertising_Parameters command. This patch adds a proper command complete handler for the command and stores the address type in a new adv_addr_type variable in the hci_dev struct. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_event.c | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 79a75edc62d0..853376df4f99 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -156,6 +156,7 @@ struct hci_dev { bdaddr_t bdaddr; bdaddr_t random_addr; bdaddr_t static_addr; + __u8 adv_addr_type; __u8 dev_name[HCI_MAX_NAME_LENGTH]; __u8 short_name[HCI_MAX_SHORT_NAME_LENGTH]; __u8 eir[HCI_MAX_EIR_LENGTH]; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index cda92db2a9fc..f26e91f72930 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1078,6 +1078,25 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev, } } +static void hci_cc_set_adv_param(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_cp_le_set_adv_param *cp; + u8 status = *((u8 *) skb->data); + + BT_DBG("%s status 0x%2.2x", hdev->name, status); + + if (status) + return; + + cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_ADV_PARAM); + if (!cp) + return; + + hci_dev_lock(hdev); + hdev->adv_addr_type = cp->own_address_type; + hci_dev_unlock(hdev); +} + static void hci_cc_write_remote_amp_assoc(struct hci_dev *hdev, struct sk_buff *skb) { @@ -2367,6 +2386,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_write_le_host_supported(hdev, skb); break; + case HCI_OP_LE_SET_ADV_PARAM: + hci_cc_set_adv_param(hdev, skb); + break; + case HCI_OP_WRITE_REMOTE_AMP_ASSOC: hci_cc_write_remote_amp_assoc(hdev, skb); break; -- cgit v1.2.3-71-gd317 From a1f4c3188bb4d51a41d2026ee08a578f56c61e47 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 27 Feb 2014 14:05:41 +0200 Subject: Bluetooth: Add hci_copy_identity_address convenience function The number of places needing the local Identity Address are starting to grow so it's better to have a single place for the logic of determining it. This patch adds a convenience function for getting the Identity Address and updates the two current places needing this to use it. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_core.c | 35 +++++++++++++++++++++++++---------- net/bluetooth/hci_event.c | 17 +---------------- 3 files changed, 28 insertions(+), 26 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 853376df4f99..093d05eeb3fa 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1292,6 +1292,8 @@ void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8], int hci_update_random_address(struct hci_request *req, bool require_privacy, u8 *own_addr_type); +void hci_copy_identity_address(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 *bdaddr_type); #define SCO_AIRMODE_MASK 0x0003 #define SCO_AIRMODE_CVSD 0x0000 diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index bbd085d32d78..7113d4cc085f 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -582,21 +582,14 @@ DEFINE_SIMPLE_ATTRIBUTE(sniff_max_interval_fops, sniff_max_interval_get, static int identity_show(struct seq_file *f, void *p) { struct hci_dev *hdev = f->private; - bdaddr_t *addr; + bdaddr_t addr; u8 addr_type; hci_dev_lock(hdev); - if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags) || - !bacmp(&hdev->bdaddr, BDADDR_ANY)) { - addr = &hdev->static_addr; - addr_type = ADDR_LE_DEV_RANDOM; - } else { - addr = &hdev->bdaddr; - addr_type = ADDR_LE_DEV_PUBLIC; - } + hci_copy_identity_address(hdev, &addr, &addr_type); - seq_printf(f, "%pMR (type %u) %*phN %pMR\n", addr, addr_type, + seq_printf(f, "%pMR (type %u) %*phN %pMR\n", &addr, addr_type, 16, hdev->irk, &hdev->rpa); hci_dev_unlock(hdev); @@ -3636,6 +3629,28 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy, return 0; } +/* Copy the Identity Address of the controller. + * + * If the controller has a public BD_ADDR, then by default use that one. + * If this is a LE only controller without a public address, default to + * the static random address. + * + * For debugging purposes it is possible to force controllers with a + * public address to use the static random address instead. + */ +void hci_copy_identity_address(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 *bdaddr_type) +{ + if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags) || + !bacmp(&hdev->bdaddr, BDADDR_ANY)) { + bacpy(bdaddr, &hdev->static_addr); + *bdaddr_type = ADDR_LE_DEV_RANDOM; + } else { + bacpy(bdaddr, &hdev->bdaddr); + *bdaddr_type = ADDR_LE_DEV_PUBLIC; + } +} + /* Alloc HCI device */ struct hci_dev *hci_alloc_dev(void) { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index f26e91f72930..162235633bf5 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3665,23 +3665,8 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) /* Ensure that the hci_conn contains the identity address type * regardless of which address the connection was made with. - * - * If the controller has a public BD_ADDR, then by default - * use that one. If this is a LE only controller without - * a public address, default to the static random address. - * - * For debugging purposes it is possible to force - * controllers with a public address to use the static - * random address instead. */ - if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags) || - !bacmp(&hdev->bdaddr, BDADDR_ANY)) { - bacpy(&conn->src, &hdev->static_addr); - conn->src_type = ADDR_LE_DEV_RANDOM; - } else { - bacpy(&conn->src, &hdev->bdaddr); - conn->src_type = ADDR_LE_DEV_PUBLIC; - } + hci_copy_identity_address(hdev, &conn->src, &conn->src_type); /* Lookup the identity address from the stored connection * address and address type. -- cgit v1.2.3-71-gd317 From bc861959103cb5ca43157db6ddb83d1e17e9c38a Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Thu, 27 Feb 2014 14:20:29 +0100 Subject: ipv6: addrconf: silence sparse endianness warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid the following sparse __CHECK_ENDIAN__ warnings: include/net/addrconf.h:318:25: warning: restricted __be64 degrades to integer include/net/addrconf.h:318:70: warning: restricted __be64 degrades to integer include/net/addrconf.h:330:25: warning: restricted __be64 degrades to integer include/net/addrconf.h:330:70: warning: restricted __be64 degrades to integer include/net/addrconf.h:347:25: warning: restricted __be64 degrades to integer include/net/addrconf.h:348:26: warning: restricted __be64 degrades to integer include/net/addrconf.h:349:18: warning: restricted __be64 degrades to integer The warnings are false but they make it harder to spot real bugs. Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- include/net/addrconf.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include/net') diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 50e39a8822b4..933a9f22a05f 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -314,7 +314,7 @@ static inline bool ipv6_addr_is_multicast(const struct in6_addr *addr) static inline bool ipv6_addr_is_ll_all_nodes(const struct in6_addr *addr) { #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64 - __u64 *p = (__u64 *)addr; + __be64 *p = (__be64 *)addr; return ((p[0] ^ cpu_to_be64(0xff02000000000000UL)) | (p[1] ^ cpu_to_be64(1))) == 0UL; #else return ((addr->s6_addr32[0] ^ htonl(0xff020000)) | @@ -326,7 +326,7 @@ static inline bool ipv6_addr_is_ll_all_nodes(const struct in6_addr *addr) static inline bool ipv6_addr_is_ll_all_routers(const struct in6_addr *addr) { #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64 - __u64 *p = (__u64 *)addr; + __be64 *p = (__be64 *)addr; return ((p[0] ^ cpu_to_be64(0xff02000000000000UL)) | (p[1] ^ cpu_to_be64(2))) == 0UL; #else return ((addr->s6_addr32[0] ^ htonl(0xff020000)) | @@ -343,7 +343,7 @@ static inline bool ipv6_addr_is_isatap(const struct in6_addr *addr) static inline bool ipv6_addr_is_solict_mult(const struct in6_addr *addr) { #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64 - __u64 *p = (__u64 *)addr; + __be64 *p = (__be64 *)addr; return ((p[0] ^ cpu_to_be64(0xff02000000000000UL)) | ((p[1] ^ cpu_to_be64(0x00000001ff000000UL)) & cpu_to_be64(0xffffffffff000000UL))) == 0UL; -- cgit v1.2.3-71-gd317 From c9507490ab1769a808fcb4af1a27bd738f4b0407 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 27 Feb 2014 19:35:54 -0800 Subject: Bluetooth: Make hci_blacklist_clear function static The hci_blacklist_clear function is not used outside of hci_core.c and can be made static. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 1 - net/bluetooth/hci_core.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 093d05eeb3fa..9493da8f7d83 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -796,7 +796,6 @@ int hci_inquiry(void __user *arg); struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); -void hci_blacklist_clear(struct hci_dev *hdev); int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 7113d4cc085f..75cf447ca000 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3238,7 +3238,7 @@ struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, return NULL; } -void hci_blacklist_clear(struct hci_dev *hdev) +static void hci_blacklist_clear(struct hci_dev *hdev) { struct list_head *p, *n; -- cgit v1.2.3-71-gd317 From d9a7b0a53f898176b31f6a560e487880a2353136 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 27 Feb 2014 20:37:28 -0800 Subject: Bluetooth: Add definitions for LE white list HCI commands Add the definitions for clearing the LE white list, adding entries to the LE white list and removing entries from the LE white list. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index c3834d3aecbb..bb3f4926d4e3 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1205,6 +1205,20 @@ struct hci_rp_le_read_white_list_size { __u8 size; } __packed; +#define HCI_OP_LE_CLEAR_WHITE_LIST 0x2010 + +#define HCI_OP_LE_ADD_TO_WHITE_LIST 0x2011 +struct hci_cp_le_add_to_white_list { + __u8 bdaddr_type; + bdaddr_t bdaddr; +} __packed; + +#define HCI_OP_LE_DEL_FROM_WHITE_LIST 0x2012 +struct hci_cp_le_del_from_white_list { + __u8 bdaddr_type; + bdaddr_t bdaddr; +} __packed; + #define HCI_OP_LE_CONN_UPDATE 0x2013 struct hci_cp_le_conn_update { __le16 handle; -- cgit v1.2.3-71-gd317 From d2ab0ac18df8735fb1431e63446e803dcd2e7326 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 27 Feb 2014 20:37:30 -0800 Subject: Bluetooth: Add support for storing LE white list entries The current LE white list entries require storing in the HCI controller structure. So provide a storage and access functions for it. In addition export the current list via debugfs. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 7 ++++ net/bluetooth/hci_core.c | 90 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 9493da8f7d83..571168811ecd 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -284,6 +284,7 @@ struct hci_dev { struct list_head long_term_keys; struct list_head identity_resolving_keys; struct list_head remote_oob_data; + struct list_head le_white_list; struct list_head le_conn_params; struct list_head pend_le_conns; @@ -799,6 +800,12 @@ struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); +struct bdaddr_list *hci_white_list_lookup(struct hci_dev *hdev, + bdaddr_t *bdaddr, u8 type); +void hci_white_list_clear(struct hci_dev *hdev); +int hci_white_list_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); +int hci_white_list_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); + struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); int hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index ab547277f909..a9ff1cbe2c41 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -702,6 +702,31 @@ static const struct file_operations force_static_address_fops = { .llseek = default_llseek, }; +static int white_list_show(struct seq_file *f, void *ptr) +{ + struct hci_dev *hdev = f->private; + struct bdaddr_list *b; + + hci_dev_lock(hdev); + list_for_each_entry(b, &hdev->le_white_list, list) + seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type); + hci_dev_unlock(hdev); + + return 0; +} + +static int white_list_open(struct inode *inode, struct file *file) +{ + return single_open(file, white_list_show, inode->i_private); +} + +static const struct file_operations white_list_fops = { + .open = white_list_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static int identity_resolving_keys_show(struct seq_file *f, void *ptr) { struct hci_dev *hdev = f->private; @@ -1786,6 +1811,8 @@ static int __hci_init(struct hci_dev *hdev) debugfs_create_u8("white_list_size", 0444, hdev->debugfs, &hdev->le_white_list_size); + debugfs_create_file("white_list", 0444, hdev->debugfs, hdev, + &white_list_fops); debugfs_create_file("identity_resolving_keys", 0400, hdev->debugfs, hdev, &identity_resolving_keys_fops); @@ -3294,6 +3321,67 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) return mgmt_device_unblocked(hdev, bdaddr, type); } +struct bdaddr_list *hci_white_list_lookup(struct hci_dev *hdev, + bdaddr_t *bdaddr, u8 type) +{ + struct bdaddr_list *b; + + list_for_each_entry(b, &hdev->le_white_list, list) { + if (!bacmp(&b->bdaddr, bdaddr) && b->bdaddr_type == type) + return b; + } + + return NULL; +} + +void hci_white_list_clear(struct hci_dev *hdev) +{ + struct list_head *p, *n; + + list_for_each_safe(p, n, &hdev->le_white_list) { + struct bdaddr_list *b = list_entry(p, struct bdaddr_list, list); + + list_del(p); + kfree(b); + } +} + +int hci_white_list_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) +{ + struct bdaddr_list *entry; + + if (!bacmp(bdaddr, BDADDR_ANY)) + return -EBADF; + + entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + bacpy(&entry->bdaddr, bdaddr); + entry->bdaddr_type = type; + + list_add(&entry->list, &hdev->le_white_list); + + return 0; +} + +int hci_white_list_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) +{ + struct bdaddr_list *entry; + + if (!bacmp(bdaddr, BDADDR_ANY)) + return -EBADF; + + entry = hci_white_list_lookup(hdev, bdaddr, type); + if (!entry) + return -ENOENT; + + list_del(&entry->list); + kfree(entry); + + return 0; +} + /* This function requires the caller holds hdev->lock */ struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) @@ -3692,6 +3780,7 @@ struct hci_dev *hci_alloc_dev(void) INIT_LIST_HEAD(&hdev->long_term_keys); INIT_LIST_HEAD(&hdev->identity_resolving_keys); INIT_LIST_HEAD(&hdev->remote_oob_data); + INIT_LIST_HEAD(&hdev->le_white_list); INIT_LIST_HEAD(&hdev->le_conn_params); INIT_LIST_HEAD(&hdev->pend_le_conns); INIT_LIST_HEAD(&hdev->conn_hash.list); @@ -3894,6 +3983,7 @@ void hci_unregister_dev(struct hci_dev *hdev) hci_smp_ltks_clear(hdev); hci_smp_irks_clear(hdev); hci_remote_oob_data_clear(hdev); + hci_white_list_clear(hdev); hci_conn_params_clear(hdev); hci_pend_le_conns_clear(hdev); hci_dev_unlock(hdev); -- cgit v1.2.3-71-gd317 From a3172b7eb4a2719711187cfca12097d2326e85a7 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 28 Feb 2014 09:33:44 +0200 Subject: Bluetooth: Add timer to force power off If some of the cleanup commands caused by mgmt_set_powered(off) never complete we should still force the adapter to be powered down. This is rather easy to do since hdev->power_off is already a delayed work struct. This patch schedules this delayed work if at least one HCI command was sent by the cleanup procedure. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 1 + net/bluetooth/mgmt.c | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index bb3f4926d4e3..35ef60febd57 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -182,6 +182,7 @@ enum { #define HCI_CMD_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ #define HCI_ACL_TX_TIMEOUT msecs_to_jiffies(45000) /* 45 seconds */ #define HCI_AUTO_OFF_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ +#define HCI_POWER_OFF_TIMEOUT msecs_to_jiffies(5000) /* 5 seconds */ /* HCI data types */ #define HCI_COMMAND_PKT 0x01 diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 73b6ff817796..e7c87231b9ea 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1031,8 +1031,10 @@ static void clean_up_hci_complete(struct hci_dev *hdev, u8 status) { BT_DBG("%s status 0x%02x", hdev->name, status); - if (hci_conn_count(hdev) == 0) + if (hci_conn_count(hdev) == 0) { + cancel_delayed_work(&hdev->power_off); queue_work(hdev->req_workqueue, &hdev->power_off.work); + } } static int clean_up_hci_state(struct hci_dev *hdev) @@ -1139,9 +1141,13 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data, } else { /* Disconnect connections, stop scans, etc */ err = clean_up_hci_state(hdev); + if (!err) + queue_delayed_work(hdev->req_workqueue, &hdev->power_off, + HCI_POWER_OFF_TIMEOUT); /* ENODATA means there were no HCI commands queued */ if (err == -ENODATA) { + cancel_delayed_work(&hdev->power_off); queue_work(hdev->req_workqueue, &hdev->power_off.work); err = 0; } @@ -5147,8 +5153,10 @@ void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, /* The connection is still in hci_conn_hash so test for 1 * instead of 0 to know if this is the last one. */ - if (!cp->val && hci_conn_count(hdev) == 1) + if (!cp->val && hci_conn_count(hdev) == 1) { + cancel_delayed_work(&hdev->power_off); queue_work(hdev->req_workqueue, &hdev->power_off.work); + } } if (!mgmt_connected) @@ -5217,8 +5225,10 @@ void mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, /* The connection is still in hci_conn_hash so test for 1 * instead of 0 to know if this is the last one. */ - if (!cp->val && hci_conn_count(hdev) == 1) + if (!cp->val && hci_conn_count(hdev) == 1) { + cancel_delayed_work(&hdev->power_off); queue_work(hdev->req_workqueue, &hdev->power_off.work); + } } bacpy(&ev.addr.bdaddr, bdaddr); -- cgit v1.2.3-71-gd317 From fe39c7b2dacf7fd4dcddc26704d01315ab92b7cb Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 27 Feb 2014 16:00:28 -0800 Subject: Bluetooth: Use __le64 type for LE random numbers The random numbers in Bluetooth Low Energy are 64-bit numbers and should also be little endian since the HCI specification is little endian. Change the whole Low Energy pairing to use __le64 instead of a byte array. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 4 ++-- include/net/bluetooth/hci_core.h | 8 ++++---- include/net/bluetooth/mgmt.h | 2 +- net/bluetooth/hci_conn.c | 6 +++--- net/bluetooth/hci_core.c | 13 ++++++------- net/bluetooth/hci_event.c | 2 +- net/bluetooth/mgmt.c | 2 +- net/bluetooth/smp.c | 22 ++++++++++------------ net/bluetooth/smp.h | 2 +- 9 files changed, 29 insertions(+), 32 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 35ef60febd57..0740fee39c73 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1234,7 +1234,7 @@ struct hci_cp_le_conn_update { #define HCI_OP_LE_START_ENC 0x2019 struct hci_cp_le_start_enc { __le16 handle; - __u8 rand[8]; + __le64 rand; __le16 ediv; __u8 ltk[16]; } __packed; @@ -1646,7 +1646,7 @@ struct hci_ev_le_conn_complete { #define HCI_EV_LE_LTK_REQ 0x05 struct hci_ev_le_ltk_req { __le16 handle; - __u8 random[8]; + __le64 rand; __le16 ediv; } __packed; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 571168811ecd..0c63a7e12d90 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -99,7 +99,7 @@ struct smp_ltk { u8 type; u8 enc_size; __le16 ediv; - u8 rand[8]; + __le64 rand; u8 val[16]; }; @@ -828,11 +828,11 @@ void hci_link_keys_clear(struct hci_dev *hdev); struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len); -struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8], +struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand, bool master); struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type, u8 authenticated, - u8 tk[16], u8 enc_size, __le16 ediv, u8 rand[8]); + u8 tk[16], u8 enc_size, __le16 ediv, __le64 rand); struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, bool master); int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type); @@ -1293,7 +1293,7 @@ struct hci_sec_filter { void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, u16 to_multiplier); -void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8], +void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand, __u8 ltk[16]); int hci_update_random_address(struct hci_request *req, bool require_privacy, diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 62d560624e3d..0326648fd799 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -187,7 +187,7 @@ struct mgmt_ltk_info { __u8 master; __u8 enc_size; __le16 ediv; - __u8 rand[8]; + __le64 rand; __u8 val[16]; } __packed; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 7d6f05e3cae8..5b0802994cbb 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -231,7 +231,7 @@ void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, hci_send_cmd(hdev, HCI_OP_LE_CONN_UPDATE, sizeof(cp), &cp); } -void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8], +void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand, __u8 ltk[16]) { struct hci_dev *hdev = conn->hdev; @@ -242,9 +242,9 @@ void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8], memset(&cp, 0, sizeof(cp)); cp.handle = cpu_to_le16(conn->handle); - memcpy(cp.ltk, ltk, sizeof(cp.ltk)); + cp.rand = rand; cp.ediv = ediv; - memcpy(cp.rand, rand, sizeof(cp.rand)); + memcpy(cp.ltk, ltk, sizeof(cp.ltk)); hci_send_cmd(hdev, HCI_OP_LE_START_ENC, sizeof(cp), &cp); } diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index a9ff1cbe2c41..32c0c2c58f66 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -765,10 +765,10 @@ static int long_term_keys_show(struct seq_file *f, void *ptr) hci_dev_lock(hdev); list_for_each_safe(p, n, &hdev->long_term_keys) { struct smp_ltk *ltk = list_entry(p, struct smp_ltk, list); - seq_printf(f, "%pMR (type %u) %u 0x%02x %u %.4x %*phN %*phN\n", + seq_printf(f, "%pMR (type %u) %u 0x%02x %u %.4x %.16llx %*phN\n", <k->bdaddr, ltk->bdaddr_type, ltk->authenticated, ltk->type, ltk->enc_size, __le16_to_cpu(ltk->ediv), - 8, ltk->rand, 16, ltk->val); + __le64_to_cpu(ltk->rand), 16, ltk->val); } hci_dev_unlock(hdev); @@ -2921,14 +2921,13 @@ static bool ltk_type_master(u8 type) return false; } -struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8], +struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand, bool master) { struct smp_ltk *k; list_for_each_entry(k, &hdev->long_term_keys, list) { - if (k->ediv != ediv || - memcmp(rand, k->rand, sizeof(k->rand))) + if (k->ediv != ediv || k->rand != rand) continue; if (ltk_type_master(k->type) != master) @@ -3046,7 +3045,7 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type, u8 authenticated, - u8 tk[16], u8 enc_size, __le16 ediv, u8 rand[8]) + u8 tk[16], u8 enc_size, __le16 ediv, __le64 rand) { struct smp_ltk *key, *old_key; bool master = ltk_type_master(type); @@ -3066,9 +3065,9 @@ struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, memcpy(key->val, tk, sizeof(key->val)); key->authenticated = authenticated; key->ediv = ediv; + key->rand = rand; key->enc_size = enc_size; key->type = type; - memcpy(key->rand, rand, sizeof(key->rand)); return key; } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 674bfdc3ecc3..e3d7151e808e 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3843,7 +3843,7 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb) if (conn == NULL) goto not_found; - ltk = hci_find_ltk(hdev, ev->ediv, ev->random, conn->out); + ltk = hci_find_ltk(hdev, ev->ediv, ev->rand, conn->out); if (ltk == NULL) goto not_found; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index e7c87231b9ea..2d11c817d082 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5025,11 +5025,11 @@ void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key) ev.key.type = key->authenticated; ev.key.enc_size = key->enc_size; ev.key.ediv = key->ediv; + ev.key.rand = key->rand; if (key->type == HCI_SMP_LTK) ev.key.master = 1; - memcpy(ev.key.rand, key->rand, sizeof(key->rand)); memcpy(ev.key.val, key->val, sizeof(key->val)); mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, &ev, sizeof(ev), NULL); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 0de98fe23330..99abffcaf16b 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -517,11 +517,9 @@ static void random_work(struct work_struct *work) } if (hcon->out) { - u8 stk[16], rand[8]; - __le16 ediv; - - memset(rand, 0, sizeof(rand)); - ediv = 0; + u8 stk[16]; + __le64 rand = 0; + __le16 ediv = 0; smp_s1(tfm, smp->tk, smp->rrnd, smp->prnd, key); swap128(key, stk); @@ -537,11 +535,9 @@ static void random_work(struct work_struct *work) hci_le_start_enc(hcon, ediv, rand, stk); hcon->enc_key_size = smp->enc_key_size; } else { - u8 stk[16], r[16], rand[8]; - __le16 ediv; - - memset(rand, 0, sizeof(rand)); - ediv = 0; + u8 stk[16], r[16]; + __le64 rand = 0; + __le16 ediv = 0; swap128(smp->prnd, r); smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r); @@ -1205,20 +1201,22 @@ int smp_distribute_keys(struct l2cap_conn *conn) struct smp_ltk *ltk; u8 authenticated; __le16 ediv; + __le64 rand; get_random_bytes(enc.ltk, sizeof(enc.ltk)); get_random_bytes(&ediv, sizeof(ediv)); - get_random_bytes(ident.rand, sizeof(ident.rand)); + get_random_bytes(&rand, sizeof(rand)); smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc); authenticated = hcon->sec_level == BT_SECURITY_HIGH; ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, HCI_SMP_LTK_SLAVE, authenticated, enc.ltk, - smp->enc_key_size, ediv, ident.rand); + smp->enc_key_size, ediv, rand); smp->slave_ltk = ltk; ident.ediv = ediv; + ident.rand = rand; smp_send_cmd(conn, SMP_CMD_MASTER_IDENT, sizeof(ident), &ident); diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h index 1b8af35b292c..a11d4281542c 100644 --- a/net/bluetooth/smp.h +++ b/net/bluetooth/smp.h @@ -78,7 +78,7 @@ struct smp_cmd_encrypt_info { #define SMP_CMD_MASTER_IDENT 0x07 struct smp_cmd_master_ident { __le16 ediv; - __u8 rand[8]; + __le64 rand; } __packed; #define SMP_CMD_IDENT_INFO 0x08 -- cgit v1.2.3-71-gd317 From cb1d68f7a337142e283ef7fc78793a57ffb4cdc3 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 28 Feb 2014 12:54:16 +0200 Subject: Bluetooth: Track LE initiator and responder address information For SMP we need the local and remote addresses (and their types) that were used to establish the connection. These may be different from the Identity Addresses or even the current RPA. To guarantee that we have this information available and it is correct track these values separately from the very beginning of the connection. For outgoing connections we set the values as soon as we get a successful command status for HCI_LE_Create_Connection (for which the patch adds a command status handler function) and for incoming connections as soon as we get a LE Connection Complete HCI event. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 4 +++ net/bluetooth/hci_event.c | 78 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 0c63a7e12d90..edf194679b7d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -332,6 +332,10 @@ struct hci_conn { __u8 dst_type; bdaddr_t src; __u8 src_type; + bdaddr_t init_addr; + __u8 init_addr_type; + bdaddr_t resp_addr; + __u8 resp_addr_type; __u16 handle; __u16 state; __u8 mode; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index e3d7151e808e..3ae8ae1a029c 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1641,6 +1641,47 @@ static void hci_cs_accept_phylink(struct hci_dev *hdev, u8 status) amp_write_remote_assoc(hdev, cp->phy_handle); } +static void hci_cs_le_create_conn(struct hci_dev *hdev, u8 status) +{ + struct hci_cp_le_create_conn *cp; + struct hci_conn *conn; + + BT_DBG("%s status 0x%2.2x", hdev->name, status); + + /* All connection failure handling is taken care of by the + * hci_le_conn_failed function which is triggered by the HCI + * request completion callbacks used for connecting. + */ + if (status) + return; + + cp = hci_sent_cmd_data(hdev, HCI_OP_LE_CREATE_CONN); + if (!cp) + return; + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->peer_addr); + if (!conn) + goto unlock; + + /* Store the initiator and responder address information which + * is needed for SMP. These values will not change during the + * lifetime of the connection. + */ + conn->init_addr_type = cp->own_address_type; + if (cp->own_address_type == ADDR_LE_DEV_RANDOM) + bacpy(&conn->init_addr, &hdev->random_addr); + else + bacpy(&conn->init_addr, &hdev->bdaddr); + + conn->resp_addr_type = cp->peer_addr_type; + bacpy(&conn->resp_addr, &cp->peer_addr); + +unlock: + hci_dev_unlock(hdev); +} + static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); @@ -2532,6 +2573,10 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cs_accept_phylink(hdev, ev->status); break; + case HCI_OP_LE_CREATE_CONN: + hci_cs_le_create_conn(hdev, ev->status); + break; + default: BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode); break; @@ -3716,6 +3761,39 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) conn->out = true; conn->link_mode |= HCI_LM_MASTER; } + + /* If we didn't have a hci_conn object previously + * but we're in master role this must be something + * initiated using a white list. Since white list based + * connections are not "first class citizens" we don't + * have full tracking of them. Therefore, we go ahead + * with a "best effort" approach of determining the + * initiator address based on the HCI_PRIVACY flag. + */ + if (conn->out) { + conn->resp_addr_type = ev->bdaddr_type; + bacpy(&conn->resp_addr, &ev->bdaddr); + if (test_bit(HCI_PRIVACY, &hdev->dev_flags)) { + conn->init_addr_type = ADDR_LE_DEV_RANDOM; + bacpy(&conn->init_addr, &hdev->rpa); + } else { + hci_copy_identity_address(hdev, + &conn->init_addr, + &conn->init_addr_type); + } + } else { + /* Set the responder (our side) address type based on + * the advertising address type. + */ + conn->resp_addr_type = hdev->adv_addr_type; + if (hdev->adv_addr_type == ADDR_LE_DEV_RANDOM) + bacpy(&conn->resp_addr, &hdev->random_addr); + else + bacpy(&conn->resp_addr, &hdev->bdaddr); + + conn->init_addr_type = ev->bdaddr_type; + bacpy(&conn->init_addr, &ev->bdaddr); + } } /* Ensure that the hci_conn contains the identity address type -- cgit v1.2.3-71-gd317 From a7139edd28215623e80c998edd34b3f750c5efc6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 28 Feb 2014 17:45:45 +0200 Subject: Bluetooth: Add defines for LE initiator filter policy This patch adds defines for the initiator filter policy parameter values of the HCI_LE_Create_Connection command. They will be used in a subsequent patch to check whether we should have a timeout for the connection attempt or not. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 0740fee39c73..439b4ebf9644 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1182,6 +1182,9 @@ struct hci_cp_le_set_scan_enable { __u8 filter_dup; } __packed; +#define HCI_LE_USE_PEER_ADDR 0x00 +#define HCI_LE_USE_WHITELIST 0x01 + #define HCI_OP_LE_CREATE_CONN 0x200d struct hci_cp_le_create_conn { __le16 scan_interval; -- cgit v1.2.3-71-gd317 From 9489eca4ab2fd5d9bbf3bab992168cc8107fc3e9 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 28 Feb 2014 17:45:46 +0200 Subject: Bluetooth: Add timeout for LE connection attempts LE connection attempts do not have a controller side timeout in the same way as BR/EDR has (in form of the page timeout). Since we always do scanning before initiating connections the attempts are always expected to succeed in some reasonable time. This patch adds a timer which forces a cancellation of the connection attempt within 20 seconds if it has not been successful by then. This way we e.g. ensure that mgmt_pair_device times out eventually and gives an error response. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 1 + include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_conn.c | 13 +++++++++++++ net/bluetooth/hci_event.c | 12 ++++++++++++ 4 files changed, 27 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 439b4ebf9644..0409f0119d2b 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -183,6 +183,7 @@ enum { #define HCI_ACL_TX_TIMEOUT msecs_to_jiffies(45000) /* 45 seconds */ #define HCI_AUTO_OFF_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ #define HCI_POWER_OFF_TIMEOUT msecs_to_jiffies(5000) /* 5 seconds */ +#define HCI_LE_CONN_TIMEOUT msecs_to_jiffies(20000) /* 20 seconds */ /* HCI data types */ #define HCI_COMMAND_PKT 0x01 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index edf194679b7d..dbb788e4f265 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -375,6 +375,7 @@ struct hci_conn { struct delayed_work disc_work; struct delayed_work auto_accept_work; struct delayed_work idle_work; + struct delayed_work le_conn_timeout; struct device dev; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 818330c1b2a2..7e47e4240c95 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -363,6 +363,16 @@ static void hci_conn_auto_accept(struct work_struct *work) &conn->dst); } +static void le_conn_timeout(struct work_struct *work) +{ + struct hci_conn *conn = container_of(work, struct hci_conn, + le_conn_timeout.work); + + BT_DBG(""); + + hci_le_create_connection_cancel(conn); +} + struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) { struct hci_conn *conn; @@ -410,6 +420,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) INIT_DELAYED_WORK(&conn->disc_work, hci_conn_timeout); INIT_DELAYED_WORK(&conn->auto_accept_work, hci_conn_auto_accept); INIT_DELAYED_WORK(&conn->idle_work, hci_conn_idle); + INIT_DELAYED_WORK(&conn->le_conn_timeout, le_conn_timeout); atomic_set(&conn->refcnt, 0); @@ -442,6 +453,8 @@ int hci_conn_del(struct hci_conn *conn) /* Unacked frames */ hdev->acl_cnt += conn->sent; } else if (conn->type == LE_LINK) { + cancel_delayed_work_sync(&conn->le_conn_timeout); + if (hdev->le_pkts) hdev->le_cnt += conn->sent; else diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 3ae8ae1a029c..a1075c713a9d 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1678,6 +1678,16 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, u8 status) conn->resp_addr_type = cp->peer_addr_type; bacpy(&conn->resp_addr, &cp->peer_addr); + /* We don't want the connection attempt to stick around + * indefinitely since LE doesn't have a page timeout concept + * like BR/EDR. Set a timer for any connection that doesn't use + * the white list for connecting. + */ + if (cp->filter_policy == HCI_LE_USE_PEER_ADDR) + queue_delayed_work(conn->hdev->workqueue, + &conn->le_conn_timeout, + HCI_LE_CONN_TIMEOUT); + unlock: hci_dev_unlock(hdev); } @@ -3794,6 +3804,8 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) conn->init_addr_type = ev->bdaddr_type; bacpy(&conn->init_addr, &ev->bdaddr); } + } else { + cancel_delayed_work(&conn->le_conn_timeout); } /* Ensure that the hci_conn contains the identity address type -- cgit v1.2.3-71-gd317 From 81ad6fd9698f659dbabdc6cd3e1667a98eb2be3b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 28 Feb 2014 20:26:13 +0200 Subject: Bluetooth: Remove unnecessary stop_scan_complete function The stop_scan_complete function was used as an intermediate step before doing the actual connection creation. Since we're using hci_request there's no reason to have this extra function around, i.e. we can simply put both HCI commands into the same request. The single task that the intermediate function had, i.e. indicating discovery as stopped is now taken care of by a new HCI_LE_SCAN_INTERRUPTED flag which allows us to do the discovery state update when the stop scan command completes. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_conn.c | 51 +++++++-------------------------------------- net/bluetooth/hci_event.c | 7 +++++++ 3 files changed, 16 insertions(+), 43 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 0409f0119d2b..be150cf8cd43 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -140,6 +140,7 @@ enum { HCI_FAST_CONNECTABLE, HCI_BREDR_ENABLED, HCI_6LOWPAN_ENABLED, + HCI_LE_SCAN_INTERRUPTED, }; /* A mask for the flags that are supposed to remain when a reset happens diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 5330fcfde93d..7c713c4675ba 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -605,44 +605,6 @@ static void hci_req_add_le_create_conn(struct hci_request *req, conn->state = BT_CONNECT; } -static void stop_scan_complete(struct hci_dev *hdev, u8 status) -{ - struct hci_request req; - struct hci_conn *conn; - int err; - - conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT); - if (!conn) - return; - - if (status) { - BT_DBG("HCI request failed to stop scanning: status 0x%2.2x", - status); - - hci_dev_lock(hdev); - hci_le_conn_failed(conn, status); - hci_dev_unlock(hdev); - return; - } - - /* Since we may have prematurely stopped discovery procedure, we should - * update discovery state. - */ - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); - - hci_req_init(&req, hdev); - - hci_req_add_le_create_conn(&req, conn); - - err = hci_req_run(&req, create_le_conn_complete); - if (err) { - hci_dev_lock(hdev); - hci_le_conn_failed(conn, HCI_ERROR_MEMORY_EXCEEDED); - hci_dev_unlock(hdev); - return; - } -} - struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, u8 dst_type, u8 sec_level, u8 auth_type) { @@ -721,16 +683,19 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, hci_req_init(&req, hdev); /* If controller is scanning, we stop it since some controllers are - * not able to scan and connect at the same time. + * not able to scan and connect at the same time. Also set the + * HCI_LE_SCAN_INTERRUPTED flag so that the command complete + * handler for scan disabling knows to set the correct discovery + * state. */ if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) { hci_req_add_le_scan_disable(&req); - err = hci_req_run(&req, stop_scan_complete); - } else { - hci_req_add_le_create_conn(&req, conn); - err = hci_req_run(&req, create_le_conn_complete); + set_bit(HCI_LE_SCAN_INTERRUPTED, &hdev->dev_flags); } + hci_req_add_le_create_conn(&req, conn); + + err = hci_req_run(&req, create_le_conn_complete); if (err) { hci_conn_del(conn); return ERR_PTR(err); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index e3335b03c992..c3b0a08f5ab4 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1024,6 +1024,13 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, cancel_delayed_work(&hdev->le_scan_disable); clear_bit(HCI_LE_SCAN, &hdev->dev_flags); + /* The HCI_LE_SCAN_INTERRUPTED flag indicates that we + * interrupted scanning due to a connect request. Mark + * therefore discovery as stopped. + */ + if (test_and_clear_bit(HCI_LE_SCAN_INTERRUPTED, + &hdev->dev_flags)) + hci_discovery_set_state(hdev, DISCOVERY_STOPPED); break; default: -- cgit v1.2.3-71-gd317 From 89745c9c41b093e4041f290343e8916f74f26051 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Fri, 28 Feb 2014 07:32:43 +0100 Subject: 6lowpan: add frag information struct This patch adds a 6lowpan fragmentation struct into cb of skb which is necessary to hold fragmentation information. Signed-off-by: Alexander Aring Signed-off-by: David S. Miller --- include/net/ieee802154_netdev.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include/net') diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h index 8196d5d40359..97b2e34d87f7 100644 --- a/include/net/ieee802154_netdev.h +++ b/include/net/ieee802154_netdev.h @@ -29,6 +29,12 @@ #include +struct ieee802154_frag_info { + __be16 d_tag; + u16 d_size; + u8 d_offset; +}; + /* * A control block of skb passed between the ARPHRD_IEEE802154 device * and other stack parts. @@ -39,6 +45,7 @@ struct ieee802154_mac_cb { struct ieee802154_addr da; u8 flags; u8 seq; + struct ieee802154_frag_info frag_info; }; static inline struct ieee802154_mac_cb *mac_cb(struct sk_buff *skb) -- cgit v1.2.3-71-gd317 From 633fc86ff621bba79dcddfd4c67fb07ae5f8467c Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Fri, 28 Feb 2014 07:32:49 +0100 Subject: net: ns: add ieee802154_6lowpan namespace This patch adds necessary ieee802154 6lowpan namespace to provide the inet_frag information. This is a initial support for handling 6lowpan fragmentation with the inet_frag api. Signed-off-by: Alexander Aring Signed-off-by: David S. Miller --- include/net/net_namespace.h | 4 ++++ include/net/netns/ieee802154_6lowpan.h | 13 +++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 include/net/netns/ieee802154_6lowpan.h (limited to 'include/net') diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 991dcd94cbbf..79387f73f875 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -90,6 +91,9 @@ struct net { #if IS_ENABLED(CONFIG_IPV6) struct netns_ipv6 ipv6; #endif +#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN) + struct netns_ieee802154_lowpan ieee802154_lowpan; +#endif #if defined(CONFIG_IP_SCTP) || defined(CONFIG_IP_SCTP_MODULE) struct netns_sctp sctp; #endif diff --git a/include/net/netns/ieee802154_6lowpan.h b/include/net/netns/ieee802154_6lowpan.h new file mode 100644 index 000000000000..88110b7e2428 --- /dev/null +++ b/include/net/netns/ieee802154_6lowpan.h @@ -0,0 +1,13 @@ +/* + * ieee802154 6lowpan in net namespaces + */ + +#include + +#ifndef __NETNS_IEEE802154_6LOWPAN_H__ +#define __NETNS_IEEE802154_6LOWPAN_H__ + +struct netns_ieee802154_lowpan { +}; + +#endif -- cgit v1.2.3-71-gd317 From 7240cdec60b136f3e64a453c7fbded4ed1aa047e Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Fri, 28 Feb 2014 07:32:50 +0100 Subject: 6lowpan: handling 6lowpan fragmentation via inet_frag api This patch drops the current way of 6lowpan fragmentation on receiving side and replace it with a implementation which use the inet_frag api. The old fragmentation handling has some race conditions and isn't rfc4944 compatible. Also adding support to match fragments on destination address, source address, tag value and datagram_size which is missing in the current implementation. Signed-off-by: Alexander Aring Signed-off-by: David S. Miller --- include/net/netns/ieee802154_6lowpan.h | 9 + net/ieee802154/6lowpan_rtnl.c | 256 +++------------ net/ieee802154/Makefile | 2 +- net/ieee802154/reassembly.c | 564 +++++++++++++++++++++++++++++++++ net/ieee802154/reassembly.h | 66 ++++ 5 files changed, 689 insertions(+), 208 deletions(-) create mode 100644 net/ieee802154/reassembly.c create mode 100644 net/ieee802154/reassembly.h (limited to 'include/net') diff --git a/include/net/netns/ieee802154_6lowpan.h b/include/net/netns/ieee802154_6lowpan.h index 88110b7e2428..079030c853d8 100644 --- a/include/net/netns/ieee802154_6lowpan.h +++ b/include/net/netns/ieee802154_6lowpan.h @@ -7,7 +7,16 @@ #ifndef __NETNS_IEEE802154_6LOWPAN_H__ #define __NETNS_IEEE802154_6LOWPAN_H__ +struct netns_sysctl_lowpan { +#ifdef CONFIG_SYSCTL + struct ctl_table_header *frags_hdr; +#endif +}; + struct netns_ieee802154_lowpan { + struct netns_sysctl_lowpan sysctl; + struct netns_frags frags; + u16 max_dsize; }; #endif diff --git a/net/ieee802154/6lowpan_rtnl.c b/net/ieee802154/6lowpan_rtnl.c index f9c954824ddb..c7bd8b55f7ce 100644 --- a/net/ieee802154/6lowpan_rtnl.c +++ b/net/ieee802154/6lowpan_rtnl.c @@ -54,6 +54,7 @@ #include #include +#include "reassembly.h" #include "6lowpan.h" static LIST_HEAD(lowpan_devices); @@ -70,18 +71,6 @@ struct lowpan_dev_record { struct list_head list; }; -struct lowpan_fragment { - struct sk_buff *skb; /* skb to be assembled */ - u16 length; /* length to be assemled */ - u32 bytes_rcv; /* bytes received */ - u16 tag; /* current fragment tag */ - struct timer_list timer; /* assembling timer */ - struct list_head list; /* fragments list */ -}; - -static LIST_HEAD(lowpan_fragments); -static DEFINE_SPINLOCK(flist_lock); - static inline struct lowpan_dev_info *lowpan_dev_info(const struct net_device *dev) { @@ -179,69 +168,6 @@ static int lowpan_give_skb_to_devices(struct sk_buff *skb, return stat; } -static void lowpan_fragment_timer_expired(unsigned long entry_addr) -{ - struct lowpan_fragment *entry = (struct lowpan_fragment *)entry_addr; - - pr_debug("timer expired for frame with tag %d\n", entry->tag); - - list_del(&entry->list); - dev_kfree_skb(entry->skb); - kfree(entry); -} - -static struct lowpan_fragment * -lowpan_alloc_new_frame(struct sk_buff *skb, u16 len, u16 tag) -{ - struct lowpan_fragment *frame; - - frame = kzalloc(sizeof(struct lowpan_fragment), - GFP_ATOMIC); - if (!frame) - goto frame_err; - - INIT_LIST_HEAD(&frame->list); - - frame->length = len; - frame->tag = tag; - - /* allocate buffer for frame assembling */ - frame->skb = netdev_alloc_skb_ip_align(skb->dev, frame->length + - sizeof(struct ipv6hdr)); - - if (!frame->skb) - goto skb_err; - - frame->skb->priority = skb->priority; - - /* reserve headroom for uncompressed ipv6 header */ - skb_reserve(frame->skb, sizeof(struct ipv6hdr)); - skb_put(frame->skb, frame->length); - - /* copy the first control block to keep a - * trace of the link-layer addresses in case - * of a link-local compressed address - */ - memcpy(frame->skb->cb, skb->cb, sizeof(skb->cb)); - - init_timer(&frame->timer); - /* time out is the same as for ipv6 - 60 sec */ - frame->timer.expires = jiffies + LOWPAN_FRAG_TIMEOUT; - frame->timer.data = (unsigned long)frame; - frame->timer.function = lowpan_fragment_timer_expired; - - add_timer(&frame->timer); - - list_add_tail(&frame->list, &lowpan_fragments); - - return frame; - -skb_err: - kfree(frame); -frame_err: - return NULL; -} - static int process_data(struct sk_buff *skb) { u8 iphc0, iphc1; @@ -255,94 +181,6 @@ static int process_data(struct sk_buff *skb) if (lowpan_fetch_skb_u8(skb, &iphc0)) goto drop; - /* fragments assembling */ - switch (iphc0 & LOWPAN_DISPATCH_MASK) { - case LOWPAN_DISPATCH_FRAG1: - case LOWPAN_DISPATCH_FRAGN: - { - struct lowpan_fragment *frame; - /* slen stores the rightmost 8 bits of the 11 bits length */ - u8 slen, offset = 0; - u16 len, tag; - bool found = false; - - if (lowpan_fetch_skb_u8(skb, &slen) || /* frame length */ - lowpan_fetch_skb_u16(skb, &tag)) /* fragment tag */ - goto drop; - - /* adds the 3 MSB to the 8 LSB to retrieve the 11 bits length */ - len = ((iphc0 & 7) << 8) | slen; - - if ((iphc0 & LOWPAN_DISPATCH_MASK) == LOWPAN_DISPATCH_FRAG1) { - pr_debug("%s received a FRAG1 packet (tag: %d, " - "size of the entire IP packet: %d)", - __func__, tag, len); - } else { /* FRAGN */ - if (lowpan_fetch_skb_u8(skb, &offset)) - goto unlock_and_drop; - pr_debug("%s received a FRAGN packet (tag: %d, " - "size of the entire IP packet: %d, " - "offset: %d)", __func__, tag, len, offset * 8); - } - - /* - * check if frame assembling with the same tag is - * already in progress - */ - spin_lock_bh(&flist_lock); - - list_for_each_entry(frame, &lowpan_fragments, list) - if (frame->tag == tag) { - found = true; - break; - } - - /* alloc new frame structure */ - if (!found) { - pr_debug("%s first fragment received for tag %d, " - "begin packet reassembly", __func__, tag); - frame = lowpan_alloc_new_frame(skb, len, tag); - if (!frame) - goto unlock_and_drop; - } - - /* if payload fits buffer, copy it */ - if (likely((offset * 8 + skb->len) <= frame->length)) - skb_copy_to_linear_data_offset(frame->skb, offset * 8, - skb->data, skb->len); - else - goto unlock_and_drop; - - frame->bytes_rcv += skb->len; - - /* frame assembling complete */ - if ((frame->bytes_rcv == frame->length) && - frame->timer.expires > jiffies) { - /* if timer haven't expired - first of all delete it */ - del_timer_sync(&frame->timer); - list_del(&frame->list); - spin_unlock_bh(&flist_lock); - - pr_debug("%s successfully reassembled fragment " - "(tag %d)", __func__, tag); - - dev_kfree_skb(skb); - skb = frame->skb; - kfree(frame); - - if (lowpan_fetch_skb_u8(skb, &iphc0)) - goto drop; - - break; - } - spin_unlock_bh(&flist_lock); - - return kfree_skb(skb), 0; - } - default: - break; - } - if (lowpan_fetch_skb_u8(skb, &iphc1)) goto drop; @@ -355,8 +193,6 @@ static int process_data(struct sk_buff *skb) IEEE802154_ADDR_LEN, iphc0, iphc1, lowpan_give_skb_to_devices); -unlock_and_drop: - spin_unlock_bh(&flist_lock); drop: kfree_skb(skb); return -EINVAL; @@ -603,44 +439,53 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { struct sk_buff *local_skb; + int ret; if (!netif_running(dev)) - goto drop; + goto drop_skb; if (dev->type != ARPHRD_IEEE802154) - goto drop; + goto drop_skb; + + local_skb = skb_clone(skb, GFP_ATOMIC); + if (!local_skb) + goto drop_skb; + + kfree_skb(skb); /* 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; /* Pull off the 1-byte of 6lowpan header. */ skb_pull(local_skb, 1); - lowpan_give_skb_to_devices(local_skb, NULL); - - kfree_skb(local_skb); - kfree_skb(skb); + ret = lowpan_give_skb_to_devices(local_skb, NULL); + if (ret == NET_RX_DROP) + goto drop; } else { switch (skb->data[0] & 0xe0) { case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */ + ret = process_data(local_skb); + if (ret == NET_RX_DROP) + goto drop; + break; case LOWPAN_DISPATCH_FRAG1: /* first fragment header */ + ret = lowpan_frag_rcv(local_skb, LOWPAN_DISPATCH_FRAG1); + if (ret == 1) { + ret = process_data(local_skb); + if (ret == NET_RX_DROP) + goto drop; + } + break; case LOWPAN_DISPATCH_FRAGN: /* next fragments headers */ - local_skb = skb_clone(skb, GFP_ATOMIC); - if (!local_skb) - goto drop; - process_data(local_skb); - - kfree_skb(skb); + ret = lowpan_frag_rcv(local_skb, LOWPAN_DISPATCH_FRAGN); + if (ret == 1) { + ret = process_data(local_skb); + if (ret == NET_RX_DROP) + goto drop; + } break; default: break; @@ -648,9 +493,9 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev, } return NET_RX_SUCCESS; - -drop: +drop_skb: kfree_skb(skb); +drop: return NET_RX_DROP; } @@ -778,43 +623,40 @@ static int __init lowpan_init_module(void) { int err = 0; - err = lowpan_netlink_init(); + err = lowpan_net_frag_init(); if (err < 0) goto out; + err = lowpan_netlink_init(); + if (err < 0) + goto out_frag; + dev_add_pack(&lowpan_packet_type); err = register_netdevice_notifier(&lowpan_dev_notifier); - if (err < 0) { - dev_remove_pack(&lowpan_packet_type); - lowpan_netlink_fini(); - } + if (err < 0) + goto out_pack; + + return 0; + +out_pack: + dev_remove_pack(&lowpan_packet_type); + lowpan_netlink_fini(); +out_frag: + lowpan_net_frag_exit(); out: return err; } static void __exit lowpan_cleanup_module(void) { - struct lowpan_fragment *frame, *tframe; - lowpan_netlink_fini(); dev_remove_pack(&lowpan_packet_type); - unregister_netdevice_notifier(&lowpan_dev_notifier); + lowpan_net_frag_exit(); - /* Now 6lowpan packet_type is removed, so no new fragments are - * expected on RX, therefore that's the time to clean incomplete - * fragments. - */ - spin_lock_bh(&flist_lock); - list_for_each_entry_safe(frame, tframe, &lowpan_fragments, list) { - del_timer_sync(&frame->timer); - list_del(&frame->list); - dev_kfree_skb(frame->skb); - kfree(frame); - } - spin_unlock_bh(&flist_lock); + unregister_netdevice_notifier(&lowpan_dev_notifier); } module_init(lowpan_init_module); diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile index 3d08adfcd175..b113fc4be3e0 100644 --- a/net/ieee802154/Makefile +++ b/net/ieee802154/Makefile @@ -2,6 +2,6 @@ obj-$(CONFIG_IEEE802154) += ieee802154.o af_802154.o obj-$(CONFIG_IEEE802154_6LOWPAN) += 6lowpan.o obj-$(CONFIG_6LOWPAN_IPHC) += 6lowpan_iphc.o -6lowpan-y := 6lowpan_rtnl.o +6lowpan-y := 6lowpan_rtnl.o reassembly.o ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o wpan-class.o af_802154-y := af_ieee802154.o raw.o dgram.o diff --git a/net/ieee802154/reassembly.c b/net/ieee802154/reassembly.c new file mode 100644 index 000000000000..eb5995e74316 --- /dev/null +++ b/net/ieee802154/reassembly.c @@ -0,0 +1,564 @@ +/* 6LoWPAN fragment reassembly + * + * + * Authors: + * Alexander Aring + * + * Based on: net/ipv6/reassembly.c + * + * 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. + */ + +#define pr_fmt(fmt) "6LoWPAN: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "6lowpan.h" +#include "reassembly.h" + +static struct inet_frags lowpan_frags; + +static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, + struct sk_buff *prev, struct net_device *dev); + +static unsigned int lowpan_hash_frag(__be16 tag, __be16 d_size, + const struct ieee802154_addr *saddr, + const struct ieee802154_addr *daddr) +{ + u32 c; + + net_get_random_once(&lowpan_frags.rnd, sizeof(lowpan_frags.rnd)); + c = jhash_3words(ieee802154_addr_hash(saddr), + ieee802154_addr_hash(daddr), + (__force u32)(tag + (d_size << 16)), + lowpan_frags.rnd); + + return c & (INETFRAGS_HASHSZ - 1); +} + +static unsigned int lowpan_hashfn(struct inet_frag_queue *q) +{ + struct lowpan_frag_queue *fq; + + fq = container_of(q, struct lowpan_frag_queue, q); + return lowpan_hash_frag(fq->tag, fq->d_size, &fq->saddr, &fq->daddr); +} + +bool lowpan_frag_match(struct inet_frag_queue *q, void *a) +{ + struct lowpan_frag_queue *fq; + struct lowpan_create_arg *arg = a; + + fq = container_of(q, struct lowpan_frag_queue, q); + return fq->tag == arg->tag && fq->d_size == arg->d_size && + ieee802154_addr_addr_equal(&fq->saddr, arg->src) && + ieee802154_addr_addr_equal(&fq->daddr, arg->dst); +} +EXPORT_SYMBOL(lowpan_frag_match); + +void lowpan_frag_init(struct inet_frag_queue *q, void *a) +{ + struct lowpan_frag_queue *fq; + struct lowpan_create_arg *arg = a; + + fq = container_of(q, struct lowpan_frag_queue, q); + + fq->tag = arg->tag; + fq->d_size = arg->d_size; + fq->saddr = *arg->src; + fq->daddr = *arg->dst; +} +EXPORT_SYMBOL(lowpan_frag_init); + +void lowpan_expire_frag_queue(struct frag_queue *fq, struct inet_frags *frags) +{ + spin_lock(&fq->q.lock); + + if (fq->q.last_in & INET_FRAG_COMPLETE) + goto out; + + inet_frag_kill(&fq->q, frags); +out: + spin_unlock(&fq->q.lock); + inet_frag_put(&fq->q, frags); +} +EXPORT_SYMBOL(lowpan_expire_frag_queue); + +static void lowpan_frag_expire(unsigned long data) +{ + struct frag_queue *fq; + struct net *net; + + fq = container_of((struct inet_frag_queue *)data, struct frag_queue, q); + net = container_of(fq->q.net, struct net, ieee802154_lowpan.frags); + + lowpan_expire_frag_queue(fq, &lowpan_frags); +} + +static inline struct lowpan_frag_queue * +fq_find(struct net *net, const struct ieee802154_frag_info *frag_info, + const struct ieee802154_addr *src, const struct ieee802154_addr *dst) +{ + struct inet_frag_queue *q; + struct lowpan_create_arg arg; + unsigned int hash; + + arg.tag = frag_info->d_tag; + arg.d_size = frag_info->d_size; + arg.src = src; + arg.dst = dst; + + read_lock(&lowpan_frags.lock); + hash = lowpan_hash_frag(frag_info->d_tag, frag_info->d_size, src, dst); + + q = inet_frag_find(&net->ieee802154_lowpan.frags, + &lowpan_frags, &arg, hash); + if (IS_ERR_OR_NULL(q)) { + inet_frag_maybe_warn_overflow(q, pr_fmt()); + return NULL; + } + return container_of(q, struct lowpan_frag_queue, q); +} + +static int lowpan_frag_queue(struct lowpan_frag_queue *fq, + struct sk_buff *skb, const u8 frag_type) +{ + struct sk_buff *prev, *next; + struct net_device *dev; + int end, offset; + + if (fq->q.last_in & INET_FRAG_COMPLETE) + goto err; + + offset = mac_cb(skb)->frag_info.d_offset << 3; + end = mac_cb(skb)->frag_info.d_size; + + /* Is this the final fragment? */ + if (offset + skb->len == end) { + /* If we already have some bits beyond end + * or have different end, the segment is corrupted. + */ + if (end < fq->q.len || + ((fq->q.last_in & INET_FRAG_LAST_IN) && end != fq->q.len)) + goto err; + fq->q.last_in |= INET_FRAG_LAST_IN; + fq->q.len = end; + } else { + if (end > fq->q.len) { + /* Some bits beyond end -> corruption. */ + if (fq->q.last_in & INET_FRAG_LAST_IN) + goto err; + fq->q.len = end; + } + } + + /* Find out which fragments are in front and at the back of us + * in the chain of fragments so far. We must know where to put + * this fragment, right? + */ + prev = fq->q.fragments_tail; + if (!prev || mac_cb(prev)->frag_info.d_offset < + mac_cb(skb)->frag_info.d_offset) { + next = NULL; + goto found; + } + prev = NULL; + for (next = fq->q.fragments; next != NULL; next = next->next) { + if (mac_cb(next)->frag_info.d_offset >= + mac_cb(skb)->frag_info.d_offset) + break; /* bingo! */ + prev = next; + } + +found: + /* Insert this fragment in the chain of fragments. */ + skb->next = next; + if (!next) + fq->q.fragments_tail = skb; + if (prev) + prev->next = skb; + else + fq->q.fragments = skb; + + dev = skb->dev; + if (dev) + skb->dev = NULL; + + fq->q.stamp = skb->tstamp; + if (frag_type == LOWPAN_DISPATCH_FRAG1) { + /* Calculate uncomp. 6lowpan header to estimate full size */ + fq->q.meat += lowpan_uncompress_size(skb, NULL); + fq->q.last_in |= INET_FRAG_FIRST_IN; + } else { + fq->q.meat += skb->len; + } + add_frag_mem_limit(&fq->q, skb->truesize); + + if (fq->q.last_in == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) && + fq->q.meat == fq->q.len) { + int res; + unsigned long orefdst = skb->_skb_refdst; + + skb->_skb_refdst = 0UL; + res = lowpan_frag_reasm(fq, prev, dev); + skb->_skb_refdst = orefdst; + return res; + } + + inet_frag_lru_move(&fq->q); + return -1; +err: + kfree_skb(skb); + return -1; +} + +/* Check if this packet is complete. + * Returns NULL on failure by any reason, and pointer + * to current nexthdr field in reassembled frame. + * + * It is called with locked fq, and caller must check that + * queue is eligible for reassembly i.e. it is not COMPLETE, + * the last and the first frames arrived and all the bits are here. + */ +static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *prev, + struct net_device *dev) +{ + struct sk_buff *fp, *head = fq->q.fragments; + int sum_truesize; + + inet_frag_kill(&fq->q, &lowpan_frags); + + /* Make the one we just received the head. */ + if (prev) { + head = prev->next; + fp = skb_clone(head, GFP_ATOMIC); + + if (!fp) + goto out_oom; + + fp->next = head->next; + if (!fp->next) + fq->q.fragments_tail = fp; + prev->next = fp; + + skb_morph(head, fq->q.fragments); + head->next = fq->q.fragments->next; + + consume_skb(fq->q.fragments); + fq->q.fragments = head; + } + + /* Head of list must not be cloned. */ + if (skb_unclone(head, GFP_ATOMIC)) + goto out_oom; + + /* If the first fragment is fragmented itself, we split + * it to two chunks: the first with data and paged part + * and the second, holding only fragments. + */ + if (skb_has_frag_list(head)) { + struct sk_buff *clone; + int i, plen = 0; + + clone = alloc_skb(0, GFP_ATOMIC); + if (!clone) + goto out_oom; + clone->next = head->next; + head->next = clone; + skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list; + skb_frag_list_init(head); + for (i = 0; i < skb_shinfo(head)->nr_frags; i++) + plen += skb_frag_size(&skb_shinfo(head)->frags[i]); + clone->len = head->data_len - plen; + clone->data_len = clone->len; + head->data_len -= clone->len; + head->len -= clone->len; + add_frag_mem_limit(&fq->q, clone->truesize); + } + + WARN_ON(head == NULL); + + sum_truesize = head->truesize; + for (fp = head->next; fp;) { + bool headstolen; + int delta; + struct sk_buff *next = fp->next; + + sum_truesize += fp->truesize; + if (skb_try_coalesce(head, fp, &headstolen, &delta)) { + kfree_skb_partial(fp, headstolen); + } else { + if (!skb_shinfo(head)->frag_list) + skb_shinfo(head)->frag_list = fp; + head->data_len += fp->len; + head->len += fp->len; + head->truesize += fp->truesize; + } + fp = next; + } + sub_frag_mem_limit(&fq->q, sum_truesize); + + head->next = NULL; + head->dev = dev; + head->tstamp = fq->q.stamp; + + fq->q.fragments = NULL; + fq->q.fragments_tail = NULL; + + return 1; +out_oom: + net_dbg_ratelimited("lowpan_frag_reasm: no memory for reassembly\n"); + return -1; +} + +static int lowpan_get_frag_info(struct sk_buff *skb, const u8 frag_type, + struct ieee802154_frag_info *frag_info) +{ + bool fail; + u8 pattern = 0, low = 0; + + fail = lowpan_fetch_skb(skb, &pattern, 1); + fail |= lowpan_fetch_skb(skb, &low, 1); + frag_info->d_size = (pattern & 7) << 8 | low; + fail |= lowpan_fetch_skb(skb, &frag_info->d_tag, 2); + + if (frag_type == LOWPAN_DISPATCH_FRAGN) { + fail |= lowpan_fetch_skb(skb, &frag_info->d_offset, 1); + } else { + skb_reset_network_header(skb); + frag_info->d_offset = 0; + } + + if (unlikely(fail)) + return -EIO; + + return 0; +} + +int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type) +{ + struct lowpan_frag_queue *fq; + struct net *net = dev_net(skb->dev); + struct ieee802154_frag_info *frag_info = &mac_cb(skb)->frag_info; + int err; + + err = lowpan_get_frag_info(skb, frag_type, frag_info); + if (err < 0) + goto err; + + if (frag_info->d_size > net->ieee802154_lowpan.max_dsize) + goto err; + + inet_frag_evictor(&net->ieee802154_lowpan.frags, &lowpan_frags, false); + + fq = fq_find(net, frag_info, &mac_cb(skb)->sa, &mac_cb(skb)->da); + if (fq != NULL) { + int ret; + spin_lock(&fq->q.lock); + ret = lowpan_frag_queue(fq, skb, frag_type); + spin_unlock(&fq->q.lock); + + inet_frag_put(&fq->q, &lowpan_frags); + return ret; + } + +err: + kfree_skb(skb); + return -1; +} +EXPORT_SYMBOL(lowpan_frag_rcv); + +#ifdef CONFIG_SYSCTL +static struct ctl_table lowpan_frags_ns_ctl_table[] = { + { + .procname = "6lowpanfrag_high_thresh", + .data = &init_net.ieee802154_lowpan.frags.high_thresh, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, + { + .procname = "6lowpanfrag_low_thresh", + .data = &init_net.ieee802154_lowpan.frags.low_thresh, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, + { + .procname = "6lowpanfrag_time", + .data = &init_net.ieee802154_lowpan.frags.timeout, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_jiffies, + }, + { + .procname = "6lowpanfrag_max_datagram_size", + .data = &init_net.ieee802154_lowpan.max_dsize, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, + { } +}; + +static struct ctl_table lowpan_frags_ctl_table[] = { + { + .procname = "6lowpanfrag_secret_interval", + .data = &lowpan_frags.secret_interval, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_jiffies, + }, + { } +}; + +static int __net_init lowpan_frags_ns_sysctl_register(struct net *net) +{ + struct ctl_table *table; + struct ctl_table_header *hdr; + + table = lowpan_frags_ns_ctl_table; + if (!net_eq(net, &init_net)) { + table = kmemdup(table, sizeof(lowpan_frags_ns_ctl_table), + GFP_KERNEL); + if (table == NULL) + goto err_alloc; + + table[0].data = &net->ieee802154_lowpan.frags.high_thresh; + table[1].data = &net->ieee802154_lowpan.frags.low_thresh; + table[2].data = &net->ieee802154_lowpan.frags.timeout; + table[2].data = &net->ieee802154_lowpan.max_dsize; + + /* Don't export sysctls to unprivileged users */ + if (net->user_ns != &init_user_ns) + table[0].procname = NULL; + } + + hdr = register_net_sysctl(net, "net/ieee802154/6lowpan", table); + if (hdr == NULL) + goto err_reg; + + net->ieee802154_lowpan.sysctl.frags_hdr = hdr; + return 0; + +err_reg: + if (!net_eq(net, &init_net)) + kfree(table); +err_alloc: + return -ENOMEM; +} + +static void __net_exit lowpan_frags_ns_sysctl_unregister(struct net *net) +{ + struct ctl_table *table; + + table = net->ieee802154_lowpan.sysctl.frags_hdr->ctl_table_arg; + unregister_net_sysctl_table(net->ieee802154_lowpan.sysctl.frags_hdr); + if (!net_eq(net, &init_net)) + kfree(table); +} + +static struct ctl_table_header *lowpan_ctl_header; + +static int lowpan_frags_sysctl_register(void) +{ + lowpan_ctl_header = register_net_sysctl(&init_net, + "net/ieee802154/6lowpan", + lowpan_frags_ctl_table); + return lowpan_ctl_header == NULL ? -ENOMEM : 0; +} + +static void lowpan_frags_sysctl_unregister(void) +{ + unregister_net_sysctl_table(lowpan_ctl_header); +} +#else +static inline int lowpan_frags_ns_sysctl_register(struct net *net) +{ + return 0; +} + +static inline void lowpan_frags_ns_sysctl_unregister(struct net *net) +{ +} + +static inline int lowpan_frags_sysctl_register(void) +{ + return 0; +} + +static inline void lowpan_frags_sysctl_unregister(void) +{ +} +#endif + +static int __net_init lowpan_frags_init_net(struct net *net) +{ + net->ieee802154_lowpan.frags.high_thresh = IPV6_FRAG_HIGH_THRESH; + net->ieee802154_lowpan.frags.low_thresh = IPV6_FRAG_LOW_THRESH; + net->ieee802154_lowpan.frags.timeout = IPV6_FRAG_TIMEOUT; + net->ieee802154_lowpan.max_dsize = 0xFFFF; + + inet_frags_init_net(&net->ieee802154_lowpan.frags); + + return lowpan_frags_ns_sysctl_register(net); +} + +static void __net_exit lowpan_frags_exit_net(struct net *net) +{ + lowpan_frags_ns_sysctl_unregister(net); + inet_frags_exit_net(&net->ieee802154_lowpan.frags, &lowpan_frags); +} + +static struct pernet_operations lowpan_frags_ops = { + .init = lowpan_frags_init_net, + .exit = lowpan_frags_exit_net, +}; + +int __init lowpan_net_frag_init(void) +{ + int ret; + + ret = lowpan_frags_sysctl_register(); + if (ret) + goto out; + + ret = register_pernet_subsys(&lowpan_frags_ops); + if (ret) + goto err_pernet; + + lowpan_frags.hashfn = lowpan_hashfn; + lowpan_frags.constructor = lowpan_frag_init; + lowpan_frags.destructor = NULL; + lowpan_frags.skb_free = NULL; + lowpan_frags.qsize = sizeof(struct frag_queue); + lowpan_frags.match = lowpan_frag_match; + lowpan_frags.frag_expire = lowpan_frag_expire; + lowpan_frags.secret_interval = 10 * 60 * HZ; + inet_frags_init(&lowpan_frags); +err_pernet: + lowpan_frags_sysctl_unregister(); +out: + return ret; +} + +void lowpan_net_frag_exit(void) +{ + inet_frags_fini(&lowpan_frags); + lowpan_frags_sysctl_unregister(); + unregister_pernet_subsys(&lowpan_frags_ops); +} diff --git a/net/ieee802154/reassembly.h b/net/ieee802154/reassembly.h new file mode 100644 index 000000000000..055518b9da2d --- /dev/null +++ b/net/ieee802154/reassembly.h @@ -0,0 +1,66 @@ +#ifndef __IEEE802154_6LOWPAN_REASSEMBLY_H__ +#define __IEEE802154_6LOWPAN_REASSEMBLY_H__ + +#include + +struct lowpan_create_arg { + __be16 tag; + u16 d_size; + const struct ieee802154_addr *src; + const struct ieee802154_addr *dst; +}; + +/* Equivalent of ipv4 struct ip + */ +struct lowpan_frag_queue { + struct inet_frag_queue q; + + __be16 tag; + u16 d_size; + struct ieee802154_addr saddr; + struct ieee802154_addr daddr; +}; + +static inline u32 ieee802154_addr_hash(const struct ieee802154_addr *a) +{ + switch (a->addr_type) { + case IEEE802154_ADDR_LONG: + return (__force u32)((((u32 *)a->hwaddr))[0] ^ + ((u32 *)(a->hwaddr))[1]); + case IEEE802154_ADDR_SHORT: + return (__force u32)(a->short_addr); + default: + return 0; + } +} + +static inline bool ieee802154_addr_addr_equal(const struct ieee802154_addr *a1, + const struct ieee802154_addr *a2) +{ + if (a1->pan_id != a2->pan_id) + return false; + + if (a1->addr_type != a2->addr_type) + return false; + + switch (a1->addr_type) { + case IEEE802154_ADDR_LONG: + if (memcmp(a1->hwaddr, a2->hwaddr, IEEE802154_ADDR_LEN)) + return false; + break; + case IEEE802154_ADDR_SHORT: + if (a1->short_addr != a2->short_addr) + return false; + break; + default: + return false; + } + + return true; +} + +int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type); +void lowpan_net_frag_exit(void); +int lowpan_net_frag_init(void); + +#endif /* __IEEE802154_6LOWPAN_REASSEMBLY_H__ */ -- cgit v1.2.3-71-gd317 From e90c14835ba2e1d3f82b8f24e429b971725afff5 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Thu, 6 Mar 2014 09:11:07 +0100 Subject: inet: remove now unused flag DST_NOPEER Commit e688a604807647 ("net: introduce DST_NOPEER dst flag") introduced DST_NOPEER because because of crashes in ipv6_select_ident called from udp6_ufo_fragment. Since commit 916e4cf46d0204 ("ipv6: reuse ip6_frag_id from ip6_ufo_append_data") we don't call ipv6_select_ident any more from ip6_ufo_append_data, thus this flag lost its purpose and can be removed. Cc: Eric Dumazet Signed-off-by: Hannes Frederic Sowa Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/dst.h | 7 +++---- net/bridge/br_netfilter.c | 2 +- net/ipv6/output_core.c | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) (limited to 'include/net') diff --git a/include/net/dst.h b/include/net/dst.h index 77eb53fabfb0..e01a826f2a9c 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -54,10 +54,9 @@ struct dst_entry { #define DST_NOHASH 0x0008 #define DST_NOCACHE 0x0010 #define DST_NOCOUNT 0x0020 -#define DST_NOPEER 0x0040 -#define DST_FAKE_RTABLE 0x0080 -#define DST_XFRM_TUNNEL 0x0100 -#define DST_XFRM_QUEUE 0x0200 +#define DST_FAKE_RTABLE 0x0040 +#define DST_XFRM_TUNNEL 0x0080 +#define DST_XFRM_QUEUE 0x0100 unsigned short pending_confirm; diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index df0f114fb8cb..80e1b0f60a30 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -167,7 +167,7 @@ void br_netfilter_rtable_init(struct net_bridge *br) rt->dst.dev = br->dev; rt->dst.path = &rt->dst; dst_init_metrics(&rt->dst, br_dst_default_metrics, true); - rt->dst.flags = DST_NOXFRM | DST_NOPEER | DST_FAKE_RTABLE; + rt->dst.flags = DST_NOXFRM | DST_FAKE_RTABLE; rt->dst.ops = &fake_dst_ops; } diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c index 827f795209cf..d1b35d377e62 100644 --- a/net/ipv6/output_core.c +++ b/net/ipv6/output_core.c @@ -13,7 +13,7 @@ void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt) int old, new; #if IS_ENABLED(CONFIG_IPV6) - if (rt && !(rt->dst.flags & DST_NOPEER)) { + if (rt) { struct inet_peer *peer; struct net *net; -- cgit v1.2.3-71-gd317 From f7324acd98ce48fcde9783884ffe8c0b90899e5e Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 6 Mar 2014 15:03:17 -0500 Subject: tcp: Use NET_ADD_STATS instead of NET_ADD_STATS_BH in tcp_event_new_data_sent() Can be invoked from non-BH context. Based upon a patch by Eric Dumazet. Fixes: f19c29e3e391 ("tcp: snmp stats for Fast Open, SYN rtx, and data pkts") Reported-by: Sergey Senozhatsky Signed-off-by: David S. Miller --- include/net/ip.h | 1 + net/ipv4/tcp_output.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/ip.h b/include/net/ip.h index b885d75cede4..25064c28e059 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -187,6 +187,7 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr, #define NET_INC_STATS(net, field) SNMP_INC_STATS((net)->mib.net_statistics, field) #define NET_INC_STATS_BH(net, field) SNMP_INC_STATS_BH((net)->mib.net_statistics, field) #define NET_INC_STATS_USER(net, field) SNMP_INC_STATS_USER((net)->mib.net_statistics, field) +#define NET_ADD_STATS(net, field, adnd) SNMP_ADD_STATS((net)->mib.net_statistics, field, adnd) #define NET_ADD_STATS_BH(net, field, adnd) SNMP_ADD_STATS_BH((net)->mib.net_statistics, field, adnd) #define NET_ADD_STATS_USER(net, field, adnd) SNMP_ADD_STATS_USER((net)->mib.net_statistics, field, adnd) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 5286228679bd..a02c884d4321 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -87,8 +87,8 @@ static void tcp_event_new_data_sent(struct sock *sk, const struct sk_buff *skb) tcp_rearm_rto(sk); } - NET_ADD_STATS_BH(sock_net(sk), LINUX_MIB_TCPORIGDATASENT, - tcp_skb_pcount(skb)); + NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPORIGDATASENT, + tcp_skb_pcount(skb)); } /* SND.NXT, if window was not shrunk. -- cgit v1.2.3-71-gd317 From cefc8c8a7c9e4867c45407f7f9a44fe80c5ea58a Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Wed, 5 Mar 2014 14:29:05 +0100 Subject: 6lowpan: move 6lowpan header to include/net This header is used by bluetooth and ieee802154 branch. This patch move this header to the include/net directory to avoid a use of a relative path in include. Signed-off-by: Alexander Aring Signed-off-by: David S. Miller --- include/net/6lowpan.h | 434 ++++++++++++++++++++++++++++++++++++++++++ net/bluetooth/6lowpan.c | 2 +- net/ieee802154/6lowpan.h | 434 ------------------------------------------ net/ieee802154/6lowpan_iphc.c | 3 +- net/ieee802154/6lowpan_rtnl.c | 2 +- net/ieee802154/reassembly.c | 2 +- 6 files changed, 438 insertions(+), 439 deletions(-) create mode 100644 include/net/6lowpan.h delete mode 100644 net/ieee802154/6lowpan.h (limited to 'include/net') diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h new file mode 100644 index 000000000000..f7d372b7d4ff --- /dev/null +++ b/include/net/6lowpan.h @@ -0,0 +1,434 @@ +/* + * Copyright 2011, Siemens AG + * written by Alexander Smirnov + */ + +/* + * Based on patches from Jon Smirl + * Copyright (c) 2011 Jon Smirl + * + * 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. + * + * 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. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* Jon's code is based on 6lowpan implementation for Contiki which is: + * Copyright (c) 2008, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __6LOWPAN_H__ +#define __6LOWPAN_H__ + +#include + +#define UIP_802154_SHORTADDR_LEN 2 /* compressed ipv6 address length */ +#define UIP_IPH_LEN 40 /* ipv6 fixed header size */ +#define UIP_PROTO_UDP 17 /* ipv6 next header value for UDP */ +#define UIP_FRAGH_LEN 8 /* ipv6 fragment header size */ + +/* + * ipv6 address based on mac + * second bit-flip (Universe/Local) is done according RFC2464 + */ +#define is_addr_mac_addr_based(a, m) \ + ((((a)->s6_addr[8]) == (((m)[0]) ^ 0x02)) && \ + (((a)->s6_addr[9]) == (m)[1]) && \ + (((a)->s6_addr[10]) == (m)[2]) && \ + (((a)->s6_addr[11]) == (m)[3]) && \ + (((a)->s6_addr[12]) == (m)[4]) && \ + (((a)->s6_addr[13]) == (m)[5]) && \ + (((a)->s6_addr[14]) == (m)[6]) && \ + (((a)->s6_addr[15]) == (m)[7])) + +/* ipv6 address is unspecified */ +#define is_addr_unspecified(a) \ + ((((a)->s6_addr32[0]) == 0) && \ + (((a)->s6_addr32[1]) == 0) && \ + (((a)->s6_addr32[2]) == 0) && \ + (((a)->s6_addr32[3]) == 0)) + +/* compare ipv6 addresses prefixes */ +#define ipaddr_prefixcmp(addr1, addr2, length) \ + (memcmp(addr1, addr2, length >> 3) == 0) + +/* local link, i.e. FE80::/10 */ +#define is_addr_link_local(a) (((a)->s6_addr16[0]) == htons(0xFE80)) + +/* + * check whether we can compress the IID to 16 bits, + * it's possible for unicast adresses with first 49 bits are zero only. + */ +#define lowpan_is_iid_16_bit_compressable(a) \ + ((((a)->s6_addr16[4]) == 0) && \ + (((a)->s6_addr[10]) == 0) && \ + (((a)->s6_addr[11]) == 0xff) && \ + (((a)->s6_addr[12]) == 0xfe) && \ + (((a)->s6_addr[13]) == 0)) + +/* multicast address */ +#define is_addr_mcast(a) (((a)->s6_addr[0]) == 0xFF) + +/* check whether the 112-bit gid of the multicast address is mappable to: */ + +/* 9 bits, for FF02::1 (all nodes) and FF02::2 (all routers) addresses only. */ +#define lowpan_is_mcast_addr_compressable(a) \ + ((((a)->s6_addr16[1]) == 0) && \ + (((a)->s6_addr16[2]) == 0) && \ + (((a)->s6_addr16[3]) == 0) && \ + (((a)->s6_addr16[4]) == 0) && \ + (((a)->s6_addr16[5]) == 0) && \ + (((a)->s6_addr16[6]) == 0) && \ + (((a)->s6_addr[14]) == 0) && \ + ((((a)->s6_addr[15]) == 1) || (((a)->s6_addr[15]) == 2))) + +/* 48 bits, FFXX::00XX:XXXX:XXXX */ +#define lowpan_is_mcast_addr_compressable48(a) \ + ((((a)->s6_addr16[1]) == 0) && \ + (((a)->s6_addr16[2]) == 0) && \ + (((a)->s6_addr16[3]) == 0) && \ + (((a)->s6_addr16[4]) == 0) && \ + (((a)->s6_addr[10]) == 0)) + +/* 32 bits, FFXX::00XX:XXXX */ +#define lowpan_is_mcast_addr_compressable32(a) \ + ((((a)->s6_addr16[1]) == 0) && \ + (((a)->s6_addr16[2]) == 0) && \ + (((a)->s6_addr16[3]) == 0) && \ + (((a)->s6_addr16[4]) == 0) && \ + (((a)->s6_addr16[5]) == 0) && \ + (((a)->s6_addr[12]) == 0)) + +/* 8 bits, FF02::00XX */ +#define lowpan_is_mcast_addr_compressable8(a) \ + ((((a)->s6_addr[1]) == 2) && \ + (((a)->s6_addr16[1]) == 0) && \ + (((a)->s6_addr16[2]) == 0) && \ + (((a)->s6_addr16[3]) == 0) && \ + (((a)->s6_addr16[4]) == 0) && \ + (((a)->s6_addr16[5]) == 0) && \ + (((a)->s6_addr16[6]) == 0) && \ + (((a)->s6_addr[14]) == 0)) + +#define lowpan_is_addr_broadcast(a) \ + ((((a)[0]) == 0xFF) && \ + (((a)[1]) == 0xFF) && \ + (((a)[2]) == 0xFF) && \ + (((a)[3]) == 0xFF) && \ + (((a)[4]) == 0xFF) && \ + (((a)[5]) == 0xFF) && \ + (((a)[6]) == 0xFF) && \ + (((a)[7]) == 0xFF)) + +#define LOWPAN_DISPATCH_IPV6 0x41 /* 01000001 = 65 */ +#define LOWPAN_DISPATCH_HC1 0x42 /* 01000010 = 66 */ +#define LOWPAN_DISPATCH_IPHC 0x60 /* 011xxxxx = ... */ +#define LOWPAN_DISPATCH_FRAG1 0xc0 /* 11000xxx */ +#define LOWPAN_DISPATCH_FRAGN 0xe0 /* 11100xxx */ + +#define LOWPAN_DISPATCH_MASK 0xf8 /* 11111000 */ + +#define LOWPAN_FRAG_TIMEOUT (HZ * 60) /* time-out 60 sec */ + +#define LOWPAN_FRAG1_HEAD_SIZE 0x4 +#define LOWPAN_FRAGN_HEAD_SIZE 0x5 + +/* + * According IEEE802.15.4 standard: + * - MTU is 127 octets + * - maximum MHR size is 37 octets + * - MFR size is 2 octets + * + * so minimal payload size that we may guarantee is: + * MTU - MHR - MFR = 88 octets + */ +#define LOWPAN_FRAG_SIZE 88 + +/* + * Values of fields within the IPHC encoding first byte + * (C stands for compressed and I for inline) + */ +#define LOWPAN_IPHC_TF 0x18 + +#define LOWPAN_IPHC_FL_C 0x10 +#define LOWPAN_IPHC_TC_C 0x08 +#define LOWPAN_IPHC_NH_C 0x04 +#define LOWPAN_IPHC_TTL_1 0x01 +#define LOWPAN_IPHC_TTL_64 0x02 +#define LOWPAN_IPHC_TTL_255 0x03 +#define LOWPAN_IPHC_TTL_I 0x00 + + +/* Values of fields within the IPHC encoding second byte */ +#define LOWPAN_IPHC_CID 0x80 + +#define LOWPAN_IPHC_ADDR_00 0x00 +#define LOWPAN_IPHC_ADDR_01 0x01 +#define LOWPAN_IPHC_ADDR_02 0x02 +#define LOWPAN_IPHC_ADDR_03 0x03 + +#define LOWPAN_IPHC_SAC 0x40 +#define LOWPAN_IPHC_SAM 0x30 + +#define LOWPAN_IPHC_SAM_BIT 4 + +#define LOWPAN_IPHC_M 0x08 +#define LOWPAN_IPHC_DAC 0x04 +#define LOWPAN_IPHC_DAM_00 0x00 +#define LOWPAN_IPHC_DAM_01 0x01 +#define LOWPAN_IPHC_DAM_10 0x02 +#define LOWPAN_IPHC_DAM_11 0x03 + +#define LOWPAN_IPHC_DAM_BIT 0 +/* + * LOWPAN_UDP encoding (works together with IPHC) + */ +#define LOWPAN_NHC_UDP_MASK 0xF8 +#define LOWPAN_NHC_UDP_ID 0xF0 +#define LOWPAN_NHC_UDP_CHECKSUMC 0x04 +#define LOWPAN_NHC_UDP_CHECKSUMI 0x00 + +#define LOWPAN_NHC_UDP_4BIT_PORT 0xF0B0 +#define LOWPAN_NHC_UDP_4BIT_MASK 0xFFF0 +#define LOWPAN_NHC_UDP_8BIT_PORT 0xF000 +#define LOWPAN_NHC_UDP_8BIT_MASK 0xFF00 + +/* values for port compression, _with checksum_ ie bit 5 set to 0 */ +#define LOWPAN_NHC_UDP_CS_P_00 0xF0 /* all inline */ +#define LOWPAN_NHC_UDP_CS_P_01 0xF1 /* source 16bit inline, + dest = 0xF0 + 8 bit inline */ +#define LOWPAN_NHC_UDP_CS_P_10 0xF2 /* source = 0xF0 + 8bit inline, + dest = 16 bit inline */ +#define LOWPAN_NHC_UDP_CS_P_11 0xF3 /* source & dest = 0xF0B + 4bit inline */ +#define LOWPAN_NHC_UDP_CS_C 0x04 /* checksum elided */ + +#ifdef DEBUG +/* 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); +} +#else +static inline void raw_dump_table(const char *caller, char *msg, + unsigned char *buf, int len) { } +static inline void raw_dump_inline(const char *caller, char *msg, + unsigned char *buf, int len) { } +#endif + +static inline int lowpan_fetch_skb_u8(struct sk_buff *skb, u8 *val) +{ + if (unlikely(!pskb_may_pull(skb, 1))) + return -EINVAL; + + *val = skb->data[0]; + skb_pull(skb, 1); + + return 0; +} + +static inline int lowpan_fetch_skb_u16(struct sk_buff *skb, u16 *val) +{ + if (unlikely(!pskb_may_pull(skb, 2))) + return -EINVAL; + + *val = (skb->data[0] << 8) | skb->data[1]; + skb_pull(skb, 2); + + return 0; +} + +static inline bool lowpan_fetch_skb(struct sk_buff *skb, + void *data, const unsigned int len) +{ + if (unlikely(!pskb_may_pull(skb, len))) + return true; + + skb_copy_from_linear_data(skb, data, len); + skb_pull(skb, len); + + return false; +} + +static inline void lowpan_push_hc_data(u8 **hc_ptr, const void *data, + const size_t len) +{ + memcpy(*hc_ptr, data, len); + *hc_ptr += len; +} + +static inline u8 lowpan_addr_mode_size(const u8 addr_mode) +{ + static const u8 addr_sizes[] = { + [LOWPAN_IPHC_ADDR_00] = 16, + [LOWPAN_IPHC_ADDR_01] = 8, + [LOWPAN_IPHC_ADDR_02] = 2, + [LOWPAN_IPHC_ADDR_03] = 0, + }; + return addr_sizes[addr_mode]; +} + +static inline u8 lowpan_next_hdr_size(const u8 h_enc, u16 *uncomp_header) +{ + u8 ret = 1; + + if ((h_enc & LOWPAN_NHC_UDP_MASK) == LOWPAN_NHC_UDP_ID) { + *uncomp_header += sizeof(struct udphdr); + + switch (h_enc & LOWPAN_NHC_UDP_CS_P_11) { + case LOWPAN_NHC_UDP_CS_P_00: + ret += 4; + break; + case LOWPAN_NHC_UDP_CS_P_01: + case LOWPAN_NHC_UDP_CS_P_10: + ret += 3; + break; + case LOWPAN_NHC_UDP_CS_P_11: + ret++; + break; + default: + break; + } + + if (!(h_enc & LOWPAN_NHC_UDP_CS_C)) + ret += 2; + } + + return ret; +} + +/** + * lowpan_uncompress_size - returns skb->len size with uncompressed header + * @skb: sk_buff with 6lowpan header inside + * @datagram_offset: optional to get the datagram_offset value + * + * Returns the skb->len with uncompressed header + */ +static inline u16 +lowpan_uncompress_size(const struct sk_buff *skb, u16 *dgram_offset) +{ + u16 ret = 2, uncomp_header = sizeof(struct ipv6hdr); + u8 iphc0, iphc1, h_enc; + + iphc0 = skb_network_header(skb)[0]; + iphc1 = skb_network_header(skb)[1]; + + switch ((iphc0 & LOWPAN_IPHC_TF) >> 3) { + case 0: + ret += 4; + break; + case 1: + ret += 3; + break; + case 2: + ret++; + break; + default: + break; + } + + if (!(iphc0 & LOWPAN_IPHC_NH_C)) + ret++; + + if (!(iphc0 & 0x03)) + ret++; + + ret += lowpan_addr_mode_size((iphc1 & LOWPAN_IPHC_SAM) >> + LOWPAN_IPHC_SAM_BIT); + + if (iphc1 & LOWPAN_IPHC_M) { + switch ((iphc1 & LOWPAN_IPHC_DAM_11) >> + LOWPAN_IPHC_DAM_BIT) { + case LOWPAN_IPHC_DAM_00: + ret += 16; + break; + case LOWPAN_IPHC_DAM_01: + ret += 6; + break; + case LOWPAN_IPHC_DAM_10: + ret += 4; + break; + case LOWPAN_IPHC_DAM_11: + ret++; + break; + default: + break; + } + } else { + ret += lowpan_addr_mode_size((iphc1 & LOWPAN_IPHC_DAM_11) >> + LOWPAN_IPHC_DAM_BIT); + } + + if (iphc0 & LOWPAN_IPHC_NH_C) { + h_enc = skb_network_header(skb)[ret]; + ret += lowpan_next_hdr_size(h_enc, &uncomp_header); + } + + if (dgram_offset) + *dgram_offset = uncomp_header; + + return skb->len + uncomp_header - ret; +} + +typedef int (*skb_delivery_cb)(struct sk_buff *skb, struct net_device *dev); + +int lowpan_process_data(struct sk_buff *skb, struct net_device *dev, + const u8 *saddr, const u8 saddr_type, const u8 saddr_len, + const u8 *daddr, const u8 daddr_type, const u8 daddr_len, + u8 iphc0, u8 iphc1, skb_delivery_cb skb_deliver); +int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev, + unsigned short type, const void *_daddr, + const void *_saddr, unsigned int len); + +#endif /* __6LOWPAN_H__ */ diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index adb3ea04adaa..73492b91105a 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -27,7 +27,7 @@ #include "6lowpan.h" -#include "../ieee802154/6lowpan.h" /* for the compression support */ +#include /* for the compression support */ #define IFACE_NAME_TEMPLATE "bt%d" #define EUI64_ADDR_LEN 8 diff --git a/net/ieee802154/6lowpan.h b/net/ieee802154/6lowpan.h deleted file mode 100644 index f7d372b7d4ff..000000000000 --- a/net/ieee802154/6lowpan.h +++ /dev/null @@ -1,434 +0,0 @@ -/* - * Copyright 2011, Siemens AG - * written by Alexander Smirnov - */ - -/* - * Based on patches from Jon Smirl - * Copyright (c) 2011 Jon Smirl - * - * 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. - * - * 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. - * - * 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/* Jon's code is based on 6lowpan implementation for Contiki which is: - * Copyright (c) 2008, Swedish Institute of Computer Science. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef __6LOWPAN_H__ -#define __6LOWPAN_H__ - -#include - -#define UIP_802154_SHORTADDR_LEN 2 /* compressed ipv6 address length */ -#define UIP_IPH_LEN 40 /* ipv6 fixed header size */ -#define UIP_PROTO_UDP 17 /* ipv6 next header value for UDP */ -#define UIP_FRAGH_LEN 8 /* ipv6 fragment header size */ - -/* - * ipv6 address based on mac - * second bit-flip (Universe/Local) is done according RFC2464 - */ -#define is_addr_mac_addr_based(a, m) \ - ((((a)->s6_addr[8]) == (((m)[0]) ^ 0x02)) && \ - (((a)->s6_addr[9]) == (m)[1]) && \ - (((a)->s6_addr[10]) == (m)[2]) && \ - (((a)->s6_addr[11]) == (m)[3]) && \ - (((a)->s6_addr[12]) == (m)[4]) && \ - (((a)->s6_addr[13]) == (m)[5]) && \ - (((a)->s6_addr[14]) == (m)[6]) && \ - (((a)->s6_addr[15]) == (m)[7])) - -/* ipv6 address is unspecified */ -#define is_addr_unspecified(a) \ - ((((a)->s6_addr32[0]) == 0) && \ - (((a)->s6_addr32[1]) == 0) && \ - (((a)->s6_addr32[2]) == 0) && \ - (((a)->s6_addr32[3]) == 0)) - -/* compare ipv6 addresses prefixes */ -#define ipaddr_prefixcmp(addr1, addr2, length) \ - (memcmp(addr1, addr2, length >> 3) == 0) - -/* local link, i.e. FE80::/10 */ -#define is_addr_link_local(a) (((a)->s6_addr16[0]) == htons(0xFE80)) - -/* - * check whether we can compress the IID to 16 bits, - * it's possible for unicast adresses with first 49 bits are zero only. - */ -#define lowpan_is_iid_16_bit_compressable(a) \ - ((((a)->s6_addr16[4]) == 0) && \ - (((a)->s6_addr[10]) == 0) && \ - (((a)->s6_addr[11]) == 0xff) && \ - (((a)->s6_addr[12]) == 0xfe) && \ - (((a)->s6_addr[13]) == 0)) - -/* multicast address */ -#define is_addr_mcast(a) (((a)->s6_addr[0]) == 0xFF) - -/* check whether the 112-bit gid of the multicast address is mappable to: */ - -/* 9 bits, for FF02::1 (all nodes) and FF02::2 (all routers) addresses only. */ -#define lowpan_is_mcast_addr_compressable(a) \ - ((((a)->s6_addr16[1]) == 0) && \ - (((a)->s6_addr16[2]) == 0) && \ - (((a)->s6_addr16[3]) == 0) && \ - (((a)->s6_addr16[4]) == 0) && \ - (((a)->s6_addr16[5]) == 0) && \ - (((a)->s6_addr16[6]) == 0) && \ - (((a)->s6_addr[14]) == 0) && \ - ((((a)->s6_addr[15]) == 1) || (((a)->s6_addr[15]) == 2))) - -/* 48 bits, FFXX::00XX:XXXX:XXXX */ -#define lowpan_is_mcast_addr_compressable48(a) \ - ((((a)->s6_addr16[1]) == 0) && \ - (((a)->s6_addr16[2]) == 0) && \ - (((a)->s6_addr16[3]) == 0) && \ - (((a)->s6_addr16[4]) == 0) && \ - (((a)->s6_addr[10]) == 0)) - -/* 32 bits, FFXX::00XX:XXXX */ -#define lowpan_is_mcast_addr_compressable32(a) \ - ((((a)->s6_addr16[1]) == 0) && \ - (((a)->s6_addr16[2]) == 0) && \ - (((a)->s6_addr16[3]) == 0) && \ - (((a)->s6_addr16[4]) == 0) && \ - (((a)->s6_addr16[5]) == 0) && \ - (((a)->s6_addr[12]) == 0)) - -/* 8 bits, FF02::00XX */ -#define lowpan_is_mcast_addr_compressable8(a) \ - ((((a)->s6_addr[1]) == 2) && \ - (((a)->s6_addr16[1]) == 0) && \ - (((a)->s6_addr16[2]) == 0) && \ - (((a)->s6_addr16[3]) == 0) && \ - (((a)->s6_addr16[4]) == 0) && \ - (((a)->s6_addr16[5]) == 0) && \ - (((a)->s6_addr16[6]) == 0) && \ - (((a)->s6_addr[14]) == 0)) - -#define lowpan_is_addr_broadcast(a) \ - ((((a)[0]) == 0xFF) && \ - (((a)[1]) == 0xFF) && \ - (((a)[2]) == 0xFF) && \ - (((a)[3]) == 0xFF) && \ - (((a)[4]) == 0xFF) && \ - (((a)[5]) == 0xFF) && \ - (((a)[6]) == 0xFF) && \ - (((a)[7]) == 0xFF)) - -#define LOWPAN_DISPATCH_IPV6 0x41 /* 01000001 = 65 */ -#define LOWPAN_DISPATCH_HC1 0x42 /* 01000010 = 66 */ -#define LOWPAN_DISPATCH_IPHC 0x60 /* 011xxxxx = ... */ -#define LOWPAN_DISPATCH_FRAG1 0xc0 /* 11000xxx */ -#define LOWPAN_DISPATCH_FRAGN 0xe0 /* 11100xxx */ - -#define LOWPAN_DISPATCH_MASK 0xf8 /* 11111000 */ - -#define LOWPAN_FRAG_TIMEOUT (HZ * 60) /* time-out 60 sec */ - -#define LOWPAN_FRAG1_HEAD_SIZE 0x4 -#define LOWPAN_FRAGN_HEAD_SIZE 0x5 - -/* - * According IEEE802.15.4 standard: - * - MTU is 127 octets - * - maximum MHR size is 37 octets - * - MFR size is 2 octets - * - * so minimal payload size that we may guarantee is: - * MTU - MHR - MFR = 88 octets - */ -#define LOWPAN_FRAG_SIZE 88 - -/* - * Values of fields within the IPHC encoding first byte - * (C stands for compressed and I for inline) - */ -#define LOWPAN_IPHC_TF 0x18 - -#define LOWPAN_IPHC_FL_C 0x10 -#define LOWPAN_IPHC_TC_C 0x08 -#define LOWPAN_IPHC_NH_C 0x04 -#define LOWPAN_IPHC_TTL_1 0x01 -#define LOWPAN_IPHC_TTL_64 0x02 -#define LOWPAN_IPHC_TTL_255 0x03 -#define LOWPAN_IPHC_TTL_I 0x00 - - -/* Values of fields within the IPHC encoding second byte */ -#define LOWPAN_IPHC_CID 0x80 - -#define LOWPAN_IPHC_ADDR_00 0x00 -#define LOWPAN_IPHC_ADDR_01 0x01 -#define LOWPAN_IPHC_ADDR_02 0x02 -#define LOWPAN_IPHC_ADDR_03 0x03 - -#define LOWPAN_IPHC_SAC 0x40 -#define LOWPAN_IPHC_SAM 0x30 - -#define LOWPAN_IPHC_SAM_BIT 4 - -#define LOWPAN_IPHC_M 0x08 -#define LOWPAN_IPHC_DAC 0x04 -#define LOWPAN_IPHC_DAM_00 0x00 -#define LOWPAN_IPHC_DAM_01 0x01 -#define LOWPAN_IPHC_DAM_10 0x02 -#define LOWPAN_IPHC_DAM_11 0x03 - -#define LOWPAN_IPHC_DAM_BIT 0 -/* - * LOWPAN_UDP encoding (works together with IPHC) - */ -#define LOWPAN_NHC_UDP_MASK 0xF8 -#define LOWPAN_NHC_UDP_ID 0xF0 -#define LOWPAN_NHC_UDP_CHECKSUMC 0x04 -#define LOWPAN_NHC_UDP_CHECKSUMI 0x00 - -#define LOWPAN_NHC_UDP_4BIT_PORT 0xF0B0 -#define LOWPAN_NHC_UDP_4BIT_MASK 0xFFF0 -#define LOWPAN_NHC_UDP_8BIT_PORT 0xF000 -#define LOWPAN_NHC_UDP_8BIT_MASK 0xFF00 - -/* values for port compression, _with checksum_ ie bit 5 set to 0 */ -#define LOWPAN_NHC_UDP_CS_P_00 0xF0 /* all inline */ -#define LOWPAN_NHC_UDP_CS_P_01 0xF1 /* source 16bit inline, - dest = 0xF0 + 8 bit inline */ -#define LOWPAN_NHC_UDP_CS_P_10 0xF2 /* source = 0xF0 + 8bit inline, - dest = 16 bit inline */ -#define LOWPAN_NHC_UDP_CS_P_11 0xF3 /* source & dest = 0xF0B + 4bit inline */ -#define LOWPAN_NHC_UDP_CS_C 0x04 /* checksum elided */ - -#ifdef DEBUG -/* 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); -} -#else -static inline void raw_dump_table(const char *caller, char *msg, - unsigned char *buf, int len) { } -static inline void raw_dump_inline(const char *caller, char *msg, - unsigned char *buf, int len) { } -#endif - -static inline int lowpan_fetch_skb_u8(struct sk_buff *skb, u8 *val) -{ - if (unlikely(!pskb_may_pull(skb, 1))) - return -EINVAL; - - *val = skb->data[0]; - skb_pull(skb, 1); - - return 0; -} - -static inline int lowpan_fetch_skb_u16(struct sk_buff *skb, u16 *val) -{ - if (unlikely(!pskb_may_pull(skb, 2))) - return -EINVAL; - - *val = (skb->data[0] << 8) | skb->data[1]; - skb_pull(skb, 2); - - return 0; -} - -static inline bool lowpan_fetch_skb(struct sk_buff *skb, - void *data, const unsigned int len) -{ - if (unlikely(!pskb_may_pull(skb, len))) - return true; - - skb_copy_from_linear_data(skb, data, len); - skb_pull(skb, len); - - return false; -} - -static inline void lowpan_push_hc_data(u8 **hc_ptr, const void *data, - const size_t len) -{ - memcpy(*hc_ptr, data, len); - *hc_ptr += len; -} - -static inline u8 lowpan_addr_mode_size(const u8 addr_mode) -{ - static const u8 addr_sizes[] = { - [LOWPAN_IPHC_ADDR_00] = 16, - [LOWPAN_IPHC_ADDR_01] = 8, - [LOWPAN_IPHC_ADDR_02] = 2, - [LOWPAN_IPHC_ADDR_03] = 0, - }; - return addr_sizes[addr_mode]; -} - -static inline u8 lowpan_next_hdr_size(const u8 h_enc, u16 *uncomp_header) -{ - u8 ret = 1; - - if ((h_enc & LOWPAN_NHC_UDP_MASK) == LOWPAN_NHC_UDP_ID) { - *uncomp_header += sizeof(struct udphdr); - - switch (h_enc & LOWPAN_NHC_UDP_CS_P_11) { - case LOWPAN_NHC_UDP_CS_P_00: - ret += 4; - break; - case LOWPAN_NHC_UDP_CS_P_01: - case LOWPAN_NHC_UDP_CS_P_10: - ret += 3; - break; - case LOWPAN_NHC_UDP_CS_P_11: - ret++; - break; - default: - break; - } - - if (!(h_enc & LOWPAN_NHC_UDP_CS_C)) - ret += 2; - } - - return ret; -} - -/** - * lowpan_uncompress_size - returns skb->len size with uncompressed header - * @skb: sk_buff with 6lowpan header inside - * @datagram_offset: optional to get the datagram_offset value - * - * Returns the skb->len with uncompressed header - */ -static inline u16 -lowpan_uncompress_size(const struct sk_buff *skb, u16 *dgram_offset) -{ - u16 ret = 2, uncomp_header = sizeof(struct ipv6hdr); - u8 iphc0, iphc1, h_enc; - - iphc0 = skb_network_header(skb)[0]; - iphc1 = skb_network_header(skb)[1]; - - switch ((iphc0 & LOWPAN_IPHC_TF) >> 3) { - case 0: - ret += 4; - break; - case 1: - ret += 3; - break; - case 2: - ret++; - break; - default: - break; - } - - if (!(iphc0 & LOWPAN_IPHC_NH_C)) - ret++; - - if (!(iphc0 & 0x03)) - ret++; - - ret += lowpan_addr_mode_size((iphc1 & LOWPAN_IPHC_SAM) >> - LOWPAN_IPHC_SAM_BIT); - - if (iphc1 & LOWPAN_IPHC_M) { - switch ((iphc1 & LOWPAN_IPHC_DAM_11) >> - LOWPAN_IPHC_DAM_BIT) { - case LOWPAN_IPHC_DAM_00: - ret += 16; - break; - case LOWPAN_IPHC_DAM_01: - ret += 6; - break; - case LOWPAN_IPHC_DAM_10: - ret += 4; - break; - case LOWPAN_IPHC_DAM_11: - ret++; - break; - default: - break; - } - } else { - ret += lowpan_addr_mode_size((iphc1 & LOWPAN_IPHC_DAM_11) >> - LOWPAN_IPHC_DAM_BIT); - } - - if (iphc0 & LOWPAN_IPHC_NH_C) { - h_enc = skb_network_header(skb)[ret]; - ret += lowpan_next_hdr_size(h_enc, &uncomp_header); - } - - if (dgram_offset) - *dgram_offset = uncomp_header; - - return skb->len + uncomp_header - ret; -} - -typedef int (*skb_delivery_cb)(struct sk_buff *skb, struct net_device *dev); - -int lowpan_process_data(struct sk_buff *skb, struct net_device *dev, - const u8 *saddr, const u8 saddr_type, const u8 saddr_len, - const u8 *daddr, const u8 daddr_type, const u8 daddr_len, - u8 iphc0, u8 iphc1, skb_delivery_cb skb_deliver); -int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev, - unsigned short type, const void *_daddr, - const void *_saddr, unsigned int len); - -#endif /* __6LOWPAN_H__ */ diff --git a/net/ieee802154/6lowpan_iphc.c b/net/ieee802154/6lowpan_iphc.c index 860aa2d445ba..211b5686d719 100644 --- a/net/ieee802154/6lowpan_iphc.c +++ b/net/ieee802154/6lowpan_iphc.c @@ -54,11 +54,10 @@ #include #include #include +#include #include #include -#include "6lowpan.h" - /* * Uncompress address function for source and * destination address(non-multicast). diff --git a/net/ieee802154/6lowpan_rtnl.c b/net/ieee802154/6lowpan_rtnl.c index e4726180fc36..1bbab8952f77 100644 --- a/net/ieee802154/6lowpan_rtnl.c +++ b/net/ieee802154/6lowpan_rtnl.c @@ -52,10 +52,10 @@ #include #include #include +#include #include #include "reassembly.h" -#include "6lowpan.h" static LIST_HEAD(lowpan_devices); diff --git a/net/ieee802154/reassembly.c b/net/ieee802154/reassembly.c index 4511fc22ef16..1cc2336eb52c 100644 --- a/net/ieee802154/reassembly.c +++ b/net/ieee802154/reassembly.c @@ -24,10 +24,10 @@ #include #include +#include #include #include -#include "6lowpan.h" #include "reassembly.h" static struct inet_frags lowpan_frags; -- cgit v1.2.3-71-gd317 From 870a2df4ca026817eb87bb2f9daaa60a93fd051a Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Thu, 6 Mar 2014 18:24:29 +0100 Subject: xfrm: rename struct xfrm_filter iproute2 already defines a structure with that name, let's use another one to avoid any conflict. CC: Stephen Hemminger Signed-off-by: Nicolas Dichtel Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 4 ++-- include/uapi/linux/xfrm.h | 4 ++-- net/key/af_key.c | 2 +- net/xfrm/xfrm_state.c | 4 ++-- net/xfrm/xfrm_user.c | 8 ++++---- 5 files changed, 11 insertions(+), 11 deletions(-) (limited to 'include/net') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 8b925288a8bc..ce3d96f752fd 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -121,7 +121,7 @@ struct xfrm_state_walk { u8 dying; u8 proto; u32 seq; - struct xfrm_filter *filter; + struct xfrm_address_filter *filter; }; /* Full description of state of transformer. */ @@ -1423,7 +1423,7 @@ static inline void xfrm_sysctl_fini(struct net *net) #endif void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto, - struct xfrm_filter *filter); + struct xfrm_address_filter *filter); 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, struct net *net); diff --git a/include/uapi/linux/xfrm.h b/include/uapi/linux/xfrm.h index 6550c679584f..25e5dd916ba4 100644 --- a/include/uapi/linux/xfrm.h +++ b/include/uapi/linux/xfrm.h @@ -299,7 +299,7 @@ enum xfrm_attr_type_t { XFRMA_REPLAY_ESN_VAL, /* struct xfrm_replay_esn */ XFRMA_SA_EXTRA_FLAGS, /* __u32 */ XFRMA_PROTO, /* __u8 */ - XFRMA_FILTER, /* struct xfrm_filter */ + XFRMA_ADDRESS_FILTER, /* struct xfrm_address_filter */ __XFRMA_MAX #define XFRMA_MAX (__XFRMA_MAX - 1) @@ -476,7 +476,7 @@ struct xfrm_user_mapping { __be16 new_sport; }; -struct xfrm_filter { +struct xfrm_address_filter { xfrm_address_t saddr; xfrm_address_t daddr; __u16 family; diff --git a/net/key/af_key.c b/net/key/af_key.c index a50d979b5926..12651b42aad8 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -1799,7 +1799,7 @@ static void pfkey_dump_sa_done(struct pfkey_sock *pfk) static int pfkey_dump(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs) { u8 proto; - struct xfrm_filter *filter = NULL; + struct xfrm_address_filter *filter = NULL; struct pfkey_sock *pfk = pfkey_sk(sk); if (pfk->dump.dump != NULL) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 5339c26bb0cf..cee850c76165 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -1598,7 +1598,7 @@ unlock: EXPORT_SYMBOL(xfrm_alloc_spi); static bool __xfrm_state_filter_match(struct xfrm_state *x, - struct xfrm_filter *filter) + struct xfrm_address_filter *filter) { if (filter) { if ((filter->family == AF_INET || @@ -1657,7 +1657,7 @@ out: EXPORT_SYMBOL(xfrm_state_walk); void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto, - struct xfrm_filter *filter) + struct xfrm_address_filter *filter) { INIT_LIST_HEAD(&walk->all); walk->proto = proto; diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 023e5e7ea4c6..903725b8cc70 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -904,7 +904,7 @@ static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb) if (!cb->args[0]) { struct nlattr *attrs[XFRMA_MAX+1]; - struct xfrm_filter *filter = NULL; + struct xfrm_address_filter *filter = NULL; u8 proto = 0; int err; @@ -915,12 +915,12 @@ static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb) if (err < 0) return err; - if (attrs[XFRMA_FILTER]) { + if (attrs[XFRMA_ADDRESS_FILTER]) { filter = kmalloc(sizeof(*filter), GFP_KERNEL); if (filter == NULL) return -ENOMEM; - memcpy(filter, nla_data(attrs[XFRMA_FILTER]), + memcpy(filter, nla_data(attrs[XFRMA_ADDRESS_FILTER]), sizeof(*filter)); } @@ -2334,7 +2334,7 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = { [XFRMA_REPLAY_ESN_VAL] = { .len = sizeof(struct xfrm_replay_state_esn) }, [XFRMA_SA_EXTRA_FLAGS] = { .type = NLA_U32 }, [XFRMA_PROTO] = { .type = NLA_U8 }, - [XFRMA_FILTER] = { .len = sizeof(struct xfrm_filter) }, + [XFRMA_ADDRESS_FILTER] = { .len = sizeof(struct xfrm_address_filter) }, }; static const struct xfrm_link { -- cgit v1.2.3-71-gd317 From b476b72a0f8514a5a4c561bab731ddd506a284e7 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Mon, 3 Mar 2014 14:44:54 +0100 Subject: netfilter: trivial code cleanup and doc changes Changes while reading through the netfilter code. Added hint about how conntrack nf_conn refcnt is accessed. And renamed repl_hash to reply_hash for readability Signed-off-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller Reviewed-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack.h | 8 +++++++- net/netfilter/nf_conntrack_core.c | 20 ++++++++++---------- net/netfilter/nf_conntrack_expect.c | 2 +- 3 files changed, 18 insertions(+), 12 deletions(-) (limited to 'include/net') diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index b2ac6246b7e0..e10d1faa6d09 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -73,7 +73,13 @@ struct nf_conn_help { struct nf_conn { /* Usage count in here is 1 for hash table/destruct timer, 1 per skb, - plus 1 for any connection(s) we are `master' for */ + * plus 1 for any connection(s) we are `master' for + * + * Hint, SKB address this struct and refcnt via skb->nfct and + * helpers nf_conntrack_get() and nf_conntrack_put(). + * Helper nf_ct_put() equals nf_conntrack_put() by dec refcnt, + * beware nf_ct_get() is different and don't inc refcnt. + */ struct nf_conntrack ct_general; spinlock_t lock; diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 356bef519fe5..965693eb1f0e 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -408,21 +408,21 @@ EXPORT_SYMBOL_GPL(nf_conntrack_find_get); static void __nf_conntrack_hash_insert(struct nf_conn *ct, unsigned int hash, - unsigned int repl_hash) + unsigned int reply_hash) { struct net *net = nf_ct_net(ct); hlist_nulls_add_head_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode, &net->ct.hash[hash]); hlist_nulls_add_head_rcu(&ct->tuplehash[IP_CT_DIR_REPLY].hnnode, - &net->ct.hash[repl_hash]); + &net->ct.hash[reply_hash]); } int nf_conntrack_hash_check_insert(struct nf_conn *ct) { struct net *net = nf_ct_net(ct); - unsigned int hash, repl_hash; + unsigned int hash, reply_hash; struct nf_conntrack_tuple_hash *h; struct hlist_nulls_node *n; u16 zone; @@ -430,7 +430,7 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct) zone = nf_ct_zone(ct); hash = hash_conntrack(net, zone, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); - repl_hash = hash_conntrack(net, zone, + reply_hash = hash_conntrack(net, zone, &ct->tuplehash[IP_CT_DIR_REPLY].tuple); spin_lock_bh(&nf_conntrack_lock); @@ -441,7 +441,7 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct) &h->tuple) && zone == nf_ct_zone(nf_ct_tuplehash_to_ctrack(h))) goto out; - hlist_nulls_for_each_entry(h, n, &net->ct.hash[repl_hash], hnnode) + hlist_nulls_for_each_entry(h, n, &net->ct.hash[reply_hash], hnnode) if (nf_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_REPLY].tuple, &h->tuple) && zone == nf_ct_zone(nf_ct_tuplehash_to_ctrack(h))) @@ -451,7 +451,7 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct) smp_wmb(); /* The caller holds a reference to this object */ atomic_set(&ct->ct_general.use, 2); - __nf_conntrack_hash_insert(ct, hash, repl_hash); + __nf_conntrack_hash_insert(ct, hash, reply_hash); NF_CT_STAT_INC(net, insert); spin_unlock_bh(&nf_conntrack_lock); @@ -483,7 +483,7 @@ EXPORT_SYMBOL_GPL(nf_conntrack_tmpl_insert); int __nf_conntrack_confirm(struct sk_buff *skb) { - unsigned int hash, repl_hash; + unsigned int hash, reply_hash; struct nf_conntrack_tuple_hash *h; struct nf_conn *ct; struct nf_conn_help *help; @@ -507,7 +507,7 @@ __nf_conntrack_confirm(struct sk_buff *skb) /* reuse the hash saved before */ hash = *(unsigned long *)&ct->tuplehash[IP_CT_DIR_REPLY].hnnode.pprev; hash = hash_bucket(hash, net); - repl_hash = hash_conntrack(net, zone, + reply_hash = hash_conntrack(net, zone, &ct->tuplehash[IP_CT_DIR_REPLY].tuple); /* We're not in hash table, and we refuse to set up related @@ -540,7 +540,7 @@ __nf_conntrack_confirm(struct sk_buff *skb) &h->tuple) && zone == nf_ct_zone(nf_ct_tuplehash_to_ctrack(h))) goto out; - hlist_nulls_for_each_entry(h, n, &net->ct.hash[repl_hash], hnnode) + hlist_nulls_for_each_entry(h, n, &net->ct.hash[reply_hash], hnnode) if (nf_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_REPLY].tuple, &h->tuple) && zone == nf_ct_zone(nf_ct_tuplehash_to_ctrack(h))) @@ -570,7 +570,7 @@ __nf_conntrack_confirm(struct sk_buff *skb) * guarantee that no other CPU can find the conntrack before the above * stores are visible. */ - __nf_conntrack_hash_insert(ct, hash, repl_hash); + __nf_conntrack_hash_insert(ct, hash, reply_hash); NF_CT_STAT_INC(net, insert); spin_unlock_bh(&nf_conntrack_lock); diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 4fd1ca94fd4a..da2f84f41777 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -417,7 +417,7 @@ out: return ret; } -int nf_ct_expect_related_report(struct nf_conntrack_expect *expect, +int nf_ct_expect_related_report(struct nf_conntrack_expect *expect, u32 portid, int report) { int ret; -- cgit v1.2.3-71-gd317 From b7779d06f9950e14a008a2de970b44233fe49c86 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Mon, 3 Mar 2014 14:45:20 +0100 Subject: netfilter: conntrack: spinlock per cpu to protect special lists. One spinlock per cpu to protect dying/unconfirmed/template special lists. (These lists are now per cpu, a bit like the untracked ct) Add a @cpu field to nf_conn, to make sure we hold the appropriate spinlock at removal time. Signed-off-by: Eric Dumazet Signed-off-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller Reviewed-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack.h | 3 +- include/net/netns/conntrack.h | 11 ++- net/netfilter/nf_conntrack_core.c | 141 +++++++++++++++++++++++++---------- net/netfilter/nf_conntrack_helper.c | 11 ++- net/netfilter/nf_conntrack_netlink.c | 81 +++++++++++--------- 5 files changed, 168 insertions(+), 79 deletions(-) (limited to 'include/net') diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index e10d1faa6d09..37252f71a380 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -82,7 +82,8 @@ struct nf_conn { */ struct nf_conntrack ct_general; - spinlock_t lock; + spinlock_t lock; + u16 cpu; /* XXX should I move this to the tail ? - Y.K */ /* These are my tuples; original and reply */ diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h index fbcc7fa536dc..c6a8994e9922 100644 --- a/include/net/netns/conntrack.h +++ b/include/net/netns/conntrack.h @@ -62,6 +62,13 @@ struct nf_ip_net { #endif }; +struct ct_pcpu { + spinlock_t lock; + struct hlist_nulls_head unconfirmed; + struct hlist_nulls_head dying; + struct hlist_nulls_head tmpl; +}; + struct netns_ct { atomic_t count; unsigned int expect_count; @@ -86,9 +93,7 @@ struct netns_ct { struct kmem_cache *nf_conntrack_cachep; struct hlist_nulls_head *hash; struct hlist_head *expect_hash; - struct hlist_nulls_head unconfirmed; - struct hlist_nulls_head dying; - struct hlist_nulls_head tmpl; + struct ct_pcpu __percpu *pcpu_lists; 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; diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 965693eb1f0e..289b27901d8c 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -192,6 +192,50 @@ clean_from_lists(struct nf_conn *ct) nf_ct_remove_expectations(ct); } +/* must be called with local_bh_disable */ +static void nf_ct_add_to_dying_list(struct nf_conn *ct) +{ + struct ct_pcpu *pcpu; + + /* add this conntrack to the (per cpu) dying list */ + ct->cpu = smp_processor_id(); + pcpu = per_cpu_ptr(nf_ct_net(ct)->ct.pcpu_lists, ct->cpu); + + spin_lock(&pcpu->lock); + hlist_nulls_add_head(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode, + &pcpu->dying); + spin_unlock(&pcpu->lock); +} + +/* must be called with local_bh_disable */ +static void nf_ct_add_to_unconfirmed_list(struct nf_conn *ct) +{ + struct ct_pcpu *pcpu; + + /* add this conntrack to the (per cpu) unconfirmed list */ + ct->cpu = smp_processor_id(); + pcpu = per_cpu_ptr(nf_ct_net(ct)->ct.pcpu_lists, ct->cpu); + + spin_lock(&pcpu->lock); + hlist_nulls_add_head(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode, + &pcpu->unconfirmed); + spin_unlock(&pcpu->lock); +} + +/* must be called with local_bh_disable */ +static void nf_ct_del_from_dying_or_unconfirmed_list(struct nf_conn *ct) +{ + struct ct_pcpu *pcpu; + + /* We overload first tuple to link into unconfirmed or dying list.*/ + pcpu = per_cpu_ptr(nf_ct_net(ct)->ct.pcpu_lists, ct->cpu); + + spin_lock(&pcpu->lock); + BUG_ON(hlist_nulls_unhashed(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode)); + hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode); + spin_unlock(&pcpu->lock); +} + static void destroy_conntrack(struct nf_conntrack *nfct) { @@ -220,9 +264,7 @@ destroy_conntrack(struct nf_conntrack *nfct) * too. */ nf_ct_remove_expectations(ct); - /* We overload first tuple to link into unconfirmed or dying list.*/ - BUG_ON(hlist_nulls_unhashed(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode)); - hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode); + nf_ct_del_from_dying_or_unconfirmed_list(ct); NF_CT_STAT_INC(net, delete); spin_unlock_bh(&nf_conntrack_lock); @@ -244,9 +286,7 @@ static void nf_ct_delete_from_lists(struct nf_conn *ct) * Otherwise we can get spurious warnings. */ NF_CT_STAT_INC(net, delete_list); clean_from_lists(ct); - /* add this conntrack to the dying list */ - hlist_nulls_add_head(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode, - &net->ct.dying); + nf_ct_add_to_dying_list(ct); spin_unlock_bh(&nf_conntrack_lock); } @@ -467,15 +507,22 @@ EXPORT_SYMBOL_GPL(nf_conntrack_hash_check_insert); /* deletion from this larval template list happens via nf_ct_put() */ void nf_conntrack_tmpl_insert(struct net *net, struct nf_conn *tmpl) { + struct ct_pcpu *pcpu; + __set_bit(IPS_TEMPLATE_BIT, &tmpl->status); __set_bit(IPS_CONFIRMED_BIT, &tmpl->status); nf_conntrack_get(&tmpl->ct_general); - spin_lock_bh(&nf_conntrack_lock); + /* add this conntrack to the (per cpu) tmpl list */ + local_bh_disable(); + tmpl->cpu = smp_processor_id(); + pcpu = per_cpu_ptr(nf_ct_net(tmpl)->ct.pcpu_lists, tmpl->cpu); + + spin_lock(&pcpu->lock); /* Overload tuple linked list to put us in template list. */ hlist_nulls_add_head_rcu(&tmpl->tuplehash[IP_CT_DIR_ORIGINAL].hnnode, - &net->ct.tmpl); - spin_unlock_bh(&nf_conntrack_lock); + &pcpu->tmpl); + spin_unlock_bh(&pcpu->lock); } EXPORT_SYMBOL_GPL(nf_conntrack_tmpl_insert); @@ -546,8 +593,7 @@ __nf_conntrack_confirm(struct sk_buff *skb) zone == nf_ct_zone(nf_ct_tuplehash_to_ctrack(h))) goto out; - /* Remove from unconfirmed list */ - hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode); + nf_ct_del_from_dying_or_unconfirmed_list(ct); /* Timer relative to confirmation time, not original setting time, otherwise we'd get timer wrap in @@ -879,10 +925,7 @@ init_conntrack(struct net *net, struct nf_conn *tmpl, /* Now it is inserted into the unconfirmed list, bump refcount */ nf_conntrack_get(&ct->ct_general); - - /* Overload tuple linked list to put us in unconfirmed list. */ - hlist_nulls_add_head_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode, - &net->ct.unconfirmed); + nf_ct_add_to_unconfirmed_list(ct); spin_unlock_bh(&nf_conntrack_lock); @@ -1254,6 +1297,7 @@ get_next_corpse(struct net *net, int (*iter)(struct nf_conn *i, void *data), struct nf_conntrack_tuple_hash *h; struct nf_conn *ct; struct hlist_nulls_node *n; + int cpu; spin_lock_bh(&nf_conntrack_lock); for (; *bucket < net->ct.htable_size; (*bucket)++) { @@ -1265,12 +1309,19 @@ get_next_corpse(struct net *net, int (*iter)(struct nf_conn *i, void *data), goto found; } } - hlist_nulls_for_each_entry(h, n, &net->ct.unconfirmed, hnnode) { - ct = nf_ct_tuplehash_to_ctrack(h); - if (iter(ct, data)) - set_bit(IPS_DYING_BIT, &ct->status); - } spin_unlock_bh(&nf_conntrack_lock); + + for_each_possible_cpu(cpu) { + struct ct_pcpu *pcpu = per_cpu_ptr(net->ct.pcpu_lists, cpu); + + spin_lock_bh(&pcpu->lock); + hlist_nulls_for_each_entry(h, n, &pcpu->unconfirmed, hnnode) { + ct = nf_ct_tuplehash_to_ctrack(h); + if (iter(ct, data)) + set_bit(IPS_DYING_BIT, &ct->status); + } + spin_unlock_bh(&pcpu->lock); + } return NULL; found: atomic_inc(&ct->ct_general.use); @@ -1323,14 +1374,19 @@ static void nf_ct_release_dying_list(struct net *net) struct nf_conntrack_tuple_hash *h; struct nf_conn *ct; struct hlist_nulls_node *n; + int cpu; - spin_lock_bh(&nf_conntrack_lock); - hlist_nulls_for_each_entry(h, n, &net->ct.dying, hnnode) { - ct = nf_ct_tuplehash_to_ctrack(h); - /* never fails to remove them, no listeners at this point */ - nf_ct_kill(ct); + for_each_possible_cpu(cpu) { + struct ct_pcpu *pcpu = per_cpu_ptr(net->ct.pcpu_lists, cpu); + + spin_lock_bh(&pcpu->lock); + hlist_nulls_for_each_entry(h, n, &pcpu->dying, hnnode) { + ct = nf_ct_tuplehash_to_ctrack(h); + /* never fails to remove them, no listeners at this point */ + nf_ct_kill(ct); + } + spin_unlock_bh(&pcpu->lock); } - spin_unlock_bh(&nf_conntrack_lock); } static int untrack_refs(void) @@ -1417,6 +1473,7 @@ i_see_dead_people: kmem_cache_destroy(net->ct.nf_conntrack_cachep); kfree(net->ct.slabname); free_percpu(net->ct.stat); + free_percpu(net->ct.pcpu_lists); } } @@ -1629,37 +1686,43 @@ void nf_conntrack_init_end(void) int nf_conntrack_init_net(struct net *net) { - int ret; + int ret = -ENOMEM; + int cpu; atomic_set(&net->ct.count, 0); - INIT_HLIST_NULLS_HEAD(&net->ct.unconfirmed, UNCONFIRMED_NULLS_VAL); - INIT_HLIST_NULLS_HEAD(&net->ct.dying, DYING_NULLS_VAL); - INIT_HLIST_NULLS_HEAD(&net->ct.tmpl, TEMPLATE_NULLS_VAL); - net->ct.stat = alloc_percpu(struct ip_conntrack_stat); - if (!net->ct.stat) { - ret = -ENOMEM; + + net->ct.pcpu_lists = alloc_percpu(struct ct_pcpu); + if (!net->ct.pcpu_lists) goto err_stat; + + for_each_possible_cpu(cpu) { + struct ct_pcpu *pcpu = per_cpu_ptr(net->ct.pcpu_lists, cpu); + + spin_lock_init(&pcpu->lock); + INIT_HLIST_NULLS_HEAD(&pcpu->unconfirmed, UNCONFIRMED_NULLS_VAL); + INIT_HLIST_NULLS_HEAD(&pcpu->dying, DYING_NULLS_VAL); + INIT_HLIST_NULLS_HEAD(&pcpu->tmpl, TEMPLATE_NULLS_VAL); } + net->ct.stat = alloc_percpu(struct ip_conntrack_stat); + if (!net->ct.stat) + goto err_pcpu_lists; + net->ct.slabname = kasprintf(GFP_KERNEL, "nf_conntrack_%p", net); - if (!net->ct.slabname) { - ret = -ENOMEM; + if (!net->ct.slabname) goto err_slabname; - } net->ct.nf_conntrack_cachep = kmem_cache_create(net->ct.slabname, sizeof(struct nf_conn), 0, SLAB_DESTROY_BY_RCU, NULL); if (!net->ct.nf_conntrack_cachep) { printk(KERN_ERR "Unable to create nf_conn slab cache\n"); - ret = -ENOMEM; goto err_cache; } net->ct.htable_size = nf_conntrack_htable_size; net->ct.hash = nf_ct_alloc_hashtable(&net->ct.htable_size, 1); if (!net->ct.hash) { - ret = -ENOMEM; printk(KERN_ERR "Unable to create nf_conntrack_hash\n"); goto err_hash; } @@ -1701,6 +1764,8 @@ err_cache: kfree(net->ct.slabname); err_slabname: free_percpu(net->ct.stat); +err_pcpu_lists: + free_percpu(net->ct.pcpu_lists); err_stat: return ret; } diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 974a2a4adefa..27d9302c2191 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -396,6 +396,7 @@ static void __nf_conntrack_helper_unregister(struct nf_conntrack_helper *me, const struct hlist_node *next; const struct hlist_nulls_node *nn; unsigned int i; + int cpu; /* Get rid of expectations */ for (i = 0; i < nf_ct_expect_hsize; i++) { @@ -414,8 +415,14 @@ static void __nf_conntrack_helper_unregister(struct nf_conntrack_helper *me, } /* Get rid of expecteds, set helpers to NULL. */ - hlist_nulls_for_each_entry(h, nn, &net->ct.unconfirmed, hnnode) - unhelp(h, me); + for_each_possible_cpu(cpu) { + struct ct_pcpu *pcpu = per_cpu_ptr(net->ct.pcpu_lists, cpu); + + spin_lock_bh(&pcpu->lock); + hlist_nulls_for_each_entry(h, nn, &pcpu->unconfirmed, hnnode) + unhelp(h, me); + spin_unlock_bh(&pcpu->lock); + } for (i = 0; i < net->ct.htable_size; i++) { hlist_nulls_for_each_entry(h, nn, &net->ct.hash[i], hnnode) unhelp(h, me); diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 47e9369997ef..4ac8ce68bc16 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -1137,50 +1137,65 @@ static int ctnetlink_done_list(struct netlink_callback *cb) } static int -ctnetlink_dump_list(struct sk_buff *skb, struct netlink_callback *cb, - struct hlist_nulls_head *list) +ctnetlink_dump_list(struct sk_buff *skb, struct netlink_callback *cb, bool dying) { - struct nf_conn *ct, *last; + struct nf_conn *ct, *last = NULL; struct nf_conntrack_tuple_hash *h; struct hlist_nulls_node *n; struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); u_int8_t l3proto = nfmsg->nfgen_family; int res; + int cpu; + struct hlist_nulls_head *list; + struct net *net = sock_net(skb->sk); if (cb->args[2]) return 0; - spin_lock_bh(&nf_conntrack_lock); - last = (struct nf_conn *)cb->args[1]; -restart: - hlist_nulls_for_each_entry(h, n, list, hnnode) { - ct = nf_ct_tuplehash_to_ctrack(h); - if (l3proto && nf_ct_l3num(ct) != l3proto) + if (cb->args[0] == nr_cpu_ids) + return 0; + + for (cpu = cb->args[0]; cpu < nr_cpu_ids; cpu++) { + struct ct_pcpu *pcpu; + + if (!cpu_possible(cpu)) continue; - if (cb->args[1]) { - if (ct != last) + + pcpu = per_cpu_ptr(net->ct.pcpu_lists, cpu); + spin_lock_bh(&pcpu->lock); + last = (struct nf_conn *)cb->args[1]; + list = dying ? &pcpu->dying : &pcpu->unconfirmed; +restart: + hlist_nulls_for_each_entry(h, n, list, hnnode) { + ct = nf_ct_tuplehash_to_ctrack(h); + if (l3proto && nf_ct_l3num(ct) != l3proto) continue; - cb->args[1] = 0; - } - rcu_read_lock(); - res = ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, - NFNL_MSG_TYPE(cb->nlh->nlmsg_type), - ct); - rcu_read_unlock(); - if (res < 0) { - nf_conntrack_get(&ct->ct_general); - cb->args[1] = (unsigned long)ct; - goto out; + if (cb->args[1]) { + if (ct != last) + continue; + cb->args[1] = 0; + } + rcu_read_lock(); + res = ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, + NFNL_MSG_TYPE(cb->nlh->nlmsg_type), + ct); + rcu_read_unlock(); + if (res < 0) { + nf_conntrack_get(&ct->ct_general); + cb->args[1] = (unsigned long)ct; + spin_unlock_bh(&pcpu->lock); + goto out; + } } + if (cb->args[1]) { + cb->args[1] = 0; + goto restart; + } else + cb->args[2] = 1; + spin_unlock_bh(&pcpu->lock); } - if (cb->args[1]) { - cb->args[1] = 0; - goto restart; - } else - cb->args[2] = 1; out: - spin_unlock_bh(&nf_conntrack_lock); if (last) nf_ct_put(last); @@ -1190,9 +1205,7 @@ out: static int ctnetlink_dump_dying(struct sk_buff *skb, struct netlink_callback *cb) { - struct net *net = sock_net(skb->sk); - - return ctnetlink_dump_list(skb, cb, &net->ct.dying); + return ctnetlink_dump_list(skb, cb, true); } static int @@ -1214,9 +1227,7 @@ ctnetlink_get_ct_dying(struct sock *ctnl, struct sk_buff *skb, static int ctnetlink_dump_unconfirmed(struct sk_buff *skb, struct netlink_callback *cb) { - struct net *net = sock_net(skb->sk); - - return ctnetlink_dump_list(skb, cb, &net->ct.unconfirmed); + return ctnetlink_dump_list(skb, cb, false); } static int -- cgit v1.2.3-71-gd317 From ca7433df3a672efc88e08222cfa4b3aa965ca324 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Mon, 3 Mar 2014 14:46:01 +0100 Subject: netfilter: conntrack: seperate expect locking from nf_conntrack_lock Netfilter expectations are protected with the same lock as conntrack entries (nf_conntrack_lock). This patch split out expectations locking to use it's own lock (nf_conntrack_expect_lock). Signed-off-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller Reviewed-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_core.h | 2 ++ net/netfilter/nf_conntrack_core.c | 60 +++++++++++++++++-------------- net/netfilter/nf_conntrack_expect.c | 20 ++++++----- net/netfilter/nf_conntrack_h323_main.c | 4 +-- net/netfilter/nf_conntrack_helper.c | 22 ++++++------ net/netfilter/nf_conntrack_netlink.c | 32 ++++++++--------- net/netfilter/nf_conntrack_sip.c | 8 ++--- 7 files changed, 79 insertions(+), 69 deletions(-) (limited to 'include/net') diff --git a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h index 15308b8eb5b5..d12a631d0415 100644 --- a/include/net/netfilter/nf_conntrack_core.h +++ b/include/net/netfilter/nf_conntrack_core.h @@ -79,4 +79,6 @@ print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple, extern spinlock_t nf_conntrack_lock ; +extern spinlock_t nf_conntrack_expect_lock; + #endif /* _NF_CONNTRACK_CORE_H */ diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 92d597788d6a..4cdf1ade1530 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -63,6 +63,9 @@ EXPORT_SYMBOL_GPL(nfnetlink_parse_nat_setup_hook); DEFINE_SPINLOCK(nf_conntrack_lock); EXPORT_SYMBOL_GPL(nf_conntrack_lock); +__cacheline_aligned_in_smp DEFINE_SPINLOCK(nf_conntrack_expect_lock); +EXPORT_SYMBOL_GPL(nf_conntrack_expect_lock); + unsigned int nf_conntrack_htable_size __read_mostly; EXPORT_SYMBOL_GPL(nf_conntrack_htable_size); @@ -247,9 +250,6 @@ destroy_conntrack(struct nf_conntrack *nfct) NF_CT_ASSERT(atomic_read(&nfct->use) == 0); NF_CT_ASSERT(!timer_pending(&ct->timeout)); - /* To make sure we don't get any weird locking issues here: - * destroy_conntrack() MUST NOT be called with a write lock - * to nf_conntrack_lock!!! -HW */ rcu_read_lock(); l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct)); if (l4proto && l4proto->destroy) @@ -257,17 +257,18 @@ destroy_conntrack(struct nf_conntrack *nfct) rcu_read_unlock(); - spin_lock_bh(&nf_conntrack_lock); + local_bh_disable(); /* Expectations will have been removed in clean_from_lists, * except TFTP can create an expectation on the first packet, * before connection is in the list, so we need to clean here, - * too. */ + * too. + */ nf_ct_remove_expectations(ct); nf_ct_del_from_dying_or_unconfirmed_list(ct); NF_CT_STAT_INC(net, delete); - spin_unlock_bh(&nf_conntrack_lock); + local_bh_enable(); if (ct->master) nf_ct_put(ct->master); @@ -851,7 +852,7 @@ init_conntrack(struct net *net, struct nf_conn *tmpl, struct nf_conn_help *help; struct nf_conntrack_tuple repl_tuple; struct nf_conntrack_ecache *ecache; - struct nf_conntrack_expect *exp; + struct nf_conntrack_expect *exp = NULL; u16 zone = tmpl ? nf_ct_zone(tmpl) : NF_CT_DEFAULT_ZONE; struct nf_conn_timeout *timeout_ext; unsigned int *timeouts; @@ -895,30 +896,35 @@ init_conntrack(struct net *net, struct nf_conn *tmpl, ecache ? ecache->expmask : 0, GFP_ATOMIC); - spin_lock_bh(&nf_conntrack_lock); - exp = nf_ct_find_expectation(net, zone, tuple); - if (exp) { - pr_debug("conntrack: expectation arrives ct=%p exp=%p\n", - ct, exp); - /* Welcome, Mr. Bond. We've been expecting you... */ - __set_bit(IPS_EXPECTED_BIT, &ct->status); - /* exp->master safe, refcnt bumped in nf_ct_find_expectation */ - ct->master = exp->master; - if (exp->helper) { - help = nf_ct_helper_ext_add(ct, exp->helper, - GFP_ATOMIC); - if (help) - rcu_assign_pointer(help->helper, exp->helper); - } + local_bh_disable(); + if (net->ct.expect_count) { + spin_lock(&nf_conntrack_expect_lock); + exp = nf_ct_find_expectation(net, zone, tuple); + if (exp) { + pr_debug("conntrack: expectation arrives ct=%p exp=%p\n", + ct, exp); + /* Welcome, Mr. Bond. We've been expecting you... */ + __set_bit(IPS_EXPECTED_BIT, &ct->status); + /* exp->master safe, refcnt bumped in nf_ct_find_expectation */ + ct->master = exp->master; + if (exp->helper) { + help = nf_ct_helper_ext_add(ct, exp->helper, + GFP_ATOMIC); + if (help) + rcu_assign_pointer(help->helper, exp->helper); + } #ifdef CONFIG_NF_CONNTRACK_MARK - ct->mark = exp->master->mark; + ct->mark = exp->master->mark; #endif #ifdef CONFIG_NF_CONNTRACK_SECMARK - ct->secmark = exp->master->secmark; + ct->secmark = exp->master->secmark; #endif - NF_CT_STAT_INC(net, expect_new); - } else { + NF_CT_STAT_INC(net, expect_new); + } + spin_unlock(&nf_conntrack_expect_lock); + } + if (!exp) { __nf_ct_try_assign_helper(ct, tmpl, GFP_ATOMIC); NF_CT_STAT_INC(net, new); } @@ -927,7 +933,7 @@ init_conntrack(struct net *net, struct nf_conn *tmpl, nf_conntrack_get(&ct->ct_general); nf_ct_add_to_unconfirmed_list(ct); - spin_unlock_bh(&nf_conntrack_lock); + local_bh_enable(); if (exp) { if (exp->expectfn) diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index f02805e0c7c5..f87e8f68ad45 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -66,9 +66,9 @@ static void nf_ct_expectation_timed_out(unsigned long ul_expect) { struct nf_conntrack_expect *exp = (void *)ul_expect; - spin_lock_bh(&nf_conntrack_lock); + spin_lock_bh(&nf_conntrack_expect_lock); nf_ct_unlink_expect(exp); - spin_unlock_bh(&nf_conntrack_lock); + spin_unlock_bh(&nf_conntrack_expect_lock); nf_ct_expect_put(exp); } @@ -191,12 +191,14 @@ void nf_ct_remove_expectations(struct nf_conn *ct) if (!help) return; + spin_lock_bh(&nf_conntrack_expect_lock); hlist_for_each_entry_safe(exp, next, &help->expectations, lnode) { if (del_timer(&exp->timeout)) { nf_ct_unlink_expect(exp); nf_ct_expect_put(exp); } } + spin_unlock_bh(&nf_conntrack_expect_lock); } EXPORT_SYMBOL_GPL(nf_ct_remove_expectations); @@ -231,12 +233,12 @@ static inline int expect_matches(const struct nf_conntrack_expect *a, /* Generally a bad idea to call this: could have matched already. */ void nf_ct_unexpect_related(struct nf_conntrack_expect *exp) { - spin_lock_bh(&nf_conntrack_lock); + spin_lock_bh(&nf_conntrack_expect_lock); if (del_timer(&exp->timeout)) { nf_ct_unlink_expect(exp); nf_ct_expect_put(exp); } - spin_unlock_bh(&nf_conntrack_lock); + spin_unlock_bh(&nf_conntrack_expect_lock); } EXPORT_SYMBOL_GPL(nf_ct_unexpect_related); @@ -349,7 +351,7 @@ static int nf_ct_expect_insert(struct nf_conntrack_expect *exp) setup_timer(&exp->timeout, nf_ct_expectation_timed_out, (unsigned long)exp); helper = rcu_dereference_protected(master_help->helper, - lockdep_is_held(&nf_conntrack_lock)); + lockdep_is_held(&nf_conntrack_expect_lock)); if (helper) { exp->timeout.expires = jiffies + helper->expect_policy[exp->class].timeout * HZ; @@ -409,7 +411,7 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect) } /* Will be over limit? */ helper = rcu_dereference_protected(master_help->helper, - lockdep_is_held(&nf_conntrack_lock)); + lockdep_is_held(&nf_conntrack_expect_lock)); if (helper) { p = &helper->expect_policy[expect->class]; if (p->max_expected && @@ -436,7 +438,7 @@ int nf_ct_expect_related_report(struct nf_conntrack_expect *expect, { int ret; - spin_lock_bh(&nf_conntrack_lock); + spin_lock_bh(&nf_conntrack_expect_lock); ret = __nf_ct_expect_check(expect); if (ret <= 0) goto out; @@ -444,11 +446,11 @@ int nf_ct_expect_related_report(struct nf_conntrack_expect *expect, ret = nf_ct_expect_insert(expect); if (ret < 0) goto out; - spin_unlock_bh(&nf_conntrack_lock); + spin_unlock_bh(&nf_conntrack_expect_lock); nf_ct_expect_event_report(IPEXP_NEW, expect, portid, report); return ret; out: - spin_unlock_bh(&nf_conntrack_lock); + spin_unlock_bh(&nf_conntrack_expect_lock); return ret; } EXPORT_SYMBOL_GPL(nf_ct_expect_related_report); diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c index 70866d192efc..3a3a60b126e0 100644 --- a/net/netfilter/nf_conntrack_h323_main.c +++ b/net/netfilter/nf_conntrack_h323_main.c @@ -1476,7 +1476,7 @@ static int process_rcf(struct sk_buff *skb, struct nf_conn *ct, nf_ct_refresh(ct, skb, info->timeout * HZ); /* Set expect timeout */ - spin_lock_bh(&nf_conntrack_lock); + spin_lock_bh(&nf_conntrack_expect_lock); exp = find_expect(ct, &ct->tuplehash[dir].tuple.dst.u3, info->sig_port[!dir]); if (exp) { @@ -1486,7 +1486,7 @@ static int process_rcf(struct sk_buff *skb, struct nf_conn *ct, nf_ct_dump_tuple(&exp->tuple); set_expect_timeout(exp, info->timeout); } - spin_unlock_bh(&nf_conntrack_lock); + spin_unlock_bh(&nf_conntrack_expect_lock); } return 0; diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 27d9302c2191..29bd704edb85 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -250,16 +250,14 @@ out: } EXPORT_SYMBOL_GPL(__nf_ct_try_assign_helper); +/* appropiate ct lock protecting must be taken by caller */ static inline int unhelp(struct nf_conntrack_tuple_hash *i, const struct nf_conntrack_helper *me) { struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(i); struct nf_conn_help *help = nfct_help(ct); - if (help && rcu_dereference_protected( - help->helper, - lockdep_is_held(&nf_conntrack_lock) - ) == me) { + if (help && rcu_dereference_raw(help->helper) == me) { nf_conntrack_event(IPCT_HELPER, ct); RCU_INIT_POINTER(help->helper, NULL); } @@ -284,17 +282,17 @@ static LIST_HEAD(nf_ct_helper_expectfn_list); void nf_ct_helper_expectfn_register(struct nf_ct_helper_expectfn *n) { - spin_lock_bh(&nf_conntrack_lock); + spin_lock_bh(&nf_conntrack_expect_lock); list_add_rcu(&n->head, &nf_ct_helper_expectfn_list); - spin_unlock_bh(&nf_conntrack_lock); + spin_unlock_bh(&nf_conntrack_expect_lock); } EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_register); void nf_ct_helper_expectfn_unregister(struct nf_ct_helper_expectfn *n) { - spin_lock_bh(&nf_conntrack_lock); + spin_lock_bh(&nf_conntrack_expect_lock); list_del_rcu(&n->head); - spin_unlock_bh(&nf_conntrack_lock); + spin_unlock_bh(&nf_conntrack_expect_lock); } EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_unregister); @@ -399,13 +397,14 @@ static void __nf_conntrack_helper_unregister(struct nf_conntrack_helper *me, int cpu; /* Get rid of expectations */ + spin_lock_bh(&nf_conntrack_expect_lock); for (i = 0; i < nf_ct_expect_hsize; i++) { hlist_for_each_entry_safe(exp, next, &net->ct.expect_hash[i], hnode) { struct nf_conn_help *help = nfct_help(exp->master); if ((rcu_dereference_protected( help->helper, - lockdep_is_held(&nf_conntrack_lock) + lockdep_is_held(&nf_conntrack_expect_lock) ) == me || exp->helper == me) && del_timer(&exp->timeout)) { nf_ct_unlink_expect(exp); @@ -413,6 +412,7 @@ static void __nf_conntrack_helper_unregister(struct nf_conntrack_helper *me, } } } + spin_unlock_bh(&nf_conntrack_expect_lock); /* Get rid of expecteds, set helpers to NULL. */ for_each_possible_cpu(cpu) { @@ -423,10 +423,12 @@ static void __nf_conntrack_helper_unregister(struct nf_conntrack_helper *me, unhelp(h, me); spin_unlock_bh(&pcpu->lock); } + spin_lock_bh(&nf_conntrack_lock); for (i = 0; i < net->ct.htable_size; i++) { hlist_nulls_for_each_entry(h, nn, &net->ct.hash[i], hnnode) unhelp(h, me); } + spin_unlock_bh(&nf_conntrack_lock); } void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me) @@ -444,10 +446,8 @@ void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me) synchronize_rcu(); rtnl_lock(); - spin_lock_bh(&nf_conntrack_lock); for_each_net(net) __nf_conntrack_helper_unregister(me, net); - spin_unlock_bh(&nf_conntrack_lock); rtnl_unlock(); } EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister); diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 4ac8ce68bc16..be4d1b0bbb6a 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -1376,14 +1376,14 @@ ctnetlink_change_helper(struct nf_conn *ct, const struct nlattr * const cda[]) nf_ct_protonum(ct)); if (helper == NULL) { #ifdef CONFIG_MODULES - spin_unlock_bh(&nf_conntrack_lock); + spin_unlock_bh(&nf_conntrack_expect_lock); if (request_module("nfct-helper-%s", helpname) < 0) { - spin_lock_bh(&nf_conntrack_lock); + spin_lock_bh(&nf_conntrack_expect_lock); return -EOPNOTSUPP; } - spin_lock_bh(&nf_conntrack_lock); + spin_lock_bh(&nf_conntrack_expect_lock); helper = __nf_conntrack_helper_find(helpname, nf_ct_l3num(ct), nf_ct_protonum(ct)); if (helper) @@ -1821,9 +1821,9 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, err = -EEXIST; ct = nf_ct_tuplehash_to_ctrack(h); if (!(nlh->nlmsg_flags & NLM_F_EXCL)) { - spin_lock_bh(&nf_conntrack_lock); + spin_lock_bh(&nf_conntrack_expect_lock); err = ctnetlink_change_conntrack(ct, cda); - spin_unlock_bh(&nf_conntrack_lock); + spin_unlock_bh(&nf_conntrack_expect_lock); if (err == 0) { nf_conntrack_eventmask_report((1 << IPCT_REPLY) | (1 << IPCT_ASSURED) | @@ -2152,9 +2152,9 @@ ctnetlink_nfqueue_parse(const struct nlattr *attr, struct nf_conn *ct) if (ret < 0) return ret; - spin_lock_bh(&nf_conntrack_lock); + spin_lock_bh(&nf_conntrack_expect_lock); ret = ctnetlink_nfqueue_parse_ct((const struct nlattr **)cda, ct); - spin_unlock_bh(&nf_conntrack_lock); + spin_unlock_bh(&nf_conntrack_expect_lock); return ret; } @@ -2709,13 +2709,13 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb, } /* after list removal, usage count == 1 */ - spin_lock_bh(&nf_conntrack_lock); + spin_lock_bh(&nf_conntrack_expect_lock); if (del_timer(&exp->timeout)) { nf_ct_unlink_expect_report(exp, NETLINK_CB(skb).portid, nlmsg_report(nlh)); nf_ct_expect_put(exp); } - spin_unlock_bh(&nf_conntrack_lock); + spin_unlock_bh(&nf_conntrack_expect_lock); /* have to put what we 'get' above. * after this line usage count == 0 */ nf_ct_expect_put(exp); @@ -2724,7 +2724,7 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb, struct nf_conn_help *m_help; /* delete all expectations for this helper */ - spin_lock_bh(&nf_conntrack_lock); + spin_lock_bh(&nf_conntrack_expect_lock); for (i = 0; i < nf_ct_expect_hsize; i++) { hlist_for_each_entry_safe(exp, next, &net->ct.expect_hash[i], @@ -2739,10 +2739,10 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb, } } } - spin_unlock_bh(&nf_conntrack_lock); + spin_unlock_bh(&nf_conntrack_expect_lock); } else { /* This basically means we have to flush everything*/ - spin_lock_bh(&nf_conntrack_lock); + spin_lock_bh(&nf_conntrack_expect_lock); for (i = 0; i < nf_ct_expect_hsize; i++) { hlist_for_each_entry_safe(exp, next, &net->ct.expect_hash[i], @@ -2755,7 +2755,7 @@ ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb, } } } - spin_unlock_bh(&nf_conntrack_lock); + spin_unlock_bh(&nf_conntrack_expect_lock); } return 0; @@ -2981,11 +2981,11 @@ ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb, if (err < 0) return err; - spin_lock_bh(&nf_conntrack_lock); + spin_lock_bh(&nf_conntrack_expect_lock); exp = __nf_ct_expect_find(net, zone, &tuple); if (!exp) { - spin_unlock_bh(&nf_conntrack_lock); + spin_unlock_bh(&nf_conntrack_expect_lock); err = -ENOENT; if (nlh->nlmsg_flags & NLM_F_CREATE) { err = ctnetlink_create_expect(net, zone, cda, @@ -2999,7 +2999,7 @@ ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb, err = -EEXIST; if (!(nlh->nlmsg_flags & NLM_F_EXCL)) err = ctnetlink_change_expect(exp, cda); - spin_unlock_bh(&nf_conntrack_lock); + spin_unlock_bh(&nf_conntrack_expect_lock); return err; } diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 466410eaa482..4c3ba1c8d682 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -800,7 +800,7 @@ static int refresh_signalling_expectation(struct nf_conn *ct, struct hlist_node *next; int found = 0; - spin_lock_bh(&nf_conntrack_lock); + spin_lock_bh(&nf_conntrack_expect_lock); hlist_for_each_entry_safe(exp, next, &help->expectations, lnode) { if (exp->class != SIP_EXPECT_SIGNALLING || !nf_inet_addr_cmp(&exp->tuple.dst.u3, addr) || @@ -815,7 +815,7 @@ static int refresh_signalling_expectation(struct nf_conn *ct, found = 1; break; } - spin_unlock_bh(&nf_conntrack_lock); + spin_unlock_bh(&nf_conntrack_expect_lock); return found; } @@ -825,7 +825,7 @@ static void flush_expectations(struct nf_conn *ct, bool media) struct nf_conntrack_expect *exp; struct hlist_node *next; - spin_lock_bh(&nf_conntrack_lock); + spin_lock_bh(&nf_conntrack_expect_lock); hlist_for_each_entry_safe(exp, next, &help->expectations, lnode) { if ((exp->class != SIP_EXPECT_SIGNALLING) ^ media) continue; @@ -836,7 +836,7 @@ static void flush_expectations(struct nf_conn *ct, bool media) if (!media) break; } - spin_unlock_bh(&nf_conntrack_lock); + spin_unlock_bh(&nf_conntrack_expect_lock); } static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, -- cgit v1.2.3-71-gd317 From 93bb0ceb75be2fdfa9fc0dd1fb522d9ada515d9c Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Mon, 3 Mar 2014 14:46:13 +0100 Subject: netfilter: conntrack: remove central spinlock nf_conntrack_lock nf_conntrack_lock is a monolithic lock and suffers from huge contention on current generation servers (8 or more core/threads). Perf locking congestion is clear on base kernel: - 72.56% ksoftirqd/6 [kernel.kallsyms] [k] _raw_spin_lock_bh - _raw_spin_lock_bh + 25.33% init_conntrack + 24.86% nf_ct_delete_from_lists + 24.62% __nf_conntrack_confirm + 24.38% destroy_conntrack + 0.70% tcp_packet + 2.21% ksoftirqd/6 [kernel.kallsyms] [k] fib_table_lookup + 1.15% ksoftirqd/6 [kernel.kallsyms] [k] __slab_free + 0.77% ksoftirqd/6 [kernel.kallsyms] [k] inet_getpeer + 0.70% ksoftirqd/6 [nf_conntrack] [k] nf_ct_delete + 0.55% ksoftirqd/6 [ip_tables] [k] ipt_do_table This patch change conntrack locking and provides a huge performance improvement. SYN-flood attack tested on a 24-core E5-2695v2(ES) with 10Gbit/s ixgbe (with tool trafgen): Base kernel: 810.405 new conntrack/sec After patch: 2.233.876 new conntrack/sec Notice other floods attack (SYN+ACK or ACK) can easily be deflected using: # iptables -A INPUT -m state --state INVALID -j DROP # sysctl -w net/netfilter/nf_conntrack_tcp_loose=0 Use an array of hashed spinlocks to protect insertions/deletions of conntracks into the hash table. 1024 spinlocks seem to give good results, at minimal cost (4KB memory). Due to lockdep max depth, 1024 becomes 8 if CONFIG_LOCKDEP=y The hash resize is a bit tricky, because we need to take all locks in the array. A seqcount_t is used to synchronize the hash table users with the resizing process. Signed-off-by: Eric Dumazet Signed-off-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller Reviewed-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_core.h | 7 +- include/net/netns/conntrack.h | 2 + net/netfilter/nf_conntrack_core.c | 219 ++++++++++++++++++++++-------- net/netfilter/nf_conntrack_helper.c | 12 +- net/netfilter/nf_conntrack_netlink.c | 15 +- 5 files changed, 188 insertions(+), 67 deletions(-) (limited to 'include/net') diff --git a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h index d12a631d0415..cc0c18827602 100644 --- a/include/net/netfilter/nf_conntrack_core.h +++ b/include/net/netfilter/nf_conntrack_core.h @@ -77,7 +77,12 @@ print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple, const struct nf_conntrack_l3proto *l3proto, const struct nf_conntrack_l4proto *proto); -extern spinlock_t nf_conntrack_lock ; +#ifdef CONFIG_LOCKDEP +# define CONNTRACK_LOCKS 8 +#else +# define CONNTRACK_LOCKS 1024 +#endif +extern spinlock_t nf_conntrack_locks[CONNTRACK_LOCKS]; extern spinlock_t nf_conntrack_expect_lock; diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h index c6a8994e9922..773cce308bc6 100644 --- a/include/net/netns/conntrack.h +++ b/include/net/netns/conntrack.h @@ -5,6 +5,7 @@ #include #include #include +#include struct ctl_table_header; struct nf_conntrack_ecache; @@ -90,6 +91,7 @@ struct netns_ct { int sysctl_checksum; unsigned int htable_size; + seqcount_t generation; struct kmem_cache *nf_conntrack_cachep; struct hlist_nulls_head *hash; struct hlist_head *expect_hash; diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 4cdf1ade1530..5d1e7d126ebd 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -60,12 +60,60 @@ int (*nfnetlink_parse_nat_setup_hook)(struct nf_conn *ct, const struct nlattr *attr) __read_mostly; EXPORT_SYMBOL_GPL(nfnetlink_parse_nat_setup_hook); -DEFINE_SPINLOCK(nf_conntrack_lock); -EXPORT_SYMBOL_GPL(nf_conntrack_lock); +__cacheline_aligned_in_smp spinlock_t nf_conntrack_locks[CONNTRACK_LOCKS]; +EXPORT_SYMBOL_GPL(nf_conntrack_locks); __cacheline_aligned_in_smp DEFINE_SPINLOCK(nf_conntrack_expect_lock); EXPORT_SYMBOL_GPL(nf_conntrack_expect_lock); +static void nf_conntrack_double_unlock(unsigned int h1, unsigned int h2) +{ + h1 %= CONNTRACK_LOCKS; + h2 %= CONNTRACK_LOCKS; + spin_unlock(&nf_conntrack_locks[h1]); + if (h1 != h2) + spin_unlock(&nf_conntrack_locks[h2]); +} + +/* return true if we need to recompute hashes (in case hash table was resized) */ +static bool nf_conntrack_double_lock(struct net *net, unsigned int h1, + unsigned int h2, unsigned int sequence) +{ + h1 %= CONNTRACK_LOCKS; + h2 %= CONNTRACK_LOCKS; + if (h1 <= h2) { + spin_lock(&nf_conntrack_locks[h1]); + if (h1 != h2) + spin_lock_nested(&nf_conntrack_locks[h2], + SINGLE_DEPTH_NESTING); + } else { + spin_lock(&nf_conntrack_locks[h2]); + spin_lock_nested(&nf_conntrack_locks[h1], + SINGLE_DEPTH_NESTING); + } + if (read_seqcount_retry(&net->ct.generation, sequence)) { + nf_conntrack_double_unlock(h1, h2); + return true; + } + return false; +} + +static void nf_conntrack_all_lock(void) +{ + int i; + + for (i = 0; i < CONNTRACK_LOCKS; i++) + spin_lock_nested(&nf_conntrack_locks[i], i); +} + +static void nf_conntrack_all_unlock(void) +{ + int i; + + for (i = 0; i < CONNTRACK_LOCKS; i++) + spin_unlock(&nf_conntrack_locks[i]); +} + unsigned int nf_conntrack_htable_size __read_mostly; EXPORT_SYMBOL_GPL(nf_conntrack_htable_size); @@ -280,15 +328,28 @@ destroy_conntrack(struct nf_conntrack *nfct) static void nf_ct_delete_from_lists(struct nf_conn *ct) { struct net *net = nf_ct_net(ct); + unsigned int hash, reply_hash; + u16 zone = nf_ct_zone(ct); + unsigned int sequence; nf_ct_helper_destroy(ct); - spin_lock_bh(&nf_conntrack_lock); - /* Inside lock so preempt is disabled on module removal path. - * Otherwise we can get spurious warnings. */ - NF_CT_STAT_INC(net, delete_list); + + local_bh_disable(); + do { + sequence = read_seqcount_begin(&net->ct.generation); + hash = hash_conntrack(net, zone, + &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + reply_hash = hash_conntrack(net, zone, + &ct->tuplehash[IP_CT_DIR_REPLY].tuple); + } while (nf_conntrack_double_lock(net, hash, reply_hash, sequence)); + clean_from_lists(ct); + nf_conntrack_double_unlock(hash, reply_hash); + nf_ct_add_to_dying_list(ct); - spin_unlock_bh(&nf_conntrack_lock); + + NF_CT_STAT_INC(net, delete_list); + local_bh_enable(); } static void death_by_event(unsigned long ul_conntrack) @@ -372,8 +433,6 @@ nf_ct_key_equal(struct nf_conntrack_tuple_hash *h, * Warning : * - Caller must take a reference on returned object * and recheck nf_ct_tuple_equal(tuple, &h->tuple) - * OR - * - Caller must lock nf_conntrack_lock before calling this function */ static struct nf_conntrack_tuple_hash * ____nf_conntrack_find(struct net *net, u16 zone, @@ -467,14 +526,18 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct) struct nf_conntrack_tuple_hash *h; struct hlist_nulls_node *n; u16 zone; + unsigned int sequence; zone = nf_ct_zone(ct); - hash = hash_conntrack(net, zone, - &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); - reply_hash = hash_conntrack(net, zone, - &ct->tuplehash[IP_CT_DIR_REPLY].tuple); - spin_lock_bh(&nf_conntrack_lock); + local_bh_disable(); + do { + sequence = read_seqcount_begin(&net->ct.generation); + hash = hash_conntrack(net, zone, + &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + reply_hash = hash_conntrack(net, zone, + &ct->tuplehash[IP_CT_DIR_REPLY].tuple); + } while (nf_conntrack_double_lock(net, hash, reply_hash, sequence)); /* See if there's one in the list already, including reverse */ hlist_nulls_for_each_entry(h, n, &net->ct.hash[hash], hnnode) @@ -493,14 +556,15 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct) /* The caller holds a reference to this object */ atomic_set(&ct->ct_general.use, 2); __nf_conntrack_hash_insert(ct, hash, reply_hash); + nf_conntrack_double_unlock(hash, reply_hash); NF_CT_STAT_INC(net, insert); - spin_unlock_bh(&nf_conntrack_lock); - + local_bh_enable(); return 0; out: + nf_conntrack_double_unlock(hash, reply_hash); NF_CT_STAT_INC(net, insert_failed); - spin_unlock_bh(&nf_conntrack_lock); + local_bh_enable(); return -EEXIST; } EXPORT_SYMBOL_GPL(nf_conntrack_hash_check_insert); @@ -540,6 +604,7 @@ __nf_conntrack_confirm(struct sk_buff *skb) enum ip_conntrack_info ctinfo; struct net *net; u16 zone; + unsigned int sequence; ct = nf_ct_get(skb, &ctinfo); net = nf_ct_net(ct); @@ -552,31 +617,37 @@ __nf_conntrack_confirm(struct sk_buff *skb) return NF_ACCEPT; zone = nf_ct_zone(ct); - /* reuse the hash saved before */ - hash = *(unsigned long *)&ct->tuplehash[IP_CT_DIR_REPLY].hnnode.pprev; - hash = hash_bucket(hash, net); - reply_hash = hash_conntrack(net, zone, - &ct->tuplehash[IP_CT_DIR_REPLY].tuple); + local_bh_disable(); + + do { + sequence = read_seqcount_begin(&net->ct.generation); + /* reuse the hash saved before */ + hash = *(unsigned long *)&ct->tuplehash[IP_CT_DIR_REPLY].hnnode.pprev; + hash = hash_bucket(hash, net); + reply_hash = hash_conntrack(net, zone, + &ct->tuplehash[IP_CT_DIR_REPLY].tuple); + + } while (nf_conntrack_double_lock(net, hash, reply_hash, sequence)); /* We're not in hash table, and we refuse to set up related - connections for unconfirmed conns. But packet copies and - REJECT will give spurious warnings here. */ + * connections for unconfirmed conns. But packet copies and + * REJECT will give spurious warnings here. + */ /* NF_CT_ASSERT(atomic_read(&ct->ct_general.use) == 1); */ /* No external references means no one else could have - confirmed us. */ + * confirmed us. + */ NF_CT_ASSERT(!nf_ct_is_confirmed(ct)); pr_debug("Confirming conntrack %p\n", ct); - - spin_lock_bh(&nf_conntrack_lock); - /* We have to check the DYING flag inside the lock to prevent a race against nf_ct_get_next_corpse() possibly called from user context, else we insert an already 'dead' hash, blocking further use of that particular connection -JM */ if (unlikely(nf_ct_is_dying(ct))) { - spin_unlock_bh(&nf_conntrack_lock); + nf_conntrack_double_unlock(hash, reply_hash); + local_bh_enable(); return NF_ACCEPT; } @@ -618,8 +689,9 @@ __nf_conntrack_confirm(struct sk_buff *skb) * stores are visible. */ __nf_conntrack_hash_insert(ct, hash, reply_hash); + nf_conntrack_double_unlock(hash, reply_hash); NF_CT_STAT_INC(net, insert); - spin_unlock_bh(&nf_conntrack_lock); + local_bh_enable(); help = nfct_help(ct); if (help && help->helper) @@ -630,8 +702,9 @@ __nf_conntrack_confirm(struct sk_buff *skb) return NF_ACCEPT; out: + nf_conntrack_double_unlock(hash, reply_hash); NF_CT_STAT_INC(net, insert_failed); - spin_unlock_bh(&nf_conntrack_lock); + local_bh_enable(); return NF_DROP; } EXPORT_SYMBOL_GPL(__nf_conntrack_confirm); @@ -674,39 +747,48 @@ EXPORT_SYMBOL_GPL(nf_conntrack_tuple_taken); /* There's a small race here where we may free a just-assured connection. Too bad: we're in trouble anyway. */ -static noinline int early_drop(struct net *net, unsigned int hash) +static noinline int early_drop(struct net *net, unsigned int _hash) { /* Use oldest entry, which is roughly LRU */ struct nf_conntrack_tuple_hash *h; struct nf_conn *ct = NULL, *tmp; struct hlist_nulls_node *n; - unsigned int i, cnt = 0; + unsigned int i = 0, cnt = 0; int dropped = 0; + unsigned int hash, sequence; + spinlock_t *lockp; - rcu_read_lock(); - for (i = 0; i < net->ct.htable_size; i++) { + local_bh_disable(); +restart: + sequence = read_seqcount_begin(&net->ct.generation); + hash = hash_bucket(_hash, net); + for (; i < net->ct.htable_size; i++) { + lockp = &nf_conntrack_locks[hash % CONNTRACK_LOCKS]; + spin_lock(lockp); + if (read_seqcount_retry(&net->ct.generation, sequence)) { + spin_unlock(lockp); + goto restart; + } hlist_nulls_for_each_entry_rcu(h, n, &net->ct.hash[hash], hnnode) { tmp = nf_ct_tuplehash_to_ctrack(h); - if (!test_bit(IPS_ASSURED_BIT, &tmp->status)) + if (!test_bit(IPS_ASSURED_BIT, &tmp->status) && + !nf_ct_is_dying(tmp) && + atomic_inc_not_zero(&tmp->ct_general.use)) { ct = tmp; + break; + } cnt++; } - if (ct != NULL) { - if (likely(!nf_ct_is_dying(ct) && - atomic_inc_not_zero(&ct->ct_general.use))) - break; - else - ct = NULL; - } + hash = (hash + 1) % net->ct.htable_size; + spin_unlock(lockp); - if (cnt >= NF_CT_EVICTION_RANGE) + if (ct || cnt >= NF_CT_EVICTION_RANGE) break; - hash = (hash + 1) % net->ct.htable_size; } - rcu_read_unlock(); + local_bh_enable(); if (!ct) return dropped; @@ -755,7 +837,7 @@ __nf_conntrack_alloc(struct net *net, u16 zone, if (nf_conntrack_max && unlikely(atomic_read(&net->ct.count) > nf_conntrack_max)) { - if (!early_drop(net, hash_bucket(hash, net))) { + if (!early_drop(net, hash)) { atomic_dec(&net->ct.count); net_warn_ratelimited("nf_conntrack: table full, dropping packet\n"); return ERR_PTR(-ENOMEM); @@ -1304,18 +1386,24 @@ get_next_corpse(struct net *net, int (*iter)(struct nf_conn *i, void *data), struct nf_conn *ct; struct hlist_nulls_node *n; int cpu; + spinlock_t *lockp; - spin_lock_bh(&nf_conntrack_lock); for (; *bucket < net->ct.htable_size; (*bucket)++) { - hlist_nulls_for_each_entry(h, n, &net->ct.hash[*bucket], hnnode) { - if (NF_CT_DIRECTION(h) != IP_CT_DIR_ORIGINAL) - continue; - ct = nf_ct_tuplehash_to_ctrack(h); - if (iter(ct, data)) - goto found; + lockp = &nf_conntrack_locks[*bucket % CONNTRACK_LOCKS]; + local_bh_disable(); + spin_lock(lockp); + if (*bucket < net->ct.htable_size) { + hlist_nulls_for_each_entry(h, n, &net->ct.hash[*bucket], hnnode) { + if (NF_CT_DIRECTION(h) != IP_CT_DIR_ORIGINAL) + continue; + ct = nf_ct_tuplehash_to_ctrack(h); + if (iter(ct, data)) + goto found; + } } + spin_unlock(lockp); + local_bh_enable(); } - spin_unlock_bh(&nf_conntrack_lock); for_each_possible_cpu(cpu) { struct ct_pcpu *pcpu = per_cpu_ptr(net->ct.pcpu_lists, cpu); @@ -1331,7 +1419,8 @@ get_next_corpse(struct net *net, int (*iter)(struct nf_conn *i, void *data), return NULL; found: atomic_inc(&ct->ct_general.use); - spin_unlock_bh(&nf_conntrack_lock); + spin_unlock(lockp); + local_bh_enable(); return ct; } @@ -1532,12 +1621,16 @@ int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp) if (!hash) return -ENOMEM; + local_bh_disable(); + nf_conntrack_all_lock(); + write_seqcount_begin(&init_net.ct.generation); + /* Lookups in the old hash might happen in parallel, which means we * might get false negatives during connection lookup. New connections * created because of a false negative won't make it into the hash - * though since that required taking the lock. + * though since that required taking the locks. */ - spin_lock_bh(&nf_conntrack_lock); + for (i = 0; i < init_net.ct.htable_size; i++) { while (!hlist_nulls_empty(&init_net.ct.hash[i])) { h = hlist_nulls_entry(init_net.ct.hash[i].first, @@ -1554,7 +1647,10 @@ int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp) init_net.ct.htable_size = nf_conntrack_htable_size = hashsize; init_net.ct.hash = hash; - spin_unlock_bh(&nf_conntrack_lock); + + write_seqcount_end(&init_net.ct.generation); + nf_conntrack_all_unlock(); + local_bh_enable(); nf_ct_free_hashtable(old_hash, old_size); return 0; @@ -1576,7 +1672,10 @@ EXPORT_SYMBOL_GPL(nf_ct_untracked_status_or); int nf_conntrack_init_start(void) { int max_factor = 8; - int ret, cpu; + int i, ret, cpu; + + for (i = 0; i < ARRAY_SIZE(nf_conntrack_locks); i++) + spin_lock_init(&nf_conntrack_locks[i]); /* Idea from tcp.c: use 1/16384 of memory. On i386: 32MB * machine has 512 buckets. >= 1GB machines have 16384 buckets. */ diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 29bd704edb85..5b3eae7d4c9a 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -423,12 +423,16 @@ static void __nf_conntrack_helper_unregister(struct nf_conntrack_helper *me, unhelp(h, me); spin_unlock_bh(&pcpu->lock); } - spin_lock_bh(&nf_conntrack_lock); + local_bh_disable(); for (i = 0; i < net->ct.htable_size; i++) { - hlist_nulls_for_each_entry(h, nn, &net->ct.hash[i], hnnode) - unhelp(h, me); + spin_lock(&nf_conntrack_locks[i % CONNTRACK_LOCKS]); + if (i < net->ct.htable_size) { + hlist_nulls_for_each_entry(h, nn, &net->ct.hash[i], hnnode) + unhelp(h, me); + } + spin_unlock(&nf_conntrack_locks[i % CONNTRACK_LOCKS]); } - spin_unlock_bh(&nf_conntrack_lock); + local_bh_enable(); } void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me) diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index be4d1b0bbb6a..8d778a9fd063 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -764,14 +764,23 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb) struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); u_int8_t l3proto = nfmsg->nfgen_family; int res; + spinlock_t *lockp; + #ifdef CONFIG_NF_CONNTRACK_MARK const struct ctnetlink_dump_filter *filter = cb->data; #endif - spin_lock_bh(&nf_conntrack_lock); last = (struct nf_conn *)cb->args[1]; + + local_bh_disable(); for (; cb->args[0] < net->ct.htable_size; cb->args[0]++) { restart: + lockp = &nf_conntrack_locks[cb->args[0] % CONNTRACK_LOCKS]; + spin_lock(lockp); + if (cb->args[0] >= net->ct.htable_size) { + spin_unlock(lockp); + goto out; + } hlist_nulls_for_each_entry(h, n, &net->ct.hash[cb->args[0]], hnnode) { if (NF_CT_DIRECTION(h) != IP_CT_DIR_ORIGINAL) @@ -803,16 +812,18 @@ restart: if (res < 0) { nf_conntrack_get(&ct->ct_general); cb->args[1] = (unsigned long)ct; + spin_unlock(lockp); goto out; } } + spin_unlock(lockp); if (cb->args[1]) { cb->args[1] = 0; goto restart; } } out: - spin_unlock_bh(&nf_conntrack_lock); + local_bh_enable(); if (last) nf_ct_put(last); -- cgit v1.2.3-71-gd317 From 62472bcefb56ae9c3a6be3284949ce758656cdec Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Fri, 7 Mar 2014 19:08:30 +0100 Subject: netfilter: nf_tables: restore context for expression destructors In order to fix set destruction notifications and get rid of unnecessary members in private data structures, pass the context to expressions' destructor functions again. In order to do so, replace various members in the nft_rule_trans structure by the full context. Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 13 ++++--------- net/netfilter/nf_tables_api.c | 34 +++++++++++++++++----------------- net/netfilter/nft_compat.c | 4 ++-- net/netfilter/nft_ct.c | 3 ++- net/netfilter/nft_immediate.c | 3 ++- net/netfilter/nft_log.c | 3 ++- net/netfilter/nft_lookup.c | 3 ++- 7 files changed, 31 insertions(+), 32 deletions(-) (limited to 'include/net') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 5af56da6d6c6..e6bc14d8fa9a 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -289,7 +289,8 @@ struct nft_expr_ops { int (*init)(const struct nft_ctx *ctx, const struct nft_expr *expr, const struct nlattr * const tb[]); - void (*destroy)(const struct nft_expr *expr); + void (*destroy)(const struct nft_ctx *ctx, + const struct nft_expr *expr); int (*dump)(struct sk_buff *skb, const struct nft_expr *expr); int (*validate)(const struct nft_ctx *ctx, @@ -343,19 +344,13 @@ struct nft_rule { * struct nft_rule_trans - nf_tables rule update in transaction * * @list: used internally + * @ctx: rule context * @rule: rule that needs to be updated - * @chain: chain that this rule belongs to - * @table: table for which this chain applies - * @nlh: netlink header of the message that contain this update - * @family: family expressesed as AF_* */ struct nft_rule_trans { struct list_head list; + struct nft_ctx ctx; struct nft_rule *rule; - const struct nft_chain *chain; - const struct nft_table *table; - const struct nlmsghdr *nlh; - u8 family; }; static inline struct nft_expr *nft_expr_first(const struct nft_rule *rule) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 611afc0cf2d5..2c10c3fe78c3 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -1253,10 +1253,11 @@ err1: return err; } -static void nf_tables_expr_destroy(struct nft_expr *expr) +static void nf_tables_expr_destroy(const struct nft_ctx *ctx, + struct nft_expr *expr) { if (expr->ops->destroy) - expr->ops->destroy(expr); + expr->ops->destroy(ctx, expr); module_put(expr->ops->type->owner); } @@ -1536,7 +1537,8 @@ err: return err; } -static void nf_tables_rule_destroy(struct nft_rule *rule) +static void nf_tables_rule_destroy(const struct nft_ctx *ctx, + struct nft_rule *rule) { struct nft_expr *expr; @@ -1546,7 +1548,7 @@ static void nf_tables_rule_destroy(struct nft_rule *rule) */ expr = nft_expr_first(rule); while (expr->ops && expr != nft_expr_last(rule)) { - nf_tables_expr_destroy(expr); + nf_tables_expr_destroy(ctx, expr); expr = nft_expr_next(expr); } kfree(rule); @@ -1565,11 +1567,8 @@ nf_tables_trans_add(struct nft_ctx *ctx, struct nft_rule *rule) if (rupd == NULL) return NULL; - rupd->chain = ctx->chain; - rupd->table = ctx->table; + rupd->ctx = *ctx; rupd->rule = rule; - rupd->family = ctx->afi->family; - rupd->nlh = ctx->nlh; list_add_tail(&rupd->list, &ctx->net->nft.commit_list); return rupd; @@ -1721,7 +1720,7 @@ err3: kfree(repl); } err2: - nf_tables_rule_destroy(rule); + nf_tables_rule_destroy(&ctx, rule); err1: for (i = 0; i < n; i++) { if (info[i].ops != NULL) @@ -1831,10 +1830,10 @@ static int nf_tables_commit(struct sk_buff *skb) */ if (nft_rule_is_active(net, rupd->rule)) { nft_rule_clear(net, rupd->rule); - nf_tables_rule_notify(skb, rupd->nlh, rupd->table, - rupd->chain, rupd->rule, - NFT_MSG_NEWRULE, 0, - rupd->family); + nf_tables_rule_notify(skb, rupd->ctx.nlh, + rupd->ctx.table, rupd->ctx.chain, + rupd->rule, NFT_MSG_NEWRULE, 0, + rupd->ctx.afi->family); list_del(&rupd->list); kfree(rupd); continue; @@ -1842,9 +1841,10 @@ static int nf_tables_commit(struct sk_buff *skb) /* This rule is in the past, get rid of it */ list_del_rcu(&rupd->rule->list); - nf_tables_rule_notify(skb, rupd->nlh, rupd->table, rupd->chain, + nf_tables_rule_notify(skb, rupd->ctx.nlh, + rupd->ctx.table, rupd->ctx.chain, rupd->rule, NFT_MSG_DELRULE, 0, - rupd->family); + rupd->ctx.afi->family); } /* Make sure we don't see any packet traversing old rules */ @@ -1852,7 +1852,7 @@ static int nf_tables_commit(struct sk_buff *skb) /* Now we can safely release unused old rules */ list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) { - nf_tables_rule_destroy(rupd->rule); + nf_tables_rule_destroy(&rupd->ctx, rupd->rule); list_del(&rupd->list); kfree(rupd); } @@ -1881,7 +1881,7 @@ static int nf_tables_abort(struct sk_buff *skb) synchronize_rcu(); list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) { - nf_tables_rule_destroy(rupd->rule); + nf_tables_rule_destroy(&rupd->ctx, rupd->rule); list_del(&rupd->list); kfree(rupd); } diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c index 82cb8236f8a1..8a779be832fb 100644 --- a/net/netfilter/nft_compat.c +++ b/net/netfilter/nft_compat.c @@ -192,7 +192,7 @@ err: } static void -nft_target_destroy(const struct nft_expr *expr) +nft_target_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) { struct xt_target *target = expr->ops->data; @@ -379,7 +379,7 @@ err: } static void -nft_match_destroy(const struct nft_expr *expr) +nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) { struct xt_match *match = expr->ops->data; diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index e59b08f9ccbd..65a2c7b6a7a0 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -321,7 +321,8 @@ static int nft_ct_init(const struct nft_ctx *ctx, return 0; } -static void nft_ct_destroy(const struct nft_expr *expr) +static void nft_ct_destroy(const struct nft_ctx *ctx, + const struct nft_expr *expr) { struct nft_ct *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c index f169501f1ad4..810385eb7249 100644 --- a/net/netfilter/nft_immediate.c +++ b/net/netfilter/nft_immediate.c @@ -70,7 +70,8 @@ err1: return err; } -static void nft_immediate_destroy(const struct nft_expr *expr) +static void nft_immediate_destroy(const struct nft_ctx *ctx, + const struct nft_expr *expr) { const struct nft_immediate_expr *priv = nft_expr_priv(expr); return nft_data_uninit(&priv->data, nft_dreg_to_type(priv->dreg)); diff --git a/net/netfilter/nft_log.c b/net/netfilter/nft_log.c index 26c5154e05f3..10cfb156cdf4 100644 --- a/net/netfilter/nft_log.c +++ b/net/netfilter/nft_log.c @@ -74,7 +74,8 @@ static int nft_log_init(const struct nft_ctx *ctx, return 0; } -static void nft_log_destroy(const struct nft_expr *expr) +static void nft_log_destroy(const struct nft_ctx *ctx, + const struct nft_expr *expr) { struct nft_log *priv = nft_expr_priv(expr); diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c index bb4ef4cccb6e..953978e8f0ba 100644 --- a/net/netfilter/nft_lookup.c +++ b/net/netfilter/nft_lookup.c @@ -89,7 +89,8 @@ static int nft_lookup_init(const struct nft_ctx *ctx, return 0; } -static void nft_lookup_destroy(const struct nft_expr *expr) +static void nft_lookup_destroy(const struct nft_ctx *ctx, + const struct nft_expr *expr) { struct nft_lookup *priv = nft_expr_priv(expr); -- cgit v1.2.3-71-gd317 From 7ee4ea3692f20b87b0e0d3884d5b2d22ec1a2df0 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 9 Mar 2014 12:19:17 -0700 Subject: Bluetooth: Add support for handling signature resolving keys The connection signature resolving key (CSRK) is used for attribute protocol signed write procedures. This change generates a new local key during pairing and requests the peer key as well. Newly generated key and received key will be provided to userspace using the New Signature Resolving Key management event. The Master CSRK can be used for verification of remote signed write PDUs and the Slave CSRK can be used for sending signed write PDUs to the remote device. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 8 +++++ include/net/bluetooth/mgmt.h | 12 +++++++ net/bluetooth/mgmt.c | 30 ++++++++++++++++++ net/bluetooth/smp.c | 67 +++++++++++++++++++++++++++++++++++++--- net/bluetooth/smp.h | 2 ++ 5 files changed, 114 insertions(+), 5 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index dbb788e4f265..e869884fbfa9 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -91,6 +91,13 @@ struct bt_uuid { u8 svc_hint; }; +struct smp_csrk { + bdaddr_t bdaddr; + u8 bdaddr_type; + u8 master; + u8 val[16]; +}; + struct smp_ltk { struct list_head list; bdaddr_t bdaddr; @@ -1265,6 +1272,7 @@ int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key); void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk); +void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk); void mgmt_reenable_advertising(struct hci_dev *hdev); void mgmt_smp_complete(struct hci_conn *conn, bool complete); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 0326648fd799..d4b571c2f9fd 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -551,3 +551,15 @@ struct mgmt_ev_new_irk { bdaddr_t rpa; struct mgmt_irk_info irk; } __packed; + +struct mgmt_csrk_info { + struct mgmt_addr_info addr; + __u8 master; + __u8 val[16]; +} __packed; + +#define MGMT_EV_NEW_CSRK 0x0019 +struct mgmt_ev_new_csrk { + __u8 store_hint; + struct mgmt_csrk_info key; +} __packed; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f2397e7ad385..9c7788914b4e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -108,6 +108,7 @@ static const u16 mgmt_events[] = { MGMT_EV_DEVICE_UNPAIRED, MGMT_EV_PASSKEY_NOTIFY, MGMT_EV_NEW_IRK, + MGMT_EV_NEW_CSRK, }; #define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000) @@ -5072,6 +5073,35 @@ void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk) mgmt_event(MGMT_EV_NEW_IRK, hdev, &ev, sizeof(ev), NULL); } +void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk) +{ + struct mgmt_ev_new_csrk ev; + + memset(&ev, 0, sizeof(ev)); + + /* Devices using resolvable or non-resolvable random addresses + * without providing an indentity resolving key don't require + * to store signature resolving keys. Their addresses will change + * the next time around. + * + * Only when a remote device provides an identity address + * make sure the signature resolving key is stored. So allow + * static random and public addresses here. + */ + if (csrk->bdaddr_type == ADDR_LE_DEV_RANDOM && + (csrk->bdaddr.b[5] & 0xc0) != 0xc0) + ev.store_hint = 0x00; + else + ev.store_hint = 0x01; + + bacpy(&ev.key.addr.bdaddr, &csrk->bdaddr); + ev.key.addr.type = link_to_bdaddr(LE_LINK, csrk->bdaddr_type); + ev.key.master = csrk->master; + memcpy(ev.key.val, csrk->val, sizeof(csrk->val)); + + mgmt_event(MGMT_EV_NEW_CSRK, hdev, &ev, sizeof(ev), NULL); +} + static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data, u8 data_len) { diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index f886bcae1b7e..fc652592daf6 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -273,8 +273,8 @@ static void build_pairing_cmd(struct l2cap_conn *conn, u8 local_dist = 0, remote_dist = 0; if (test_bit(HCI_PAIRABLE, &conn->hcon->hdev->dev_flags)) { - local_dist = SMP_DIST_ENC_KEY; - remote_dist = SMP_DIST_ENC_KEY; + local_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN; + remote_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN; authreq |= SMP_AUTH_BONDING; } else { authreq &= ~SMP_AUTH_BONDING; @@ -596,6 +596,9 @@ void smp_chan_destroy(struct l2cap_conn *conn) complete = test_bit(SMP_FLAG_COMPLETE, &smp->smp_flags); mgmt_smp_complete(conn->hcon, complete); + kfree(smp->csrk); + kfree(smp->slave_csrk); + /* If pairing failed clean up any keys we might have */ if (!complete) { if (smp->ltk) { @@ -1065,6 +1068,41 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn, return 0; } +static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb) +{ + struct smp_cmd_sign_info *rp = (void *) skb->data; + struct smp_chan *smp = conn->smp_chan; + struct hci_dev *hdev = conn->hcon->hdev; + struct smp_csrk *csrk; + + BT_DBG("conn %p", conn); + + if (skb->len < sizeof(*rp)) + return SMP_UNSPECIFIED; + + /* Ignore this PDU if it wasn't requested */ + if (!(smp->remote_key_dist & SMP_DIST_SIGN)) + return 0; + + /* Mark the information as received */ + smp->remote_key_dist &= ~SMP_DIST_SIGN; + + skb_pull(skb, sizeof(*rp)); + + hci_dev_lock(hdev); + csrk = kzalloc(sizeof(*csrk), GFP_KERNEL); + if (csrk) { + csrk->master = 0x01; + memcpy(csrk->val, rp->csrk, sizeof(csrk->val)); + } + smp->csrk = csrk; + if (!(smp->remote_key_dist & SMP_DIST_SIGN)) + smp_distribute_keys(conn); + hci_dev_unlock(hdev); + + return 0; +} + int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) { struct hci_conn *hcon = conn->hcon; @@ -1147,8 +1185,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) break; case SMP_CMD_SIGN_INFO: - /* Just ignored */ - reason = 0; + reason = smp_cmd_sign_info(conn, skb); break; default: @@ -1176,6 +1213,18 @@ static void smp_notify_keys(struct l2cap_conn *conn) if (smp->remote_irk) mgmt_new_irk(hdev, smp->remote_irk); + if (smp->csrk) { + smp->csrk->bdaddr_type = hcon->dst_type; + bacpy(&smp->csrk->bdaddr, &hcon->dst); + mgmt_new_csrk(hdev, smp->csrk); + } + + if (smp->slave_csrk) { + smp->slave_csrk->bdaddr_type = hcon->dst_type; + bacpy(&smp->slave_csrk->bdaddr, &hcon->dst); + mgmt_new_csrk(hdev, smp->slave_csrk); + } + if (smp->ltk) { smp->ltk->bdaddr_type = hcon->dst_type; bacpy(&smp->ltk->bdaddr, &hcon->dst); @@ -1274,10 +1323,18 @@ int smp_distribute_keys(struct l2cap_conn *conn) if (*keydist & SMP_DIST_SIGN) { struct smp_cmd_sign_info sign; + struct smp_csrk *csrk; - /* Send a dummy key */ + /* Generate a new random key */ get_random_bytes(sign.csrk, sizeof(sign.csrk)); + csrk = kzalloc(sizeof(*csrk), GFP_KERNEL); + if (csrk) { + csrk->master = 0x00; + memcpy(csrk->val, sign.csrk, sizeof(csrk->val)); + } + smp->slave_csrk = csrk; + smp_send_cmd(conn, SMP_CMD_SIGN_INFO, sizeof(sign), &sign); *keydist &= ~SMP_DIST_SIGN; diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h index f55d83617218..f223b9d38b61 100644 --- a/net/bluetooth/smp.h +++ b/net/bluetooth/smp.h @@ -136,6 +136,8 @@ struct smp_chan { bdaddr_t id_addr; u8 id_addr_type; u8 irk[16]; + struct smp_csrk *csrk; + struct smp_csrk *slave_csrk; struct smp_ltk *ltk; struct smp_ltk *slave_ltk; struct smp_irk *remote_irk; -- cgit v1.2.3-71-gd317 From 53ac6ab612456a13bf0f6bad89c1503616e4de3b Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 9 Mar 2014 23:38:42 -0700 Subject: Bluetooth: Make LTK and CSRK only persisent when bonding In case the pairable option has been disabled, the pairing procedure does not create keys for bonding. This means that these generated keys should not be stored persistently. For LTK and CSRK this is important to tell userspace to not store these new keys. They will be available for the lifetime of the device, but after the next power cycle they should not be used anymore. Inform userspace to actually store the keys persistently only if both sides request bonding. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 5 +++-- net/bluetooth/mgmt.c | 9 +++++---- net/bluetooth/smp.c | 16 ++++++++++++---- 3 files changed, 20 insertions(+), 10 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index e869884fbfa9..b8cc39a4a9a5 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1270,9 +1270,10 @@ void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, void mgmt_discovering(struct hci_dev *hdev, u8 discovering); int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); -void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key); +void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent); void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk); -void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk); +void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk, + bool persistent); void mgmt_reenable_advertising(struct hci_dev *hdev); void mgmt_smp_complete(struct hci_conn *conn, bool complete); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 9c7788914b4e..fbcf9d4f130b 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5005,7 +5005,7 @@ void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL); } -void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key) +void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent) { struct mgmt_ev_new_long_term_key ev; @@ -5026,7 +5026,7 @@ void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key) (key->bdaddr.b[5] & 0xc0) != 0xc0) ev.store_hint = 0x00; else - ev.store_hint = 0x01; + ev.store_hint = persistent; bacpy(&ev.key.addr.bdaddr, &key->bdaddr); ev.key.addr.type = link_to_bdaddr(LE_LINK, key->bdaddr_type); @@ -5073,7 +5073,8 @@ void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk) mgmt_event(MGMT_EV_NEW_IRK, hdev, &ev, sizeof(ev), NULL); } -void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk) +void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk, + bool persistent) { struct mgmt_ev_new_csrk ev; @@ -5092,7 +5093,7 @@ void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk) (csrk->bdaddr.b[5] & 0xc0) != 0xc0) ev.store_hint = 0x00; else - ev.store_hint = 0x01; + ev.store_hint = persistent; bacpy(&ev.key.addr.bdaddr, &csrk->bdaddr); ev.key.addr.type = link_to_bdaddr(LE_LINK, csrk->bdaddr_type); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index fc652592daf6..7f25dda9c770 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -1209,32 +1209,40 @@ static void smp_notify_keys(struct l2cap_conn *conn) struct smp_chan *smp = conn->smp_chan; struct hci_conn *hcon = conn->hcon; struct hci_dev *hdev = hcon->hdev; + struct smp_cmd_pairing *req = (void *) &smp->preq[1]; + struct smp_cmd_pairing *rsp = (void *) &smp->prsp[1]; + bool persistent; if (smp->remote_irk) mgmt_new_irk(hdev, smp->remote_irk); + /* The LTKs and CSRKs should be persistent only if both sides + * had the bonding bit set in their authentication requests. + */ + persistent = !!((req->auth_req & rsp->auth_req) & SMP_AUTH_BONDING); + if (smp->csrk) { smp->csrk->bdaddr_type = hcon->dst_type; bacpy(&smp->csrk->bdaddr, &hcon->dst); - mgmt_new_csrk(hdev, smp->csrk); + mgmt_new_csrk(hdev, smp->csrk, persistent); } if (smp->slave_csrk) { smp->slave_csrk->bdaddr_type = hcon->dst_type; bacpy(&smp->slave_csrk->bdaddr, &hcon->dst); - mgmt_new_csrk(hdev, smp->slave_csrk); + mgmt_new_csrk(hdev, smp->slave_csrk, persistent); } if (smp->ltk) { smp->ltk->bdaddr_type = hcon->dst_type; bacpy(&smp->ltk->bdaddr, &hcon->dst); - mgmt_new_ltk(hdev, smp->ltk); + mgmt_new_ltk(hdev, smp->ltk, persistent); } if (smp->slave_ltk) { smp->slave_ltk->bdaddr_type = hcon->dst_type; bacpy(&smp->slave_ltk->bdaddr, &hcon->dst); - mgmt_new_ltk(hdev, smp->slave_ltk); + mgmt_new_ltk(hdev, smp->slave_ltk, persistent); } } -- cgit v1.2.3-71-gd317 From 5812521be0f79583a26e203ac5f23de679cbdd94 Mon Sep 17 00:00:00 2001 From: Gu Zheng Date: Mon, 10 Mar 2014 09:57:34 +0800 Subject: net: add a pre-check of net_ns in sk_change_net() We do not need to switch the net_ns if the target net_ns the same as the current one, so here we add a pre-check of net_ns to avoid this as David suggested. Signed-off-by: Gu Zheng Signed-off-by: David S. Miller --- include/net/sock.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/sock.h b/include/net/sock.h index 5c3f7c3624aa..967856970a51 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -2252,8 +2252,12 @@ void sock_net_set(struct sock *sk, struct net *net) */ static inline void sk_change_net(struct sock *sk, struct net *net) { - put_net(sock_net(sk)); - sock_net_set(sk, hold_net(net)); + struct net *current_net = sock_net(sk); + + if (!net_eq(current_net, net)) { + put_net(current_net); + sock_net_set(sk, hold_net(net)); + } } static inline struct sock *skb_steal_sock(struct sk_buff *skb) -- cgit v1.2.3-71-gd317 From ceeee42d85b4c91b16b6019e69c584589b72be04 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Thu, 6 Mar 2014 07:39:19 -0700 Subject: NFC: digital: Rename Type V tags to Type 5 tags According to the latest draft specification from the NFC-V committee, ISO/IEC 15693 tags will be referred to as "Type 5" tags and not "Type V" tags anymore. Make the code reflect the new terminology. Signed-off-by: Mark A. Greer Signed-off-by: Samuel Ortiz --- include/net/nfc/digital.h | 2 +- net/nfc/digital_core.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/nfc/digital.h b/include/net/nfc/digital.h index b9699d7dd039..7655cfe27c34 100644 --- a/include/net/nfc/digital.h +++ b/include/net/nfc/digital.h @@ -60,7 +60,7 @@ enum { NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED, NFC_DIGITAL_FRAMING_ISO15693_INVENTORY, - NFC_DIGITAL_FRAMING_ISO15693_TVT, /* Type V Tag (ISO/IEC 15693) */ + NFC_DIGITAL_FRAMING_ISO15693_T5T, NFC_DIGITAL_FRAMING_LAST, }; diff --git a/net/nfc/digital_core.c b/net/nfc/digital_core.c index 492fa7355e0d..e01e15dbf1ab 100644 --- a/net/nfc/digital_core.c +++ b/net/nfc/digital_core.c @@ -334,7 +334,7 @@ int digital_target_found(struct nfc_digital_dev *ddev, break; case NFC_PROTO_ISO15693: - framing = NFC_DIGITAL_FRAMING_ISO15693_TVT; + framing = NFC_DIGITAL_FRAMING_ISO15693_T5T; check_crc = digital_skb_check_crc_b; add_crc = digital_skb_add_crc_b; break; -- cgit v1.2.3-71-gd317 From d32d9bb85c65f52bed99a0149b47e9f6578c44c5 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 10 Mar 2014 07:09:07 -0700 Subject: flowcache: restore a single flow_cache kmem_cache It is not legal to create multiple kmem_cache having the same name. flowcache can use a single kmem_cache, no need for a per netns one. Fixes: ca925cf1534e ("flowcache: Make flow cache name space aware") Reported-by: Jakub Kicinski Tested-by: Jakub Kicinski Tested-by: Fan Du Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/xfrm.h | 1 - net/core/flow.c | 14 ++++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'include/net') diff --git a/include/net/netns/xfrm.h b/include/net/netns/xfrm.h index 51f0dce7b643..3492434baf88 100644 --- a/include/net/netns/xfrm.h +++ b/include/net/netns/xfrm.h @@ -64,7 +64,6 @@ struct netns_xfrm { /* flow cache part */ struct flow_cache flow_cache_global; - struct kmem_cache *flow_cachep; atomic_t flow_cache_genid; struct list_head flow_cache_gc_list; spinlock_t flow_cache_gc_lock; diff --git a/net/core/flow.c b/net/core/flow.c index 344a184011fd..102f8ea2eb6e 100644 --- a/net/core/flow.c +++ b/net/core/flow.c @@ -45,6 +45,8 @@ struct flow_flush_info { struct completion completion; }; +static struct kmem_cache *flow_cachep __read_mostly; + #define flow_cache_hash_size(cache) (1 << (cache)->hash_shift) #define FLOW_HASH_RND_PERIOD (10 * 60 * HZ) @@ -75,7 +77,7 @@ static void flow_entry_kill(struct flow_cache_entry *fle, { if (fle->object) fle->object->ops->delete(fle->object); - kmem_cache_free(xfrm->flow_cachep, fle); + kmem_cache_free(flow_cachep, fle); } static void flow_cache_gc_task(struct work_struct *work) @@ -230,7 +232,7 @@ flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir, if (fcp->hash_count > fc->high_watermark) flow_cache_shrink(fc, fcp); - fle = kmem_cache_alloc(net->xfrm.flow_cachep, GFP_ATOMIC); + fle = kmem_cache_alloc(flow_cachep, GFP_ATOMIC); if (fle) { fle->net = net; fle->family = family; @@ -435,10 +437,10 @@ int flow_cache_init(struct net *net) int i; struct flow_cache *fc = &net->xfrm.flow_cache_global; - /* Initialize per-net flow cache global variables here */ - net->xfrm.flow_cachep = kmem_cache_create("flow_cache", - sizeof(struct flow_cache_entry), - 0, SLAB_PANIC, NULL); + if (!flow_cachep) + flow_cachep = kmem_cache_create("flow_cache", + sizeof(struct flow_cache_entry), + 0, SLAB_PANIC, NULL); spin_lock_init(&net->xfrm.flow_cache_gc_lock); INIT_LIST_HEAD(&net->xfrm.flow_cache_gc_list); INIT_WORK(&net->xfrm.flow_cache_gc_work, flow_cache_gc_task); -- cgit v1.2.3-71-gd317 From 4a93f5095a628d812b0b30c16d7bacea1efd783c Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Wed, 12 Mar 2014 09:43:17 +0100 Subject: flowcache: Fix resource leaks on namespace exit. We leak an active timer, the hotcpu notifier and all allocated resources when we exit a namespace. Fix this by introducing a flow_cache_fini() function where we release the resources before we exit. Fixes: ca925cf1534e ("flowcache: Make flow cache name space aware") Reported-by: Jakub Kicinski Tested-by: Jakub Kicinski Cc: Eric Dumazet Cc: Fan Du Signed-off-by: Steffen Klassert Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/flow.h | 1 + net/core/flow.c | 19 +++++++++++++++++++ net/xfrm/xfrm_policy.c | 7 ++++++- 3 files changed, 26 insertions(+), 1 deletion(-) (limited to 'include/net') diff --git a/include/net/flow.h b/include/net/flow.h index bee3741e5a6f..64fd24836650 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -219,6 +219,7 @@ struct flow_cache_object *flow_cache_lookup(struct net *net, u8 dir, flow_resolve_t resolver, void *ctx); int flow_cache_init(struct net *net); +void flow_cache_fini(struct net *net); void flow_cache_flush(struct net *net); void flow_cache_flush_deferred(struct net *net); diff --git a/net/core/flow.c b/net/core/flow.c index 102f8ea2eb6e..31cfb365e0c6 100644 --- a/net/core/flow.c +++ b/net/core/flow.c @@ -484,3 +484,22 @@ err: return -ENOMEM; } EXPORT_SYMBOL(flow_cache_init); + +void flow_cache_fini(struct net *net) +{ + int i; + struct flow_cache *fc = &net->xfrm.flow_cache_global; + + del_timer_sync(&fc->rnd_timer); + unregister_hotcpu_notifier(&fc->hotcpu_notifier); + + for_each_possible_cpu(i) { + struct flow_cache_percpu *fcp = per_cpu_ptr(fc->percpu, i); + kfree(fcp->hash_table); + fcp->hash_table = NULL; + } + + free_percpu(fc->percpu); + fc->percpu = NULL; +} +EXPORT_SYMBOL(flow_cache_fini); diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index a75fae4b045a..f02f511b7107 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -2913,15 +2913,19 @@ static int __net_init xfrm_net_init(struct net *net) rv = xfrm_sysctl_init(net); if (rv < 0) goto out_sysctl; + rv = flow_cache_init(net); + if (rv < 0) + goto out; /* Initialize the per-net locks here */ spin_lock_init(&net->xfrm.xfrm_state_lock); rwlock_init(&net->xfrm.xfrm_policy_lock); mutex_init(&net->xfrm.xfrm_cfg_mutex); - flow_cache_init(net); return 0; +out: + xfrm_sysctl_fini(net); out_sysctl: xfrm_policy_fini(net); out_policy: @@ -2934,6 +2938,7 @@ out_statistics: static void __net_exit xfrm_net_exit(struct net *net) { + flow_cache_fini(net); xfrm_sysctl_fini(net); xfrm_policy_fini(net); xfrm_state_fini(net); -- cgit v1.2.3-71-gd317 From 2f32b51b609faea1e40bb8c5bd305f1351740936 Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Fri, 14 Mar 2014 07:28:07 +0100 Subject: xfrm: Introduce xfrm_input_afinfo to access the the callbacks properly IPv6 can be build as a module, so we need mechanism to access the address family dependent callback functions properly. Therefore we introduce xfrm_input_afinfo, similar to that what we have for the address family dependent part of policies and states. Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 23 +++++++-------- net/ipv4/xfrm4_policy.c | 1 + net/ipv4/xfrm4_protocol.c | 13 +++++++- net/xfrm/xfrm_input.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 13 deletions(-) (limited to 'include/net') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index ce3d96f752fd..af13599b60a0 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -349,6 +349,16 @@ int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo); struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family); void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo); +struct xfrm_input_afinfo { + unsigned int family; + struct module *owner; + int (*callback)(struct sk_buff *skb, u8 protocol, + int err); +}; + +int xfrm_input_register_afinfo(struct xfrm_input_afinfo *afinfo); +int xfrm_input_unregister_afinfo(struct xfrm_input_afinfo *afinfo); + void xfrm_state_delete_tunnel(struct xfrm_state *x); struct xfrm_type { @@ -1392,6 +1402,7 @@ void xfrm4_init(void); int xfrm_state_init(struct net *net); void xfrm_state_fini(struct net *net); void xfrm4_state_init(void); +void xfrm4_protocol_init(void); #ifdef CONFIG_XFRM int xfrm6_init(void); void xfrm6_fini(void); @@ -1773,18 +1784,6 @@ static inline int xfrm_mark_put(struct sk_buff *skb, const struct xfrm_mark *m) return ret; } -static inline int xfrm_rcv_cb(struct sk_buff *skb, unsigned int family, - u8 protocol, int err) -{ - switch(family) { -#ifdef CONFIG_INET - case AF_INET: - return xfrm4_rcv_cb(skb, protocol, err); -#endif - } - return 0; -} - static inline int xfrm_tunnel_check(struct sk_buff *skb, struct xfrm_state *x, unsigned int family) { diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index e1a63930a967..6156f68a1e90 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -325,6 +325,7 @@ void __init xfrm4_init(void) xfrm4_state_init(); xfrm4_policy_init(); + xfrm4_protocol_init(); #ifdef CONFIG_SYSCTL register_pernet_subsys(&xfrm4_net_ops); #endif diff --git a/net/ipv4/xfrm4_protocol.c b/net/ipv4/xfrm4_protocol.c index cdc09efca442..7f7b243e8139 100644 --- a/net/ipv4/xfrm4_protocol.c +++ b/net/ipv4/xfrm4_protocol.c @@ -179,6 +179,12 @@ static const struct net_protocol ipcomp4_protocol = { .netns_ok = 1, }; +static struct xfrm_input_afinfo xfrm4_input_afinfo = { + .family = AF_INET, + .owner = THIS_MODULE, + .callback = xfrm4_rcv_cb, +}; + static inline const struct net_protocol *netproto(unsigned char protocol) { switch (protocol) { @@ -199,7 +205,6 @@ int xfrm4_protocol_register(struct xfrm4_protocol *handler, struct xfrm4_protocol __rcu **pprev; struct xfrm4_protocol *t; bool add_netproto = false; - int ret = -EEXIST; int priority = handler->priority; @@ -273,3 +278,9 @@ int xfrm4_protocol_deregister(struct xfrm4_protocol *handler, return ret; } EXPORT_SYMBOL(xfrm4_protocol_deregister); + +void __init xfrm4_protocol_init(void) +{ + xfrm_input_register_afinfo(&xfrm4_input_afinfo); +} +EXPORT_SYMBOL(xfrm4_protocol_init); diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index 4218164f4f5e..85d1d4764612 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c @@ -16,6 +16,81 @@ static struct kmem_cache *secpath_cachep __read_mostly; +static DEFINE_SPINLOCK(xfrm_input_afinfo_lock); +static struct xfrm_input_afinfo __rcu *xfrm_input_afinfo[NPROTO]; + +int xfrm_input_register_afinfo(struct xfrm_input_afinfo *afinfo) +{ + int err = 0; + + if (unlikely(afinfo == NULL)) + return -EINVAL; + if (unlikely(afinfo->family >= NPROTO)) + return -EAFNOSUPPORT; + spin_lock_bh(&xfrm_input_afinfo_lock); + if (unlikely(xfrm_input_afinfo[afinfo->family] != NULL)) + err = -ENOBUFS; + else + rcu_assign_pointer(xfrm_input_afinfo[afinfo->family], afinfo); + spin_unlock_bh(&xfrm_input_afinfo_lock); + return err; +} +EXPORT_SYMBOL(xfrm_input_register_afinfo); + +int xfrm_input_unregister_afinfo(struct xfrm_input_afinfo *afinfo) +{ + int err = 0; + + if (unlikely(afinfo == NULL)) + return -EINVAL; + if (unlikely(afinfo->family >= NPROTO)) + return -EAFNOSUPPORT; + spin_lock_bh(&xfrm_input_afinfo_lock); + if (likely(xfrm_input_afinfo[afinfo->family] != NULL)) { + if (unlikely(xfrm_input_afinfo[afinfo->family] != afinfo)) + err = -EINVAL; + else + RCU_INIT_POINTER(xfrm_input_afinfo[afinfo->family], NULL); + } + spin_unlock_bh(&xfrm_input_afinfo_lock); + synchronize_rcu(); + return err; +} +EXPORT_SYMBOL(xfrm_input_unregister_afinfo); + +static struct xfrm_input_afinfo *xfrm_input_get_afinfo(unsigned int family) +{ + struct xfrm_input_afinfo *afinfo; + + if (unlikely(family >= NPROTO)) + return NULL; + rcu_read_lock(); + afinfo = rcu_dereference(xfrm_input_afinfo[family]); + if (unlikely(!afinfo)) + rcu_read_unlock(); + return afinfo; +} + +static void xfrm_input_put_afinfo(struct xfrm_input_afinfo *afinfo) +{ + rcu_read_unlock(); +} + +static int xfrm_rcv_cb(struct sk_buff *skb, unsigned int family, u8 protocol, + int err) +{ + int ret; + struct xfrm_input_afinfo *afinfo = xfrm_input_get_afinfo(family); + + if (!afinfo) + return -EAFNOSUPPORT; + + ret = afinfo->callback(skb, protocol, err); + xfrm_input_put_afinfo(afinfo); + + return ret; +} + void __secpath_destroy(struct sec_path *sp) { int i; -- cgit v1.2.3-71-gd317 From 7e14ea1521d9249d9de7f0ea39c9af054745eebd Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Fri, 14 Mar 2014 07:28:07 +0100 Subject: xfrm6: Add IPsec protocol multiplexer This patch adds an IPsec protocol multiplexer for ipv6. With this it is possible to add alternative protocol handlers, as needed for IPsec virtual tunnel interfaces. Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 15 +++ net/ipv6/Makefile | 2 +- net/ipv6/xfrm6_policy.c | 7 ++ net/ipv6/xfrm6_protocol.c | 270 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 293 insertions(+), 1 deletion(-) create mode 100644 net/ipv6/xfrm6_protocol.c (limited to 'include/net') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index af13599b60a0..6304ec394c4a 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1374,6 +1374,16 @@ struct xfrm4_protocol { int priority; }; +struct xfrm6_protocol { + int (*handler)(struct sk_buff *skb); + int (*cb_handler)(struct sk_buff *skb, int err); + int (*err_handler)(struct sk_buff *skb, struct inet6_skb_parm *opt, + u8 type, u8 code, int offset, __be32 info); + + struct xfrm6_protocol __rcu *next; + int priority; +}; + /* XFRM tunnel handlers. */ struct xfrm_tunnel { int (*handler)(struct sk_buff *skb); @@ -1408,6 +1418,8 @@ int xfrm6_init(void); void xfrm6_fini(void); int xfrm6_state_init(void); void xfrm6_state_fini(void); +int xfrm6_protocol_init(void); +void xfrm6_protocol_fini(void); #else static inline int xfrm6_init(void) { @@ -1552,6 +1564,9 @@ int xfrm6_rcv(struct sk_buff *skb); int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto); void xfrm6_local_error(struct sk_buff *skb, u32 mtu); +int xfrm6_rcv_cb(struct sk_buff *skb, u8 protocol, int err); +int xfrm6_protocol_register(struct xfrm6_protocol *handler, unsigned char protocol); +int xfrm6_protocol_deregister(struct xfrm6_protocol *handler, unsigned char protocol); int xfrm6_tunnel_register(struct xfrm6_tunnel *handler, unsigned short family); int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler, unsigned short family); __be32 xfrm6_tunnel_alloc_spi(struct net *net, xfrm_address_t *saddr); diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index 17bb830872db..2fe68364bb20 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile @@ -16,7 +16,7 @@ ipv6-$(CONFIG_SYSCTL) = sysctl_net_ipv6.o ipv6-$(CONFIG_IPV6_MROUTE) += ip6mr.o ipv6-$(CONFIG_XFRM) += xfrm6_policy.o xfrm6_state.o xfrm6_input.o \ - xfrm6_output.o + xfrm6_output.o xfrm6_protocol.o ipv6-$(CONFIG_NETFILTER) += netfilter.o ipv6-$(CONFIG_IPV6_MULTIPLE_TABLES) += fib6_rules.o ipv6-$(CONFIG_PROC_FS) += proc.o diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 5f8e128c512d..2a0bbda2c76a 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -389,11 +389,17 @@ int __init xfrm6_init(void) if (ret) goto out_policy; + ret = xfrm6_protocol_init(); + if (ret) + goto out_state; + #ifdef CONFIG_SYSCTL register_pernet_subsys(&xfrm6_net_ops); #endif out: return ret; +out_state: + xfrm6_state_fini(); out_policy: xfrm6_policy_fini(); goto out; @@ -404,6 +410,7 @@ void xfrm6_fini(void) #ifdef CONFIG_SYSCTL unregister_pernet_subsys(&xfrm6_net_ops); #endif + xfrm6_protocol_fini(); xfrm6_policy_fini(); xfrm6_state_fini(); dst_entries_destroy(&xfrm6_dst_ops); diff --git a/net/ipv6/xfrm6_protocol.c b/net/ipv6/xfrm6_protocol.c new file mode 100644 index 000000000000..6ab989c486f7 --- /dev/null +++ b/net/ipv6/xfrm6_protocol.c @@ -0,0 +1,270 @@ +/* xfrm6_protocol.c - Generic xfrm protocol multiplexer for ipv6. + * + * Copyright (C) 2013 secunet Security Networks AG + * + * Author: + * Steffen Klassert + * + * Based on: + * net/ipv4/xfrm4_protocol.c + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +static struct xfrm6_protocol __rcu *esp6_handlers __read_mostly; +static struct xfrm6_protocol __rcu *ah6_handlers __read_mostly; +static struct xfrm6_protocol __rcu *ipcomp6_handlers __read_mostly; +static DEFINE_MUTEX(xfrm6_protocol_mutex); + +static inline struct xfrm6_protocol __rcu **proto_handlers(u8 protocol) +{ + switch (protocol) { + case IPPROTO_ESP: + return &esp6_handlers; + case IPPROTO_AH: + return &ah6_handlers; + case IPPROTO_COMP: + return &ipcomp6_handlers; + } + + return NULL; +} + +#define for_each_protocol_rcu(head, handler) \ + for (handler = rcu_dereference(head); \ + handler != NULL; \ + handler = rcu_dereference(handler->next)) \ + +int xfrm6_rcv_cb(struct sk_buff *skb, u8 protocol, int err) +{ + int ret; + struct xfrm6_protocol *handler; + + for_each_protocol_rcu(*proto_handlers(protocol), handler) + if ((ret = handler->cb_handler(skb, err)) <= 0) + return ret; + + return 0; +} +EXPORT_SYMBOL(xfrm6_rcv_cb); + +static int xfrm6_esp_rcv(struct sk_buff *skb) +{ + int ret; + struct xfrm6_protocol *handler; + + XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; + + for_each_protocol_rcu(esp6_handlers, handler) + if ((ret = handler->handler(skb)) != -EINVAL) + return ret; + + icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); + + kfree_skb(skb); + return 0; +} + +static void xfrm6_esp_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + u8 type, u8 code, int offset, __be32 info) +{ + struct xfrm6_protocol *handler; + + for_each_protocol_rcu(esp6_handlers, handler) + if (!handler->err_handler(skb, opt, type, code, offset, info)) + break; +} + +static int xfrm6_ah_rcv(struct sk_buff *skb) +{ + int ret; + struct xfrm6_protocol *handler; + + XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; + + for_each_protocol_rcu(ah6_handlers, handler) + if ((ret = handler->handler(skb)) != -EINVAL) + return ret; + + icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); + + kfree_skb(skb); + return 0; +} + +static void xfrm6_ah_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + u8 type, u8 code, int offset, __be32 info) +{ + struct xfrm6_protocol *handler; + + for_each_protocol_rcu(ah6_handlers, handler) + if (!handler->err_handler(skb, opt, type, code, offset, info)) + break; +} + +static int xfrm6_ipcomp_rcv(struct sk_buff *skb) +{ + int ret; + struct xfrm6_protocol *handler; + + XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; + + for_each_protocol_rcu(ipcomp6_handlers, handler) + if ((ret = handler->handler(skb)) != -EINVAL) + return ret; + + icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); + + kfree_skb(skb); + return 0; +} + +static void xfrm6_ipcomp_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + u8 type, u8 code, int offset, __be32 info) +{ + struct xfrm6_protocol *handler; + + for_each_protocol_rcu(ipcomp6_handlers, handler) + if (!handler->err_handler(skb, opt, type, code, offset, info)) + break; +} + +static const struct inet6_protocol esp6_protocol = { + .handler = xfrm6_esp_rcv, + .err_handler = xfrm6_esp_err, + .flags = INET6_PROTO_NOPOLICY, +}; + +static const struct inet6_protocol ah6_protocol = { + .handler = xfrm6_ah_rcv, + .err_handler = xfrm6_ah_err, + .flags = INET6_PROTO_NOPOLICY, +}; + +static const struct inet6_protocol ipcomp6_protocol = { + .handler = xfrm6_ipcomp_rcv, + .err_handler = xfrm6_ipcomp_err, + .flags = INET6_PROTO_NOPOLICY, +}; + +static struct xfrm_input_afinfo xfrm6_input_afinfo = { + .family = AF_INET6, + .owner = THIS_MODULE, + .callback = xfrm6_rcv_cb, +}; + +static inline const struct inet6_protocol *netproto(unsigned char protocol) +{ + switch (protocol) { + case IPPROTO_ESP: + return &esp6_protocol; + case IPPROTO_AH: + return &ah6_protocol; + case IPPROTO_COMP: + return &ipcomp6_protocol; + } + + return NULL; +} + +int xfrm6_protocol_register(struct xfrm6_protocol *handler, + unsigned char protocol) +{ + struct xfrm6_protocol __rcu **pprev; + struct xfrm6_protocol *t; + bool add_netproto = false; + + int ret = -EEXIST; + int priority = handler->priority; + + mutex_lock(&xfrm6_protocol_mutex); + + if (!rcu_dereference_protected(*proto_handlers(protocol), + lockdep_is_held(&xfrm6_protocol_mutex))) + add_netproto = true; + + for (pprev = proto_handlers(protocol); + (t = rcu_dereference_protected(*pprev, + lockdep_is_held(&xfrm6_protocol_mutex))) != NULL; + pprev = &t->next) { + if (t->priority < priority) + break; + if (t->priority == priority) + goto err; + } + + handler->next = *pprev; + rcu_assign_pointer(*pprev, handler); + + ret = 0; + +err: + mutex_unlock(&xfrm6_protocol_mutex); + + if (add_netproto) { + if (inet6_add_protocol(netproto(protocol), protocol)) { + pr_err("%s: can't add protocol\n", __func__); + ret = -EAGAIN; + } + } + + return ret; +} +EXPORT_SYMBOL(xfrm6_protocol_register); + +int xfrm6_protocol_deregister(struct xfrm6_protocol *handler, + unsigned char protocol) +{ + struct xfrm6_protocol __rcu **pprev; + struct xfrm6_protocol *t; + int ret = -ENOENT; + + mutex_lock(&xfrm6_protocol_mutex); + + for (pprev = proto_handlers(protocol); + (t = rcu_dereference_protected(*pprev, + lockdep_is_held(&xfrm6_protocol_mutex))) != NULL; + pprev = &t->next) { + if (t == handler) { + *pprev = handler->next; + ret = 0; + break; + } + } + + if (!rcu_dereference_protected(*proto_handlers(protocol), + lockdep_is_held(&xfrm6_protocol_mutex))) { + if (inet6_del_protocol(netproto(protocol), protocol) < 0) { + pr_err("%s: can't remove protocol\n", __func__); + ret = -EAGAIN; + } + } + + mutex_unlock(&xfrm6_protocol_mutex); + + synchronize_net(); + + return ret; +} +EXPORT_SYMBOL(xfrm6_protocol_deregister); + +int __init xfrm6_protocol_init(void) +{ + return xfrm_input_register_afinfo(&xfrm6_input_afinfo); +} + +void xfrm6_protocol_fini(void) +{ + xfrm_input_unregister_afinfo(&xfrm6_input_afinfo); +} -- cgit v1.2.3-71-gd317 From 573ce1c11b0d93a08b988d2713ef02214404aad1 Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Fri, 14 Mar 2014 07:28:08 +0100 Subject: xfrm6: Remove xfrm_tunnel_notifier This was used from vti and is replaced by the IPsec protocol multiplexer hooks. It is now unused, so remove it. Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 8 ------ net/ipv6/xfrm6_mode_tunnel.c | 63 -------------------------------------------- 2 files changed, 71 deletions(-) (limited to 'include/net') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 6304ec394c4a..7c13ef6d6564 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1393,12 +1393,6 @@ struct xfrm_tunnel { int priority; }; -struct xfrm_tunnel_notifier { - int (*handler)(struct sk_buff *skb); - struct xfrm_tunnel_notifier __rcu *next; - int priority; -}; - struct xfrm6_tunnel { int (*handler)(struct sk_buff *skb); int (*err_handler)(struct sk_buff *skb, struct inet6_skb_parm *opt, @@ -1554,8 +1548,6 @@ int xfrm4_protocol_deregister(struct xfrm4_protocol *handler, unsigned char prot int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family); int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family); void xfrm4_local_error(struct sk_buff *skb, u32 mtu); -int xfrm6_mode_tunnel_input_register(struct xfrm_tunnel_notifier *handler); -int xfrm6_mode_tunnel_input_deregister(struct xfrm_tunnel_notifier *handler); int xfrm6_extract_header(struct sk_buff *skb); int xfrm6_extract_input(struct xfrm_state *x, struct sk_buff *skb); int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi); diff --git a/net/ipv6/xfrm6_mode_tunnel.c b/net/ipv6/xfrm6_mode_tunnel.c index cb04f7a16b5e..901ef6f8addc 100644 --- a/net/ipv6/xfrm6_mode_tunnel.c +++ b/net/ipv6/xfrm6_mode_tunnel.c @@ -18,65 +18,6 @@ #include #include -/* Informational hook. The decap is still done here. */ -static struct xfrm_tunnel_notifier __rcu *rcv_notify_handlers __read_mostly; -static DEFINE_MUTEX(xfrm6_mode_tunnel_input_mutex); - -int xfrm6_mode_tunnel_input_register(struct xfrm_tunnel_notifier *handler) -{ - struct xfrm_tunnel_notifier __rcu **pprev; - struct xfrm_tunnel_notifier *t; - int ret = -EEXIST; - int priority = handler->priority; - - mutex_lock(&xfrm6_mode_tunnel_input_mutex); - - for (pprev = &rcv_notify_handlers; - (t = rcu_dereference_protected(*pprev, - lockdep_is_held(&xfrm6_mode_tunnel_input_mutex))) != NULL; - pprev = &t->next) { - if (t->priority > priority) - break; - if (t->priority == priority) - goto err; - - } - - handler->next = *pprev; - rcu_assign_pointer(*pprev, handler); - - ret = 0; - -err: - mutex_unlock(&xfrm6_mode_tunnel_input_mutex); - return ret; -} -EXPORT_SYMBOL_GPL(xfrm6_mode_tunnel_input_register); - -int xfrm6_mode_tunnel_input_deregister(struct xfrm_tunnel_notifier *handler) -{ - struct xfrm_tunnel_notifier __rcu **pprev; - struct xfrm_tunnel_notifier *t; - int ret = -ENOENT; - - mutex_lock(&xfrm6_mode_tunnel_input_mutex); - for (pprev = &rcv_notify_handlers; - (t = rcu_dereference_protected(*pprev, - lockdep_is_held(&xfrm6_mode_tunnel_input_mutex))) != NULL; - pprev = &t->next) { - if (t == handler) { - *pprev = handler->next; - ret = 0; - break; - } - } - mutex_unlock(&xfrm6_mode_tunnel_input_mutex); - synchronize_net(); - - return ret; -} -EXPORT_SYMBOL_GPL(xfrm6_mode_tunnel_input_deregister); - static inline void ipip6_ecn_decapsulate(struct sk_buff *skb) { const struct ipv6hdr *outer_iph = ipv6_hdr(skb); @@ -130,7 +71,6 @@ static int xfrm6_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb) static int xfrm6_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb) { - struct xfrm_tunnel_notifier *handler; int err = -EINVAL; if (XFRM_MODE_SKB_CB(skb)->protocol != IPPROTO_IPV6) @@ -138,9 +78,6 @@ static int xfrm6_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb) if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) goto out; - for_each_input_rcu(rcv_notify_handlers, handler) - handler->handler(skb); - err = skb_unclone(skb, GFP_ATOMIC); if (err) goto out; -- cgit v1.2.3-71-gd317 From 376b7bd3558eaf12d3e5c24aa71d0c162d2701fd Mon Sep 17 00:00:00 2001 From: Phoebe Buckheister Date: Fri, 14 Mar 2014 21:23:57 +0100 Subject: ieee802154: rename struct ieee802154_addr to *_sa The struct as currently defined uses host byte order for some fields, and most big endian/EUI display byte order for other fields. Inside the stack, endianness should ideally match network byte order where possible to minimize the number of byteswaps done in critical paths, but this patch does not address this; it is only preparatory. Signed-off-by: Phoebe Buckheister Signed-off-by: David S. Miller --- drivers/net/ieee802154/fakehard.c | 14 +++++++------- include/net/af_ieee802154.h | 4 ++-- include/net/ieee802154_netdev.h | 12 ++++++------ include/net/nl802154.h | 6 +++--- net/ieee802154/6lowpan_rtnl.c | 4 ++-- net/ieee802154/af802154.h | 2 +- net/ieee802154/af_ieee802154.c | 2 +- net/ieee802154/dgram.c | 8 ++++---- net/ieee802154/nl-mac.c | 12 ++++++------ net/ieee802154/reassembly.c | 6 +++--- net/ieee802154/reassembly.h | 15 ++++++++------- net/mac802154/mac_cmd.c | 2 +- net/mac802154/wpan.c | 10 +++++----- 13 files changed, 49 insertions(+), 48 deletions(-) (limited to 'include/net') diff --git a/drivers/net/ieee802154/fakehard.c b/drivers/net/ieee802154/fakehard.c index bf0d55e2dd63..06a400f10565 100644 --- a/drivers/net/ieee802154/fakehard.c +++ b/drivers/net/ieee802154/fakehard.c @@ -119,7 +119,7 @@ static u8 fake_get_dsn(const struct net_device *dev) * 802.15.4-2006 document. */ static int fake_assoc_req(struct net_device *dev, - struct ieee802154_addr *addr, u8 channel, u8 page, u8 cap) + struct ieee802154_addr_sa *addr, u8 channel, u8 page, u8 cap) { struct wpan_phy *phy = fake_to_phy(dev); @@ -149,7 +149,7 @@ static int fake_assoc_req(struct net_device *dev, * 802.15.4-2006 document. */ static int fake_assoc_resp(struct net_device *dev, - struct ieee802154_addr *addr, u16 short_addr, u8 status) + struct ieee802154_addr_sa *addr, u16 short_addr, u8 status) { return 0; } @@ -167,7 +167,7 @@ static int fake_assoc_resp(struct net_device *dev, * document, with the reason described in 7.3.3.2. */ static int fake_disassoc_req(struct net_device *dev, - struct ieee802154_addr *addr, u8 reason) + struct ieee802154_addr_sa *addr, u8 reason) { return ieee802154_nl_disassoc_confirm(dev, IEEE802154_SUCCESS); } @@ -191,10 +191,10 @@ static int fake_disassoc_req(struct net_device *dev, * Note: This is in section 7.5.2.3 of the IEEE 802.15.4-2006 * document, with 7.3.8 describing coordinator realignment. */ -static int fake_start_req(struct net_device *dev, struct ieee802154_addr *addr, - u8 channel, u8 page, - u8 bcn_ord, u8 sf_ord, u8 pan_coord, u8 blx, - u8 coord_realign) +static int fake_start_req(struct net_device *dev, + struct ieee802154_addr_sa *addr, u8 channel, u8 page, + u8 bcn_ord, u8 sf_ord, u8 pan_coord, u8 blx, + u8 coord_realign) { struct wpan_phy *phy = fake_to_phy(dev); diff --git a/include/net/af_ieee802154.h b/include/net/af_ieee802154.h index 75e64c7a2960..f79ae2aa76d6 100644 --- a/include/net/af_ieee802154.h +++ b/include/net/af_ieee802154.h @@ -36,7 +36,7 @@ enum { /* address length, octets */ #define IEEE802154_ADDR_LEN 8 -struct ieee802154_addr { +struct ieee802154_addr_sa { int addr_type; u16 pan_id; union { @@ -51,7 +51,7 @@ struct ieee802154_addr { struct sockaddr_ieee802154 { sa_family_t family; /* AF_IEEE802154 */ - struct ieee802154_addr addr; + struct ieee802154_addr_sa addr; }; /* get/setsockopt */ diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h index 97b2e34d87f7..53937cdbcd82 100644 --- a/include/net/ieee802154_netdev.h +++ b/include/net/ieee802154_netdev.h @@ -41,8 +41,8 @@ struct ieee802154_frag_info { */ struct ieee802154_mac_cb { u8 lqi; - struct ieee802154_addr sa; - struct ieee802154_addr da; + struct ieee802154_addr_sa sa; + struct ieee802154_addr_sa da; u8 flags; u8 seq; struct ieee802154_frag_info frag_info; @@ -95,16 +95,16 @@ struct ieee802154_mlme_ops { /* The following fields are optional (can be NULL). */ int (*assoc_req)(struct net_device *dev, - struct ieee802154_addr *addr, + struct ieee802154_addr_sa *addr, u8 channel, u8 page, u8 cap); int (*assoc_resp)(struct net_device *dev, - struct ieee802154_addr *addr, + struct ieee802154_addr_sa *addr, u16 short_addr, u8 status); int (*disassoc_req)(struct net_device *dev, - struct ieee802154_addr *addr, + struct ieee802154_addr_sa *addr, u8 reason); int (*start_req)(struct net_device *dev, - struct ieee802154_addr *addr, + struct ieee802154_addr_sa *addr, u8 channel, u8 page, u8 bcn_ord, u8 sf_ord, u8 pan_coord, u8 blx, u8 coord_realign); int (*scan_req)(struct net_device *dev, diff --git a/include/net/nl802154.h b/include/net/nl802154.h index 99d2ba1c7e03..06ead976755a 100644 --- a/include/net/nl802154.h +++ b/include/net/nl802154.h @@ -22,7 +22,7 @@ #define IEEE802154_NL_H struct net_device; -struct ieee802154_addr; +struct ieee802154_addr_sa; /** * ieee802154_nl_assoc_indic - Notify userland of an association request. @@ -37,7 +37,7 @@ struct ieee802154_addr; * Note: This is in section 7.3.1 of the IEEE 802.15.4-2006 document. */ int ieee802154_nl_assoc_indic(struct net_device *dev, - struct ieee802154_addr *addr, u8 cap); + struct ieee802154_addr_sa *addr, u8 cap); /** * ieee802154_nl_assoc_confirm - Notify userland of association. @@ -65,7 +65,7 @@ int ieee802154_nl_assoc_confirm(struct net_device *dev, * Note: This is in section 7.3.3 of the IEEE 802.15.4 document. */ int ieee802154_nl_disassoc_indic(struct net_device *dev, - struct ieee802154_addr *addr, u8 reason); + struct ieee802154_addr_sa *addr, u8 reason); /** * ieee802154_nl_disassoc_confirm - Notify userland of disassociation diff --git a/net/ieee802154/6lowpan_rtnl.c b/net/ieee802154/6lowpan_rtnl.c index 48a8f52b5991..331180e617ca 100644 --- a/net/ieee802154/6lowpan_rtnl.c +++ b/net/ieee802154/6lowpan_rtnl.c @@ -91,7 +91,7 @@ static int lowpan_header_create(struct sk_buff *skb, { const u8 *saddr = _saddr; const u8 *daddr = _daddr; - struct ieee802154_addr sa, da; + struct ieee802154_addr_sa sa, da; /* TODO: * if this package isn't ipv6 one, where should it be routed? @@ -171,7 +171,7 @@ static int lowpan_give_skb_to_devices(struct sk_buff *skb, static int process_data(struct sk_buff *skb) { u8 iphc0, iphc1; - const struct ieee802154_addr *_saddr, *_daddr; + const struct ieee802154_addr_sa *_saddr, *_daddr; raw_dump_table(__func__, "raw skb data dump", skb->data, skb->len); /* at least two bytes will be used for the encoding */ diff --git a/net/ieee802154/af802154.h b/net/ieee802154/af802154.h index b1ec52537522..331d15cb93a7 100644 --- a/net/ieee802154/af802154.h +++ b/net/ieee802154/af802154.h @@ -31,6 +31,6 @@ extern struct proto ieee802154_dgram_prot; void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb); int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb); struct net_device *ieee802154_get_dev(struct net *net, - struct ieee802154_addr *addr); + struct ieee802154_addr_sa *addr); #endif diff --git a/net/ieee802154/af_ieee802154.c b/net/ieee802154/af_ieee802154.c index a56ab9c47278..a8db341581ac 100644 --- a/net/ieee802154/af_ieee802154.c +++ b/net/ieee802154/af_ieee802154.c @@ -44,7 +44,7 @@ * Utility function for families */ struct net_device *ieee802154_get_dev(struct net *net, - struct ieee802154_addr *addr) + struct ieee802154_addr_sa *addr) { struct net_device *dev = NULL; struct net_device *tmp; diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c index 1846c1fe0d06..405fdf9bf5e1 100644 --- a/net/ieee802154/dgram.c +++ b/net/ieee802154/dgram.c @@ -41,8 +41,8 @@ static DEFINE_RWLOCK(dgram_lock); struct dgram_sock { struct sock sk; - struct ieee802154_addr src_addr; - struct ieee802154_addr dst_addr; + struct ieee802154_addr_sa src_addr; + struct ieee802154_addr_sa dst_addr; unsigned int bound:1; unsigned int want_ack:1; @@ -113,7 +113,7 @@ static int dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len) goto out_put; } - memcpy(&ro->src_addr, &addr->addr, sizeof(struct ieee802154_addr)); + memcpy(&ro->src_addr, &addr->addr, sizeof(struct ieee802154_addr_sa)); ro->bound = 1; err = 0; @@ -181,7 +181,7 @@ static int dgram_connect(struct sock *sk, struct sockaddr *uaddr, goto out; } - memcpy(&ro->dst_addr, &addr->addr, sizeof(struct ieee802154_addr)); + memcpy(&ro->dst_addr, &addr->addr, sizeof(struct ieee802154_addr_sa)); out: release_sock(sk); diff --git a/net/ieee802154/nl-mac.c b/net/ieee802154/nl-mac.c index ba5c1e002f37..7ae93e1f8aa0 100644 --- a/net/ieee802154/nl-mac.c +++ b/net/ieee802154/nl-mac.c @@ -40,7 +40,7 @@ #include "ieee802154.h" int ieee802154_nl_assoc_indic(struct net_device *dev, - struct ieee802154_addr *addr, u8 cap) + struct ieee802154_addr_sa *addr, u8 cap) { struct sk_buff *msg; @@ -99,7 +99,7 @@ nla_put_failure: EXPORT_SYMBOL(ieee802154_nl_assoc_confirm); int ieee802154_nl_disassoc_indic(struct net_device *dev, - struct ieee802154_addr *addr, u8 reason) + struct ieee802154_addr_sa *addr, u8 reason) { struct sk_buff *msg; @@ -304,7 +304,7 @@ static struct net_device *ieee802154_nl_get_dev(struct genl_info *info) int ieee802154_associate_req(struct sk_buff *skb, struct genl_info *info) { struct net_device *dev; - struct ieee802154_addr addr; + struct ieee802154_addr_sa addr; u8 page; int ret = -EOPNOTSUPP; @@ -351,7 +351,7 @@ out: int ieee802154_associate_resp(struct sk_buff *skb, struct genl_info *info) { struct net_device *dev; - struct ieee802154_addr addr; + struct ieee802154_addr_sa addr; int ret = -EOPNOTSUPP; if (!info->attrs[IEEE802154_ATTR_STATUS] || @@ -383,7 +383,7 @@ out: int ieee802154_disassociate_req(struct sk_buff *skb, struct genl_info *info) { struct net_device *dev; - struct ieee802154_addr addr; + struct ieee802154_addr_sa addr; int ret = -EOPNOTSUPP; if ((!info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] && @@ -425,7 +425,7 @@ out: int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info) { struct net_device *dev; - struct ieee802154_addr addr; + struct ieee802154_addr_sa addr; u8 channel, bcn_ord, sf_ord; u8 page; diff --git a/net/ieee802154/reassembly.c b/net/ieee802154/reassembly.c index 1dae1991883d..f08b37a24b1d 100644 --- a/net/ieee802154/reassembly.c +++ b/net/ieee802154/reassembly.c @@ -36,8 +36,8 @@ static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *prev, struct net_device *dev); static unsigned int lowpan_hash_frag(__be16 tag, u16 d_size, - const struct ieee802154_addr *saddr, - const struct ieee802154_addr *daddr) + const struct ieee802154_addr_sa *saddr, + const struct ieee802154_addr_sa *daddr) { u32 c; @@ -103,7 +103,7 @@ out: static inline struct lowpan_frag_queue * fq_find(struct net *net, const struct ieee802154_frag_info *frag_info, - const struct ieee802154_addr *src, const struct ieee802154_addr *dst) + const struct ieee802154_addr_sa *src, const struct ieee802154_addr_sa *dst) { struct inet_frag_queue *q; struct lowpan_create_arg arg; diff --git a/net/ieee802154/reassembly.h b/net/ieee802154/reassembly.h index 055518b9da2d..895721ae71e1 100644 --- a/net/ieee802154/reassembly.h +++ b/net/ieee802154/reassembly.h @@ -6,8 +6,8 @@ struct lowpan_create_arg { __be16 tag; u16 d_size; - const struct ieee802154_addr *src; - const struct ieee802154_addr *dst; + const struct ieee802154_addr_sa *src; + const struct ieee802154_addr_sa *dst; }; /* Equivalent of ipv4 struct ip @@ -17,11 +17,11 @@ struct lowpan_frag_queue { __be16 tag; u16 d_size; - struct ieee802154_addr saddr; - struct ieee802154_addr daddr; + struct ieee802154_addr_sa saddr; + struct ieee802154_addr_sa daddr; }; -static inline u32 ieee802154_addr_hash(const struct ieee802154_addr *a) +static inline u32 ieee802154_addr_hash(const struct ieee802154_addr_sa *a) { switch (a->addr_type) { case IEEE802154_ADDR_LONG: @@ -34,8 +34,9 @@ static inline u32 ieee802154_addr_hash(const struct ieee802154_addr *a) } } -static inline bool ieee802154_addr_addr_equal(const struct ieee802154_addr *a1, - const struct ieee802154_addr *a2) +static inline bool +ieee802154_addr_addr_equal(const struct ieee802154_addr_sa *a1, + const struct ieee802154_addr_sa *a2) { if (a1->pan_id != a2->pan_id) return false; diff --git a/net/mac802154/mac_cmd.c b/net/mac802154/mac_cmd.c index a99910d4d52f..e079c57c48ca 100644 --- a/net/mac802154/mac_cmd.c +++ b/net/mac802154/mac_cmd.c @@ -34,7 +34,7 @@ #include "mac802154.h" static int mac802154_mlme_start_req(struct net_device *dev, - struct ieee802154_addr *addr, + struct ieee802154_addr_sa *addr, u8 channel, u8 page, u8 bcn_ord, u8 sf_ord, u8 pan_coord, u8 blx, diff --git a/net/mac802154/wpan.c b/net/mac802154/wpan.c index 372d8a222b91..b2bc3f030190 100644 --- a/net/mac802154/wpan.c +++ b/net/mac802154/wpan.c @@ -132,9 +132,9 @@ static int mac802154_header_create(struct sk_buff *skb, const void *_saddr, unsigned len) { - const struct ieee802154_addr *saddr = _saddr; - const struct ieee802154_addr *daddr = _daddr; - struct ieee802154_addr dev_addr; + const struct ieee802154_addr_sa *saddr = _saddr; + const struct ieee802154_addr_sa *daddr = _daddr; + struct ieee802154_addr_sa dev_addr; struct mac802154_sub_if_data *priv = netdev_priv(dev); int pos = 2; u8 head[MAC802154_FRAME_HARD_HEADER_LEN]; @@ -219,7 +219,7 @@ mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr) { const u8 *hdr = skb_mac_header(skb); const u8 *tail = skb_tail_pointer(skb); - struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr; + struct ieee802154_addr_sa *addr = (struct ieee802154_addr_sa *)haddr; u16 fc; int da_type; @@ -304,7 +304,7 @@ mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr) goto malformed; } - return sizeof(struct ieee802154_addr); + return sizeof(struct ieee802154_addr_sa); malformed: pr_debug("malformed packet\n"); -- cgit v1.2.3-71-gd317 From 46ef0eb3ea65e7043aac17cb92982be879c65366 Mon Sep 17 00:00:00 2001 From: Phoebe Buckheister Date: Fri, 14 Mar 2014 21:23:58 +0100 Subject: ieee802154: add address struct with proper endiannes and some operations Add a replacement ieee802154_addr struct with proper endianness on fields. Short address fields are stored as __le16 as on the network, extended (EUI64) addresses are __le64 as opposed to the u8[8] format used previously. This disconnect with the netdev address, which is stored as big-endian u8[8], is intentional. Signed-off-by: Phoebe Buckheister Signed-off-by: David S. Miller --- include/net/ieee802154_netdev.h | 72 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) (limited to 'include/net') diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h index 53937cdbcd82..86d5d50a6a53 100644 --- a/include/net/ieee802154_netdev.h +++ b/include/net/ieee802154_netdev.h @@ -29,6 +29,78 @@ #include +struct ieee802154_addr { + u8 mode; + __le16 pan_id; + union { + __le16 short_addr; + __le64 extended_addr; + }; +}; + +static inline bool ieee802154_addr_equal(const struct ieee802154_addr *a1, + const struct ieee802154_addr *a2) +{ + if (a1->pan_id != a2->pan_id || a1->mode != a2->mode) + return false; + + if ((a1->mode == IEEE802154_ADDR_LONG && + a1->extended_addr != a2->extended_addr) || + (a1->mode == IEEE802154_ADDR_SHORT && + a1->short_addr != a2->short_addr)) + return false; + + return true; +} + +static inline __le64 ieee802154_devaddr_from_raw(const void *raw) +{ + u64 temp; + + memcpy(&temp, raw, IEEE802154_ADDR_LEN); + return (__force __le64)swab64(temp); +} + +static inline void ieee802154_devaddr_to_raw(void *raw, __le64 addr) +{ + u64 temp = swab64((__force u64)addr); + + memcpy(raw, &temp, IEEE802154_ADDR_LEN); +} + +static inline void ieee802154_addr_from_sa(struct ieee802154_addr *a, + const struct ieee802154_addr_sa *sa) +{ + a->mode = sa->addr_type; + a->pan_id = cpu_to_le16(sa->pan_id); + + switch (a->mode) { + case IEEE802154_ADDR_SHORT: + a->short_addr = cpu_to_le16(sa->short_addr); + break; + case IEEE802154_ADDR_LONG: + a->extended_addr = ieee802154_devaddr_from_raw(sa->hwaddr); + break; + } +} + +static inline void ieee802154_addr_to_sa(struct ieee802154_addr_sa *sa, + const struct ieee802154_addr *a) +{ + sa->addr_type = a->mode; + sa->pan_id = le16_to_cpu(a->pan_id); + + switch (a->mode) { + case IEEE802154_ADDR_SHORT: + sa->short_addr = le16_to_cpu(a->short_addr); + break; + case IEEE802154_ADDR_LONG: + ieee802154_devaddr_to_raw(sa->hwaddr, a->extended_addr); + break; + } +} + + struct ieee802154_frag_info { __be16 d_tag; u16 d_size; -- cgit v1.2.3-71-gd317 From b70ab2e87f17176d18f67ef331064441a032b5f3 Mon Sep 17 00:00:00 2001 From: Phoebe Buckheister Date: Fri, 14 Mar 2014 21:23:59 +0100 Subject: ieee802154: enforce consistent endianness in the 802.15.4 stack Enable sparse warnings about endianness, replace the remaining fields regarding network operations without explicit endianness annotations with such that are annotated, and propagate this through the entire stack. Uses of ieee802154_addr_sa are not changed yet, this patch is only concerned with all other fields (such as address filters, operation parameters and the likes). Signed-off-by: Phoebe Buckheister Signed-off-by: David S. Miller --- drivers/net/ieee802154/at86rf230.c | 25 +++++++++++----------- drivers/net/ieee802154/fakehard.c | 14 ++++++------- drivers/net/ieee802154/mrf24j40.c | 17 ++++++++------- include/net/ieee802154_netdev.h | 6 +++--- include/net/mac802154.h | 5 ++--- include/net/nl802154.h | 6 +++--- net/ieee802154/6lowpan_rtnl.c | 8 +++---- net/ieee802154/Makefile | 2 ++ net/ieee802154/af_ieee802154.c | 10 ++++----- net/ieee802154/dgram.c | 4 ++-- net/ieee802154/nl-mac.c | 32 +++++++++++++++------------- net/mac802154/Makefile | 2 ++ net/mac802154/ieee802154_dev.c | 5 ++++- net/mac802154/mac802154.h | 9 ++++---- net/mac802154/mac_cmd.c | 4 ++-- net/mac802154/mib.c | 24 +++++++++++---------- net/mac802154/wpan.c | 43 ++++++++++++++++++++++++-------------- 17 files changed, 121 insertions(+), 95 deletions(-) (limited to 'include/net') diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index b8e732121a85..934a12c03552 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -745,30 +745,31 @@ at86rf230_set_hw_addr_filt(struct ieee802154_dev *dev, struct at86rf230_local *lp = dev->priv; if (changed & IEEE802515_AFILT_SADDR_CHANGED) { + u16 addr = le16_to_cpu(filt->short_addr); + dev_vdbg(&lp->spi->dev, "at86rf230_set_hw_addr_filt called for saddr\n"); - __at86rf230_write(lp, RG_SHORT_ADDR_0, filt->short_addr); - __at86rf230_write(lp, RG_SHORT_ADDR_1, filt->short_addr >> 8); + __at86rf230_write(lp, RG_SHORT_ADDR_0, addr); + __at86rf230_write(lp, RG_SHORT_ADDR_1, addr >> 8); } if (changed & IEEE802515_AFILT_PANID_CHANGED) { + u16 pan = le16_to_cpu(filt->pan_id); + dev_vdbg(&lp->spi->dev, "at86rf230_set_hw_addr_filt called for pan id\n"); - __at86rf230_write(lp, RG_PAN_ID_0, filt->pan_id); - __at86rf230_write(lp, RG_PAN_ID_1, filt->pan_id >> 8); + __at86rf230_write(lp, RG_PAN_ID_0, pan); + __at86rf230_write(lp, RG_PAN_ID_1, pan >> 8); } if (changed & IEEE802515_AFILT_IEEEADDR_CHANGED) { + u8 i, addr[8]; + + memcpy(addr, &filt->ieee_addr, 8); dev_vdbg(&lp->spi->dev, "at86rf230_set_hw_addr_filt called for IEEE addr\n"); - at86rf230_write_subreg(lp, SR_IEEE_ADDR_0, filt->ieee_addr[7]); - at86rf230_write_subreg(lp, SR_IEEE_ADDR_1, filt->ieee_addr[6]); - at86rf230_write_subreg(lp, SR_IEEE_ADDR_2, filt->ieee_addr[5]); - at86rf230_write_subreg(lp, SR_IEEE_ADDR_3, filt->ieee_addr[4]); - at86rf230_write_subreg(lp, SR_IEEE_ADDR_4, filt->ieee_addr[3]); - at86rf230_write_subreg(lp, SR_IEEE_ADDR_5, filt->ieee_addr[2]); - at86rf230_write_subreg(lp, SR_IEEE_ADDR_6, filt->ieee_addr[1]); - at86rf230_write_subreg(lp, SR_IEEE_ADDR_7, filt->ieee_addr[0]); + for (i = 0; i < 8; i++) + __at86rf230_write(lp, RG_IEEE_ADDR_0 + i, addr[i]); } if (changed & IEEE802515_AFILT_PANC_CHANGED) { diff --git a/drivers/net/ieee802154/fakehard.c b/drivers/net/ieee802154/fakehard.c index 06a400f10565..3c98030e0e0b 100644 --- a/drivers/net/ieee802154/fakehard.c +++ b/drivers/net/ieee802154/fakehard.c @@ -63,11 +63,11 @@ static struct wpan_phy *fake_get_phy(const struct net_device *dev) * * Return the ID of the PAN from the PIB. */ -static u16 fake_get_pan_id(const struct net_device *dev) +static __le16 fake_get_pan_id(const struct net_device *dev) { BUG_ON(dev->type != ARPHRD_IEEE802154); - return 0xeba1; + return cpu_to_le16(0xeba1); } /** @@ -78,11 +78,11 @@ static u16 fake_get_pan_id(const struct net_device *dev) * device. If the device has not yet had a short address assigned * then this should return 0xFFFF to indicate a lack of association. */ -static u16 fake_get_short_addr(const struct net_device *dev) +static __le16 fake_get_short_addr(const struct net_device *dev) { BUG_ON(dev->type != ARPHRD_IEEE802154); - return 0x1; + return cpu_to_le16(0x1); } /** @@ -149,7 +149,7 @@ static int fake_assoc_req(struct net_device *dev, * 802.15.4-2006 document. */ static int fake_assoc_resp(struct net_device *dev, - struct ieee802154_addr_sa *addr, u16 short_addr, u8 status) + struct ieee802154_addr_sa *addr, __le16 short_addr, u8 status) { return 0; } @@ -281,8 +281,8 @@ static int ieee802154_fake_ioctl(struct net_device *dev, struct ifreq *ifr, switch (cmd) { case SIOCGIFADDR: /* FIXME: fixed here, get from device IRL */ - pan_id = fake_get_pan_id(dev); - short_addr = fake_get_short_addr(dev); + pan_id = le16_to_cpu(fake_get_pan_id(dev)); + short_addr = le16_to_cpu(fake_get_short_addr(dev)); if (pan_id == IEEE802154_PANID_BROADCAST || short_addr == IEEE802154_ADDR_BROADCAST) return -EADDRNOTAVAIL; diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index 246befa4ba05..78a6552ed707 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -465,8 +465,8 @@ static int mrf24j40_filter(struct ieee802154_dev *dev, if (changed & IEEE802515_AFILT_SADDR_CHANGED) { /* Short Addr */ u8 addrh, addrl; - addrh = filt->short_addr >> 8 & 0xff; - addrl = filt->short_addr & 0xff; + addrh = le16_to_cpu(filt->short_addr) >> 8 & 0xff; + addrl = le16_to_cpu(filt->short_addr) & 0xff; write_short_reg(devrec, REG_SADRH, addrh); write_short_reg(devrec, REG_SADRL, addrl); @@ -476,15 +476,16 @@ static int mrf24j40_filter(struct ieee802154_dev *dev, if (changed & IEEE802515_AFILT_IEEEADDR_CHANGED) { /* Device Address */ - int i; + u8 i, addr[8]; + + memcpy(addr, &filt->ieee_addr, 8); for (i = 0; i < 8; i++) - write_short_reg(devrec, REG_EADR0+i, - filt->ieee_addr[7-i]); + write_short_reg(devrec, REG_EADR0 + i, addr[i]); #ifdef DEBUG printk(KERN_DEBUG "Set long addr to: "); for (i = 0; i < 8; i++) - printk("%02hhx ", filt->ieee_addr[i]); + printk("%02hhx ", addr[7 - i]); printk(KERN_DEBUG "\n"); #endif } @@ -492,8 +493,8 @@ static int mrf24j40_filter(struct ieee802154_dev *dev, if (changed & IEEE802515_AFILT_PANID_CHANGED) { /* PAN ID */ u8 panidl, panidh; - panidh = filt->pan_id >> 8 & 0xff; - panidl = filt->pan_id & 0xff; + panidh = le16_to_cpu(filt->pan_id) >> 8 & 0xff; + panidl = le16_to_cpu(filt->pan_id) & 0xff; write_short_reg(devrec, REG_PANIDH, panidh); write_short_reg(devrec, REG_PANIDL, panidl); diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h index 86d5d50a6a53..e4810d566b1b 100644 --- a/include/net/ieee802154_netdev.h +++ b/include/net/ieee802154_netdev.h @@ -171,7 +171,7 @@ struct ieee802154_mlme_ops { u8 channel, u8 page, u8 cap); int (*assoc_resp)(struct net_device *dev, struct ieee802154_addr_sa *addr, - u16 short_addr, u8 status); + __le16 short_addr, u8 status); int (*disassoc_req)(struct net_device *dev, struct ieee802154_addr_sa *addr, u8 reason); @@ -190,8 +190,8 @@ struct ieee802154_mlme_ops { * FIXME: these should become the part of PIB/MIB interface. * However we still don't have IB interface of any kind */ - u16 (*get_pan_id)(const struct net_device *dev); - u16 (*get_short_addr)(const struct net_device *dev); + __le16 (*get_pan_id)(const struct net_device *dev); + __le16 (*get_short_addr)(const struct net_device *dev); u8 (*get_dsn)(const struct net_device *dev); }; diff --git a/include/net/mac802154.h b/include/net/mac802154.h index 8ca3d04e7558..f74b2a8bf2b6 100644 --- a/include/net/mac802154.h +++ b/include/net/mac802154.h @@ -50,7 +50,7 @@ struct ieee802154_hw_addr_filt { * devices across independent networks. */ __le16 short_addr; - u8 ieee_addr[IEEE802154_ADDR_LEN]; + __le64 ieee_addr; u8 pan_coord; }; @@ -153,8 +153,7 @@ struct ieee802154_ops { int (*set_hw_addr_filt)(struct ieee802154_dev *dev, struct ieee802154_hw_addr_filt *filt, unsigned long changed); - int (*ieee_addr)(struct ieee802154_dev *dev, - u8 addr[IEEE802154_ADDR_LEN]); + int (*ieee_addr)(struct ieee802154_dev *dev, __le64 addr); int (*set_txpower)(struct ieee802154_dev *dev, int db); int (*set_lbt)(struct ieee802154_dev *dev, bool on); int (*set_cca_mode)(struct ieee802154_dev *dev, u8 mode); diff --git a/include/net/nl802154.h b/include/net/nl802154.h index 06ead976755a..3121ed047c1e 100644 --- a/include/net/nl802154.h +++ b/include/net/nl802154.h @@ -52,7 +52,7 @@ int ieee802154_nl_assoc_indic(struct net_device *dev, * Note: This is in section 7.3.2 of the IEEE 802.15.4 document. */ int ieee802154_nl_assoc_confirm(struct net_device *dev, - u16 short_addr, u8 status); + __le16 short_addr, u8 status); /** * ieee802154_nl_disassoc_indic - Notify userland of disassociation. @@ -111,8 +111,8 @@ int ieee802154_nl_scan_confirm(struct net_device *dev, * Note: This API cannot indicate a beacon frame for a coordinator * operating in long addressing mode. */ -int ieee802154_nl_beacon_indic(struct net_device *dev, u16 panid, - u16 coord_addr); +int ieee802154_nl_beacon_indic(struct net_device *dev, __le16 panid, + __le16 coord_addr); /** * ieee802154_nl_start_confirm - Notify userland of completion of start. diff --git a/net/ieee802154/6lowpan_rtnl.c b/net/ieee802154/6lowpan_rtnl.c index 331180e617ca..c23349d737ae 100644 --- a/net/ieee802154/6lowpan_rtnl.c +++ b/net/ieee802154/6lowpan_rtnl.c @@ -120,11 +120,11 @@ static int lowpan_header_create(struct sk_buff *skb, /* prepare wpan address data */ sa.addr_type = IEEE802154_ADDR_LONG; - sa.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); + sa.pan_id = le16_to_cpu(ieee802154_mlme_ops(dev)->get_pan_id(dev)); memcpy(&(sa.hwaddr), saddr, 8); /* intra-PAN communications */ - da.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); + da.pan_id = sa.pan_id; /* if the destination address is the broadcast address, use the * corresponding short address @@ -352,13 +352,13 @@ static struct wpan_phy *lowpan_get_phy(const struct net_device *dev) return ieee802154_mlme_ops(real_dev)->get_phy(real_dev); } -static u16 lowpan_get_pan_id(const struct net_device *dev) +static __le16 lowpan_get_pan_id(const struct net_device *dev) { struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; return ieee802154_mlme_ops(real_dev)->get_pan_id(real_dev); } -static u16 lowpan_get_short_addr(const struct net_device *dev) +static __le16 lowpan_get_short_addr(const struct net_device *dev) { struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; return ieee802154_mlme_ops(real_dev)->get_short_addr(real_dev); diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile index b113fc4be3e0..78b1fa23d30e 100644 --- a/net/ieee802154/Makefile +++ b/net/ieee802154/Makefile @@ -5,3 +5,5 @@ obj-$(CONFIG_6LOWPAN_IPHC) += 6lowpan_iphc.o 6lowpan-y := 6lowpan_rtnl.o reassembly.o ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o wpan-class.o af_802154-y := af_ieee802154.o raw.o dgram.o + +ccflags-y += -D__CHECK_ENDIAN__ diff --git a/net/ieee802154/af_ieee802154.c b/net/ieee802154/af_ieee802154.c index a8db341581ac..973cb11da42b 100644 --- a/net/ieee802154/af_ieee802154.c +++ b/net/ieee802154/af_ieee802154.c @@ -48,7 +48,7 @@ struct net_device *ieee802154_get_dev(struct net *net, { struct net_device *dev = NULL; struct net_device *tmp; - u16 pan_id, short_addr; + __le16 pan_id, short_addr; switch (addr->addr_type) { case IEEE802154_ADDR_LONG: @@ -59,9 +59,9 @@ struct net_device *ieee802154_get_dev(struct net *net, rcu_read_unlock(); break; case IEEE802154_ADDR_SHORT: - if (addr->pan_id == 0xffff || + if (addr->pan_id == IEEE802154_PANID_BROADCAST || addr->short_addr == IEEE802154_ADDR_UNDEF || - addr->short_addr == 0xffff) + addr->short_addr == IEEE802154_ADDR_UNDEF) break; rtnl_lock(); @@ -74,8 +74,8 @@ struct net_device *ieee802154_get_dev(struct net *net, short_addr = ieee802154_mlme_ops(tmp)->get_short_addr(tmp); - if (pan_id == addr->pan_id && - short_addr == addr->short_addr) { + if (le16_to_cpu(pan_id) == addr->pan_id && + le16_to_cpu(short_addr) == addr->short_addr) { dev = tmp; dev_hold(dev); break; diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c index 405fdf9bf5e1..9df3a1d94376 100644 --- a/net/ieee802154/dgram.c +++ b/net/ieee802154/dgram.c @@ -363,8 +363,8 @@ int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb) /* Data frame processing */ BUG_ON(dev->type != ARPHRD_IEEE802154); - pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); - short_addr = ieee802154_mlme_ops(dev)->get_short_addr(dev); + pan_id = le16_to_cpu(ieee802154_mlme_ops(dev)->get_pan_id(dev)); + short_addr = le16_to_cpu(ieee802154_mlme_ops(dev)->get_short_addr(dev)); read_lock(&dgram_lock); sk_for_each(sk, &dgram_head) { diff --git a/net/ieee802154/nl-mac.c b/net/ieee802154/nl-mac.c index 7ae93e1f8aa0..58fa523fb536 100644 --- a/net/ieee802154/nl-mac.c +++ b/net/ieee802154/nl-mac.c @@ -72,7 +72,7 @@ nla_put_failure: } EXPORT_SYMBOL(ieee802154_nl_assoc_indic); -int ieee802154_nl_assoc_confirm(struct net_device *dev, u16 short_addr, +int ieee802154_nl_assoc_confirm(struct net_device *dev, __le16 short_addr, u8 status) { struct sk_buff *msg; @@ -87,7 +87,8 @@ int ieee802154_nl_assoc_confirm(struct net_device *dev, u16 short_addr, nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, dev->dev_addr) || - nla_put_u16(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) || + nla_put_u16(msg, IEEE802154_ATTR_SHORT_ADDR, + le16_to_cpu(short_addr)) || nla_put_u8(msg, IEEE802154_ATTR_STATUS, status)) goto nla_put_failure; return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP); @@ -157,8 +158,8 @@ nla_put_failure: } EXPORT_SYMBOL(ieee802154_nl_disassoc_confirm); -int ieee802154_nl_beacon_indic(struct net_device *dev, - u16 panid, u16 coord_addr) +int ieee802154_nl_beacon_indic(struct net_device *dev, __le16 panid, + __le16 coord_addr) { struct sk_buff *msg; @@ -172,8 +173,10 @@ int ieee802154_nl_beacon_indic(struct net_device *dev, nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, dev->dev_addr) || - nla_put_u16(msg, IEEE802154_ATTR_COORD_SHORT_ADDR, coord_addr) || - nla_put_u16(msg, IEEE802154_ATTR_COORD_PAN_ID, panid)) + nla_put_u16(msg, IEEE802154_ATTR_COORD_SHORT_ADDR, + le16_to_cpu(coord_addr)) || + nla_put_u16(msg, IEEE802154_ATTR_COORD_PAN_ID, + le16_to_cpu(panid))) goto nla_put_failure; return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP); @@ -243,6 +246,7 @@ static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid, { void *hdr; struct wpan_phy *phy; + u16 short_addr, pan_id; pr_debug("%s\n", __func__); @@ -254,15 +258,16 @@ static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid, phy = ieee802154_mlme_ops(dev)->get_phy(dev); BUG_ON(!phy); + short_addr = le16_to_cpu(ieee802154_mlme_ops(dev)->get_short_addr(dev)); + pan_id = le16_to_cpu(ieee802154_mlme_ops(dev)->get_pan_id(dev)); + if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) || nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, dev->dev_addr) || - nla_put_u16(msg, IEEE802154_ATTR_SHORT_ADDR, - ieee802154_mlme_ops(dev)->get_short_addr(dev)) || - nla_put_u16(msg, IEEE802154_ATTR_PAN_ID, - ieee802154_mlme_ops(dev)->get_pan_id(dev))) + nla_put_u16(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) || + nla_put_u16(msg, IEEE802154_ATTR_PAN_ID, pan_id)) goto nla_put_failure; wpan_phy_put(phy); return genlmsg_end(msg, hdr); @@ -368,11 +373,10 @@ int ieee802154_associate_resp(struct sk_buff *skb, struct genl_info *info) addr.addr_type = IEEE802154_ADDR_LONG; nla_memcpy(addr.hwaddr, info->attrs[IEEE802154_ATTR_DEST_HW_ADDR], IEEE802154_ADDR_LEN); - addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); - + addr.pan_id = le16_to_cpu(ieee802154_mlme_ops(dev)->get_pan_id(dev)); ret = ieee802154_mlme_ops(dev)->assoc_resp(dev, &addr, - nla_get_u16(info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]), + cpu_to_le16(nla_get_u16(info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR])), nla_get_u8(info->attrs[IEEE802154_ATTR_STATUS])); out: @@ -407,7 +411,7 @@ int ieee802154_disassociate_req(struct sk_buff *skb, struct genl_info *info) addr.short_addr = nla_get_u16( info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]); } - addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); + addr.pan_id = le16_to_cpu(ieee802154_mlme_ops(dev)->get_pan_id(dev)); ret = ieee802154_mlme_ops(dev)->disassoc_req(dev, &addr, nla_get_u8(info->attrs[IEEE802154_ATTR_REASON])); diff --git a/net/mac802154/Makefile b/net/mac802154/Makefile index 57cf5d1a2e4a..15d62df52182 100644 --- a/net/mac802154/Makefile +++ b/net/mac802154/Makefile @@ -1,2 +1,4 @@ obj-$(CONFIG_MAC802154) += mac802154.o mac802154-objs := ieee802154_dev.o rx.o tx.o mac_cmd.o mib.o monitor.o wpan.o + +ccflags-y += -D__CHECK_ENDIAN__ diff --git a/net/mac802154/ieee802154_dev.c b/net/mac802154/ieee802154_dev.c index b75bb01e5c6b..10cdb091b775 100644 --- a/net/mac802154/ieee802154_dev.c +++ b/net/mac802154/ieee802154_dev.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -46,7 +47,9 @@ int mac802154_slave_open(struct net_device *dev) } if (ipriv->ops->ieee_addr) { - res = ipriv->ops->ieee_addr(&ipriv->hw, dev->dev_addr); + __le64 addr = ieee802154_devaddr_from_raw(dev->dev_addr); + + res = ipriv->ops->ieee_addr(&ipriv->hw, addr); WARN_ON(res); if (res) goto err; diff --git a/net/mac802154/mac802154.h b/net/mac802154/mac802154.h index d48422e27110..4619486f1da2 100644 --- a/net/mac802154/mac802154.h +++ b/net/mac802154/mac802154.h @@ -76,6 +76,7 @@ struct mac802154_sub_if_data { __le16 pan_id; __le16 short_addr; + __le64 extended_addr; u8 chan; u8 page; @@ -106,11 +107,11 @@ netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb, u8 page, u8 chan); /* MIB callbacks */ -void mac802154_dev_set_short_addr(struct net_device *dev, u16 val); -u16 mac802154_dev_get_short_addr(const struct net_device *dev); +void mac802154_dev_set_short_addr(struct net_device *dev, __le16 val); +__le16 mac802154_dev_get_short_addr(const struct net_device *dev); void mac802154_dev_set_ieee_addr(struct net_device *dev); -u16 mac802154_dev_get_pan_id(const struct net_device *dev); -void mac802154_dev_set_pan_id(struct net_device *dev, u16 val); +__le16 mac802154_dev_get_pan_id(const struct net_device *dev); +void mac802154_dev_set_pan_id(struct net_device *dev, __le16 val); void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan); u8 mac802154_dev_get_dsn(const struct net_device *dev); diff --git a/net/mac802154/mac_cmd.c b/net/mac802154/mac_cmd.c index e079c57c48ca..f551ef2cdf56 100644 --- a/net/mac802154/mac_cmd.c +++ b/net/mac802154/mac_cmd.c @@ -42,8 +42,8 @@ static int mac802154_mlme_start_req(struct net_device *dev, { BUG_ON(addr->addr_type != IEEE802154_ADDR_SHORT); - mac802154_dev_set_pan_id(dev, addr->pan_id); - mac802154_dev_set_short_addr(dev, addr->short_addr); + mac802154_dev_set_pan_id(dev, cpu_to_le16(addr->pan_id)); + mac802154_dev_set_short_addr(dev, cpu_to_le16(addr->short_addr)); mac802154_dev_set_ieee_addr(dev); mac802154_dev_set_page_channel(dev, page, channel); diff --git a/net/mac802154/mib.c b/net/mac802154/mib.c index f48f40c1da1a..ba5abdcbd25f 100644 --- a/net/mac802154/mib.c +++ b/net/mac802154/mib.c @@ -24,6 +24,7 @@ #include #include +#include #include #include "mac802154.h" @@ -79,7 +80,7 @@ static void set_hw_addr_filt(struct net_device *dev, unsigned long changed) queue_work(priv->hw->dev_workqueue, &work->work); } -void mac802154_dev_set_short_addr(struct net_device *dev, u16 val) +void mac802154_dev_set_short_addr(struct net_device *dev, __le16 val) { struct mac802154_sub_if_data *priv = netdev_priv(dev); @@ -96,10 +97,10 @@ void mac802154_dev_set_short_addr(struct net_device *dev, u16 val) } } -u16 mac802154_dev_get_short_addr(const struct net_device *dev) +__le16 mac802154_dev_get_short_addr(const struct net_device *dev) { struct mac802154_sub_if_data *priv = netdev_priv(dev); - u16 ret; + __le16 ret; BUG_ON(dev->type != ARPHRD_IEEE802154); @@ -114,20 +115,21 @@ void mac802154_dev_set_ieee_addr(struct net_device *dev) { struct mac802154_sub_if_data *priv = netdev_priv(dev); struct mac802154_priv *mac = priv->hw; + __le64 addr; - if (mac->ops->set_hw_addr_filt && - memcmp(mac->hw.hw_filt.ieee_addr, - dev->dev_addr, IEEE802154_ADDR_LEN)) { - memcpy(mac->hw.hw_filt.ieee_addr, - dev->dev_addr, IEEE802154_ADDR_LEN); + addr = ieee802154_devaddr_from_raw(dev->dev_addr); + priv->extended_addr = addr; + + if (mac->ops->set_hw_addr_filt && mac->hw.hw_filt.ieee_addr != addr) { + mac->hw.hw_filt.ieee_addr = addr; set_hw_addr_filt(dev, IEEE802515_AFILT_IEEEADDR_CHANGED); } } -u16 mac802154_dev_get_pan_id(const struct net_device *dev) +__le16 mac802154_dev_get_pan_id(const struct net_device *dev) { struct mac802154_sub_if_data *priv = netdev_priv(dev); - u16 ret; + __le16 ret; BUG_ON(dev->type != ARPHRD_IEEE802154); @@ -138,7 +140,7 @@ u16 mac802154_dev_get_pan_id(const struct net_device *dev) return ret; } -void mac802154_dev_set_pan_id(struct net_device *dev, u16 val) +void mac802154_dev_set_pan_id(struct net_device *dev, __le16 val) { struct mac802154_sub_if_data *priv = netdev_priv(dev); diff --git a/net/mac802154/wpan.c b/net/mac802154/wpan.c index b2bc3f030190..43e886bb9073 100644 --- a/net/mac802154/wpan.c +++ b/net/mac802154/wpan.c @@ -76,19 +76,25 @@ mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) switch (cmd) { case SIOCGIFADDR: - if (priv->pan_id == IEEE802154_PANID_BROADCAST || - priv->short_addr == IEEE802154_ADDR_BROADCAST) { + { + u16 pan_id, short_addr; + + pan_id = le16_to_cpu(priv->pan_id); + short_addr = le16_to_cpu(priv->short_addr); + if (pan_id == IEEE802154_PANID_BROADCAST || + short_addr == IEEE802154_ADDR_BROADCAST) { err = -EADDRNOTAVAIL; break; } sa->family = AF_IEEE802154; sa->addr.addr_type = IEEE802154_ADDR_SHORT; - sa->addr.pan_id = priv->pan_id; - sa->addr.short_addr = priv->short_addr; + sa->addr.pan_id = pan_id; + sa->addr.short_addr = short_addr; err = 0; break; + } case SIOCSIFADDR: dev_warn(&dev->dev, "Using DEBUGing ioctl SIOCSIFADDR isn't recommened!\n"); @@ -101,8 +107,8 @@ mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) break; } - priv->pan_id = sa->addr.pan_id; - priv->short_addr = sa->addr.short_addr; + priv->pan_id = cpu_to_le16(sa->addr.pan_id); + priv->short_addr = cpu_to_le16(sa->addr.short_addr); err = 0; break; @@ -151,18 +157,18 @@ static int mac802154_header_create(struct sk_buff *skb, if (!saddr) { spin_lock_bh(&priv->mib_lock); - if (priv->short_addr == IEEE802154_ADDR_BROADCAST || - priv->short_addr == IEEE802154_ADDR_UNDEF || - priv->pan_id == IEEE802154_PANID_BROADCAST) { + if (priv->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST) || + priv->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) || + priv->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) { dev_addr.addr_type = IEEE802154_ADDR_LONG; memcpy(dev_addr.hwaddr, dev->dev_addr, IEEE802154_ADDR_LEN); } else { dev_addr.addr_type = IEEE802154_ADDR_SHORT; - dev_addr.short_addr = priv->short_addr; + dev_addr.short_addr = le16_to_cpu(priv->short_addr); } - dev_addr.pan_id = priv->pan_id; + dev_addr.pan_id = le16_to_cpu(priv->pan_id); saddr = &dev_addr; spin_unlock_bh(&priv->mib_lock); @@ -382,8 +388,8 @@ void mac802154_wpan_setup(struct net_device *dev) get_random_bytes(&priv->bsn, 1); get_random_bytes(&priv->dsn, 1); - priv->pan_id = IEEE802154_PANID_BROADCAST; - priv->short_addr = IEEE802154_ADDR_BROADCAST; + priv->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST); + priv->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST); } static int mac802154_process_data(struct net_device *dev, struct sk_buff *skb) @@ -394,10 +400,15 @@ static int mac802154_process_data(struct net_device *dev, struct sk_buff *skb) static int mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb) { + u16 span, sshort; + pr_debug("getting packet via slave interface %s\n", sdata->dev->name); spin_lock_bh(&sdata->mib_lock); + span = le16_to_cpu(sdata->pan_id); + sshort = le16_to_cpu(sdata->short_addr); + switch (mac_cb(skb)->da.addr_type) { case IEEE802154_ADDR_NONE: if (mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_NONE) @@ -408,7 +419,7 @@ mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb) skb->pkt_type = PACKET_HOST; break; case IEEE802154_ADDR_LONG: - if (mac_cb(skb)->da.pan_id != sdata->pan_id && + if (mac_cb(skb)->da.pan_id != span && mac_cb(skb)->da.pan_id != IEEE802154_PANID_BROADCAST) skb->pkt_type = PACKET_OTHERHOST; else if (!memcmp(mac_cb(skb)->da.hwaddr, sdata->dev->dev_addr, @@ -418,10 +429,10 @@ mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb) skb->pkt_type = PACKET_OTHERHOST; break; case IEEE802154_ADDR_SHORT: - if (mac_cb(skb)->da.pan_id != sdata->pan_id && + if (mac_cb(skb)->da.pan_id != span && mac_cb(skb)->da.pan_id != IEEE802154_PANID_BROADCAST) skb->pkt_type = PACKET_OTHERHOST; - else if (mac_cb(skb)->da.short_addr == sdata->short_addr) + else if (mac_cb(skb)->da.short_addr == sshort) skb->pkt_type = PACKET_HOST; else if (mac_cb(skb)->da.short_addr == IEEE802154_ADDR_BROADCAST) -- cgit v1.2.3-71-gd317 From 94b4f6c21cf54029377a0645675a9d81b6cf890d Mon Sep 17 00:00:00 2001 From: Phoebe Buckheister Date: Fri, 14 Mar 2014 21:24:00 +0100 Subject: ieee802154: add header structs with endiannes and operations This patch provides a set of structures to represent 802.15.4 MAC headers, and a set of operations to push/pull/peek these structs from skbs. We cannot simply pointer-cast the skb MAC header pointer to these structs, because 802.15.4 headers are wildly variable - depending on the first three bytes, virtually all other fields of the header may be present or not, and be present with different lengths. The new header creation/parsing routines also support 802.15.4 security headers, which are currently not supported by the mac802154 implementation of the protocol. Signed-off-by: Phoebe Buckheister Signed-off-by: David S. Miller --- include/net/ieee802154.h | 28 +++- include/net/ieee802154_netdev.h | 87 ++++++++++++ include/net/mac802154.h | 1 + net/ieee802154/Makefile | 3 +- net/ieee802154/header_ops.c | 287 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 401 insertions(+), 5 deletions(-) create mode 100644 net/ieee802154/header_ops.c (limited to 'include/net') diff --git a/include/net/ieee802154.h b/include/net/ieee802154.h index ee59f8b188dd..c7ae0ac528dc 100644 --- a/include/net/ieee802154.h +++ b/include/net/ieee802154.h @@ -42,22 +42,42 @@ (((x) << IEEE802154_FC_TYPE_SHIFT) & IEEE802154_FC_TYPE_MASK)); \ } while (0) -#define IEEE802154_FC_SECEN (1 << 3) -#define IEEE802154_FC_FRPEND (1 << 4) -#define IEEE802154_FC_ACK_REQ (1 << 5) -#define IEEE802154_FC_INTRA_PAN (1 << 6) +#define IEEE802154_FC_SECEN_SHIFT 3 +#define IEEE802154_FC_SECEN (1 << IEEE802154_FC_SECEN_SHIFT) +#define IEEE802154_FC_FRPEND_SHIFT 4 +#define IEEE802154_FC_FRPEND (1 << IEEE802154_FC_FRPEND_SHIFT) +#define IEEE802154_FC_ACK_REQ_SHIFT 5 +#define IEEE802154_FC_ACK_REQ (1 << IEEE802154_FC_ACK_REQ_SHIFT) +#define IEEE802154_FC_INTRA_PAN_SHIFT 6 +#define IEEE802154_FC_INTRA_PAN (1 << IEEE802154_FC_INTRA_PAN_SHIFT) #define IEEE802154_FC_SAMODE_SHIFT 14 #define IEEE802154_FC_SAMODE_MASK (3 << IEEE802154_FC_SAMODE_SHIFT) #define IEEE802154_FC_DAMODE_SHIFT 10 #define IEEE802154_FC_DAMODE_MASK (3 << IEEE802154_FC_DAMODE_SHIFT) +#define IEEE802154_FC_VERSION_SHIFT 12 +#define IEEE802154_FC_VERSION_MASK (3 << IEEE802154_FC_VERSION_SHIFT) +#define IEEE802154_FC_VERSION(x) ((x & IEEE802154_FC_VERSION_MASK) >> IEEE802154_FC_VERSION_SHIFT) + #define IEEE802154_FC_SAMODE(x) \ (((x) & IEEE802154_FC_SAMODE_MASK) >> IEEE802154_FC_SAMODE_SHIFT) #define IEEE802154_FC_DAMODE(x) \ (((x) & IEEE802154_FC_DAMODE_MASK) >> IEEE802154_FC_DAMODE_SHIFT) +#define IEEE802154_SCF_SECLEVEL_MASK 7 +#define IEEE802154_SCF_SECLEVEL_SHIFT 0 +#define IEEE802154_SCF_SECLEVEL(x) (x & IEEE802154_SCF_SECLEVEL_MASK) +#define IEEE802154_SCF_KEY_ID_MODE_SHIFT 3 +#define IEEE802154_SCF_KEY_ID_MODE_MASK (3 << IEEE802154_SCF_KEY_ID_MODE_SHIFT) +#define IEEE802154_SCF_KEY_ID_MODE(x) \ + ((x & IEEE802154_SCF_KEY_ID_MODE_MASK) >> IEEE802154_SCF_KEY_ID_MODE_SHIFT) + +#define IEEE802154_SCF_KEY_IMPLICIT 0 +#define IEEE802154_SCF_KEY_INDEX 1 +#define IEEE802154_SCF_KEY_SHORT_INDEX 2 +#define IEEE802154_SCF_KEY_HW_INDEX 3 /* MAC footer size */ #define IEEE802154_MFR_SIZE 2 /* 2 octets */ diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h index e4810d566b1b..c3fc33a78920 100644 --- a/include/net/ieee802154_netdev.h +++ b/include/net/ieee802154_netdev.h @@ -28,6 +28,28 @@ #define IEEE802154_NETDEVICE_H #include +#include +#include + +struct ieee802154_sechdr { +#if defined(__LITTLE_ENDIAN_BITFIELD) + u8 level:3, + key_id_mode:2, + reserved:3; +#elif defined(__BIG_ENDIAN_BITFIELD) + u8 reserved:3, + key_id_mode:2, + level:3; +#else +#error "Please fix " +#endif + u8 key_id; + __le32 frame_counter; + union { + __le32 short_src; + __le64 extended_src; + }; +}; struct ieee802154_addr { u8 mode; @@ -38,6 +60,71 @@ struct ieee802154_addr { }; }; +struct ieee802154_hdr_fc { +#if defined(__LITTLE_ENDIAN_BITFIELD) + u16 type:3, + security_enabled:1, + frame_pending:1, + ack_request:1, + intra_pan:1, + reserved:3, + dest_addr_mode:2, + version:2, + source_addr_mode:2; +#elif defined(__BIG_ENDIAN_BITFIELD) + u16 reserved:1, + intra_pan:1, + ack_request:1, + frame_pending:1, + security_enabled:1, + type:3, + source_addr_mode:2, + version:2, + dest_addr_mode:2, + reserved2:2; +#else +#error "Please fix " +#endif +}; + +struct ieee802154_hdr { + struct ieee802154_hdr_fc fc; + u8 seq; + struct ieee802154_addr source; + struct ieee802154_addr dest; + struct ieee802154_sechdr sec; +}; + +/* pushes hdr onto the skb. fields of hdr->fc that can be calculated from + * the contents of hdr will be, and the actual value of those bits in + * hdr->fc will be ignored. this includes the INTRA_PAN bit and the frame + * version, if SECEN is set. + */ +int ieee802154_hdr_push(struct sk_buff *skb, const struct ieee802154_hdr *hdr); + +/* pulls the entire 802.15.4 header off of the skb, including the security + * header, and performs pan id decompression + */ +int ieee802154_hdr_pull(struct sk_buff *skb, struct ieee802154_hdr *hdr); + +/* parses the frame control, sequence number of address fields in a given skb + * and stores them into hdr, performing pan id decompression and length checks + * to be suitable for use in header_ops.parse + */ +int ieee802154_hdr_peek_addrs(const struct sk_buff *skb, + struct ieee802154_hdr *hdr); + +static inline int ieee802154_hdr_length(struct sk_buff *skb) +{ + struct ieee802154_hdr hdr; + int len = ieee802154_hdr_pull(skb, &hdr); + + if (len > 0) + skb_push(skb, len); + + return len; +} + static inline bool ieee802154_addr_equal(const struct ieee802154_addr *a1, const struct ieee802154_addr *a2) { diff --git a/include/net/mac802154.h b/include/net/mac802154.h index f74b2a8bf2b6..a591053cae63 100644 --- a/include/net/mac802154.h +++ b/include/net/mac802154.h @@ -20,6 +20,7 @@ #define NET_MAC802154_H #include +#include /* General MAC frame format: * 2 bytes: Frame Control diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile index 78b1fa23d30e..bf1b51497a41 100644 --- a/net/ieee802154/Makefile +++ b/net/ieee802154/Makefile @@ -3,7 +3,8 @@ obj-$(CONFIG_IEEE802154_6LOWPAN) += 6lowpan.o obj-$(CONFIG_6LOWPAN_IPHC) += 6lowpan_iphc.o 6lowpan-y := 6lowpan_rtnl.o reassembly.o -ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o wpan-class.o +ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o wpan-class.o \ + header_ops.o af_802154-y := af_ieee802154.o raw.o dgram.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/net/ieee802154/header_ops.c b/net/ieee802154/header_ops.c new file mode 100644 index 000000000000..bed42a48408c --- /dev/null +++ b/net/ieee802154/header_ops.c @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2014 Fraunhofer ITWM + * + * 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. + * + * 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. + * + * Written by: + * Phoebe Buckheister + */ + +#include +#include +#include + +static int +ieee802154_hdr_push_addr(u8 *buf, const struct ieee802154_addr *addr, + bool omit_pan) +{ + int pos = 0; + + if (addr->mode == IEEE802154_ADDR_NONE) + return 0; + + if (!omit_pan) { + memcpy(buf + pos, &addr->pan_id, 2); + pos += 2; + } + + switch (addr->mode) { + case IEEE802154_ADDR_SHORT: + memcpy(buf + pos, &addr->short_addr, 2); + pos += 2; + break; + + case IEEE802154_ADDR_LONG: + memcpy(buf + pos, &addr->extended_addr, IEEE802154_ADDR_LEN); + pos += IEEE802154_ADDR_LEN; + break; + + default: + return -EINVAL; + } + + return pos; +} + +static int +ieee802154_hdr_push_sechdr(u8 *buf, const struct ieee802154_sechdr *hdr) +{ + int pos = 5; + + memcpy(buf, hdr, 1); + memcpy(buf + 1, &hdr->frame_counter, 4); + + switch (hdr->key_id_mode) { + case IEEE802154_SCF_KEY_IMPLICIT: + return pos; + + case IEEE802154_SCF_KEY_INDEX: + break; + + case IEEE802154_SCF_KEY_SHORT_INDEX: + memcpy(buf + pos, &hdr->short_src, 4); + pos += 4; + break; + + case IEEE802154_SCF_KEY_HW_INDEX: + memcpy(buf + pos, &hdr->extended_src, IEEE802154_ADDR_LEN); + pos += IEEE802154_ADDR_LEN; + break; + } + + buf[pos++] = hdr->key_id; + + return pos; +} + +int +ieee802154_hdr_push(struct sk_buff *skb, const struct ieee802154_hdr *hdr) +{ + u8 buf[MAC802154_FRAME_HARD_HEADER_LEN]; + int pos = 2; + int rc; + struct ieee802154_hdr_fc fc = hdr->fc; + + buf[pos++] = hdr->seq; + + fc.dest_addr_mode = hdr->dest.mode; + + rc = ieee802154_hdr_push_addr(buf + pos, &hdr->dest, false); + if (rc < 0) + return -EINVAL; + pos += rc; + + fc.source_addr_mode = hdr->source.mode; + + if (hdr->source.pan_id == hdr->dest.pan_id && + hdr->dest.mode != IEEE802154_ADDR_NONE) + fc.intra_pan = true; + + rc = ieee802154_hdr_push_addr(buf + pos, &hdr->source, fc.intra_pan); + if (rc < 0) + return -EINVAL; + pos += rc; + + if (fc.security_enabled) { + fc.version = 1; + + rc = ieee802154_hdr_push_sechdr(buf + pos, &hdr->sec); + if (rc < 0) + return -EINVAL; + + pos += rc; + } + + memcpy(buf, &fc, 2); + + memcpy(skb_push(skb, pos), buf, pos); + + return pos; +} +EXPORT_SYMBOL_GPL(ieee802154_hdr_push); + +static int +ieee802154_hdr_get_addr(const u8 *buf, int mode, bool omit_pan, + struct ieee802154_addr *addr) +{ + int pos = 0; + + addr->mode = mode; + + if (mode == IEEE802154_ADDR_NONE) + return 0; + + if (!omit_pan) { + memcpy(&addr->pan_id, buf + pos, 2); + pos += 2; + } + + if (mode == IEEE802154_ADDR_SHORT) { + memcpy(&addr->short_addr, buf + pos, 2); + return pos + 2; + } else { + memcpy(&addr->extended_addr, buf + pos, IEEE802154_ADDR_LEN); + return pos + IEEE802154_ADDR_LEN; + } +} + +static int ieee802154_hdr_addr_len(int mode, bool omit_pan) +{ + int pan_len = omit_pan ? 0 : 2; + + switch (mode) { + case IEEE802154_ADDR_NONE: return 0; + case IEEE802154_ADDR_SHORT: return 2 + pan_len; + case IEEE802154_ADDR_LONG: return IEEE802154_ADDR_LEN + pan_len; + default: return -EINVAL; + } +} + +static int +ieee802154_hdr_get_sechdr(const u8 *buf, struct ieee802154_sechdr *hdr) +{ + int pos = 5; + + memcpy(hdr, buf, 1); + memcpy(&hdr->frame_counter, buf + 1, 4); + + switch (hdr->key_id_mode) { + case IEEE802154_SCF_KEY_IMPLICIT: + return pos; + + case IEEE802154_SCF_KEY_INDEX: + break; + + case IEEE802154_SCF_KEY_SHORT_INDEX: + memcpy(&hdr->short_src, buf + pos, 4); + pos += 4; + break; + + case IEEE802154_SCF_KEY_HW_INDEX: + memcpy(&hdr->extended_src, buf + pos, IEEE802154_ADDR_LEN); + pos += IEEE802154_ADDR_LEN; + break; + } + + hdr->key_id = buf[pos++]; + + return pos; +} + +static int ieee802154_hdr_sechdr_len(u8 sc) +{ + switch (IEEE802154_SCF_KEY_ID_MODE(sc)) { + case IEEE802154_SCF_KEY_IMPLICIT: return 5; + case IEEE802154_SCF_KEY_INDEX: return 6; + case IEEE802154_SCF_KEY_SHORT_INDEX: return 10; + case IEEE802154_SCF_KEY_HW_INDEX: return 14; + default: return -EINVAL; + } +} + +static int ieee802154_hdr_minlen(const struct ieee802154_hdr *hdr) +{ + int dlen, slen; + + dlen = ieee802154_hdr_addr_len(hdr->fc.dest_addr_mode, false); + slen = ieee802154_hdr_addr_len(hdr->fc.source_addr_mode, + hdr->fc.intra_pan); + + if (slen < 0 || dlen < 0) + return -EINVAL; + + return 3 + dlen + slen + hdr->fc.security_enabled; +} + +static int +ieee802154_hdr_get_addrs(const u8 *buf, struct ieee802154_hdr *hdr) +{ + int pos = 0; + + pos += ieee802154_hdr_get_addr(buf + pos, hdr->fc.dest_addr_mode, + false, &hdr->dest); + pos += ieee802154_hdr_get_addr(buf + pos, hdr->fc.source_addr_mode, + hdr->fc.intra_pan, &hdr->source); + + if (hdr->fc.intra_pan) + hdr->source.pan_id = hdr->dest.pan_id; + + return pos; +} + +int +ieee802154_hdr_pull(struct sk_buff *skb, struct ieee802154_hdr *hdr) +{ + int pos = 3, rc; + + if (!pskb_may_pull(skb, 3)) + return -EINVAL; + + memcpy(hdr, skb->data, 3); + + rc = ieee802154_hdr_minlen(hdr); + if (rc < 0 || !pskb_may_pull(skb, rc)) + return -EINVAL; + + pos += ieee802154_hdr_get_addrs(skb->data + pos, hdr); + + if (hdr->fc.security_enabled) { + int want = pos + ieee802154_hdr_sechdr_len(skb->data[pos]); + + if (!pskb_may_pull(skb, want)) + return -EINVAL; + + pos += ieee802154_hdr_get_sechdr(skb->data + pos, &hdr->sec); + } + + skb_pull(skb, pos); + return pos; +} +EXPORT_SYMBOL_GPL(ieee802154_hdr_pull); + +int +ieee802154_hdr_peek_addrs(const struct sk_buff *skb, struct ieee802154_hdr *hdr) +{ + const u8 *buf = skb_mac_header(skb); + int pos = 3, rc; + + if (buf + 3 > skb_tail_pointer(skb)) + return -EINVAL; + + memcpy(hdr, buf, 3); + + rc = ieee802154_hdr_minlen(hdr); + if (rc < 0 || buf + rc > skb_tail_pointer(skb)) + return -EINVAL; + + pos += ieee802154_hdr_get_addrs(buf + pos, hdr); + return pos; +} +EXPORT_SYMBOL_GPL(ieee802154_hdr_peek_addrs); -- cgit v1.2.3-71-gd317 From e6278d92005e9d6e374f269b4ce39c908a68ad5d Mon Sep 17 00:00:00 2001 From: Phoebe Buckheister Date: Fri, 14 Mar 2014 21:24:01 +0100 Subject: mac802154: use header operations to create/parse headers Use the operations on 802.15.4 header structs introduced in a previous patch to create and parse all headers in the mac802154 stack. This patch reduces code duplication between different parts of the mac802154 stack that needed information from headers, and also fixes a few bugs that seem to have gone unnoticed until now: * 802.15.4 dgram sockets would return a slightly incorrect value for the SIOCINQ ioctl * mac802154 would not drop frames with the "security enabled" bit set, even though it does not support security, in violation of the standard Signed-off-by: Phoebe Buckheister Signed-off-by: David S. Miller --- include/net/ieee802154_netdev.h | 10 +- net/ieee802154/6lowpan_rtnl.c | 16 +- net/ieee802154/af802154.h | 5 +- net/ieee802154/af_ieee802154.c | 22 +-- net/ieee802154/dgram.c | 60 ++++---- net/ieee802154/raw.c | 14 +- net/mac802154/mib.c | 10 +- net/mac802154/wpan.c | 321 +++++++++++----------------------------- 8 files changed, 154 insertions(+), 304 deletions(-) (limited to 'include/net') diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h index c3fc33a78920..8e7f6903db98 100644 --- a/include/net/ieee802154_netdev.h +++ b/include/net/ieee802154_netdev.h @@ -216,23 +216,17 @@ static inline struct ieee802154_mac_cb *mac_cb(struct sk_buff *skb) #define MAC_CB_FLAG_ACKREQ (1 << 3) #define MAC_CB_FLAG_SECEN (1 << 4) -#define MAC_CB_FLAG_INTRAPAN (1 << 5) -static inline int mac_cb_is_ackreq(struct sk_buff *skb) +static inline bool mac_cb_is_ackreq(struct sk_buff *skb) { return mac_cb(skb)->flags & MAC_CB_FLAG_ACKREQ; } -static inline int mac_cb_is_secen(struct sk_buff *skb) +static inline bool mac_cb_is_secen(struct sk_buff *skb) { return mac_cb(skb)->flags & MAC_CB_FLAG_SECEN; } -static inline int mac_cb_is_intrapan(struct sk_buff *skb) -{ - return mac_cb(skb)->flags & MAC_CB_FLAG_INTRAPAN; -} - static inline int mac_cb_type(struct sk_buff *skb) { return mac_cb(skb)->flags & MAC_CB_FLAG_TYPEMASK; diff --git a/net/ieee802154/6lowpan_rtnl.c b/net/ieee802154/6lowpan_rtnl.c index c23349d737ae..678564c7718b 100644 --- a/net/ieee802154/6lowpan_rtnl.c +++ b/net/ieee802154/6lowpan_rtnl.c @@ -91,7 +91,7 @@ static int lowpan_header_create(struct sk_buff *skb, { const u8 *saddr = _saddr; const u8 *daddr = _daddr; - struct ieee802154_addr_sa sa, da; + struct ieee802154_addr sa, da; /* TODO: * if this package isn't ipv6 one, where should it be routed? @@ -119,10 +119,10 @@ static int lowpan_header_create(struct sk_buff *skb, mac_cb(skb)->seq = ieee802154_mlme_ops(dev)->get_dsn(dev); /* prepare wpan address data */ - sa.addr_type = IEEE802154_ADDR_LONG; - sa.pan_id = le16_to_cpu(ieee802154_mlme_ops(dev)->get_pan_id(dev)); + sa.mode = IEEE802154_ADDR_LONG; + sa.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); + sa.extended_addr = ieee802154_devaddr_from_raw(saddr); - memcpy(&(sa.hwaddr), saddr, 8); /* intra-PAN communications */ da.pan_id = sa.pan_id; @@ -130,11 +130,11 @@ static int lowpan_header_create(struct sk_buff *skb, * corresponding short address */ if (lowpan_is_addr_broadcast(daddr)) { - da.addr_type = IEEE802154_ADDR_SHORT; - da.short_addr = IEEE802154_ADDR_BROADCAST; + da.mode = IEEE802154_ADDR_SHORT; + da.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST); } else { - da.addr_type = IEEE802154_ADDR_LONG; - memcpy(&(da.hwaddr), daddr, IEEE802154_ADDR_LEN); + da.mode = IEEE802154_ADDR_LONG; + da.extended_addr = ieee802154_devaddr_from_raw(daddr); /* request acknowledgment */ mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ; diff --git a/net/ieee802154/af802154.h b/net/ieee802154/af802154.h index 331d15cb93a7..8330a09bfc95 100644 --- a/net/ieee802154/af802154.h +++ b/net/ieee802154/af802154.h @@ -25,12 +25,13 @@ #define AF802154_H struct sk_buff; -struct net_devce; +struct net_device; +struct ieee802154_addr; extern struct proto ieee802154_raw_prot; extern struct proto ieee802154_dgram_prot; void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb); int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb); struct net_device *ieee802154_get_dev(struct net *net, - struct ieee802154_addr_sa *addr); + const struct ieee802154_addr *addr); #endif diff --git a/net/ieee802154/af_ieee802154.c b/net/ieee802154/af_ieee802154.c index 973cb11da42b..be44a86751aa 100644 --- a/net/ieee802154/af_ieee802154.c +++ b/net/ieee802154/af_ieee802154.c @@ -43,25 +43,27 @@ /* * Utility function for families */ -struct net_device *ieee802154_get_dev(struct net *net, - struct ieee802154_addr_sa *addr) +struct net_device* +ieee802154_get_dev(struct net *net, const struct ieee802154_addr *addr) { struct net_device *dev = NULL; struct net_device *tmp; __le16 pan_id, short_addr; + u8 hwaddr[IEEE802154_ADDR_LEN]; - switch (addr->addr_type) { + switch (addr->mode) { case IEEE802154_ADDR_LONG: + ieee802154_devaddr_to_raw(hwaddr, addr->extended_addr); rcu_read_lock(); - dev = dev_getbyhwaddr_rcu(net, ARPHRD_IEEE802154, addr->hwaddr); + dev = dev_getbyhwaddr_rcu(net, ARPHRD_IEEE802154, hwaddr); if (dev) dev_hold(dev); rcu_read_unlock(); break; case IEEE802154_ADDR_SHORT: - if (addr->pan_id == IEEE802154_PANID_BROADCAST || - addr->short_addr == IEEE802154_ADDR_UNDEF || - addr->short_addr == IEEE802154_ADDR_UNDEF) + if (addr->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST) || + addr->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) || + addr->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF)) break; rtnl_lock(); @@ -74,8 +76,8 @@ struct net_device *ieee802154_get_dev(struct net *net, short_addr = ieee802154_mlme_ops(tmp)->get_short_addr(tmp); - if (le16_to_cpu(pan_id) == addr->pan_id && - le16_to_cpu(short_addr) == addr->short_addr) { + if (pan_id == addr->pan_id && + short_addr == addr->short_addr) { dev = tmp; dev_hold(dev); break; @@ -86,7 +88,7 @@ struct net_device *ieee802154_get_dev(struct net *net, break; default: pr_warning("Unsupported ieee802154 address type: %d\n", - addr->addr_type); + addr->mode); break; } diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c index 9df3a1d94376..0a926c6bc8ca 100644 --- a/net/ieee802154/dgram.c +++ b/net/ieee802154/dgram.c @@ -41,8 +41,8 @@ static DEFINE_RWLOCK(dgram_lock); struct dgram_sock { struct sock sk; - struct ieee802154_addr_sa src_addr; - struct ieee802154_addr_sa dst_addr; + struct ieee802154_addr src_addr; + struct ieee802154_addr dst_addr; unsigned int bound:1; unsigned int want_ack:1; @@ -73,10 +73,10 @@ static int dgram_init(struct sock *sk) { struct dgram_sock *ro = dgram_sk(sk); - ro->dst_addr.addr_type = IEEE802154_ADDR_LONG; - ro->dst_addr.pan_id = 0xffff; + ro->dst_addr.mode = IEEE802154_ADDR_LONG; + ro->dst_addr.pan_id = cpu_to_le16(IEEE802154_ADDR_BROADCAST); ro->want_ack = 1; - memset(&ro->dst_addr.hwaddr, 0xff, sizeof(ro->dst_addr.hwaddr)); + memset(&ro->dst_addr.extended_addr, 0xff, IEEE802154_ADDR_LEN); return 0; } @@ -88,6 +88,7 @@ static void dgram_close(struct sock *sk, long timeout) static int dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len) { struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr; + struct ieee802154_addr haddr; struct dgram_sock *ro = dgram_sk(sk); int err = -EINVAL; struct net_device *dev; @@ -102,7 +103,8 @@ static int dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len) if (addr->family != AF_IEEE802154) goto out; - dev = ieee802154_get_dev(sock_net(sk), &addr->addr); + ieee802154_addr_from_sa(&haddr, &addr->addr); + dev = ieee802154_get_dev(sock_net(sk), &haddr); if (!dev) { err = -ENODEV; goto out; @@ -113,7 +115,7 @@ static int dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len) goto out_put; } - memcpy(&ro->src_addr, &addr->addr, sizeof(struct ieee802154_addr_sa)); + ro->src_addr = haddr; ro->bound = 1; err = 0; @@ -149,8 +151,7 @@ static int dgram_ioctl(struct sock *sk, int cmd, unsigned long arg) * of this packet since that is all * that will be read. */ - /* FIXME: parse the header for more correct value */ - amount = skb->len - (3+8+8); + amount = skb->len - ieee802154_hdr_length(skb); } spin_unlock_bh(&sk->sk_receive_queue.lock); return put_user(amount, (int __user *)arg); @@ -181,7 +182,7 @@ static int dgram_connect(struct sock *sk, struct sockaddr *uaddr, goto out; } - memcpy(&ro->dst_addr, &addr->addr, sizeof(struct ieee802154_addr_sa)); + ieee802154_addr_from_sa(&ro->dst_addr, &addr->addr); out: release_sock(sk); @@ -194,8 +195,8 @@ static int dgram_disconnect(struct sock *sk, int flags) lock_sock(sk); - ro->dst_addr.addr_type = IEEE802154_ADDR_LONG; - memset(&ro->dst_addr.hwaddr, 0xff, sizeof(ro->dst_addr.hwaddr)); + ro->dst_addr.mode = IEEE802154_ADDR_LONG; + memset(&ro->dst_addr.extended_addr, 0xff, IEEE802154_ADDR_LEN); release_sock(sk); @@ -336,40 +337,43 @@ static int dgram_rcv_skb(struct sock *sk, struct sk_buff *skb) return NET_RX_SUCCESS; } -static inline int ieee802154_match_sock(u8 *hw_addr, u16 pan_id, - u16 short_addr, struct dgram_sock *ro) +static inline bool +ieee802154_match_sock(__le64 hw_addr, __le16 pan_id, __le16 short_addr, + struct dgram_sock *ro) { if (!ro->bound) - return 1; + return true; - if (ro->src_addr.addr_type == IEEE802154_ADDR_LONG && - !memcmp(ro->src_addr.hwaddr, hw_addr, IEEE802154_ADDR_LEN)) - return 1; + if (ro->src_addr.mode == IEEE802154_ADDR_LONG && + hw_addr == ro->src_addr.extended_addr) + return true; - if (ro->src_addr.addr_type == IEEE802154_ADDR_SHORT && - pan_id == ro->src_addr.pan_id && - short_addr == ro->src_addr.short_addr) - return 1; + if (ro->src_addr.mode == IEEE802154_ADDR_SHORT && + pan_id == ro->src_addr.pan_id && + short_addr == ro->src_addr.short_addr) + return true; - return 0; + return false; } int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb) { struct sock *sk, *prev = NULL; int ret = NET_RX_SUCCESS; - u16 pan_id, short_addr; + __le16 pan_id, short_addr; + __le64 hw_addr; /* Data frame processing */ BUG_ON(dev->type != ARPHRD_IEEE802154); - pan_id = le16_to_cpu(ieee802154_mlme_ops(dev)->get_pan_id(dev)); - short_addr = le16_to_cpu(ieee802154_mlme_ops(dev)->get_short_addr(dev)); + pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); + short_addr = ieee802154_mlme_ops(dev)->get_short_addr(dev); + hw_addr = ieee802154_devaddr_from_raw(dev->dev_addr); read_lock(&dgram_lock); sk_for_each(sk, &dgram_head) { - if (ieee802154_match_sock(dev->dev_addr, pan_id, short_addr, - dgram_sk(sk))) { + if (ieee802154_match_sock(hw_addr, pan_id, short_addr, + dgram_sk(sk))) { if (prev) { struct sk_buff *clone; clone = skb_clone(skb, GFP_ATOMIC); diff --git a/net/ieee802154/raw.c b/net/ieee802154/raw.c index 41f538b8e59c..e5258cf6773b 100644 --- a/net/ieee802154/raw.c +++ b/net/ieee802154/raw.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "af802154.h" @@ -55,21 +56,24 @@ static void raw_close(struct sock *sk, long timeout) sk_common_release(sk); } -static int raw_bind(struct sock *sk, struct sockaddr *uaddr, int len) +static int raw_bind(struct sock *sk, struct sockaddr *_uaddr, int len) { - struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr; + struct ieee802154_addr addr; + struct sockaddr_ieee802154 *uaddr = (struct sockaddr_ieee802154 *)_uaddr; int err = 0; struct net_device *dev = NULL; - if (len < sizeof(*addr)) + if (len < sizeof(*uaddr)) return -EINVAL; - if (addr->family != AF_IEEE802154) + uaddr = (struct sockaddr_ieee802154 *)_uaddr; + if (uaddr->family != AF_IEEE802154) return -EINVAL; lock_sock(sk); - dev = ieee802154_get_dev(sock_net(sk), &addr->addr); + ieee802154_addr_from_sa(&addr, &uaddr->addr); + dev = ieee802154_get_dev(sock_net(sk), &addr); if (!dev) { err = -ENODEV; goto out; diff --git a/net/mac802154/mib.c b/net/mac802154/mib.c index ba5abdcbd25f..153bd1ddbfbb 100644 --- a/net/mac802154/mib.c +++ b/net/mac802154/mib.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "mac802154.h" @@ -115,13 +116,12 @@ void mac802154_dev_set_ieee_addr(struct net_device *dev) { struct mac802154_sub_if_data *priv = netdev_priv(dev); struct mac802154_priv *mac = priv->hw; - __le64 addr; - addr = ieee802154_devaddr_from_raw(dev->dev_addr); - priv->extended_addr = addr; + priv->extended_addr = ieee802154_devaddr_from_raw(dev->dev_addr); - if (mac->ops->set_hw_addr_filt && mac->hw.hw_filt.ieee_addr != addr) { - mac->hw.hw_filt.ieee_addr = addr; + if (mac->ops->set_hw_addr_filt && + mac->hw.hw_filt.ieee_addr != priv->extended_addr) { + mac->hw.hw_filt.ieee_addr = priv->extended_addr; set_hw_addr_filt(dev, IEEE802515_AFILT_IEEEADDR_CHANGED); } } diff --git a/net/mac802154/wpan.c b/net/mac802154/wpan.c index 43e886bb9073..051ed46ffca9 100644 --- a/net/mac802154/wpan.c +++ b/net/mac802154/wpan.c @@ -35,35 +35,6 @@ #include "mac802154.h" -static inline int mac802154_fetch_skb_u8(struct sk_buff *skb, u8 *val) -{ - if (unlikely(!pskb_may_pull(skb, 1))) - return -EINVAL; - - *val = skb->data[0]; - skb_pull(skb, 1); - - return 0; -} - -static inline int mac802154_fetch_skb_u16(struct sk_buff *skb, u16 *val) -{ - if (unlikely(!pskb_may_pull(skb, 2))) - return -EINVAL; - - *val = skb->data[0] | (skb->data[1] << 8); - skb_pull(skb, 2); - - return 0; -} - -static inline void mac802154_haddr_copy_swap(u8 *dest, const u8 *src) -{ - int i; - for (i = 0; i < IEEE802154_ADDR_LEN; i++) - dest[IEEE802154_ADDR_LEN - i - 1] = src[i]; -} - static int mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { @@ -134,25 +105,21 @@ static int mac802154_wpan_mac_addr(struct net_device *dev, void *p) static int mac802154_header_create(struct sk_buff *skb, struct net_device *dev, unsigned short type, - const void *_daddr, - const void *_saddr, + const void *daddr, + const void *saddr, unsigned len) { - const struct ieee802154_addr_sa *saddr = _saddr; - const struct ieee802154_addr_sa *daddr = _daddr; - struct ieee802154_addr_sa dev_addr; + struct ieee802154_hdr hdr; struct mac802154_sub_if_data *priv = netdev_priv(dev); - int pos = 2; - u8 head[MAC802154_FRAME_HARD_HEADER_LEN]; - u16 fc; + int hlen; if (!daddr) return -EINVAL; - head[pos++] = mac_cb(skb)->seq; /* DSN/BSN */ - fc = mac_cb_type(skb); - if (mac_cb_is_ackreq(skb)) - fc |= IEEE802154_FC_ACK_REQ; + memset(&hdr.fc, 0, sizeof(hdr.fc)); + hdr.fc.type = mac_cb_type(skb); + hdr.fc.security_enabled = mac_cb_is_secen(skb); + hdr.fc.ack_request = mac_cb_is_ackreq(skb); if (!saddr) { spin_lock_bh(&priv->mib_lock); @@ -160,161 +127,45 @@ static int mac802154_header_create(struct sk_buff *skb, if (priv->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST) || priv->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) || priv->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) { - dev_addr.addr_type = IEEE802154_ADDR_LONG; - memcpy(dev_addr.hwaddr, dev->dev_addr, - IEEE802154_ADDR_LEN); + hdr.source.mode = IEEE802154_ADDR_LONG; + hdr.source.extended_addr = priv->extended_addr; } else { - dev_addr.addr_type = IEEE802154_ADDR_SHORT; - dev_addr.short_addr = le16_to_cpu(priv->short_addr); + hdr.source.mode = IEEE802154_ADDR_SHORT; + hdr.source.short_addr = priv->short_addr; } - dev_addr.pan_id = le16_to_cpu(priv->pan_id); - saddr = &dev_addr; + hdr.source.pan_id = priv->pan_id; spin_unlock_bh(&priv->mib_lock); + } else { + hdr.source = *(const struct ieee802154_addr *)saddr; } - if (daddr->addr_type != IEEE802154_ADDR_NONE) { - fc |= (daddr->addr_type << IEEE802154_FC_DAMODE_SHIFT); - - head[pos++] = daddr->pan_id & 0xff; - head[pos++] = daddr->pan_id >> 8; - - if (daddr->addr_type == IEEE802154_ADDR_SHORT) { - head[pos++] = daddr->short_addr & 0xff; - head[pos++] = daddr->short_addr >> 8; - } else { - mac802154_haddr_copy_swap(head + pos, daddr->hwaddr); - pos += IEEE802154_ADDR_LEN; - } - } - - if (saddr->addr_type != IEEE802154_ADDR_NONE) { - fc |= (saddr->addr_type << IEEE802154_FC_SAMODE_SHIFT); - - if ((saddr->pan_id == daddr->pan_id) && - (saddr->pan_id != IEEE802154_PANID_BROADCAST)) { - /* PANID compression/intra PAN */ - fc |= IEEE802154_FC_INTRA_PAN; - } else { - head[pos++] = saddr->pan_id & 0xff; - head[pos++] = saddr->pan_id >> 8; - } + hdr.dest = *(const struct ieee802154_addr *)daddr; - if (saddr->addr_type == IEEE802154_ADDR_SHORT) { - head[pos++] = saddr->short_addr & 0xff; - head[pos++] = saddr->short_addr >> 8; - } else { - mac802154_haddr_copy_swap(head + pos, saddr->hwaddr); - pos += IEEE802154_ADDR_LEN; - } - } - - head[0] = fc; - head[1] = fc >> 8; + hlen = ieee802154_hdr_push(skb, &hdr); + if (hlen < 0) + return -EINVAL; - memcpy(skb_push(skb, pos), head, pos); skb_reset_mac_header(skb); - skb->mac_len = pos; + skb->mac_len = hlen; - return pos; + return hlen; } static int mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr) { - const u8 *hdr = skb_mac_header(skb); - const u8 *tail = skb_tail_pointer(skb); - struct ieee802154_addr_sa *addr = (struct ieee802154_addr_sa *)haddr; - u16 fc; - int da_type; - - if (hdr + 3 > tail) - goto malformed; - - fc = hdr[0] | (hdr[1] << 8); + struct ieee802154_hdr hdr; + struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr; - hdr += 3; - - da_type = IEEE802154_FC_DAMODE(fc); - addr->addr_type = IEEE802154_FC_SAMODE(fc); - - switch (da_type) { - case IEEE802154_ADDR_NONE: - if (fc & IEEE802154_FC_INTRA_PAN) - goto malformed; - break; - case IEEE802154_ADDR_LONG: - if (fc & IEEE802154_FC_INTRA_PAN) { - if (hdr + 2 > tail) - goto malformed; - addr->pan_id = hdr[0] | (hdr[1] << 8); - hdr += 2; - } - - if (hdr + IEEE802154_ADDR_LEN > tail) - goto malformed; - - hdr += IEEE802154_ADDR_LEN; - break; - case IEEE802154_ADDR_SHORT: - if (fc & IEEE802154_FC_INTRA_PAN) { - if (hdr + 2 > tail) - goto malformed; - addr->pan_id = hdr[0] | (hdr[1] << 8); - hdr += 2; - } - - if (hdr + 2 > tail) - goto malformed; - - hdr += 2; - break; - default: - goto malformed; - - } - - switch (addr->addr_type) { - case IEEE802154_ADDR_NONE: - break; - case IEEE802154_ADDR_LONG: - if (!(fc & IEEE802154_FC_INTRA_PAN)) { - if (hdr + 2 > tail) - goto malformed; - addr->pan_id = hdr[0] | (hdr[1] << 8); - hdr += 2; - } - - if (hdr + IEEE802154_ADDR_LEN > tail) - goto malformed; - - mac802154_haddr_copy_swap(addr->hwaddr, hdr); - hdr += IEEE802154_ADDR_LEN; - break; - case IEEE802154_ADDR_SHORT: - if (!(fc & IEEE802154_FC_INTRA_PAN)) { - if (hdr + 2 > tail) - goto malformed; - addr->pan_id = hdr[0] | (hdr[1] << 8); - hdr += 2; - } - - if (hdr + 2 > tail) - goto malformed; - - addr->short_addr = hdr[0] | (hdr[1] << 8); - hdr += 2; - break; - default: - goto malformed; + if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) { + pr_debug("malformed packet\n"); + return 0; } - return sizeof(struct ieee802154_addr_sa); - -malformed: - pr_debug("malformed packet\n"); - return 0; + *addr = hdr.source; + return sizeof(*addr); } static netdev_tx_t @@ -462,88 +313,82 @@ mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb) } } -static int mac802154_parse_frame_start(struct sk_buff *skb) +static void mac802154_print_addr(const char *name, + const struct ieee802154_addr *addr) { - u8 *head = skb->data; - u16 fc; - - if (mac802154_fetch_skb_u16(skb, &fc) || - mac802154_fetch_skb_u8(skb, &(mac_cb(skb)->seq))) - goto err; + if (addr->mode == IEEE802154_ADDR_NONE) + pr_debug("%s not present\n", name); - pr_debug("fc: %04x dsn: %02x\n", fc, head[2]); + pr_debug("%s PAN ID: %04x\n", name, le16_to_cpu(addr->pan_id)); + if (addr->mode == IEEE802154_ADDR_SHORT) { + pr_debug("%s is short: %04x\n", name, + le16_to_cpu(addr->short_addr)); + } else { + u64 hw = swab64((__force u64) addr->extended_addr); - mac_cb(skb)->flags = IEEE802154_FC_TYPE(fc); - mac_cb(skb)->sa.addr_type = IEEE802154_FC_SAMODE(fc); - mac_cb(skb)->da.addr_type = IEEE802154_FC_DAMODE(fc); + pr_debug("%s is hardware: %8phC\n", name, &hw); + } +} - if (fc & IEEE802154_FC_INTRA_PAN) - mac_cb(skb)->flags |= MAC_CB_FLAG_INTRAPAN; +static int mac802154_parse_frame_start(struct sk_buff *skb) +{ + struct ieee802154_hdr hdr; + int hlen; - if (mac_cb(skb)->da.addr_type != IEEE802154_ADDR_NONE) { - if (mac802154_fetch_skb_u16(skb, &(mac_cb(skb)->da.pan_id))) - goto err; + hlen = ieee802154_hdr_pull(skb, &hdr); + if (hlen < 0) + return -EINVAL; - /* source PAN id compression */ - if (mac_cb_is_intrapan(skb)) - mac_cb(skb)->sa.pan_id = mac_cb(skb)->da.pan_id; + skb->mac_len = hlen; - pr_debug("dest PAN addr: %04x\n", mac_cb(skb)->da.pan_id); + pr_debug("fc: %04x dsn: %02x\n", le16_to_cpup((__le16 *)&hdr.fc), + hdr.seq); - if (mac_cb(skb)->da.addr_type == IEEE802154_ADDR_SHORT) { - u16 *da = &(mac_cb(skb)->da.short_addr); + mac_cb(skb)->flags = hdr.fc.type; - if (mac802154_fetch_skb_u16(skb, da)) - goto err; + ieee802154_addr_to_sa(&mac_cb(skb)->sa, &hdr.source); + ieee802154_addr_to_sa(&mac_cb(skb)->da, &hdr.dest); - pr_debug("destination address is short: %04x\n", - mac_cb(skb)->da.short_addr); - } else { - if (!pskb_may_pull(skb, IEEE802154_ADDR_LEN)) - goto err; + if (hdr.fc.ack_request) + mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ; + if (hdr.fc.security_enabled) + mac_cb(skb)->flags |= MAC_CB_FLAG_SECEN; - mac802154_haddr_copy_swap(mac_cb(skb)->da.hwaddr, - skb->data); - skb_pull(skb, IEEE802154_ADDR_LEN); + mac802154_print_addr("destination", &hdr.dest); + mac802154_print_addr("source", &hdr.source); - pr_debug("destination address is hardware\n"); - } - } + if (hdr.fc.security_enabled) { + u64 key; - if (mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_NONE) { - /* non PAN-compression, fetch source address id */ - if (!(mac_cb_is_intrapan(skb))) { - u16 *sa_pan = &(mac_cb(skb)->sa.pan_id); + pr_debug("seclevel %i\n", hdr.sec.level); - if (mac802154_fetch_skb_u16(skb, sa_pan)) - goto err; - } - - pr_debug("source PAN addr: %04x\n", mac_cb(skb)->da.pan_id); - - if (mac_cb(skb)->sa.addr_type == IEEE802154_ADDR_SHORT) { - u16 *sa = &(mac_cb(skb)->sa.short_addr); - - if (mac802154_fetch_skb_u16(skb, sa)) - goto err; + switch (hdr.sec.key_id_mode) { + case IEEE802154_SCF_KEY_IMPLICIT: + pr_debug("implicit key\n"); + break; - pr_debug("source address is short: %04x\n", - mac_cb(skb)->sa.short_addr); - } else { - if (!pskb_may_pull(skb, IEEE802154_ADDR_LEN)) - goto err; + case IEEE802154_SCF_KEY_INDEX: + pr_debug("key %02x\n", hdr.sec.key_id); + break; - mac802154_haddr_copy_swap(mac_cb(skb)->sa.hwaddr, - skb->data); - skb_pull(skb, IEEE802154_ADDR_LEN); + case IEEE802154_SCF_KEY_SHORT_INDEX: + pr_debug("key %04x:%04x %02x\n", + le32_to_cpu(hdr.sec.short_src) >> 16, + le32_to_cpu(hdr.sec.short_src) & 0xffff, + hdr.sec.key_id); + break; - pr_debug("source address is hardware\n"); + case IEEE802154_SCF_KEY_HW_INDEX: + key = swab64((__force u64) hdr.sec.extended_src); + pr_debug("key source %8phC %02x\n", &key, + hdr.sec.key_id); + break; } + + return -EINVAL; } return 0; -err: - return -EINVAL; } void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb) -- cgit v1.2.3-71-gd317 From ae531b9475f62c5e1863508604cd6b3faf362d56 Mon Sep 17 00:00:00 2001 From: Phoebe Buckheister Date: Fri, 14 Mar 2014 21:24:02 +0100 Subject: ieee802154: use ieee802154_addr instead of *_sa variants Change all internal uses of ieee802154_addr_sa to ieee802154_addr, except for those instances that communicate directly with userspace. Signed-off-by: Phoebe Buckheister Signed-off-by: David S. Miller --- drivers/net/ieee802154/fakehard.c | 8 +-- include/net/ieee802154_netdev.h | 12 ++-- include/net/nl802154.h | 6 +- net/ieee802154/6lowpan_rtnl.c | 38 +++++++++---- net/ieee802154/dgram.c | 2 +- net/ieee802154/nl-mac.c | 114 ++++++++++++++++++++++---------------- net/ieee802154/reassembly.c | 17 ++++-- net/ieee802154/reassembly.h | 42 +++----------- net/mac802154/mac_cmd.c | 8 +-- net/mac802154/wpan.c | 35 ++++++------ 10 files changed, 146 insertions(+), 136 deletions(-) (limited to 'include/net') diff --git a/drivers/net/ieee802154/fakehard.c b/drivers/net/ieee802154/fakehard.c index 3c98030e0e0b..78f18be3bbf2 100644 --- a/drivers/net/ieee802154/fakehard.c +++ b/drivers/net/ieee802154/fakehard.c @@ -119,7 +119,7 @@ static u8 fake_get_dsn(const struct net_device *dev) * 802.15.4-2006 document. */ static int fake_assoc_req(struct net_device *dev, - struct ieee802154_addr_sa *addr, u8 channel, u8 page, u8 cap) + struct ieee802154_addr *addr, u8 channel, u8 page, u8 cap) { struct wpan_phy *phy = fake_to_phy(dev); @@ -149,7 +149,7 @@ static int fake_assoc_req(struct net_device *dev, * 802.15.4-2006 document. */ static int fake_assoc_resp(struct net_device *dev, - struct ieee802154_addr_sa *addr, __le16 short_addr, u8 status) + struct ieee802154_addr *addr, __le16 short_addr, u8 status) { return 0; } @@ -167,7 +167,7 @@ static int fake_assoc_resp(struct net_device *dev, * document, with the reason described in 7.3.3.2. */ static int fake_disassoc_req(struct net_device *dev, - struct ieee802154_addr_sa *addr, u8 reason) + struct ieee802154_addr *addr, u8 reason) { return ieee802154_nl_disassoc_confirm(dev, IEEE802154_SUCCESS); } @@ -192,7 +192,7 @@ static int fake_disassoc_req(struct net_device *dev, * document, with 7.3.8 describing coordinator realignment. */ static int fake_start_req(struct net_device *dev, - struct ieee802154_addr_sa *addr, u8 channel, u8 page, + struct ieee802154_addr *addr, u8 channel, u8 page, u8 bcn_ord, u8 sf_ord, u8 pan_coord, u8 blx, u8 coord_realign) { diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h index 8e7f6903db98..827e3e33c422 100644 --- a/include/net/ieee802154_netdev.h +++ b/include/net/ieee802154_netdev.h @@ -200,11 +200,11 @@ struct ieee802154_frag_info { */ struct ieee802154_mac_cb { u8 lqi; - struct ieee802154_addr_sa sa; - struct ieee802154_addr_sa da; u8 flags; u8 seq; struct ieee802154_frag_info frag_info; + struct ieee802154_addr source; + struct ieee802154_addr dest; }; static inline struct ieee802154_mac_cb *mac_cb(struct sk_buff *skb) @@ -248,16 +248,16 @@ struct ieee802154_mlme_ops { /* The following fields are optional (can be NULL). */ int (*assoc_req)(struct net_device *dev, - struct ieee802154_addr_sa *addr, + struct ieee802154_addr *addr, u8 channel, u8 page, u8 cap); int (*assoc_resp)(struct net_device *dev, - struct ieee802154_addr_sa *addr, + struct ieee802154_addr *addr, __le16 short_addr, u8 status); int (*disassoc_req)(struct net_device *dev, - struct ieee802154_addr_sa *addr, + struct ieee802154_addr *addr, u8 reason); int (*start_req)(struct net_device *dev, - struct ieee802154_addr_sa *addr, + struct ieee802154_addr *addr, u8 channel, u8 page, u8 bcn_ord, u8 sf_ord, u8 pan_coord, u8 blx, u8 coord_realign); int (*scan_req)(struct net_device *dev, diff --git a/include/net/nl802154.h b/include/net/nl802154.h index 3121ed047c1e..b23548e04098 100644 --- a/include/net/nl802154.h +++ b/include/net/nl802154.h @@ -22,7 +22,7 @@ #define IEEE802154_NL_H struct net_device; -struct ieee802154_addr_sa; +struct ieee802154_addr; /** * ieee802154_nl_assoc_indic - Notify userland of an association request. @@ -37,7 +37,7 @@ struct ieee802154_addr_sa; * Note: This is in section 7.3.1 of the IEEE 802.15.4-2006 document. */ int ieee802154_nl_assoc_indic(struct net_device *dev, - struct ieee802154_addr_sa *addr, u8 cap); + struct ieee802154_addr *addr, u8 cap); /** * ieee802154_nl_assoc_confirm - Notify userland of association. @@ -65,7 +65,7 @@ int ieee802154_nl_assoc_confirm(struct net_device *dev, * Note: This is in section 7.3.3 of the IEEE 802.15.4 document. */ int ieee802154_nl_disassoc_indic(struct net_device *dev, - struct ieee802154_addr_sa *addr, u8 reason); + struct ieee802154_addr *addr, u8 reason); /** * ieee802154_nl_disassoc_confirm - Notify userland of disassociation diff --git a/net/ieee802154/6lowpan_rtnl.c b/net/ieee802154/6lowpan_rtnl.c index 678564c7718b..d4edd20dab5f 100644 --- a/net/ieee802154/6lowpan_rtnl.c +++ b/net/ieee802154/6lowpan_rtnl.c @@ -168,10 +168,11 @@ static int lowpan_give_skb_to_devices(struct sk_buff *skb, return stat; } -static int process_data(struct sk_buff *skb) +static int process_data(struct sk_buff *skb, const struct ieee802154_hdr *hdr) { u8 iphc0, iphc1; - const struct ieee802154_addr_sa *_saddr, *_daddr; + struct ieee802154_addr_sa sa, da; + void *sap, *dap; raw_dump_table(__func__, "raw skb data dump", skb->data, skb->len); /* at least two bytes will be used for the encoding */ @@ -184,14 +185,23 @@ static int process_data(struct sk_buff *skb) if (lowpan_fetch_skb_u8(skb, &iphc1)) goto drop; - _saddr = &mac_cb(skb)->sa; - _daddr = &mac_cb(skb)->da; + ieee802154_addr_to_sa(&sa, &hdr->source); + ieee802154_addr_to_sa(&da, &hdr->dest); - return lowpan_process_data(skb, skb->dev, (u8 *)_saddr->hwaddr, - _saddr->addr_type, IEEE802154_ADDR_LEN, - (u8 *)_daddr->hwaddr, _daddr->addr_type, - IEEE802154_ADDR_LEN, iphc0, iphc1, - lowpan_give_skb_to_devices); + if (sa.addr_type == IEEE802154_ADDR_SHORT) + sap = &sa.short_addr; + else + sap = &sa.hwaddr; + + if (da.addr_type == IEEE802154_ADDR_SHORT) + dap = &da.short_addr; + else + dap = &da.hwaddr; + + return lowpan_process_data(skb, skb->dev, sap, sa.addr_type, + IEEE802154_ADDR_LEN, dap, da.addr_type, + IEEE802154_ADDR_LEN, iphc0, iphc1, + lowpan_give_skb_to_devices); drop: kfree_skb(skb); @@ -438,6 +448,7 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { struct sk_buff *local_skb; + struct ieee802154_hdr hdr; int ret; if (!netif_running(dev)) @@ -446,6 +457,9 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev, if (dev->type != ARPHRD_IEEE802154) goto drop_skb; + if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) + goto drop_skb; + local_skb = skb_clone(skb, GFP_ATOMIC); if (!local_skb) goto drop_skb; @@ -466,14 +480,14 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev, } else { switch (skb->data[0] & 0xe0) { case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */ - ret = process_data(local_skb); + ret = process_data(local_skb, &hdr); if (ret == NET_RX_DROP) goto drop; break; case LOWPAN_DISPATCH_FRAG1: /* first fragment header */ ret = lowpan_frag_rcv(local_skb, LOWPAN_DISPATCH_FRAG1); if (ret == 1) { - ret = process_data(local_skb); + ret = process_data(local_skb, &hdr); if (ret == NET_RX_DROP) goto drop; } @@ -481,7 +495,7 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev, case LOWPAN_DISPATCH_FRAGN: /* next fragments headers */ ret = lowpan_frag_rcv(local_skb, LOWPAN_DISPATCH_FRAGN); if (ret == 1) { - ret = process_data(local_skb); + ret = process_data(local_skb, &hdr); if (ret == NET_RX_DROP) goto drop; } diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c index 0a926c6bc8ca..55f2dc45a7dc 100644 --- a/net/ieee802154/dgram.c +++ b/net/ieee802154/dgram.c @@ -313,7 +313,7 @@ static int dgram_recvmsg(struct kiocb *iocb, struct sock *sk, if (saddr) { saddr->family = AF_IEEE802154; - saddr->addr = mac_cb(skb)->sa; + ieee802154_addr_to_sa(&saddr->addr, &mac_cb(skb)->source); *addr_len = sizeof(*saddr); } diff --git a/net/ieee802154/nl-mac.c b/net/ieee802154/nl-mac.c index 58fa523fb536..bda8dba4f993 100644 --- a/net/ieee802154/nl-mac.c +++ b/net/ieee802154/nl-mac.c @@ -39,14 +39,34 @@ #include "ieee802154.h" +static int nla_put_hwaddr(struct sk_buff *msg, int type, __le64 hwaddr) +{ + return nla_put_u64(msg, type, swab64((__force u64)hwaddr)); +} + +static __le64 nla_get_hwaddr(const struct nlattr *nla) +{ + return ieee802154_devaddr_from_raw(nla_data(nla)); +} + +static int nla_put_shortaddr(struct sk_buff *msg, int type, __le16 addr) +{ + return nla_put_u16(msg, type, le16_to_cpu(addr)); +} + +static __le16 nla_get_shortaddr(const struct nlattr *nla) +{ + return cpu_to_le16(nla_get_u16(nla)); +} + int ieee802154_nl_assoc_indic(struct net_device *dev, - struct ieee802154_addr_sa *addr, u8 cap) + struct ieee802154_addr *addr, u8 cap) { struct sk_buff *msg; pr_debug("%s\n", __func__); - if (addr->addr_type != IEEE802154_ADDR_LONG) { + if (addr->mode != IEEE802154_ADDR_LONG) { pr_err("%s: received non-long source address!\n", __func__); return -EINVAL; } @@ -59,8 +79,8 @@ int ieee802154_nl_assoc_indic(struct net_device *dev, nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, dev->dev_addr) || - nla_put(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN, - addr->hwaddr) || + nla_put_hwaddr(msg, IEEE802154_ATTR_SRC_HW_ADDR, + addr->extended_addr) || nla_put_u8(msg, IEEE802154_ATTR_CAPABILITY, cap)) goto nla_put_failure; @@ -87,8 +107,7 @@ int ieee802154_nl_assoc_confirm(struct net_device *dev, __le16 short_addr, nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, dev->dev_addr) || - nla_put_u16(msg, IEEE802154_ATTR_SHORT_ADDR, - le16_to_cpu(short_addr)) || + nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) || nla_put_u8(msg, IEEE802154_ATTR_STATUS, status)) goto nla_put_failure; return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP); @@ -100,7 +119,7 @@ nla_put_failure: EXPORT_SYMBOL(ieee802154_nl_assoc_confirm); int ieee802154_nl_disassoc_indic(struct net_device *dev, - struct ieee802154_addr_sa *addr, u8 reason) + struct ieee802154_addr *addr, u8 reason) { struct sk_buff *msg; @@ -115,13 +134,13 @@ int ieee802154_nl_disassoc_indic(struct net_device *dev, nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, dev->dev_addr)) goto nla_put_failure; - if (addr->addr_type == IEEE802154_ADDR_LONG) { - if (nla_put(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN, - addr->hwaddr)) + if (addr->mode == IEEE802154_ADDR_LONG) { + if (nla_put_hwaddr(msg, IEEE802154_ATTR_SRC_HW_ADDR, + addr->extended_addr)) goto nla_put_failure; } else { - if (nla_put_u16(msg, IEEE802154_ATTR_SRC_SHORT_ADDR, - addr->short_addr)) + if (nla_put_shortaddr(msg, IEEE802154_ATTR_SRC_SHORT_ADDR, + addr->short_addr)) goto nla_put_failure; } if (nla_put_u8(msg, IEEE802154_ATTR_REASON, reason)) @@ -173,10 +192,9 @@ int ieee802154_nl_beacon_indic(struct net_device *dev, __le16 panid, nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, dev->dev_addr) || - nla_put_u16(msg, IEEE802154_ATTR_COORD_SHORT_ADDR, - le16_to_cpu(coord_addr)) || - nla_put_u16(msg, IEEE802154_ATTR_COORD_PAN_ID, - le16_to_cpu(panid))) + nla_put_shortaddr(msg, IEEE802154_ATTR_COORD_SHORT_ADDR, + coord_addr) || + nla_put_shortaddr(msg, IEEE802154_ATTR_COORD_PAN_ID, panid)) goto nla_put_failure; return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP); @@ -246,7 +264,7 @@ static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid, { void *hdr; struct wpan_phy *phy; - u16 short_addr, pan_id; + __le16 short_addr, pan_id; pr_debug("%s\n", __func__); @@ -258,16 +276,16 @@ static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid, phy = ieee802154_mlme_ops(dev)->get_phy(dev); BUG_ON(!phy); - short_addr = le16_to_cpu(ieee802154_mlme_ops(dev)->get_short_addr(dev)); - pan_id = le16_to_cpu(ieee802154_mlme_ops(dev)->get_pan_id(dev)); + short_addr = ieee802154_mlme_ops(dev)->get_short_addr(dev); + pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) || nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, dev->dev_addr) || - nla_put_u16(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) || - nla_put_u16(msg, IEEE802154_ATTR_PAN_ID, pan_id)) + nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) || + nla_put_shortaddr(msg, IEEE802154_ATTR_PAN_ID, pan_id)) goto nla_put_failure; wpan_phy_put(phy); return genlmsg_end(msg, hdr); @@ -309,7 +327,7 @@ static struct net_device *ieee802154_nl_get_dev(struct genl_info *info) int ieee802154_associate_req(struct sk_buff *skb, struct genl_info *info) { struct net_device *dev; - struct ieee802154_addr_sa addr; + struct ieee802154_addr addr; u8 page; int ret = -EOPNOTSUPP; @@ -327,16 +345,16 @@ int ieee802154_associate_req(struct sk_buff *skb, struct genl_info *info) goto out; if (info->attrs[IEEE802154_ATTR_COORD_HW_ADDR]) { - addr.addr_type = IEEE802154_ADDR_LONG; - nla_memcpy(addr.hwaddr, - info->attrs[IEEE802154_ATTR_COORD_HW_ADDR], - IEEE802154_ADDR_LEN); + addr.mode = IEEE802154_ADDR_LONG; + addr.extended_addr = nla_get_hwaddr( + info->attrs[IEEE802154_ATTR_COORD_HW_ADDR]); } else { - addr.addr_type = IEEE802154_ADDR_SHORT; - addr.short_addr = nla_get_u16( + addr.mode = IEEE802154_ADDR_SHORT; + addr.short_addr = nla_get_shortaddr( info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]); } - addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]); + addr.pan_id = nla_get_shortaddr( + info->attrs[IEEE802154_ATTR_COORD_PAN_ID]); if (info->attrs[IEEE802154_ATTR_PAGE]) page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]); @@ -356,7 +374,7 @@ out: int ieee802154_associate_resp(struct sk_buff *skb, struct genl_info *info) { struct net_device *dev; - struct ieee802154_addr_sa addr; + struct ieee802154_addr addr; int ret = -EOPNOTSUPP; if (!info->attrs[IEEE802154_ATTR_STATUS] || @@ -370,13 +388,13 @@ int ieee802154_associate_resp(struct sk_buff *skb, struct genl_info *info) if (!ieee802154_mlme_ops(dev)->assoc_resp) goto out; - addr.addr_type = IEEE802154_ADDR_LONG; - nla_memcpy(addr.hwaddr, info->attrs[IEEE802154_ATTR_DEST_HW_ADDR], - IEEE802154_ADDR_LEN); - addr.pan_id = le16_to_cpu(ieee802154_mlme_ops(dev)->get_pan_id(dev)); + addr.mode = IEEE802154_ADDR_LONG; + addr.extended_addr = nla_get_hwaddr( + info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]); + addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); ret = ieee802154_mlme_ops(dev)->assoc_resp(dev, &addr, - cpu_to_le16(nla_get_u16(info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR])), + nla_get_shortaddr(info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]), nla_get_u8(info->attrs[IEEE802154_ATTR_STATUS])); out: @@ -387,7 +405,7 @@ out: int ieee802154_disassociate_req(struct sk_buff *skb, struct genl_info *info) { struct net_device *dev; - struct ieee802154_addr_sa addr; + struct ieee802154_addr addr; int ret = -EOPNOTSUPP; if ((!info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] && @@ -402,16 +420,15 @@ int ieee802154_disassociate_req(struct sk_buff *skb, struct genl_info *info) goto out; if (info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]) { - addr.addr_type = IEEE802154_ADDR_LONG; - nla_memcpy(addr.hwaddr, - info->attrs[IEEE802154_ATTR_DEST_HW_ADDR], - IEEE802154_ADDR_LEN); + addr.mode = IEEE802154_ADDR_LONG; + addr.extended_addr = nla_get_hwaddr( + info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]); } else { - addr.addr_type = IEEE802154_ADDR_SHORT; - addr.short_addr = nla_get_u16( + addr.mode = IEEE802154_ADDR_SHORT; + addr.short_addr = nla_get_shortaddr( info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]); } - addr.pan_id = le16_to_cpu(ieee802154_mlme_ops(dev)->get_pan_id(dev)); + addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); ret = ieee802154_mlme_ops(dev)->disassoc_req(dev, &addr, nla_get_u8(info->attrs[IEEE802154_ATTR_REASON])); @@ -429,7 +446,7 @@ out: int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info) { struct net_device *dev; - struct ieee802154_addr_sa addr; + struct ieee802154_addr addr; u8 channel, bcn_ord, sf_ord; u8 page; @@ -453,10 +470,11 @@ int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info) if (!ieee802154_mlme_ops(dev)->start_req) goto out; - addr.addr_type = IEEE802154_ADDR_SHORT; - addr.short_addr = nla_get_u16( + addr.mode = IEEE802154_ADDR_SHORT; + addr.short_addr = nla_get_shortaddr( info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]); - addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]); + addr.pan_id = nla_get_shortaddr( + info->attrs[IEEE802154_ATTR_COORD_PAN_ID]); channel = nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]); bcn_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_BCN_ORD]); @@ -471,7 +489,7 @@ int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info) page = 0; - if (addr.short_addr == IEEE802154_ADDR_BROADCAST) { + if (addr.short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST)) { ieee802154_nl_start_confirm(dev, IEEE802154_NO_SHORT_ADDRESS); dev_put(dev); return -EINVAL; diff --git a/net/ieee802154/reassembly.c b/net/ieee802154/reassembly.c index f08b37a24b1d..a2b9e4e533f8 100644 --- a/net/ieee802154/reassembly.c +++ b/net/ieee802154/reassembly.c @@ -36,8 +36,8 @@ static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *prev, struct net_device *dev); static unsigned int lowpan_hash_frag(__be16 tag, u16 d_size, - const struct ieee802154_addr_sa *saddr, - const struct ieee802154_addr_sa *daddr) + const struct ieee802154_addr *saddr, + const struct ieee802154_addr *daddr) { u32 c; @@ -65,8 +65,8 @@ static bool lowpan_frag_match(struct inet_frag_queue *q, void *a) fq = container_of(q, struct lowpan_frag_queue, q); return fq->tag == arg->tag && fq->d_size == arg->d_size && - ieee802154_addr_addr_equal(&fq->saddr, arg->src) && - ieee802154_addr_addr_equal(&fq->daddr, arg->dst); + ieee802154_addr_equal(&fq->saddr, arg->src) && + ieee802154_addr_equal(&fq->daddr, arg->dst); } static void lowpan_frag_init(struct inet_frag_queue *q, void *a) @@ -103,7 +103,8 @@ out: static inline struct lowpan_frag_queue * fq_find(struct net *net, const struct ieee802154_frag_info *frag_info, - const struct ieee802154_addr_sa *src, const struct ieee802154_addr_sa *dst) + const struct ieee802154_addr *src, + const struct ieee802154_addr *dst) { struct inet_frag_queue *q; struct lowpan_create_arg arg; @@ -346,8 +347,12 @@ int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type) struct lowpan_frag_queue *fq; struct net *net = dev_net(skb->dev); struct ieee802154_frag_info *frag_info = &mac_cb(skb)->frag_info; + struct ieee802154_addr source, dest; int err; + source = mac_cb(skb)->source; + dest = mac_cb(skb)->dest; + err = lowpan_get_frag_info(skb, frag_type, frag_info); if (err < 0) goto err; @@ -357,7 +362,7 @@ int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type) inet_frag_evictor(&net->ieee802154_lowpan.frags, &lowpan_frags, false); - fq = fq_find(net, frag_info, &mac_cb(skb)->sa, &mac_cb(skb)->da); + fq = fq_find(net, frag_info, &source, &dest); if (fq != NULL) { int ret; spin_lock(&fq->q.lock); diff --git a/net/ieee802154/reassembly.h b/net/ieee802154/reassembly.h index 895721ae71e1..74e4a7c98191 100644 --- a/net/ieee802154/reassembly.h +++ b/net/ieee802154/reassembly.h @@ -6,8 +6,8 @@ struct lowpan_create_arg { __be16 tag; u16 d_size; - const struct ieee802154_addr_sa *src; - const struct ieee802154_addr_sa *dst; + const struct ieee802154_addr *src; + const struct ieee802154_addr *dst; }; /* Equivalent of ipv4 struct ip @@ -17,16 +17,16 @@ struct lowpan_frag_queue { __be16 tag; u16 d_size; - struct ieee802154_addr_sa saddr; - struct ieee802154_addr_sa daddr; + struct ieee802154_addr saddr; + struct ieee802154_addr daddr; }; -static inline u32 ieee802154_addr_hash(const struct ieee802154_addr_sa *a) +static inline u32 ieee802154_addr_hash(const struct ieee802154_addr *a) { - switch (a->addr_type) { + switch (a->mode) { case IEEE802154_ADDR_LONG: - return (__force u32)((((u32 *)a->hwaddr))[0] ^ - ((u32 *)(a->hwaddr))[1]); + return (((__force u64)a->extended_addr) >> 32) ^ + (((__force u64)a->extended_addr) & 0xffffffff); case IEEE802154_ADDR_SHORT: return (__force u32)(a->short_addr); default: @@ -34,32 +34,6 @@ static inline u32 ieee802154_addr_hash(const struct ieee802154_addr_sa *a) } } -static inline bool -ieee802154_addr_addr_equal(const struct ieee802154_addr_sa *a1, - const struct ieee802154_addr_sa *a2) -{ - if (a1->pan_id != a2->pan_id) - return false; - - if (a1->addr_type != a2->addr_type) - return false; - - switch (a1->addr_type) { - case IEEE802154_ADDR_LONG: - if (memcmp(a1->hwaddr, a2->hwaddr, IEEE802154_ADDR_LEN)) - return false; - break; - case IEEE802154_ADDR_SHORT: - if (a1->short_addr != a2->short_addr) - return false; - break; - default: - return false; - } - - return true; -} - int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type); void lowpan_net_frag_exit(void); int lowpan_net_frag_init(void); diff --git a/net/mac802154/mac_cmd.c b/net/mac802154/mac_cmd.c index f551ef2cdf56..15bac3358889 100644 --- a/net/mac802154/mac_cmd.c +++ b/net/mac802154/mac_cmd.c @@ -34,16 +34,16 @@ #include "mac802154.h" static int mac802154_mlme_start_req(struct net_device *dev, - struct ieee802154_addr_sa *addr, + struct ieee802154_addr *addr, u8 channel, u8 page, u8 bcn_ord, u8 sf_ord, u8 pan_coord, u8 blx, u8 coord_realign) { - BUG_ON(addr->addr_type != IEEE802154_ADDR_SHORT); + BUG_ON(addr->mode != IEEE802154_ADDR_SHORT); - mac802154_dev_set_pan_id(dev, cpu_to_le16(addr->pan_id)); - mac802154_dev_set_short_addr(dev, cpu_to_le16(addr->short_addr)); + mac802154_dev_set_pan_id(dev, addr->pan_id); + mac802154_dev_set_short_addr(dev, addr->short_addr); mac802154_dev_set_ieee_addr(dev); mac802154_dev_set_page_channel(dev, page, channel); diff --git a/net/mac802154/wpan.c b/net/mac802154/wpan.c index 051ed46ffca9..b61426662867 100644 --- a/net/mac802154/wpan.c +++ b/net/mac802154/wpan.c @@ -251,18 +251,18 @@ static int mac802154_process_data(struct net_device *dev, struct sk_buff *skb) static int mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb) { - u16 span, sshort; + __le16 span, sshort; pr_debug("getting packet via slave interface %s\n", sdata->dev->name); spin_lock_bh(&sdata->mib_lock); - span = le16_to_cpu(sdata->pan_id); - sshort = le16_to_cpu(sdata->short_addr); + span = sdata->pan_id; + sshort = sdata->short_addr; - switch (mac_cb(skb)->da.addr_type) { + switch (mac_cb(skb)->dest.mode) { case IEEE802154_ADDR_NONE: - if (mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_NONE) + if (mac_cb(skb)->dest.mode != IEEE802154_ADDR_NONE) /* FIXME: check if we are PAN coordinator */ skb->pkt_type = PACKET_OTHERHOST; else @@ -270,23 +270,22 @@ mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb) skb->pkt_type = PACKET_HOST; break; case IEEE802154_ADDR_LONG: - if (mac_cb(skb)->da.pan_id != span && - mac_cb(skb)->da.pan_id != IEEE802154_PANID_BROADCAST) + if (mac_cb(skb)->dest.pan_id != span && + mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST)) skb->pkt_type = PACKET_OTHERHOST; - else if (!memcmp(mac_cb(skb)->da.hwaddr, sdata->dev->dev_addr, - IEEE802154_ADDR_LEN)) + else if (mac_cb(skb)->dest.extended_addr == sdata->extended_addr) skb->pkt_type = PACKET_HOST; else skb->pkt_type = PACKET_OTHERHOST; break; case IEEE802154_ADDR_SHORT: - if (mac_cb(skb)->da.pan_id != span && - mac_cb(skb)->da.pan_id != IEEE802154_PANID_BROADCAST) + if (mac_cb(skb)->dest.pan_id != span && + mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST)) skb->pkt_type = PACKET_OTHERHOST; - else if (mac_cb(skb)->da.short_addr == sshort) + else if (mac_cb(skb)->dest.short_addr == sshort) skb->pkt_type = PACKET_HOST; - else if (mac_cb(skb)->da.short_addr == - IEEE802154_ADDR_BROADCAST) + else if (mac_cb(skb)->dest.short_addr == + cpu_to_le16(IEEE802154_ADDR_BROADCAST)) skb->pkt_type = PACKET_BROADCAST; else skb->pkt_type = PACKET_OTHERHOST; @@ -332,8 +331,8 @@ static void mac802154_print_addr(const char *name, static int mac802154_parse_frame_start(struct sk_buff *skb) { - struct ieee802154_hdr hdr; int hlen; + struct ieee802154_hdr hdr; hlen = ieee802154_hdr_pull(skb, &hdr); if (hlen < 0) @@ -346,9 +345,6 @@ static int mac802154_parse_frame_start(struct sk_buff *skb) mac_cb(skb)->flags = hdr.fc.type; - ieee802154_addr_to_sa(&mac_cb(skb)->sa, &hdr.source); - ieee802154_addr_to_sa(&mac_cb(skb)->da, &hdr.dest); - if (hdr.fc.ack_request) mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ; if (hdr.fc.security_enabled) @@ -357,6 +353,9 @@ static int mac802154_parse_frame_start(struct sk_buff *skb) mac802154_print_addr("destination", &hdr.dest); mac802154_print_addr("source", &hdr.source); + mac_cb(skb)->source = hdr.source; + mac_cb(skb)->dest = hdr.dest; + if (hdr.fc.security_enabled) { u64 key; -- cgit v1.2.3-71-gd317 From a13061ec04e9168625427a591235b167d5499bc6 Mon Sep 17 00:00:00 2001 From: Phoebe Buckheister Date: Fri, 14 Mar 2014 21:24:03 +0100 Subject: 6lowpan: move lowpan frag_info out of 802.15.4 headers Fragmentation and reassembly information for 6lowpan is independent from the 802.15.4 stack and used only by the 6lowpan reassembly process. Move the ieee802154_frag_info struct to a private are, it needn't be in the 802.15.4 skb control block. Signed-off-by: Phoebe Buckheister Signed-off-by: David S. Miller --- include/net/ieee802154_netdev.h | 8 -------- net/ieee802154/reassembly.c | 27 ++++++++++++++++++--------- 2 files changed, 18 insertions(+), 17 deletions(-) (limited to 'include/net') diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h index 827e3e33c422..e1717cbf609b 100644 --- a/include/net/ieee802154_netdev.h +++ b/include/net/ieee802154_netdev.h @@ -187,13 +187,6 @@ static inline void ieee802154_addr_to_sa(struct ieee802154_addr_sa *sa, } } - -struct ieee802154_frag_info { - __be16 d_tag; - u16 d_size; - u8 d_offset; -}; - /* * A control block of skb passed between the ARPHRD_IEEE802154 device * and other stack parts. @@ -202,7 +195,6 @@ struct ieee802154_mac_cb { u8 lqi; u8 flags; u8 seq; - struct ieee802154_frag_info frag_info; struct ieee802154_addr source; struct ieee802154_addr dest; }; diff --git a/net/ieee802154/reassembly.c b/net/ieee802154/reassembly.c index a2b9e4e533f8..ef2d54372b13 100644 --- a/net/ieee802154/reassembly.c +++ b/net/ieee802154/reassembly.c @@ -30,6 +30,17 @@ #include "reassembly.h" +struct lowpan_frag_info { + __be16 d_tag; + u16 d_size; + u8 d_offset; +}; + +struct lowpan_frag_info *lowpan_cb(struct sk_buff *skb) +{ + return (struct lowpan_frag_info *)skb->cb; +} + static struct inet_frags lowpan_frags; static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, @@ -102,7 +113,7 @@ out: } static inline struct lowpan_frag_queue * -fq_find(struct net *net, const struct ieee802154_frag_info *frag_info, +fq_find(struct net *net, const struct lowpan_frag_info *frag_info, const struct ieee802154_addr *src, const struct ieee802154_addr *dst) { @@ -137,8 +148,8 @@ static int lowpan_frag_queue(struct lowpan_frag_queue *fq, if (fq->q.last_in & INET_FRAG_COMPLETE) goto err; - offset = mac_cb(skb)->frag_info.d_offset << 3; - end = mac_cb(skb)->frag_info.d_size; + offset = lowpan_cb(skb)->d_offset << 3; + end = lowpan_cb(skb)->d_size; /* Is this the final fragment? */ if (offset + skb->len == end) { @@ -164,15 +175,13 @@ static int lowpan_frag_queue(struct lowpan_frag_queue *fq, * this fragment, right? */ prev = fq->q.fragments_tail; - if (!prev || mac_cb(prev)->frag_info.d_offset < - mac_cb(skb)->frag_info.d_offset) { + if (!prev || lowpan_cb(prev)->d_offset < lowpan_cb(skb)->d_offset) { next = NULL; goto found; } prev = NULL; for (next = fq->q.fragments; next != NULL; next = next->next) { - if (mac_cb(next)->frag_info.d_offset >= - mac_cb(skb)->frag_info.d_offset) + if (lowpan_cb(next)->d_offset >= lowpan_cb(skb)->d_offset) break; /* bingo! */ prev = next; } @@ -319,7 +328,7 @@ out_oom: } static int lowpan_get_frag_info(struct sk_buff *skb, const u8 frag_type, - struct ieee802154_frag_info *frag_info) + struct lowpan_frag_info *frag_info) { bool fail; u8 pattern = 0, low = 0; @@ -346,7 +355,7 @@ int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type) { struct lowpan_frag_queue *fq; struct net *net = dev_net(skb->dev); - struct ieee802154_frag_info *frag_info = &mac_cb(skb)->frag_info; + struct lowpan_frag_info *frag_info = lowpan_cb(skb); struct ieee802154_addr source, dest; int err; -- cgit v1.2.3-71-gd317 From a0f995a561d90636120bce2e89157b537a174c92 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 13 Mar 2014 14:30:47 +0200 Subject: mac80211: add status_driver_data array to ieee80211_tx_info Drivers might want to have private data in addition to all other ieee80211_tx_info.status fields. The current ieee80211_tx_info.rate_driver_data overlaps with some of the non-rate data (e.g. ampdu_ack_len), so it might not be good enough. Since we already know how much free bytes remained, simply use this size to define (void *) array. While on it, change ack_signal type from int to the more explicit s32 type. Signed-off-by: Eliad Peller Signed-off-by: Johannes Berg --- include/net/mac80211.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 86faa413b37d..f6988fe8dfd2 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -697,11 +697,11 @@ struct ieee80211_tx_info { } control; struct { struct ieee80211_tx_rate rates[IEEE80211_TX_MAX_RATES]; - int ack_signal; + s32 ack_signal; u8 ampdu_ack_len; u8 ampdu_len; u8 antenna; - /* 21 bytes free */ + void *status_driver_data[21 / sizeof(void *)]; } status; struct { struct ieee80211_tx_rate driver_rates[ -- cgit v1.2.3-71-gd317 From 3afc2167f60a327a2c1e1e2600ef209a3c2b75b7 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 4 Mar 2014 16:50:13 +0200 Subject: cfg80211/mac80211: ignore signal if the frame was heard on wrong channel On 2.4Ghz band, the channels overlap since the delta between different channels is 5Mhz while the width of the receiver is 20Mhz (at least). This means that we can hear beacons or probe responses from adjacent channels. These frames will have a significant lower RSSI which will feed all kinds of logic with inaccurate data. An obvious example is the roaming algorithm that will think our AP is getting weak and will try to move to another AP. In order to avoid this, update the signal only if the frame has been heard on the same channel as the one advertised by the AP in its DS / HT IEs. We refrain from updating the values only if the AP is already in the BSS list so that we will still have a valid (but inaccurate) value if the AP was heard on an adjacent channel only. To achieve this, stop taking the channel from DS / HT IEs in mac80211. The DS / HT IEs is taken into account to discard the frame if it was received on a disabled channel. This can happen due to the same phenomenon: the frame is sent on channel 12, but heard on channel 11 while channel 12 can be disabled on certain devices. Since this check is done in cfg80211, stop even checking this in mac80211. Signed-off-by: Emmanuel Grumbach [remove unused rx_freq variable] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 16 ++++++++-------- net/mac80211/ibss.c | 12 ++---------- net/mac80211/mlme.c | 12 ++---------- net/wireless/scan.c | 28 +++++++++++++++++++--------- 4 files changed, 31 insertions(+), 37 deletions(-) (limited to 'include/net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ff3af16eba21..f3539a15c411 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3672,7 +3672,7 @@ void cfg80211_sched_scan_stopped(struct wiphy *wiphy); * cfg80211_inform_bss_width_frame - inform cfg80211 of a received BSS frame * * @wiphy: the wiphy reporting the BSS - * @channel: The channel the frame was received on + * @rx_channel: The channel the frame was received on * @scan_width: width of the control channel * @mgmt: the management frame (probe response or beacon) * @len: length of the management frame @@ -3687,18 +3687,18 @@ void cfg80211_sched_scan_stopped(struct wiphy *wiphy); */ struct cfg80211_bss * __must_check cfg80211_inform_bss_width_frame(struct wiphy *wiphy, - struct ieee80211_channel *channel, + struct ieee80211_channel *rx_channel, enum nl80211_bss_scan_width scan_width, struct ieee80211_mgmt *mgmt, size_t len, s32 signal, gfp_t gfp); static inline struct cfg80211_bss * __must_check cfg80211_inform_bss_frame(struct wiphy *wiphy, - struct ieee80211_channel *channel, + struct ieee80211_channel *rx_channel, struct ieee80211_mgmt *mgmt, size_t len, s32 signal, gfp_t gfp) { - return cfg80211_inform_bss_width_frame(wiphy, channel, + return cfg80211_inform_bss_width_frame(wiphy, rx_channel, NL80211_BSS_CHAN_WIDTH_20, mgmt, len, signal, gfp); } @@ -3707,7 +3707,7 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, * cfg80211_inform_bss - inform cfg80211 of a new BSS * * @wiphy: the wiphy reporting the BSS - * @channel: The channel the frame was received on + * @rx_channel: The channel the frame was received on * @scan_width: width of the control channel * @bssid: the BSSID of the BSS * @tsf: the TSF sent by the peer in the beacon/probe response (or 0) @@ -3726,7 +3726,7 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, */ struct cfg80211_bss * __must_check cfg80211_inform_bss_width(struct wiphy *wiphy, - struct ieee80211_channel *channel, + struct ieee80211_channel *rx_channel, enum nl80211_bss_scan_width scan_width, const u8 *bssid, u64 tsf, u16 capability, u16 beacon_interval, const u8 *ie, size_t ielen, @@ -3734,12 +3734,12 @@ cfg80211_inform_bss_width(struct wiphy *wiphy, static inline struct cfg80211_bss * __must_check cfg80211_inform_bss(struct wiphy *wiphy, - struct ieee80211_channel *channel, + struct ieee80211_channel *rx_channel, const u8 *bssid, u64 tsf, u16 capability, u16 beacon_interval, const u8 *ie, size_t ielen, s32 signal, gfp_t gfp) { - return cfg80211_inform_bss_width(wiphy, channel, + return cfg80211_inform_bss_width(wiphy, rx_channel, NL80211_BSS_CHAN_WIDTH_20, bssid, tsf, capability, beacon_interval, ie, ielen, signal, diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index e458ca0dffec..06d28787945b 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -991,7 +991,6 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, struct ieee802_11_elems *elems) { struct ieee80211_local *local = sdata->local; - int freq; struct cfg80211_bss *cbss; struct ieee80211_bss *bss; struct sta_info *sta; @@ -1003,15 +1002,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band]; bool rates_updated = false; - if (elems->ds_params) - freq = ieee80211_channel_to_frequency(elems->ds_params[0], - band); - else - freq = rx_status->freq; - - channel = ieee80211_get_channel(local->hw.wiphy, freq); - - if (!channel || channel->flags & IEEE80211_CHAN_DISABLED) + channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq); + if (!channel) return; if (sdata->vif.type == NL80211_IFTYPE_ADHOC && diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 6d24e6c8f320..bbc2175e4bfe 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2760,21 +2760,13 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, struct ieee802_11_elems *elems) { struct ieee80211_local *local = sdata->local; - int freq; struct ieee80211_bss *bss; struct ieee80211_channel *channel; sdata_assert_lock(sdata); - if (elems->ds_params) - freq = ieee80211_channel_to_frequency(elems->ds_params[0], - rx_status->band); - else - freq = rx_status->freq; - - channel = ieee80211_get_channel(local->hw.wiphy, freq); - - if (!channel || channel->flags & IEEE80211_CHAN_DISABLED) + channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq); + if (!channel) return; bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems, diff --git a/net/wireless/scan.c b/net/wireless/scan.c index d1ed4aebbbb7..7d9f5264a63c 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -680,7 +680,8 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev, /* Returned bss is reference counted and must be cleaned up appropriately. */ static struct cfg80211_internal_bss * cfg80211_bss_update(struct cfg80211_registered_device *dev, - struct cfg80211_internal_bss *tmp) + struct cfg80211_internal_bss *tmp, + bool signal_valid) { struct cfg80211_internal_bss *found = NULL; @@ -765,7 +766,12 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, } found->pub.beacon_interval = tmp->pub.beacon_interval; - found->pub.signal = tmp->pub.signal; + /* + * don't update the signal if beacon was heard on + * adjacent channel. + */ + if (signal_valid) + found->pub.signal = tmp->pub.signal; found->pub.capability = tmp->pub.capability; found->ts = tmp->ts; } else { @@ -869,13 +875,14 @@ cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen, /* Returned bss is reference counted and must be cleaned up appropriately. */ struct cfg80211_bss* cfg80211_inform_bss_width(struct wiphy *wiphy, - struct ieee80211_channel *channel, + struct ieee80211_channel *rx_channel, enum nl80211_bss_scan_width scan_width, const u8 *bssid, u64 tsf, u16 capability, u16 beacon_interval, const u8 *ie, size_t ielen, s32 signal, gfp_t gfp) { struct cfg80211_bss_ies *ies; + struct ieee80211_channel *channel; struct cfg80211_internal_bss tmp = {}, *res; if (WARN_ON(!wiphy)) @@ -885,7 +892,7 @@ cfg80211_inform_bss_width(struct wiphy *wiphy, (signal < 0 || signal > 100))) return NULL; - channel = cfg80211_get_bss_channel(wiphy, ie, ielen, channel); + channel = cfg80211_get_bss_channel(wiphy, ie, ielen, rx_channel); if (!channel) return NULL; @@ -913,7 +920,8 @@ cfg80211_inform_bss_width(struct wiphy *wiphy, rcu_assign_pointer(tmp.pub.beacon_ies, ies); rcu_assign_pointer(tmp.pub.ies, ies); - res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp); + res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp, + rx_channel == channel); if (!res) return NULL; @@ -929,20 +937,21 @@ EXPORT_SYMBOL(cfg80211_inform_bss_width); /* Returned bss is reference counted and must be cleaned up appropriately. */ struct cfg80211_bss * cfg80211_inform_bss_width_frame(struct wiphy *wiphy, - struct ieee80211_channel *channel, + struct ieee80211_channel *rx_channel, enum nl80211_bss_scan_width scan_width, struct ieee80211_mgmt *mgmt, size_t len, s32 signal, gfp_t gfp) { struct cfg80211_internal_bss tmp = {}, *res; struct cfg80211_bss_ies *ies; + struct ieee80211_channel *channel; size_t ielen = len - offsetof(struct ieee80211_mgmt, u.probe_resp.variable); BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) != offsetof(struct ieee80211_mgmt, u.beacon.variable)); - trace_cfg80211_inform_bss_width_frame(wiphy, channel, scan_width, mgmt, + trace_cfg80211_inform_bss_width_frame(wiphy, rx_channel, scan_width, mgmt, len, signal); if (WARN_ON(!mgmt)) @@ -959,7 +968,7 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy, return NULL; channel = cfg80211_get_bss_channel(wiphy, mgmt->u.beacon.variable, - ielen, channel); + ielen, rx_channel); if (!channel) return NULL; @@ -983,7 +992,8 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy, tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); - res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp); + res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp, + rx_channel == channel); if (!res) return NULL; -- cgit v1.2.3-71-gd317 From fb378c231daf93c7c806848e8247781322867ece Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 4 Mar 2014 10:35:25 +0200 Subject: mac80211: set beamforming bit in radiotap Add a bit in rx_status.vht_flags to let the low level driver notify mac80211 about a beamformed packet. Propagate this to the radiotap header. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- include/net/mac80211.h | 2 ++ net/mac80211/rx.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'include/net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index f6988fe8dfd2..2de7ff42ff3a 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -877,11 +877,13 @@ enum mac80211_rx_flags { * @RX_VHT_FLAG_80MHZ: 80 MHz was used * @RX_VHT_FLAG_80P80MHZ: 80+80 MHz was used * @RX_VHT_FLAG_160MHZ: 160 MHz was used + * @RX_VHT_FLAG_BF: packet was beamformed */ enum mac80211_rx_vht_flags { RX_VHT_FLAG_80MHZ = BIT(0), RX_VHT_FLAG_80P80MHZ = BIT(1), RX_VHT_FLAG_160MHZ = BIT(2), + RX_VHT_FLAG_BF = BIT(3), }; /** diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index f3719e669893..ac8322818b28 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -333,6 +333,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, /* in VHT, STBC is binary */ if (status->flag & RX_FLAG_STBC_MASK) *pos |= IEEE80211_RADIOTAP_VHT_FLAG_STBC; + if (status->vht_flag & RX_VHT_FLAG_BF) + *pos |= IEEE80211_RADIOTAP_VHT_FLAG_BEAMFORMED; pos++; /* bandwidth */ if (status->vht_flag & RX_VHT_FLAG_80MHZ) -- cgit v1.2.3-71-gd317 From 39adbffe4b16285c54016d3e64471396354ae49f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 20 Mar 2014 08:18:14 +0200 Subject: Bluetooth: Fix passkey endianess in user_confirm and notify_passkey The passkey_notify and user_confirm functions in mgmt.c were expecting different endianess for the passkey, leading to a big endian bug and sparse warning in recently added SMP code. This patch converts both functions to expect host endianess and do the conversion to little endian only when assigning to the mgmt event struct. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_event.c | 4 ++-- net/bluetooth/mgmt.c | 4 ++-- net/bluetooth/smp.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b8cc39a4a9a5..afbea388eda1 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1236,7 +1236,7 @@ void mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, void mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status); int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, __le32 value, + u8 link_type, u8 addr_type, u32 value, u8 confirm_hint); int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index a6a3d32553c5..1e386edc338f 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3459,8 +3459,8 @@ static void hci_user_confirm_request_evt(struct hci_dev *hdev, } confirm: - mgmt_user_confirm_request(hdev, &ev->bdaddr, ACL_LINK, 0, ev->passkey, - confirm_hint); + mgmt_user_confirm_request(hdev, &ev->bdaddr, ACL_LINK, 0, + le32_to_cpu(ev->passkey), confirm_hint); unlock: hci_dev_unlock(hdev); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 96670f581bb0..739887c6b286 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5338,7 +5338,7 @@ void mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, } int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, __le32 value, + u8 link_type, u8 addr_type, u32 value, u8 confirm_hint) { struct mgmt_ev_user_confirm_request ev; @@ -5348,7 +5348,7 @@ int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr, bacpy(&ev.addr.bdaddr, bdaddr); ev.addr.type = link_to_bdaddr(link_type, addr_type); ev.confirm_hint = confirm_hint; - ev.value = value; + ev.value = cpu_to_le32(value); return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, hdev, &ev, sizeof(ev), NULL); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 2a7ee7f6cd8b..13919ff82e05 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -425,7 +425,7 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, else ret = mgmt_user_passkey_notify(hcon->hdev, &hcon->dst, hcon->type, hcon->dst_type, - cpu_to_le32(passkey), 0); + passkey, 0); hci_dev_unlock(hcon->hdev); -- cgit v1.2.3-71-gd317 From 533553f8738184bcf957d97fed9eb4d5f023e4a7 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 21 Mar 2014 12:18:10 -0700 Subject: Bluetooth: Track current configured LE scan type parameter The LE scan type paramter defines if active scanning or passive scanning is in use. Track the currently set value so it can be used for decision making from other pieces in the core. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_event.c | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index afbea388eda1..5f8bc05694ac 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -189,6 +189,7 @@ struct hci_dev { __u16 page_scan_window; __u8 page_scan_type; __u8 le_adv_channel_map; + __u8 le_scan_type; __u16 le_scan_interval; __u16 le_scan_window; __u16 le_conn_min_interval; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 1e386edc338f..9ee081b9c064 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -199,6 +199,8 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) memset(hdev->scan_rsp_data, 0, sizeof(hdev->scan_rsp_data)); hdev->scan_rsp_data_len = 0; + hdev->le_scan_type = LE_SCAN_PASSIVE; + hdev->ssp_debug_mode = 0; } @@ -997,6 +999,25 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_unlock(hdev); } +static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_cp_le_set_scan_param *cp; + __u8 status = *((__u8 *) skb->data); + + BT_DBG("%s status 0x%2.2x", hdev->name, status); + + cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_SCAN_PARAM); + if (!cp) + return; + + hci_dev_lock(hdev); + + if (!status) + hdev->le_scan_type = cp->type; + + hci_dev_unlock(hdev); +} + static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) { @@ -2488,6 +2509,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_le_set_adv_enable(hdev, skb); break; + case HCI_OP_LE_SET_SCAN_PARAM: + hci_cc_le_set_scan_param(hdev, skb); + break; + case HCI_OP_LE_SET_SCAN_ENABLE: hci_cc_le_set_scan_enable(hdev, skb); break; -- cgit v1.2.3-71-gd317 From 99f0b958b194f7d88973f1c2190d207e0a2c7e79 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 23 Mar 2014 19:51:36 -0700 Subject: net: optimize csum_replace2() When changing one 16bit value by another in IP header, we can adjust the IP checksum by doing a simple operation described in RFC 1624, as reminded by David. csum_partial() is a complex function on x86_64, not really suited for small number of checksummed bytes. I spotted csum_partial() being in the top 20 most consuming functions (more than 1 %) in a GRO workload, which was rather unexpected. The caller was inet_gro_complete() doing a csum_replace2() when building the new IP header for the GRO packet. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/checksum.h | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'include/net') diff --git a/include/net/checksum.h b/include/net/checksum.h index 37a0e24adbe7..a28f4e0f6251 100644 --- a/include/net/checksum.h +++ b/include/net/checksum.h @@ -69,6 +69,19 @@ static inline __wsum csum_sub(__wsum csum, __wsum addend) return csum_add(csum, ~addend); } +static inline __sum16 csum16_add(__sum16 csum, __be16 addend) +{ + u16 res = (__force u16)csum; + + res += (__force u16)addend; + return (__force __sum16)(res + (res < (__force u16)addend)); +} + +static inline __sum16 csum16_sub(__sum16 csum, __be16 addend) +{ + return csum16_add(csum, ~addend); +} + static inline __wsum csum_block_add(__wsum csum, __wsum csum2, int offset) { @@ -112,9 +125,15 @@ static inline void csum_replace4(__sum16 *sum, __be32 from, __be32 to) *sum = csum_fold(csum_partial(diff, sizeof(diff), ~csum_unfold(*sum))); } -static inline void csum_replace2(__sum16 *sum, __be16 from, __be16 to) +/* Implements RFC 1624 (Incremental Internet Checksum) + * 3. Discussion states : + * HC' = ~(~HC + ~m + m') + * m : old value of a 16bit field + * m' : new value of a 16bit field + */ +static inline void csum_replace2(__sum16 *sum, __be16 old, __be16 new) { - csum_replace4(sum, (__force __be32)from, (__force __be32)to); + *sum = ~csum16_add(csum16_sub(~(*sum), old), new); } struct sk_buff; -- cgit v1.2.3-71-gd317 From 0b8c7f6f2a26ed2dee24881299fc69f554596dbb Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Fri, 21 Mar 2014 11:33:10 +0800 Subject: ipv4: remove ip_rt_dump from route.c ip_rt_dump do nothing after IPv4 route caches removal, so we can remove it. Signed-off-by: Li RongQing Signed-off-by: David S. Miller --- include/net/route.h | 1 - net/ipv4/fib_frontend.c | 2 +- net/ipv4/route.c | 5 ----- 3 files changed, 1 insertion(+), 7 deletions(-) (limited to 'include/net') diff --git a/include/net/route.h b/include/net/route.h index 9d1f423d5944..b17cf28f996e 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -191,7 +191,6 @@ unsigned int inet_dev_addr_type(struct net *net, const struct net_device *dev, void ip_rt_multicast_event(struct in_device *); int ip_rt_ioctl(struct net *, unsigned int cmd, void __user *arg); void ip_rt_get_source(u8 *src, struct sk_buff *skb, struct rtable *rt); -int ip_rt_dump(struct sk_buff *skb, struct netlink_callback *cb); struct in_ifaddr; void fib_add_ifaddr(struct in_ifaddr *); diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index c7539e22868b..1a629f870274 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -659,7 +659,7 @@ static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) if (nlmsg_len(cb->nlh) >= sizeof(struct rtmsg) && ((struct rtmsg *) nlmsg_data(cb->nlh))->rtm_flags & RTM_F_CLONED) - return ip_rt_dump(skb, cb); + return skb->len; s_h = cb->args[0]; s_e = cb->args[1]; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 57527ee1867f..1be9e990514d 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2468,11 +2468,6 @@ errout_free: goto errout; } -int ip_rt_dump(struct sk_buff *skb, struct netlink_callback *cb) -{ - return skb->len; -} - void ip_rt_multicast_event(struct in_device *in_dev) { rt_cache_flush(dev_net(in_dev->dev)); -- cgit v1.2.3-71-gd317 From 61b905da33ae25edb6b9d2a5de21e34c3a77efe3 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Mon, 24 Mar 2014 15:34:47 -0700 Subject: net: Rename skb->rxhash to skb->hash The packet hash can be considered a property of the packet, not just on RX path. This patch changes name of rxhash and l4_rxhash skbuff fields to be hash and l4_hash respectively. This includes changing uses of the field in the code which don't call the access functions. Signed-off-by: Tom Herbert Signed-off-by: Eric Dumazet Cc: Mahesh Bandewar Signed-off-by: David S. Miller --- arch/arm/net/bpf_jit_32.c | 4 ++-- arch/powerpc/net/bpf_jit_comp.c | 4 ++-- arch/s390/net/bpf_jit_comp.c | 8 ++++---- arch/sparc/net/bpf_jit_comp.c | 2 +- arch/x86/net/bpf_jit_comp.c | 8 ++++---- include/linux/skbuff.h | 28 ++++++++++++++-------------- include/net/sock.h | 4 ++-- include/trace/events/net.h | 12 ++++++------ net/core/dev.c | 13 +++++++------ net/core/filter.c | 2 +- net/core/flow_dissector.c | 10 +++++----- net/packet/af_packet.c | 3 +-- 12 files changed, 49 insertions(+), 49 deletions(-) (limited to 'include/net') diff --git a/arch/arm/net/bpf_jit_32.c b/arch/arm/net/bpf_jit_32.c index 271b5e971568..7ddb9c83cdfc 100644 --- a/arch/arm/net/bpf_jit_32.c +++ b/arch/arm/net/bpf_jit_32.c @@ -825,8 +825,8 @@ b_epilogue: break; case BPF_S_ANC_RXHASH: ctx->seen |= SEEN_SKB; - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, rxhash) != 4); - off = offsetof(struct sk_buff, rxhash); + BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4); + off = offsetof(struct sk_buff, hash); emit(ARM_LDR_I(r_A, r_skb, off), ctx); break; case BPF_S_ANC_VLAN_TAG: diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c index 555034f8505e..4afad6c17d50 100644 --- a/arch/powerpc/net/bpf_jit_comp.c +++ b/arch/powerpc/net/bpf_jit_comp.c @@ -390,9 +390,9 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image, mark)); break; case BPF_S_ANC_RXHASH: - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, rxhash) != 4); + BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4); PPC_LWZ_OFFS(r_A, r_skb, offsetof(struct sk_buff, - rxhash)); + hash)); break; case BPF_S_ANC_VLAN_TAG: case BPF_S_ANC_VLAN_TAG_PRESENT: diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c index 708d60e40066..153f8f2cfd56 100644 --- a/arch/s390/net/bpf_jit_comp.c +++ b/arch/s390/net/bpf_jit_comp.c @@ -737,10 +737,10 @@ call_fn: /* lg %r1,(%r13) */ /* icm %r5,3,(%r1) */ EMIT4_DISP(0xbf531000, offsetof(struct net_device, type)); break; - case BPF_S_ANC_RXHASH: /* A = skb->rxhash */ - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, rxhash) != 4); - /* l %r5,(%r2) */ - EMIT4_DISP(0x58502000, offsetof(struct sk_buff, rxhash)); + case BPF_S_ANC_RXHASH: /* A = skb->hash */ + BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4); + /* l %r5,(%r2) */ + EMIT4_DISP(0x58502000, offsetof(struct sk_buff, hash)); break; case BPF_S_ANC_VLAN_TAG: case BPF_S_ANC_VLAN_TAG_PRESENT: diff --git a/arch/sparc/net/bpf_jit_comp.c b/arch/sparc/net/bpf_jit_comp.c index 01fe9946d388..d96d2a7c78ee 100644 --- a/arch/sparc/net/bpf_jit_comp.c +++ b/arch/sparc/net/bpf_jit_comp.c @@ -618,7 +618,7 @@ void bpf_jit_compile(struct sk_filter *fp) emit_load16(r_A, struct net_device, type, r_A); break; case BPF_S_ANC_RXHASH: - emit_skb_load32(rxhash, r_A); + emit_skb_load32(hash, r_A); break; case BPF_S_ANC_VLAN_TAG: case BPF_S_ANC_VLAN_TAG_PRESENT: diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index 4ed75dd81d05..293c57b74edc 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -553,13 +553,13 @@ void bpf_jit_compile(struct sk_filter *fp) } break; case BPF_S_ANC_RXHASH: - BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, rxhash) != 4); - if (is_imm8(offsetof(struct sk_buff, rxhash))) { + BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4); + if (is_imm8(offsetof(struct sk_buff, hash))) { /* mov off8(%rdi),%eax */ - EMIT3(0x8b, 0x47, offsetof(struct sk_buff, rxhash)); + EMIT3(0x8b, 0x47, offsetof(struct sk_buff, hash)); } else { EMIT2(0x8b, 0x87); - EMIT(offsetof(struct sk_buff, rxhash), 4); + EMIT(offsetof(struct sk_buff, hash), 4); } break; case BPF_S_ANC_QUEUE: diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 03db95ab8a8c..aa2c22cb8158 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -444,11 +444,11 @@ static inline u32 skb_mstamp_us_delta(const struct skb_mstamp *t1, * @skb_iif: ifindex of device we arrived on * @tc_index: Traffic control index * @tc_verd: traffic control verdict - * @rxhash: the packet hash computed on receive + * @hash: the packet hash * @queue_mapping: Queue mapping for multiqueue devices * @ndisc_nodetype: router type (from link layer) * @ooo_okay: allow the mapping of a socket to a queue to be changed - * @l4_rxhash: indicate rxhash is a canonical 4-tuple hash over transport + * @l4_hash: indicate hash is a canonical 4-tuple hash over transport * ports. * @wifi_acked_valid: wifi_acked was set * @wifi_acked: whether frame was acked on wifi or not @@ -537,7 +537,7 @@ struct sk_buff { int skb_iif; - __u32 rxhash; + __u32 hash; __be16 vlan_proto; __u16 vlan_tci; @@ -556,7 +556,7 @@ struct sk_buff { #endif __u8 pfmemalloc:1; __u8 ooo_okay:1; - __u8 l4_rxhash:1; + __u8 l4_hash:1; __u8 wifi_acked_valid:1; __u8 wifi_acked:1; __u8 no_fcs:1; @@ -815,40 +815,40 @@ enum pkt_hash_types { static inline void skb_set_hash(struct sk_buff *skb, __u32 hash, enum pkt_hash_types type) { - skb->l4_rxhash = (type == PKT_HASH_TYPE_L4); - skb->rxhash = hash; + skb->l4_hash = (type == PKT_HASH_TYPE_L4); + skb->hash = hash; } void __skb_get_hash(struct sk_buff *skb); static inline __u32 skb_get_hash(struct sk_buff *skb) { - if (!skb->l4_rxhash) + if (!skb->l4_hash) __skb_get_hash(skb); - return skb->rxhash; + return skb->hash; } static inline __u32 skb_get_hash_raw(const struct sk_buff *skb) { - return skb->rxhash; + return skb->hash; } static inline void skb_clear_hash(struct sk_buff *skb) { - skb->rxhash = 0; - skb->l4_rxhash = 0; + skb->hash = 0; + skb->l4_hash = 0; } static inline void skb_clear_hash_if_not_l4(struct sk_buff *skb) { - if (!skb->l4_rxhash) + if (!skb->l4_hash) skb_clear_hash(skb); } static inline void skb_copy_hash(struct sk_buff *to, const struct sk_buff *from) { - to->rxhash = from->rxhash; - to->l4_rxhash = from->l4_rxhash; + to->hash = from->hash; + to->l4_hash = from->l4_hash; }; #ifdef NET_SKBUFF_DATA_USES_OFFSET diff --git a/include/net/sock.h b/include/net/sock.h index 625e65b12366..8d7c431a0660 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -862,9 +862,9 @@ static inline void sock_rps_save_rxhash(struct sock *sk, const struct sk_buff *skb) { #ifdef CONFIG_RPS - if (unlikely(sk->sk_rxhash != skb->rxhash)) { + if (unlikely(sk->sk_rxhash != skb->hash)) { sock_rps_reset_flow(sk); - sk->sk_rxhash = skb->rxhash; + sk->sk_rxhash = skb->hash; } #endif } diff --git a/include/trace/events/net.h b/include/trace/events/net.h index a34f27b2e394..1de256b35807 100644 --- a/include/trace/events/net.h +++ b/include/trace/events/net.h @@ -153,8 +153,8 @@ DECLARE_EVENT_CLASS(net_dev_rx_verbose_template, __field( u16, vlan_tci ) __field( u16, protocol ) __field( u8, ip_summed ) - __field( u32, rxhash ) - __field( bool, l4_rxhash ) + __field( u32, hash ) + __field( bool, l4_hash ) __field( unsigned int, len ) __field( unsigned int, data_len ) __field( unsigned int, truesize ) @@ -179,8 +179,8 @@ DECLARE_EVENT_CLASS(net_dev_rx_verbose_template, __entry->vlan_tci = vlan_tx_tag_get(skb); __entry->protocol = ntohs(skb->protocol); __entry->ip_summed = skb->ip_summed; - __entry->rxhash = skb->rxhash; - __entry->l4_rxhash = skb->l4_rxhash; + __entry->hash = skb->hash; + __entry->l4_hash = skb->l4_hash; __entry->len = skb->len; __entry->data_len = skb->data_len; __entry->truesize = skb->truesize; @@ -191,11 +191,11 @@ DECLARE_EVENT_CLASS(net_dev_rx_verbose_template, __entry->gso_type = skb_shinfo(skb)->gso_type; ), - TP_printk("dev=%s napi_id=%#x queue_mapping=%u skbaddr=%p vlan_tagged=%d vlan_proto=0x%04x vlan_tci=0x%04x protocol=0x%04x ip_summed=%d rxhash=0x%08x l4_rxhash=%d len=%u data_len=%u truesize=%u mac_header_valid=%d mac_header=%d nr_frags=%d gso_size=%d gso_type=%#x", + TP_printk("dev=%s napi_id=%#x queue_mapping=%u skbaddr=%p vlan_tagged=%d vlan_proto=0x%04x vlan_tci=0x%04x protocol=0x%04x ip_summed=%d hash=0x%08x l4_hash=%d len=%u data_len=%u truesize=%u mac_header_valid=%d mac_header=%d nr_frags=%d gso_size=%d gso_type=%#x", __get_str(name), __entry->napi_id, __entry->queue_mapping, __entry->skbaddr, __entry->vlan_tagged, __entry->vlan_proto, __entry->vlan_tci, __entry->protocol, __entry->ip_summed, - __entry->rxhash, __entry->l4_rxhash, __entry->len, + __entry->hash, __entry->l4_hash, __entry->len, __entry->data_len, __entry->truesize, __entry->mac_header_valid, __entry->mac_header, __entry->nr_frags, __entry->gso_size, __entry->gso_type) diff --git a/net/core/dev.c b/net/core/dev.c index 55f8e64c03a2..48dd323d5918 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2952,7 +2952,7 @@ set_rps_cpu(struct net_device *dev, struct sk_buff *skb, flow_table = rcu_dereference(rxqueue->rps_flow_table); if (!flow_table) goto out; - flow_id = skb->rxhash & flow_table->mask; + flow_id = skb_get_hash(skb) & flow_table->mask; rc = dev->netdev_ops->ndo_rx_flow_steer(dev, skb, rxq_index, flow_id); if (rc < 0) @@ -2986,6 +2986,7 @@ static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb, struct rps_sock_flow_table *sock_flow_table; int cpu = -1; u16 tcpu; + u32 hash; if (skb_rx_queue_recorded(skb)) { u16 index = skb_get_rx_queue(skb); @@ -3014,7 +3015,8 @@ static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb, } skb_reset_network_header(skb); - if (!skb_get_hash(skb)) + hash = skb_get_hash(skb); + if (!hash) goto done; flow_table = rcu_dereference(rxqueue->rps_flow_table); @@ -3023,11 +3025,10 @@ static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb, u16 next_cpu; struct rps_dev_flow *rflow; - rflow = &flow_table->flows[skb->rxhash & flow_table->mask]; + rflow = &flow_table->flows[hash & flow_table->mask]; tcpu = rflow->cpu; - next_cpu = sock_flow_table->ents[skb->rxhash & - sock_flow_table->mask]; + next_cpu = sock_flow_table->ents[hash & sock_flow_table->mask]; /* * If the desired CPU (where last recvmsg was done) is @@ -3056,7 +3057,7 @@ static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb, } if (map) { - tcpu = map->cpus[((u64) skb->rxhash * map->len) >> 32]; + tcpu = map->cpus[((u64) hash * map->len) >> 32]; if (cpu_online(tcpu)) { cpu = tcpu; diff --git a/net/core/filter.c b/net/core/filter.c index ad30d626a5bd..65b75966e206 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -336,7 +336,7 @@ load_b: A = skb->dev->type; continue; case BPF_S_ANC_RXHASH: - A = skb->rxhash; + A = skb->hash; continue; case BPF_S_ANC_CPU: A = raw_smp_processor_id(); diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 80201bf69d59..107ed12a5323 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -203,8 +203,8 @@ static __always_inline u32 __flow_hash_1word(u32 a) /* * __skb_get_hash: calculate a flow hash based on src/dst addresses - * and src/dst port numbers. Sets rxhash in skb to non-zero hash value - * on success, zero indicates no valid hash. Also, sets l4_rxhash in skb + * and src/dst port numbers. Sets hash in skb to non-zero hash value + * on success, zero indicates no valid hash. Also, sets l4_hash in skb * if hash is a canonical 4-tuple hash over transport ports. */ void __skb_get_hash(struct sk_buff *skb) @@ -216,7 +216,7 @@ void __skb_get_hash(struct sk_buff *skb) return; if (keys.ports) - skb->l4_rxhash = 1; + skb->l4_hash = 1; /* get a consistent hash (same value on both flow directions) */ if (((__force u32)keys.dst < (__force u32)keys.src) || @@ -232,7 +232,7 @@ void __skb_get_hash(struct sk_buff *skb) if (!hash) hash = 1; - skb->rxhash = hash; + skb->hash = hash; } EXPORT_SYMBOL(__skb_get_hash); @@ -344,7 +344,7 @@ static inline int get_xps_queue(struct net_device *dev, struct sk_buff *skb) hash = skb->sk->sk_hash; else hash = (__force u16) skb->protocol ^ - skb->rxhash; + skb->hash; hash = __flow_hash_1word(hash); queue_index = map->queues[ ((u64)hash * map->len) >> 32]; diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 292304404fda..097a354ec8cd 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1277,7 +1277,7 @@ static unsigned int fanout_demux_hash(struct packet_fanout *f, struct sk_buff *skb, unsigned int num) { - return reciprocal_scale(skb->rxhash, num); + return reciprocal_scale(skb_get_hash(skb), num); } static unsigned int fanout_demux_lb(struct packet_fanout *f, @@ -1362,7 +1362,6 @@ static int packet_rcv_fanout(struct sk_buff *skb, struct net_device *dev, if (!skb) return 0; } - skb_get_hash(skb); idx = fanout_demux_hash(f, skb, num); break; case PACKET_FANOUT_LB: -- cgit v1.2.3-71-gd317 From e5fd387ad5b30ca3971fbccb0735c843cdebf967 Mon Sep 17 00:00:00 2001 From: Michal Kubeček Date: Thu, 27 Mar 2014 13:04:08 +0100 Subject: ipv6: do not overwrite inetpeer metrics prematurely If an IPv6 host route with metrics exists, an attempt to add a new route for the same target with different metrics fails but rewrites the metrics anyway: 12sp0:~ # ip route add fec0::1 dev eth0 rto_min 1000 12sp0:~ # ip -6 route show fe80::/64 dev eth0 proto kernel metric 256 fec0::1 dev eth0 metric 1024 rto_min lock 1s 12sp0:~ # ip route add fec0::1 dev eth0 rto_min 1500 RTNETLINK answers: File exists 12sp0:~ # ip -6 route show fe80::/64 dev eth0 proto kernel metric 256 fec0::1 dev eth0 metric 1024 rto_min lock 1.5s This is caused by all IPv6 host routes using the metrics in their inetpeer (or the shared default). This also holds for the new route created in ip6_route_add() which shares the metrics with the already existing route and thus ip6_route_add() rewrites the metrics even if the new route ends up not being used at all. Another problem is that old metrics in inetpeer can reappear unexpectedly for a new route, e.g. 12sp0:~ # ip route add fec0::1 dev eth0 rto_min 1000 12sp0:~ # ip route del fec0::1 12sp0:~ # ip route add fec0::1 dev eth0 12sp0:~ # ip route change fec0::1 dev eth0 hoplimit 10 12sp0:~ # ip -6 route show fe80::/64 dev eth0 proto kernel metric 256 fec0::1 dev eth0 metric 1024 hoplimit 10 rto_min lock 1s Resolve the first problem by moving the setting of metrics down into fib6_add_rt2node() to the point we are sure we are inserting the new route into the tree. Second problem is addressed by introducing new flag DST_METRICS_FORCE_OVERWRITE which is set for a new host route in ip6_route_add() and makes ipv6_cow_metrics() always overwrite the metrics in inetpeer (even if they are not "new"); it is reset after that. v5: use a flag in _metrics member rather than one in flags v4: fix a typo making a condition always true (thanks to Hannes Frederic Sowa) v3: rewritten based on David Miller's idea to move setting the metrics (and allocation in non-host case) down to the point we already know the route is to be inserted. Also rebased to net-next as it is quite late in the cycle. Signed-off-by: Michal Kubecek Acked-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/net/dst.h | 11 +++++++++-- include/net/ip6_fib.h | 3 ++- net/ipv6/ip6_fib.c | 47 ++++++++++++++++++++++++++++++++++++++++++++--- net/ipv6/route.c | 44 +++++++++++--------------------------------- 4 files changed, 66 insertions(+), 39 deletions(-) (limited to 'include/net') diff --git a/include/net/dst.h b/include/net/dst.h index e01a826f2a9c..46ed958e0c6e 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -108,9 +108,11 @@ struct dst_entry { u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old); extern const u32 dst_default_metrics[]; -#define DST_METRICS_READ_ONLY 0x1UL +#define DST_METRICS_READ_ONLY 0x1UL +#define DST_METRICS_FORCE_OVERWRITE 0x2UL +#define DST_METRICS_FLAGS 0x3UL #define __DST_METRICS_PTR(Y) \ - ((u32 *)((Y) & ~DST_METRICS_READ_ONLY)) + ((u32 *)((Y) & ~DST_METRICS_FLAGS)) #define DST_METRICS_PTR(X) __DST_METRICS_PTR((X)->_metrics) static inline bool dst_metrics_read_only(const struct dst_entry *dst) @@ -118,6 +120,11 @@ static inline bool dst_metrics_read_only(const struct dst_entry *dst) return dst->_metrics & DST_METRICS_READ_ONLY; } +static inline void dst_metrics_set_force_overwrite(struct dst_entry *dst) +{ + dst->_metrics |= DST_METRICS_FORCE_OVERWRITE; +} + void __dst_destroy_metrics_generic(struct dst_entry *dst, unsigned long old); static inline void dst_destroy_metrics_generic(struct dst_entry *dst) diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index aca0c2709fd6..9bcb220bd4ad 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -284,7 +284,8 @@ struct fib6_node *fib6_locate(struct fib6_node *root, void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *arg), void *arg); -int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info); +int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info, + struct nlattr *mx, int mx_len); int fib6_del(struct rt6_info *rt, struct nl_info *info); diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 075602fc6b6a..4ee487b103ae 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -638,12 +638,41 @@ static inline bool rt6_qualify_for_ecmp(struct rt6_info *rt) RTF_GATEWAY; } +static int fib6_commit_metrics(struct dst_entry *dst, + struct nlattr *mx, int mx_len) +{ + struct nlattr *nla; + int remaining; + u32 *mp; + + if (dst->flags & DST_HOST) { + mp = dst_metrics_write_ptr(dst); + } else { + mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL); + if (!mp) + return -ENOMEM; + dst_init_metrics(dst, mp, 0); + } + + nla_for_each_attr(nla, mx, mx_len, remaining) { + int type = nla_type(nla); + + if (type) { + if (type > RTAX_MAX) + return -EINVAL; + + mp[type - 1] = nla_get_u32(nla); + } + } + return 0; +} + /* * Insert routing information in a node. */ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, - struct nl_info *info) + struct nl_info *info, struct nlattr *mx, int mx_len) { struct rt6_info *iter = NULL; struct rt6_info **ins; @@ -653,6 +682,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, (info->nlh->nlmsg_flags & NLM_F_CREATE)); int found = 0; bool rt_can_ecmp = rt6_qualify_for_ecmp(rt); + int err; ins = &fn->leaf; @@ -751,6 +781,11 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, pr_warn("NLM_F_CREATE should be set when creating new route\n"); add: + if (mx) { + err = fib6_commit_metrics(&rt->dst, mx, mx_len); + if (err) + return err; + } rt->dst.rt6_next = iter; *ins = rt; rt->rt6i_node = fn; @@ -770,6 +805,11 @@ add: pr_warn("NLM_F_REPLACE set, but no existing node found!\n"); return -ENOENT; } + if (mx) { + err = fib6_commit_metrics(&rt->dst, mx, mx_len); + if (err) + return err; + } *ins = rt; rt->rt6i_node = fn; rt->dst.rt6_next = iter->dst.rt6_next; @@ -806,7 +846,8 @@ void fib6_force_start_gc(struct net *net) * with source addr info in sub-trees */ -int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info) +int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info, + struct nlattr *mx, int mx_len) { struct fib6_node *fn, *pn = NULL; int err = -ENOMEM; @@ -900,7 +941,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info) } #endif - err = fib6_add_rt2node(fn, rt, info); + err = fib6_add_rt2node(fn, rt, info, mx, mx_len); if (!err) { fib6_start_gc(info->nl_net, rt); if (!(rt->rt6i_flags & RTF_CACHE)) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index fba54a407bb2..b93ae6a6a31c 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -149,7 +149,8 @@ static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old) unsigned long prev, new; p = peer->metrics; - if (inet_metrics_new(peer)) + if (inet_metrics_new(peer) || + (old & DST_METRICS_FORCE_OVERWRITE)) memcpy(p, old_p, sizeof(u32) * RTAX_MAX); new = (unsigned long) p; @@ -857,14 +858,15 @@ EXPORT_SYMBOL(rt6_lookup); be destroyed. */ -static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info) +static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info, + struct nlattr *mx, int mx_len) { int err; struct fib6_table *table; table = rt->rt6i_table; write_lock_bh(&table->tb6_lock); - err = fib6_add(&table->tb6_root, rt, info); + err = fib6_add(&table->tb6_root, rt, info, mx, mx_len); write_unlock_bh(&table->tb6_lock); return err; @@ -875,7 +877,7 @@ int ip6_ins_rt(struct rt6_info *rt) struct nl_info info = { .nl_net = dev_net(rt->dst.dev), }; - return __ip6_ins_rt(rt, &info); + return __ip6_ins_rt(rt, &info, NULL, 0); } static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, @@ -1543,17 +1545,11 @@ int ip6_route_add(struct fib6_config *cfg) ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len); rt->rt6i_dst.plen = cfg->fc_dst_len; - if (rt->rt6i_dst.plen == 128) - rt->dst.flags |= DST_HOST; - - if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) { - u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL); - if (!metrics) { - err = -ENOMEM; - goto out; - } - dst_init_metrics(&rt->dst, metrics, 0); + if (rt->rt6i_dst.plen == 128) { + rt->dst.flags |= DST_HOST; + dst_metrics_set_force_overwrite(&rt->dst); } + #ifdef CONFIG_IPV6_SUBTREES ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len); rt->rt6i_src.plen = cfg->fc_src_len; @@ -1672,31 +1668,13 @@ int ip6_route_add(struct fib6_config *cfg) rt->rt6i_flags = cfg->fc_flags; install_route: - if (cfg->fc_mx) { - struct nlattr *nla; - int remaining; - - nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { - int type = nla_type(nla); - - if (type) { - if (type > RTAX_MAX) { - err = -EINVAL; - goto out; - } - - dst_metric_set(&rt->dst, type, nla_get_u32(nla)); - } - } - } - rt->dst.dev = dev; rt->rt6i_idev = idev; rt->rt6i_table = table; cfg->fc_nlinfo.nl_net = dev_net(dev); - return __ip6_ins_rt(rt, &cfg->fc_nlinfo); + return __ip6_ins_rt(rt, &cfg->fc_nlinfo, cfg->fc_mx, cfg->fc_mx_len); out: if (dev) -- cgit v1.2.3-71-gd317 From fbc907f0b1386c02e00516aa78a0fa6b0454fd0b Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 28 Mar 2014 18:58:20 +0100 Subject: net: filter: move filter accounting to filter core This patch basically does two things, i) removes the extern keyword from the include/linux/filter.h file to be more consistent with the rest of Joe's changes, and ii) moves filter accounting into the filter core framework. Filter accounting mainly done through sk_filter_{un,}charge() take care of the case when sockets are being cloned through sk_clone_lock() so that removal of the filter on one socket won't result in eviction as it's still referenced by the other. These functions actually belong to net/core/filter.c and not include/net/sock.h as we want to keep all that in a central place. It's also not in fast-path so uninlining them is fine and even allows us to get rd of sk_filter_release_rcu()'s EXPORT_SYMBOL and a forward declaration. Joint work with Alexei Starovoitov. Signed-off-by: Daniel Borkmann Signed-off-by: Alexei Starovoitov Cc: Pavel Emelyanov Signed-off-by: David S. Miller --- include/linux/filter.h | 30 +++++++++++++++++------------- include/net/sock.h | 27 --------------------------- net/core/filter.c | 27 +++++++++++++++++++++++++-- 3 files changed, 42 insertions(+), 42 deletions(-) (limited to 'include/net') diff --git a/include/linux/filter.h b/include/linux/filter.h index 93a9792e27bc..9bde3ed19fe6 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -50,28 +50,32 @@ static inline unsigned int sk_filter_size(unsigned int proglen) #define sk_filter_proglen(fprog) \ (fprog->len * sizeof(fprog->filter[0])) -extern int sk_filter(struct sock *sk, struct sk_buff *skb); -extern unsigned int sk_run_filter(const struct sk_buff *skb, - const struct sock_filter *filter); +int sk_filter(struct sock *sk, struct sk_buff *skb); +unsigned int sk_run_filter(const struct sk_buff *skb, + const struct sock_filter *filter); -extern int sk_unattached_filter_create(struct sk_filter **pfp, - struct sock_fprog *fprog); -extern void sk_unattached_filter_destroy(struct sk_filter *fp); +int sk_unattached_filter_create(struct sk_filter **pfp, + struct sock_fprog *fprog); +void sk_unattached_filter_destroy(struct sk_filter *fp); -extern int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk); -extern int sk_detach_filter(struct sock *sk); +int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk); +int sk_detach_filter(struct sock *sk); -extern int sk_chk_filter(struct sock_filter *filter, unsigned int flen); -extern int sk_get_filter(struct sock *sk, struct sock_filter __user *filter, unsigned len); -extern void sk_decode_filter(struct sock_filter *filt, struct sock_filter *to); +int sk_chk_filter(struct sock_filter *filter, unsigned int flen); +int sk_get_filter(struct sock *sk, struct sock_filter __user *filter, + unsigned int len); +void sk_decode_filter(struct sock_filter *filt, struct sock_filter *to); + +void sk_filter_charge(struct sock *sk, struct sk_filter *fp); +void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp); #ifdef CONFIG_BPF_JIT #include #include #include -extern void bpf_jit_compile(struct sk_filter *fp); -extern void bpf_jit_free(struct sk_filter *fp); +void bpf_jit_compile(struct sk_filter *fp); +void bpf_jit_free(struct sk_filter *fp); static inline void bpf_jit_dump(unsigned int flen, unsigned int proglen, u32 pass, void *image) diff --git a/include/net/sock.h b/include/net/sock.h index 8d7c431a0660..06a5668f05c9 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1621,33 +1621,6 @@ void sk_common_release(struct sock *sk); /* Initialise core socket variables */ void sock_init_data(struct socket *sock, struct sock *sk); -void sk_filter_release_rcu(struct rcu_head *rcu); - -/** - * sk_filter_release - release a socket filter - * @fp: filter to remove - * - * Remove a filter from a socket and release its resources. - */ - -static inline void sk_filter_release(struct sk_filter *fp) -{ - if (atomic_dec_and_test(&fp->refcnt)) - call_rcu(&fp->rcu, sk_filter_release_rcu); -} - -static inline void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp) -{ - atomic_sub(sk_filter_size(fp->len), &sk->sk_omem_alloc); - sk_filter_release(fp); -} - -static inline void sk_filter_charge(struct sock *sk, struct sk_filter *fp) -{ - atomic_inc(&fp->refcnt); - atomic_add(sk_filter_size(fp->len), &sk->sk_omem_alloc); -} - /* * Socket reference counting postulates. * diff --git a/net/core/filter.c b/net/core/filter.c index 9730e7fe4770..5b3427aaeca5 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -664,14 +664,37 @@ static void sk_release_orig_filter(struct sk_filter *fp) * sk_filter_release_rcu - Release a socket filter by rcu_head * @rcu: rcu_head that contains the sk_filter to free */ -void sk_filter_release_rcu(struct rcu_head *rcu) +static void sk_filter_release_rcu(struct rcu_head *rcu) { struct sk_filter *fp = container_of(rcu, struct sk_filter, rcu); sk_release_orig_filter(fp); bpf_jit_free(fp); } -EXPORT_SYMBOL(sk_filter_release_rcu); + +/** + * sk_filter_release - release a socket filter + * @fp: filter to remove + * + * Remove a filter from a socket and release its resources. + */ +static void sk_filter_release(struct sk_filter *fp) +{ + if (atomic_dec_and_test(&fp->refcnt)) + call_rcu(&fp->rcu, sk_filter_release_rcu); +} + +void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp) +{ + atomic_sub(sk_filter_size(fp->len), &sk->sk_omem_alloc); + sk_filter_release(fp); +} + +void sk_filter_charge(struct sock *sk, struct sk_filter *fp) +{ + atomic_inc(&fp->refcnt); + atomic_add(sk_filter_size(fp->len), &sk->sk_omem_alloc); +} static int __sk_prepare_filter(struct sk_filter *fp) { -- cgit v1.2.3-71-gd317 From 60ea37f7a5c776be4631c535c7cdd3ce62cf4dee Mon Sep 17 00:00:00 2001 From: Wang Yufen Date: Sat, 29 Mar 2014 09:27:30 +0800 Subject: ipv6: reuse rt6_need_strict Move the whole rt6_need_strict as static inline into ip6_route.h, so that it can be reused Signed-off-by: Wang Yufen Signed-off-by: David S. Miller --- include/net/ip6_route.h | 5 +++++ net/ipv6/route.c | 6 ------ 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'include/net') diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 00e3f12cb2f9..3c3bb184eb8f 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -51,6 +51,11 @@ static inline unsigned int rt6_flags2srcprefs(int flags) return (flags >> 3) & 7; } +static inline bool rt6_need_strict(const struct in6_addr *daddr) +{ + return ipv6_addr_type(daddr) & + (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK); +} void ip6_route_input(struct sk_buff *skb); diff --git a/net/ipv6/route.c b/net/ipv6/route.c index b93ae6a6a31c..5015c50a5ba7 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -374,12 +374,6 @@ static bool rt6_check_expired(const struct rt6_info *rt) return false; } -static bool rt6_need_strict(const struct in6_addr *daddr) -{ - return ipv6_addr_type(daddr) & - (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK); -} - /* Multipath route selection: * Hash based function using packet header and flowlabel. * Adapted from fib_info_hashfn() -- cgit v1.2.3-71-gd317 From e462ded699aa2cca04b68fbf203ea4675d4c44d4 Mon Sep 17 00:00:00 2001 From: Phoebe Buckheister Date: Mon, 31 Mar 2014 21:37:46 +0200 Subject: mac802154: make csma/cca parameters per-wpan Commit 9b2777d6089bcd (ieee802154: add TX power control to wpan_phy) and following erroneously added CSMA and CCA parameters for 802.15.4 devices as PHY parameters, while they are actually MAC parameters and can differ for any two WPAN instances. Since it is now sensible to have multiple WPAN devices with differing CSMA/CCA parameters, make these parameters MAC parameters instead. Signed-off-by: Phoebe Buckheister Signed-off-by: David S. Miller --- include/linux/nl802154.h | 2 +- include/net/ieee802154_netdev.h | 17 ++++ net/ieee802154/ieee802154.h | 2 +- net/ieee802154/netlink.c | 2 +- net/ieee802154/nl-mac.c | 122 +++++++++++++++++++++++- net/ieee802154/nl-phy.c | 200 +--------------------------------------- net/ieee802154/wpan-class.c | 6 -- net/mac802154/ieee802154_dev.c | 36 +++----- net/mac802154/mac802154.h | 9 ++ net/mac802154/mac_cmd.c | 3 + net/mac802154/wpan.c | 89 +++++++++++++++++- 11 files changed, 252 insertions(+), 236 deletions(-) (limited to 'include/net') diff --git a/include/linux/nl802154.h b/include/linux/nl802154.h index e110b8c266f5..c8d7f3965fff 100644 --- a/include/linux/nl802154.h +++ b/include/linux/nl802154.h @@ -132,7 +132,7 @@ enum { IEEE802154_ADD_IFACE, IEEE802154_DEL_IFACE, - IEEE802154_SET_PHYPARAMS, + IEEE802154_SET_MACPARAMS, __IEEE802154_CMD_MAX, }; diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h index e1717cbf609b..5a719ca892f4 100644 --- a/include/net/ieee802154_netdev.h +++ b/include/net/ieee802154_netdev.h @@ -229,6 +229,18 @@ static inline int mac_cb_type(struct sk_buff *skb) #define IEEE802154_MAC_SCAN_PASSIVE 2 #define IEEE802154_MAC_SCAN_ORPHAN 3 +struct ieee802154_mac_params { + s8 transmit_power; + u8 min_be; + u8 max_be; + u8 csma_retries; + s8 frame_retries; + + bool lbt; + u8 cca_mode; + s32 cca_ed_level; +}; + struct wpan_phy; /* * This should be located at net_device->ml_priv @@ -255,6 +267,11 @@ struct ieee802154_mlme_ops { int (*scan_req)(struct net_device *dev, u8 type, u32 channels, u8 page, u8 duration); + int (*set_mac_params)(struct net_device *dev, + const struct ieee802154_mac_params *params); + void (*get_mac_params)(struct net_device *dev, + struct ieee802154_mac_params *params); + /* The fields below are required. */ struct wpan_phy *(*get_phy)(const struct net_device *dev); diff --git a/net/ieee802154/ieee802154.h b/net/ieee802154/ieee802154.h index 6cbc8965be91..6693a5cf01ce 100644 --- a/net/ieee802154/ieee802154.h +++ b/net/ieee802154/ieee802154.h @@ -53,7 +53,6 @@ int ieee802154_list_phy(struct sk_buff *skb, struct genl_info *info); int ieee802154_dump_phy(struct sk_buff *skb, struct netlink_callback *cb); int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info); int ieee802154_del_iface(struct sk_buff *skb, struct genl_info *info); -int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info); enum ieee802154_mcgrp_ids { IEEE802154_COORD_MCGRP, @@ -67,5 +66,6 @@ int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info); int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info); int ieee802154_list_iface(struct sk_buff *skb, struct genl_info *info); int ieee802154_dump_iface(struct sk_buff *skb, struct netlink_callback *cb); +int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info); #endif diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c index 67c151bf4b91..04b20589d97a 100644 --- a/net/ieee802154/netlink.c +++ b/net/ieee802154/netlink.c @@ -115,7 +115,6 @@ static const struct genl_ops ieee8021154_ops[] = { ieee802154_dump_phy), IEEE802154_OP(IEEE802154_ADD_IFACE, ieee802154_add_iface), IEEE802154_OP(IEEE802154_DEL_IFACE, ieee802154_del_iface), - IEEE802154_OP(IEEE802154_SET_PHYPARAMS, ieee802154_set_phyparams), /* see nl-mac.c */ IEEE802154_OP(IEEE802154_ASSOCIATE_REQ, ieee802154_associate_req), IEEE802154_OP(IEEE802154_ASSOCIATE_RESP, ieee802154_associate_resp), @@ -124,6 +123,7 @@ static const struct genl_ops ieee8021154_ops[] = { IEEE802154_OP(IEEE802154_START_REQ, ieee802154_start_req), IEEE802154_DUMP(IEEE802154_LIST_IFACE, ieee802154_list_iface, ieee802154_dump_iface), + IEEE802154_OP(IEEE802154_SET_MACPARAMS, ieee802154_set_macparams), }; static const struct genl_multicast_group ieee802154_mcgrps[] = { diff --git a/net/ieee802154/nl-mac.c b/net/ieee802154/nl-mac.c index bda8dba4f993..5d285498c0f6 100644 --- a/net/ieee802154/nl-mac.c +++ b/net/ieee802154/nl-mac.c @@ -264,6 +264,7 @@ static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid, { void *hdr; struct wpan_phy *phy; + struct ieee802154_mlme_ops *ops; __le16 short_addr, pan_id; pr_debug("%s\n", __func__); @@ -273,11 +274,12 @@ static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid, if (!hdr) goto out; - phy = ieee802154_mlme_ops(dev)->get_phy(dev); + ops = ieee802154_mlme_ops(dev); + phy = ops->get_phy(dev); BUG_ON(!phy); - short_addr = ieee802154_mlme_ops(dev)->get_short_addr(dev); - pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); + short_addr = ops->get_short_addr(dev); + pan_id = ops->get_pan_id(dev); if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) || @@ -287,6 +289,30 @@ static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid, nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) || nla_put_shortaddr(msg, IEEE802154_ATTR_PAN_ID, pan_id)) goto nla_put_failure; + + if (ops->get_mac_params) { + struct ieee802154_mac_params params; + + ops->get_mac_params(dev, ¶ms); + + if (nla_put_s8(msg, IEEE802154_ATTR_TXPOWER, + params.transmit_power) || + nla_put_u8(msg, IEEE802154_ATTR_LBT_ENABLED, params.lbt) || + nla_put_u8(msg, IEEE802154_ATTR_CCA_MODE, + params.cca_mode) || + nla_put_s32(msg, IEEE802154_ATTR_CCA_ED_LEVEL, + params.cca_ed_level) || + nla_put_u8(msg, IEEE802154_ATTR_CSMA_RETRIES, + params.csma_retries) || + nla_put_u8(msg, IEEE802154_ATTR_CSMA_MIN_BE, + params.min_be) || + nla_put_u8(msg, IEEE802154_ATTR_CSMA_MAX_BE, + params.max_be) || + nla_put_s8(msg, IEEE802154_ATTR_FRAME_RETRIES, + params.frame_retries)) + goto nla_put_failure; + } + wpan_phy_put(phy); return genlmsg_end(msg, hdr); @@ -599,3 +625,93 @@ cont: return skb->len; } + +int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info) +{ + struct net_device *dev = NULL; + struct ieee802154_mlme_ops *ops; + struct ieee802154_mac_params params; + struct wpan_phy *phy; + int rc = -EINVAL; + + pr_debug("%s\n", __func__); + + dev = ieee802154_nl_get_dev(info); + if (!dev) + return -ENODEV; + + ops = ieee802154_mlme_ops(dev); + + if (!ops->get_mac_params || !ops->set_mac_params) { + rc = -EOPNOTSUPP; + goto out; + } + + if (netif_running(dev)) { + rc = -EBUSY; + goto out; + } + + if (!info->attrs[IEEE802154_ATTR_LBT_ENABLED] && + !info->attrs[IEEE802154_ATTR_CCA_MODE] && + !info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL] && + !info->attrs[IEEE802154_ATTR_CSMA_RETRIES] && + !info->attrs[IEEE802154_ATTR_CSMA_MIN_BE] && + !info->attrs[IEEE802154_ATTR_CSMA_MAX_BE] && + !info->attrs[IEEE802154_ATTR_FRAME_RETRIES]) + goto out; + + phy = ops->get_phy(dev); + + if ((!phy->set_lbt && info->attrs[IEEE802154_ATTR_LBT_ENABLED]) || + (!phy->set_cca_mode && info->attrs[IEEE802154_ATTR_CCA_MODE]) || + (!phy->set_cca_ed_level && + info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]) || + (!phy->set_csma_params && + (info->attrs[IEEE802154_ATTR_CSMA_RETRIES] || + info->attrs[IEEE802154_ATTR_CSMA_MIN_BE] || + info->attrs[IEEE802154_ATTR_CSMA_MAX_BE])) || + (!phy->set_frame_retries && + info->attrs[IEEE802154_ATTR_FRAME_RETRIES])) { + rc = -EOPNOTSUPP; + goto out_phy; + } + + ops->get_mac_params(dev, ¶ms); + + if (info->attrs[IEEE802154_ATTR_TXPOWER]) + params.transmit_power = nla_get_s8(info->attrs[IEEE802154_ATTR_TXPOWER]); + + if (info->attrs[IEEE802154_ATTR_LBT_ENABLED]) + params.lbt = nla_get_u8(info->attrs[IEEE802154_ATTR_LBT_ENABLED]); + + if (info->attrs[IEEE802154_ATTR_CCA_MODE]) + params.cca_mode = nla_get_u8(info->attrs[IEEE802154_ATTR_CCA_MODE]); + + if (info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]) + params.cca_ed_level = nla_get_s32(info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]); + + if (info->attrs[IEEE802154_ATTR_CSMA_RETRIES]) + params.csma_retries = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_RETRIES]); + + if (info->attrs[IEEE802154_ATTR_CSMA_MIN_BE]) + params.min_be = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_MIN_BE]); + + if (info->attrs[IEEE802154_ATTR_CSMA_MAX_BE]) + params.max_be = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_MAX_BE]); + + if (info->attrs[IEEE802154_ATTR_FRAME_RETRIES]) + params.frame_retries = nla_get_s8(info->attrs[IEEE802154_ATTR_FRAME_RETRIES]); + + rc = ops->set_mac_params(dev, ¶ms); + + wpan_phy_put(phy); + dev_put(dev); + return rc; + +out_phy: + wpan_phy_put(phy); +out: + dev_put(dev); + return rc; +} diff --git a/net/ieee802154/nl-phy.c b/net/ieee802154/nl-phy.c index 222310a07762..89b265aea151 100644 --- a/net/ieee802154/nl-phy.c +++ b/net/ieee802154/nl-phy.c @@ -55,15 +55,7 @@ static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid, mutex_lock(&phy->pib_lock); if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) || nla_put_u8(msg, IEEE802154_ATTR_PAGE, phy->current_page) || - nla_put_u8(msg, IEEE802154_ATTR_CHANNEL, phy->current_channel) || - nla_put_s8(msg, IEEE802154_ATTR_TXPOWER, phy->transmit_power) || - nla_put_u8(msg, IEEE802154_ATTR_LBT_ENABLED, phy->lbt) || - nla_put_u8(msg, IEEE802154_ATTR_CCA_MODE, phy->cca_mode) || - nla_put_s32(msg, IEEE802154_ATTR_CCA_ED_LEVEL, phy->cca_ed_level) || - nla_put_u8(msg, IEEE802154_ATTR_CSMA_RETRIES, phy->csma_retries) || - nla_put_u8(msg, IEEE802154_ATTR_CSMA_MIN_BE, phy->min_be) || - nla_put_u8(msg, IEEE802154_ATTR_CSMA_MAX_BE, phy->max_be) || - nla_put_s8(msg, IEEE802154_ATTR_FRAME_RETRIES, phy->frame_retries)) + nla_put_u8(msg, IEEE802154_ATTR_CHANNEL, phy->current_channel)) goto nla_put_failure; for (i = 0; i < 32; i++) { if (phy->channels_supported[i]) @@ -362,193 +354,3 @@ out_dev: return rc; } - -static int phy_set_txpower(struct wpan_phy *phy, struct genl_info *info) -{ - int txpower = nla_get_s8(info->attrs[IEEE802154_ATTR_TXPOWER]); - int rc; - - rc = phy->set_txpower(phy, txpower); - if (rc < 0) - return rc; - - phy->transmit_power = txpower; - - return 0; -} - -static int phy_set_lbt(struct wpan_phy *phy, struct genl_info *info) -{ - u8 on = !!nla_get_u8(info->attrs[IEEE802154_ATTR_LBT_ENABLED]); - int rc; - - rc = phy->set_lbt(phy, on); - if (rc < 0) - return rc; - - phy->lbt = on; - - return 0; -} - -static int phy_set_cca_mode(struct wpan_phy *phy, struct genl_info *info) -{ - u8 mode = nla_get_u8(info->attrs[IEEE802154_ATTR_CCA_MODE]); - int rc; - - if (mode > 3) - return -EINVAL; - - rc = phy->set_cca_mode(phy, mode); - if (rc < 0) - return rc; - - phy->cca_mode = mode; - - return 0; -} - -static int phy_set_cca_ed_level(struct wpan_phy *phy, struct genl_info *info) -{ - s32 level = nla_get_s32(info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]); - int rc; - - rc = phy->set_cca_ed_level(phy, level); - if (rc < 0) - return rc; - - phy->cca_ed_level = level; - - return 0; -} - -static int phy_set_csma_params(struct wpan_phy *phy, struct genl_info *info) -{ - int rc; - u8 min_be = phy->min_be; - u8 max_be = phy->max_be; - u8 retries = phy->csma_retries; - - if (info->attrs[IEEE802154_ATTR_CSMA_RETRIES]) - retries = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_RETRIES]); - if (info->attrs[IEEE802154_ATTR_CSMA_MIN_BE]) - min_be = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_MIN_BE]); - if (info->attrs[IEEE802154_ATTR_CSMA_MAX_BE]) - max_be = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_MAX_BE]); - - if (retries > 5 || max_be < 3 || max_be > 8 || min_be > max_be) - return -EINVAL; - - rc = phy->set_csma_params(phy, min_be, max_be, retries); - if (rc < 0) - return rc; - - phy->min_be = min_be; - phy->max_be = max_be; - phy->csma_retries = retries; - - return 0; -} - -static int phy_set_frame_retries(struct wpan_phy *phy, struct genl_info *info) -{ - s8 retries = nla_get_s8(info->attrs[IEEE802154_ATTR_FRAME_RETRIES]); - int rc; - - if (retries < -1 || retries > 7) - return -EINVAL; - - rc = phy->set_frame_retries(phy, retries); - if (rc < 0) - return rc; - - phy->frame_retries = retries; - - return 0; -} - -int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info) -{ - struct wpan_phy *phy; - const char *name; - int rc = -ENOTSUPP; - - pr_debug("%s\n", __func__); - - if (!info->attrs[IEEE802154_ATTR_PHY_NAME] && - !info->attrs[IEEE802154_ATTR_LBT_ENABLED] && - !info->attrs[IEEE802154_ATTR_CCA_MODE] && - !info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL] && - !info->attrs[IEEE802154_ATTR_CSMA_RETRIES] && - !info->attrs[IEEE802154_ATTR_CSMA_MIN_BE] && - !info->attrs[IEEE802154_ATTR_CSMA_MAX_BE] && - !info->attrs[IEEE802154_ATTR_FRAME_RETRIES]) - return -EINVAL; - - name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]); - if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0') - return -EINVAL; /* phy name should be null-terminated */ - - phy = wpan_phy_find(name); - if (!phy) - return -ENODEV; - - if ((!phy->set_txpower && info->attrs[IEEE802154_ATTR_TXPOWER]) || - (!phy->set_lbt && info->attrs[IEEE802154_ATTR_LBT_ENABLED]) || - (!phy->set_cca_mode && info->attrs[IEEE802154_ATTR_CCA_MODE]) || - (!phy->set_cca_ed_level && - info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL])) - goto out; - - mutex_lock(&phy->pib_lock); - - if (info->attrs[IEEE802154_ATTR_TXPOWER]) { - rc = phy_set_txpower(phy, info); - if (rc < 0) - goto error; - } - - if (info->attrs[IEEE802154_ATTR_LBT_ENABLED]) { - rc = phy_set_lbt(phy, info); - if (rc < 0) - goto error; - } - - if (info->attrs[IEEE802154_ATTR_CCA_MODE]) { - rc = phy_set_cca_mode(phy, info); - if (rc < 0) - goto error; - } - - if (info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]) { - rc = phy_set_cca_ed_level(phy, info); - if (rc < 0) - goto error; - } - - if (info->attrs[IEEE802154_ATTR_CSMA_RETRIES] || - info->attrs[IEEE802154_ATTR_CSMA_MIN_BE] || - info->attrs[IEEE802154_ATTR_CSMA_MAX_BE]) { - rc = phy_set_csma_params(phy, info); - if (rc < 0) - goto error; - } - - if (info->attrs[IEEE802154_ATTR_FRAME_RETRIES]) { - rc = phy_set_frame_retries(phy, info); - if (rc < 0) - goto error; - } - - mutex_unlock(&phy->pib_lock); - - wpan_phy_put(phy); - - return 0; - -error: - mutex_unlock(&phy->pib_lock); -out: - wpan_phy_put(phy); - return rc; -} diff --git a/net/ieee802154/wpan-class.c b/net/ieee802154/wpan-class.c index edd0962d55f9..8d6f6704da84 100644 --- a/net/ieee802154/wpan-class.c +++ b/net/ieee802154/wpan-class.c @@ -169,12 +169,6 @@ struct wpan_phy *wpan_phy_alloc(size_t priv_size) phy->current_channel = -1; /* not initialised */ phy->current_page = 0; /* for compatibility */ - /* defaults per 802.15.4-2011 */ - phy->min_be = 3; - phy->max_be = 5; - phy->csma_retries = 4; - phy->frame_retries = -1; /* for compatibility, actual default is 3 */ - return phy; out: diff --git a/net/mac802154/ieee802154_dev.c b/net/mac802154/ieee802154_dev.c index e7aa76445fe1..2cf66d885e68 100644 --- a/net/mac802154/ieee802154_dev.c +++ b/net/mac802154/ieee802154_dev.c @@ -197,9 +197,6 @@ static int mac802154_set_txpower(struct wpan_phy *phy, int db) { struct mac802154_priv *priv = wpan_phy_priv(phy); - if (!priv->ops->set_txpower) - return -ENOTSUPP; - return priv->ops->set_txpower(&priv->hw, db); } @@ -207,9 +204,6 @@ static int mac802154_set_lbt(struct wpan_phy *phy, bool on) { struct mac802154_priv *priv = wpan_phy_priv(phy); - if (!priv->ops->set_lbt) - return -ENOTSUPP; - return priv->ops->set_lbt(&priv->hw, on); } @@ -217,9 +211,6 @@ static int mac802154_set_cca_mode(struct wpan_phy *phy, u8 mode) { struct mac802154_priv *priv = wpan_phy_priv(phy); - if (!priv->ops->set_cca_mode) - return -ENOTSUPP; - return priv->ops->set_cca_mode(&priv->hw, mode); } @@ -227,9 +218,6 @@ static int mac802154_set_cca_ed_level(struct wpan_phy *phy, s32 level) { struct mac802154_priv *priv = wpan_phy_priv(phy); - if (!priv->ops->set_cca_ed_level) - return -ENOTSUPP; - return priv->ops->set_cca_ed_level(&priv->hw, level); } @@ -238,9 +226,6 @@ static int mac802154_set_csma_params(struct wpan_phy *phy, u8 min_be, { struct mac802154_priv *priv = wpan_phy_priv(phy); - if (!priv->ops->set_csma_params) - return -ENOTSUPP; - return priv->ops->set_csma_params(&priv->hw, min_be, max_be, retries); } @@ -248,9 +233,6 @@ static int mac802154_set_frame_retries(struct wpan_phy *phy, s8 retries) { struct mac802154_priv *priv = wpan_phy_priv(phy); - if (!priv->ops->set_frame_retries) - return -ENOTSUPP; - return priv->ops->set_frame_retries(&priv->hw, retries); } @@ -331,12 +313,18 @@ int ieee802154_register_device(struct ieee802154_dev *dev) priv->phy->add_iface = mac802154_add_iface; priv->phy->del_iface = mac802154_del_iface; - priv->phy->set_txpower = mac802154_set_txpower; - priv->phy->set_lbt = mac802154_set_lbt; - priv->phy->set_cca_mode = mac802154_set_cca_mode; - priv->phy->set_cca_ed_level = mac802154_set_cca_ed_level; - priv->phy->set_csma_params = mac802154_set_csma_params; - priv->phy->set_frame_retries = mac802154_set_frame_retries; + if (priv->ops->set_txpower) + priv->phy->set_txpower = mac802154_set_txpower; + if (priv->ops->set_lbt) + priv->phy->set_lbt = mac802154_set_lbt; + if (priv->ops->set_cca_mode) + priv->phy->set_cca_mode = mac802154_set_cca_mode; + if (priv->ops->set_cca_ed_level) + priv->phy->set_cca_ed_level = mac802154_set_cca_ed_level; + if (priv->ops->set_csma_params) + priv->phy->set_csma_params = mac802154_set_csma_params; + if (priv->ops->set_frame_retries) + priv->phy->set_frame_retries = mac802154_set_frame_retries; rc = wpan_phy_register(priv->phy); if (rc < 0) diff --git a/net/mac802154/mac802154.h b/net/mac802154/mac802154.h index f40522ef288c..28ef59c566e6 100644 --- a/net/mac802154/mac802154.h +++ b/net/mac802154/mac802154.h @@ -23,6 +23,8 @@ #ifndef MAC802154_H #define MAC802154_H +#include + /* mac802154 device private data */ struct mac802154_priv { struct ieee802154_dev hw; @@ -82,6 +84,8 @@ struct mac802154_sub_if_data { u8 chan; u8 page; + struct ieee802154_mac_params mac_params; + /* MAC BSN field */ u8 bsn; /* MAC DSN field */ @@ -116,4 +120,9 @@ void mac802154_dev_set_pan_id(struct net_device *dev, __le16 val); void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan); u8 mac802154_dev_get_dsn(const struct net_device *dev); +int mac802154_set_mac_params(struct net_device *dev, + const struct ieee802154_mac_params *params); +void mac802154_get_mac_params(struct net_device *dev, + struct ieee802154_mac_params *params); + #endif /* MAC802154_H */ diff --git a/net/mac802154/mac_cmd.c b/net/mac802154/mac_cmd.c index 15bac3358889..d40c0928bc62 100644 --- a/net/mac802154/mac_cmd.c +++ b/net/mac802154/mac_cmd.c @@ -74,4 +74,7 @@ struct ieee802154_mlme_ops mac802154_mlme_wpan = { .get_pan_id = mac802154_dev_get_pan_id, .get_short_addr = mac802154_dev_get_short_addr, .get_dsn = mac802154_dev_get_dsn, + + .set_mac_params = mac802154_set_mac_params, + .get_mac_params = mac802154_get_mac_params, }; diff --git a/net/mac802154/wpan.c b/net/mac802154/wpan.c index 80cbee1a2f56..1df7a6a57386 100644 --- a/net/mac802154/wpan.c +++ b/net/mac802154/wpan.c @@ -102,6 +102,87 @@ static int mac802154_wpan_mac_addr(struct net_device *dev, void *p) return 0; } +int mac802154_set_mac_params(struct net_device *dev, + const struct ieee802154_mac_params *params) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + + mutex_lock(&priv->hw->slaves_mtx); + priv->mac_params = *params; + mutex_unlock(&priv->hw->slaves_mtx); + + return 0; +} + +void mac802154_get_mac_params(struct net_device *dev, + struct ieee802154_mac_params *params) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + + mutex_lock(&priv->hw->slaves_mtx); + *params = priv->mac_params; + mutex_unlock(&priv->hw->slaves_mtx); +} + +int mac802154_wpan_open(struct net_device *dev) +{ + int rc; + struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct wpan_phy *phy = priv->hw->phy; + + rc = mac802154_slave_open(dev); + if (rc < 0) + return rc; + + mutex_lock(&phy->pib_lock); + + if (phy->set_txpower) { + rc = phy->set_txpower(phy, priv->mac_params.transmit_power); + if (rc < 0) + goto out; + } + + if (phy->set_lbt) { + rc = phy->set_lbt(phy, priv->mac_params.lbt); + if (rc < 0) + goto out; + } + + if (phy->set_cca_mode) { + rc = phy->set_cca_mode(phy, priv->mac_params.cca_mode); + if (rc < 0) + goto out; + } + + if (phy->set_cca_ed_level) { + rc = phy->set_cca_ed_level(phy, priv->mac_params.cca_ed_level); + if (rc < 0) + goto out; + } + + if (phy->set_csma_params) { + rc = phy->set_csma_params(phy, priv->mac_params.min_be, + priv->mac_params.max_be, + priv->mac_params.csma_retries); + if (rc < 0) + goto out; + } + + if (phy->set_frame_retries) { + rc = phy->set_frame_retries(phy, + priv->mac_params.frame_retries); + if (rc < 0) + goto out; + } + + mutex_unlock(&phy->pib_lock); + return 0; + +out: + mutex_unlock(&phy->pib_lock); + return rc; +} + static int mac802154_header_create(struct sk_buff *skb, struct net_device *dev, unsigned short type, @@ -204,7 +285,7 @@ static struct header_ops mac802154_header_ops = { }; static const struct net_device_ops mac802154_wpan_ops = { - .ndo_open = mac802154_slave_open, + .ndo_open = mac802154_wpan_open, .ndo_stop = mac802154_slave_close, .ndo_start_xmit = mac802154_wpan_xmit, .ndo_do_ioctl = mac802154_wpan_ioctl, @@ -242,6 +323,12 @@ void mac802154_wpan_setup(struct net_device *dev) get_random_bytes(&priv->bsn, 1); get_random_bytes(&priv->dsn, 1); + /* defaults per 802.15.4-2011 */ + priv->mac_params.min_be = 3; + priv->mac_params.max_be = 5; + priv->mac_params.csma_retries = 4; + priv->mac_params.frame_retries = -1; /* for compatibility, actual default is 3 */ + priv->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST); priv->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST); } -- cgit v1.2.3-71-gd317