Re: Wifi Tethering
by Daniel Wagner
[Cc: iwd mailing list]
On Tue, Apr 28, 2020 at 06:45:25AM -0500, KeithG wrote:
> Daniel,
>
> I built the kernel/modules (I am on Arch on an RPi and the kernel I am
> using is 4.19.114) and tried to diagnose the ap mode problem with nlmon and
> get this, though I am sure I am doing it wrong:
> # modprobe nlmon
> # ip link add name nlmon type nlmon
> # ip link set dev nlmon allmulticast on
> # ip link set dev nlmon up
> # tcpdump -i nlmon -w trace-file.pcap
> tcpdump: listening on nlmon, link-type NETLINK (Linux netlink), capture
> size 262144 bytes
> ^C158 packets captured
> 163 packets received by filter
> 0 packets dropped by kernel
> # iwmon -r trace-file.pcap
> Wireless monitor ver 1.6
> Invalid packet format
Could you upload the pcap file somewhere? I suppose the iwd developers might
be interested to inspect it.
> In another window, I did this to see what was going on:
> ~# connmanctl tether wifi on myssid password
> Wifi SSID set
> Wifi passphrase set
> Enabled tethering for wifi
> # ip addr
> 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group
> default qlen 1000
> link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
> inet 127.0.0.1/8 scope host lo
> valid_lft forever preferred_lft forever
> inet6 ::1/128 scope host
> valid_lft forever preferred_lft forever
> 2: eth0: <BROADCAST,MULTICAST,DYNAMIC,UP,LOWER_UP> mtu 1500 qdisc fq_codel
> state UP group default qlen 1000
> link/ether a0:ce:c8:12:ed:05 brd ff:ff:ff:ff:ff:ff
> inet 192.168.2.25/24 brd 192.168.2.255 scope global eth0
> valid_lft forever preferred_lft forever
> inet6 fe80::a2ce:c8ff:fe12:ed05/64 scope link
> valid_lft forever preferred_lft forever
> 3: wlan0: <NO-CARRIER,BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc
> fq_codel master tether state DORMANT group default qlen 1000
> link/ether b8:27:eb:28:18:48 brd ff:ff:ff:ff:ff:ff
> 4: nlmon: <NOARP,ALLMULTI,UP,LOWER_UP> mtu 3904 qdisc noqueue state UNKNOWN
> group default qlen 1000
> link/netlink
> 5: tether: <NO-CARRIER,BROADCAST,MULTICAST,DYNAMIC,UP> mtu 1500 qdisc
> noqueue state DOWN group default qlen 1000
> link/ether 9a:69:3a:48:c1:32 brd ff:ff:ff:ff:ff:ff
> inet 192.168.0.1/24 brd 192.168.0.255 scope global tether
> valid_lft forever preferred_lft forever
> inet6 fe80::c20:faff:fec4:92b9/64 scope link
> valid_lft forever preferred_lft forever
>
> I tried to connect to this AP with my Win10 laptop and get the response:
> "Can't connect to this network". My Arch Laptop likewise will not connect.
>
> Now, when I run iwd -d, I get this:
> # /usr/lib/iwd/iwd -d
> No Diffie-Hellman support found, WPS will not be available
> No asymmetric key support found.
> TLS based WPA-Enterprise authentication methods will not function.
> Kernel 4.20+ is required for this feature.
> The following options are missing in the kernel:
> CONFIG_ASYMMETRIC_KEY_TYPE
> CONFIG_KEY_DH_OPERATIONS
> CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE
> CONFIG_PKCS7_MESSAGE_PARSER
> CONFIG_X509_CERTIFICATE_PARSER
> CONFIG_PKCS8_PRIVATE_KEY_PARSER
> Wireless daemon version 1.6
> src/main.c:main() Using configuration directory /etc/iwd
> src/storage.c:storage_create_dirs() Using state directory /var/lib/iwd
> src/main.c:nl80211_appeared() Found nl80211 interface
> src/module.c:iwd_modules_init()
> src/netdev.c:netdev_init() Opening route netlink socket
> netconfig: Network configuration is disabled.
> src/wsc.c:wsc_init()
> src/eap.c:__eap_method_enable()
> src/eap-wsc.c:eap_wsc_init()
> src/eap-md5.c:eap_md5_init()
> src/eap-tls.c:eap_tls_init()
> src/eap-ttls.c:eap_ttls_init()
> src/eap-mschapv2.c:eap_mschapv2_init()
> src/eap-sim.c:eap_sim_init()
> src/eap-aka.c:eap_aka_prime_init()
> src/eap-aka.c:eap_aka_init()
> src/eap-peap.c:eap_peap_init()
> src/eap-gtc.c:eap_gtc_init()
> src/eap-pwd.c:eap_pwd_init()
> plugins/sim_hardcoded.c:sim_hardcoded_init() IWD_SIM_KEYS not set in env
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/wiphy.c:parse_supported_bands()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/wiphy.c:parse_supported_bands()
> src/wiphy.c:parse_supported_frequencies()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/wiphy.c:parse_supported_bands()
> src/wiphy.c:parse_supported_frequencies()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/wiphy.c:parse_supported_bands()
> src/wiphy.c:parse_supported_frequencies()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/wiphy.c:parse_supported_bands()
> src/wiphy.c:parse_supported_frequencies()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/wiphy.c:parse_supported_bands()
> src/wiphy.c:parse_supported_frequencies()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/wiphy.c:parse_supported_bands()
> src/wiphy.c:parse_supported_frequencies()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/wiphy.c:parse_supported_bands()
> src/wiphy.c:parse_supported_frequencies()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/wiphy.c:parse_supported_bands()
> src/wiphy.c:parse_supported_frequencies()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/wiphy.c:parse_supported_bands()
> src/wiphy.c:parse_supported_frequencies()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/wiphy.c:parse_supported_bands()
> src/wiphy.c:parse_supported_frequencies()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/wiphy.c:parse_supported_bands()
> src/wiphy.c:parse_supported_frequencies()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/wiphy.c:parse_supported_bands()
> src/wiphy.c:parse_supported_frequencies()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/wiphy.c:parse_supported_bands()
> src/wiphy.c:parse_supported_frequencies()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/wiphy.c:parse_supported_bands()
> src/wiphy.c:parse_supported_frequencies()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/wiphy.c:parse_supported_bands()
> src/wiphy.c:parse_supported_frequencies()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/wiphy.c:parse_supported_bands()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/manager.c:manager_wiphy_dump_callback()
> src/wiphy.c:wiphy_update_from_genl()
> src/agent.c:agent_register() agent register called
> src/agent.c:agent_register() agent :1.7 path /net/connman/iwd_agent
> Wiphy: 0, Name: phy0
> Permanent Address: b8:27:eb:28:18:48
> Bands: 2.4 GHz
> Ciphers: CCMP TKIP
> Supported iftypes: ad-hoc station ap p2p-client p2p-go p2p-device
> Wiphy phy0 will only use the default interface
> src/manager.c:manager_interface_dump_callback()
> src/manager.c:manager_get_interface_cb()
> src/manager.c:manager_use_default()
> src/netdev.c:netdev_create_from_genl() Created interface wlan0[3 1]
> src/netdev.c:netdev_link_notify() event 16 on ifindex 3
> src/netdev.c:netdev_set_4addr() netdev: 3 use_4addr: 0
> src/netdev.c:netdev_initial_up_cb() Interface 3 initialized
>
> strange thing is that most of those features are built in the kernel. My
> kernel is 4.19.114 and the config specifies:
> # CONFIG_CRYPTO_HW is not set
> CONFIG_ASYMMETRIC_KEY_TYPE=y
> CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y
> CONFIG_X509_CERTIFICATE_PARSER=y
> CONFIG_PKCS7_MESSAGE_PARSER=y
> I am currently re-building the kernel with:
> CONFIG_KEY_DH_OPERATIONS=y
> as it was previously not set.
> There does not seem to be any reference to PKCS8 for this kernel.
>
> Thanks!
>
> Keith
>
> On Mon, Apr 27, 2020 at 2:36 AM Daniel Wagner <wagi(a)monom.org> wrote:
>
> > On Sat, Apr 25, 2020 at 11:20:45AM -0500, KeithG wrote:
> > > I looked at the IWD readme and cannot enable this on the RPi:
> > >
> > > # ip link set dev nlmon allmulticast on
> > > > Cannot find device "nlmon"
> >
> > The RPi kernel has no support for nlmon enabled. If you want to debug this
> > you
> > propably need to compile your own RPi kernel with nlmon enabled.
> >
> > > I did verify a couple things. I do have iwd set explicitly when I start
> > > connman
> > > /usr/bin/connmand --wifi=iwd_agent -n --nodnsproxy
> >
> > --wifi=iwd_agent is wrong. If you want iwd support you need to define this
> > at compile time only:
> >
> > ./configure --enable-iwd --disable-wifi
> >
> > which adds the iwd plugin and disables the wpa_supplicant plugin. But I
> > don't
> > think it matter. --iwfi-iwd_agent will be ignored and ConnMan will
> > dynamically
> > discover iwd. Just make sure wpa_supplicant is not running. With the above
> > command line you would make sure wpa_supplicant is not accidentally used.
> >
> > > When I issue the command from connman, the mode changes in iwd:
> > >
> > > > # connmanctl tether wifi on myssid password
> > > > Wifi SSID set
> > > > Wifi passphrase set
> > > > Enabled tethering for wifi
> > > > # ip addr
> > > > 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
> > group
> > > > default qlen 1000
> > > > link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
> > > > inet 127.0.0.1/8 scope host lo
> > > > valid_lft forever preferred_lft forever
> > > > inet6 ::1/128 scope host
> > > > valid_lft forever preferred_lft forever
> > > > 2: eth0: <BROADCAST,MULTICAST,DYNAMIC,UP,LOWER_UP> mtu 1500 qdisc
> > fq_codel
> > > > state UP group default qlen 1000
> > > > link/ether a0:ce:c8:12:ed:05 brd ff:ff:ff:ff:ff:ff
> > > > inet 192.168.2.25/24 brd 192.168.2.255 scope global eth0
> > > > valid_lft forever preferred_lft forever
> > > > inet6 fe80::a2ce:c8ff:fe12:ed05/64 scope link
> > > > valid_lft forever preferred_lft forever
> > > > 3: wlan0: <NO-CARRIER,BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc
> > > > fq_codel master tether state DORMANT group default qlen 1000
> > > > link/ether b8:27:eb:28:18:48 brd ff:ff:ff:ff:ff:ff
> > > > 4: tether: <NO-CARRIER,BROADCAST,MULTICAST,DYNAMIC,UP> mtu 1500 qdisc
> > > > noqueue state DOWN group default qlen 1000
> > > > link/ether 9a:69:3a:48:c1:32 brd ff:ff:ff:ff:ff:ff
> > > > inet 192.168.0.1/24 brd 192.168.0.255 scope global tether
> > > > valid_lft forever preferred_lft forever
> > > > inet6 fe80::d024:5eff:fe80:1a57/64 scope link
> > > > valid_lft forever preferred_lft forever
> > > > # iwctl device wlan0 show
> > > > Device: wlan0
> > > >
> > > >
> > --------------------------------------------------------------------------------
> > > > Settable Property Value
> > > >
> > > >
> > --------------------------------------------------------------------------------
> > > > Name wlan0
> > > > * Mode ap
> > > > * Powered on
> > > > Address b8:27:eb:28:18:48
> > > > Adapter phy0
> > > >
> > >
> > > I get these responses in the journal when I try to connect but it never
> > > connects:
> > >
> > > > src/netdev.c:netdev_mlme_notify() MLME notification New Station(19)
> > > > src/netdev.c:netdev_mlme_notify() MLME notification Del Station(20)
> > > > src/netdev.c:netdev_mlme_notify() MLME notification Del Station(20)
> > > >
> >
> > Did you try to run iwd with debug enabled 'iwd -d'? Maybe there is more
> > info. And if there isn't any clue, the best way forward is to get
> > nlmon running and provide the information the iwd developers. From what I
> > see ConnMan is talking to iwd and setups the AP mode.
> >
> > > I currently use hostapd and dnsmasq to have this headless RPi audio
> > > appliance work as an AP for initial setup, but want to remove hostapd and
> > > dnsmasq if I can get connman/iwd to do the same thing. I tried to get iwd
> > > to go into ap mode and connect, but cannot do it there, either. I do get
> > > some messages at startup of iwd. I do not think these are the problem,
> > but
> > > they are missing kernel modules:
> > >
> > > No Diffie-Hellman support found, WPS will not be available
> > > > No asymmetric key support found.
> > > > TLS based WPA-Enterprise authentication methods will not function.
> > > > Kernel 4.20+ is required for this feature.
> > > > The following options are missing in the kernel:
> > > > CONFIG_ASYMMETRIC_KEY_TYPE
> > > > CONFIG_KEY_DH_OPERATIONS
> > > > CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE
> > > > CONFIG_PKCS7_MESSAGE_PARSER
> > > > CONFIG_X509_CERTIFICATE_PARSER
> > > > CONFIG_PKCS8_PRIVATE_KEY_PARSER
> > > > Wireless daemon version 1.6
> >
> > I don't know but I would suggest to address this in the same go when you
> > build
> > a new kernel with nlmon support.
> >
> > Thanks,
> > Daniel
> >
2 years
Re: Compatibility problems with wired 802.1x
by Denis Kenzior
Hi Diego,
<snip>
>> Hmm, its been a while since I looked into ead. On the iwd side we
>> default to replying with the same protocol version as the request, but
>> looks like ead does indeed hard-code 2004 as the version.
>>
>
> In ead context I think there is not always something to reply to, the EAPPOL-Start message is sent unsolicited, so there is no request to look at to get the version. That may explain why it is hardcoded.
Yes, with WiFi it is a bit simpler because 95% of the time the AP sends
us a RequestIdentity right away with a protocol version set. So we just
use that one. However, there have been instances where even this
strategy fails. For example, Time Warner / Spectrum hotspots send a
RequestIdentity with 2004 version, but refuse to process any packets
unless they're sent with 2001 version. We do send EAPOL-Start with 2001
in iwd, but for some reason ead uses 2004.
<snip>
>> Yes, indeed it should. I suspect the reason is that it sort of just
>> happened to work even when the operstate wasn't set properly, but this
>> should be fixed.
>>
>
> I have second thoughts about this. At least in the switch I am playing with, it can be configured so that an unauthenticated supplicant is put into a guest VLAN and one which fails authentication into an auth-fail VLAN (but my guess is that this is pretty common functionality). If ead were to put the link into dormant state until authentication succeeded it would mean daemons that look at the link's running flag would not attempt to communicate before authentication succeeded (e.g., DHCP client), thus loosing the possibility to use the guest or auth-fail VLAN.
This area is tricky and not very well documented (on Linux and in
general) At least according to
https://www.kernel.org/doc/Documentation/networking/operstates.txt, dhcp
daemons expect the supplicant to take the interface out of dormant mode
as a hint that DHCP requests can now be sent. So ideally,
EAP-RequestIdentity should trigger the interface going into OPER_DORMANT
mode. And EAP-Success should trigger it going into OPER_UP.
But, the complication is that there are implementations that send
RequestIdentity / reply to EAPoL-Start only after the initial DHCP
Discover has been sent out. This is a big reason why we think it makes
sense to put dhcp right into ead eventually.
I suspect ead will need to try to auto-detect (or have the user
configure) if this 'guest-vlan' type scenario is supported. And if it
isn't, leave the interface in OPER_DORMANT state.
Regards,
-Denis
2 years, 1 month
[PATCH 1/4] netconfig: Move EnableNetworkConfiguration check to station
by Andrew Zaborowski
Allow p2p to use netconfig even if not enabled for Infrastructure mode
connections.
---
src/netconfig.c | 17 -----------------
src/station.c | 19 ++++++++++++++++++-
2 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/src/netconfig.c b/src/netconfig.c
index 1e6af3d2..6a618e8f 100644
--- a/src/netconfig.c
+++ b/src/netconfig.c
@@ -1193,28 +1193,11 @@ void netconfig_destroy(struct netconfig *netconfig)
static int netconfig_init(void)
{
- bool enabled;
uint32_t r;
if (netconfig_list)
return -EALREADY;
- if (!l_settings_get_bool(iwd_get_config(), "General",
- "EnableNetworkConfiguration",
- &enabled)) {
- if (l_settings_get_bool(iwd_get_config(), "General",
- "enable_network_config", &enabled))
- l_warn("[General].enable_network_config is deprecated,"
- " use [General].EnableNetworkConfiguration");
- else
- enabled = false;
- }
-
- if (!enabled) {
- l_info("netconfig: Network configuration is disabled.");
- return 0;
- }
-
rtnl = iwd_get_rtnl();
r = l_netlink_register(rtnl, RTNLGRP_IPV4_IFADDR,
diff --git a/src/station.c b/src/station.c
index 3b3bb30a..c532319a 100644
--- a/src/station.c
+++ b/src/station.c
@@ -58,6 +58,7 @@ static struct l_queue *station_list;
static uint32_t netdev_watch;
static uint32_t mfp_setting;
static bool anqp_disabled;
+static bool netconfig_enabled;
struct station {
enum station_state state;
@@ -3098,7 +3099,8 @@ static struct station *station_create(struct netdev *netdev)
l_dbus_object_add_interface(dbus, netdev_get_path(netdev),
IWD_STATION_INTERFACE, station);
- station->netconfig = netconfig_new(netdev_get_ifindex(netdev));
+ if (netconfig_enabled)
+ station->netconfig = netconfig_new(netdev_get_ifindex(netdev));
station->anqp_pending = l_queue_new();
@@ -3250,6 +3252,21 @@ static int station_init(void)
&anqp_disabled))
anqp_disabled = true;
+ if (!l_settings_get_bool(iwd_get_config(), "General",
+ "EnableNetworkConfiguration",
+ &netconfig_enabled)) {
+ if (l_settings_get_bool(iwd_get_config(), "General",
+ "enable_network_config",
+ &netconfig_enabled))
+ l_warn("[General].enable_network_config is deprecated,"
+ " use [General].EnableNetworkConfiguration");
+ else
+ netconfig_enabled = false;
+ }
+
+ if (!netconfig_enabled)
+ l_info("station: Network configuration is disabled.");
+
return 0;
}
--
2.25.1
2 years, 1 month
[RFC] Wi-Fi Display DBus API doc
by Andrew Zaborowski
---
doc/p2p-service-api.txt | 84 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 84 insertions(+)
create mode 100644 doc/p2p-service-api.txt
diff --git a/doc/p2p-service-api.txt b/doc/p2p-service-api.txt
new file mode 100644
index 00000000..9ab59355
--- /dev/null
+++ b/doc/p2p-service-api.txt
@@ -0,0 +1,84 @@
+P2P Service Manager hierarchy
+=============================
+
+Service net.connman.iwd
+Interface net.connman.iwd.p2p.ServiceManager [Experimental]
+Object path /net/connman/iwd
+
+Methods void RegisterService(string service_name, object path)
+
+ Register a P2P service handler. An application
+ can register as implementing a specific P2P service,
+ a concept similar to Bluetooth profiles. When IWD
+ knows there's an application running that handles
+ a supported service, it may advertise that
+ capability to P2P peers, negotiate connection
+ parameters related to that service when establishing
+ a new connection, and expose discovered peers'
+ capilities relating to the service. Once a P2P
+ connection is set up to a peer supporting the same
+ service, the application is resposible for setting
+ up higher layers of the service protocols and making
+ use of it.
+
+ The only service supported by IWD is 'wifi-display'.
+ The application must pass a path to an object
+ having the net.connman.iwd.p2p.Service and
+ net.connman.iwd.p2p.WifiDisplay interfaces attached.
+ The latter interface is also going to be added on
+ objects representing discovered peers.
+
+ Possible Errors: [service].Error.InvalidArguments
+ [service].Error.AlreadyExists
+ [service].Error.NotSupported
+
+ void UnregisterService(object path)
+
+ Unregister a P2P service handler.
+
+ Possible Errors: [service].Error.NotFound
+
+
+P2P Service hierarchy
+=====================
+
+Service unique name
+Interface net.connman.iwd.p2p.Service [Experimental]
+Object path freely definable
+
+Methods void Release()
+
+ This method gets called when IWD unregisters its
+ services. There is no need for the application to
+ unregister the service at that point because when
+ this method is called it has already been
+ unregistered.
+
+
+Wi-fi Display hierarchy
+=======================
+
+Service unique name
+Interface net.connman.iwd.p2p.WifiDisplay [Experimental]
+Object path freely definable
+
+Properties boolean Source [readonly]
+
+ Whether the peer represented by the object is
+ a WFD source.
+
+ boolean Sink [readonly]
+
+ Whether the peer represented by the object has
+ a WFD sink capability. At least one of this and
+ 'Source' should be true.
+
+ uint16 Port [readonly]
+
+ WFD Session Management Control port -- a TCP & UDP
+ port number.
+
+ uint16 MaxThroughput [readonly]
+
+ Declared WFD maximum throughput in megabits per
+ second.
--
2.25.1
2 years, 1 month
[PATCH 01/11] p2p: Add the Listen State
by Andrew Zaborowski
Start a remain-on-channel cmd implementing the Listen State, after each
the Scan Phase implemented as an active scan.
---
src/p2p.c | 342 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 340 insertions(+), 2 deletions(-)
diff --git a/src/p2p.c b/src/p2p.c
index 2955efe0..70aa2f02 100644
--- a/src/p2p.c
+++ b/src/p2p.c
@@ -70,13 +70,19 @@ struct p2p_device {
uint8_t listen_country[3];
uint8_t listen_oper_class;
uint32_t listen_channel;
+ unsigned int scan_interval;
+ time_t next_scan_ts;
+ struct l_timeout *scan_timeout;
uint32_t scan_id;
unsigned int chans_per_scan;
unsigned int scan_chan_idx;
+ uint64_t roc_cookie;
+ unsigned int listen_duration;
struct l_queue *discovery_users;
struct l_queue *peer_list;
bool enabled : 1;
+ bool have_roc_cookie : 1;
};
struct p2p_discovery_user {
@@ -105,6 +111,11 @@ static struct l_queue *p2p_device_list;
static const int channels_social[] = { 1, 6, 11 };
static const int channels_scan_2_4_other[] = { 2, 3, 4, 5, 7, 8, 9, 10 };
+enum {
+ FRAME_GROUP_DEFAULT = 0,
+ FRAME_GROUP_LISTEN,
+};
+
static bool p2p_device_match(const void *a, const void *b)
{
const struct p2p_device *dev = a;
@@ -252,9 +263,156 @@ static void p2p_scan_destroy(void *user_data)
dev->scan_id = 0;
}
+#define SCAN_INTERVAL_MAX 3
+#define SCAN_INTERVAL_STEP 1
#define CHANS_PER_SCAN_INITIAL 2
#define CHANS_PER_SCAN 2
+static bool p2p_device_scan_start(struct p2p_device *dev);
+static void p2p_device_roc_start(struct p2p_device *dev);
+
+static void p2p_device_roc_timeout(struct l_timeout *timeout, void *user_data)
+{
+ struct p2p_device *dev = user_data;
+
+ l_timeout_remove(dev->scan_timeout);
+
+ if (time(NULL) < dev->next_scan_ts) {
+ /*
+ * dev->scan_timeout destroy function will have been called
+ * by now so it won't overwrite the new timeout set by
+ * p2p_device_roc_start.
+ */
+ p2p_device_roc_start(dev);
+ return;
+ }
+
+ p2p_device_scan_start(dev);
+}
+
+static void p2p_device_roc_cancel(struct p2p_device *dev)
+{
+ struct l_genl_msg *msg;
+
+ if (!dev->have_roc_cookie)
+ return;
+
+ l_debug("");
+
+ msg = l_genl_msg_new_sized(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, 32);
+ l_genl_msg_append_attr(msg, NL80211_ATTR_WDEV, 8, &dev->wdev_id);
+ l_genl_msg_append_attr(msg, NL80211_ATTR_COOKIE, 8, &dev->roc_cookie);
+ l_genl_family_send(dev->nl80211, msg, NULL, NULL, NULL);
+
+ dev->have_roc_cookie = false;
+}
+
+static void p2p_scan_timeout_destroy(void *user_data)
+{
+ struct p2p_device *dev = user_data;
+
+ dev->scan_timeout = NULL;
+
+ if (dev->nl80211) {
+ /*
+ * Most likely when the timer expires the ROC period
+ * has finished but send a cancel command to make sure,
+ * as well as handle situations like disabling P2P.
+ */
+ p2p_device_roc_cancel(dev);
+ }
+}
+
+static void p2p_device_roc_cb(struct l_genl_msg *msg, void *user_data)
+{
+ struct p2p_device *dev = user_data;
+ uint64_t cookie;
+ int error = l_genl_msg_get_error(msg);
+
+ l_debug("ROC: %s (%i)", strerror(-error), -error);
+
+ if (error)
+ return;
+
+ if (nl80211_parse_attrs(msg, NL80211_ATTR_COOKIE, &cookie,
+ NL80211_ATTR_UNSPEC) < 0)
+ return;
+
+ dev->roc_cookie = cookie;
+ dev->have_roc_cookie = true;
+
+ /*
+ * Has the command taken so long that P2P has been since disabled
+ * or the timeout otherwise ran out?
+ */
+ if (!dev->scan_timeout)
+ p2p_device_roc_cancel(dev);
+}
+
+static void p2p_device_roc_start(struct p2p_device *dev)
+{
+ struct l_genl_msg *msg;
+ uint32_t listen_freq;
+ uint32_t duration;
+ uint32_t cmd_id;
+
+ l_debug("");
+
+ /*
+ * One second granularity is fine here because some randomess
+ * is desired and the intervals don't have strictly defined
+ * limits.
+ */
+ duration = (dev->next_scan_ts - time(NULL)) * 1000;
+
+ if (duration < 200)
+ duration = 200;
+
+ /*
+ * Driver max duration seems to be 5000ms or more for all drivers
+ * except mac80211_hwsim where it is only 1000ms.
+ */
+ if (duration > wiphy_get_max_roc_duration(dev->wiphy))
+ duration = wiphy_get_max_roc_duration(dev->wiphy);
+
+ /*
+ * Some drivers seem to miss fewer frames if we start new requests
+ * often.
+ */
+ if (duration > 1000)
+ duration = 1000;
+
+ listen_freq = scan_channel_to_freq(dev->listen_channel,
+ SCAN_BAND_2_4_GHZ);
+
+ msg = l_genl_msg_new_sized(NL80211_CMD_REMAIN_ON_CHANNEL, 64);
+ l_genl_msg_append_attr(msg, NL80211_ATTR_WDEV, 8, &dev->wdev_id);
+ l_genl_msg_append_attr(msg, NL80211_ATTR_WIPHY_FREQ, 4, &listen_freq);
+ l_genl_msg_append_attr(msg, NL80211_ATTR_DURATION, 4, &duration);
+
+ cmd_id = l_genl_family_send(dev->nl80211, msg, p2p_device_roc_cb, dev,
+ NULL);
+ if (!cmd_id)
+ l_genl_msg_unref(msg);
+
+ /*
+ * Time out after @duration ms independent of whether we were able to
+ * start the ROC command. If we receive the CMD_REMAIN_ON_CHANNEL
+ * event we'll update the timeout to give the ROC command enough time
+ * to finish. On an error or if we time out before the ROC command
+ * even starts, we'll just retry after @duration ms so we don't even
+ * need to handle errors specifically.
+ */
+ dev->scan_timeout = l_timeout_create_ms(duration,
+ p2p_device_roc_timeout, dev,
+ p2p_scan_timeout_destroy);
+ dev->listen_duration = duration;
+ dev->have_roc_cookie = false;
+
+ l_debug("started a ROC command on channel %i for %i ms",
+ (int) dev->listen_channel, (int) duration);
+}
+
static bool p2p_device_peer_add(struct p2p_device *dev, struct p2p_peer *peer)
{
if (!strlen(peer->name) || !l_utf8_validate(
@@ -391,7 +549,32 @@ static bool p2p_scan_notify(int err, struct l_queue *bss_list,
l_queue_destroy(bss_list, NULL);
schedule:
- /* TODO: move to listen state */
+ /*
+ * Calculate interval between now and when we want the next active
+ * scan to start. Keep issuing Remain-on-Channel commands of
+ * maximum duration until it's time to start the new scan.
+ * The listen periods are actually like a passive scan except that
+ * instead of listening for Beacons only, we also look at Probe
+ * Requests and Probe Responses because they, too, carry P2P IEs
+ * with all the information we need about peer devices. Beacons
+ * also do, in case of GOs, but we will already get the same
+ * information from the Probe Responses and (even if we can
+ * receive the beacons in userspace in the first place) we don't
+ * want to handle so many frames.
+ *
+ * According to 3.1.2.1.1 we shall be available in listen state
+ * during Find for at least 500ms continuously at least once in
+ * every 5s. According to 3.1.2.1.3, the Listen State lasts for
+ * between 1 and 3 one-hundred TU Intervals.
+ *
+ * The Search State duration is implementation dependent.
+ */
+ if (dev->scan_interval < SCAN_INTERVAL_MAX)
+ dev->scan_interval += SCAN_INTERVAL_STEP;
+
+ dev->next_scan_ts = time(NULL) + dev->scan_interval;
+
+ p2p_device_roc_start(dev);
return true;
}
@@ -475,11 +658,118 @@ static bool p2p_device_scan_start(struct p2p_device *dev)
return dev->scan_id != 0;
}
+static void p2p_device_probe_cb(const struct mmpdu_header *mpdu,
+ const void *body, size_t body_len,
+ int rssi, void *user_data)
+{
+ struct p2p_device *dev = user_data;
+ struct p2p_peer *peer;
+ struct p2p_probe_req p2p_info;
+ struct wsc_probe_request wsc_info;
+ int r;
+ uint8_t *wsc_payload;
+ ssize_t wsc_len;
+ struct scan_bss *bss;
+ struct p2p_channel_attr *channel;
+ enum scan_band band;
+ uint32_t frequency;
+
+ l_debug("");
+
+ if (!dev->scan_timeout && !dev->scan_id)
+ return;
+
+ wsc_payload = ie_tlv_extract_wsc_payload(body, body_len, &wsc_len);
+ if (!wsc_payload) /* Not a P2P Probe Req, ignore */
+ return;
+
+ r = wsc_parse_probe_request(wsc_payload, wsc_len, &wsc_info);
+ l_free(wsc_payload);
+
+ if (r < 0) {
+ l_error("Probe Request WSC IE parse error %s (%i)",
+ strerror(-r), -r);
+ return;
+ }
+
+ r = p2p_parse_probe_req(body, body_len, &p2p_info);
+ if (r < 0) {
+ if (r == -ENOENT) /* Not a P2P Probe Req, ignore */
+ return;
+
+ l_error("Probe Request P2P IE parse error %s (%i)",
+ strerror(-r), -r);
+ return;
+ }
+
+ /*
+ * We don't currently have a use case for replying to Probe Requests
+ * except when waiting for a GO Negotiation Request from our target
+ * peer.
+ */
+
+ /*
+ * The peer's listen frequency may be different from ours.
+ * The Listen Channel attribute is optional but if neither
+ * it nor the Operating Channel are set then we have no way
+ * to contact that peer. Ignore such peers.
+ */
+ if (p2p_info.listen_channel.country[0])
+ channel = &p2p_info.listen_channel;
+ else if (p2p_info.operating_channel.country[0])
+ channel = &p2p_info.operating_channel;
+ else
+ goto p2p_free;
+
+ band = scan_oper_class_to_band((const uint8_t *) channel->country,
+ channel->oper_class);
+ frequency = scan_channel_to_freq(channel->channel_num, band);
+ if (!frequency)
+ goto p2p_free;
+
+ bss = scan_bss_new_from_probe_req(mpdu, body, body_len, frequency,
+ rssi);
+ if (!bss)
+ goto p2p_free;
+
+ bss->time_stamp = l_time_now();
+
+ if (p2p_peer_update_existing(bss, dev->peer_list, dev->peer_list))
+ goto p2p_free;
+
+ peer = l_new(struct p2p_peer, 1);
+ peer->dev = dev;
+ peer->bss = bss;
+ peer->name = l_strdup(wsc_info.device_name);
+ peer->primary_device_type = wsc_info.primary_device_type;
+ peer->group = !!(p2p_info.capability.group_caps & P2P_GROUP_CAP_GO);
+ /*
+ * The Device Info attribute is present conditionally so we can't get
+ * the Device Address from there. In theory only P2P Devices send
+ * out Probe Requests, not P2P GOs, so we assume the source address
+ * is the Device Address.
+ */
+ peer->device_addr = bss->addr;
+
+ if (!p2p_device_peer_add(dev, peer))
+ p2p_peer_free(peer);
+
+ /*
+ * TODO: check SSID/BSSID are wildcard values if present and
+ * reply with a Probe Response -- not useful in our current usage
+ * scenarios but required by the spec.
+ */
+
+p2p_free:
+ p2p_clear_probe_req(&p2p_info);
+}
+
static void p2p_device_discovery_start(struct p2p_device *dev)
{
- if (dev->scan_id)
+ if (dev->scan_timeout || dev->scan_id)
return;
+ dev->scan_interval = 1;
dev->chans_per_scan = CHANS_PER_SCAN_INITIAL;
dev->scan_chan_idx = 0;
@@ -493,13 +783,24 @@ static void p2p_device_discovery_start(struct p2p_device *dev)
dev->listen_channel = channels_social[l_getrandom_uint32() %
L_ARRAY_SIZE(channels_social)];
+ frame_watch_add(dev->wdev_id, FRAME_GROUP_LISTEN, 0x0040,
+ (uint8_t *) "", 0, p2p_device_probe_cb, dev, NULL);
+
p2p_device_scan_start(dev);
}
static void p2p_device_discovery_stop(struct p2p_device *dev)
{
+ dev->scan_interval = 0;
+
if (dev->scan_id)
scan_cancel(dev->wdev_id, dev->scan_id);
+
+ if (dev->scan_timeout)
+ l_timeout_remove(dev->scan_timeout);
+
+ p2p_device_roc_cancel(dev);
+ frame_watch_group_remove(dev->wdev_id, FRAME_GROUP_LISTEN);
}
static void p2p_device_enable_cb(struct l_genl_msg *msg, void *user_data)
@@ -576,6 +877,39 @@ static void p2p_device_start_stop(struct p2p_device *dev,
}
}
+static void p2p_mlme_notify(struct l_genl_msg *msg, void *user_data)
+{
+ struct p2p_device *dev = user_data;
+ uint64_t wdev_id;
+ uint64_t cookie;
+
+ if (nl80211_parse_attrs(msg, NL80211_ATTR_WDEV, &wdev_id,
+ NL80211_ATTR_COOKIE, &cookie,
+ NL80211_ATTR_UNSPEC) < 0 ||
+ wdev_id != dev->wdev_id)
+ return;
+
+ switch (l_genl_msg_get_command(msg)) {
+ case NL80211_CMD_REMAIN_ON_CHANNEL:
+ if (!dev->have_roc_cookie || cookie != dev->roc_cookie)
+ break;
+
+ if (!dev->scan_timeout)
+ break;
+
+ /*
+ * The Listen phase is actually starting here, update the
+ * timeout so we know more or less when it ends.
+ */
+ l_debug("ROC started");
+ l_timeout_modify_ms(dev->scan_timeout, dev->listen_duration);
+ break;
+ case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL:
+ /* TODO */
+ break;
+ }
+}
+
#define P2P_SUPPORTED_METHODS ( \
WSC_CONFIGURATION_METHOD_LABEL | \
WSC_CONFIGURATION_METHOD_KEYPAD | \
@@ -746,6 +1080,10 @@ struct p2p_device *p2p_device_update_from_genl(struct l_genl_msg *msg,
scan_wdev_add(dev->wdev_id);
+ if (!l_genl_family_register(dev->nl80211, NL80211_MULTICAST_GROUP_MLME,
+ p2p_mlme_notify, dev, NULL))
+ l_error("Registering for MLME notifications failed");
+
if (!l_dbus_object_add_interface(dbus_get_bus(),
p2p_device_get_path(dev),
IWD_P2P_INTERFACE, dev))
--
2.25.1
2 years, 1 month
[PATCH] wired: use 802.1X-2001 instead of 802.1X-2004
by Diego Santa Cruz
For wired authentication the protocol version used in the EAPOL
packets sent by ead is fixed to 802.1X-2004 (2) but some switches
implementing only 802.1X-2001 erroneously ignore these packets.
As ead only sends EAPOL-Start and EAP-Packet packets and these have
not changed between 802.1X-2001 and 802.1X-2004 there should be
no reason to use 802.1X-2004. Hence, this changes ead to always use
802.1X-2001 (1) instead.
Switches implementing newer versions of 802.1X should not have
problems responding to packets using the original version.
---
wired/ethdev.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/wired/ethdev.c b/wired/ethdev.c
index c80d4fa0..e000afae 100644
--- a/wired/ethdev.c
+++ b/wired/ethdev.c
@@ -83,7 +83,7 @@ struct eapol_hdr {
__be16 pkt_len;
} __attribute__ ((packed));
-static const uint8_t eapol_start[] = { 0x02, 0x01, 0x00, 0x00 };
+static const uint8_t eapol_start[] = { 0x01, 0x01, 0x00, 0x00 };
static const uint8_t pae_group_addr[] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
static bool pae_write(struct ethdev *dev, const uint8_t *addr,
@@ -149,7 +149,7 @@ static void eap_tx_packet(const uint8_t *eap_data, size_t len, void *user_data)
struct eapol *eapol = user_data;
uint8_t frame[1500];
- l_put_u8(0x02, frame);
+ l_put_u8(0x01, frame);
l_put_u8(0x00, frame + 1);
l_put_be16(len, frame + 2);
memcpy(frame + 4, eap_data, len);
--
2.18.1
2 years, 1 month
Re: Compatibility problems with wired 802.1x
by Denis Kenzior
Hi Diego,
On 4/27/20 4:52 AM, Diego Santa Cruz wrote:
> Hi,
>
> I have been attempting to use ead do wired 802.1x authentication and I
> am having problems with an HP V1910-48G switch (admittedly an old switch).
>
> The problem I see is that the switch never responds to the EAPOL packets
> sent by ead. Investigating I saw that the protocol version in the EAPOL
> packets sent by ead is set to 802.1X-2004 (2) but in the unsolicited
> packets the switch sends it is set to 802.1X-2001 (1).
Hmm, its been a while since I looked into ead. On the iwd side we
default to replying with the same protocol version as the request, but
looks like ead does indeed hard-code 2004 as the version.
>
> I am new to 802.1X but I went through the specs and as I understand the
> switch should respond even if it receives an EAPOL version higher that
> what it uses, but this switch does not.
>
Yes, it should.
> Looking through the ead sources I think the new 802.1X-2004 features are
> not used, so I went ahead and changed ead to send 802.1X-2001 (1) in its
> packets, and voilà it now works!
>
> Is there any reason that the packets sent by ead use version
> 802.1X-2004? If not I can send a patch to change it to always use
> 802.1X-2001. I suspect it is safer to use the older version of the
> standard, as switches implementing 802.1X-2004 surely work with 802.1X-2001.
There's really no particular reason. And I pretty much concur that
using 2001 is likely the safest choice for compatibility /
interoperability reasons. If you want to send a patch, I can take it in.
>
> Also, I see ead does not manipulate the link state and mode of the
> interface, I would have expected it to put the interface into dormant
> mode and the link state changed between up and dormant depending on the
> authenticated state (like iwd does for WLAN links). Is there any reason
> for this? I guess one could be the support of guest or auth-fail VLANs
> in some switches.
>
Yes, indeed it should. I suspect the reason is that it sort of just
happened to work even when the operstate wasn't set properly, but this
should be fixed.
Regards,
-Denis
2 years, 1 month
Compatibility problems with wired 802.1x
by Diego Santa Cruz
Hi,
I have been attempting to use ead do wired 802.1x authentication and I am having problems with an HP V1910-48G switch (admittedly an old switch).
The problem I see is that the switch never responds to the EAPOL packets sent by ead. Investigating I saw that the protocol version in the EAPOL packets sent by ead is set to 802.1X-2004 (2) but in the unsolicited packets the switch sends it is set to 802.1X-2001 (1).
I am new to 802.1X but I went through the specs and as I understand the switch should respond even if it receives an EAPOL version higher that what it uses, but this switch does not.
Looking through the ead sources I think the new 802.1X-2004 features are not used, so I went ahead and changed ead to send 802.1X-2001 (1) in its packets, and voilà it now works!
Is there any reason that the packets sent by ead use version 802.1X-2004? If not I can send a patch to change it to always use 802.1X-2001. I suspect it is safer to use the older version of the standard, as switches implementing 802.1X-2004 surely work with 802.1X-2001.
Also, I see ead does not manipulate the link state and mode of the interface, I would have expected it to put the interface into dormant mode and the link state changed between up and dormant depending on the authenticated state (like iwd does for WLAN links). Is there any reason for this? I guess one could be the support of guest or auth-fail VLANs in some switches.
Best,
Diego
--
Diego Santa Cruz, PhD
Technology Architect
spinetix.com
2 years, 1 month
[PATCH 01/17] dbus: Add P2P interface name defines
by Andrew Zaborowski
---
src/dbus.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/dbus.h b/src/dbus.h
index 6416fa42..ebae85d4 100644
--- a/src/dbus.h
+++ b/src/dbus.h
@@ -35,6 +35,8 @@
#define IWD_AP_INTERFACE "net.connman.iwd.AccessPoint"
#define IWD_ADHOC_INTERFACE "net.connman.iwd.AdHoc"
#define IWD_STATION_INTERFACE "net.connman.iwd.Station"
+#define IWD_P2P_INTERFACE "net.connman.iwd.p2p.Device"
+#define IWD_P2P_PEER_INTERFACE "net.connman.iwd.p2p.Peer"
#define IWD_BASE_PATH "/net/connman/iwd"
#define IWD_AGENT_MANAGER_PATH IWD_BASE_PATH
--
2.20.1
2 years, 2 months
[PATCH 01/19] wscutil: Add device type category/subcategory string api
by Andrew Zaborowski
FTR ie.h is included for @microsoft_oui
---
src/wscutil.c | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++
src/wscutil.h | 6 ++
2 files changed, 206 insertions(+)
diff --git a/src/wscutil.c b/src/wscutil.c
index 4f22bd83..cb6d47c0 100644
--- a/src/wscutil.c
+++ b/src/wscutil.c
@@ -32,6 +32,7 @@
#include <ell/ell.h>
#include "src/util.h"
+#include "src/ie.h"
#include "src/wscutil.h"
const unsigned char wsc_wfa_oui[3] = { 0x00, 0x37, 0x2a };
@@ -2791,3 +2792,202 @@ bool wsc_pin_generate(char *pin)
return true;
}
+
+struct device_type_category_info {
+ const char *category_str;
+ unsigned int subcategory_max;
+ const char **subcategory_str;
+};
+
+/* WSC 2.0.5, Table 41 strings adapted to IWD DBus enum convention */
+struct device_type_category_info device_type_categories[] = {
+ [1] = {
+ "computer",
+ 10,
+ (const char *[]) {
+ [1] = "pc",
+ [2] = "server",
+ [3] = "media-center",
+ [4] = "ultra-mobile-pc",
+ [5] = "notebook",
+ [6] = "desktop",
+ [7] = "mobile-internet-device",
+ [8] = "netbook",
+ [9] = "tablet",
+ [10] = "ultrabook",
+ },
+ },
+ [2] = {
+ "input device",
+ 9,
+ (const char *[]) {
+ [1] = "keyboard",
+ [2] = "mouse",
+ [3] = "joystick",
+ [4] = "trackball",
+ [5] = "gaming-controller",
+ [6] = "remote",
+ [7] = "touchscreen",
+ [8] = "biometric-reader",
+ [9] = "barcode-reader",
+ },
+ },
+ [3] = {
+ "printer-scanner",
+ 5,
+ (const char *[]) {
+ [1] = "printer-print-server",
+ [2] = "scanner",
+ [3] = "fax",
+ [4] = "copier",
+ [5] = "printer-scanner-fax-copier",
+ },
+ },
+ [4] = {
+ "camera",
+ 4,
+ (const char *[]) {
+ [1] = "digital-still-camera",
+ [2] = "video-camera",
+ [3] = "web-camera",
+ [4] = "security-camera",
+ },
+ },
+ [5] = {
+ "storage",
+ 1,
+ (const char *[]) {
+ [1] = "nas",
+ },
+ },
+ [6] = {
+ "network-infrastructure",
+ 5,
+ (const char *[]) {
+ [1] = "ap",
+ [2] = "router",
+ [3] = "switch",
+ [4] = "gateway",
+ [5] = "bridge",
+ },
+ },
+ [7] = {
+ "display",
+ 4,
+ (const char *[]) {
+ [1] = "television",
+ [2] = "electronic-picture-frame",
+ [3] = "projector",
+ [4] = "monitor",
+ },
+ },
+ [8] = {
+ "multimedia-device",
+ 6,
+ (const char *[]) {
+ [1] = "dar",
+ [2] = "pvr",
+ [3] = "mcx",
+ [4] = "set-top-box",
+ [5] = "media-server-adapter-extender",
+ [6] = "portable-video-player",
+ },
+ },
+ [9] = {
+ "gaming-device",
+ 5,
+ (const char *[]) {
+ [1] = "xbox",
+ [2] = "xbox360",
+ [3] = "playstation",
+ [4] = "game-console-adapter",
+ [5] = "portable-gaming-device",
+ },
+ },
+ [10] = {
+ "telephone",
+ 5,
+ (const char *[]) {
+ [1] = "windows-mobile",
+ [2] = "single-mode-phone",
+ [3] = "dual-mode-phone",
+ [4] = "single-mode-smartphone",
+ [5] = "dual-mode-smartphone",
+ },
+ },
+ [11] = {
+ "audio-device",
+ 7,
+ (const char *[]) {
+ [1] = "audio-tuner-receiver",
+ [2] = "speakers",
+ [3] = "portable-music-player",
+ [4] = "headset",
+ [5] = "headphones",
+ [6] = "microphone",
+ [7] = "home-theater-system",
+ },
+ },
+ [12] = {
+ "docking-device",
+ 2,
+ (const char *[]) {
+ [1] = "computer-docking-station",
+ [2] = "media-kiosk",
+ },
+ },
+};
+
+bool wsc_device_type_to_dbus_str(const struct wsc_primary_device_type *val,
+ const char **category_str,
+ const char **subcategory_str)
+{
+ struct device_type_category_info *cat;
+
+ if (val->category >= L_ARRAY_SIZE(device_type_categories))
+ return false;
+
+ cat = &device_type_categories[val->category];
+
+ if (!cat->category_str)
+ return false;
+
+ if (category_str)
+ *category_str = cat->category_str;
+
+ if (!subcategory_str)
+ return true;
+
+ if (memcmp(val->oui, microsoft_oui, 3) || val->oui_type != 4)
+ *subcategory_str = NULL; /* Vendor-specific */
+ else if (val->subcategory <= cat->subcategory_max &&
+ cat->subcategory_str[val->subcategory])
+ *subcategory_str = cat->subcategory_str[val->subcategory];
+ else
+ *subcategory_str = NULL; /* Unknown */
+
+ return true;
+}
+
+bool wsc_device_type_from_subcategory_str(struct wsc_primary_device_type *out,
+ const char *subcategory_str)
+{
+ struct device_type_category_info *cat = device_type_categories;
+ unsigned int i;
+
+ for (i = 1; i < L_ARRAY_SIZE(device_type_categories); i++, cat++) {
+ unsigned int j;
+
+ for (j = 1; j <= cat->subcategory_max; j++)
+ if (!strcasecmp(subcategory_str,
+ cat->subcategory_str[j])) {
+ out->category = i;
+ memcpy(out->oui, microsoft_oui, 3);
+ out->oui_type = 4;
+ out->subcategory = j;
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/src/wscutil.h b/src/wscutil.h
index fc28a01e..45d72fbb 100644
--- a/src/wscutil.h
+++ b/src/wscutil.h
@@ -674,3 +674,9 @@ bool wsc_kdf(const void *kdk, void *output, size_t size);
bool wsc_pin_is_valid(const char *pin);
bool wsc_pin_is_checksum_valid(const char *pin);
bool wsc_pin_generate(char *pin);
+
+bool wsc_device_type_to_dbus_str(const struct wsc_primary_device_type *val,
+ const char **category_str,
+ const char **subcategory_str);
+bool wsc_device_type_from_subcategory_str(struct wsc_primary_device_type *out,
+ const char *subcategory_str);
--
2.20.1
2 years, 2 months