go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/puntplugin/vppcalls/vpp2202/punt_vppcalls.go (about) 1 // Copyright (c) 2022 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 vpp2202 16 17 import ( 18 "fmt" 19 "strings" 20 21 "github.com/pkg/errors" 22 23 "go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2202/interface_types" 24 vpp_ip "go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2202/ip" 25 "go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2202/ip_types" 26 vpp_punt "go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2202/punt" 27 punt "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/punt" 28 ) 29 30 const PuntSocketHeaderVersion = 1 31 32 // Socket path from the VPP startup config file, returned when a punt socket 33 // is retrieved. Limited to single entry as supported in the VPP. 34 var vppConfigSocketPath string 35 36 // AddPunt configures new punt entry 37 func (h *PuntVppHandler) AddPunt(p *punt.ToHost) error { 38 return errors.Errorf("passive punt add is currently not available") 39 } 40 41 // DeletePunt removes punt entry 42 func (h *PuntVppHandler) DeletePunt(p *punt.ToHost) error { 43 return errors.Errorf("passive punt del is currently not available") 44 } 45 46 // AddPuntException adds new punt exception entry 47 func (h *PuntVppHandler) AddPuntException(p *punt.Exception) (string, error) { 48 return h.addDelPuntException(p, true) 49 } 50 51 // DeletePuntException removes punt exception entry 52 func (h *PuntVppHandler) DeletePuntException(p *punt.Exception) error { 53 _, err := h.addDelPuntException(p, false) 54 return err 55 } 56 57 func (h *PuntVppHandler) addDelPuntException(p *punt.Exception, isAdd bool) (pathName string, err error) { 58 reasons, err := h.dumpPuntReasons() 59 if err != nil { 60 return "", fmt.Errorf("dumping punt reasons failed: %v", err) 61 } 62 63 h.log.Debugf("dumped %d punt reasons: %+v", len(reasons), reasons) 64 65 var reasonID *uint32 66 for _, r := range reasons { 67 if r.Reason.Name == p.Reason { 68 id := r.ID 69 reasonID = &id 70 break 71 } 72 } 73 if reasonID == nil { 74 return "", fmt.Errorf("punt reason %q not found", p.Reason) 75 } 76 77 baPunt := getPuntExceptionConfig(*reasonID) 78 79 if isAdd { 80 h.log.Debugf("adding punt exception: %+v", p) 81 pathName, err = h.handleRegisterPuntSocket(baPunt, p.SocketPath) 82 if err != nil { 83 return "", err 84 } 85 } else { 86 err = h.handleDeregisterPuntSocket(baPunt) 87 if err != nil { 88 return "", err 89 } 90 } 91 92 return pathName, nil 93 } 94 95 // RegisterPuntSocket registers new punt to unix domain socket entry 96 func (h *PuntVppHandler) RegisterPuntSocket(p *punt.ToHost) (pathName string, err error) { 97 ipProto := resolveL4Proto(p.L4Protocol) 98 99 if p.L3Protocol == punt.L3Protocol_IPV4 || p.L3Protocol == punt.L3Protocol_ALL { 100 baPunt := getPuntL4Config(ip_types.ADDRESS_IP4, ipProto, uint16(p.Port)) 101 if pathName, err = h.handleRegisterPuntSocket(baPunt, p.SocketPath); err != nil { 102 return "", err 103 } 104 } 105 if p.L3Protocol == punt.L3Protocol_IPV6 || p.L3Protocol == punt.L3Protocol_ALL { 106 baPunt := getPuntL4Config(ip_types.ADDRESS_IP6, ipProto, uint16(p.Port)) 107 if pathName, err = h.handleRegisterPuntSocket(baPunt, p.SocketPath); err != nil { 108 return "", err 109 } 110 } 111 112 return pathName, nil 113 } 114 115 // DeregisterPuntSocket removes existing punt to socket registration 116 func (h *PuntVppHandler) DeregisterPuntSocket(p *punt.ToHost) error { 117 ipProto := resolveL4Proto(p.L4Protocol) 118 119 if p.L3Protocol == punt.L3Protocol_IPV4 || p.L3Protocol == punt.L3Protocol_ALL { 120 baPunt := getPuntL4Config(ip_types.ADDRESS_IP4, ipProto, uint16(p.Port)) 121 if err := h.handleDeregisterPuntSocket(baPunt); err != nil { 122 return err 123 } 124 } 125 if p.L3Protocol == punt.L3Protocol_IPV6 || p.L3Protocol == punt.L3Protocol_ALL { 126 baPunt := getPuntL4Config(ip_types.ADDRESS_IP6, ipProto, uint16(p.Port)) 127 if err := h.handleDeregisterPuntSocket(baPunt); err != nil { 128 return err 129 } 130 } 131 132 return nil 133 } 134 135 func (h *PuntVppHandler) handleRegisterPuntSocket(punt vpp_punt.Punt, path string) (string, error) { 136 req := &vpp_punt.PuntSocketRegister{ 137 HeaderVersion: PuntSocketHeaderVersion, 138 Punt: punt, 139 Pathname: path, 140 } 141 reply := &vpp_punt.PuntSocketRegisterReply{} 142 143 h.log.Debugf("registering punt socket: %+v (pathname: %s)", req.Punt, req.Pathname) 144 if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { 145 return "", err 146 } 147 148 // socket pathname from VPP config 149 pathName := strings.SplitN(string(reply.Pathname), "\x00", 2)[0] 150 151 // VPP startup config socket path name is always the same 152 if vppConfigSocketPath != pathName { 153 h.log.Debugf("setting vpp punt socket path to: %q (%s)", pathName, vppConfigSocketPath) 154 vppConfigSocketPath = pathName 155 } 156 157 return pathName, nil 158 } 159 160 // DeregisterPuntSocket removes existing punt to socket registration 161 func (h *PuntVppHandler) handleDeregisterPuntSocket(punt vpp_punt.Punt) error { 162 req := &vpp_punt.PuntSocketDeregister{ 163 Punt: punt, 164 } 165 reply := &vpp_punt.PuntSocketDeregisterReply{} 166 167 if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { 168 return err 169 } 170 171 return nil 172 } 173 174 func getPuntExceptionConfig(reasonID uint32) vpp_punt.Punt { 175 p := vpp_punt.PuntException{ 176 ID: reasonID, 177 } 178 return vpp_punt.Punt{ 179 Type: vpp_punt.PUNT_API_TYPE_EXCEPTION, 180 Punt: vpp_punt.PuntUnionException(p), 181 } 182 } 183 184 func getPuntL4Config(ipv ip_types.AddressFamily, ipProto ip_types.IPProto, port uint16) vpp_punt.Punt { 185 puntL4 := vpp_punt.PuntL4{ 186 Af: ipv, 187 Protocol: ipProto, 188 Port: port, 189 } 190 return vpp_punt.Punt{ 191 Type: vpp_punt.PUNT_API_TYPE_L4, 192 Punt: vpp_punt.PuntUnionL4(puntL4), 193 } 194 } 195 196 // AddPuntRedirect adds new redirect entry 197 func (h *PuntVppHandler) AddPuntRedirect(puntCfg *punt.IPRedirect) error { 198 if puntCfg.L3Protocol == punt.L3Protocol_IPV4 || puntCfg.L3Protocol == punt.L3Protocol_ALL { 199 if err := h.handlePuntRedirectIPv4(puntCfg, true); err != nil { 200 return err 201 } 202 } 203 if puntCfg.L3Protocol == punt.L3Protocol_IPV6 || puntCfg.L3Protocol == punt.L3Protocol_ALL { 204 if err := h.handlePuntRedirectIPv6(puntCfg, true); err != nil { 205 return err 206 } 207 } 208 return nil 209 } 210 211 // DeletePuntRedirect removes existing redirect entry 212 func (h *PuntVppHandler) DeletePuntRedirect(puntCfg *punt.IPRedirect) error { 213 if puntCfg.L3Protocol == punt.L3Protocol_IPV4 || puntCfg.L3Protocol == punt.L3Protocol_ALL { 214 if err := h.handlePuntRedirectIPv4(puntCfg, false); err != nil { 215 return err 216 } 217 } 218 if puntCfg.L3Protocol == punt.L3Protocol_IPV6 || puntCfg.L3Protocol == punt.L3Protocol_ALL { 219 if err := h.handlePuntRedirectIPv6(puntCfg, false); err != nil { 220 return err 221 } 222 } 223 return nil 224 } 225 226 func (h *PuntVppHandler) handlePuntRedirectIPv4(punt *punt.IPRedirect, isAdd bool) error { 227 return h.handlePuntRedirect(punt, true, isAdd) 228 } 229 230 func (h *PuntVppHandler) handlePuntRedirectIPv6(punt *punt.IPRedirect, isAdd bool) error { 231 return h.handlePuntRedirect(punt, false, isAdd) 232 } 233 234 func (h *PuntVppHandler) handlePuntRedirect(punt *punt.IPRedirect, isIPv4, isAdd bool) error { 235 // rx interface 236 var rxIfIdx uint32 237 if punt.RxInterface == "" { 238 rxIfIdx = ^uint32(0) 239 } else { 240 rxMetadata, exists := h.ifIndexes.LookupByName(punt.RxInterface) 241 if !exists { 242 return errors.Errorf("index not found for interface %s", punt.RxInterface) 243 } 244 rxIfIdx = rxMetadata.SwIfIndex 245 } 246 247 // tx interface 248 txMetadata, exists := h.ifIndexes.LookupByName(punt.TxInterface) 249 if !exists { 250 return errors.Errorf("index not found for interface %s", punt.TxInterface) 251 } 252 253 // next hop address 254 // - remove mask from IP address if necessary 255 nextHopStr := punt.NextHop 256 ipParts := strings.Split(punt.NextHop, "/") 257 if len(ipParts) > 1 { 258 h.log.Debugf("IP punt redirect next hop IP address %s is defined with mask, removing it") 259 nextHopStr = ipParts[0] 260 } 261 nextHop, err := ipToAddress(nextHopStr) 262 if err != nil { 263 return err 264 } 265 266 req := &vpp_ip.IPPuntRedirect{ 267 IsAdd: isAdd, 268 Punt: vpp_ip.PuntRedirect{ 269 RxSwIfIndex: interface_types.InterfaceIndex(rxIfIdx), 270 TxSwIfIndex: interface_types.InterfaceIndex(txMetadata.SwIfIndex), 271 Nh: nextHop, 272 }, 273 } 274 reply := &vpp_ip.IPPuntRedirectReply{} 275 if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { 276 return err 277 } 278 279 return nil 280 } 281 282 func resolveL4Proto(protocol punt.L4Protocol) ip_types.IPProto { 283 if protocol == punt.L4Protocol_UDP { 284 return ip_types.IP_API_PROTO_UDP 285 } 286 return ip_types.IP_API_PROTO_TCP 287 }