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 }