Overview
MAC addresses (Media Access Control addresses) serve as unique identifiers for network interface controllers at the data link layer (Layer 2) of the OSI model. Each network interface card possesses a MAC address burned into its hardware during manufacturing, creating a globally unique identifier for device communication on local networks.
Software developers encounter MAC addresses in network programming, device management, security implementations, and system administration tasks. Applications that monitor network traffic, manage DHCP leases, implement MAC filtering, or track devices across networks require MAC address parsing, validation, and manipulation capabilities.
MAC addresses consist of 48 bits (6 bytes) typically represented as six groups of two hexadecimal digits. The IEEE assigns the first three bytes as the Organizationally Unique Identifier (OUI) to hardware manufacturers, while manufacturers assign the last three bytes to create unique addresses within their allocation.
00:1A:2B:3C:4D:5E
└──┬──┘ └───┬───┘
OUI Device ID
Network protocols like Ethernet, Wi-Fi, and Bluetooth rely on MAC addresses for frame delivery between devices on the same network segment. Unlike IP addresses that route packets across networks, MAC addresses facilitate direct communication between devices sharing a broadcast domain.
Key Principles
MAC addresses follow the IEEE 802 standard specification, defining both the structure and assignment methodology. The 48-bit address space provides approximately 281 trillion unique addresses, though certain addresses serve reserved purposes and cannot appear on network interfaces.
The address structure divides into two components. The first 24 bits form the Organizationally Unique Identifier, assigned by the IEEE Registration Authority to manufacturers and vendors. The remaining 24 bits represent the Network Interface Controller specific identifier, managed by the OUI holder to guarantee uniqueness within their allocation.
Individual bits within the MAC address carry specific meanings. The least significant bit of the first byte indicates whether the address is unicast (0) or multicast (1). The second least significant bit of the first byte distinguishes between universally administered addresses (0) assigned by manufacturers and locally administered addresses (1) configured by network administrators.
01:23:45:67:89:AB
│
└─ First byte: 0x01 = 00000001 binary
│ │
│ └─ Unicast/Multicast bit
└────── Universal/Local bit
Universally administered addresses guarantee global uniqueness through IEEE coordination. Manufacturers obtain OUI blocks and assign unique addresses within their range. Locally administered addresses allow network administrators to override manufacturer assignments, useful for virtualization, testing, or specific network configurations.
Broadcast addresses represent a special case where all 48 bits are set to one (FF:FF:FF:FF:FF:FF), directing frames to all devices on the network segment. Multicast addresses begin with specific bit patterns that network hardware recognizes for efficient group communication.
Address representation varies across systems and applications. Colon-separated hexadecimal pairs (00:1A:2B:3C:4D:5E) dominate Unix-like systems. Hyphen-separated notation (00-1A-2B-3C-4D-5E) appears in Windows environments. Cisco equipment often uses dot-separated quadruplets (001A.2B3C.4D5E). Some systems omit delimiters entirely (001A2B3C4D5E).
Case sensitivity in hexadecimal representation does not affect address interpretation. The address 00:1a:2b:3c:4d:5e equals 00:1A:2B:3C:4D:5E functionally, though applications typically normalize to uppercase or lowercase for consistency.
Ruby Implementation
Ruby lacks native MAC address types but provides string manipulation, regular expressions, and binary operations for working with MAC addresses. Applications typically represent MAC addresses as strings, byte arrays, or integer values depending on the operation context.
Parsing MAC addresses from various formats requires flexible regular expressions that accommodate different delimiter styles and case variations:
def parse_mac_address(input)
# Remove common delimiters and whitespace
cleaned = input.to_s.strip.gsub(/[\s:.\-]/, '')
# Validate hexadecimal and length
return nil unless cleaned.match?(/^[0-9A-Fa-f]{12}$/)
# Convert to standard colon-separated format
cleaned.upcase.scan(/.{2}/).join(':')
end
parse_mac_address('00:1A:2B:3C:4D:5E')
# => "00:1A:2B:3C:4D:5E"
parse_mac_address('00-1a-2b-3c-4d-5e')
# => "00:1A:2B:3C:4D:5E"
parse_mac_address('001a.2b3c.4d5e')
# => "00:1A:2B:3C:4D:5E"
Validation extends beyond format checking to verify address characteristics and detect invalid configurations:
class MacAddress
VALID_FORMAT = /^([0-9A-Fa-f]{2}[:\-.]?){5}[0-9A-Fa-f]{2}$/
BROADCAST = 'FF:FF:FF:FF:FF:FF'
attr_reader :address
def initialize(address)
@address = normalize(address)
raise ArgumentError, 'Invalid MAC address format' unless valid?
end
def valid?
return false if @address.nil?
return false if broadcast?
return false if null?
true
end
def broadcast?
@address == BROADCAST
end
def null?
@address == '00:00:00:00:00:00'
end
def multicast?
# Check least significant bit of first byte
first_byte = @address[0..1].to_i(16)
(first_byte & 1) == 1
end
def locally_administered?
# Check second least significant bit of first byte
first_byte = @address[0..1].to_i(16)
(first_byte & 2) == 2
end
def oui
@address[0..7]
end
private
def normalize(input)
cleaned = input.to_s.strip.gsub(/[\s:.\-]/, '')
return nil unless cleaned.match?(/^[0-9A-Fa-f]{12}$/)
cleaned.upcase.scan(/.{2}/).join(':')
end
end
mac = MacAddress.new('00:1A:2B:3C:4D:5E')
mac.multicast?
# => false
mac.locally_administered?
# => false
mac.oui
# => "00:1A:2B"
Generating MAC addresses for testing or virtualization requires careful attention to bit flags. Test environments often use locally administered addresses to avoid conflicts with physical hardware:
def generate_local_mac
# Set locally administered bit (bit 1 of first byte)
# Clear multicast bit (bit 0 of first byte)
bytes = 6.times.map { rand(256) }
bytes[0] = (bytes[0] & 0xFE) | 0x02
bytes.map { |b| format('%02X', b) }.join(':')
end
5.times { puts generate_local_mac }
# => 02:A3:4F:1B:8C:D2
# => 06:7E:2A:9F:4B:1C
# => 0A:5D:8B:3E:F7:29
# => 0E:91:6C:2D:A8:4F
# => 02:4C:7A:E3:5B:96
Converting between representations supports integration with different systems and protocols:
class MacAddress
def to_cisco_format
@address.gsub(':', '').scan(/.{4}/).join('.')
end
def to_windows_format
@address.tr(':', '-')
end
def to_compact
@address.gsub(':', '')
end
def to_integer
@address.gsub(':', '').to_i(16)
end
def self.from_integer(value)
raise ArgumentError, 'Value too large' if value > 0xFFFFFFFFFFFF
hex_string = format('%012X', value)
new(hex_string.scan(/.{2}/).join(':'))
end
end
mac = MacAddress.new('00:1A:2B:3C:4D:5E')
mac.to_cisco_format
# => "001A.2B3C.4D5E"
mac.to_integer
# => 7124049582
MacAddress.from_integer(7124049582).address
# => "00:1A:2B:3C:4D:5E"
Reading MAC addresses from system interfaces requires platform-specific approaches. Unix-like systems provide interface information through network commands:
require 'open3'
def get_interface_mac(interface = 'eth0')
case RbConfig::CONFIG['host_os']
when /linux/
output, = Open3.capture2('cat', "/sys/class/net/#{interface}/address")
output.strip.upcase
when /darwin/
output, = Open3.capture2('ifconfig', interface)
output[/ether ([0-9a-f:]+)/i, 1]&.upcase
else
raise NotImplementedError, 'Platform not supported'
end
rescue Errno::ENOENT
nil
end
get_interface_mac('eth0')
# => "00:1A:2B:3C:4D:5E"
Practical Examples
Network device inventory systems track hardware by MAC address, requiring parsing from diverse data sources and format normalization:
class DeviceInventory
def initialize
@devices = {}
end
def add_device(mac, device_info)
normalized_mac = MacAddress.new(mac).address
@devices[normalized_mac] = device_info
end
def find_device(mac)
normalized_mac = MacAddress.new(mac).address
@devices[normalized_mac]
end
def import_from_arp_table(arp_output)
arp_output.each_line do |line|
# Parse ARP table format: "192.168.1.100 at 00:1a:2b:3c:4d:5e on eth0"
if line =~ /(\d+\.\d+\.\d+\.\d+)\s+at\s+([\da-f:]+)/i
ip_address = $1
mac_address = $2
add_device(mac_address, { ip: ip_address, discovered_at: Time.now })
end
end
end
end
inventory = DeviceInventory.new
inventory.add_device('00:1A:2B:3C:4D:5E', { hostname: 'server-01', location: 'rack-3' })
# Finding device works with any MAC format
inventory.find_device('00-1a-2b-3c-4d-5e')
# => { hostname: 'server-01', location: 'rack-3' }
DHCP server implementations associate IP addresses with MAC addresses, requiring address validation and lease management:
class DhcpLeaseManager
def initialize
@leases = {}
end
def assign_lease(mac, ip_address, duration: 86400)
mac_obj = MacAddress.new(mac)
# Reject multicast and broadcast addresses
if mac_obj.multicast? || mac_obj.broadcast?
raise ArgumentError, 'Cannot assign lease to multicast or broadcast MAC'
end
@leases[mac_obj.address] = {
ip: ip_address,
assigned_at: Time.now,
expires_at: Time.now + duration,
renewals: 0
}
end
def renew_lease(mac)
mac_obj = MacAddress.new(mac)
lease = @leases[mac_obj.address]
return nil unless lease
return nil if lease[:expires_at] < Time.now
lease[:expires_at] = Time.now + 86400
lease[:renewals] += 1
lease
end
def active_leases
now = Time.now
@leases.select { |_, lease| lease[:expires_at] > now }
end
end
manager = DhcpLeaseManager.new
manager.assign_lease('00:1A:2B:3C:4D:5E', '192.168.1.100')
manager.renew_lease('00:1A:2B:3C:4D:5E')
# => { ip: '192.168.1.100', assigned_at: ..., expires_at: ..., renewals: 1 }
Network security applications implement MAC address filtering and monitoring for access control:
class MacAddressFilter
def initialize
@whitelist = Set.new
@blacklist = Set.new
end
def add_to_whitelist(mac_addresses)
mac_addresses.each do |mac|
normalized = MacAddress.new(mac).address
@whitelist.add(normalized)
@blacklist.delete(normalized)
end
end
def add_to_blacklist(mac_addresses)
mac_addresses.each do |mac|
normalized = MacAddress.new(mac).address
@blacklist.add(normalized)
@whitelist.delete(normalized)
end
end
def allowed?(mac)
normalized = MacAddress.new(mac).address
# Whitelist takes precedence
return true if @whitelist.include?(normalized)
return false if @blacklist.include?(normalized)
# Default policy when neither list matches
@whitelist.empty? # Allow all if no whitelist configured
end
def vendor_allowed?(mac, allowed_vendors)
mac_obj = MacAddress.new(mac)
allowed_vendors.include?(mac_obj.oui)
end
end
filter = MacAddressFilter.new
filter.add_to_whitelist(['00:1A:2B:3C:4D:5E', '00:1A:2B:11:22:33'])
filter.allowed?('00:1A:2B:3C:4D:5E')
# => true
filter.allowed?('AA:BB:CC:DD:EE:FF')
# => false
Log file analysis requires extracting MAC addresses from unstructured text and aggregating statistics:
class MacAddressLogParser
def initialize
@mac_pattern = /\b([0-9A-Fa-f]{2}[:\-.]){5}[0-9A-Fa-f]{2}\b/
end
def extract_macs(log_content)
addresses = Set.new
log_content.scan(@mac_pattern) do
match = Regexp.last_match(0)
begin
normalized = MacAddress.new(match).address
addresses.add(normalized)
rescue ArgumentError
# Skip invalid addresses
end
end
addresses.to_a
end
def analyze_log_file(file_path)
macs = Hash.new(0)
File.foreach(file_path) do |line|
extract_macs(line).each do |mac|
macs[mac] += 1
end
end
macs.sort_by { |_, count| -count }
end
end
parser = MacAddressLogParser.new
log_text = <<~LOG
2025-01-15 10:23:45 Device 00:1A:2B:3C:4D:5E connected
2025-01-15 10:24:12 Traffic from 00-1a-2b-3c-4d-5e to gateway
2025-01-15 10:25:33 Device AA:BB:CC:DD:EE:FF connected
LOG
parser.extract_macs(log_text)
# => ["00:1A:2B:3C:4D:5E", "AA:BB:CC:DD:EE:FF"]
Common Patterns
Address normalization establishes a canonical representation for consistent storage and comparison operations:
module MacAddressNormalizer
def self.normalize(address, format: :colon, case_style: :upper)
# Strip all delimiters and whitespace
cleaned = address.to_s.gsub(/[\s:.\-]/, '')
# Validate length and characters
raise ArgumentError, 'Invalid MAC address' unless cleaned.match?(/^[0-9A-Fa-f]{12}$/)
# Apply case transformation
cleaned = case_style == :upper ? cleaned.upcase : cleaned.downcase
# Apply format
case format
when :colon
cleaned.scan(/.{2}/).join(':')
when :hyphen
cleaned.scan(/.{2}/).join('-')
when :cisco
cleaned.scan(/.{4}/).join('.')
when :compact
cleaned
else
raise ArgumentError, "Unknown format: #{format}"
end
end
end
MacAddressNormalizer.normalize('00-1a-2b-3c-4d-5e', format: :colon, case_style: :upper)
# => "00:1A:2B:3C:4D:5E"
MacAddressNormalizer.normalize('00:1A:2B:3C:4D:5E', format: :cisco, case_style: :lower)
# => "001a.2b3c.4d5e"
Address range generation supports testing and allocation management:
class MacAddressRange
def initialize(base_address, count)
@base = MacAddress.new(base_address).to_integer
@count = count
end
def each
return enum_for(__method__) unless block_given?
@count.times do |offset|
address_int = @base + offset
raise RangeError, 'MAC address overflow' if address_int > 0xFFFFFFFFFFFF
yield MacAddress.from_integer(address_int).address
end
end
def to_a
each.to_a
end
end
range = MacAddressRange.new('00:1A:2B:3C:4D:5E', 5)
range.to_a
# => [
# "00:1A:2B:3C:4D:5E",
# "00:1A:2B:3C:4D:5F",
# "00:1A:2B:3C:4D:60",
# "00:1A:2B:3C:4D:61",
# "00:1A:2B:3C:4D:62"
# ]
Vendor identification uses OUI lookups to determine hardware manufacturers:
class OuiDatabase
def initialize
@oui_map = {}
load_database
end
def lookup(mac_address)
mac_obj = MacAddress.new(mac_address)
oui = mac_obj.oui.gsub(':', '')
@oui_map[oui]
end
def load_database
# Load from IEEE OUI database file
# Format: OUI/Organization/Address
File.foreach('oui.txt') do |line|
if line =~ /^([0-9A-F]{6})\s+\(hex\)\s+(.+)$/
oui = $1
vendor = $2.strip
@oui_map[oui] = vendor
end
end
rescue Errno::ENOENT
# Database file not found
end
end
# Example usage with mock data
db = OuiDatabase.new
db.instance_variable_get(:@oui_map)['001A2B'] = 'Example Corporation'
db.lookup('00:1A:2B:3C:4D:5E')
# => "Example Corporation"
Address anonymization protects privacy while maintaining analytical value:
module MacAddressAnonymizer
def self.anonymize(address, preserve_oui: true)
mac_obj = MacAddress.new(address)
if preserve_oui
# Keep OUI, randomize device identifier
oui = mac_obj.oui
device_bytes = 3.times.map { rand(256) }
"#{oui}:#{device_bytes.map { |b| format('%02X', b) }.join(':')}"
else
# Generate completely random locally-administered address
bytes = 6.times.map { rand(256) }
bytes[0] = (bytes[0] & 0xFE) | 0x02
bytes.map { |b| format('%02X', b) }.join(':')
end
end
def self.anonymize_batch(addresses)
# Consistent anonymization using hash mapping
mapping = {}
addresses.map do |addr|
normalized = MacAddress.new(addr).address
mapping[normalized] ||= anonymize(normalized)
mapping[normalized]
end
end
end
MacAddressAnonymizer.anonymize('00:1A:2B:3C:4D:5E', preserve_oui: true)
# => "00:1A:2B:A7:F3:91" (random device ID)
MacAddressAnonymizer.anonymize('00:1A:2B:3C:4D:5E', preserve_oui: false)
# => "02:8C:E4:1B:9A:5F" (fully random)
Error Handling & Edge Cases
Invalid format detection requires comprehensive validation beyond simple pattern matching:
class MacAddressValidator
ERRORS = {
invalid_format: 'MAC address format is invalid',
invalid_length: 'MAC address must be 12 hexadecimal digits',
invalid_characters: 'MAC address contains invalid characters',
broadcast_address: 'Broadcast address not allowed',
null_address: 'Null address not allowed',
multicast_address: 'Multicast address not allowed in this context'
}
def self.validate(address, allow_multicast: false, allow_null: false)
errors = []
# Remove delimiters
cleaned = address.to_s.gsub(/[\s:.\-]/, '')
# Check length
if cleaned.length != 12
errors << ERRORS[:invalid_length]
return errors
end
# Check characters
unless cleaned.match?(/^[0-9A-Fa-f]{12}$/)
errors << ERRORS[:invalid_characters]
return errors
end
# Convert to normalized format
normalized = cleaned.upcase.scan(/.{2}/).join(':')
# Check for broadcast
if normalized == 'FF:FF:FF:FF:FF:FF'
errors << ERRORS[:broadcast_address]
end
# Check for null
if normalized == '00:00:00:00:00:00' && !allow_null
errors << ERRORS[:null_address]
end
# Check for multicast
first_byte = normalized[0..1].to_i(16)
if (first_byte & 1) == 1 && !allow_multicast
errors << ERRORS[:multicast_address]
end
errors
end
end
MacAddressValidator.validate('00:1A:2B:3C:4D:5E')
# => []
MacAddressValidator.validate('00:1A:2B:3C:4D')
# => ["MAC address must be 12 hexadecimal digits"]
MacAddressValidator.validate('FF:FF:FF:FF:FF:FF')
# => ["Broadcast address not allowed"]
MacAddressValidator.validate('01:00:5E:00:00:01')
# => ["Multicast address not allowed in this context"]
Delimiter ambiguity requires heuristic detection for mixed-format inputs:
class MacAddressParser
def self.detect_format(address)
cleaned = address.to_s.strip
case cleaned
when /^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$/
:colon
when /^([0-9A-Fa-f]{2}-){5}[0-9A-Fa-f]{2}$/
:hyphen
when /^[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4}$/
:cisco
when /^[0-9A-Fa-f]{12}$/
:compact
else
:unknown
end
end
def self.parse_flexible(address)
format = detect_format(address)
case format
when :colon, :hyphen, :cisco
address.gsub(/[:\-.]/, '').upcase.scan(/.{2}/).join(':')
when :compact
address.upcase.scan(/.{2}/).join(':')
else
raise ArgumentError, "Unable to parse MAC address: #{address}"
end
end
end
MacAddressParser.parse_flexible('00:1A:2B:3C:4D:5E')
# => "00:1A:2B:3C:4D:5E"
MacAddressParser.parse_flexible('001A.2B3C.4D5E')
# => "00:1A:2B:3C:4D:5E"
MacAddressParser.parse_flexible('001a2b3c4d5e')
# => "00:1A:2B:3C:4D:5E"
Database storage decisions impact query performance and indexing strategies:
# Store as normalized string for human readability
class Device < ActiveRecord::Base
before_validation :normalize_mac_address
validates :mac_address, presence: true, uniqueness: true
validates :mac_address, format: {
with: /\A([0-9A-F]{2}:){5}[0-9A-F]{2}\z/,
message: 'must be in format XX:XX:XX:XX:XX:XX'
}
private
def normalize_mac_address
return if mac_address.nil?
self.mac_address = MacAddress.new(mac_address).address
rescue ArgumentError => e
errors.add(:mac_address, e.message)
end
end
# Store as 64-bit integer for efficient indexing and range queries
class DeviceOptimized < ActiveRecord::Base
def mac_address=(value)
mac_obj = MacAddress.new(value)
self.mac_address_int = mac_obj.to_integer
@mac_address_formatted = mac_obj.address
end
def mac_address
@mac_address_formatted ||= MacAddress.from_integer(mac_address_int).address
end
end
Collision detection in address pools prevents duplicate assignments:
class MacAddressPool
def initialize(oui_prefix)
@oui = oui_prefix
@assigned = Set.new
@max_attempts = 1000
end
def allocate
attempts = 0
loop do
candidate = generate_address
if @assigned.add?(candidate)
return candidate
end
attempts += 1
raise 'Address pool exhausted' if attempts >= @max_attempts
end
end
def release(address)
normalized = MacAddress.new(address).address
@assigned.delete(normalized)
end
private
def generate_address
device_bytes = 3.times.map { rand(256) }
device_part = device_bytes.map { |b| format('%02X', b) }.join(':')
"#{@oui}:#{device_part}"
end
end
pool = MacAddressPool.new('00:1A:2B')
addr1 = pool.allocate
addr2 = pool.allocate
# => "00:1A:2B:7F:3E:A1"
pool.release(addr1)
Tools & Ecosystem
The ipaddr gem from Ruby's standard library handles IP addresses but lacks MAC address support. The macaddr gem provides MAC address handling:
require 'macaddr'
# System MAC address retrieval
Mac.address
# => "00:1A:2B:3C:4D:5E"
# All network interface addresses
Mac.addr
# => ["00:1A:2B:3C:4D:5E", "AA:BB:CC:DD:EE:FF"]
The net-ping gem enables network discovery using MAC addresses:
require 'net/ping'
# ICMP ping with ARP table inspection
pinger = Net::Ping::External.new('192.168.1.1')
if pinger.ping?
# Extract MAC from ARP cache after ping
arp_output = `arp -a 192.168.1.1`
mac = arp_output[/([0-9a-f]{2}[:\-]){5}[0-9a-f]{2}/i]
end
The packetfu gem provides raw packet manipulation including Ethernet frames:
require 'packetfu'
# Create Ethernet frame with specific MAC addresses
packet = PacketFu::EthPacket.new
packet.eth_saddr = '00:1A:2B:3C:4D:5E'
packet.eth_daddr = 'AA:BB:CC:DD:EE:FF'
packet.eth_proto = 0x0800 # IPv4
# Inspect frame
packet.peek
# => Ethernet header with source and destination MACs
System utilities provide MAC address information outside Ruby:
# Linux - interface MAC address
cat /sys/class/net/eth0/address
# macOS/BSD - interface configuration
ifconfig en0 | grep ether
# Windows - network adapter configuration
getmac /v /fo csv
# Cross-platform - ARP table
arp -a
Network monitoring tools like tcpdump and Wireshark capture and filter packets by MAC address:
# Capture packets from specific MAC
tcpdump -i eth0 ether src 00:1a:2b:3c:4d:5e
# Capture packets to specific MAC
tcpdump -i eth0 ether dst aa:bb:cc:dd:ee:ff
Reference
MAC Address Format Specifications
| Format | Pattern | Example | Common Usage |
|---|---|---|---|
| Colon-separated | XX:XX:XX:XX:XX:XX | 00:1A:2B:3C:4D:5E | Linux, Unix, BSD |
| Hyphen-separated | XX-XX-XX-XX-XX-XX | 00-1A-2B-3C-4D-5E | Windows, Microsoft |
| Cisco format | XXXX.XXXX.XXXX | 001A.2B3C.4D5E | Cisco devices |
| Compact | XXXXXXXXXXXX | 001A2B3C4D5E | Storage, databases |
| IEEE canonical | XX-XX-XX-XX-XX-XX | 00-1A-2B-3C-4D-5E | IEEE documentation |
Address Type Classification
| Type | Bit Pattern | First Byte Examples | Description |
|---|---|---|---|
| Unicast | Bit 0 = 0 | 00, 02, 04, 06 | Single destination device |
| Multicast | Bit 0 = 1 | 01, 03, 05, 07 | Group of devices |
| Broadcast | All bits 1 | FF | All devices on segment |
| Universal | Bit 1 = 0 | 00, 01, 04, 05 | Manufacturer assigned |
| Local | Bit 1 = 1 | 02, 03, 06, 07 | Locally administered |
Special MAC Addresses
| Address | Purpose | Usage Context |
|---|---|---|
| FF:FF:FF:FF:FF:FF | Broadcast | ARP requests, DHCP discovery |
| 00:00:00:00:00:00 | Null address | Uninitialized interfaces |
| 01:00:5E:XX:XX:XX | IPv4 multicast | IP multicast over Ethernet |
| 33:33:XX:XX:XX:XX | IPv6 multicast | IPv6 multicast over Ethernet |
| 01:80:C2:00:00:00 | STP BPDU | Spanning Tree Protocol |
| FF:FF:FF:FF:FF:FE | Reserved | Invalid, commonly used in tests |
Ruby Regular Expressions
| Pattern | Matches | Use Case |
|---|---|---|
| /^([0-9A-Fa-f]{2}[:-.]?){5}[0-9A-Fa-f]{2}$/ | Any delimiter format | Flexible parsing |
| /^([0-9A-F]{2}:){5}[0-9A-F]{2}$/ | Uppercase colon-separated | Strict validation |
| /^[0-9A-Fa-f]{12}$/ | Compact format | After delimiter removal |
| /\b([0-9A-Fa-f]{2}[:-.]){5}[0-9A-Fa-f]{2}\b/ | Extract from text | Log file parsing |
Validation Rules
| Rule | Check | Implementation |
|---|---|---|
| Length | Exactly 12 hex digits | cleaned.length == 12 |
| Characters | Only 0-9, A-F | match?(/^[0-9A-Fa-f]{12}$/) |
| Not broadcast | Not FF:FF:FF:FF:FF:FF | normalized != 'FF:FF:FF:FF:FF:FF' |
| Not null | Not 00:00:00:00:00:00 | normalized != '00:00:00:00:00:00' |
| Unicast only | Bit 0 of byte 0 is 0 | (first_byte & 1) == 0 |
| Universal only | Bit 1 of byte 0 is 0 | (first_byte & 2) == 0 |
Bit Operations
| Operation | Ruby Code | Result |
|---|---|---|
| Extract first byte | address[0..1].to_i(16) | Integer 0-255 |
| Check multicast bit | (first_byte & 1) == 1 | Boolean |
| Check local bit | (first_byte & 2) == 2 | Boolean |
| Set local bit | byte OR 0x02 | Locally administered |
| Clear multicast bit | byte AND 0xFE | Unicast |
| Convert to integer | address.gsub(':', '').to_i(16) | 48-bit integer |
Common OUI Prefixes
| OUI | Manufacturer | Notes |
|---|---|---|
| 00:1A:2B | Example Corp | Reserved for documentation |
| 00:00:5E | IANA | Reserved for protocols |
| 00:0C:29 | VMware | Virtual machine interfaces |
| 08:00:27 | Oracle VirtualBox | Virtual machine interfaces |
| 52:54:00 | QEMU/KVM | Virtual machine interfaces |
| AC:DE:48 | Private ranges | Reserved for local use |