go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/puntplugin/descriptor/punt_to_host.go (about) 1 // Copyright (c) 2018 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 package descriptor 16 17 import ( 18 "errors" 19 "strings" 20 21 "go.ligato.io/cn-infra/v2/logging" 22 "google.golang.org/protobuf/proto" 23 24 "go.ligato.io/vpp-agent/v3/pkg/models" 25 kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api" 26 "go.ligato.io/vpp-agent/v3/plugins/vpp/puntplugin/descriptor/adapter" 27 "go.ligato.io/vpp-agent/v3/plugins/vpp/puntplugin/vppcalls" 28 punt "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/punt" 29 ) 30 31 const ( 32 // PuntToHostDescriptorName is the name of the descriptor for the VPP punt to host/socket 33 PuntToHostDescriptorName = "vpp-punt-to-host" 34 ) 35 36 // A list of non-retriable errors: 37 var ( 38 // ErrPuntWithoutL3Protocol is returned when VPP punt has undefined L3 protocol. 39 ErrPuntWithoutL3Protocol = errors.New("VPP punt defined without L3 protocol") 40 41 // ErrPuntWithoutL4Protocol is returned when VPP punt has undefined L4 protocol. 42 ErrPuntWithoutL4Protocol = errors.New("VPP punt defined without L4 protocol") 43 44 // ErrPuntWithoutPort is returned when VPP punt has undefined port. 45 ErrPuntWithoutPort = errors.New("VPP punt defined without port") 46 47 // ErrPuntWithoutSocketPath is returned when VPP punt has undefined socket path. 48 ErrPuntWithoutSocketPath = errors.New("VPP punt defined without socket path") 49 ) 50 51 // PuntToHostDescriptor teaches KVScheduler how to configure VPP punt to host or unix domain socket. 52 type PuntToHostDescriptor struct { 53 RegisterSocketFn func(register bool, toHost *punt.ToHost, socketPath string) 54 55 // dependencies 56 log logging.Logger 57 puntHandler vppcalls.PuntVppAPI 58 } 59 60 // NewPuntToHostDescriptor creates a new instance of the punt to host descriptor. 61 func NewPuntToHostDescriptor(puntHandler vppcalls.PuntVppAPI, log logging.LoggerFactory) *PuntToHostDescriptor { 62 return &PuntToHostDescriptor{ 63 log: log.NewLogger("punt-to-host-descriptor"), 64 puntHandler: puntHandler, 65 } 66 } 67 68 // GetDescriptor returns descriptor suitable for registration (via adapter) with 69 // the KVScheduler. 70 func (d *PuntToHostDescriptor) GetDescriptor() *adapter.PuntToHostDescriptor { 71 return &adapter.PuntToHostDescriptor{ 72 Name: PuntToHostDescriptorName, 73 NBKeyPrefix: punt.ModelToHost.KeyPrefix(), 74 ValueTypeName: punt.ModelToHost.ProtoName(), 75 KeySelector: punt.ModelToHost.IsKeyValid, 76 KeyLabel: punt.ModelToHost.StripKeyPrefix, 77 ValueComparator: d.EquivalentPuntToHost, 78 Validate: d.Validate, 79 Create: d.Create, 80 Delete: d.Delete, 81 Retrieve: d.Retrieve, 82 } 83 } 84 85 // EquivalentPuntToHost is case-insensitive comparison function for punt.ToHost. 86 func (d *PuntToHostDescriptor) EquivalentPuntToHost(key string, oldPunt, newPunt *punt.ToHost) bool { 87 if oldPunt.L3Protocol != newPunt.L3Protocol || 88 oldPunt.L4Protocol != newPunt.L4Protocol || 89 oldPunt.Port != newPunt.Port { 90 return false 91 } 92 93 // if the socket path contains '!' as prefix we return false 94 // to force scheduler to recreate (register) punt socket 95 if strings.HasPrefix(oldPunt.SocketPath, "!") { 96 return false 97 } 98 99 return true 100 } 101 102 // Validate validates VPP punt configuration. 103 func (d *PuntToHostDescriptor) Validate(key string, puntCfg *punt.ToHost) error { 104 // validate L3 protocol 105 switch puntCfg.L3Protocol { 106 case punt.L3Protocol_IPV4: 107 case punt.L3Protocol_IPV6: 108 case punt.L3Protocol_ALL: 109 default: 110 return kvs.NewInvalidValueError(ErrPuntWithoutL3Protocol, "l3_protocol") 111 } 112 113 // validate L4 protocol 114 switch puntCfg.L4Protocol { 115 case punt.L4Protocol_TCP: 116 case punt.L4Protocol_UDP: 117 default: 118 return kvs.NewInvalidValueError(ErrPuntWithoutL4Protocol, "l4_protocol") 119 } 120 121 if puntCfg.Port == 0 { 122 return kvs.NewInvalidValueError(ErrPuntWithoutPort, "port") 123 } 124 125 // TODO: maybe this should also have dependency on socket file existing?? 126 if puntCfg.SocketPath == "" { 127 return kvs.NewInvalidValueError(ErrPuntWithoutSocketPath, "socket_path") 128 } 129 130 return nil 131 } 132 133 // Create adds new punt to host entry or registers new punt to unix domain socket. 134 func (d *PuntToHostDescriptor) Create(key string, punt *punt.ToHost) (interface{}, error) { 135 // register punt to socket 136 pathname, err := d.puntHandler.RegisterPuntSocket(punt) 137 if err != nil { 138 d.log.Error(err) 139 return nil, err 140 } 141 142 if d.RegisterSocketFn != nil { 143 d.RegisterSocketFn(true, punt, pathname) 144 } 145 146 return nil, nil 147 } 148 149 // Delete removes VPP punt configuration. 150 func (d *PuntToHostDescriptor) Delete(key string, p *punt.ToHost, metadata interface{}) error { 151 // check if the socketpath contains '!' as prefix from retrieve 152 punt := proto.Clone(p).(*punt.ToHost) 153 punt.SocketPath = strings.TrimPrefix(punt.SocketPath, "!") 154 155 // deregister punt to socket 156 if err := d.puntHandler.DeregisterPuntSocket(punt); err != nil { 157 d.log.Error(err) 158 return err 159 } 160 161 if d.RegisterSocketFn != nil { 162 d.RegisterSocketFn(false, punt, "") 163 } 164 165 return nil 166 } 167 168 // Retrieve returns all configured VPP punt to host entries. 169 func (d *PuntToHostDescriptor) Retrieve(correlate []adapter.PuntToHostKVWithMetadata) (retrieved []adapter.PuntToHostKVWithMetadata, err error) { 170 // Dump registered punt sockets 171 socks, err := d.puntHandler.DumpRegisteredPuntSockets() 172 if err != nil { 173 return nil, err 174 } 175 176 // for all dumped punts that were not yet registered and for which 177 // the VPP socket is unknown we prepend '!' as prefix 178 // to allow descriptor to recognize this in equivalent 179 // and force recreation or make it possible to delete it 180 for _, s := range socks { 181 if s.PuntData.SocketPath == "" && s.SocketPath != "" { 182 s.PuntData.SocketPath = "!" + s.SocketPath 183 } 184 } 185 186 // 1. Find NB equivalent of the punt entry with L3 set to 'ALL'. If found, cache 187 // the VPP entry. If not found, add to retrieved values. 188 var cachedIpv4, cachedIpv6 []*vppcalls.PuntDetails 189 Retrieved: 190 for _, fromVPP := range socks { 191 for _, fromNB := range correlate { 192 if fromNB.Value.L3Protocol != punt.L3Protocol_ALL { 193 continue 194 } 195 if fromVPP.PuntData.Port == fromNB.Value.Port && 196 fromVPP.PuntData.L4Protocol == fromNB.Value.L4Protocol { 197 if fromVPP.PuntData.L3Protocol == punt.L3Protocol_IPV4 { 198 cachedIpv4 = append(cachedIpv4, fromVPP) 199 } 200 if fromVPP.PuntData.L3Protocol == punt.L3Protocol_IPV6 { 201 cachedIpv6 = append(cachedIpv6, fromVPP) 202 } 203 continue Retrieved 204 } 205 } 206 retrieved = append(retrieved, adapter.PuntToHostKVWithMetadata{ 207 Key: models.Key(fromVPP.PuntData), 208 Value: fromVPP.PuntData, 209 Origin: kvs.FromNB, 210 }) 211 } 212 213 // 2. Find pairs of the same config. 214 // 215 // Note: only if both, IPv4 and IPv6 exists the entry is added. Cached IPv4 216 // without IPv6 (and all remaining IPv6) are ignored, causing agent to configure 217 // the missing one and re-configure the existing one. 218 for _, cachedIPv4Punt := range cachedIpv4 { 219 // look for IPv6 counterpart 220 var found bool 221 for _, cachedIPv6Punt := range cachedIpv6 { 222 if cachedIPv4Punt.PuntData.L4Protocol == cachedIPv6Punt.PuntData.L4Protocol && 223 cachedIPv4Punt.PuntData.Port == cachedIPv6Punt.PuntData.Port { 224 found = true 225 } 226 } 227 // Store as 'ALL entry' 228 if found { 229 cachedIPv4Punt.PuntData.L3Protocol = punt.L3Protocol_ALL 230 retrieved = append(retrieved, adapter.PuntToHostKVWithMetadata{ 231 Key: models.Key(cachedIPv4Punt.PuntData), 232 Value: cachedIPv4Punt.PuntData, 233 Origin: kvs.FromNB, 234 }) 235 } 236 } 237 238 return retrieved, nil 239 }