cfg80211
概念
cfg80211 是 Linux 内核中一个用于无线网络的配置和管理的子系统,它为多个无线网络驱动提供了一个统一的接口,以便于无线设备的配置和操作。cfg80211 的设计目标是提供一个简化的管理和配置无线设备的框架,同时支持多种无线设备的标准和功能,其在内核中的/net/wireless/core.c文件会对wifi使用的cfg80211进行初始化。
iw,hostapd,wpa_supplicant 等通过Netlink 套接字和构建消息来与内核空间进行交互。当启动 AP 模式时,hostapd
会将请求发送到内核,内核接收并调用 cfg80211
的 start_ap等
函数来处理 AP 启动。这个设计使得用户空间能够灵活地管理无线接入点,同时保持与内核的高效通信
static int __init cfg80211_init(void)
{
int err;
err = register_pernet_device(&cfg80211_pernet_ops); //注册 cfg80211 作为一个网络命名空间的设备。这使得每个网络命名空间都能够独立地管理自己的无线设备
if (err)
goto out_fail_pernet;
err = wiphy_sysfs_init(); //此函数创建并初始化与 wiphy 相关的 SysFS 属性,允许用户通过 /sys 文件系统管理和查看无线设备的状态
if (err)
goto out_fail_sysfs;
err = register_netdevice_notifier(&cfg80211_netdev_notifier);
if (err)
goto out_fail_notifier;
err = nl80211_init(); //初始化 cfg80211 的 Netlink 接口,使得用户空间可以通过 Netlink 与内核进行无线配置和管理通信
if (err)
goto out_fail_nl80211;
ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL); //创建一个调试文件系统目录 ieee80211,用于调试和特定信息输出
err = regulatory_init(); //初始化监管域(Regulatory Domain)子系统,负责管理无线设备的频谱使用和符合法规的需求
if (err)
goto out_fail_reg;
cfg80211_wq = create_singlethread_workqueue("cfg80211"); //工作队列提供了异步任务处理机制,使得无线配置任务可以在后台线程中执行,不阻塞主要系统线程。
if (!cfg80211_wq) {
err = -ENOMEM;
goto out_fail_wq;
}
return 0;
}
cfg80211_netdev_notifier的实例
这个notifier,再被操作网络设备时,会执行cfg80211对应的操作:比如注册,启动,停止,注销等行为
static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
unsigned long state,
void *ndev)
{
struct net_device *dev = ndev;
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev;
int ret;
if (!wdev)
return NOTIFY_DONE;
rdev = wiphy_to_dev(wdev->wiphy);
WARN_ON(wdev->iftype == NL80211_IFTYPE_UNSPECIFIED);
switch (state) {
case NETDEV_POST_INIT:
SET_NETDEV_DEVTYPE(dev, &wiphy_type);
break;
case NETDEV_REGISTER:
/*
* NB: cannot take rdev->mtx here because this may be
* called within code protected by it when interfaces
* are added with nl80211.
*/
mutex_init(&wdev->mtx);
INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work);
INIT_LIST_HEAD(&wdev->event_list);
spin_lock_init(&wdev->event_lock);
INIT_LIST_HEAD(&wdev->mgmt_registrations);
spin_lock_init(&wdev->mgmt_registrations_lock);
mutex_lock(&rdev->devlist_mtx);
wdev->identifier = ++rdev->wdev_id;
list_add_rcu(&wdev->list, &rdev->wdev_list);
rdev->devlist_generation++;
/* can only change netns with wiphy */
dev->features |= NETIF_F_NETNS_LOCAL;
if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj,
"phy80211")) {
pr_err("failed to add phy80211 symlink to netdev!\n");
}
wdev->netdev = dev;
wdev->sme_state = CFG80211_SME_IDLE;
mutex_unlock(&rdev->devlist_mtx);
#ifdef CONFIG_CFG80211_WEXT
wdev->wext.default_key = -1;
wdev->wext.default_mgmt_key = -1;
wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
#endif
if (wdev->wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT)
wdev->ps = true;
else
wdev->ps = false;
/* allow mac80211 to determine the timeout */
wdev->ps_timeout = -1;
netdev_set_default_ethtool_ops(dev, &cfg80211_ethtool_ops);
if ((wdev->iftype == NL80211_IFTYPE_STATION ||
wdev->iftype == NL80211_IFTYPE_P2P_CLIENT ||
wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr)
dev->priv_flags |= IFF_DONT_BRIDGE;
break;
case NETDEV_GOING_DOWN:
cfg80211_leave(rdev, wdev);
break;
case NETDEV_DOWN:
cfg80211_update_iface_num(rdev, wdev->iftype, -1);
dev_hold(dev);
queue_work(cfg80211_wq, &wdev->cleanup_work);
break;
case NETDEV_UP:
/*
* If we have a really quick DOWN/UP succession we may
* have this work still pending ... cancel it and see
* if it was pending, in which case we need to account
* for some of the work it would have done.
*/
if (cancel_work_sync(&wdev->cleanup_work)) {
mutex_lock(&rdev->devlist_mtx);
rdev->opencount--;
mutex_unlock(&rdev->devlist_mtx);
dev_put(dev);
}
cfg80211_update_iface_num(rdev, wdev->iftype, 1);
cfg80211_lock_rdev(rdev);
mutex_lock(&rdev->devlist_mtx);
mutex_lock(&rdev->sched_scan_mtx);
wdev_lock(wdev);
switch (wdev->iftype) {
#ifdef CONFIG_CFG80211_WEXT
case NL80211_IFTYPE_ADHOC:
cfg80211_ibss_wext_join(rdev, wdev);
break;
case NL80211_IFTYPE_STATION:
cfg80211_mgd_wext_connect(rdev, wdev);
break;
#endif
#ifdef CONFIG_MAC80211_MESH
case NL80211_IFTYPE_MESH_POINT:
{
/* backward compat code... */
struct mesh_setup setup;
memcpy(&setup, &default_mesh_setup,
sizeof(setup));
/* back compat only needed for mesh_id */
setup.mesh_id = wdev->ssid;
setup.mesh_id_len = wdev->mesh_id_up_len;
if (wdev->mesh_id_up_len)
__cfg80211_join_mesh(rdev, dev,
&setup,
&default_mesh_config);
break;
}
#endif
default:
break;
}
wdev_unlock(wdev);
mutex_unlock(&rdev->sched_scan_mtx);
rdev->opencount++;
mutex_unlock(&rdev->devlist_mtx);
cfg80211_unlock_rdev(rdev);
/*
* Configure power management to the driver here so that its
* correctly set also after interface type changes etc.
*/
if ((wdev->iftype == NL80211_IFTYPE_STATION ||
wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) &&
rdev->ops->set_power_mgmt)
if (rdev_set_power_mgmt(rdev, dev, wdev->ps,
wdev->ps_timeout)) {
/* assume this means it's off */
wdev->ps = false;
}
break;
case NETDEV_UNREGISTER:
/*
* NB: cannot take rdev->mtx here because this may be
* called within code protected by it when interfaces
* are removed with nl80211.
*/
mutex_lock(&rdev->devlist_mtx);
/*
* It is possible to get NETDEV_UNREGISTER
* multiple times. To detect that, check
* that the interface is still on the list
* of registered interfaces, and only then
* remove and clean it up.
*/
if (!list_empty(&wdev->list)) {
sysfs_remove_link(&dev->dev.kobj, "phy80211");
list_del_rcu(&wdev->list);
rdev->devlist_generation++;
cfg80211_mlme_purge_registrations(wdev);
#ifdef CONFIG_CFG80211_WEXT
kfree(wdev->wext.keys);
#endif
}
mutex_unlock(&rdev->devlist_mtx);
/*
* synchronise (so that we won't find this netdev
* from other code any more) and then clear the list
* head so that the above code can safely check for
* !list_empty() to avoid double-cleanup.
*/
synchronize_rcu();
INIT_LIST_HEAD(&wdev->list);
/*
* Ensure that all events have been processed and
* freed.
*/
cfg80211_process_wdev_events(wdev);
break;
case NETDEV_PRE_UP:
if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype)))
return notifier_from_errno(-EOPNOTSUPP);
if (rfkill_blocked(rdev->rfkill))
return notifier_from_errno(-ERFKILL);
mutex_lock(&rdev->devlist_mtx);
ret = cfg80211_can_add_interface(rdev, wdev->iftype);
mutex_unlock(&rdev->devlist_mtx);
if (ret)
return notifier_from_errno(ret);
break;
}
return NOTIFY_DONE;
}
static struct notifier_block cfg80211_netdev_notifier = {
.notifier_call = cfg80211_netdev_notifier_call,
};
NETDEV_GOING_DOWN
比如调用这个通知链时的事件是down钓一个wlan0设备,就会调用cfg80211_leave的cfg80211_stop_ap
void cfg80211_leave(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev)
{
struct net_device *dev = wdev->netdev;
switch (wdev->iftype) {
case NL80211_IFTYPE_ADHOC:
cfg80211_leave_ibss(rdev, dev, true);
break;
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_STATION:
mutex_lock(&rdev->sched_scan_mtx);
__cfg80211_stop_sched_scan(rdev, false);
mutex_unlock(&rdev->sched_scan_mtx);
wdev_lock(wdev);
#ifdef CONFIG_CFG80211_WEXT
kfree(wdev->wext.ie);
wdev->wext.ie = NULL;
wdev->wext.ie_len = 0;
wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
#endif
__cfg80211_disconnect(rdev, dev,
WLAN_REASON_DEAUTH_LEAVING, true);
wdev_unlock(wdev);
break;
case NL80211_IFTYPE_MESH_POINT:
cfg80211_leave_mesh(rdev, dev);
break;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
cfg80211_stop_ap(rdev, dev);
break;
default:
break;
}
wdev->beacon_interval = 0;
}
会判断网络设备的ops有没有stop_ap成员
static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
struct net_device *dev)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
int err;
ASSERT_WDEV_LOCK(wdev);
if (!rdev->ops->stop_ap)
return -EOPNOTSUPP;
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
return -EOPNOTSUPP;
if (!wdev->beacon_interval)
return -ENOENT;
err = rdev_stop_ap(rdev, dev);
if (!err) {
wdev->beacon_interval = 0;
wdev->channel = NULL;
wdev->ssid_len = 0;
}
return err;
}
rdev_stop_ap会最终调用注册的设备的ops成员--stop_ap
static inline int rdev_stop_ap(struct cfg80211_registered_device *rdev,
struct net_device *dev)
{
int ret;
trace_rdev_stop_ap(&rdev->wiphy, dev);
ret = rdev->ops->stop_ap(&rdev->wiphy, dev);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
cfg80211_registered_device是怎么初始化的?
某个wifi驱动将具体实现的ops,赋值给要注册的cfg80211_registered_device的ops成员
void my_wifi_init(){
wiphy = wiphy_new(&ssv_cfg80211_ops, sizeof(struct ssv_softc));
}
struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
{
static int wiphy_counter;
struct cfg80211_registered_device *rdev;
int alloc_size;
WARN_ON(ops->add_key && (!ops->del_key || !ops->set_default_key));
WARN_ON(ops->auth && (!ops->assoc || !ops->deauth || !ops->disassoc));
WARN_ON(ops->connect && !ops->disconnect);
WARN_ON(ops->join_ibss && !ops->leave_ibss);
WARN_ON(ops->add_virtual_intf && !ops->del_virtual_intf);
WARN_ON(ops->add_station && !ops->del_station);
WARN_ON(ops->add_mpath && !ops->del_mpath);
WARN_ON(ops->join_mesh && !ops->leave_mesh);
alloc_size = sizeof(*rdev) + sizeof_priv;
rdev = kzalloc(alloc_size, GFP_KERNEL);
if (!rdev)
return NULL;
rdev->ops = ops;
mutex_lock(&cfg80211_mutex);
rdev->wiphy_idx = wiphy_counter++;
if (unlikely(rdev->wiphy_idx < 0)) {
wiphy_counter--;
mutex_unlock(&cfg80211_mutex);
/* ugh, wrapped! */
kfree(rdev);
return NULL;
}
mutex_unlock(&cfg80211_mutex);
/* give it a proper name */
dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx);
mutex_init(&rdev->mtx);
mutex_init(&rdev->devlist_mtx);
mutex_init(&rdev->sched_scan_mtx);
INIT_LIST_HEAD(&rdev->wdev_list);
INIT_LIST_HEAD(&rdev->beacon_registrations);
spin_lock_init(&rdev->beacon_registrations_lock);
spin_lock_init(&rdev->bss_lock);
INIT_LIST_HEAD(&rdev->bss_list);
INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done);
INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results);
INIT_DELAYED_WORK(&rdev->dfs_update_channels_wk,
cfg80211_dfs_channels_update_work);
#ifdef CONFIG_CFG80211_WEXT
rdev->wiphy.wext = &cfg80211_wext_handler;
#endif
device_initialize(&rdev->wiphy.dev);
rdev->wiphy.dev.class = &ieee80211_class;
rdev->wiphy.dev.platform_data = rdev;
#ifdef CONFIG_CFG80211_DEFAULT_PS
rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
#endif
wiphy_net_set(&rdev->wiphy, &init_net);
rdev->rfkill_ops.set_block = cfg80211_rfkill_set_block;
rdev->rfkill = rfkill_alloc(dev_name(&rdev->wiphy.dev),
&rdev->wiphy.dev, RFKILL_TYPE_WLAN,
&rdev->rfkill_ops, rdev);
if (!rdev->rfkill) {
kfree(rdev);
return NULL;
}
INIT_WORK(&rdev->rfkill_sync, cfg80211_rfkill_sync_work);
INIT_WORK(&rdev->conn_work, cfg80211_conn_work);
INIT_WORK(&rdev->event_work, cfg80211_event_work);
init_waitqueue_head(&rdev->dev_wait);
/*
* Initialize wiphy parameters to IEEE 802.11 MIB default values.
* Fragmentation and RTS threshold are disabled by default with the
* special -1 value.
*/
rdev->wiphy.retry_short = 7;
rdev->wiphy.retry_long = 4;
rdev->wiphy.frag_threshold = (u32) -1;
rdev->wiphy.rts_threshold = (u32) -1;
rdev->wiphy.coverage_class = 0;
rdev->wiphy.features = NL80211_FEATURE_SCAN_FLUSH;
return &rdev->wiphy;
}
EXPORT_SYMBOL(wiphy_new);
wifi驱动会初始化一个cfg80211_ops的实例,并填充对应的操作函数
struct cfg80211_ops ssv_cfg80211_ops = {
.add_virtual_intf = ssv_cfg80211_add_iface,
.del_virtual_intf = ssv_cfg80211_del_iface,
.change_virtual_intf = ssv_cfg80211_change_iface,
.scan = ssv_cfg80211_scan,
.connect = ssv_cfg80211_connect,
.disconnect = ssv_cfg80211_disconnect,
.add_key = ssv_cfg80211_add_key,
.get_key = ssv_cfg80211_get_key,
.del_key = ssv_cfg80211_del_key,
.set_default_key = ssv_cfg80211_set_default_key,
.set_default_mgmt_key = ssv_cfg80211_set_default_mgmt_key,
.add_station = ssv_cfg80211_add_station,
.del_station = ssv_cfg80211_del_station,
.change_station = ssv_cfg80211_change_station,
.mgmt_tx = ssv_cfg80211_mgmt_tx,
.start_ap = ssv_cfg80211_start_ap,
.change_beacon = ssv_cfg80211_change_beacon,
.stop_ap = ssv_cfg80211_stop_ap,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
.set_monitor_channel = ssv_cfg80211_set_monitor_channel,
#endif
.probe_client = ssv_cfg80211_probe_client,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
.update_mgmt_frame_registrations = ssv_cfg80211_update_mgmt_frame_registrations,
#else
.mgmt_frame_register = ssv_cfg80211_mgmt_frame_register,
#endif
.set_wiphy_params = ssv_cfg80211_set_wiphy_params,
.set_txq_params = ssv_cfg80211_set_txq_params,
.set_tx_power = ssv_cfg80211_set_tx_power,
.remain_on_channel = ssv_cfg80211_remain_on_channel,
.cancel_remain_on_channel = ssv_cfg80211_cancel_remain_on_channel,
.dump_survey = ssv_cfg80211_dump_survey,
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
.set_channel = ssv_cfg80211_set_channel,
#endif
.get_channel = ssv_cfg80211_get_channel,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
.update_ft_ies = ssv_cfg80211_update_ft_ies,
#endif
.set_cqm_rssi_config = ssv_cfg80211_set_cqm_rssi_config,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
.channel_switch = ssv_cfg80211_channel_switch,
#endif
.change_bss = ssv_cfg80211_change_bss,
.get_station = ssv_cfg80211_get_station,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) || defined(CONFIG_SUPPORT_WPA3))
.external_auth = ssv_cfg80211_external_auth,
#endif
};
比如stop_ap的实现如下
static int ssv_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
{
struct ssv_softc *sc = wiphy_priv(wiphy);
struct ssv_vif *ssv_vif = netdev_priv(dev);
struct ssv_sta *sta;
int mgmt_txq;
struct ssv_sta *cur, *tmp;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
struct station_del_parameters params;
params.mac = NULL;
#endif
SSV_LOG_DBG("[%s][%d]\n", __FUNCTION__, __LINE__);
sc->is_stoping_apm=true;
{ // turn off traffic for use_monitor interface
struct ssv_vif *tmp_vif = NULL;
list_for_each_entry(tmp_vif, &sc->vifs, list) {
if (tmp_vif->use_monitor) {
netif_tx_stop_all_queues(tmp_vif->ndev);
netif_carrier_off(tmp_vif->ndev);
break;
}
}
}
netif_tx_stop_all_queues(dev);
netif_carrier_off(dev);
mgmt_txq = (0 == ssv_vif->drv_vif_index) ? SSV_SW_TXQ_ID_MNG0 : SSV_SW_TXQ_ID_MNG1;
ssv_drv_hci_tx_pause_by_sta(sc->hci_priv, sc->hci_ops, mgmt_txq); //pause mgmt txq
ssv_drv_hci_tx_inactive_by_sta(sc->hci_priv, sc->hci_ops, mgmt_txq); //inactive mgmt txq
list_for_each_entry_safe(cur, tmp, &ssv_vif->ap.sta_list, list) {
ssv_drv_hci_tx_pause_by_sta(sc->hci_priv, sc->hci_ops, cur->sta_idx); //pause hci tx queue by sta
ssv_drv_hci_tx_inactive_by_sta(sc->hci_priv, sc->hci_ops, cur->sta_idx); //inactive hci tx queue by sta
}
msleep(200);
#if 0
ssv_send_apm_stop_req(sc, ssv_vif);
mutex_lock(&sc->cb_lock);
ssv_chanctx_unlink(ssv_vif);
mutex_unlock(&sc->cb_lock);
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
ssv_vif->bchan_setting = false;
#endif
/* delete any remaining STA*/
while (!list_empty(&ssv_vif->ap.sta_list))
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
ssv_cfg80211_del_station(wiphy, dev, ¶ms);
#else
ssv_cfg80211_del_station(wiphy, dev, NULL);
#endif
}
#if 1
//give more times to cousmer the all tx packets
//msleep(100);
ssv_send_apm_stop_req(sc, ssv_vif);
mutex_lock(&sc->cb_lock);
ssv_chanctx_unlink(ssv_vif);
mutex_unlock(&sc->cb_lock);
#endif
/* delete BC/MC STA */
sta = &sc->sta_table[ssv_vif->ap.bcmc_index];
ssv_del_bcn(&ssv_vif->ap.bcn);
ssv_del_csa(ssv_vif);
ssv_drv_hci_tx_active_by_sta(sc->hci_priv, sc->hci_ops, mgmt_txq); //active hci mng. queue
ssv_drv_hci_tx_resume_by_sta(sc->hci_priv, sc->hci_ops, mgmt_txq); //resume hci mng. queue
SSV_LOG_DBG(KERN_ERR"AP stop!!\r\n");
sc->is_stoping_apm=false;
return 0;
}
register_netdevice_notifier的使用
各网络模块的使用
主要是以通知链的形式,来对内核的各个模块。比如mac80211,ipv4/6,bridge,netfilter等模块也会调用register_netdevice_notifier注册对应函数,来响应网络设备的某种操作,以做出该模块需要做出的操作
int register_netdevice_notifier(struct notifier_block *nb)
{
struct net_device *dev;
struct net_device *last;
struct net *net;
int err;
rtnl_lock();
err = raw_notifier_chain_register(&netdev_chain, nb);
if (err)
goto unlock;
if (dev_boot_phase)
goto unlock;
for_each_net(net) {
for_each_netdev(net, dev) {
err = nb->notifier_call(nb, NETDEV_REGISTER, dev);
err = notifier_to_errno(err);
if (err)
goto rollback;
if (!(dev->flags & IFF_UP))
continue;
nb->notifier_call(nb, NETDEV_UP, dev);
}
}
unlock:
rtnl_unlock();
return err;
rollback:
last = dev;
for_each_net(net) {
for_each_netdev(net, dev) {
if (dev == last)
goto outroll;
if (dev->flags & IFF_UP) {
nb->notifier_call(nb, NETDEV_GOING_DOWN, dev);
nb->notifier_call(nb, NETDEV_DOWN, dev);
}
nb->notifier_call(nb, NETDEV_UNREGISTER, dev);
}
}
outroll:
raw_notifier_chain_unregister(&netdev_chain, nb);
goto unlock;
}
EXPORT_SYMBOL(register_netdevice_notifier);
比如bridge在初始化的时候也会调用register_netdevice_notifier来在netdev_chain链上注册一个对事件响应的notifier
static int __init br_init(void)
{
int err;
BUILD_BUG_ON(sizeof(struct br_input_skb_cb) > FIELD_SIZEOF(struct sk_buff, cb));
err = stp_proto_register(&br_stp_proto);
if (err < 0) {
pr_err("bridge: can't register sap for STP\n");
return err;
}
err = br_fdb_init();
if (err)
goto err_out;
err = register_pernet_subsys(&br_net_ops);
if (err)
goto err_out1;
err = br_nf_core_init();
if (err)
goto err_out2;
err = register_netdevice_notifier(&br_device_notifier);
if (err)
goto err_out3;
err = register_switchdev_notifier(&br_switchdev_notifier);
if (err)
goto err_out4;
err = br_netlink_init();
if (err)
goto err_out5;
brioctl_set(br_ioctl_deviceless_stub);
#if IS_ENABLED(CONFIG_ATM_LANE)
br_fdb_test_addr_hook = br_fdb_test_addr;
#endif
#if IS_MODULE(CONFIG_BRIDGE_NETFILTER)
pr_info("bridge: filtering via arp/ip/ip6tables is no longer available "
"by default. Update your scripts to load br_netfilter if you "
"need this.\n");
#endif
return 0;
err_out5:
unregister_switchdev_notifier(&br_switchdev_notifier);
err_out4:
unregister_netdevice_notifier(&br_device_notifier);
err_out3:
br_nf_core_fini();
err_out2:
unregister_pernet_subsys(&br_net_ops);
err_out1:
br_fdb_fini();
err_out:
stp_proto_unregister(&br_stp_proto);
return err;
}
会对各种事件,比如网络设备的up,down,register等做出桥的对应操作
static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct net_bridge_port *p;
struct net_bridge *br;
bool changed_addr;
int err;
/* register of bridge completed, add sysfs entries */
if ((dev->priv_flags & IFF_EBRIDGE) && event == NETDEV_REGISTER) {
br_sysfs_addbr(dev);
return NOTIFY_DONE;
}
/* not a port of a bridge */
p = br_port_get_rtnl(dev);
if (!p)
return NOTIFY_DONE;
br = p->br;
switch (event) {
case NETDEV_CHANGEMTU:
dev_set_mtu(br->dev, br_min_mtu(br));
break;
case NETDEV_CHANGEADDR:
spin_lock_bh(&br->lock);
br_fdb_changeaddr(p, dev->dev_addr);
changed_addr = br_stp_recalculate_bridge_id(br);
spin_unlock_bh(&br->lock);
if (changed_addr)
call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev);
break;
case NETDEV_CHANGE:
br_port_carrier_check(p);
break;
case NETDEV_FEAT_CHANGE:
netdev_update_features(br->dev);
break;
case NETDEV_DOWN:
spin_lock_bh(&br->lock);
if (br->dev->flags & IFF_UP)
br_stp_disable_port(p);
spin_unlock_bh(&br->lock);
break;
case NETDEV_UP:
if (netif_running(br->dev) && netif_oper_up(dev)) {
spin_lock_bh(&br->lock);
br_stp_enable_port(p);
spin_unlock_bh(&br->lock);
}
break;
case NETDEV_UNREGISTER:
br_del_if(br, dev);
break;
case NETDEV_CHANGENAME:
err = br_sysfs_renameif(p);
if (err)
return notifier_from_errno(err);
break;
case NETDEV_PRE_TYPE_CHANGE:
/* Forbid underlaying device to change its type. */
return NOTIFY_BAD;
case NETDEV_RESEND_IGMP:
/* Propagate to master device */
call_netdevice_notifiers(event, br->dev);
break;
}
/* Events that may cause spanning tree to refresh */
if (event == NETDEV_CHANGEADDR || event == NETDEV_UP ||
event == NETDEV_CHANGE || event == NETDEV_DOWN)
br_ifinfo_notify(RTM_NEWLINK, p);
return NOTIFY_DONE;
}
static struct notifier_block br_device_notifier = {
.notifier_call = br_device_event
};
ifconfig wlan0 up怎么实现的?
ioctl
针对这个最常见的命令,是通过ioctl来实现的,核心在/net/core/dev_ioctl.c
int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
{
struct ifreq ifr;
int ret;
char *colon;
/* One special case: SIOCGIFCONF takes ifconf argument
and requires shared lock, because it sleeps writing
to user space.
*/
if (cmd == SIOCGIFCONF) {
rtnl_lock();
ret = dev_ifconf(net, (char __user *) arg);
rtnl_unlock();
return ret;
}
if (cmd == SIOCGIFNAME)
return dev_ifname(net, (struct ifreq __user *)arg);
/*
* Take care of Wireless Extensions. Unfortunately struct iwreq
* isn't a proper subset of struct ifreq (it's 8 byte shorter)
* so we need to treat it specially, otherwise applications may
* fault if the struct they're passing happens to land at the
* end of a mapped page.
*/
if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
struct iwreq iwr;
if (copy_from_user(&iwr, arg, sizeof(iwr)))
return -EFAULT;
iwr.ifr_name[sizeof(iwr.ifr_name) - 1] = 0;
return wext_handle_ioctl(net, &iwr, cmd, arg);
}
if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
return -EFAULT;
ifr.ifr_name[IFNAMSIZ-1] = 0;
colon = strchr(ifr.ifr_name, ':');
if (colon)
*colon = 0;
/*
* See which interface the caller is talking about.
*/
switch (cmd) {
/*
* These ioctl calls:
* - can be done by all.
* - atomic and do not require locking.
* - return a value
*/
case SIOCGIFFLAGS:
case SIOCGIFMETRIC:
case SIOCGIFMTU:
case SIOCGIFHWADDR:
case SIOCGIFSLAVE:
case SIOCGIFMAP:
case SIOCGIFINDEX:
case SIOCGIFTXQLEN:
dev_load(net, ifr.ifr_name);
rcu_read_lock();
ret = dev_ifsioc_locked(net, &ifr, cmd);
rcu_read_unlock();
if (!ret) {
if (colon)
*colon = ':';
if (copy_to_user(arg, &ifr,
sizeof(struct ifreq)))
ret = -EFAULT;
}
return ret;
case SIOCETHTOOL:
dev_load(net, ifr.ifr_name);
rtnl_lock();
ret = dev_ethtool(net, &ifr);
rtnl_unlock();
if (!ret) {
if (colon)
*colon = ':';
if (copy_to_user(arg, &ifr,
sizeof(struct ifreq)))
ret = -EFAULT;
}
return ret;
/*
* These ioctl calls:
* - require superuser power.
* - require strict serialization.
* - return a value
*/
case SIOCGMIIPHY:
case SIOCGMIIREG:
case SIOCSIFNAME:
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
dev_load(net, ifr.ifr_name);
rtnl_lock();
ret = dev_ifsioc(net, &ifr, cmd);
rtnl_unlock();
if (!ret) {
if (colon)
*colon = ':';
if (copy_to_user(arg, &ifr,
sizeof(struct ifreq)))
ret = -EFAULT;
}
return ret;
/*
* These ioctl calls:
* - require superuser power.
* - require strict serialization.
* - do not return a value
*/
case SIOCSIFMAP:
case SIOCSIFTXQLEN:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
/* fall through */
/*
* These ioctl calls:
* - require local superuser power.
* - require strict serialization.
* - do not return a value
*/
case SIOCSIFFLAGS:
case SIOCSIFMETRIC:
case SIOCSIFMTU:
case SIOCSIFHWADDR:
case SIOCSIFSLAVE:
case SIOCADDMULTI:
case SIOCDELMULTI:
case SIOCSIFHWBROADCAST:
case SIOCSMIIREG:
case SIOCBONDENSLAVE:
case SIOCBONDRELEASE:
case SIOCBONDSETHWADDR:
case SIOCBONDCHANGEACTIVE:
case SIOCBRADDIF:
case SIOCBRDELIF:
case SIOCSHWTSTAMP:
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
/* fall through */
case SIOCBONDSLAVEINFOQUERY:
case SIOCBONDINFOQUERY:
dev_load(net, ifr.ifr_name);
rtnl_lock();
ret = dev_ifsioc(net, &ifr, cmd);
rtnl_unlock();
return ret;
case SIOCGIFMEM:
/* Get the per device memory space. We can add this but
* currently do not support it */
case SIOCSIFMEM:
/* Set the per device memory buffer space.
* Not applicable in our case */
case SIOCSIFLINK:
return -ENOTTY;
/*
* Unknown or private ioctl.
*/
default:
if (cmd == SIOCWANDEV ||
cmd == SIOCGHWTSTAMP ||
(cmd >= SIOCDEVPRIVATE &&
cmd <= SIOCDEVPRIVATE + 15)) {
dev_load(net, ifr.ifr_name);
rtnl_lock();
ret = dev_ifsioc(net, &ifr, cmd);
rtnl_unlock();
if (!ret && copy_to_user(arg, &ifr,
sizeof(struct ifreq)))
ret = -EFAULT;
return ret;
}
return -ENOTTY;
}
}
dev_ifsioc就是对ifocnfig命令做出响应的具体函数
static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
{
int err;
struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name);
const struct net_device_ops *ops;
if (!dev)
return -ENODEV;
ops = dev->netdev_ops;
switch (cmd) {
case SIOCSIFFLAGS: /* Set interface flags */
return dev_change_flags(dev, ifr->ifr_flags);
case SIOCSIFMETRIC: /* Set the metric on the interface
(currently unused) */
return -EOPNOTSUPP;
case SIOCSIFMTU: /* Set the MTU of a device */
return dev_set_mtu(dev, ifr->ifr_mtu);
case SIOCSIFHWADDR:
if (dev->addr_len > sizeof(struct sockaddr))
return -EINVAL;
return dev_set_mac_address(dev, &ifr->ifr_hwaddr);
case SIOCSIFHWBROADCAST:
if (ifr->ifr_hwaddr.sa_family != dev->type)
return -EINVAL;
memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data,
min(sizeof(ifr->ifr_hwaddr.sa_data),
(size_t)dev->addr_len));
call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
return 0;
case SIOCSIFMAP:
if (ops->ndo_set_config) {
if (!netif_device_present(dev))
return -ENODEV;
return ops->ndo_set_config(dev, &ifr->ifr_map);
}
return -EOPNOTSUPP;
case SIOCADDMULTI:
if (!ops->ndo_set_rx_mode ||
ifr->ifr_hwaddr.sa_family != AF_UNSPEC)
return -EINVAL;
if (!netif_device_present(dev))
return -ENODEV;
return dev_mc_add_global(dev, ifr->ifr_hwaddr.sa_data);
case SIOCDELMULTI:
if (!ops->ndo_set_rx_mode ||
ifr->ifr_hwaddr.sa_family != AF_UNSPEC)
return -EINVAL;
if (!netif_device_present(dev))
return -ENODEV;
return dev_mc_del_global(dev, ifr->ifr_hwaddr.sa_data);
case SIOCSIFTXQLEN:
if (ifr->ifr_qlen < 0)
return -EINVAL;
if (dev->tx_queue_len ^ ifr->ifr_qlen) {
unsigned int orig_len = dev->tx_queue_len;
dev->tx_queue_len = ifr->ifr_qlen;
err = call_netdevice_notifiers(
NETDEV_CHANGE_TX_QUEUE_LEN, dev);
err = notifier_to_errno(err);
if (err) {
dev->tx_queue_len = orig_len;
return err;
}
}
return 0;
case SIOCSIFNAME:
ifr->ifr_newname[IFNAMSIZ-1] = '\0';
return dev_change_name(dev, ifr->ifr_newname);
case SIOCSHWTSTAMP:
err = net_hwtstamp_validate(ifr);
if (err)
return err;
/* fall through */
/*
* Unknown or private ioctl
*/
default:
if ((cmd >= SIOCDEVPRIVATE &&
cmd <= SIOCDEVPRIVATE + 15) ||
cmd == SIOCBONDENSLAVE ||
cmd == SIOCBONDRELEASE ||
cmd == SIOCBONDSETHWADDR ||
cmd == SIOCBONDSLAVEINFOQUERY ||
cmd == SIOCBONDINFOQUERY ||
cmd == SIOCBONDCHANGEACTIVE ||
cmd == SIOCGMIIPHY ||
cmd == SIOCGMIIREG ||
cmd == SIOCSMIIREG ||
cmd == SIOCBRADDIF ||
cmd == SIOCBRDELIF ||
cmd == SIOCSHWTSTAMP ||
cmd == SIOCGHWTSTAMP ||
cmd == SIOCWANDEV) {
err = -EOPNOTSUPP;
if (ops->ndo_do_ioctl) {
if (netif_device_present(dev))
err = ops->ndo_do_ioctl(dev, ifr, cmd);
else
err = -ENODEV;
}
} else
err = -EINVAL;
}
return err;
}
这个dev_change_flags就会检测对应的flag是否更改,以对更改的做出响应
int dev_change_flags(struct net_device *dev, unsigned int flags)
{
int ret;
unsigned int changes, old_flags = dev->flags, old_gflags = dev->gflags;
ret = __dev_change_flags(dev, flags);
if (ret < 0)
return ret;
changes = (old_flags ^ dev->flags) | (old_gflags ^ dev->gflags);
__dev_notify_flags(dev, old_flags, changes);
return ret;
}
call_netdevice_notifiers
最终通过通知连的形式去调用netdev_chain链的通知函数,cfg80211注册的有,最终是调用的是网络设备驱动提供的函数
void __dev_notify_flags(struct net_device *dev, unsigned int old_flags)
{
unsigned int changes = dev->flags ^ old_flags;
if (changes & IFF_UP) {
if (dev->flags & IFF_UP)
call_netdevice_notifiers(NETDEV_UP, dev);
else
call_netdevice_notifiers(NETDEV_DOWN, dev);
}
if (dev->flags & IFF_UP &&
(changes & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI | IFF_VOLATILE)))
call_netdevice_notifiers(NETDEV_CHANGE, dev);
}
int call_netdevice_notifiers(unsigned long val, struct net_device *dev)
{
ASSERT_RTNL();
return raw_notifier_call_chain(&netdev_chain, val, dev);
}
EXPORT_SYMBOL(call_netdevice_notifiers);
wlan0的注册
通过register_netdevice来注册网络设备,比如wlan0;call_netdevice_notifiers(NETDEV_POST_INIT, dev)调用事件为初始化call_netdevice_notifiers(NETDEV_REGISTER, dev);调用事件为注册
int register_netdevice(struct net_device *dev)
{
int ret;
struct net *net = dev_net(dev);
BUG_ON(dev_boot_phase);
ASSERT_RTNL();
might_sleep();
/* When net_device's are persistent, this will be fatal. */
BUG_ON(dev->reg_state != NETREG_UNINITIALIZED);
BUG_ON(!net);
spin_lock_init(&dev->addr_list_lock);
netdev_set_addr_lockdep_class(dev);
ret = dev_get_valid_name(net, dev, dev->name);
if (ret < 0)
goto out;
/* Init, if this function is available */
if (dev->netdev_ops->ndo_init) {
ret = dev->netdev_ops->ndo_init(dev);
if (ret) {
if (ret > 0)
ret = -EIO;
goto out;
}
}
if (((dev->hw_features | dev->features) &
NETIF_F_HW_VLAN_CTAG_FILTER) &&
(!dev->netdev_ops->ndo_vlan_rx_add_vid ||
!dev->netdev_ops->ndo_vlan_rx_kill_vid)) {
netdev_WARN(dev, "Buggy VLAN acceleration in driver!\n");
ret = -EINVAL;
goto err_uninit;
}
ret = -EBUSY;
if (!dev->ifindex)
dev->ifindex = dev_new_index(net);
else if (__dev_get_by_index(net, dev->ifindex))
goto err_uninit;
/* Transfer changeable features to wanted_features and enable
* software offloads (GSO and GRO).
*/
dev->hw_features |= NETIF_F_SOFT_FEATURES;
dev->features |= NETIF_F_SOFT_FEATURES;
if (dev->netdev_ops->ndo_udp_tunnel_add) {
dev->features |= NETIF_F_RX_UDP_TUNNEL_PORT;
dev->hw_features |= NETIF_F_RX_UDP_TUNNEL_PORT;
}
dev->wanted_features = dev->features & dev->hw_features;
if (!(dev->flags & IFF_LOOPBACK))
dev->hw_features |= NETIF_F_NOCACHE_COPY;
/* If IPv4 TCP segmentation offload is supported we should also
* allow the device to enable segmenting the frame with the option
* of ignoring a static IP ID value. This doesn't enable the
* feature itself but allows the user to enable it later.
*/
if (dev->hw_features & NETIF_F_TSO)
dev->hw_features |= NETIF_F_TSO_MANGLEID;
if (dev->vlan_features & NETIF_F_TSO)
dev->vlan_features |= NETIF_F_TSO_MANGLEID;
if (dev->mpls_features & NETIF_F_TSO)
dev->mpls_features |= NETIF_F_TSO_MANGLEID;
if (dev->hw_enc_features & NETIF_F_TSO)
dev->hw_enc_features |= NETIF_F_TSO_MANGLEID;
/* Make NETIF_F_HIGHDMA inheritable to VLAN devices.
*/
dev->vlan_features |= NETIF_F_HIGHDMA;
/* Make NETIF_F_SG inheritable to tunnel devices.
*/
dev->hw_enc_features |= NETIF_F_SG | NETIF_F_GSO_PARTIAL;
/* Make NETIF_F_SG inheritable to MPLS.
*/
dev->mpls_features |= NETIF_F_SG;
ret = call_netdevice_notifiers(NETDEV_POST_INIT, dev); //调用初始化
ret = notifier_to_errno(ret);
if (ret)
goto err_uninit;
ret = netdev_register_kobject(dev);
if (ret)
goto err_uninit;
dev->reg_state = NETREG_REGISTERED;
__netdev_update_features(dev);
/*
* Default initial state at registry is that the
* device is present.
*/
set_bit(__LINK_STATE_PRESENT, &dev->state);
linkwatch_init_dev(dev);
dev_init_scheduler(dev);
dev_hold(dev);
list_netdevice(dev);
add_device_randomness(dev->dev_addr, dev->addr_len);
/* If the device has permanent device address, driver should
* set dev_addr and also addr_assign_type should be set to
* NET_ADDR_PERM (default value).
*/
if (dev->addr_assign_type == NET_ADDR_PERM)
memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
/* Notify protocols, that a new device appeared. */
ret = call_netdevice_notifiers(NETDEV_REGISTER, dev); //调用注册
ret = notifier_to_errno(ret);
if (ret) {
rollback_registered(dev);
dev->reg_state = NETREG_UNREGISTERED;
}
/*
* Prevent userspace races by waiting until the network
* device is fully setup before sending notifications.
*/
if (!dev->rtnl_link_ops ||
dev->rtnl_link_state == RTNL_LINK_INITIALIZED)
rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U, GFP_KERNEL);
out:
return ret;
err_uninit:
if (dev->netdev_ops->ndo_uninit)
dev->netdev_ops->ndo_uninit(dev);
if (dev->priv_destructor)
dev->priv_destructor(dev);
goto out;
}
EXPORT_SYMBOL(register_netdevice);