summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/kern/kern_uuid.c126
-rw-r--r--sys/net/if_ethersubr.c8
-rw-r--r--sys/sys/uuid.h3
3 files changed, 103 insertions, 34 deletions
diff --git a/sys/kern/kern_uuid.c b/sys/kern/kern_uuid.c
index 14ace89..c647a3f 100644
--- a/sys/kern/kern_uuid.c
+++ b/sys/kern/kern_uuid.c
@@ -71,54 +71,41 @@ struct uuid_private {
CTASSERT(sizeof(struct uuid_private) == 16);
+struct uuid_macaddr {
+ uint16_t state;
+#define UUID_ETHER_EMPTY 0
+#define UUID_ETHER_RANDOM 1
+#define UUID_ETHER_UNIQUE 2
+ uint16_t node[UUID_NODE_LEN>>1];
+};
+
static struct uuid_private uuid_last;
+#define UUID_NETHER 4
+static struct uuid_macaddr uuid_ether[UUID_NETHER];
+
static struct mtx uuid_mutex;
MTX_SYSINIT(uuid_lock, &uuid_mutex, "UUID generator mutex lock", MTX_DEF);
/*
- * Return the first MAC address we encounter or, if none was found,
- * construct a sufficiently random multicast address. We don't try
- * to return the same MAC address as previously returned. We always
- * generate a new multicast address if no MAC address exists in the
- * system.
- * It would be nice to know if 'ifnet' or any of its sub-structures
- * has been changed in any way. If not, we could simply skip the
- * scan and safely return the MAC address we returned before.
+ * Return the first MAC address added in the array. If it's empty, then
+ * construct a sufficiently random multicast MAC address first. Any
+ * addresses added later will bump the random MAC address up tp the next
+ * index.
*/
static void
uuid_node(uint16_t *node)
{
- struct ifnet *ifp;
- struct ifaddr *ifa;
- struct sockaddr_dl *sdl;
int i;
- CURVNET_SET(TD_TO_VNET(curthread));
- IFNET_RLOCK_NOSLEEP();
- TAILQ_FOREACH(ifp, &V_ifnet, if_link) {
- /* Walk the address list */
- IF_ADDR_RLOCK(ifp);
- TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
- sdl = (struct sockaddr_dl*)ifa->ifa_addr;
- if (sdl != NULL && sdl->sdl_family == AF_LINK &&
- sdl->sdl_type == IFT_ETHER) {
- /* Got a MAC address. */
- bcopy(LLADDR(sdl), node, UUID_NODE_LEN);
- IF_ADDR_RUNLOCK(ifp);
- IFNET_RUNLOCK_NOSLEEP();
- CURVNET_RESTORE();
- return;
- }
- }
- IF_ADDR_RUNLOCK(ifp);
+ if (uuid_ether[0].state == UUID_ETHER_EMPTY) {
+ for (i = 0; i < (UUID_NODE_LEN>>1); i++)
+ uuid_ether[0].node[i] = (uint16_t)arc4random();
+ *((uint8_t*)uuid_ether[0].node) |= 0x01;
+ uuid_ether[0].state = UUID_ETHER_RANDOM;
}
- IFNET_RUNLOCK_NOSLEEP();
-
for (i = 0; i < (UUID_NODE_LEN>>1); i++)
- node[i] = (uint16_t)arc4random();
- *((uint8_t*)node) |= 0x01;
- CURVNET_RESTORE();
+ node[i] = uuid_ether[0].node[i];
}
/*
@@ -211,6 +198,77 @@ sys_uuidgen(struct thread *td, struct uuidgen_args *uap)
}
int
+uuid_ether_add(const uint8_t *addr)
+{
+ int i;
+ uint8_t c;
+
+ /*
+ * Validate input. No multicast addresses and no addresses that
+ * are all zeroes.
+ */
+ if (addr[0] & 0x01)
+ return (EINVAL);
+ c = 0;
+ for (i = 0; i < UUID_NODE_LEN; i++)
+ c += addr[i];
+ if (c == 0)
+ return (EINVAL);
+
+ mtx_lock(&uuid_mutex);
+
+ /* Make sure the MAC isn't known already and that there's space. */
+ i = 0;
+ while (i < UUID_NETHER && uuid_ether[i].state == UUID_ETHER_UNIQUE) {
+ if (!bcmp(addr, uuid_ether[i].node, UUID_NODE_LEN)) {
+ mtx_unlock(&uuid_mutex);
+ return (EEXIST);
+ }
+ i++;
+ }
+ if (i == UUID_NETHER) {
+ mtx_unlock(&uuid_mutex);
+ return (ENOSPC);
+ }
+
+ /* Insert MAC at index, moving the non-empty entry if possible. */
+ if (uuid_ether[i].state == UUID_ETHER_RANDOM && i < UUID_NETHER - 1)
+ uuid_ether[i + 1] = uuid_ether[i];
+ uuid_ether[i].state = UUID_ETHER_UNIQUE;
+ bcopy(addr, uuid_ether[i].node, UUID_NODE_LEN);
+ mtx_unlock(&uuid_mutex);
+ return (0);
+}
+
+int
+uuid_ether_del(const uint8_t *addr)
+{
+ int i;
+
+ mtx_lock(&uuid_mutex);
+ i = 0;
+ while (i < UUID_NETHER && uuid_ether[i].state == UUID_ETHER_UNIQUE &&
+ bcmp(addr, uuid_ether[i].node, UUID_NODE_LEN))
+ i++;
+ if (i == UUID_NETHER || uuid_ether[i].state != UUID_ETHER_UNIQUE) {
+ mtx_unlock(&uuid_mutex);
+ return (ENOENT);
+ }
+
+ /* Remove it by shifting higher index entries down. */
+ while (i < UUID_NETHER - 1 && uuid_ether[i].state != UUID_ETHER_EMPTY) {
+ uuid_ether[i] = uuid_ether[i + 1];
+ i++;
+ }
+ if (uuid_ether[i].state != UUID_ETHER_EMPTY) {
+ uuid_ether[i].state = UUID_ETHER_EMPTY;
+ bzero(uuid_ether[i].node, UUID_NODE_LEN);
+ }
+ mtx_unlock(&uuid_mutex);
+ return (0);
+}
+
+int
snprintf_uuid(char *buf, size_t sz, struct uuid *uuid)
{
struct uuid_private *id;
diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c
index 3fd6c9d..4f9672d 100644
--- a/sys/net/if_ethersubr.c
+++ b/sys/net/if_ethersubr.c
@@ -48,6 +48,7 @@
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/sysctl.h>
+#include <sys/uuid.h>
#include <net/if.h>
#include <net/if_arp.h>
@@ -926,6 +927,8 @@ ether_ifattach(struct ifnet *ifp, const u_int8_t *lla)
break;
if (i != ifp->if_addrlen)
if_printf(ifp, "Ethernet address: %6D\n", lla, ":");
+
+ uuid_ether_add(LLADDR(sdl));
}
/*
@@ -934,6 +937,11 @@ ether_ifattach(struct ifnet *ifp, const u_int8_t *lla)
void
ether_ifdetach(struct ifnet *ifp)
{
+ struct sockaddr_dl *sdl;
+
+ sdl = (struct sockaddr_dl *)(ifp->if_addr->ifa_addr);
+ uuid_ether_del(LLADDR(sdl));
+
if (IFP2AC(ifp)->ac_netgraph != NULL) {
KASSERT(ng_ether_detach_p != NULL,
("ng_ether_detach_p is NULL"));
diff --git a/sys/sys/uuid.h b/sys/sys/uuid.h
index 0f40bb2..0748f61 100644
--- a/sys/sys/uuid.h
+++ b/sys/sys/uuid.h
@@ -58,6 +58,9 @@ struct sbuf;
struct uuid *kern_uuidgen(struct uuid *, size_t);
+int uuid_ether_add(const uint8_t *);
+int uuid_ether_del(const uint8_t *);
+
int snprintf_uuid(char *, size_t, struct uuid *);
int printf_uuid(struct uuid *);
int sbuf_printf_uuid(struct sbuf *, struct uuid *);
OpenPOWER on IntegriCloud