2025-08-06 12:10:18 -04:00
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
2013-07-04 16:56:19 -04:00
*
2025-08-06 12:10:18 -04:00
* (c) ZeroTier, Inc.
* https://www.zerotier.com/
2013-07-04 16:56:19 -04:00
*/
2013-12-06 16:49:20 -08:00
# ifndef ZT_MAC_HPP
# define ZT_MAC_HPP
2013-07-04 16:56:19 -04:00
2014-05-23 14:32:31 -07:00
# include "Address.hpp"
# include "Buffer.hpp"
2025-07-03 11:26:23 -04:00
# include "Constants.hpp"
# include "Utils.hpp"
# include <stdint.h>
# include <stdio.h>
# include <stdlib.h>
2013-07-04 16:56:19 -04:00
namespace ZeroTier {
/**
2014-05-23 14:32:31 -07:00
* 48-byte Ethernet MAC address
2013-07-04 16:56:19 -04:00
*/
2025-07-03 11:26:23 -04:00
class MAC {
public :
MAC ( ) : _m ( 0ULL )
{
}
MAC ( const MAC & m ) : _m ( m . _m )
{
}
2013-07-04 16:56:19 -04:00
2025-07-03 11:26:23 -04:00
MAC ( const unsigned char a , const unsigned char b , const unsigned char c , const unsigned char d , const unsigned char e , const unsigned char f )
: _m ( ( ( ( ( uint64_t ) a ) & 0xffULL ) < < 40 ) | ( ( ( ( uint64_t ) b ) & 0xffULL ) < < 32 ) | ( ( ( ( uint64_t ) c ) & 0xffULL ) < < 24 ) | ( ( ( ( uint64_t ) d ) & 0xffULL ) < < 16 ) | ( ( ( ( uint64_t ) e ) & 0xffULL ) < < 8 ) | ( ( ( uint64_t ) f ) & 0xffULL ) )
{
}
MAC ( const void * bits , unsigned int len )
{
setTo ( bits , len ) ;
}
MAC ( const Address & ztaddr , uint64_t nwid )
{
fromAddress ( ztaddr , nwid ) ;
}
MAC ( const uint64_t m ) : _m ( m & 0xffffffffffffULL )
{
}
2015-03-31 17:53:34 -07:00
/**
* @return MAC in 64-bit integer
*/
2025-07-03 11:26:23 -04:00
inline uint64_t toInt ( ) const
{
return _m ;
}
2015-03-31 17:53:34 -07:00
2013-07-04 16:56:19 -04:00
/**
2014-05-23 14:32:31 -07:00
* Set MAC to zero
2013-07-04 16:56:19 -04:00
*/
2025-07-03 11:26:23 -04:00
inline void zero ( )
{
_m = 0ULL ;
}
2013-07-04 16:56:19 -04:00
/**
2014-05-23 14:32:31 -07:00
* @return True if MAC is non-zero
2013-07-04 16:56:19 -04:00
*/
2025-07-03 11:26:23 -04:00
inline operator bool ( ) const
{
return ( _m ! = 0ULL ) ;
}
2014-05-23 14:32:31 -07:00
/**
* @param bits Raw MAC in big-endian byte order
* @param len Length, must be >= 6 or result is zero
*/
2025-07-03 11:26:23 -04:00
inline void setTo ( const void * bits , unsigned int len )
2013-07-04 16:56:19 -04:00
{
2014-05-23 14:32:31 -07:00
if ( len < 6 ) {
_m = 0ULL ;
return ;
2013-07-04 16:56:19 -04:00
}
2025-07-03 11:26:23 -04:00
const unsigned char * b = ( const unsigned char * ) bits ;
_m = ( ( ( ( uint64_t ) * b ) & 0xff ) < < 40 ) ;
2023-05-01 14:48:16 -04:00
+ + b ;
_m | = ( ( ( ( uint64_t ) * b ) & 0xff ) < < 32 ) ;
+ + b ;
_m | = ( ( ( ( uint64_t ) * b ) & 0xff ) < < 24 ) ;
+ + b ;
_m | = ( ( ( ( uint64_t ) * b ) & 0xff ) < < 16 ) ;
+ + b ;
_m | = ( ( ( ( uint64_t ) * b ) & 0xff ) < < 8 ) ;
+ + b ;
2014-05-23 14:32:31 -07:00
_m | = ( ( ( uint64_t ) * b ) & 0xff ) ;
2013-07-04 16:56:19 -04:00
}
/**
2014-05-23 14:32:31 -07:00
* @param buf Destination buffer for MAC in big-endian byte order
* @param len Length of buffer, must be >= 6 or nothing is copied
2013-07-04 16:56:19 -04:00
*/
2025-07-03 11:26:23 -04:00
inline void copyTo ( void * buf , unsigned int len ) const
2013-07-04 16:56:19 -04:00
{
2023-05-01 14:48:16 -04:00
if ( len < 6 ) {
2014-05-23 14:32:31 -07:00
return ;
2023-05-01 14:48:16 -04:00
}
2025-07-03 11:26:23 -04:00
unsigned char * b = ( unsigned char * ) buf ;
2014-05-23 14:32:31 -07:00
* ( b + + ) = ( unsigned char ) ( ( _m > > 40 ) & 0xff ) ;
* ( b + + ) = ( unsigned char ) ( ( _m > > 32 ) & 0xff ) ;
* ( b + + ) = ( unsigned char ) ( ( _m > > 24 ) & 0xff ) ;
* ( b + + ) = ( unsigned char ) ( ( _m > > 16 ) & 0xff ) ;
* ( b + + ) = ( unsigned char ) ( ( _m > > 8 ) & 0xff ) ;
* b = ( unsigned char ) ( _m & 0xff ) ;
2013-07-04 16:56:19 -04:00
}
/**
2014-05-23 14:32:31 -07:00
* Append to a buffer in big-endian byte order
*
* @param b Buffer to append to
2013-07-04 16:56:19 -04:00
*/
2025-07-03 11:26:23 -04:00
template < unsigned int C > inline void appendTo ( Buffer < C > & b ) const
2013-07-04 16:56:19 -04:00
{
2025-07-03 11:26:23 -04:00
unsigned char * p = ( unsigned char * ) b . appendField ( 6 ) ;
2014-05-23 14:32:31 -07:00
* ( p + + ) = ( unsigned char ) ( ( _m > > 40 ) & 0xff ) ;
* ( p + + ) = ( unsigned char ) ( ( _m > > 32 ) & 0xff ) ;
* ( p + + ) = ( unsigned char ) ( ( _m > > 24 ) & 0xff ) ;
* ( p + + ) = ( unsigned char ) ( ( _m > > 16 ) & 0xff ) ;
* ( p + + ) = ( unsigned char ) ( ( _m > > 8 ) & 0xff ) ;
* p = ( unsigned char ) ( _m & 0xff ) ;
2013-07-04 16:56:19 -04:00
}
/**
2014-05-23 14:32:31 -07:00
* @return True if this is broadcast (all 0xff)
2013-07-04 16:56:19 -04:00
*/
2025-07-03 11:26:23 -04:00
inline bool isBroadcast ( ) const
{
return ( _m = = 0xffffffffffffULL ) ;
}
2013-07-04 16:56:19 -04:00
/**
2014-05-23 14:32:31 -07:00
* @return True if this is a multicast MAC
2013-07-04 16:56:19 -04:00
*/
2025-07-03 11:26:23 -04:00
inline bool isMulticast ( ) const
{
return ( ( _m & 0x010000000000ULL ) ! = 0ULL ) ;
}
2013-07-04 16:56:19 -04:00
/**
2014-05-23 14:32:31 -07:00
* @param True if this is a locally-administered MAC
*/
2025-07-03 11:26:23 -04:00
inline bool isLocallyAdministered ( ) const
{
return ( ( _m & 0x020000000000ULL ) ! = 0ULL ) ;
}
2014-05-23 14:32:31 -07:00
/**
* Set this MAC to a MAC derived from an address and a network ID
*
* @param ztaddr ZeroTier address
* @param nwid 64-bit network ID
*/
2025-07-03 11:26:23 -04:00
inline void fromAddress ( const Address & ztaddr , uint64_t nwid )
2014-05-23 14:32:31 -07:00
{
uint64_t m = ( ( uint64_t ) firstOctetForNetwork ( nwid ) ) < < 40 ;
2025-07-03 11:26:23 -04:00
m | = ztaddr . toInt ( ) ; // a is 40 bits
2014-05-23 14:32:31 -07:00
m ^ = ( ( nwid > > 8 ) & 0xff ) < < 32 ;
m ^ = ( ( nwid > > 16 ) & 0xff ) < < 24 ;
m ^ = ( ( nwid > > 24 ) & 0xff ) < < 16 ;
m ^ = ( ( nwid > > 32 ) & 0xff ) < < 8 ;
m ^ = ( nwid > > 40 ) & 0xff ;
_m = m ;
}
/**
* Get the ZeroTier address for this MAC on this network (assuming no bridging of course, basic unicast)
*
2023-01-11 13:42:30 -05:00
* This just XORs the next-least-significant 5 bytes of the network ID again to unmask.
2014-05-23 14:32:31 -07:00
*
* @param nwid Network ID
*/
inline Address toAddress ( uint64_t nwid ) const
{
2025-07-03 11:26:23 -04:00
uint64_t a = _m & 0xffffffffffULL ; // least significant 40 bits of MAC are formed from address
a ^ = ( ( nwid > > 8 ) & 0xff ) < < 32 ; // ... XORed with bits 8-48 of the nwid in little-endian byte order, so unmask it
2014-05-23 14:32:31 -07:00
a ^ = ( ( nwid > > 16 ) & 0xff ) < < 24 ;
a ^ = ( ( nwid > > 24 ) & 0xff ) < < 16 ;
a ^ = ( ( nwid > > 32 ) & 0xff ) < < 8 ;
a ^ = ( nwid > > 40 ) & 0xff ;
return Address ( a ) ;
}
/**
* @param nwid Network ID
* @return First octet of MAC for this network
*/
static inline unsigned char firstOctetForNetwork ( uint64_t nwid )
{
2025-07-03 11:26:23 -04:00
unsigned char a = ( ( unsigned char ) ( nwid & 0xfe ) | 0x02 ) ; // locally administered, not multicast, from LSB of network ID
return ( ( a = = 0x52 ) ? 0x32 : a ) ; // blacklist 0x52 since it's used by KVM, libvirt, and other popular virtualization engines... seems de-facto standard on Linux
2014-05-23 14:32:31 -07:00
}
/**
* @param i Value from 0 to 5 (inclusive)
* @return Byte at said position (address interpreted in big-endian order)
*/
2025-07-03 11:26:23 -04:00
inline unsigned char operator [ ] ( unsigned int i ) const
{
return ( unsigned char ) ( ( _m > > ( 40 - ( i * 8 ) ) ) & 0xff ) ;
}
2014-05-23 14:32:31 -07:00
/**
* @return 6, which is the number of bytes in a MAC, for container compliance
*/
2025-07-03 11:26:23 -04:00
inline unsigned int size ( ) const
{
return 6 ;
}
2014-05-23 14:32:31 -07:00
2025-07-03 11:26:23 -04:00
inline unsigned long hashCode ( ) const
{
return ( unsigned long ) _m ;
}
2015-08-27 16:17:21 -07:00
2025-07-03 11:26:23 -04:00
inline char * toString ( char buf [ 18 ] ) const
2017-12-04 14:40:10 -08:00
{
buf [ 0 ] = Utils : : HEXCHARS [ ( _m > > 44 ) & 0xf ] ;
buf [ 1 ] = Utils : : HEXCHARS [ ( _m > > 40 ) & 0xf ] ;
buf [ 2 ] = ' : ' ;
buf [ 3 ] = Utils : : HEXCHARS [ ( _m > > 36 ) & 0xf ] ;
buf [ 4 ] = Utils : : HEXCHARS [ ( _m > > 32 ) & 0xf ] ;
buf [ 5 ] = ' : ' ;
buf [ 6 ] = Utils : : HEXCHARS [ ( _m > > 28 ) & 0xf ] ;
buf [ 7 ] = Utils : : HEXCHARS [ ( _m > > 24 ) & 0xf ] ;
buf [ 8 ] = ' : ' ;
buf [ 9 ] = Utils : : HEXCHARS [ ( _m > > 20 ) & 0xf ] ;
buf [ 10 ] = Utils : : HEXCHARS [ ( _m > > 16 ) & 0xf ] ;
buf [ 11 ] = ' : ' ;
buf [ 12 ] = Utils : : HEXCHARS [ ( _m > > 12 ) & 0xf ] ;
buf [ 13 ] = Utils : : HEXCHARS [ ( _m > > 8 ) & 0xf ] ;
buf [ 14 ] = ' : ' ;
buf [ 15 ] = Utils : : HEXCHARS [ ( _m > > 4 ) & 0xf ] ;
buf [ 16 ] = Utils : : HEXCHARS [ _m & 0xf ] ;
buf [ 17 ] = ( char ) 0 ;
return buf ;
}
2025-07-03 11:26:23 -04:00
inline MAC & operator = ( const MAC & m )
2014-05-23 14:32:31 -07:00
{
_m = m . _m ;
return * this ;
}
2025-07-03 11:26:23 -04:00
inline MAC & operator = ( const uint64_t m )
2015-09-11 11:45:04 -07:00
{
_m = m ;
return * this ;
}
2014-05-23 14:32:31 -07:00
2025-07-03 11:26:23 -04:00
inline bool operator = = ( const MAC & m ) const
{
return ( _m = = m . _m ) ;
}
inline bool operator ! = ( const MAC & m ) const
{
return ( _m ! = m . _m ) ;
}
inline bool operator < ( const MAC & m ) const
{
return ( _m < m . _m ) ;
}
inline bool operator < = ( const MAC & m ) const
{
return ( _m < = m . _m ) ;
}
inline bool operator > ( const MAC & m ) const
{
return ( _m > m . _m ) ;
}
inline bool operator > = ( const MAC & m ) const
{
return ( _m > = m . _m ) ;
}
2014-05-23 14:32:31 -07:00
2025-07-03 11:26:23 -04:00
private :
2014-05-23 14:32:31 -07:00
uint64_t _m ;
2013-07-04 16:56:19 -04:00
} ;
2025-07-03 11:26:23 -04:00
} // namespace ZeroTier
2013-07-04 16:56:19 -04:00
# endif