Decouple the UUID generator from network interfaces by having MAC

addresses added to the UUID generator using uuid_ether_add(). The
UUID generator keeps an arbitrary number of MAC addresses, under
the assumption that they are rarely removed (= uuid_ether_del()).
This achieves the following:
1.  It brings up closer to having the network stack as a loadable
    module.
2.  It allows the UUID generator to filter MAC addresses for best
    results (= highest chance of uniqeness).
3.  MAC addresses can come from anywhere, irrespactive of whether
    it's used for an interface or not.

A side-effect of the change is that when no MAC addresses have been
added, a random multicast MAC address is created once and re-used if
needed. Previusly, when a random MAC address was needed, it was
created for every call. Thus, a change in behaviour is introduced
for when no MAC addresses exist.

Obtained from:	Juniper Networks, Inc.
This commit is contained in:
Marcel Moolenaar 2013-07-24 04:24:21 +00:00
parent d2034d1b22
commit ef1f916971
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=253590
3 changed files with 103 additions and 34 deletions

View File

@ -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];
}
/*
@ -210,6 +197,77 @@ sys_uuidgen(struct thread *td, struct uuidgen_args *uap)
return (error);
}
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)
{

View File

@ -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"));

View File

@ -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 *);