Linux wireless drivers development
 help / color / mirror / Atom feed
From: Yue Sun <samsun1006219@gmail.com>
To: Johannes Berg <johannes@sipsolutions.net>
Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [BUG REPORT] wifi: mac80211: userspace can trigger WARN_ON in ieee80211_add_nan_func()
Date: Tue, 30 Jun 2026 16:57:15 +0800	[thread overview]
Message-ID: <20260630085719.3313-1-samsun1006219@gmail.com> (raw)

Hello,

I can reproduce a kernel warning in ieee80211_add_nan_func() on current
upstream master. The reproducer uses mac80211_hwsim to create a NAN-capable
radio, starts a NAN interface, and then sends NL80211_CMD_ADD_NAN_FUNCTION.

Summary
-------

The hwsim radio created with HWSIM_ATTR_SUPPORT_NAN_DEVICE advertises
WIPHY_NAN_FLAGS_USERSPACE_DE:

  drivers/net/wireless/virtual/mac80211_hwsim_main.c:
    hw->wiphy->nan_capa.flags = WIPHY_NAN_FLAGS_CONFIGURABLE_SYNC |
                                WIPHY_NAN_FLAGS_USERSPACE_DE;

nl80211_nan_add_func() validates the NAN function and calls
rdev_add_nan_func() without rejecting devices that have
WIPHY_NAN_FLAGS_USERSPACE_DE set.

mac80211 then reaches this invariant check:

  net/mac80211/cfg.c:
    if (WARN_ON(wiphy->nan_capa.flags & WIPHY_NAN_FLAGS_USERSPACE_DE))
            return -EOPNOTSUPP;

So an ordinary nl80211 request against such a wiphy can trigger a kernel
WARNING. Systems with panic_on_warn=1 would panic.

Likely fix direction
--------------------

Reject NL80211_CMD_ADD_NAN_FUNCTION (and likely DEL_NAN_FUNCTION) in nl80211
when wiphy->nan_capa.flags has WIPHY_NAN_FLAGS_USERSPACE_DE set, or otherwise
turn the mac80211 WARN_ON into a non-warning error return if this path is
expected to be reachable from userspace input.

Tested kernel:
- tree: /home/sy/linux
- branch: master
- HEAD: dc59e4fea9d83f03bad6bddf3fa2e52491777482
- uname in guest: 7.2.0-rc1-dirty #15 PREEMPT(full)
- bzImage: /home/sy/linux/arch/x86/boot/bzImage mtime=2026-06-29T12:23:31+00:00
- vmlinux: /home/sy/linux/vmlinux mtime=2026-06-29T12:22:03+00:00

Reproducer stdout
-----------------

candidate=round3i_hwsim_nan_wdev_no_panic_on_warn
panic_on_warn_disabled=1
nl80211_family=38
create_hwsim_nan_radio=2
wiphy_dump=0 count=4
create_nan_iface(wiphy=3)=0 name=klrnan9303
created_ifindex=0
create_nan_iface(wiphy=2)=-95 name=klrnan9303
create_nan_iface(wiphy=1)=-95 name=klrnan9303
create_nan_iface(wiphy=0)=-95 name=klrnan9303
post_nan_create_iface_dump=0 count=4
nan_iface ifindex=0 wdev=0x300000002 iftype=12
start_nan(ifindex=0 wdev=0x300000002 iftype=12)=0
add_nan_function_256_empty_tx_filters(ifindex=0 wdev=0x300000002 iftype=12)=-95

Crash log
---------

[   55.935664] ------------[ cut here ]------------
[   55.936899] wiphy->nan_capa.flags & WIPHY_NAN_FLAGS_USERSPACE_DE
[   55.936917] WARNING: net/mac80211/cfg.c:501 at ieee80211_add_nan_func+0x755/0x8f0, CPU#1: poc/9303
[   55.940025] Modules linked in:
[   55.941407] CPU: 1 UID: 0 PID: 9303 Comm: poc Not tainted 7.2.0-rc1-dirty #15 PREEMPT(full) 
[   55.943097] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
[   55.944742] RIP: 0010:ieee80211_add_nan_func+0x755/0x8f0
[   55.945943] Code: c6 e8 0f 3e c6 f6 84 db 0f 85 91 fc ff ff e8 32 44 c6 f6 48 8d 3d 0b 2b ef 05 67 48 0f b9 3a e9 7b fc ff ff e8 1c 44 c6 f6 90 <0f> 0b 90 41 bc a1 ff ff ff e9 2e fd ff ff e8 08 44 c6 f6 90 0f 0b
[   55.949426] RSP: 0018:ffffc90000627358 EFLAGS: 00010293
[   55.951038] RAX: 0000000000000000 RBX: ffff8880489f0010 RCX: ffffffff8afaaeac
[   55.952473] RDX: ffff888023033b80 RSI: ffffffff8afab504 RDI: 0000000000000005
[   55.953912] RBP: ffff8880271bbb00 R08: 0000000000000000 R09: ffffed100913e15a
[   55.955373] R10: 0000000000000002 R11: ffffffff81000130 R12: 0000000000000002
[   55.956808] R13: 0000000000000001 R14: ffff888037cf0740 R15: ffff8880489f1e10
[   55.958248] FS:  00000000092123c0(0000) GS:ffff8880d683e000(0000) knlGS:0000000000000000
[   55.959860] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[   55.961120] CR2: 00007ff4e81986f4 CR3: 000000004a924000 CR4: 00000000000006f0
[   55.962563] Call Trace:
[   55.963177]  <TASK>
[   55.963726]  ? genlmsg_put+0x265/0x2e0
[   55.964608]  nl80211_nan_add_func+0x10a3/0x1990
[   55.965630]  ? __mutex_lock+0x27a/0x1d80
[   55.966457]  ? __pfx_nl80211_nan_add_func+0x10/0x10
[   55.967442]  ? __rtnl_unlock+0x68/0xf0
[   55.968242]  ? netdev_run_todo+0x9e1/0x14f0
[   55.969112]  ? __pfx___mutex_lock+0x10/0x10
[   55.969979]  ? mutex_is_locked+0x17/0x60
[   55.970888]  ? nl80211_pre_doit+0x120/0xa70
[   55.971778]  genl_family_rcv_msg_doit+0x1ff/0x2f0
[   55.972740]  ? __pfx_genl_family_rcv_msg_doit+0x10/0x10
[   55.973802]  ? bpf_lsm_capable+0x9/0x10
[   55.974613]  ? security_capable+0x210/0x250
[   55.975494]  genl_rcv_msg+0x532/0x7e0
[   55.976274]  ? __pfx_genl_rcv_msg+0x10/0x10
[   55.977147]  ? __pfx_nl80211_pre_doit+0x10/0x10
[   55.978024]  ? __pfx_nl80211_nan_add_func+0x10/0x10
[   55.978877]  ? __pfx_nl80211_post_doit+0x10/0x10
[   55.979700]  ? __lock_acquire+0x476/0x2420
[   55.980482]  netlink_rcv_skb+0x147/0x430
[   55.981216]  ? __pfx_genl_rcv_msg+0x10/0x10
[   55.981962]  ? __pfx_netlink_rcv_skb+0x10/0x10
[   55.982762]  ? netlink_deliver_tap+0x1ae/0xd10
[   55.983557]  genl_rcv+0x28/0x40
[   55.984168]  netlink_unicast+0x58d/0x850
[   55.984893]  ? __pfx_netlink_unicast+0x10/0x10
[   55.985698]  netlink_sendmsg+0x88d/0xd90
[   55.986425]  ? __pfx_netlink_sendmsg+0x10/0x10
[   55.987224]  ? __pfx_netlink_sendmsg+0x10/0x10
[   55.988018]  ____sys_sendmsg+0xa27/0xb90
[   55.988758]  ? __pfx_____sys_sendmsg+0x10/0x10
[   55.989555]  ? __pfx_copy_msghdr_from_user+0x10/0x10
[   55.990335]  ___sys_sendmsg+0x11c/0x1b0
[   55.991041]  ? __pfx____sys_sendmsg+0x10/0x10
[   55.991720]  ? selinux_file_permission+0x127/0x660
[   55.992473]  ? bpf_lsm_file_permission+0x9/0x10
[   55.993181]  ? security_file_permission+0x75/0x230
[   55.993921]  ? rw_verify_area+0xcf/0x6e0
[   55.994559]  ? vfs_write+0x169/0x1150
[   55.995167]  ? __pfx_anon_pipe_write+0x10/0x10
[   55.995859]  ? __pfx_vfs_write+0x10/0x10
[   55.996505]  __sys_sendmsg+0x142/0x1f0
[   55.997115]  ? __pfx___sys_sendmsg+0x10/0x10
[   55.997796]  do_syscall_64+0x11f/0x860
[   55.998410]  entry_SYSCALL_64_after_hwframe+0x77/0x7f
[   55.999178] RIP: 0033:0x45a037
[   55.999693] Code: ff ff f7 d8 64 89 02 48 c7 c0 ff ff ff ff eb b9 0f 1f 00 f3 0f 1e fa 64 8b 04 25 18 00 00 00 85 c0 75 10 b8 2e 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 51 c3 48 83 ec 28 89 54 24 1c 48 89 74 24 10
[   56.002154] RSP: 002b:00007ffcef8a8468 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
[   56.003176] RAX: ffffffffffffffda RBX: 00007ffcef8acca8 RCX: 000000000045a037
[   56.004138] RDX: 0000000000000000 RSI: 00007ffcef8a84a0 RDI: 0000000000000003
[   56.005118] RBP: 00007ffcef8a84e0 R08: 0000000000000000 R09: 0000000000000000
[   56.006087] R10: 000000000000000a R11: 0000000000000246 R12: 0000000000000001
[   56.007050] R13: 00007ffcef8acc98 R14: 00000000004cc7d0 R15: 0000000000000001
[   56.008023]  </TASK>
[   56.008404] irq event stamp: 21493
[   56.008924] hardirqs last  enabled at (21501): [<ffffffff819e796e>] __up_console_sem+0xae/0xc0
[   56.010081] hardirqs last disabled at (21508): [<ffffffff819e7953>] __up_console_sem+0x93/0xc0
[   56.011259] softirqs last  enabled at (21458): [<ffffffff817ed4f2>] __irq_exit_rcu+0x172/0x220
[   56.012406] softirqs last disabled at (21439): [<ffffffff817ed4f2>] __irq_exit_rcu+0x172/0x220
[   56.013560] ---[ end trace 0000000000000000 ]---

Warning: Permanently added '[127.0.0.1]:18122' (ED25519) to the list of known hosts.

PoC: poc.c
----------

#define _GNU_SOURCE
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/genetlink.h>
#include <linux/netlink.h>
#include <linux/nl80211.h>
#include <net/if.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>

#ifndef NLA_TYPE_MASK
#define NLA_TYPE_MASK 0x3fff
#endif
#ifndef NLA_HDRLEN
#define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr)))
#endif
#ifndef GENL_HDRLEN
#define GENL_HDRLEN NLMSG_ALIGN(sizeof(struct genlmsghdr))
#endif

#define BUF_SZ 16384
#define MAX_WIPHYS 32
#define MAX_IFS 64

#define HWSIM_CMD_NEW_RADIO 4
#define HWSIM_ATTR_CHANNELS 9
#define HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE 16
#define HWSIM_ATTR_RADIO_NAME 17
#define HWSIM_ATTR_SUPPORT_NAN_DEVICE 30

struct msg {
	char buf[BUF_SZ];
	size_t len;
};

struct if_info {
	int ifindex;
	int iftype;
	uint64_t wdev;
};

struct dump_state {
	int vals[MAX_WIPHYS];
	int count;
};

struct if_dump_state {
	struct if_info ifs[MAX_IFS];
	int count;
};

static int seqno = 100;

static void disable_panic_on_warn(void)
{
	int fd = open("/proc/sys/kernel/panic_on_warn", O_WRONLY | O_CLOEXEC);

	if (fd < 0) {
		printf("panic_on_warn_open=%d\n", -errno);
		return;
	}
	if (write(fd, "0\n", 2) != 2)
		printf("panic_on_warn_write=%d\n", -errno);
	else
		printf("panic_on_warn_disabled=1\n");
	close(fd);
}

static int nla_ok(const struct nlattr *nla, int rem)
{
	return rem >= (int)sizeof(*nla) &&
	       nla->nla_len >= sizeof(*nla) &&
	       nla->nla_len <= rem;
}

static int nla_type(const struct nlattr *nla)
{
	return nla->nla_type & NLA_TYPE_MASK;
}

static void *nla_data(const struct nlattr *nla)
{
	return (char *)nla + NLA_HDRLEN;
}

static int nla_payload(const struct nlattr *nla)
{
	return nla->nla_len - NLA_HDRLEN;
}

static void parse_attrs(struct nlattr **tb, int max, void *data, int len)
{
	struct nlattr *nla = data;
	memset(tb, 0, sizeof(*tb) * (max + 1));
	while (nla_ok(nla, len)) {
		int type = nla_type(nla);
		if (type <= max)
			tb[type] = nla;
		len -= NLA_ALIGN(nla->nla_len);
		nla = (struct nlattr *)((char *)nla + NLA_ALIGN(nla->nla_len));
	}
}

static void msg_init(struct msg *m, int family, int flags, int cmd)
{
	struct nlmsghdr *nlh;
	struct genlmsghdr *gh;

	memset(m, 0, sizeof(*m));
	nlh = (struct nlmsghdr *)m->buf;
	nlh->nlmsg_type = family;
	nlh->nlmsg_flags = flags;
	nlh->nlmsg_seq = seqno++;
	nlh->nlmsg_pid = 0;
	nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
	gh = (struct genlmsghdr *)NLMSG_DATA(nlh);
	gh->cmd = cmd;
	gh->version = 0;
	m->len = nlh->nlmsg_len;
}

static int add_attr(struct msg *m, int type, const void *data, int len)
{
	struct nlmsghdr *nlh = (struct nlmsghdr *)m->buf;
	struct nlattr *nla;
	size_t need = NLA_HDRLEN + len;
	size_t aligned = NLA_ALIGN(need);

	if (m->len + aligned > sizeof(m->buf))
		return -1;
	nla = (struct nlattr *)(m->buf + m->len);
	nla->nla_type = type;
	nla->nla_len = need;
	if (len > 0 && data)
		memcpy((char *)nla + NLA_HDRLEN, data, len);
	if (aligned > need)
		memset((char *)nla + need, 0, aligned - need);
	m->len += aligned;
	nlh->nlmsg_len = m->len;
	return 0;
}

static int add_u8(struct msg *m, int type, uint8_t val)
{
	return add_attr(m, type, &val, sizeof(val));
}

static int add_u32(struct msg *m, int type, uint32_t val)
{
	return add_attr(m, type, &val, sizeof(val));
}

static int add_u64(struct msg *m, int type, uint64_t val)
{
	return add_attr(m, type, &val, sizeof(val));
}

static int nest_start(struct msg *m, int type)
{
	struct nlmsghdr *nlh = (struct nlmsghdr *)m->buf;
	struct nlattr *nla;
	size_t off = m->len;

	if (m->len + NLA_HDRLEN > sizeof(m->buf))
		return -1;
	nla = (struct nlattr *)(m->buf + m->len);
	nla->nla_type = type | NLA_F_NESTED;
	nla->nla_len = NLA_HDRLEN;
	m->len += NLA_HDRLEN;
	nlh->nlmsg_len = m->len;
	return (int)off;
}

static void nest_end(struct msg *m, int off)
{
	struct nlmsghdr *nlh = (struct nlmsghdr *)m->buf;
	struct nlattr *nla = (struct nlattr *)(m->buf + off);

	nla->nla_len = m->len - off;
	m->len = NLA_ALIGN(m->len);
	nlh->nlmsg_len = m->len;
}

static int nl_sock(void)
{
	struct sockaddr_nl sa;
	struct timeval tv = { .tv_sec = 3, .tv_usec = 0 };
	int fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_GENERIC);
	if (fd < 0)
		return -1;
	setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
	memset(&sa, 0, sizeof(sa));
	sa.nl_family = AF_NETLINK;
	if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
		close(fd);
		return -1;
	}
	return fd;
}

static int send_msg(int fd, struct msg *m)
{
	struct sockaddr_nl sa;
	struct iovec iov;
	struct msghdr mh;

	memset(&sa, 0, sizeof(sa));
	sa.nl_family = AF_NETLINK;
	iov.iov_base = m->buf;
	iov.iov_len = ((struct nlmsghdr *)m->buf)->nlmsg_len;
	memset(&mh, 0, sizeof(mh));
	mh.msg_name = &sa;
	mh.msg_namelen = sizeof(sa);
	mh.msg_iov = &iov;
	mh.msg_iovlen = 1;
	return sendmsg(fd, &mh, 0);
}

static int recv_genl(int fd, int seq, bool dump,
		     void (*cb)(struct nlmsghdr *, void *), void *arg)
{
	char rbuf[BUF_SZ];

	for (;;) {
		ssize_t n = recv(fd, rbuf, sizeof(rbuf), 0);
		struct nlmsghdr *nlh;
		int rem;

		if (n < 0)
			return -errno;
		rem = (int)n;
		for (nlh = (struct nlmsghdr *)rbuf; NLMSG_OK(nlh, rem);
		     nlh = NLMSG_NEXT(nlh, rem)) {
			if (nlh->nlmsg_seq != (unsigned)seq)
				continue;
			if (nlh->nlmsg_type == NLMSG_ERROR) {
				struct nlmsgerr *err = NLMSG_DATA(nlh);
				if (err->error)
					return err->error;
				return 0;
			}
			if (nlh->nlmsg_type == NLMSG_DONE)
				return 0;
			if (cb)
				cb(nlh, arg);
			if (!dump)
				return 0;
		}
	}
}

static int transact(int fd, struct msg *m, bool dump,
		    void (*cb)(struct nlmsghdr *, void *), void *arg)
{
	int seq = ((struct nlmsghdr *)m->buf)->nlmsg_seq;
	if (send_msg(fd, m) < 0)
		return -errno;
	return recv_genl(fd, seq, dump, cb, arg);
}

static void family_cb(struct nlmsghdr *nlh, void *arg)
{
	int *family = arg;
	struct genlmsghdr *gh = NLMSG_DATA(nlh);
	struct nlattr *tb[CTRL_ATTR_MAX + 1];
	int len = nlh->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN);

	if (len < 0)
		return;
	parse_attrs(tb, CTRL_ATTR_MAX, (char *)gh + GENL_HDRLEN, len);
	if (tb[CTRL_ATTR_FAMILY_ID] &&
	    nla_payload(tb[CTRL_ATTR_FAMILY_ID]) >= 2)
		*family = *(uint16_t *)nla_data(tb[CTRL_ATTR_FAMILY_ID]);
}

static int resolve_family(int fd, const char *name)
{
	struct msg m;
	int family = -1;

	msg_init(&m, GENL_ID_CTRL, NLM_F_REQUEST, CTRL_CMD_GETFAMILY);
	add_attr(&m, CTRL_ATTR_FAMILY_NAME, name, strlen(name) + 1);
	if (transact(fd, &m, false, family_cb, &family) < 0)
		return -1;
	return family;
}

static int resolve_nl80211(int fd)
{
	return resolve_family(fd, "nl80211");
}

static int create_hwsim_nan_radio(int fd)
{
	struct msg m;
	int family = resolve_family(fd, "MAC80211_HWSIM");
	uint32_t channels = 1;
	char name[32];

	if (family < 0)
		return -ENOENT;

	snprintf(name, sizeof(name), "klr-nan-%d", getpid() % 10000);
	msg_init(&m, family, NLM_F_REQUEST | NLM_F_ACK, HWSIM_CMD_NEW_RADIO);
	add_u32(&m, HWSIM_ATTR_CHANNELS, channels);
	add_attr(&m, HWSIM_ATTR_RADIO_NAME, name, strlen(name) + 1);
	add_attr(&m, HWSIM_ATTR_SUPPORT_NAN_DEVICE, NULL, 0);
	add_attr(&m, HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE, NULL, 0);
	return transact(fd, &m, false, NULL, NULL);
}

static void wiphy_cb(struct nlmsghdr *nlh, void *arg)
{
	struct dump_state *st = arg;
	struct genlmsghdr *gh = NLMSG_DATA(nlh);
	struct nlattr *tb[NL80211_ATTR_MAX + 1];
	int len = nlh->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN);
	int wiphy, i;

	if (len < 0)
		return;
	parse_attrs(tb, NL80211_ATTR_MAX, (char *)gh + GENL_HDRLEN, len);
	if (!tb[NL80211_ATTR_WIPHY] ||
	    nla_payload(tb[NL80211_ATTR_WIPHY]) < 4)
		return;
	wiphy = *(uint32_t *)nla_data(tb[NL80211_ATTR_WIPHY]);
	for (i = 0; i < st->count; i++)
		if (st->vals[i] == wiphy)
			return;
	if (st->count < MAX_WIPHYS)
		st->vals[st->count++] = wiphy;
}

static int dump_wiphys(int fd, int family, struct dump_state *st)
{
	struct msg m;
	memset(st, 0, sizeof(*st));
	msg_init(&m, family, NLM_F_REQUEST | NLM_F_DUMP, NL80211_CMD_GET_WIPHY);
	return transact(fd, &m, true, wiphy_cb, st);
}

static void iface_cb(struct nlmsghdr *nlh, void *arg)
{
	struct if_dump_state *st = arg;
	struct genlmsghdr *gh = NLMSG_DATA(nlh);
	struct nlattr *tb[NL80211_ATTR_MAX + 1];
	int len = nlh->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN);
	int iftype, ifindex = 0;
	uint64_t wdev = 0;

	if (len < 0)
		return;
	parse_attrs(tb, NL80211_ATTR_MAX, (char *)gh + GENL_HDRLEN, len);
	if (!tb[NL80211_ATTR_IFTYPE] ||
	    nla_payload(tb[NL80211_ATTR_IFTYPE]) < 4)
		return;
	iftype = *(uint32_t *)nla_data(tb[NL80211_ATTR_IFTYPE]);
	if (tb[NL80211_ATTR_IFINDEX] &&
	    nla_payload(tb[NL80211_ATTR_IFINDEX]) >= 4)
		ifindex = *(uint32_t *)nla_data(tb[NL80211_ATTR_IFINDEX]);
	if (tb[NL80211_ATTR_WDEV] &&
	    nla_payload(tb[NL80211_ATTR_WDEV]) >= 8)
		wdev = *(uint64_t *)nla_data(tb[NL80211_ATTR_WDEV]);
	if (st->count >= MAX_IFS)
		return;
	st->ifs[st->count].ifindex = ifindex;
	st->ifs[st->count].iftype = iftype;
	st->ifs[st->count].wdev = wdev;
	st->count++;
}

static int dump_ifaces(int fd, int family, struct if_dump_state *st)
{
	struct msg m;
	memset(st, 0, sizeof(*st));
	msg_init(&m, family, NLM_F_REQUEST | NLM_F_DUMP, NL80211_CMD_GET_INTERFACE);
	return transact(fd, &m, true, iface_cb, st);
}

static int create_nan_iface(int fd, int family, int wiphy, char *ifname,
			    size_t ifname_len)
{
	struct msg m;
	int ret;

	snprintf(ifname, ifname_len, "klrnan%d", getpid() % 10000);
	msg_init(&m, family, NLM_F_REQUEST | NLM_F_ACK, NL80211_CMD_NEW_INTERFACE);
	add_u32(&m, NL80211_ATTR_WIPHY, wiphy);
	add_attr(&m, NL80211_ATTR_IFNAME, ifname, strlen(ifname) + 1);
	add_u32(&m, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_NAN);
	ret = transact(fd, &m, false, NULL, NULL);
	return ret;
}

static int create_station_iface(int fd, int family, int wiphy, char *ifname,
				size_t ifname_len)
{
	struct msg m;
	int ret;

	snprintf(ifname, ifname_len, "klrwlan%d", getpid() % 10000);
	msg_init(&m, family, NLM_F_REQUEST | NLM_F_ACK, NL80211_CMD_NEW_INTERFACE);
	add_u32(&m, NL80211_ATTR_WIPHY, wiphy);
	add_attr(&m, NL80211_ATTR_IFNAME, ifname, strlen(ifname) + 1);
	add_u32(&m, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_STATION);
	ret = transact(fd, &m, false, NULL, NULL);
	return ret;
}

static void add_wdev_selector(struct msg *m, int ifindex, uint64_t wdev)
{
	if (wdev)
		add_u64(m, NL80211_ATTR_WDEV, wdev);
	else
		add_u32(m, NL80211_ATTR_IFINDEX, ifindex);
}

static int start_nan(int fd, int family, int ifindex, uint64_t wdev)
{
	struct msg m;
	uint8_t pref = 2;

	msg_init(&m, family, NLM_F_REQUEST | NLM_F_ACK, NL80211_CMD_START_NAN);
	add_wdev_selector(&m, ifindex, wdev);
	add_u8(&m, NL80211_ATTR_NAN_MASTER_PREF, pref);
	return transact(fd, &m, false, NULL, NULL);
}

static int set_if_up(int ifindex)
{
	struct ifreq ifr;
	int s, ret;
	char name[IFNAMSIZ];

	if (!if_indextoname(ifindex, name))
		return -errno;
	s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
	if (s < 0)
		return -errno;
	memset(&ifr, 0, sizeof(ifr));
	strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name) - 1);
	ret = ioctl(s, SIOCGIFFLAGS, &ifr);
	if (ret < 0) {
		ret = -errno;
		close(s);
		return ret;
	}
	ifr.ifr_flags |= IFF_UP;
	ret = ioctl(s, SIOCSIFFLAGS, &ifr);
	if (ret < 0)
		ret = -errno;
	else
		ret = 0;
	close(s);
	return ret;
}

static int add_nan_func_overflow(int fd, int family, int ifindex, uint64_t wdev)
{
	struct msg m;
	uint8_t service_id[NL80211_NAN_FUNC_SERVICE_ID_LEN] = {0};
	uint8_t type = NL80211_NAN_FUNC_SUBSCRIBE;
	int func, filt, i;

	msg_init(&m, family, NLM_F_REQUEST, NL80211_CMD_ADD_NAN_FUNCTION);
	add_wdev_selector(&m, ifindex, wdev);

	func = nest_start(&m, NL80211_ATTR_NAN_FUNC);
	if (func < 0)
		return -EMSGSIZE;
	add_u8(&m, NL80211_NAN_FUNC_TYPE, type);
	add_attr(&m, NL80211_NAN_FUNC_SERVICE_ID, service_id, sizeof(service_id));

	filt = nest_start(&m, NL80211_NAN_FUNC_TX_MATCH_FILTER);
	if (filt < 0)
		return -EMSGSIZE;
	for (i = 0; i < 256; i++) {
		if (add_attr(&m, i + 1, NULL, 0) < 0)
			return -EMSGSIZE;
	}
	nest_end(&m, filt);
	nest_end(&m, func);

	return transact(fd, &m, false, NULL, NULL);
}

static void try_one_if(int fd, int family, struct if_info info)
{
	int ret;

	if (info.ifindex > 0) {
		ret = set_if_up(info.ifindex);
		printf("set_if_up(ifindex=%d)=%d\n", info.ifindex, ret);
		sleep(1);
	}
	ret = start_nan(fd, family, info.ifindex, info.wdev);
	printf("start_nan(ifindex=%d wdev=0x%llx iftype=%d)=%d\n",
	       info.ifindex, (unsigned long long)info.wdev, info.iftype, ret);
	ret = add_nan_func_overflow(fd, family, info.ifindex, info.wdev);
	printf("add_nan_function_256_empty_tx_filters(ifindex=%d wdev=0x%llx iftype=%d)=%d\n",
	       info.ifindex, (unsigned long long)info.wdev, info.iftype, ret);
}

int main(void)
{
	struct dump_state wiphys;
	struct if_dump_state ifs;
	int fd, family, ret, i;

	setvbuf(stdout, NULL, _IONBF, 0);
	printf("candidate=round3i_hwsim_nan_wdev_no_panic_on_warn\n");
	disable_panic_on_warn();

	fd = nl_sock();
	if (fd < 0) {
		perror("netlink");
		return 1;
	}
	family = resolve_nl80211(fd);
	if (family < 0) {
		printf("nl80211 family not available\n");
		return 0;
	}
	printf("nl80211_family=%d\n", family);

	ret = create_hwsim_nan_radio(fd);
	printf("create_hwsim_nan_radio=%d\n", ret);
	sleep(2);

	ret = dump_wiphys(fd, family, &wiphys);
	printf("wiphy_dump=%d count=%d\n", ret, wiphys.count);
	for (i = 0; i < wiphys.count; i++) {
		char ifname[IFNAMSIZ];
		int ifindex;

		ret = create_nan_iface(fd, family, wiphys.vals[i], ifname,
				       sizeof(ifname));
		printf("create_nan_iface(wiphy=%d)=%d name=%s\n",
		       wiphys.vals[i], ret, ifname);
		if (ret < 0)
			continue;
		sleep(1);
		ifindex = if_nametoindex(ifname);
		printf("created_ifindex=%d\n", ifindex);
	}

	ret = dump_ifaces(fd, family, &ifs);
	printf("post_nan_create_iface_dump=%d count=%d\n", ret, ifs.count);
	for (i = 0; i < ifs.count; i++) {
		if (ifs.ifs[i].iftype != NL80211_IFTYPE_NAN)
			continue;
		printf("nan_iface ifindex=%d wdev=0x%llx iftype=%d\n",
		       ifs.ifs[i].ifindex, (unsigned long long)ifs.ifs[i].wdev,
		       ifs.ifs[i].iftype);
		try_one_if(fd, family, ifs.ifs[i]);
		break;
	}

	close(fd);
	return 0;
}

                 reply	other threads:[~2026-06-30  8:57 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260630085719.3313-1-samsun1006219@gmail.com \
    --to=samsun1006219@gmail.com \
    --cc=johannes@sipsolutions.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-wireless@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox