Re: [PATCH net-next v3] wireless-drivers: rtnetlink wifi simulation device
From: Cody Schuffelen
Date: Tue Nov 20 2018 - 22:20:18 EST
> > + informed_bss =
> > + cfg80211_inform_bss_data(wiphy, &mock_inform_bss,
> > + CFG80211_BSS_FTYPE_PRESP,
> > + fake_router_bssid,
> > + mock_inform_bss.boottime_ns,
> > + WLAN_CAPABILITY_ESS, 0, ssid.data,
> > + sizeof(ssid), GFP_KERNEL);
>
> It is possible to simplify this part switching to cfg80211_inform_bss
> function: this function wraps your scan data in into cfg80211_inform_bss
> structure internally using some reasonable defaults, e.g. channel width.
>
> Besides, signal strength for scan entries should be passed in mBm units,
> so use DBM_TO_MBM macro. For instance, with your current code 'iw' tool
> produces the following output:
> $ sudo iw dev wlan0 scan
> ...
> signal: 0.-60 dBm
> ...
Good catch, fixed.
> > +static void virt_wifi_connect_complete(struct work_struct *work)
> > +{
> > + struct virt_wifi_priv *priv =
> > + container_of(work, struct virt_wifi_priv, connect.work);
> > + u8 *requested_bss = priv->connect_requested_bss;
> > + bool has_addr = !is_zero_ether_addr(requested_bss);
> > + bool right_addr = ether_addr_equal(requested_bss, fake_router_bssid);
> > + u16 status = WLAN_STATUS_SUCCESS;
> > +
> > + rtnl_lock();
> > + if (!priv->netdev_is_up || (has_addr && !right_addr))
> > + status = WLAN_STATUS_UNSPECIFIED_FAILURE;
> > + else
> > + priv->is_connected = true;
> > +
> > + cfg80211_connect_result(priv->netdev, requested_bss, NULL, 0, NULL, 0,
> > + status, GFP_KERNEL);
> > + rtnl_unlock();
> > +}
>
> Carrier state for wireless device depends on its connection state.
> E.g., carrier is set when wireless connection succeeds and cleared
> when device disconnects. So use netif_carrier_on/netif_carrier_off
> calls in connect/disconnect handlers to set correct carrier state.
> IIUC the following locations look reasonable:
> - netif_carrier_off on init
> - netif_carrier_on in virt_wifi_connect_complete on success
> - netif_carrier_off in virt_wifi_disconnect
Thanks, added.
> > +static void virt_wifi_disconnect_complete(struct work_struct *work)
> > +{
> > + struct virt_wifi_priv *priv =
> > + container_of(work, struct virt_wifi_priv, disconnect.work);
> > +
> > + cfg80211_disconnected(priv->netdev, priv->disconnect_reason, NULL, 0,
> > + true, GFP_KERNEL);
> > + priv->is_connected = false;
> > +}
>
> Why do you need delayed disconnect processing ? IIUC it can be dropped
> and cfg80211_disconnected call can be moved to virt_wifi_disconnect.
Done.
> > +
> > +static int virt_wifi_get_station(struct wiphy *wiphy,
> > + struct net_device *dev,
> > + const u8 *mac,
> > + struct station_info *sinfo)
> > +{
> > + wiphy_debug(wiphy, "get_station\n");
> > +
> > + if (!ether_addr_equal(mac, fake_router_bssid))
> > + return -ENOENT;
> > +
> > + sinfo->filled = BIT(NL80211_STA_INFO_TX_PACKETS) |
> > + BIT(NL80211_STA_INFO_TX_FAILED) | BIT(NL80211_STA_INFO_SIGNAL) |
> > + BIT(NL80211_STA_INFO_TX_BITRATE);
>
> Recently some of NL80211_STA_INFO_ attribute types has been modified to
> use BIT_ULL macro. Could you please check commit 22d0d2fafca93ba1d92a
> for details and modify your coded if needed.
Thanks for the the reference, updated to use BIT_ULL with the station commands.
> > + sinfo->tx_packets = 1;
>
> Only one packet, really ? Not sure if you plan to use the output of 'iw'
> or any other tool. If yes, then it probably makes sense to use stats
> from the original network link. Otherwise, your 'iw' output is
> going to look like this:
>
> $ iw dev wlan0 station dump
> ...
> tx packets: 1
> ...
>
> > + sinfo->tx_failed = 0;
>
> ...
Added bookkeeping to the net_device packet forwarded to track how many
packets were sent, and how many failed being sent due to no
connection.
> > +static int virt_wifi_dump_station(struct wiphy *wiphy,
> > + struct net_device *dev,
> > + int idx,
> > + u8 *mac,
> > + struct station_info *sinfo)
> > +{
> > + wiphy_debug(wiphy, "dump_station\n");
> > +
> > + if (idx != 0)
> > + return -ENOENT;
> > +
> > + ether_addr_copy(mac, fake_router_bssid);
> > + return virt_wifi_get_station(wiphy, dev, fake_router_bssid, sinfo);
> > +}
>
> Callback dump_station should return AP data only when STA is connected.
> Currently your driver returns fake AP data even when it is not
> connected.
Thanks, fixed.
> > +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,
> > +};
>
> Hey, this minimal cfg80211 implementation works fine with wpa_supplicant
> and open AP config. By the way, if you plan to add more features, then
> I would suggest to consider the following cfg80211 callbacks:
> - change_station, get_channel
> to provide more info in connected state, e.g. compare the output
> of the following commands between your virtual interface and
> actual wireless interface:
> $ iw dev wlan0 link
> $ iw dev wlan0 info
>
> - stubs for add_key, del_key to enable encrypted AP simulation
Thanks for testing it out!
I've uploaded the new version here: https://lkml.org/lkml/2018/11/21/251