fix: harden cfg80211 backport ABI handling

This commit is contained in:
Shen Mintao
2026-08-05 10:38:26 +08:00
parent d9688590c2
commit bef3cc252f
2 changed files with 213 additions and 53 deletions
+161 -34
View File
@@ -4420,13 +4420,29 @@ cfg80211_chandef_identical(const struct cfg80211_chan_def *chandef1,
} }
#endif #endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 13, 0)) #define RWNX_CFG80211_OP_TYPE(_member) \
static int rwnx_cfg80211_set_monitor_channel(struct wiphy *wiphy, struct net_device *dev, typeof(((struct cfg80211_ops *)0)->_member)
struct cfg80211_chan_def *chandef) #define RWNX_CFG80211_OP_MATCH(_member, _type) \
#else __builtin_types_compatible_p(RWNX_CFG80211_OP_TYPE(_member), _type)
static int rwnx_cfg80211_set_monitor_channel(struct wiphy *wiphy, #define RWNX_CFG80211_OP_ASSERT(_member, _callback) \
struct cfg80211_chan_def *chandef) typedef char rwnx_cfg80211_##_member##_signature_must_match[ \
#endif __builtin_types_compatible_p(RWNX_CFG80211_OP_TYPE(_member), \
typeof(&(_callback))) ? 1 : -1]
typedef int (*rwnx_set_monitor_channel_legacy_t)(
struct wiphy *wiphy, struct cfg80211_chan_def *chandef);
typedef int (*rwnx_set_monitor_channel_dev_t)(
struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_chan_def *chandef);
typedef char rwnx_set_monitor_channel_signature_must_be_supported[
(RWNX_CFG80211_OP_MATCH(set_monitor_channel,
rwnx_set_monitor_channel_legacy_t) ||
RWNX_CFG80211_OP_MATCH(set_monitor_channel,
rwnx_set_monitor_channel_dev_t)) ? 1 : -1];
static int rwnx_cfg80211_set_monitor_channel_common(
struct wiphy *wiphy, struct cfg80211_chan_def *chandef)
{ {
struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy); struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
struct rwnx_vif *rwnx_vif; struct rwnx_vif *rwnx_vif;
@@ -4479,19 +4495,40 @@ static int rwnx_cfg80211_set_monitor_channel(struct wiphy *wiphy,
return 0; return 0;
} }
static int rwnx_cfg80211_set_monitor_channel_legacy(
struct wiphy *wiphy, struct cfg80211_chan_def *chandef)
{
return rwnx_cfg80211_set_monitor_channel_common(wiphy, chandef);
}
static int rwnx_cfg80211_set_monitor_channel_dev(
struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_chan_def *chandef)
{
(void)dev;
return rwnx_cfg80211_set_monitor_channel_common(wiphy, chandef);
}
#define RWNX_CFG80211_SET_MONITOR_CHANNEL_CB \
__builtin_choose_expr( \
RWNX_CFG80211_OP_MATCH(set_monitor_channel, \
rwnx_set_monitor_channel_dev_t), \
rwnx_cfg80211_set_monitor_channel_dev, \
rwnx_cfg80211_set_monitor_channel_legacy)
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 13, 0)) #if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 13, 0))
int rwnx_cfg80211_set_monitor_channel_(struct wiphy *wiphy, int rwnx_cfg80211_set_monitor_channel_(struct wiphy *wiphy,
struct net_device *dev, struct net_device *dev,
struct cfg80211_chan_def *chandef) struct cfg80211_chan_def *chandef)
{ {
return rwnx_cfg80211_set_monitor_channel(wiphy, dev, chandef); (void)dev;
return rwnx_cfg80211_set_monitor_channel_common(wiphy, chandef);
} }
#else #else
int rwnx_cfg80211_set_monitor_channel_(struct wiphy *wiphy, int rwnx_cfg80211_set_monitor_channel_(struct wiphy *wiphy,
struct cfg80211_chan_def *chandef) struct cfg80211_chan_def *chandef)
{ {
return rwnx_cfg80211_set_monitor_channel(wiphy, chandef); return rwnx_cfg80211_set_monitor_channel_common(wiphy, chandef);
} }
#endif #endif
@@ -4558,11 +4595,6 @@ typedef int (*rwnx_set_wiphy_params_legacy_t)(struct wiphy *wiphy,
typedef int (*rwnx_set_wiphy_params_radio_t)(struct wiphy *wiphy, typedef int (*rwnx_set_wiphy_params_radio_t)(struct wiphy *wiphy,
int radio_idx, u32 changed); int radio_idx, u32 changed);
#define RWNX_CFG80211_OP_TYPE(_member) \
typeof(((struct cfg80211_ops *)0)->_member)
#define RWNX_CFG80211_OP_MATCH(_member, _type) \
__builtin_types_compatible_p(RWNX_CFG80211_OP_TYPE(_member), _type)
typedef char rwnx_set_wiphy_params_signature_must_be_supported[ typedef char rwnx_set_wiphy_params_signature_must_be_supported[
(RWNX_CFG80211_OP_MATCH(set_wiphy_params, (RWNX_CFG80211_OP_MATCH(set_wiphy_params,
rwnx_set_wiphy_params_legacy_t) || rwnx_set_wiphy_params_legacy_t) ||
@@ -5122,11 +5154,7 @@ static int rwnx_cfg80211_get_channel(struct wiphy *wiphy,
if (rwnx_vif->vif_index == rwnx_hw->monitor_vif) if (rwnx_vif->vif_index == rwnx_hw->monitor_vif)
{ {
//retrieve channel from firmware //retrieve channel from firmware
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 13, 0)) rwnx_cfg80211_set_monitor_channel_common(wiphy, NULL);
rwnx_cfg80211_set_monitor_channel(wiphy, wdev->netdev, NULL);
#else
rwnx_cfg80211_set_monitor_channel(wiphy, NULL);
#endif
} }
//Check if channel context is valid //Check if channel context is valid
@@ -5313,17 +5341,28 @@ send_frame:
/** /**
* @start_radar_detection: Start radar detection in the driver. * @start_radar_detection: Start radar detection in the driver.
*/ */
static typedef int (*rwnx_start_radar_detection_legacy_t)(
int rwnx_cfg80211_start_radar_detection(struct wiphy *wiphy, struct wiphy *wiphy, struct net_device *dev,
struct net_device *dev, struct cfg80211_chan_def *chandef);
struct cfg80211_chan_def *chandef typedef int (*rwnx_start_radar_detection_cac_t)(
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)) struct wiphy *wiphy, struct net_device *dev,
, u32 cac_time_ms struct cfg80211_chan_def *chandef, u32 cac_time_ms);
#endif typedef int (*rwnx_start_radar_detection_link_t)(
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)) struct wiphy *wiphy, struct net_device *dev,
, int link_id struct cfg80211_chan_def *chandef, u32 cac_time_ms, int link_id);
#endif
) typedef char rwnx_start_radar_detection_signature_must_be_supported[
(RWNX_CFG80211_OP_MATCH(start_radar_detection,
rwnx_start_radar_detection_legacy_t) ||
RWNX_CFG80211_OP_MATCH(start_radar_detection,
rwnx_start_radar_detection_cac_t) ||
RWNX_CFG80211_OP_MATCH(start_radar_detection,
rwnx_start_radar_detection_link_t)) ? 1 : -1];
static int rwnx_cfg80211_start_radar_detection_common(
struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_chan_def *chandef, u32 cac_time_ms,
bool start_cac_timer)
{ {
struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy); struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
struct rwnx_vif *rwnx_vif = netdev_priv(dev); struct rwnx_vif *rwnx_vif = netdev_priv(dev);
@@ -5331,9 +5370,8 @@ int rwnx_cfg80211_start_radar_detection(struct wiphy *wiphy,
printk("%s\n", __func__); printk("%s\n", __func__);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)) if (start_cac_timer)
rwnx_radar_start_cac(&rwnx_hw->radar, cac_time_ms, rwnx_vif); rwnx_radar_start_cac(&rwnx_hw->radar, cac_time_ms, rwnx_vif);
#endif
rwnx_send_apm_start_cac_req(rwnx_hw, rwnx_vif, chandef, &cfm); rwnx_send_apm_start_cac_req(rwnx_hw, rwnx_vif, chandef, &cfm);
if (cfm.status == CO_OK) { if (cfm.status == CO_OK) {
@@ -5351,6 +5389,42 @@ int rwnx_cfg80211_start_radar_detection(struct wiphy *wiphy,
return 0; return 0;
} }
static int rwnx_cfg80211_start_radar_detection_legacy(
struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_chan_def *chandef)
{
return rwnx_cfg80211_start_radar_detection_common(
wiphy, dev, chandef, 0, false);
}
static int rwnx_cfg80211_start_radar_detection_cac(
struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_chan_def *chandef, u32 cac_time_ms)
{
return rwnx_cfg80211_start_radar_detection_common(
wiphy, dev, chandef, cac_time_ms, true);
}
static int rwnx_cfg80211_start_radar_detection_link(
struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_chan_def *chandef, u32 cac_time_ms, int link_id)
{
(void)link_id;
return rwnx_cfg80211_start_radar_detection_common(
wiphy, dev, chandef, cac_time_ms, true);
}
#define RWNX_CFG80211_START_RADAR_DETECTION_CB \
__builtin_choose_expr( \
RWNX_CFG80211_OP_MATCH(start_radar_detection, \
rwnx_start_radar_detection_link_t), \
rwnx_cfg80211_start_radar_detection_link, \
__builtin_choose_expr( \
RWNX_CFG80211_OP_MATCH(start_radar_detection, \
rwnx_start_radar_detection_cac_t), \
rwnx_cfg80211_start_radar_detection_cac, \
rwnx_cfg80211_start_radar_detection_legacy))
/** /**
* @update_ft_ies: Provide updated Fast BSS Transition information to the * @update_ft_ies: Provide updated Fast BSS Transition information to the
* driver. If the SME is in the driver/firmware, this information can be * driver. If the SME is in the driver/firmware, this information can be
@@ -6643,6 +6717,59 @@ static int rwnx_cfg80211_leave_mesh(struct wiphy *wiphy, struct net_device *dev)
return 0; return 0;
} }
/*
* Keep every direct cfg80211_ops assignment as a hard compile-time check.
* OpenWrt can backport cfg80211 APIs without changing LINUX_VERSION_CODE, and
* an incompatible callback must remain a build failure even when warnings are
* relaxed by an external package.
*/
RWNX_CFG80211_OP_ASSERT(add_virtual_intf, rwnx_cfg80211_add_iface);
RWNX_CFG80211_OP_ASSERT(del_virtual_intf, rwnx_cfg80211_del_iface);
RWNX_CFG80211_OP_ASSERT(change_virtual_intf, rwnx_cfg80211_change_iface);
RWNX_CFG80211_OP_ASSERT(start_p2p_device, rwnx_cfgp2p_start_p2p_device);
RWNX_CFG80211_OP_ASSERT(stop_p2p_device, rwnx_cfgp2p_stop_p2p_device);
RWNX_CFG80211_OP_ASSERT(scan, rwnx_cfg80211_scan);
RWNX_CFG80211_OP_ASSERT(connect, rwnx_cfg80211_connect);
RWNX_CFG80211_OP_ASSERT(disconnect, rwnx_cfg80211_disconnect);
RWNX_CFG80211_OP_ASSERT(add_key, rwnx_cfg80211_add_key);
RWNX_CFG80211_OP_ASSERT(get_key, rwnx_cfg80211_get_key);
RWNX_CFG80211_OP_ASSERT(del_key, rwnx_cfg80211_del_key);
RWNX_CFG80211_OP_ASSERT(set_default_key, rwnx_cfg80211_set_default_key);
RWNX_CFG80211_OP_ASSERT(set_default_mgmt_key,
rwnx_cfg80211_set_default_mgmt_key);
RWNX_CFG80211_OP_ASSERT(add_station, rwnx_cfg80211_add_station);
RWNX_CFG80211_OP_ASSERT(del_station, rwnx_cfg80211_del_station_compat);
RWNX_CFG80211_OP_ASSERT(change_station, rwnx_cfg80211_change_station);
RWNX_CFG80211_OP_ASSERT(mgmt_tx, rwnx_cfg80211_mgmt_tx);
RWNX_CFG80211_OP_ASSERT(start_ap, rwnx_cfg80211_start_ap);
RWNX_CFG80211_OP_ASSERT(change_beacon, rwnx_cfg80211_change_beacon);
RWNX_CFG80211_OP_ASSERT(stop_ap, rwnx_cfg80211_stop_ap);
RWNX_CFG80211_OP_ASSERT(probe_client, rwnx_cfg80211_probe_client);
RWNX_CFG80211_OP_ASSERT(set_txq_params, rwnx_cfg80211_set_txq_params);
RWNX_CFG80211_OP_ASSERT(set_power_mgmt, rwnx_cfg80211_set_power_mgmt);
RWNX_CFG80211_OP_ASSERT(get_station, rwnx_cfg80211_get_station);
RWNX_CFG80211_OP_ASSERT(remain_on_channel,
rwnx_cfg80211_remain_on_channel);
RWNX_CFG80211_OP_ASSERT(cancel_remain_on_channel,
rwnx_cfg80211_cancel_remain_on_channel);
RWNX_CFG80211_OP_ASSERT(dump_survey, rwnx_cfg80211_dump_survey);
RWNX_CFG80211_OP_ASSERT(get_channel, rwnx_cfg80211_get_channel);
RWNX_CFG80211_OP_ASSERT(update_ft_ies, rwnx_cfg80211_update_ft_ies);
RWNX_CFG80211_OP_ASSERT(set_cqm_rssi_config,
rwnx_cfg80211_set_cqm_rssi_config);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
RWNX_CFG80211_OP_ASSERT(channel_switch, rwnx_cfg80211_channel_switch);
#endif
RWNX_CFG80211_OP_ASSERT(change_bss, rwnx_cfg80211_change_bss);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) || \
defined(CONFIG_WPA3_FOR_OLD_KERNEL)
RWNX_CFG80211_OP_ASSERT(external_auth, rwnx_cfg80211_external_auth);
#endif
#ifdef CONFIG_SCHED_SCAN
RWNX_CFG80211_OP_ASSERT(sched_scan_start, rwnx_cfg80211_sched_scan_start);
RWNX_CFG80211_OP_ASSERT(sched_scan_stop, rwnx_cfg80211_sched_scan_stop);
#endif
static struct cfg80211_ops rwnx_cfg80211_ops = { static struct cfg80211_ops rwnx_cfg80211_ops = {
.add_virtual_intf = rwnx_cfg80211_add_iface, .add_virtual_intf = rwnx_cfg80211_add_iface,
.del_virtual_intf = rwnx_cfg80211_del_iface, .del_virtual_intf = rwnx_cfg80211_del_iface,
@@ -6664,7 +6791,7 @@ static struct cfg80211_ops rwnx_cfg80211_ops = {
.start_ap = rwnx_cfg80211_start_ap, .start_ap = rwnx_cfg80211_start_ap,
.change_beacon = rwnx_cfg80211_change_beacon, .change_beacon = rwnx_cfg80211_change_beacon,
.stop_ap = rwnx_cfg80211_stop_ap, .stop_ap = rwnx_cfg80211_stop_ap,
.set_monitor_channel = rwnx_cfg80211_set_monitor_channel, .set_monitor_channel = RWNX_CFG80211_SET_MONITOR_CHANNEL_CB,
.probe_client = rwnx_cfg80211_probe_client, .probe_client = rwnx_cfg80211_probe_client,
// .mgmt_frame_register = rwnx_cfg80211_mgmt_frame_register, // .mgmt_frame_register = rwnx_cfg80211_mgmt_frame_register,
.set_wiphy_params = RWNX_CFG80211_SET_WIPHY_PARAMS_CB, .set_wiphy_params = RWNX_CFG80211_SET_WIPHY_PARAMS_CB,
@@ -6677,7 +6804,7 @@ static struct cfg80211_ops rwnx_cfg80211_ops = {
.cancel_remain_on_channel = rwnx_cfg80211_cancel_remain_on_channel, .cancel_remain_on_channel = rwnx_cfg80211_cancel_remain_on_channel,
.dump_survey = rwnx_cfg80211_dump_survey, .dump_survey = rwnx_cfg80211_dump_survey,
.get_channel = rwnx_cfg80211_get_channel, .get_channel = rwnx_cfg80211_get_channel,
.start_radar_detection = rwnx_cfg80211_start_radar_detection, .start_radar_detection = RWNX_CFG80211_START_RADAR_DETECTION_CB,
.update_ft_ies = rwnx_cfg80211_update_ft_ies, .update_ft_ies = rwnx_cfg80211_update_ft_ies,
.set_cqm_rssi_config = rwnx_cfg80211_set_cqm_rssi_config, .set_cqm_rssi_config = rwnx_cfg80211_set_cqm_rssi_config,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+48 -15
View File
@@ -22,6 +22,52 @@
#include "rwnx_events.h" #include "rwnx_events.h"
#include "rwnx_compat.h" #include "rwnx_compat.h"
#ifdef CONFIG_RWNX_FULLMAC
/* OpenWrt can backport cfg80211 helpers without changing LINUX_VERSION_CODE. */
typedef void (*rwnx_cfg80211_cac_event_no_chandef_t)(
struct net_device *netdev, enum nl80211_radar_event event, gfp_t gfp);
typedef void (*rwnx_cfg80211_cac_event_chandef_t)(
struct net_device *netdev, const struct cfg80211_chan_def *chandef,
enum nl80211_radar_event event, gfp_t gfp);
typedef void (*rwnx_cfg80211_cac_event_link_t)(
struct net_device *netdev, const struct cfg80211_chan_def *chandef,
enum nl80211_radar_event event, gfp_t gfp, unsigned int link_id);
#define RWNX_CFG80211_CAC_EVENT_MATCH(_type) \
__builtin_types_compatible_p(typeof(&cfg80211_cac_event), _type)
typedef char rwnx_cfg80211_cac_event_signature_must_be_supported[
(RWNX_CFG80211_CAC_EVENT_MATCH(
rwnx_cfg80211_cac_event_no_chandef_t) ||
RWNX_CFG80211_CAC_EVENT_MATCH(rwnx_cfg80211_cac_event_chandef_t) ||
RWNX_CFG80211_CAC_EVENT_MATCH(rwnx_cfg80211_cac_event_link_t)) ? 1 : -1];
static void rwnx_cfg80211_cac_event_compat(
struct net_device *netdev, const struct cfg80211_chan_def *chandef,
enum nl80211_radar_event event, gfp_t gfp)
{
union {
typeof(&cfg80211_cac_event) actual;
rwnx_cfg80211_cac_event_no_chandef_t no_chandef;
rwnx_cfg80211_cac_event_chandef_t chandef;
rwnx_cfg80211_cac_event_link_t link;
} helper = { .actual = &cfg80211_cac_event };
if (RWNX_CFG80211_CAC_EVENT_MATCH(
rwnx_cfg80211_cac_event_no_chandef_t)) {
helper.no_chandef(netdev, event, gfp);
return;
}
if (RWNX_CFG80211_CAC_EVENT_MATCH(rwnx_cfg80211_cac_event_link_t)) {
helper.link(netdev, chandef, event, gfp, 0);
return;
}
helper.chandef(netdev, chandef, event, gfp);
}
#endif
/* /*
* tolerated deviation of radar time stamp in usecs on both sides * tolerated deviation of radar time stamp in usecs on both sides
* TODO: this might need to be HW-dependent * TODO: this might need to be HW-dependent
@@ -1471,15 +1517,8 @@ static void rwnx_radar_cac_work(struct work_struct *ws)
} }
ctxt = &rwnx_hw->chanctx_table[radar->cac_vif->ch_index]; ctxt = &rwnx_hw->chanctx_table[radar->cac_vif->ch_index];
cfg80211_cac_event(radar->cac_vif->ndev, rwnx_cfg80211_cac_event_compat(radar->cac_vif->ndev, &ctxt->chan_def,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
&ctxt->chan_def,
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
NL80211_RADAR_CAC_FINISHED, GFP_KERNEL, 0);
#else
NL80211_RADAR_CAC_FINISHED, GFP_KERNEL); NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
#endif
rwnx_send_apm_stop_cac_req(rwnx_hw, radar->cac_vif); rwnx_send_apm_stop_cac_req(rwnx_hw, radar->cac_vif);
rwnx_chanctx_unlink(radar->cac_vif); rwnx_chanctx_unlink(radar->cac_vif);
@@ -1613,15 +1652,9 @@ void rwnx_radar_cancel_cac(struct rwnx_radar *radar)
struct rwnx_chanctx *ctxt; struct rwnx_chanctx *ctxt;
ctxt = &rwnx_hw->chanctx_table[radar->cac_vif->ch_index]; ctxt = &rwnx_hw->chanctx_table[radar->cac_vif->ch_index];
rwnx_send_apm_stop_cac_req(rwnx_hw, radar->cac_vif); rwnx_send_apm_stop_cac_req(rwnx_hw, radar->cac_vif);
cfg80211_cac_event(radar->cac_vif->ndev, rwnx_cfg80211_cac_event_compat(radar->cac_vif->ndev,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
&ctxt->chan_def, &ctxt->chan_def,
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
NL80211_RADAR_CAC_FINISHED, GFP_KERNEL, 0);
#else
NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
#endif
rwnx_chanctx_unlink(radar->cac_vif); rwnx_chanctx_unlink(radar->cac_vif);
} }