go.ligato.io/vpp-agent/v3@v3.5.0/plugins/netalloc/netalloc_plugin.go (about) 1 // Copyright (c) 2019 Cisco and/or its affiliates. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at: 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //go:generate descriptor-adapter --descriptor-name IPAlloc --value-type *netalloc.IPAllocation --meta-type *netalloc.IPAllocMetadata --import "go.ligato.io/vpp-agent/v3/proto/ligato/netalloc" --output-dir "descriptor" 16 17 package netalloc 18 19 import ( 20 "bytes" 21 "errors" 22 "fmt" 23 "net" 24 25 "go.ligato.io/cn-infra/v2/infra" 26 27 "go.ligato.io/cn-infra/v2/idxmap" 28 29 "go.ligato.io/vpp-agent/v3/pkg/models" 30 kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api" 31 "go.ligato.io/vpp-agent/v3/plugins/netalloc/descriptor" 32 "go.ligato.io/vpp-agent/v3/plugins/netalloc/utils" 33 "go.ligato.io/vpp-agent/v3/proto/ligato/netalloc" 34 ) 35 36 // Plugin implements network allocation features. 37 // For more information, please refer to the netalloc proto model: proto/ligato/netalloc/netalloc.proto 38 type Plugin struct { 39 Deps 40 41 // IP address allocation 42 ipAllocDescriptor *kvs.KVDescriptor 43 ipIndex idxmap.NamedMapping 44 } 45 46 // Deps lists dependencies of the netalloc plugin. 47 type Deps struct { 48 infra.PluginDeps 49 KVScheduler kvs.KVScheduler 50 } 51 52 // Init initializes netalloc descriptors. 53 func (p *Plugin) Init() error { 54 // init & register descriptors 55 p.ipAllocDescriptor = descriptor.NewAddrAllocDescriptor(p.Log) 56 err := p.Deps.KVScheduler.RegisterKVDescriptor(p.ipAllocDescriptor) 57 if err != nil { 58 return err 59 } 60 61 // obtain map with metadata of allocated addresses 62 p.ipIndex = p.KVScheduler.GetMetadataMap(descriptor.IPAllocDescriptorName) 63 if p.ipIndex == nil { 64 return errors.New("missing index with metadata of allocated addresses") 65 } 66 return nil 67 } 68 69 // Close does nothing. 70 func (p *Plugin) Close() error { 71 return nil 72 } 73 74 // CreateAddressAllocRef creates reference to an allocated IP address. 75 func (p *Plugin) CreateAddressAllocRef(network, iface string, getGW bool) string { 76 ref := netalloc.AllocRefPrefix + network 77 if iface != "" { 78 ref += "/" + iface 79 } 80 if getGW { 81 ref += netalloc.AllocRefGWSuffix 82 } 83 return ref 84 } 85 86 // ParseAddressAllocRef parses reference to an allocated IP address. 87 func (p *Plugin) ParseAddressAllocRef(addrAllocRef, expIface string) ( 88 network, iface string, isGW, isRef bool, err error) { 89 return utils.ParseAddrAllocRef(addrAllocRef, expIface) 90 } 91 92 // GetAddressAllocDep reads what can be potentially a reference to an allocated 93 // IP address. If <allocRef> is indeed a reference, the function returns 94 // the corresponding dependency to be passed further into KVScheduler 95 // from the descriptor. Otherwise <hasAllocDep> is returned as false, and 96 // <allocRef> should be an actual address and not a reference. 97 func (p *Plugin) GetAddressAllocDep(addrOrAllocRef, ifaceName, depLabelPrefix string) ( 98 dep kvs.Dependency, hasAllocDep bool) { 99 100 network, iface, _, isRef, err := utils.ParseAddrAllocRef(addrOrAllocRef, ifaceName) 101 if !isRef || err != nil { 102 return kvs.Dependency{}, false 103 } 104 105 return kvs.Dependency{ 106 Label: depLabelPrefix + addrOrAllocRef, 107 Key: models.Key(&netalloc.IPAllocation{ 108 NetworkName: network, 109 InterfaceName: iface, 110 }), 111 }, true 112 } 113 114 // ValidateIPAddress checks validity of address reference or, if <addrOrAllocRef> 115 // already contains an actual IP address, it tries to parse it. 116 func (p *Plugin) ValidateIPAddress(addrOrAllocRef, ifaceName, fieldName string, gwCheck GwValidityCheck) error { 117 _, _, isGW, isRef, err := utils.ParseAddrAllocRef(addrOrAllocRef, ifaceName) 118 if !isRef { 119 _, _, err = utils.ParseIPAddr(addrOrAllocRef, nil) 120 } else if err == nil { 121 switch gwCheck { 122 case GWRefRequired: 123 if !isGW { 124 err = errors.New("expected GW address reference") 125 } 126 case GwRefUnexpected: 127 if isGW { 128 err = errors.New("expected non-GW address reference") 129 } 130 } 131 } 132 if err != nil { 133 if fieldName != "" { 134 return kvs.NewInvalidValueError(err, fieldName) 135 } else { 136 return kvs.NewInvalidValueError(err) 137 } 138 } 139 return nil 140 } 141 142 // GetOrParseIPAddress tries to get allocated interface (or GW) IP address 143 // referenced by <addrOrAllocRef> in the requested form. But if the string 144 // contains/ an actual IP address instead of a reference, the address is parsed 145 // using methods from the net package and returned in the requested form. 146 // For ADDR_ONLY address form, the returned <addr> will have the mask unset 147 // and the IP address should be accessed as <addr>.IP 148 func (p *Plugin) GetOrParseIPAddress(addrOrAllocRef string, ifaceName string, 149 addrForm netalloc.IPAddressForm) (addr *net.IPNet, err error) { 150 151 network, iface, getGW, isRef, err := utils.ParseAddrAllocRef(addrOrAllocRef, ifaceName) 152 if isRef && err != nil { 153 return nil, err 154 } 155 156 if isRef { 157 // reference to allocated IP address 158 allocName := models.Name(&netalloc.IPAllocation{ 159 NetworkName: network, 160 InterfaceName: iface, 161 }) 162 allocVal, found := p.ipIndex.GetValue(allocName) 163 if !found { 164 return nil, fmt.Errorf("failed to find metadata for IP address allocation '%s'", 165 allocName) 166 } 167 allocMeta, ok := allocVal.(*netalloc.IPAllocMetadata) 168 if !ok { 169 return nil, fmt.Errorf("invalid type of metadata stored for IP address allocation '%s'", 170 allocName) 171 } 172 if getGW { 173 if allocMeta.GwAddr == nil { 174 return nil, fmt.Errorf("gw address is not defined for IP address allocation '%s'", 175 allocName) 176 } 177 return utils.GetIPAddrInGivenForm(allocMeta.GwAddr, addrForm), nil 178 } 179 return utils.GetIPAddrInGivenForm(allocMeta.IfaceAddr, addrForm), nil 180 } 181 182 // not a reference - try to parse the address 183 ipAddr, _, err := utils.ParseIPAddr(addrOrAllocRef, nil) 184 if err != nil { 185 return nil, err 186 } 187 return utils.GetIPAddrInGivenForm(ipAddr, addrForm), nil 188 } 189 190 // CorrelateRetrievedIPs should be used in Retrieve to correlate one or group 191 // of (model-wise indistinguishable) retrieved interface or GW IP addresses 192 // with the expected configuration. The method will replace retrieved addresses 193 // with the corresponding allocation references from the expected configuration 194 // if there are any. 195 // The method returns one IP address or address-allocation reference for every 196 // address from <retrievedAddrs>. 197 func (p *Plugin) CorrelateRetrievedIPs(expAddrsOrRefs []string, retrievedAddrs []string, 198 ifaceName string, addrForm netalloc.IPAddressForm) (correlated []string) { 199 200 expParsed := make([]*net.IPNet, len(expAddrsOrRefs)) 201 for i, addr := range expAddrsOrRefs { 202 expParsed[i], _ = p.GetOrParseIPAddress(addr, ifaceName, addrForm) 203 } 204 205 for _, addr := range retrievedAddrs { 206 ipAddr, _, err := utils.ParseIPAddr(addr, nil) 207 if err != nil { 208 // invalid - do not try to correlate, just return as is 209 correlated = append(correlated, addr) 210 continue 211 } 212 var addrCorrelated bool 213 for i, expAddr := range expParsed { 214 if expAddr == nil { 215 continue 216 } 217 if bytes.Equal(ipAddr.IP, expAddr.IP) { 218 if addrForm == netalloc.IPAddressForm_ADDR_ONLY || 219 bytes.Equal(ipAddr.Mask, expAddr.Mask) { 220 // found match in the expected configuration 221 correlated = append(correlated, expAddrsOrRefs[i]) 222 addrCorrelated = true 223 break 224 } 225 } 226 } 227 if !addrCorrelated { 228 // couldn't find match in the expected configuration, just return as is 229 correlated = append(correlated, addr) 230 } 231 } 232 return correlated 233 }