Index: uspace/lib/drv/generic/remote_ieee80211.c
===================================================================
--- uspace/lib/drv/generic/remote_ieee80211.c	(revision b4edc96c3455b41ece7d35a9752221342dec22f8)
+++ uspace/lib/drv/generic/remote_ieee80211.c	(revision a7f7b9c3744bb1a355f84327e6b489c2a79f47c5)
@@ -1,3 +1,4 @@
 /*
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2015 Jan Kolarik
  * All rights reserved.
@@ -84,19 +85,13 @@
 }
 
-static bool mac_matches(uint8_t *mac1, uint8_t *mac2)
-{
-	for (size_t i = 0; i < ETH_ADDR; i++) {
-		if (mac1[i] != mac2[i])
-			return false;
-	}
-
-	return true;
-}
-
+// XXX This is wrong. Wifi should not have anything to do with IP links
 static sysarg_t get_link_id(uint8_t *mac)
 {
 	sysarg_t *link_list;
 	inet_link_info_t link_info;
+	eth_addr_t eth_addr;
 	size_t count;
+
+	eth_addr_decode(mac, &eth_addr);
 
 	errno_t rc = inetcfg_get_link_list(&link_list, &count);
@@ -109,5 +104,5 @@
 			return -1;
 
-		if (mac_matches(mac, link_info.mac_addr.b))
+		if (eth_addr_compare(&eth_addr, &link_info.mac_addr) == 0)
 			return link_list[i];
 	}
@@ -170,4 +165,6 @@
 		return rc;
 
+	// XXX This is wrong. Wifi should not initiate DHCP
+
 	/* Send DHCP discover. */
 	nic_address_t wifi_mac;
@@ -207,4 +204,7 @@
 	if (rc != EOK)
 		return rc;
+
+	eth_addr_t eth_addr;
+	eth_addr_decode(wifi_mac.address, &eth_addr);
 
 	inet_link_info_t link_info;
@@ -215,4 +215,6 @@
 	size_t count;
 
+	/// XXX This is wrong. Wifi should do nothing with DHCP
+
 	/* Remove previous DHCP address. */
 	rc = inetcfg_get_addr_list(&addr_list, &count);
@@ -229,5 +231,5 @@
 			return rc;
 
-		if (mac_matches(wifi_mac.address, link_info.mac_addr.b)) {
+		if (eth_addr_compare(&eth_addr, &link_info.mac_addr) == 0) {
 			if (str_test_prefix(addr_info.name, "dhcp")) {
 				rc = inetcfg_addr_delete(addr_list[i]);
Index: uspace/lib/inet/include/inet/eth_addr.h
===================================================================
--- uspace/lib/inet/include/inet/eth_addr.h	(revision b4edc96c3455b41ece7d35a9752221342dec22f8)
+++ uspace/lib/inet/include/inet/eth_addr.h	(revision a7f7b9c3744bb1a355f84327e6b489c2a79f47c5)
@@ -41,8 +41,34 @@
 
 #define ETH_ADDR_SIZE 6
+#define ETH_ADDR_STR_SIZE (6 * 2 + 5)
 
+#define ETH_ADDR_INITIALIZER(aa, bb, cc, dd, ee, ff) \
+    { .a = ((uint64_t)(aa) << 40) | ((uint64_t)(bb) << 32) | \
+    ((uint64_t)(cc) << 24) | ((uint64_t)(dd) << 16) | \
+    ((uint64_t)(ee) << 8) | (ff) }
+
+/** Ethernet address.
+ *
+ * Defined as a structure. This provides strong type checking.
+ *
+ * Since the structure is not opaque, this allows eth_addr_t to be
+ * allocated statically and copied around using the assignment operator.
+ *
+ * It is stored in the lower 48 bits of a 64-bit integer. This is an internal
+ * representation that allows simple and efficient operation. Most CPUs
+ * will be much faster (and we will need less instructions) operating
+ * on a single 64-bit integer than on six individual 8-bit integers.
+ *
+ * Kind reader will appreciate the cleverness and elegance of this
+ * representation.
+ */
 typedef struct {
-	uint8_t b[ETH_ADDR_SIZE];
+	uint64_t a;
 } eth_addr_t;
+
+/** Ethernet address in the form of a string */
+typedef struct {
+	char str[ETH_ADDR_STR_SIZE + 1];
+} eth_addr_str_t;
 
 extern const eth_addr_t eth_addr_broadcast;
@@ -52,4 +78,5 @@
 
 extern int eth_addr_compare(const eth_addr_t *, const eth_addr_t *);
+extern void eth_addr_format(eth_addr_t *, eth_addr_str_t *);
 
 #endif
Index: uspace/lib/inet/meson.build
===================================================================
--- uspace/lib/inet/meson.build	(revision b4edc96c3455b41ece7d35a9752221342dec22f8)
+++ uspace/lib/inet/meson.build	(revision a7f7b9c3744bb1a355f84327e6b489c2a79f47c5)
@@ -44,2 +44,7 @@
 	'src/udp.c',
 )
+
+test_src = files(
+	'test/eth_addr.c',
+	'test/main.c',
+)
Index: uspace/lib/inet/src/addr.c
===================================================================
--- uspace/lib/inet/src/addr.c	(revision b4edc96c3455b41ece7d35a9752221342dec22f8)
+++ uspace/lib/inet/src/addr.c	(revision a7f7b9c3744bb1a355f84327e6b489c2a79f47c5)
@@ -55,7 +55,6 @@
 const addr32_t addr32_broadcast_all_hosts = 0xffffffff;
 
-static const eth_addr_t inet_eth_addr_solicited_node = {
-	0x33, 0x33, 0xff, 0, 0, 0
-};
+static eth_addr_t inet_eth_addr_solicited_node =
+    ETH_ADDR_INITIALIZER(0x33, 0x33, 0xff, 0, 0, 0);
 
 static const inet_addr_t inet_addr_any_addr = {
@@ -91,6 +90,10 @@
 void eth_addr_solicited_node(const addr128_t ip, eth_addr_t *mac)
 {
-	memcpy(&mac->b[0], &inet_eth_addr_solicited_node.b[0], 3);
-	memcpy(&mac->b[3], ip + 13, 3);
+	uint8_t b[6];
+	mac->a = inet_eth_addr_solicited_node.a;
+
+	eth_addr_encode(&inet_eth_addr_solicited_node, b);
+	memcpy(&b[3], ip + 13, 3);
+	eth_addr_decode(b, mac);
 }
 
Index: uspace/lib/inet/src/eth_addr.c
===================================================================
--- uspace/lib/inet/src/eth_addr.c	(revision b4edc96c3455b41ece7d35a9752221342dec22f8)
+++ uspace/lib/inet/src/eth_addr.c	(revision a7f7b9c3744bb1a355f84327e6b489c2a79f47c5)
@@ -37,14 +37,19 @@
 #include <inet/eth_addr.h>
 #include <mem.h>
+#include <stdio.h>
 
-const eth_addr_t eth_addr_broadcast = {
-	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
-};
+const eth_addr_t eth_addr_broadcast =
+    ETH_ADDR_INITIALIZER(0xff, 0xff, 0xff, 0xff, 0xff, 0xff);
 
 void eth_addr_encode(eth_addr_t *addr, void *buf)
 {
 	uint8_t *bp = (uint8_t *)buf;
+	uint64_t a;
+	int i;
 
-	memcpy(bp, &addr->b[0], ETH_ADDR_SIZE);
+	a = addr->a;
+
+	for (i = 0; i < ETH_ADDR_SIZE; i++)
+		bp[i] = (a >> (40 - 8 * i)) & 0xff;
 }
 
@@ -52,6 +57,12 @@
 {
 	const uint8_t *bp = (uint8_t *)buf;
+	uint64_t a;
+	int i;
 
-	memcpy(&addr->b[0], bp, ETH_ADDR_SIZE);
+	a = 0;
+	for (i = 0; i < ETH_ADDR_SIZE; i++)
+		a |= (uint64_t)bp[i] << (40 - 8 * i);
+
+	addr->a = a;
 }
 
@@ -62,5 +73,22 @@
 int eth_addr_compare(const eth_addr_t *a, const eth_addr_t *b)
 {
-	return memcmp(a->b, b->b, ETH_ADDR_SIZE) == 0;
+	if (a->a < b->a)
+		return -1;
+	else if (a->a == b->a)
+		return 0;
+	else
+		return 1;
+}
+
+void eth_addr_format(eth_addr_t *addr, eth_addr_str_t *saddr)
+{
+	int i;
+
+	snprintf(saddr->str, 3, "%02x",
+	    (unsigned)((addr->a >> 40) & 0xff));
+	for (i = 1; i < ETH_ADDR_SIZE; i++) {
+		snprintf(saddr->str + 2 + 3 * (i - 1), 4, ":%02x",
+		    (unsigned)((addr->a >> (40 - i * 8)) & 0xff));
+	}
 }
 
Index: uspace/lib/inet/test/eth_addr.c
===================================================================
--- uspace/lib/inet/test/eth_addr.c	(revision a7f7b9c3744bb1a355f84327e6b489c2a79f47c5)
+++ uspace/lib/inet/test/eth_addr.c	(revision a7f7b9c3744bb1a355f84327e6b489c2a79f47c5)
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2021 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <inet/eth_addr.h>
+#include <pcut/pcut.h>
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(eth_addr);
+
+/** Test ETH_ADDR_INITIALIZER constructs correct Ethernet address */
+PCUT_TEST(initializer)
+{
+	eth_addr_t addr = ETH_ADDR_INITIALIZER(0x11, 0x22, 0x33, 0x44, 0x55,
+	    0x66);
+	PCUT_ASSERT_INT_EQUALS(0x112233445566, addr.a);
+}
+
+/** Test eth_addr_decode() correctly decodes from array of bytes */
+PCUT_TEST(decode)
+{
+	eth_addr_t addr;
+	uint8_t b[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
+
+	eth_addr_decode(b, &addr);
+	PCUT_ASSERT_INT_EQUALS(0x112233445566, addr.a);
+}
+
+/** Test eth_addr_encode() correctly encodes to array of bytes */
+PCUT_TEST(encode)
+{
+	eth_addr_t addr;
+	uint8_t b[7] = { 0, 0, 0, 0, 0, 0, 0 };
+
+	addr.a = 0x112233445566;
+	eth_addr_encode(&addr, b);
+
+	PCUT_ASSERT_INT_EQUALS(0x11, b[0]);
+	PCUT_ASSERT_INT_EQUALS(0x22, b[1]);
+	PCUT_ASSERT_INT_EQUALS(0x33, b[2]);
+	PCUT_ASSERT_INT_EQUALS(0x44, b[3]);
+	PCUT_ASSERT_INT_EQUALS(0x55, b[4]);
+	PCUT_ASSERT_INT_EQUALS(0x66, b[5]);
+}
+
+/** Test eth_addr_compare() correctly compares two Ethernet addresses */
+PCUT_TEST(compare)
+{
+	eth_addr_t a;
+	eth_addr_t b;
+
+	a.a = 1;
+	b.a = 2;
+	PCUT_ASSERT_INT_EQUALS(-1, eth_addr_compare(&a, &b));
+
+	a.a = 2;
+	b.a = 2;
+	PCUT_ASSERT_INT_EQUALS(0, eth_addr_compare(&a, &b));
+
+	a.a = 2;
+	b.a = 1;
+	PCUT_ASSERT_INT_EQUALS(1, eth_addr_compare(&a, &b));
+}
+
+/** Tets eth_addr_format() correctly formats Ethernet address to string */
+PCUT_TEST(format)
+{
+	eth_addr_t addr1 = ETH_ADDR_INITIALIZER(0x11, 0x22, 0x33, 0x44, 0x55, 0x66);
+	eth_addr_t addr2 = ETH_ADDR_INITIALIZER(0x01, 0x02, 0x03, 0x04, 0x05, 0x06);
+	eth_addr_t addr3 = ETH_ADDR_INITIALIZER(0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff);
+	eth_addr_str_t saddr;
+
+	eth_addr_format(&addr1, &saddr);
+	PCUT_ASSERT_STR_EQUALS("11:22:33:44:55:66", saddr.str);
+
+	eth_addr_format(&addr2, &saddr);
+	PCUT_ASSERT_STR_EQUALS("01:02:03:04:05:06", saddr.str);
+
+	eth_addr_format(&addr3, &saddr);
+	PCUT_ASSERT_STR_EQUALS("aa:bb:cc:dd:ee:ff", saddr.str);
+}
+
+PCUT_EXPORT(eth_addr);
Index: uspace/lib/inet/test/main.c
===================================================================
--- uspace/lib/inet/test/main.c	(revision a7f7b9c3744bb1a355f84327e6b489c2a79f47c5)
+++ uspace/lib/inet/test/main.c	(revision a7f7b9c3744bb1a355f84327e6b489c2a79f47c5)
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2021 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <pcut/pcut.h>
+
+PCUT_INIT;
+
+PCUT_IMPORT(eth_addr);
+
+PCUT_MAIN();
