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  }