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

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package mcastmanager
     5  
     6  import (
     7  	"net/netip"
     8  
     9  	"github.com/sirupsen/logrus"
    10  
    11  	"github.com/cilium/cilium/pkg/lock"
    12  	"github.com/cilium/cilium/pkg/logging"
    13  	"github.com/cilium/cilium/pkg/logging/logfields"
    14  	"github.com/cilium/cilium/pkg/multicast"
    15  )
    16  
    17  var (
    18  	log = logging.DefaultLogger.WithField(logfields.LogSubsys, "mcast-manager")
    19  )
    20  
    21  // MCastManager manages IPv6 address
    22  type MCastManager struct {
    23  	mutex lock.Mutex
    24  
    25  	// iface is the interface that mcast addresses are applied to
    26  	iface string
    27  
    28  	// addresses keeps track of all ipv6 addresses by grouping them based on the
    29  	// last 3 bytes of the address. The last 3 bytes of an IPv6 address determines
    30  	// the solicited node multicast address: https://tools.ietf.org/html/rfc4291#section-2.7.1
    31  	addresses map[int32]map[netip.Addr]struct{}
    32  
    33  	// state tracks all the IPv6 multicast addresses created by MCastManager
    34  	state map[netip.Addr]struct{}
    35  }
    36  
    37  // New creates a McastManager instance. Create a dummy manager when iface is empty
    38  // string.
    39  func New(iface string) *MCastManager {
    40  	return &MCastManager{
    41  		addresses: make(map[int32]map[netip.Addr]struct{}),
    42  		state:     make(map[netip.Addr]struct{}),
    43  		iface:     iface,
    44  	}
    45  }
    46  
    47  // AddAddress is called when a new endpoint is added
    48  func (mgr *MCastManager) AddAddress(ipv6 netip.Addr) {
    49  	if mgr.iface == "" || !ipv6.IsValid() {
    50  		return
    51  	}
    52  
    53  	key := multicast.Address(ipv6).Key()
    54  
    55  	mgr.mutex.Lock()
    56  	defer mgr.mutex.Unlock()
    57  
    58  	if _, ok := mgr.addresses[key]; !ok {
    59  		// First IP that has the solicited node maddr
    60  		mgr.joinGroup(ipv6)
    61  		mgr.addresses[key] = map[netip.Addr]struct{}{}
    62  	}
    63  
    64  	mgr.addresses[key][ipv6] = struct{}{}
    65  }
    66  
    67  // RemoveAddress is called when an endpoint is removed
    68  func (mgr *MCastManager) RemoveAddress(ipv6 netip.Addr) {
    69  	if mgr.iface == "" || !ipv6.IsValid() {
    70  		return
    71  	}
    72  
    73  	key := multicast.Address(ipv6).Key()
    74  
    75  	mgr.mutex.Lock()
    76  	defer mgr.mutex.Unlock()
    77  
    78  	if m, ok := mgr.addresses[key]; ok {
    79  		delete(m, ipv6)
    80  		if len(m) == 0 {
    81  			// Last IP that has the solicited node maddr
    82  			mgr.leaveGroup(ipv6)
    83  			delete(mgr.addresses, key)
    84  		}
    85  	}
    86  }
    87  
    88  func (mgr *MCastManager) joinGroup(ipv6 netip.Addr) {
    89  	maddr := multicast.Address(ipv6).SolicitedNodeMaddr()
    90  	if err := multicast.JoinGroup(mgr.iface, maddr); err != nil {
    91  		log.WithError(err).WithField("maddr", maddr).Warn("failed to join multicast group")
    92  		return
    93  	}
    94  
    95  	log.WithFields(logrus.Fields{
    96  		"device": mgr.iface,
    97  		"mcast":  maddr,
    98  	}).Info("Joined multicast group")
    99  
   100  	mgr.state[maddr] = struct{}{}
   101  }
   102  
   103  func (mgr *MCastManager) leaveGroup(ipv6 netip.Addr) {
   104  	maddr := multicast.Address(ipv6).SolicitedNodeMaddr()
   105  	if err := multicast.LeaveGroup(mgr.iface, maddr); err != nil {
   106  		log.WithError(err).WithField("maddr", maddr).Warn("failed to leave multicast group")
   107  		return
   108  	}
   109  
   110  	log.WithFields(logrus.Fields{
   111  		"device": mgr.iface,
   112  		"mcast":  maddr,
   113  	}).Info("Left multicast group")
   114  
   115  	delete(mgr.state, maddr)
   116  }