github.com/cilium/cilium@v1.16.2/pkg/multicast/multicast.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package multicast
     5  
     6  import (
     7  	"net"
     8  	"net/netip"
     9  
    10  	"github.com/vishvananda/netlink"
    11  	"golang.org/x/net/ipv6"
    12  
    13  	"github.com/cilium/cilium/pkg/lock"
    14  	"github.com/cilium/cilium/pkg/logging"
    15  	"github.com/cilium/cilium/pkg/logging/logfields"
    16  )
    17  
    18  var (
    19  	// v6Socket is the udp socket used to join/leave a multicast group
    20  	v6Socket net.PacketConn
    21  
    22  	mutex lock.Mutex
    23  
    24  	log = logging.DefaultLogger.WithField(logfields.LogSubsys, "multicast")
    25  )
    26  
    27  // Pre-Defined Multicast Addresses
    28  // as defined in https://tools.ietf.org/html/rfc4291#section-2.7.1
    29  
    30  var (
    31  	// AllNodesIfcLocalMaddr is the multicast address that identifies the group of
    32  	// all IPv6 nodes, within scope 1 (interface-local)
    33  	AllNodesIfcLocalMaddr net.IP = net.ParseIP("ff01::1")
    34  
    35  	// AllNodesLinkLocalMaddr is the multicast address that identifies the group of
    36  	// all IPv6 nodes, within scope 2 (link-local)
    37  	AllNodesLinkLocalMaddr net.IP = net.ParseIP("ff02::1")
    38  
    39  	// AllRoutersIfcLocalMaddr is the multicast address that identifies the group of
    40  	// all IPv6 routers, within scope 1 (interface-local)
    41  	AllRoutersIfcLocalMaddr net.IP = net.ParseIP("ff01::2")
    42  
    43  	// AllRoutersLinkLocalMaddr is the multicast address that identifies the group of
    44  	// all IPv6 routers, within scope 2 (link-local)
    45  	AllRoutersLinkLocalMaddr net.IP = net.ParseIP("ff02::2")
    46  
    47  	// AllRoutersSiteLocalMaddr is the multicast address that identifies the group of
    48  	// all IPv6 routers, within scope 5 (site-local)
    49  	AllRoutersSiteLocalMaddr net.IP = net.ParseIP("ff05::2")
    50  
    51  	// SolicitedNodeMaddrPrefix is the prefix of the multicast address that is used
    52  	// as part of NDP
    53  	SolicitedNodeMaddrPrefix net.IP = net.ParseIP("ff02::1:ff00:0")
    54  )
    55  
    56  func initSocket() error {
    57  	mutex.Lock()
    58  	defer mutex.Unlock()
    59  
    60  	if v6Socket != nil {
    61  		return nil
    62  	}
    63  
    64  	c, err := net.ListenPacket("udp6", "[::]:0")
    65  	if err != nil {
    66  		log.WithError(err).Warn("Failed to listen on socket for multicast")
    67  		return err
    68  	}
    69  
    70  	v6Socket = c
    71  	return nil
    72  }
    73  
    74  // JoinGroup joins the group address group on the interface ifc
    75  func JoinGroup(ifc string, ip netip.Addr) error {
    76  	if err := initSocket(); err != nil {
    77  		return err
    78  	}
    79  
    80  	dev, err := interfaceByName(ifc)
    81  	if err != nil {
    82  		return err
    83  	}
    84  
    85  	return ipv6.NewPacketConn(v6Socket).JoinGroup(dev, &net.UDPAddr{IP: ip.AsSlice()})
    86  }
    87  
    88  // LeaveGroup leaves the group address group on the interface ifc
    89  func LeaveGroup(ifc string, ip netip.Addr) error {
    90  	if err := initSocket(); err != nil {
    91  		return err
    92  	}
    93  
    94  	dev, err := interfaceByName(ifc)
    95  	if err != nil {
    96  		return err
    97  	}
    98  
    99  	return ipv6.NewPacketConn(v6Socket).LeaveGroup(dev, &net.UDPAddr{IP: ip.AsSlice()})
   100  }
   101  
   102  // ListGroup lists multicast addresses on the interface ifc
   103  func ListGroup(ifc string) ([]net.Addr, error) {
   104  	dev, err := interfaceByName(ifc)
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  
   109  	return dev.MulticastAddrs()
   110  }
   111  
   112  // IsInGroup tells if interface ifc belongs to group represented by maddr
   113  func IsInGroup(ifc string, maddr netip.Addr) (bool, error) {
   114  	ips, err := ListGroup(ifc)
   115  	if err != nil {
   116  		return false, err
   117  	}
   118  
   119  	maddrStr := maddr.String()
   120  	for _, gip := range ips {
   121  		if gip.String() == maddrStr {
   122  			return true, nil
   123  		}
   124  	}
   125  
   126  	return false, nil
   127  }
   128  
   129  // Address encapsulates the functionality to generate solicated node multicast address
   130  type Address netip.Addr
   131  
   132  // Key takes the last 3 bytes of endpoint's IPv6 address and compile them in to
   133  // an int32 value as key of the endpoint. It assumes the input is a valid IPv6 address.
   134  // Otherwise it returns 0 (https://tools.ietf.org/html/rfc4291#section-2.7.1)
   135  func (a Address) Key() int32 {
   136  	ipv6 := netip.Addr(a)
   137  	if !ipv6.IsValid() {
   138  		return 0
   139  	}
   140  
   141  	var key int32
   142  	for _, v := range ipv6.AsSlice()[13:] {
   143  		key <<= 8
   144  		key += int32(v)
   145  	}
   146  
   147  	return key
   148  }
   149  
   150  // SolicitedNodeMaddr returns solicited node multicast address
   151  func (a Address) SolicitedNodeMaddr() netip.Addr {
   152  	ipv6 := netip.Addr(a)
   153  	if !ipv6.IsValid() {
   154  		return netip.Addr{}
   155  	}
   156  
   157  	maddr := make([]byte, 16)
   158  	copy(maddr[:13], SolicitedNodeMaddrPrefix[:13])
   159  	copy(maddr[13:], ipv6.AsSlice()[13:])
   160  
   161  	// Use slice-to-array-pointer conversion, available since Go 1.17.
   162  	// TODO: use slice-to-array conversion when switching to Go 1.20
   163  	return netip.AddrFrom16(*(*[16]byte)(maddr))
   164  }
   165  
   166  // interfaceByName get *net.Interface by name using netlink.
   167  //
   168  // The reason not to use net.InterfaceByName directly is to avoid potential
   169  // deadlocks (#15051).
   170  func interfaceByName(name string) (*net.Interface, error) {
   171  	link, err := netlink.LinkByName(name)
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  
   176  	return &net.Interface{
   177  		Index:        link.Attrs().Index,
   178  		MTU:          link.Attrs().MTU,
   179  		Name:         link.Attrs().Name,
   180  		Flags:        link.Attrs().Flags,
   181  		HardwareAddr: link.Attrs().HardwareAddr,
   182  	}, nil
   183  }