github.com/cilium/cilium@v1.16.2/pkg/mac/mac.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package mac 5 6 import ( 7 "bytes" 8 "crypto/rand" 9 "encoding/hex" 10 "fmt" 11 "net" 12 ) 13 14 // Untagged ethernet (IEEE 802.3) frame header len 15 const EthHdrLen = 14 16 17 // Uint64MAC is the __u64 representation of a MAC address. 18 // It corresponds to the C mac_t type used in bpf/. 19 type Uint64MAC uint64 20 21 func (m Uint64MAC) String() string { 22 return fmt.Sprintf("%02X:%02X:%02X:%02X:%02X:%02X", 23 uint64((m & 0x0000000000FF)), 24 uint64((m&0x00000000FF00)>>8), 25 uint64((m&0x000000FF0000)>>16), 26 uint64((m&0x0000FF000000)>>24), 27 uint64((m&0x00FF00000000)>>32), 28 uint64((m&0xFF0000000000)>>40), 29 ) 30 } 31 32 // MAC address is an net.HardwareAddr encapsulation to force cilium to only use MAC-48. 33 type MAC net.HardwareAddr 34 35 // String returns the string representation of m. 36 func (m MAC) String() string { 37 return net.HardwareAddr(m).String() 38 } 39 40 // ParseMAC parses s only as an IEEE 802 MAC-48. 41 func ParseMAC(s string) (MAC, error) { 42 ha, err := net.ParseMAC(s) 43 if err != nil { 44 return nil, err 45 } 46 if len(ha) != 6 { 47 return nil, fmt.Errorf("invalid MAC address %s", s) 48 } 49 50 return MAC(ha), nil 51 } 52 53 // Uint64 returns the MAC in uint64 format. The MAC is represented as little-endian in 54 // the returned value. 55 // Example: 56 // 57 // m := MAC([]{0x11, 0x12, 0x23, 0x34, 0x45, 0x56}) 58 // v, err := m.Uint64() 59 // fmt.Printf("0x%X", v) // 0x564534231211 60 func (m MAC) Uint64() (Uint64MAC, error) { 61 if len(m) != 6 { 62 return 0, fmt.Errorf("invalid MAC address %s", m.String()) 63 } 64 65 res := uint64(m[5])<<40 | uint64(m[4])<<32 | uint64(m[3])<<24 | 66 uint64(m[2])<<16 | uint64(m[1])<<8 | uint64(m[0]) 67 return Uint64MAC(res), nil 68 } 69 70 func (m MAC) MarshalJSON() ([]byte, error) { 71 if len(m) == 0 { 72 return []byte(`""`), nil 73 } 74 if len(m) != 6 { 75 return nil, fmt.Errorf("invalid MAC address length %s", string(m)) 76 } 77 return []byte(fmt.Sprintf("\"%02x:%02x:%02x:%02x:%02x:%02x\"", m[0], m[1], m[2], m[3], m[4], m[5])), nil 78 } 79 80 func (m MAC) MarshalIndentJSON(prefix, indent string) ([]byte, error) { 81 return m.MarshalJSON() 82 } 83 84 func (m *MAC) UnmarshalJSON(data []byte) error { 85 if len(data) == len([]byte(`""`)) { 86 if m == nil { 87 m = new(MAC) 88 } 89 *m = MAC{} 90 return nil 91 } 92 if len(data) != 19 { 93 return fmt.Errorf("invalid MAC address length %s", string(data)) 94 } 95 data = data[1 : len(data)-1] 96 macStr := bytes.Replace(data, []byte(`:`), []byte(``), -1) 97 if len(macStr) != 12 { 98 return fmt.Errorf("invalid MAC address format") 99 } 100 macByte := make([]byte, len(macStr)) 101 hex.Decode(macByte, macStr) 102 *m = MAC{macByte[0], macByte[1], macByte[2], macByte[3], macByte[4], macByte[5]} 103 return nil 104 } 105 106 // GenerateRandMAC generates a random unicast and locally administered MAC address. 107 func GenerateRandMAC() (MAC, error) { 108 buf := make([]byte, 6) 109 if _, err := rand.Read(buf); err != nil { 110 return nil, fmt.Errorf("Unable to retrieve 6 rnd bytes: %w", err) 111 } 112 113 // Set locally administered addresses bit and reset multicast bit 114 buf[0] = (buf[0] | 0x02) & 0xfe 115 116 return buf, nil 117 } 118 119 // HaveMACAddrs returns true if all given network interfaces have L2 addr. 120 func HaveMACAddrs(ifaces []string) bool { 121 for _, iface := range ifaces { 122 if !HasMacAddr(iface) { 123 return false 124 } 125 } 126 return true 127 } 128 129 // CArrayString returns a string which can be used for assigning the given 130 // MAC addr to "union macaddr" in C. 131 func CArrayString(m net.HardwareAddr) string { 132 if m == nil || len(m) != 6 { 133 return "{0x0,0x0,0x0,0x0,0x0,0x0}" 134 } 135 136 return fmt.Sprintf("{0x%x,0x%x,0x%x,0x%x,0x%x,0x%x}", 137 m[0], m[1], m[2], m[3], m[4], m[5]) 138 }