Add NL80211_CMD_NAN_NDP_RESPOND to allow userspace to accept or reject an incoming NDP request when NDP management is offloaded to firmware. Add the corresponding parameter structure, nl80211 policy, command handler, and driver callback. Reject the request if the driver has not advertised NDP offload capability or if security material is provided without secure NAN support. Signed-off-by: Shivani Baranwal --- include/net/cfg80211.h | 48 +++++++++++++ include/uapi/linux/nl80211.h | 13 ++++ net/wireless/nl80211.c | 129 +++++++++++++++++++++++++++++++++++ net/wireless/rdev-ops.h | 16 +++++ net/wireless/trace.h | 24 +++++++ 5 files changed, 230 insertions(+) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ed1f476c526a..15b9c88d7ace 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4378,6 +4378,49 @@ struct cfg80211_nan_ndp_initiate { bool ndpe; }; +/** + * struct cfg80211_nan_ndp_respond - NAN NDP respond parameters + * + * @peer_nmi: peer NMI address + * @ndp_id: NDP ID from the received NDP Request + * @init_ndi: initiator NDI MAC from the received NDP Request + * @resp_ndi: responder NDI MAC. The corresponding + * %NL80211_IFTYPE_NAN_DATA interface must exist (if accepting). + * @accept: if true, accept the NDP; if false, reject it + * @reason: rejection reason code (valid when @accept is false) + * @ssi: response service-specific info (may be NULL) + * @ssi_len: length of @ssi + * @cipher_suite: NAN cipher suite ID (must match initiator's) + * @pmk: NAN Data Path PMK (%NL80211_NAN_NDP_PMK_LEN bytes, + * valid when @pmk_set is true) + * @pmk_set: if true, @pmk contains a valid PMK + * @gtk_required: if true, GTK protection is required + * @gtk_cipher: GTK cipher suite ID + * @ipv6_intf_id: IPv6 link-local interface identifier + * (%NL80211_NAN_NDP_IPV6_INTF_ID_LEN bytes, + * valid when @ipv6_intf_id_set is true) + * @ipv6_intf_id_set: if true, @ipv6_intf_id is valid + * @ndpe: if true, use NDPE attribute instead of NDP attribute + */ +struct cfg80211_nan_ndp_respond { + const u8 *peer_nmi; + u8 ndp_id; + const u8 *init_ndi; + const u8 *resp_ndi; + bool accept; + u8 reason; + const u8 *ssi; + u8 ssi_len; + u32 cipher_suite; + u8 pmk[NL80211_NAN_NDP_PMK_LEN]; + bool pmk_set; + bool gtk_required; + u32 gtk_cipher; + u8 ipv6_intf_id[NL80211_NAN_NDP_IPV6_INTF_ID_LEN]; + bool ipv6_intf_id_set; + bool ndpe; +}; + /** * enum cfg80211_nan_conf_changes - indicates changed fields in NAN * configuration @@ -5358,6 +5401,8 @@ struct mgmt_frame_regs { * * @nan_ndp_initiate: Initiate a NAN Data Path session as the initiator. * Only valid if the driver has set %WIPHY_NAN_FLAGS_OFFLOAD_NDP. + * @nan_ndp_respond: Respond to an incoming NDP request as the responder. + * Only valid if the driver has set %WIPHY_NAN_FLAGS_OFFLOAD_NDP. * * @set_multicast_to_unicast: configure multicast to unicast conversion for BSS * @@ -5769,6 +5814,9 @@ struct cfg80211_ops { int (*nan_ndp_initiate)(struct wiphy *wiphy, struct wireless_dev *wdev, struct cfg80211_nan_ndp_initiate *params); + int (*nan_ndp_respond)(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct cfg80211_nan_ndp_respond *params); int (*set_multicast_to_unicast)(struct wiphy *wiphy, struct net_device *dev, const bool enabled); diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 02705ce77685..9eb96251f0c2 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1474,6 +1474,11 @@ * interface specified in %NL80211_NAN_NDP_ATTR_INIT_NDI must already * exist as a %NL80211_IFTYPE_NAN_DATA interface. Carries * %NL80211_ATTR_NAN_NDP nested with %NL80211_NAN_NDP_ATTR_* fields. + * @NL80211_CMD_NAN_NDP_RESPOND: Respond to a received NDP request as + * the responder. Requires %WIPHY_NAN_FLAGS_OFFLOAD_NDP. The NDI + * interface specified in %NL80211_NAN_NDP_ATTR_RESP_NDI must already + * exist as a %NL80211_IFTYPE_NAN_DATA interface. Carries + * %NL80211_ATTR_NAN_NDP nested with %NL80211_NAN_NDP_ATTR_* fields. * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use @@ -1758,6 +1763,7 @@ enum nl80211_commands { NL80211_CMD_UPDATE_BEACON, NL80211_CMD_NAN_NDP_INITIATE, + NL80211_CMD_NAN_NDP_RESPOND, /* add new commands above here */ @@ -8105,6 +8111,11 @@ enum nl80211_nan_conf_attributes { * interface identifier. Included as NDPE TLV type 0 when present. * @NL80211_NAN_NDP_ATTR_NDPE: flag. Use NDPE attribute instead of NDP * attribute when peer supports NDPE. + * @NL80211_NAN_NDP_ATTR_ACCEPT: flag. If present in + * %NL80211_CMD_NAN_NDP_RESPOND, the NDP is accepted. If absent, it is + * rejected. + * @NL80211_NAN_NDP_ATTR_REASON: (u8) Termination or rejection reason + * code (enum nan_reason from the Wi-Fi Aware Specification). * @__NL80211_NAN_NDP_ATTR_LAST: Internal. * @NL80211_NAN_NDP_ATTR_MAX: Highest NDP attribute. */ @@ -8124,6 +8135,8 @@ enum nl80211_nan_ndp_attributes { NL80211_NAN_NDP_ATTR_GTK_CIPHER, NL80211_NAN_NDP_ATTR_IPV6_INTF_ID, NL80211_NAN_NDP_ATTR_NDPE, + NL80211_NAN_NDP_ATTR_ACCEPT, + NL80211_NAN_NDP_ATTR_REASON, /* keep last */ __NL80211_NAN_NDP_ATTR_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e0700aa12dbc..7593e69c5b3e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -794,6 +794,8 @@ nl80211_nan_ndp_policy[NL80211_NAN_NDP_ATTR_MAX + 1] = { [NL80211_NAN_NDP_ATTR_IPV6_INTF_ID] = NLA_POLICY_EXACT_LEN(NL80211_NAN_NDP_IPV6_INTF_ID_LEN), [NL80211_NAN_NDP_ATTR_NDPE] = { .type = NLA_FLAG }, + [NL80211_NAN_NDP_ATTR_ACCEPT] = { .type = NLA_FLAG }, + [NL80211_NAN_NDP_ATTR_REASON] = { .type = NLA_U8 }, }; static const struct nla_policy @@ -18623,6 +18625,127 @@ static int nl80211_nan_ndp_initiate(struct sk_buff *skb, return rdev_nan_ndp_initiate(rdev, wdev, ¶ms); } +static int nl80211_nan_ndp_respond(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct wireless_dev *wdev = info->user_ptr[1]; + struct nlattr *tb[NL80211_NAN_NDP_ATTR_MAX + 1]; + struct cfg80211_nan_ndp_respond params = {}; + int err; + + if (wdev->iftype != NL80211_IFTYPE_NAN) { + NL_SET_ERR_MSG(info->extack, + "NDP respond requires NAN interface"); + return -EOPNOTSUPP; + } + + if (!(rdev->wiphy.nan_capa.flags & WIPHY_NAN_FLAGS_OFFLOAD_NDP)) { + NL_SET_ERR_MSG(info->extack, + "NAN NDP offload not supported by driver"); + return -EOPNOTSUPP; + } + + if (!info->attrs[NL80211_ATTR_MAC] || + !info->attrs[NL80211_ATTR_NAN_NDP]) { + NL_SET_ERR_MSG(info->extack, + "NDP respond requires peer NMI and NDP params"); + return -EINVAL; + } + + err = nla_parse_nested(tb, NL80211_NAN_NDP_ATTR_MAX, + info->attrs[NL80211_ATTR_NAN_NDP], + nl80211_nan_ndp_policy, + info->extack); + if (err) + return err; + + if (!tb[NL80211_NAN_NDP_ATTR_ID] || + !tb[NL80211_NAN_NDP_ATTR_INIT_NDI]) { + NL_SET_ERR_MSG(info->extack, + "NDP respond requires NDP ID and INIT_NDI"); + return -EINVAL; + } + + params.peer_nmi = nla_data(info->attrs[NL80211_ATTR_MAC]); + params.ndp_id = nla_get_u8(tb[NL80211_NAN_NDP_ATTR_ID]); + params.init_ndi = nla_data(tb[NL80211_NAN_NDP_ATTR_INIT_NDI]); + params.accept = nla_get_flag(tb[NL80211_NAN_NDP_ATTR_ACCEPT]); + + if (params.accept) { + if (!tb[NL80211_NAN_NDP_ATTR_RESP_NDI]) { + NL_SET_ERR_MSG(info->extack, + "NDP accept requires RESP_NDI"); + return -EINVAL; + } + params.resp_ndi = nla_data(tb[NL80211_NAN_NDP_ATTR_RESP_NDI]); + } else { + if (tb[NL80211_NAN_NDP_ATTR_REASON]) + params.reason = + nla_get_u8(tb[NL80211_NAN_NDP_ATTR_REASON]); + } + + if (tb[NL80211_NAN_NDP_ATTR_SSI]) { + params.ssi = nla_data(tb[NL80211_NAN_NDP_ATTR_SSI]); + params.ssi_len = nla_len(tb[NL80211_NAN_NDP_ATTR_SSI]); + } + + if (tb[NL80211_NAN_NDP_ATTR_CIPHER_SUITE]) + params.cipher_suite = + nla_get_u32(tb[NL80211_NAN_NDP_ATTR_CIPHER_SUITE]); + + if (params.accept && tb[NL80211_NAN_NDP_ATTR_PMK]) { + if (!wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_SECURE_NAN)) { + NL_SET_ERR_MSG_ATTR(info->extack, + tb[NL80211_NAN_NDP_ATTR_PMK], + "PMK requires SECURE_NAN feature"); + return -EOPNOTSUPP; + } + memcpy(params.pmk, + nla_data(tb[NL80211_NAN_NDP_ATTR_PMK]), + NL80211_NAN_NDP_PMK_LEN); + params.pmk_set = true; + } + + if (params.accept && tb[NL80211_NAN_NDP_ATTR_GTK_REQUIRED]) { + if (!wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_SECURE_NAN)) { + NL_SET_ERR_MSG_ATTR(info->extack, + tb[NL80211_NAN_NDP_ATTR_GTK_REQUIRED], + "GTK_REQUIRED requires SECURE_NAN feature"); + return -EOPNOTSUPP; + } + + if (!tb[NL80211_NAN_NDP_ATTR_GTK_CIPHER]) { + NL_SET_ERR_MSG_ATTR(info->extack, + tb[NL80211_NAN_NDP_ATTR_GTK_REQUIRED], + "GTK_CIPHER is required when GTK_REQUIRED is set"); + return -EINVAL; + } + + params.gtk_required = true; + params.gtk_cipher = + nla_get_u32(tb[NL80211_NAN_NDP_ATTR_GTK_CIPHER]); + } else if (params.accept && tb[NL80211_NAN_NDP_ATTR_GTK_CIPHER]) { + NL_SET_ERR_MSG_ATTR(info->extack, + tb[NL80211_NAN_NDP_ATTR_GTK_CIPHER], + "GTK_CIPHER requires GTK_REQUIRED"); + return -EINVAL; + } + + if (tb[NL80211_NAN_NDP_ATTR_IPV6_INTF_ID]) { + memcpy(params.ipv6_intf_id, + nla_data(tb[NL80211_NAN_NDP_ATTR_IPV6_INTF_ID]), + NL80211_NAN_NDP_IPV6_INTF_ID_LEN); + params.ipv6_intf_id_set = true; + } + + params.ndpe = nla_get_flag(tb[NL80211_NAN_NDP_ATTR_NDPE]); + + return rdev_nan_ndp_respond(rdev, wdev, ¶ms); +} + static bool nl80211_nan_is_sched_empty(struct cfg80211_nan_local_sched *sched) { if (!sched->n_channels) @@ -21781,6 +21904,12 @@ static const struct genl_small_ops nl80211_small_ops[] = { .flags = GENL_ADMIN_PERM, .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), }, + { + .cmd = NL80211_CMD_NAN_NDP_RESPOND, + .doit = nl80211_nan_ndp_respond, + .flags = GENL_ADMIN_PERM, + .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), + }, }; static struct genl_family nl80211_fam __ro_after_init = { diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index dcf2a596a574..3fbc5b701768 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -1199,6 +1199,22 @@ rdev_nan_ndp_initiate(struct cfg80211_registered_device *rdev, return ret; } +static inline int +rdev_nan_ndp_respond(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct cfg80211_nan_ndp_respond *params) +{ + int ret; + + if (!rdev->ops->nan_ndp_respond) + return -EOPNOTSUPP; + + trace_rdev_nan_ndp_respond(&rdev->wiphy, wdev, params); + ret = rdev->ops->nan_ndp_respond(&rdev->wiphy, wdev, params); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + static inline int rdev_set_mac_acl(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_acl_data *params) diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 0da1c159315b..f937941c6076 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -2574,6 +2574,30 @@ TRACE_EVENT(rdev_nan_ndp_initiate, __entry->peer_nmi, __entry->publish_id) ); +TRACE_EVENT(rdev_nan_ndp_respond, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_nan_ndp_respond *params), + TP_ARGS(wiphy, wdev, params), + TP_STRUCT__entry( + WIPHY_ENTRY + WDEV_ENTRY + __array(u8, peer_nmi, ETH_ALEN) + __field(u8, ndp_id) + __field(bool, accept) + ), + TP_fast_assign( + WIPHY_ASSIGN; + WDEV_ASSIGN; + memcpy(__entry->peer_nmi, params->peer_nmi, ETH_ALEN); + __entry->ndp_id = params->ndp_id; + __entry->accept = params->accept; + ), + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT + ", peer: %pM, ndp_id: %u, accept: %d", + WIPHY_PR_ARG, WDEV_PR_ARG, + __entry->peer_nmi, __entry->ndp_id, __entry->accept) +); + TRACE_EVENT(rdev_set_mac_acl, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, struct cfg80211_acl_data *params), -- 2.34.1