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, &params);
#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);

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部