static int nl80211_nan_add_func(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[NUM_NL80211_NAN_FUNC_ATTR], *func_attr;
	struct cfg80211_nan_func *func;
	struct sk_buff *msg = NULL;
	void *hdr = NULL;
	int err = 0;

	if (wdev->iftype != NL80211_IFTYPE_NAN)
		return -EOPNOTSUPP;

	if (!wdev_running(wdev))
		return -ENOTCONN;

	if (!info->attrs[NL80211_ATTR_NAN_FUNC])
		return -EINVAL;

	err = nla_parse_nested_deprecated(tb, NL80211_NAN_FUNC_ATTR_MAX,
					  info->attrs[NL80211_ATTR_NAN_FUNC],
					  nl80211_nan_func_policy,
					  info->extack);
	if (err)
		return err;

	func = kzalloc(sizeof(*func), GFP_KERNEL);
	if (!func)
		return -ENOMEM;

	func->cookie = cfg80211_assign_cookie(rdev);

	if (!tb[NL80211_NAN_FUNC_TYPE] ||
	    nla_get_u8(tb[NL80211_NAN_FUNC_TYPE]) > NL80211_NAN_FUNC_MAX_TYPE) {
		err = -EINVAL;
		goto out;
	}


	func->type = nla_get_u8(tb[NL80211_NAN_FUNC_TYPE]);

	if (!tb[NL80211_NAN_FUNC_SERVICE_ID]) {
		err = -EINVAL;
		goto out;
	}

	memcpy(func->service_id, nla_data(tb[NL80211_NAN_FUNC_SERVICE_ID]),
	       sizeof(func->service_id));

	func->close_range =
		nla_get_flag(tb[NL80211_NAN_FUNC_CLOSE_RANGE]);

	if (tb[NL80211_NAN_FUNC_SERVICE_INFO]) {
		func->serv_spec_info_len =
			nla_len(tb[NL80211_NAN_FUNC_SERVICE_INFO]);
		func->serv_spec_info =
			kmemdup(nla_data(tb[NL80211_NAN_FUNC_SERVICE_INFO]),
				func->serv_spec_info_len,
				GFP_KERNEL);
		if (!func->serv_spec_info) {
			err = -ENOMEM;
			goto out;
		}
	}

	if (tb[NL80211_NAN_FUNC_TTL])
		func->ttl = nla_get_u32(tb[NL80211_NAN_FUNC_TTL]);

	switch (func->type) {
	case NL80211_NAN_FUNC_PUBLISH:
		if (!tb[NL80211_NAN_FUNC_PUBLISH_TYPE]) {
			err = -EINVAL;
			goto out;
		}

		func->publish_type =
			nla_get_u8(tb[NL80211_NAN_FUNC_PUBLISH_TYPE]);
		func->publish_bcast =
			nla_get_flag(tb[NL80211_NAN_FUNC_PUBLISH_BCAST]);

		if ((!(func->publish_type & NL80211_NAN_SOLICITED_PUBLISH)) &&
			func->publish_bcast) {
			err = -EINVAL;
			goto out;
		}
		break;
	case NL80211_NAN_FUNC_SUBSCRIBE:
		func->subscribe_active =
			nla_get_flag(tb[NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE]);
		break;
	case NL80211_NAN_FUNC_FOLLOW_UP:
		if (!tb[NL80211_NAN_FUNC_FOLLOW_UP_ID] ||
		    !tb[NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID] ||
		    !tb[NL80211_NAN_FUNC_FOLLOW_UP_DEST]) {
			err = -EINVAL;
			goto out;
		}

		func->followup_id =
			nla_get_u8(tb[NL80211_NAN_FUNC_FOLLOW_UP_ID]);
		func->followup_reqid =
			nla_get_u8(tb[NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID]);
		memcpy(func->followup_dest.addr,
		       nla_data(tb[NL80211_NAN_FUNC_FOLLOW_UP_DEST]),
		       sizeof(func->followup_dest.addr));
		if (func->ttl) {
			err = -EINVAL;
			goto out;
		}
		break;
	default:
		err = -EINVAL;
		goto out;
	}

	if (tb[NL80211_NAN_FUNC_SRF]) {
		struct nlattr *srf_tb[NUM_NL80211_NAN_SRF_ATTR];

		err = nla_parse_nested_deprecated(srf_tb,
						  NL80211_NAN_SRF_ATTR_MAX,
						  tb[NL80211_NAN_FUNC_SRF],
						  nl80211_nan_srf_policy,
						  info->extack);
		if (err)
			goto out;

		func->srf_include =
			nla_get_flag(srf_tb[NL80211_NAN_SRF_INCLUDE]);

		if (srf_tb[NL80211_NAN_SRF_BF]) {
			if (srf_tb[NL80211_NAN_SRF_MAC_ADDRS] ||
			    !srf_tb[NL80211_NAN_SRF_BF_IDX]) {
				err = -EINVAL;
				goto out;
			}

			func->srf_bf_len =
				nla_len(srf_tb[NL80211_NAN_SRF_BF]);
			func->srf_bf =
				kmemdup(nla_data(srf_tb[NL80211_NAN_SRF_BF]),
					func->srf_bf_len, GFP_KERNEL);
			if (!func->srf_bf) {
				err = -ENOMEM;
				goto out;
			}

			func->srf_bf_idx =
				nla_get_u8(srf_tb[NL80211_NAN_SRF_BF_IDX]);
		} else {
			struct nlattr *attr, *mac_attr =
				srf_tb[NL80211_NAN_SRF_MAC_ADDRS];
			int n_entries, rem, i = 0;

			if (!mac_attr) {
				err = -EINVAL;
				goto out;
			}

			n_entries = validate_acl_mac_addrs(mac_attr);
			if (n_entries <= 0) {
				err = -EINVAL;
				goto out;
			}

			func->srf_num_macs = n_entries;
			func->srf_macs =
				kcalloc(n_entries, sizeof(*func->srf_macs),
					GFP_KERNEL);
			if (!func->srf_macs) {
				err = -ENOMEM;
				goto out;
			}

			nla_for_each_nested(attr, mac_attr, rem)
				memcpy(func->srf_macs[i++].addr, nla_data(attr),
				       sizeof(*func->srf_macs));
		}
	}

	if (tb[NL80211_NAN_FUNC_TX_MATCH_FILTER]) {
		err = handle_nan_filter(tb[NL80211_NAN_FUNC_TX_MATCH_FILTER],
					func, true);
		if (err)
			goto out;
	}

	if (tb[NL80211_NAN_FUNC_RX_MATCH_FILTER]) {
		err = handle_nan_filter(tb[NL80211_NAN_FUNC_RX_MATCH_FILTER],
					func, false);
		if (err)
			goto out;
	}

	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
	if (!msg) {
		err = -ENOMEM;
		goto out;
	}

	hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
			     NL80211_CMD_ADD_NAN_FUNCTION);
	/* This can't really happen - we just allocated 4KB */
	if (WARN_ON(!hdr)) {
		err = -ENOMEM;
		goto out;
	}

	err = rdev_add_nan_func(rdev, wdev, func);
out:
	if (err < 0) {
		cfg80211_free_nan_func(func);
		nlmsg_free(msg);
		return err;
	}

	/* propagate the instance id and cookie to userspace  */
	if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, func->cookie,
			      NL80211_ATTR_PAD))
		goto nla_put_failure;

	func_attr = nla_nest_start_noflag(msg, NL80211_ATTR_NAN_FUNC);
	if (!func_attr)
		goto nla_put_failure;

	if (nla_put_u8(msg, NL80211_NAN_FUNC_INSTANCE_ID,
		       func->instance_id))
		goto nla_put_failure;

	nla_nest_end(msg, func_attr);

	genlmsg_end(msg, hdr);
	return genlmsg_reply(msg, info);

nla_put_failure:
	nlmsg_free(msg);
	return -ENOBUFS;
}