Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,20 @@
config MAC80211_HWSIM
tristate "Simulated radio testing tool for mac80211"
depends on MAC80211
help
This driver is a developer testing tool that can be used to test
IEEE 802.11 networking stack (mac80211) functionality. This is not
needed for normal wireless LAN usage and is only for testing. See
Documentation/networking/mac80211_hwsim for more information on how
to use this tool.
To compile this driver as a module, choose M here: the module will be
called mac80211_hwsim. If unsure, say N.
config VIRT_WIFI
tristate "Wifi wrapper for ethernet drivers"
depends on CFG80211
help
This option adds support for ethernet connections to appear as if they
are wifi connections through a special rtnetlink device.
@@ -0,0 +1,5 @@
obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o
mac80211_hwsim-objs += mac80211_hwsim_main.o
mac80211_hwsim-objs += mac80211_hwsim_nan.o
obj-$(CONFIG_VIRT_WIFI) += virt_wifi.o
@@ -0,0 +1,348 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
* Copyright (c) 2011, Javier Lopez <jlopex@gmail.com>
* Copyright (C) 2020, 2022-2025 Intel Corporation
*/
#ifndef __MAC80211_HWSIM_H
#define __MAC80211_HWSIM_H
/**
* enum hwsim_tx_control_flags - flags to describe transmission info/status
*
* These flags are used to give the wmediumd extra information in order to
* modify its behavior for each frame
*
* @HWSIM_TX_CTL_REQ_TX_STATUS: require TX status callback for this frame.
* @HWSIM_TX_CTL_NO_ACK: tell the wmediumd not to wait for an ack
* @HWSIM_TX_STAT_ACK: Frame was acknowledged
*
*/
enum hwsim_tx_control_flags {
HWSIM_TX_CTL_REQ_TX_STATUS = BIT(0),
HWSIM_TX_CTL_NO_ACK = BIT(1),
HWSIM_TX_STAT_ACK = BIT(2),
};
/**
* DOC: Frame transmission/registration support
*
* Frame transmission and registration support exists to allow userspace
* entities such as wmediumd to receive and process all broadcasted
* frames from a mac80211_hwsim radio device.
*
* This allow user space applications to decide if the frame should be
* dropped or not and implement a wireless medium simulator at user space.
*
* Registration is done by sending a register message to the driver and
* will be automatically unregistered if the user application doesn't
* responds to sent frames.
* Once registered the user application has to take responsibility of
* broadcasting the frames to all listening mac80211_hwsim radio
* interfaces.
*
* For more technical details, see the corresponding command descriptions
* below.
*/
/**
* enum hwsim_commands - supported hwsim commands
*
* @HWSIM_CMD_UNSPEC: unspecified command to catch errors
*
* @HWSIM_CMD_REGISTER: request to register and received all broadcasted
* frames by any mac80211_hwsim radio device.
* @HWSIM_CMD_FRAME: send/receive a broadcasted frame from/to kernel/user
* space, uses:
* %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_ADDR_RECEIVER,
* %HWSIM_ATTR_FRAME, %HWSIM_ATTR_FLAGS, %HWSIM_ATTR_RX_RATE,
* %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE, %HWSIM_ATTR_FREQ (optional)
* @HWSIM_CMD_TX_INFO_FRAME: Transmission info report from user space to
* kernel, uses:
* %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_FLAGS,
* %HWSIM_ATTR_TX_INFO, %HWSIM_ATTR_TX_INFO_FLAGS,
* %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
* @HWSIM_CMD_NEW_RADIO: create a new radio with the given parameters,
* returns the radio ID (>= 0) or negative on errors, if successful
* then multicast the result, uses optional parameter:
* %HWSIM_ATTR_REG_STRICT_REG, %HWSIM_ATTR_SUPPORT_P2P_DEVICE,
* %HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE, %HWSIM_ATTR_CHANNELS,
* %HWSIM_ATTR_NO_VIF, %HWSIM_ATTR_RADIO_NAME, %HWSIM_ATTR_USE_CHANCTX,
* %HWSIM_ATTR_REG_HINT_ALPHA2, %HWSIM_ATTR_REG_CUSTOM_REG,
* %HWSIM_ATTR_PERM_ADDR
* @HWSIM_CMD_DEL_RADIO: destroy a radio, reply is multicasted
* @HWSIM_CMD_GET_RADIO: fetch information about existing radios, uses:
* %HWSIM_ATTR_RADIO_ID
* @HWSIM_CMD_ADD_MAC_ADDR: add a receive MAC address (given in the
* %HWSIM_ATTR_ADDR_RECEIVER attribute) to a device identified by
* %HWSIM_ATTR_ADDR_TRANSMITTER. This lets wmediumd forward frames
* to this receiver address for a given station.
* @HWSIM_CMD_DEL_MAC_ADDR: remove the MAC address again, the attributes
* are the same as to @HWSIM_CMD_ADD_MAC_ADDR.
* @HWSIM_CMD_START_PMSR: request to start peer measurement with the
* %HWSIM_ATTR_PMSR_REQUEST. Result will be sent back asynchronously
* with %HWSIM_CMD_REPORT_PMSR.
* @HWSIM_CMD_ABORT_PMSR: Abort previously started peer measurement.
* @HWSIM_CMD_REPORT_PMSR: Report peer measurement data.
* @__HWSIM_CMD_MAX: enum limit
*/
enum hwsim_commands {
HWSIM_CMD_UNSPEC,
HWSIM_CMD_REGISTER,
HWSIM_CMD_FRAME,
HWSIM_CMD_TX_INFO_FRAME,
HWSIM_CMD_NEW_RADIO,
HWSIM_CMD_DEL_RADIO,
HWSIM_CMD_GET_RADIO,
HWSIM_CMD_ADD_MAC_ADDR,
HWSIM_CMD_DEL_MAC_ADDR,
HWSIM_CMD_START_PMSR,
HWSIM_CMD_ABORT_PMSR,
HWSIM_CMD_REPORT_PMSR,
__HWSIM_CMD_MAX,
};
#define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1)
#define HWSIM_CMD_CREATE_RADIO HWSIM_CMD_NEW_RADIO
#define HWSIM_CMD_DESTROY_RADIO HWSIM_CMD_DEL_RADIO
/**
* enum hwsim_attrs - hwsim netlink attributes
*
* @HWSIM_ATTR_UNSPEC: unspecified attribute to catch errors
*
* @HWSIM_ATTR_ADDR_RECEIVER: MAC address of the radio device that
* the frame is broadcasted to
* @HWSIM_ATTR_ADDR_TRANSMITTER: MAC address of the radio device that
* the frame was broadcasted from
* @HWSIM_ATTR_FRAME: Data array
* @HWSIM_ATTR_FLAGS: mac80211 transmission flags, used to process
* properly the frame at user space
* @HWSIM_ATTR_RX_RATE: estimated rx rate index for this frame at user
* space
* @HWSIM_ATTR_SIGNAL: estimated RX signal for this frame at user
* space
* @HWSIM_ATTR_TX_INFO: ieee80211_tx_rate array
* @HWSIM_ATTR_COOKIE: sk_buff cookie to identify the frame
* @HWSIM_ATTR_CHANNELS: u32 attribute used with the %HWSIM_CMD_NEW_RADIO
* command giving the number of channels supported by the new radio
* @HWSIM_ATTR_RADIO_ID: u32 attribute used with %HWSIM_CMD_DESTROY_RADIO
* only to destroy a radio
* @HWSIM_ATTR_REG_HINT_ALPHA2: alpha2 for regulatory driver hint
* (nla string, length 2)
* @HWSIM_ATTR_REG_CUSTOM_REG: custom regulatory domain index (u32 attribute)
* @HWSIM_ATTR_REG_STRICT_REG: request REGULATORY_STRICT_REG (flag attribute)
* @HWSIM_ATTR_SUPPORT_P2P_DEVICE: support P2P Device virtual interface (flag)
* @HWSIM_ATTR_USE_CHANCTX: used with the %HWSIM_CMD_NEW_RADIO
* command to force use of channel contexts even when only a
* single channel is supported
* @HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE: used with the %HWSIM_CMD_NEW_RADIO
* command to force radio removal when process that created the radio dies
* @HWSIM_ATTR_RADIO_NAME: Name of radio, e.g. phy666
* @HWSIM_ATTR_NO_VIF: Do not create vif (wlanX) when creating radio.
* @HWSIM_ATTR_FREQ: Frequency at which packet is transmitted or received.
* @HWSIM_ATTR_PAD: padding attribute for 64-bit values, ignore
* @HWSIM_ATTR_TX_INFO_FLAGS: additional flags for corresponding
* rates of %HWSIM_ATTR_TX_INFO
* @HWSIM_ATTR_PERM_ADDR: permanent mac address of new radio
* @HWSIM_ATTR_IFTYPE_SUPPORT: u32 attribute of supported interface types bits
* @HWSIM_ATTR_CIPHER_SUPPORT: u32 array of supported cipher types
* @HWSIM_ATTR_MLO_SUPPORT: claim MLO support (exact parameters TBD) for
* the new radio
* @HWSIM_ATTR_PMSR_SUPPORT: nested attribute used with %HWSIM_CMD_NEW_RADIO
* to provide peer measurement capabilities. (nl80211_peer_measurement_attrs)
* @HWSIM_ATTR_PMSR_REQUEST: nested attribute used with %HWSIM_CMD_START_PMSR
* to provide details about peer measurement request (nl80211_peer_measurement_attrs)
* @HWSIM_ATTR_PMSR_RESULT: nested attributed used with %HWSIM_CMD_REPORT_PMSR
* to provide peer measurement result (nl80211_peer_measurement_attrs)
* @HWSIM_ATTR_MULTI_RADIO: Register multiple wiphy radios (flag).
* Adds one radio for each band. Number of supported channels will be set for
* each radio instead of for the wiphy.
* @HWSIM_ATTR_SUPPORT_NAN_DEVICE: support NAN Device virtual interface (flag)
* @HWSIM_ATTR_SUPPORT_BACKGROUND_RADAR: background radar/CAC support (flag)
* @__HWSIM_ATTR_MAX: enum limit
*/
enum hwsim_attrs {
HWSIM_ATTR_UNSPEC,
HWSIM_ATTR_ADDR_RECEIVER,
HWSIM_ATTR_ADDR_TRANSMITTER,
HWSIM_ATTR_FRAME,
HWSIM_ATTR_FLAGS,
HWSIM_ATTR_RX_RATE,
HWSIM_ATTR_SIGNAL,
HWSIM_ATTR_TX_INFO,
HWSIM_ATTR_COOKIE,
HWSIM_ATTR_CHANNELS,
HWSIM_ATTR_RADIO_ID,
HWSIM_ATTR_REG_HINT_ALPHA2,
HWSIM_ATTR_REG_CUSTOM_REG,
HWSIM_ATTR_REG_STRICT_REG,
HWSIM_ATTR_SUPPORT_P2P_DEVICE,
HWSIM_ATTR_USE_CHANCTX,
HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE,
HWSIM_ATTR_RADIO_NAME,
HWSIM_ATTR_NO_VIF,
HWSIM_ATTR_FREQ,
HWSIM_ATTR_PAD,
HWSIM_ATTR_TX_INFO_FLAGS,
HWSIM_ATTR_PERM_ADDR,
HWSIM_ATTR_IFTYPE_SUPPORT,
HWSIM_ATTR_CIPHER_SUPPORT,
HWSIM_ATTR_MLO_SUPPORT,
HWSIM_ATTR_PMSR_SUPPORT,
HWSIM_ATTR_PMSR_REQUEST,
HWSIM_ATTR_PMSR_RESULT,
HWSIM_ATTR_MULTI_RADIO,
HWSIM_ATTR_SUPPORT_NAN_DEVICE,
HWSIM_ATTR_SUPPORT_BACKGROUND_RADAR,
__HWSIM_ATTR_MAX,
};
#define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
/**
* struct hwsim_tx_rate - rate selection/status
*
* @idx: rate index to attempt to send with
* @count: number of tries in this rate before going to the next rate
*
* A value of -1 for @idx indicates an invalid rate and, if used
* in an array of retry rates, that no more rates should be tried.
*
* When used for transmit status reporting, the driver should
* always report the rate and number of retries used.
*
*/
struct hwsim_tx_rate {
s8 idx;
u8 count;
} __packed;
/**
* enum hwsim_tx_rate_flags - per-rate flags set by the rate control algorithm.
* Inspired by structure mac80211_rate_control_flags. New flags may be
* appended, but old flags not deleted, to keep compatibility for
* userspace.
*
* These flags are set by the Rate control algorithm for each rate during tx,
* in the @flags member of struct ieee80211_tx_rate.
*
* @MAC80211_HWSIM_TX_RC_USE_RTS_CTS: Use RTS/CTS exchange for this rate.
* @MAC80211_HWSIM_TX_RC_USE_CTS_PROTECT: CTS-to-self protection is required.
* This is set if the current BSS requires ERP protection.
* @MAC80211_HWSIM_TX_RC_USE_SHORT_PREAMBLE: Use short preamble.
* @MAC80211_HWSIM_TX_RC_MCS: HT rate.
* @MAC80211_HWSIM_TX_RC_VHT_MCS: VHT MCS rate, in this case the idx field is
* split into a higher 4 bits (Nss) and lower 4 bits (MCS number)
* @MAC80211_HWSIM_TX_RC_GREEN_FIELD: Indicates whether this rate should be used
* in Greenfield mode.
* @MAC80211_HWSIM_TX_RC_40_MHZ_WIDTH: Indicates if the Channel Width should be
* 40 MHz.
* @MAC80211_HWSIM_TX_RC_80_MHZ_WIDTH: Indicates 80 MHz transmission
* @MAC80211_HWSIM_TX_RC_160_MHZ_WIDTH: Indicates 160 MHz transmission
* (80+80 isn't supported yet)
* @MAC80211_HWSIM_TX_RC_DUP_DATA: The frame should be transmitted on both of
* the adjacent 20 MHz channels, if the current channel type is
* NL80211_CHAN_HT40MINUS or NL80211_CHAN_HT40PLUS.
* @MAC80211_HWSIM_TX_RC_SHORT_GI: Short Guard interval should be used for this
* rate.
*/
enum hwsim_tx_rate_flags {
MAC80211_HWSIM_TX_RC_USE_RTS_CTS = BIT(0),
MAC80211_HWSIM_TX_RC_USE_CTS_PROTECT = BIT(1),
MAC80211_HWSIM_TX_RC_USE_SHORT_PREAMBLE = BIT(2),
/* rate index is an HT/VHT MCS instead of an index */
MAC80211_HWSIM_TX_RC_MCS = BIT(3),
MAC80211_HWSIM_TX_RC_GREEN_FIELD = BIT(4),
MAC80211_HWSIM_TX_RC_40_MHZ_WIDTH = BIT(5),
MAC80211_HWSIM_TX_RC_DUP_DATA = BIT(6),
MAC80211_HWSIM_TX_RC_SHORT_GI = BIT(7),
MAC80211_HWSIM_TX_RC_VHT_MCS = BIT(8),
MAC80211_HWSIM_TX_RC_80_MHZ_WIDTH = BIT(9),
MAC80211_HWSIM_TX_RC_160_MHZ_WIDTH = BIT(10),
};
/**
* struct hwsim_tx_rate_flag - rate selection/status
*
* @idx: rate index to attempt to send with
* @flags: the rate flags according to &enum hwsim_tx_rate_flags
*
* A value of -1 for @idx indicates an invalid rate and, if used
* in an array of retry rates, that no more rates should be tried.
*
* When used for transmit status reporting, the driver should
* always report the rate and number of retries used.
*
*/
struct hwsim_tx_rate_flag {
s8 idx;
u16 flags;
} __packed;
/**
* DOC: Frame transmission support over virtio
*
* Frame transmission is also supported over virtio to allow communication
* with external entities.
*/
/**
* enum hwsim_vqs - queues for virtio frame transmission
*
* @HWSIM_VQ_TX: send frames to external entity
* @HWSIM_VQ_RX: receive frames and transmission info reports
* @HWSIM_NUM_VQS: enum limit
*/
enum hwsim_vqs {
HWSIM_VQ_TX,
HWSIM_VQ_RX,
HWSIM_NUM_VQS,
};
/**
* enum hwsim_rate_info_attributes - bitrate information.
*
* Information about a receiving or transmitting bitrate
* that can be mapped to struct rate_info
*
* @__HWSIM_RATE_INFO_ATTR_INVALID: reserved, netlink attribute 0 is invalid
* @HWSIM_RATE_INFO_ATTR_FLAGS: bitflag of flags from &enum rate_info_flags
* @HWSIM_RATE_INFO_ATTR_MCS: mcs index if struct describes an HT/VHT/HE rate
* @HWSIM_RATE_INFO_ATTR_LEGACY: bitrate in 100kbit/s for 802.11abg
* @HWSIM_RATE_INFO_ATTR_NSS: number of streams (VHT & HE only)
* @HWSIM_RATE_INFO_ATTR_BW: bandwidth (from &enum rate_info_bw)
* @HWSIM_RATE_INFO_ATTR_HE_GI: HE guard interval (from &enum nl80211_he_gi)
* @HWSIM_RATE_INFO_ATTR_HE_DCM: HE DCM value
* @HWSIM_RATE_INFO_ATTR_HE_RU_ALLOC: HE RU allocation (from &enum nl80211_he_ru_alloc,
* only valid if bw is %RATE_INFO_BW_HE_RU)
* @HWSIM_RATE_INFO_ATTR_N_BOUNDED_CH: In case of EDMG the number of bonded channels (1-4)
* @HWSIM_RATE_INFO_ATTR_EHT_GI: EHT guard interval (from &enum nl80211_eht_gi)
* @HWSIM_RATE_INFO_ATTR_EHT_RU_ALLOC: EHT RU allocation (from &enum nl80211_eht_ru_alloc,
* only valid if bw is %RATE_INFO_BW_EHT_RU)
* @NUM_HWSIM_RATE_INFO_ATTRS: internal
* @HWSIM_RATE_INFO_ATTR_MAX: highest attribute number
*/
enum hwsim_rate_info_attributes {
__HWSIM_RATE_INFO_ATTR_INVALID,
HWSIM_RATE_INFO_ATTR_FLAGS,
HWSIM_RATE_INFO_ATTR_MCS,
HWSIM_RATE_INFO_ATTR_LEGACY,
HWSIM_RATE_INFO_ATTR_NSS,
HWSIM_RATE_INFO_ATTR_BW,
HWSIM_RATE_INFO_ATTR_HE_GI,
HWSIM_RATE_INFO_ATTR_HE_DCM,
HWSIM_RATE_INFO_ATTR_HE_RU_ALLOC,
HWSIM_RATE_INFO_ATTR_N_BOUNDED_CH,
HWSIM_RATE_INFO_ATTR_EHT_GI,
HWSIM_RATE_INFO_ATTR_EHT_RU_ALLOC,
/* keep last */
NUM_HWSIM_RATE_INFO_ATTRS,
HWSIM_RATE_INFO_ATTR_MAX = NUM_HWSIM_RATE_INFO_ATTRS - 1
};
#endif /* __MAC80211_HWSIM_H */
@@ -0,0 +1,168 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
* Copyright (c) 2011, Javier Lopez <jlopex@gmail.com>
* Copyright (c) 2016 - 2017 Intel Deutschland GmbH
* Copyright (C) 2018 - 2026 Intel Corporation
*/
#ifndef __MAC80211_HWSIM_I_H
#define __MAC80211_HWSIM_I_H
#include <net/mac80211.h>
#include "mac80211_hwsim.h"
#include "mac80211_hwsim_nan.h"
struct hwsim_sta_nan_sched {
/* Later members are protected by this lock */
spinlock_t lock;
u16 committed_dw;
struct {
u8 map_id;
struct cfg80211_chan_def chans[CFG80211_NAN_SCHED_NUM_TIME_SLOTS];
} maps[CFG80211_NAN_MAX_PEER_MAPS];
};
struct hwsim_sta_priv {
u32 magic;
unsigned int last_link;
u16 active_links_rx;
/* NAN peer schedule - must be accessed under nan_sched.lock */
struct hwsim_sta_nan_sched nan_sched;
};
#define HWSIM_STA_MAGIC 0x6d537749
struct mac80211_hwsim_link_data {
u32 link_id;
u64 beacon_int /* beacon interval in us */;
struct hrtimer beacon_timer;
};
#define HWSIM_NUM_CHANNELS_2GHZ 14
#define HWSIM_NUM_CHANNELS_5GHZ 40
#define HWSIM_NUM_CHANNELS_6GHZ 59
#define HWSIM_NUM_S1G_CHANNELS_US 51
#define HWSIM_NUM_RATES 12
#define HWSIM_NUM_CIPHERS 11
struct mac80211_hwsim_data {
struct list_head list;
struct rhash_head rht;
struct ieee80211_hw *hw;
struct device *dev;
struct ieee80211_supported_band bands[NUM_NL80211_BANDS];
struct ieee80211_channel channels_2ghz[HWSIM_NUM_CHANNELS_2GHZ];
struct ieee80211_channel channels_5ghz[HWSIM_NUM_CHANNELS_5GHZ];
struct ieee80211_channel channels_6ghz[HWSIM_NUM_CHANNELS_6GHZ];
struct ieee80211_channel channels_s1g[HWSIM_NUM_S1G_CHANNELS_US];
struct ieee80211_rate rates[HWSIM_NUM_RATES];
struct ieee80211_iface_combination if_combination;
struct ieee80211_iface_limit if_limits[5];
int n_if_limits;
/* Storage space for channels, etc. */
struct mac80211_hwsim_phy_data *phy_data;
struct ieee80211_iface_combination if_combination_radio;
struct wiphy_radio_freq_range radio_range[NUM_NL80211_BANDS];
struct wiphy_radio radio[NUM_NL80211_BANDS];
u32 ciphers[HWSIM_NUM_CIPHERS];
struct mac_address addresses[3];
int channels, idx;
bool use_chanctx;
bool destroy_on_close;
u32 portid;
char alpha2[2];
const struct ieee80211_regdomain *regd;
struct ieee80211_channel *tmp_chan;
struct ieee80211_channel *roc_chan;
u32 roc_duration;
struct delayed_work roc_start;
struct delayed_work roc_done;
struct delayed_work hw_scan;
struct cfg80211_scan_request *hw_scan_request;
struct ieee80211_vif *hw_scan_vif;
int scan_chan_idx;
u8 scan_addr[ETH_ALEN];
struct {
struct ieee80211_channel *channel;
unsigned long next_start, start, end;
} survey_data[HWSIM_NUM_CHANNELS_2GHZ +
HWSIM_NUM_CHANNELS_5GHZ +
HWSIM_NUM_CHANNELS_6GHZ];
struct ieee80211_channel *channel;
enum nl80211_chan_width bw;
unsigned int rx_filter;
bool started, idle, scanning;
struct mutex mutex;
enum ps_mode {
PS_DISABLED, PS_ENABLED, PS_AUTO_POLL, PS_MANUAL_POLL
} ps;
bool ps_poll_pending;
struct dentry *debugfs;
struct cfg80211_chan_def radar_background_chandef;
atomic_t pending_cookie;
struct sk_buff_head pending; /* packets pending */
/*
* Only radios in the same group can communicate together (the
* channel has to match too). Each bit represents a group. A
* radio can be in more than one group.
*/
u64 group;
/* group shared by radios created in the same netns */
int netgroup;
/* wmediumd portid responsible for netgroup of this radio */
u32 wmediumd;
/* difference between this hw's clock and the real clock, in usecs */
spinlock_t tsf_offset_lock;
s64 tsf_offset;
/* Stats */
u64 tx_pkts;
u64 rx_pkts;
u64 tx_bytes;
u64 rx_bytes;
u64 tx_dropped;
u64 tx_failed;
/* RSSI in rx status of the receiver */
int rx_rssi;
/* only used when pmsr capability is supplied */
struct cfg80211_pmsr_capabilities pmsr_capa;
struct cfg80211_pmsr_request *pmsr_request;
struct wireless_dev *pmsr_request_wdev;
struct mac80211_hwsim_link_data link_data[IEEE80211_MLD_MAX_NUM_LINKS];
struct mac80211_hwsim_nan_data nan;
};
extern spinlock_t hwsim_radio_lock;
extern struct list_head hwsim_radios;
ktime_t mac80211_hwsim_tsf_to_boottime(struct mac80211_hwsim_data *data,
u64 tsf);
u64 mac80211_hwsim_boottime_to_tsf(struct mac80211_hwsim_data *data,
ktime_t ts);
u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
struct sk_buff *skb,
struct ieee80211_channel *chan);
void ieee80211_hwsim_wake_tx_queue(struct ieee80211_hw *hw,
struct ieee80211_txq *txq);
#endif /* __MAC80211_HWSIM_I_H */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* mac80211_hwsim_nan - NAN software simulation for mac80211_hwsim
* Copyright (C) 2025-2026 Intel Corporation
*/
#ifndef __MAC80211_HWSIM_NAN_H
#define __MAC80211_HWSIM_NAN_H
enum mac80211_hwsim_nan_phase {
MAC80211_HWSIM_NAN_PHASE_SCAN,
MAC80211_HWSIM_NAN_PHASE_WARMUP,
MAC80211_HWSIM_NAN_PHASE_UP,
};
enum mac80211_hwsim_nan_role {
MAC80211_HWSIM_NAN_ROLE_MASTER,
MAC80211_HWSIM_NAN_ROLE_SYNC,
MAC80211_HWSIM_NAN_ROLE_NON_SYNC,
};
struct mac80211_hwsim_nan_data {
struct ieee80211_vif *device_vif;
u8 bands;
struct hrtimer slot_timer;
struct hrtimer resume_txqs_timer;
bool notify_dw;
struct hrtimer discovery_beacon_timer;
/* Later members are protected by this lock */
spinlock_t state_lock;
u8 master_pref;
u8 random_factor;
u8 random_factor_valid_dwst;
enum mac80211_hwsim_nan_phase phase;
enum mac80211_hwsim_nan_role role;
u8 cluster_id[ETH_ALEN];
struct ieee80211_nan_anchor_master_info current_ami;
struct ieee80211_nan_anchor_master_info last_ami;
/* Wi-Fi Aware version 4.0, section 3.3.6.1 and 3.3.6.2 */
int master_transition_score;
/* Wi-Fi Aware version 4.0, section 3.3.6.3 and 3.3.6.4 */
int sync_transition_score;
bool tsf_adjusted;
bool tsf_discontinuity;
/*
* Local schedule - stores channel definition for each 16TU slot.
* Derived from NMI vif->cfg.nan_schedule. chan == NULL means not
* available in that slot (except DW which is implicit).
*/
struct cfg80211_chan_def local_sched[CFG80211_NAN_SCHED_NUM_TIME_SLOTS];
};
enum hrtimer_restart
mac80211_hwsim_nan_slot_timer(struct hrtimer *timer);
enum hrtimer_restart
mac80211_hwsim_nan_resume_txqs_timer(struct hrtimer *timer);
enum hrtimer_restart
mac80211_hwsim_nan_discovery_beacon_timer(struct hrtimer *timer);
int mac80211_hwsim_nan_start(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_nan_conf *conf);
int mac80211_hwsim_nan_stop(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
int mac80211_hwsim_nan_change_config(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_nan_conf *conf,
u32 changes);
int mac80211_hwsim_nan_peer_sched_changed(struct ieee80211_hw *hw,
struct ieee80211_sta *sta);
bool mac80211_hwsim_nan_txq_transmitting(struct ieee80211_hw *hw,
struct ieee80211_txq *txq);
void mac80211_hwsim_nan_get_tx_chandef(struct ieee80211_hw *hw,
struct cfg80211_chan_def *chandef);
bool mac80211_hwsim_nan_receive(struct ieee80211_hw *hw,
struct ieee80211_channel *channel,
struct ieee80211_rx_status *rx_status);
void mac80211_hwsim_nan_rx(struct ieee80211_hw *hw,
struct sk_buff *skb);
void mac80211_hwsim_nan_local_sched_changed(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
#endif /* __MAC80211_HWSIM_NAN_H */
@@ -0,0 +1,716 @@
// SPDX-License-Identifier: GPL-2.0
/* drivers/net/wireless/virt_wifi.c
*
* A fake implementation of cfg80211_ops that can be tacked on to an ethernet
* net_device to make it appear as a wireless connection.
*
* Copyright (C) 2018 Google, Inc.
*
* Author: schuffelen@google.com
*/
#include <net/cfg80211.h>
#include <net/rtnetlink.h>
#include <linux/etherdevice.h>
#include <linux/math64.h>
#include <linux/module.h>
static struct wiphy *common_wiphy;
struct virt_wifi_wiphy_priv {
struct delayed_work scan_result;
struct cfg80211_scan_request *scan_request;
bool being_deleted;
};
static struct ieee80211_channel channel_2ghz = {
.band = NL80211_BAND_2GHZ,
.center_freq = 2432,
.hw_value = 2432,
.max_power = 20,
};
static struct ieee80211_rate bitrates_2ghz[] = {
{ .bitrate = 10 },
{ .bitrate = 20 },
{ .bitrate = 55 },
{ .bitrate = 110 },
{ .bitrate = 60 },
{ .bitrate = 120 },
{ .bitrate = 240 },
};
static struct ieee80211_supported_band band_2ghz = {
.channels = &channel_2ghz,
.bitrates = bitrates_2ghz,
.band = NL80211_BAND_2GHZ,
.n_channels = 1,
.n_bitrates = ARRAY_SIZE(bitrates_2ghz),
.ht_cap = {
.ht_supported = true,
.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
IEEE80211_HT_CAP_GRN_FLD |
IEEE80211_HT_CAP_SGI_20 |
IEEE80211_HT_CAP_SGI_40 |
IEEE80211_HT_CAP_DSSSCCK40,
.ampdu_factor = 0x3,
.ampdu_density = 0x6,
.mcs = {
.rx_mask = {0xff, 0xff},
.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
},
},
};
static struct ieee80211_channel channel_5ghz = {
.band = NL80211_BAND_5GHZ,
.center_freq = 5240,
.hw_value = 5240,
.max_power = 20,
};
static struct ieee80211_rate bitrates_5ghz[] = {
{ .bitrate = 60 },
{ .bitrate = 120 },
{ .bitrate = 240 },
};
#define RX_MCS_MAP (IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | \
IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | \
IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | \
IEEE80211_VHT_MCS_SUPPORT_0_9 << 6 | \
IEEE80211_VHT_MCS_SUPPORT_0_9 << 8 | \
IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | \
IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | \
IEEE80211_VHT_MCS_SUPPORT_0_9 << 14)
#define TX_MCS_MAP (IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | \
IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | \
IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | \
IEEE80211_VHT_MCS_SUPPORT_0_9 << 6 | \
IEEE80211_VHT_MCS_SUPPORT_0_9 << 8 | \
IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | \
IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | \
IEEE80211_VHT_MCS_SUPPORT_0_9 << 14)
static struct ieee80211_supported_band band_5ghz = {
.channels = &channel_5ghz,
.bitrates = bitrates_5ghz,
.band = NL80211_BAND_5GHZ,
.n_channels = 1,
.n_bitrates = ARRAY_SIZE(bitrates_5ghz),
.ht_cap = {
.ht_supported = true,
.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
IEEE80211_HT_CAP_GRN_FLD |
IEEE80211_HT_CAP_SGI_20 |
IEEE80211_HT_CAP_SGI_40 |
IEEE80211_HT_CAP_DSSSCCK40,
.ampdu_factor = 0x3,
.ampdu_density = 0x6,
.mcs = {
.rx_mask = {0xff, 0xff},
.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
},
},
.vht_cap = {
.vht_supported = true,
.cap = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 |
IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ |
IEEE80211_VHT_CAP_RXLDPC |
IEEE80211_VHT_CAP_SHORT_GI_80 |
IEEE80211_VHT_CAP_SHORT_GI_160 |
IEEE80211_VHT_CAP_TXSTBC |
IEEE80211_VHT_CAP_RXSTBC_1 |
IEEE80211_VHT_CAP_RXSTBC_2 |
IEEE80211_VHT_CAP_RXSTBC_3 |
IEEE80211_VHT_CAP_RXSTBC_4 |
IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK,
.vht_mcs = {
.rx_mcs_map = cpu_to_le16(RX_MCS_MAP),
.tx_mcs_map = cpu_to_le16(TX_MCS_MAP),
}
},
};
/* Assigned at module init. Guaranteed locally-administered and unicast. */
static u8 fake_router_bssid[ETH_ALEN] __ro_after_init = {};
#define VIRT_WIFI_SSID "VirtWifi"
#define VIRT_WIFI_SSID_LEN 8
static void virt_wifi_inform_bss(struct wiphy *wiphy)
{
u64 tsf = div_u64(ktime_get_boottime_ns(), 1000);
struct cfg80211_bss *informed_bss;
static const struct {
u8 tag;
u8 len;
u8 ssid[8] __nonstring;
} __packed ssid = {
.tag = WLAN_EID_SSID,
.len = VIRT_WIFI_SSID_LEN,
.ssid = VIRT_WIFI_SSID,
};
informed_bss = cfg80211_inform_bss(wiphy, &channel_5ghz,
CFG80211_BSS_FTYPE_PRESP,
fake_router_bssid, tsf,
WLAN_CAPABILITY_ESS, 0,
(void *)&ssid, sizeof(ssid),
DBM_TO_MBM(-50), GFP_KERNEL);
cfg80211_put_bss(wiphy, informed_bss);
}
/* Called with the rtnl lock held. */
static int virt_wifi_scan(struct wiphy *wiphy,
struct cfg80211_scan_request *request)
{
struct virt_wifi_wiphy_priv *priv = wiphy_priv(wiphy);
wiphy_debug(wiphy, "scan\n");
if (priv->scan_request || priv->being_deleted)
return -EBUSY;
priv->scan_request = request;
schedule_delayed_work(&priv->scan_result, HZ * 2);
return 0;
}
/* Acquires and releases the rdev BSS lock. */
static void virt_wifi_scan_result(struct work_struct *work)
{
struct virt_wifi_wiphy_priv *priv =
container_of(work, struct virt_wifi_wiphy_priv,
scan_result.work);
struct wiphy *wiphy = priv_to_wiphy(priv);
struct cfg80211_scan_info scan_info = { .aborted = false };
virt_wifi_inform_bss(wiphy);
/* Schedules work which acquires and releases the rtnl lock. */
cfg80211_scan_done(priv->scan_request, &scan_info);
priv->scan_request = NULL;
}
/* May acquire and release the rdev BSS lock. */
static void virt_wifi_cancel_scan(struct wiphy *wiphy)
{
struct virt_wifi_wiphy_priv *priv = wiphy_priv(wiphy);
cancel_delayed_work_sync(&priv->scan_result);
/* Clean up dangling callbacks if necessary. */
if (priv->scan_request) {
struct cfg80211_scan_info scan_info = { .aborted = true };
/* Schedules work which acquires and releases the rtnl lock. */
cfg80211_scan_done(priv->scan_request, &scan_info);
priv->scan_request = NULL;
}
}
struct virt_wifi_netdev_priv {
struct delayed_work connect;
struct net_device *lowerdev;
struct net_device *upperdev;
u32 tx_packets;
u32 tx_failed;
u32 connect_requested_ssid_len;
u8 connect_requested_ssid[IEEE80211_MAX_SSID_LEN];
u8 connect_requested_bss[ETH_ALEN];
bool is_up;
bool is_connected;
bool being_deleted;
};
/* Called with the rtnl lock held. */
static int virt_wifi_connect(struct wiphy *wiphy, struct net_device *netdev,
struct cfg80211_connect_params *sme)
{
struct virt_wifi_netdev_priv *priv = netdev_priv(netdev);
bool could_schedule;
if (priv->being_deleted || !priv->is_up)
return -EBUSY;
if (!sme->ssid)
return -EINVAL;
priv->connect_requested_ssid_len = sme->ssid_len;
memcpy(priv->connect_requested_ssid, sme->ssid, sme->ssid_len);
could_schedule = schedule_delayed_work(&priv->connect, HZ * 2);
if (!could_schedule)
return -EBUSY;
if (sme->bssid) {
ether_addr_copy(priv->connect_requested_bss, sme->bssid);
} else {
virt_wifi_inform_bss(wiphy);
eth_zero_addr(priv->connect_requested_bss);
}
wiphy_debug(wiphy, "connect\n");
return 0;
}
/* Acquires and releases the rdev event lock. */
static void virt_wifi_connect_complete(struct work_struct *work)
{
struct virt_wifi_netdev_priv *priv =
container_of(work, struct virt_wifi_netdev_priv, connect.work);
u8 *requested_bss = priv->connect_requested_bss;
bool right_addr = ether_addr_equal(requested_bss, fake_router_bssid);
bool right_ssid = priv->connect_requested_ssid_len == VIRT_WIFI_SSID_LEN &&
!memcmp(priv->connect_requested_ssid, VIRT_WIFI_SSID,
priv->connect_requested_ssid_len);
u16 status = WLAN_STATUS_SUCCESS;
if (is_zero_ether_addr(requested_bss))
requested_bss = NULL;
if (!priv->is_up || (requested_bss && !right_addr) || !right_ssid)
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
else
priv->is_connected = true;
/* Schedules an event that acquires the rtnl lock. */
cfg80211_connect_result(priv->upperdev,
priv->is_connected ? fake_router_bssid : NULL,
NULL, 0, NULL, 0,
status, GFP_KERNEL);
netif_carrier_on(priv->upperdev);
}
/* May acquire and release the rdev event lock. */
static void virt_wifi_cancel_connect(struct net_device *netdev)
{
struct virt_wifi_netdev_priv *priv = netdev_priv(netdev);
/* If there is work pending, clean up dangling callbacks. */
if (cancel_delayed_work_sync(&priv->connect)) {
/* Schedules an event that acquires the rtnl lock. */
cfg80211_connect_result(priv->upperdev,
priv->connect_requested_bss, NULL, 0,
NULL, 0,
WLAN_STATUS_UNSPECIFIED_FAILURE,
GFP_KERNEL);
}
}
/* Called with the rtnl lock held. Acquires the rdev event lock. */
static int virt_wifi_disconnect(struct wiphy *wiphy, struct net_device *netdev,
u16 reason_code)
{
struct virt_wifi_netdev_priv *priv = netdev_priv(netdev);
if (priv->being_deleted)
return -EBUSY;
wiphy_debug(wiphy, "disconnect\n");
virt_wifi_cancel_connect(netdev);
cfg80211_disconnected(netdev, reason_code, NULL, 0, true, GFP_KERNEL);
priv->is_connected = false;
netif_carrier_off(netdev);
return 0;
}
/* Called with the rtnl lock held. */
static int virt_wifi_get_station(struct wiphy *wiphy,
struct wireless_dev *wdev, const u8 *mac,
struct station_info *sinfo)
{
struct net_device *dev = wdev->netdev;
struct virt_wifi_netdev_priv *priv = netdev_priv(dev);
wiphy_debug(wiphy, "get_station\n");
if (!priv->is_connected || !ether_addr_equal(mac, fake_router_bssid))
return -ENOENT;
sinfo->filled = BIT_ULL(NL80211_STA_INFO_TX_PACKETS) |
BIT_ULL(NL80211_STA_INFO_TX_FAILED) |
BIT_ULL(NL80211_STA_INFO_SIGNAL) |
BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
sinfo->tx_packets = priv->tx_packets;
sinfo->tx_failed = priv->tx_failed;
/* For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_ */
sinfo->signal = -50;
sinfo->txrate = (struct rate_info) {
.legacy = 10, /* units are 100kbit/s */
};
return 0;
}
/* Called with the rtnl lock held. */
static int virt_wifi_dump_station(struct wiphy *wiphy, struct wireless_dev *wdev,
int idx, u8 *mac, struct station_info *sinfo)
{
struct virt_wifi_netdev_priv *priv = netdev_priv(wdev->netdev);
wiphy_debug(wiphy, "dump_station\n");
if (idx != 0 || !priv->is_connected)
return -ENOENT;
ether_addr_copy(mac, fake_router_bssid);
return virt_wifi_get_station(wiphy, wdev, fake_router_bssid, sinfo);
}
static const struct cfg80211_ops virt_wifi_cfg80211_ops = {
.scan = virt_wifi_scan,
.connect = virt_wifi_connect,
.disconnect = virt_wifi_disconnect,
.get_station = virt_wifi_get_station,
.dump_station = virt_wifi_dump_station,
};
/* Acquires and releases the rtnl lock. */
static struct wiphy *virt_wifi_make_wiphy(void)
{
struct wiphy *wiphy;
struct virt_wifi_wiphy_priv *priv;
int err;
wiphy = wiphy_new(&virt_wifi_cfg80211_ops, sizeof(*priv));
if (!wiphy)
return NULL;
wiphy->max_scan_ssids = 4;
wiphy->max_scan_ie_len = 1000;
wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
wiphy->bands[NL80211_BAND_2GHZ] = &band_2ghz;
wiphy->bands[NL80211_BAND_5GHZ] = &band_5ghz;
wiphy->bands[NL80211_BAND_60GHZ] = NULL;
wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
priv = wiphy_priv(wiphy);
priv->being_deleted = false;
priv->scan_request = NULL;
INIT_DELAYED_WORK(&priv->scan_result, virt_wifi_scan_result);
err = wiphy_register(wiphy);
if (err < 0) {
wiphy_free(wiphy);
return NULL;
}
return wiphy;
}
/* Acquires and releases the rtnl lock. */
static void virt_wifi_destroy_wiphy(struct wiphy *wiphy)
{
struct virt_wifi_wiphy_priv *priv;
WARN(!wiphy, "%s called with null wiphy", __func__);
if (!wiphy)
return;
priv = wiphy_priv(wiphy);
priv->being_deleted = true;
virt_wifi_cancel_scan(wiphy);
if (wiphy->registered)
wiphy_unregister(wiphy);
wiphy_free(wiphy);
}
/* Enters and exits a RCU-bh critical section. */
static netdev_tx_t virt_wifi_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct virt_wifi_netdev_priv *priv = netdev_priv(dev);
priv->tx_packets++;
if (!priv->is_connected) {
priv->tx_failed++;
return NET_XMIT_DROP;
}
skb->dev = priv->lowerdev;
return dev_queue_xmit(skb);
}
/* Called with rtnl lock held. */
static int virt_wifi_net_device_open(struct net_device *dev)
{
struct virt_wifi_netdev_priv *priv = netdev_priv(dev);
priv->is_up = true;
return 0;
}
/* Called with rtnl lock held. */
static int virt_wifi_net_device_stop(struct net_device *dev)
{
struct virt_wifi_netdev_priv *n_priv = netdev_priv(dev);
n_priv->is_up = false;
if (!dev->ieee80211_ptr)
return 0;
virt_wifi_cancel_scan(dev->ieee80211_ptr->wiphy);
virt_wifi_cancel_connect(dev);
netif_carrier_off(dev);
return 0;
}
static int virt_wifi_net_device_get_iflink(const struct net_device *dev)
{
struct virt_wifi_netdev_priv *priv = netdev_priv(dev);
return READ_ONCE(priv->lowerdev->ifindex);
}
static const struct net_device_ops virt_wifi_ops = {
.ndo_start_xmit = virt_wifi_start_xmit,
.ndo_open = virt_wifi_net_device_open,
.ndo_stop = virt_wifi_net_device_stop,
.ndo_get_iflink = virt_wifi_net_device_get_iflink,
};
/* Invoked as part of rtnl lock release. */
static void virt_wifi_net_device_destructor(struct net_device *dev)
{
/* Delayed past dellink to allow nl80211 to react to the device being
* deleted.
*/
kfree(dev->ieee80211_ptr);
dev->ieee80211_ptr = NULL;
}
/* No lock interaction. */
static void virt_wifi_setup(struct net_device *dev)
{
ether_setup(dev);
dev->netdev_ops = &virt_wifi_ops;
dev->needs_free_netdev = true;
}
/* Called in a RCU read critical section from netif_receive_skb */
static rx_handler_result_t virt_wifi_rx_handler(struct sk_buff **pskb)
{
struct sk_buff *skb = *pskb;
struct virt_wifi_netdev_priv *priv =
rcu_dereference(skb->dev->rx_handler_data);
if (!priv->is_connected)
return RX_HANDLER_PASS;
/* GFP_ATOMIC because this is a packet interrupt handler. */
skb = skb_share_check(skb, GFP_ATOMIC);
if (!skb) {
dev_err(&priv->upperdev->dev, "can't skb_share_check\n");
return RX_HANDLER_CONSUMED;
}
*pskb = skb;
skb->dev = priv->upperdev;
skb->pkt_type = PACKET_HOST;
return RX_HANDLER_ANOTHER;
}
/* Called with rtnl lock held. */
static int virt_wifi_newlink(struct net_device *dev,
struct rtnl_newlink_params *params,
struct netlink_ext_ack *extack)
{
struct virt_wifi_netdev_priv *priv = netdev_priv(dev);
struct net *link_net = rtnl_newlink_link_net(params);
struct nlattr **tb = params->tb;
int err;
if (!tb[IFLA_LINK])
return -EINVAL;
netif_carrier_off(dev);
priv->upperdev = dev;
priv->lowerdev = __dev_get_by_index(link_net,
nla_get_u32(tb[IFLA_LINK]));
if (!priv->lowerdev)
return -ENODEV;
if (!tb[IFLA_MTU])
dev->mtu = priv->lowerdev->mtu;
else if (dev->mtu > priv->lowerdev->mtu)
return -EINVAL;
err = netdev_rx_handler_register(priv->lowerdev, virt_wifi_rx_handler,
priv);
if (err) {
dev_err(&priv->lowerdev->dev,
"can't netdev_rx_handler_register: %d\n", err);
return err;
}
eth_hw_addr_inherit(dev, priv->lowerdev);
netif_stacked_transfer_operstate(priv->lowerdev, dev);
dev->ieee80211_ptr = kzalloc_obj(*dev->ieee80211_ptr);
if (!dev->ieee80211_ptr) {
err = -ENOMEM;
goto remove_handler;
}
dev->ieee80211_ptr->iftype = NL80211_IFTYPE_STATION;
dev->ieee80211_ptr->wiphy = common_wiphy;
err = register_netdevice(dev);
if (err) {
dev_err(&priv->lowerdev->dev, "can't register_netdevice: %d\n",
err);
goto free_wireless_dev;
}
err = netdev_upper_dev_link(priv->lowerdev, dev, extack);
if (err) {
dev_err(&priv->lowerdev->dev, "can't netdev_upper_dev_link: %d\n",
err);
goto unregister_netdev;
}
dev->priv_destructor = virt_wifi_net_device_destructor;
priv->being_deleted = false;
priv->is_connected = false;
priv->is_up = false;
INIT_DELAYED_WORK(&priv->connect, virt_wifi_connect_complete);
__module_get(THIS_MODULE);
return 0;
unregister_netdev:
unregister_netdevice(dev);
free_wireless_dev:
kfree(dev->ieee80211_ptr);
dev->ieee80211_ptr = NULL;
remove_handler:
netdev_rx_handler_unregister(priv->lowerdev);
return err;
}
/* Called with rtnl lock held. */
static void virt_wifi_dellink(struct net_device *dev,
struct list_head *head)
{
struct virt_wifi_netdev_priv *priv = netdev_priv(dev);
if (dev->ieee80211_ptr)
virt_wifi_cancel_scan(dev->ieee80211_ptr->wiphy);
priv->being_deleted = true;
virt_wifi_cancel_connect(dev);
netif_carrier_off(dev);
netdev_rx_handler_unregister(priv->lowerdev);
netdev_upper_dev_unlink(priv->lowerdev, dev);
unregister_netdevice_queue(dev, head);
module_put(THIS_MODULE);
/* Deleting the wiphy is handled in the module destructor. */
}
static struct rtnl_link_ops virt_wifi_link_ops = {
.kind = "virt_wifi",
.setup = virt_wifi_setup,
.newlink = virt_wifi_newlink,
.dellink = virt_wifi_dellink,
.priv_size = sizeof(struct virt_wifi_netdev_priv),
};
static bool netif_is_virt_wifi_dev(const struct net_device *dev)
{
return rcu_access_pointer(dev->rx_handler) == virt_wifi_rx_handler;
}
static int virt_wifi_event(struct notifier_block *this, unsigned long event,
void *ptr)
{
struct net_device *lower_dev = netdev_notifier_info_to_dev(ptr);
struct virt_wifi_netdev_priv *priv;
struct net_device *upper_dev;
LIST_HEAD(list_kill);
if (!netif_is_virt_wifi_dev(lower_dev))
return NOTIFY_DONE;
switch (event) {
case NETDEV_UNREGISTER:
priv = rtnl_dereference(lower_dev->rx_handler_data);
if (!priv)
return NOTIFY_DONE;
upper_dev = priv->upperdev;
upper_dev->rtnl_link_ops->dellink(upper_dev, &list_kill);
unregister_netdevice_many(&list_kill);
break;
}
return NOTIFY_DONE;
}
static struct notifier_block virt_wifi_notifier = {
.notifier_call = virt_wifi_event,
};
/* Acquires and releases the rtnl lock. */
static int __init virt_wifi_init_module(void)
{
int err;
/* Guaranteed to be locally-administered and not multicast. */
eth_random_addr(fake_router_bssid);
err = register_netdevice_notifier(&virt_wifi_notifier);
if (err)
return err;
err = -ENOMEM;
common_wiphy = virt_wifi_make_wiphy();
if (!common_wiphy)
goto notifier;
err = rtnl_link_register(&virt_wifi_link_ops);
if (err)
goto destroy_wiphy;
return 0;
destroy_wiphy:
virt_wifi_destroy_wiphy(common_wiphy);
notifier:
unregister_netdevice_notifier(&virt_wifi_notifier);
return err;
}
/* Acquires and releases the rtnl lock. */
static void __exit virt_wifi_cleanup_module(void)
{
/* Will delete any devices that depend on the wiphy. */
rtnl_link_unregister(&virt_wifi_link_ops);
virt_wifi_destroy_wiphy(common_wiphy);
unregister_netdevice_notifier(&virt_wifi_notifier);
}
module_init(virt_wifi_init_module);
module_exit(virt_wifi_cleanup_module);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Cody Schuffelen <schuffelen@google.com>");
MODULE_DESCRIPTION("Driver for a wireless wrapper of ethernet devices");
MODULE_ALIAS_RTNL_LINK("virt_wifi");