github.com/cilium/cilium@v1.16.2/pkg/testutils/mockmaps/lbmap.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package mockmaps 5 6 import ( 7 "fmt" 8 "net" 9 10 "github.com/cilium/cilium/pkg/cidr" 11 cmtypes "github.com/cilium/cilium/pkg/clustermesh/types" 12 datapathTypes "github.com/cilium/cilium/pkg/datapath/types" 13 "github.com/cilium/cilium/pkg/ip" 14 lb "github.com/cilium/cilium/pkg/loadbalancer" 15 "github.com/cilium/cilium/pkg/lock" 16 "github.com/cilium/cilium/pkg/maps/lbmap" 17 ) 18 19 type LBMockMap struct { 20 lock.Mutex 21 BackendByID map[lb.BackendID]*lb.Backend 22 ServiceByID map[uint16]*lb.SVC 23 AffinityMatch datapathTypes.BackendIDByServiceIDSet 24 SourceRanges datapathTypes.SourceRangeSetByServiceID 25 DummyMaglevTable map[uint16]int // svcID => backends count 26 SvcActiveBackendsCount map[uint16]int 27 SockRevNat4 map[lbmap.SockRevNat4Key]lbmap.SockRevNat4Value 28 SockRevNat6 map[lbmap.SockRevNat6Key]lbmap.SockRevNat6Value 29 } 30 31 func NewLBMockMap() *LBMockMap { 32 return &LBMockMap{ 33 BackendByID: map[lb.BackendID]*lb.Backend{}, 34 ServiceByID: map[uint16]*lb.SVC{}, 35 AffinityMatch: datapathTypes.BackendIDByServiceIDSet{}, 36 SourceRanges: datapathTypes.SourceRangeSetByServiceID{}, 37 DummyMaglevTable: map[uint16]int{}, 38 SvcActiveBackendsCount: map[uint16]int{}, 39 SockRevNat4: map[lbmap.SockRevNat4Key]lbmap.SockRevNat4Value{}, 40 SockRevNat6: map[lbmap.SockRevNat6Key]lbmap.SockRevNat6Value{}, 41 } 42 } 43 44 func (m *LBMockMap) UpsertService(p *datapathTypes.UpsertServiceParams) error { 45 m.Lock() 46 defer m.Unlock() 47 48 backendIDs := p.GetOrderedBackends() 49 backendsList := make([]*lb.Backend, 0, len(backendIDs)) 50 for _, backendID := range backendIDs { 51 b, found := m.BackendByID[backendID] 52 if !found { 53 return fmt.Errorf("backend %d not found", p.ID) 54 } 55 backendsList = append(backendsList, b) 56 } 57 backends := p.ActiveBackends 58 if len(p.PreferredBackends) > 0 { 59 backends = p.PreferredBackends 60 } 61 if p.UseMaglev && len(backends) != 0 { 62 if err := m.upsertMaglevLookupTable(p.ID, backends, p.IPv6); err != nil { 63 return err 64 } 65 } 66 svc, found := m.ServiceByID[p.ID] 67 if !found { 68 frontend := lb.NewL3n4AddrID(lb.NONE, cmtypes.MustAddrClusterFromIP(p.IP), p.Port, p.Scope, lb.ID(p.ID)) 69 svc = &lb.SVC{Frontend: *frontend} 70 } else { 71 if p.PrevBackendsCount != len(svc.Backends) { 72 return fmt.Errorf("Invalid backends count: %d vs %d", p.PrevBackendsCount, len(svc.Backends)) 73 } 74 } 75 svc.Backends = backendsList 76 svc.SessionAffinity = p.SessionAffinity 77 svc.SessionAffinityTimeoutSec = p.SessionAffinityTimeoutSec 78 svc.Type = p.Type 79 svc.Name = p.Name 80 81 m.ServiceByID[p.ID] = svc 82 m.SvcActiveBackendsCount[p.ID] = len(p.ActiveBackends) 83 84 return nil 85 } 86 87 func (m *LBMockMap) upsertMaglevLookupTable(svcID uint16, backends map[string]*lb.Backend, ipv6 bool) error { 88 m.DummyMaglevTable[svcID] = len(backends) 89 return nil 90 } 91 92 func (m *LBMockMap) UpsertMaglevLookupTable(svcID uint16, backends map[string]*lb.Backend, ipv6 bool) error { 93 m.Lock() 94 defer m.Unlock() 95 return m.upsertMaglevLookupTable(svcID, backends, ipv6) 96 } 97 98 func (*LBMockMap) IsMaglevLookupTableRecreated(ipv6 bool) bool { 99 return true 100 } 101 102 func (m *LBMockMap) DeleteService(addr lb.L3n4AddrID, backendCount int, maglev bool, natPolicy lb.SVCNatPolicy) error { 103 m.Lock() 104 defer m.Unlock() 105 svc, found := m.ServiceByID[uint16(addr.ID)] 106 if !found { 107 return fmt.Errorf("Service not found %+v", addr) 108 } 109 if count := len(svc.Backends); count != backendCount { 110 return fmt.Errorf("Invalid backends count: %d vs %d", 111 count, backendCount) 112 } 113 114 delete(m.ServiceByID, uint16(addr.ID)) 115 116 return nil 117 } 118 119 func (m *LBMockMap) AddBackend(b *lb.Backend, ipv6 bool) error { 120 m.Lock() 121 defer m.Unlock() 122 id := b.ID 123 port := b.Port 124 125 // Backends can be added to both v4 and v6 lb maps (when nat64 policies 126 // are enabled). 127 if _, found := m.BackendByID[id]; found && !b.L3n4Addr.IsIPv6() && !ipv6 { 128 return fmt.Errorf("Backend %d already exists", id) 129 } 130 131 be := lb.NewBackendWithState(id, b.Protocol, b.AddrCluster, port, b.ZoneID, b.State) 132 m.BackendByID[id] = be 133 134 return nil 135 } 136 137 func (m *LBMockMap) UpdateBackendWithState(b *lb.Backend) error { 138 m.Lock() 139 defer m.Unlock() 140 id := b.ID 141 142 be, found := m.BackendByID[id] 143 if !found { 144 return fmt.Errorf("update failed : backend %d doesn't exist", id) 145 } 146 if b.ID != be.ID || b.Port != be.Port || !b.AddrCluster.Equal(be.AddrCluster) { 147 return fmt.Errorf("backend in the map %+v doesn't match %+v: only backend"+ 148 "state can be updated", be.String(), b.String()) 149 } 150 be.State = b.State 151 return nil 152 } 153 154 func (m *LBMockMap) DeleteBackendByID(id lb.BackendID) error { 155 m.Lock() 156 defer m.Unlock() 157 if _, found := m.BackendByID[id]; !found { 158 return fmt.Errorf("Backend %d does not exist", id) 159 } 160 161 delete(m.BackendByID, id) 162 163 return nil 164 } 165 166 func (m *LBMockMap) DumpServiceMaps() ([]*lb.SVC, []error) { 167 m.Lock() 168 defer m.Unlock() 169 list := make([]*lb.SVC, 0, len(m.ServiceByID)) 170 for _, svc := range m.ServiceByID { 171 list = append(list, svc) 172 } 173 return list, nil 174 } 175 176 func (m *LBMockMap) DumpBackendMaps() ([]*lb.Backend, error) { 177 m.Lock() 178 defer m.Unlock() 179 list := make([]*lb.Backend, 0, len(m.BackendByID)) 180 for _, backend := range m.BackendByID { 181 list = append(list, backend) 182 } 183 return list, nil 184 } 185 186 func (m *LBMockMap) AddAffinityMatch(revNATID uint16, backendID lb.BackendID) error { 187 m.Lock() 188 defer m.Unlock() 189 if _, ok := m.AffinityMatch[revNATID]; !ok { 190 m.AffinityMatch[revNATID] = map[lb.BackendID]struct{}{} 191 } 192 if _, ok := m.AffinityMatch[revNATID][backendID]; ok { 193 return fmt.Errorf("Backend %d already exists in %d affinity map", 194 backendID, revNATID) 195 } 196 m.AffinityMatch[revNATID][backendID] = struct{}{} 197 return nil 198 } 199 200 func (m *LBMockMap) DeleteAffinityMatch(revNATID uint16, backendID lb.BackendID) error { 201 m.Lock() 202 defer m.Unlock() 203 if _, ok := m.AffinityMatch[revNATID]; !ok { 204 return fmt.Errorf("Affinity map for %d does not exist", revNATID) 205 } 206 if _, ok := m.AffinityMatch[revNATID][backendID]; !ok { 207 return fmt.Errorf("Backend %d does not exist in %d affinity map", 208 backendID, revNATID) 209 } 210 delete(m.AffinityMatch[revNATID], backendID) 211 if len(m.AffinityMatch[revNATID]) == 0 { 212 delete(m.AffinityMatch, revNATID) 213 } 214 return nil 215 } 216 217 func (m *LBMockMap) DumpAffinityMatches() (datapathTypes.BackendIDByServiceIDSet, error) { 218 return m.AffinityMatch, nil 219 } 220 221 func (m *LBMockMap) UpdateSourceRanges(revNATID uint16, prevRanges []*cidr.CIDR, 222 ranges []*cidr.CIDR, ipv6 bool) error { 223 m.Lock() 224 defer m.Unlock() 225 226 if len(prevRanges) == 0 { 227 m.SourceRanges[revNATID] = []*cidr.CIDR{} 228 } 229 if len(prevRanges) != len(m.SourceRanges[revNATID]) { 230 return fmt.Errorf("Inconsistent view of source ranges") 231 } 232 srcRanges := []*cidr.CIDR{} 233 for _, cidr := range ranges { 234 if ip.IsIPv6(cidr.IP) == !ipv6 { 235 continue 236 } 237 srcRanges = append(srcRanges, cidr) 238 } 239 m.SourceRanges[revNATID] = srcRanges 240 241 return nil 242 } 243 244 func (m *LBMockMap) DumpSourceRanges(ipv6 bool) (datapathTypes.SourceRangeSetByServiceID, error) { 245 return m.SourceRanges, nil 246 } 247 248 func (m *LBMockMap) ExistsSockRevNat(cookie uint64, addr net.IP, port uint16) bool { 249 if addr.To4() != nil { 250 key := lbmap.NewSockRevNat4Key(cookie, addr, port) 251 if _, ok := m.SockRevNat4[*key]; ok { 252 return true 253 } 254 } else { 255 key := lbmap.NewSockRevNat6Key(cookie, addr, port) 256 if _, ok := m.SockRevNat6[*key]; ok { 257 return true 258 } 259 } 260 261 return false 262 }