go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/natplugin/vppcalls/vpp2210/nat_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 vpp2210 16 17 import ( 18 "fmt" 19 "net" 20 21 "go.fd.io/govpp/api" 22 23 "github.com/pkg/errors" 24 25 "go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2210/interface_types" 26 "go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2210/ip_types" 27 vpp_nat_ed "go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2210/nat44_ed" 28 vpp_nat_ei "go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2210/nat44_ei" 29 "go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2210/nat_types" 30 "go.ligato.io/vpp-agent/v3/plugins/vpp/natplugin/vppcalls" 31 nat "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/nat" 32 ) 33 34 // Num protocol representation 35 const ( 36 ICMP uint8 = 1 37 TCP uint8 = 6 38 UDP uint8 = 17 39 ) 40 41 const ( 42 // NoInterface is sw-if-idx which means 'no interface' 43 NoInterface = interface_types.InterfaceIndex(^uint32(0)) 44 // Maximal length of tag 45 maxTagLen = 64 46 ) 47 48 // holds a list of NAT44 ED flags set 49 type nat44EdFlags struct { 50 isTwiceNat bool 51 isSelfTwiceNat bool 52 isOut2In bool 53 isAddrOnly bool 54 isOutside bool 55 isInside bool 56 isStatic bool 57 isExtHostValid bool 58 } 59 60 // holds a list of NAT44 EI flags set 61 type nat44EiFlags struct { 62 eiStaticMappingOnly bool 63 eiConnectionTracking bool 64 eiOut2InDpo bool 65 eiAddrOnlyMapping bool 66 eiIfInside bool 67 eiIfOutside bool 68 eiStaticMapping bool 69 } 70 71 func (h *NatVppHandler) enableNAT44EdPlugin(opts vppcalls.Nat44InitOpts) error { 72 var flags vpp_nat_ed.Nat44ConfigFlags 73 if opts.ConnectionTracking { 74 flags |= vpp_nat_ed.NAT44_IS_CONNECTION_TRACKING 75 } 76 if opts.StaticMappingOnly { 77 flags |= vpp_nat_ed.NAT44_IS_STATIC_MAPPING_ONLY 78 } 79 80 req := &vpp_nat_ed.Nat44EdPluginEnableDisable{ 81 Enable: true, 82 Flags: flags, 83 } 84 reply := &vpp_nat_ed.Nat44EdPluginEnableDisableReply{} 85 86 if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { 87 return err 88 } 89 return nil 90 } 91 92 func (h *NatVppHandler) enableNAT44EiPlugin(opts vppcalls.Nat44InitOpts) error { 93 var flags vpp_nat_ei.Nat44EiConfigFlags 94 if opts.ConnectionTracking { 95 flags |= vpp_nat_ei.NAT44_EI_CONNECTION_TRACKING 96 } 97 if opts.StaticMappingOnly { 98 flags |= vpp_nat_ei.NAT44_EI_STATIC_MAPPING_ONLY 99 } 100 if opts.OutToInDPO { 101 flags |= vpp_nat_ei.NAT44_EI_OUT2IN_DPO 102 } 103 104 req := &vpp_nat_ei.Nat44EiPluginEnableDisable{ 105 Enable: true, 106 Flags: flags, 107 } 108 reply := &vpp_nat_ei.Nat44EiPluginEnableDisableReply{} 109 110 if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { 111 return err 112 } 113 return nil 114 } 115 116 // EnableNAT44plugin and apply the given set of options. 117 func (h *NatVppHandler) EnableNAT44Plugin(opts vppcalls.Nat44InitOpts) error { 118 // VPP since version 21.06 supports running both NAT EI and NAT ED simultaneously, 119 // but not vpp-agent yet. 120 // TODO: separate vpp-agent natplugin into 2 separate plugins 121 // (for ED and EI NAT) or create a separate handlers inside one plugin, 122 // this have number of advantages and will probably also become necessary as VPP 123 // NAT plugins will differ more and more over time. 124 if opts.EndpointDependent { 125 h.ed = true 126 return h.enableNAT44EdPlugin(opts) 127 } else { 128 h.ed = false 129 return h.enableNAT44EiPlugin(opts) 130 } 131 } 132 133 func (h *NatVppHandler) disableNAT44EdPlugin() error { 134 req := &vpp_nat_ed.Nat44EdPluginEnableDisable{ 135 Enable: false, 136 } 137 reply := &vpp_nat_ed.Nat44EdPluginEnableDisableReply{} 138 err := h.callsChannel.SendRequest(req).ReceiveReply(reply) 139 if err == api.VPPApiError(1) { 140 return nil 141 } else if err != nil { 142 return err 143 } 144 return nil 145 } 146 147 func (h *NatVppHandler) disableNAT44EiPlugin() error { 148 req := &vpp_nat_ei.Nat44EiPluginEnableDisable{ 149 Enable: false, 150 } 151 reply := &vpp_nat_ei.Nat44EiPluginEnableDisableReply{} 152 err := h.callsChannel.SendRequest(req).ReceiveReply(reply) 153 if err == api.VPPApiError(1) { 154 return nil 155 } else if err != nil { 156 return err 157 } 158 return nil 159 } 160 161 // DisableNAT44Plugin disables NAT44 plugin. 162 func (h *NatVppHandler) DisableNAT44Plugin() error { 163 if h.ed { 164 return h.disableNAT44EdPlugin() 165 } else { 166 return h.disableNAT44EiPlugin() 167 } 168 } 169 170 func (h *NatVppHandler) setNat44EdForwarding(enableFwd bool) error { 171 req := &vpp_nat_ed.Nat44ForwardingEnableDisable{ 172 Enable: enableFwd, 173 } 174 reply := &vpp_nat_ed.Nat44ForwardingEnableDisableReply{} 175 176 if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { 177 return err 178 } 179 180 return nil 181 } 182 183 func (h *NatVppHandler) setNat44EiForwarding(enableFwd bool) error { 184 req := &vpp_nat_ei.Nat44EiForwardingEnableDisable{ 185 Enable: enableFwd, 186 } 187 reply := &vpp_nat_ei.Nat44EiForwardingEnableDisableReply{} 188 189 if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { 190 return err 191 } 192 193 return nil 194 } 195 196 // SetNat44Forwarding configures NAT44 forwarding. 197 func (h *NatVppHandler) SetNat44Forwarding(enableFwd bool) error { 198 if h.ed { 199 return h.setNat44EdForwarding(enableFwd) 200 } else { 201 return h.setNat44EiForwarding(enableFwd) 202 } 203 } 204 205 // EnableNat44Interface enables NAT44 feature for provided interface. 206 func (h *NatVppHandler) EnableNat44Interface(iface string, isInside, isOutput bool) error { 207 if isOutput { 208 return h.handleNat44InterfaceOutputFeature(iface, isInside, true) 209 } 210 return h.handleNat44Interface(iface, isInside, true) 211 } 212 213 // DisableNat44Interface disables NAT44 feature for provided interface. 214 func (h *NatVppHandler) DisableNat44Interface(iface string, isInside, isOutput bool) error { 215 if isOutput { 216 return h.handleNat44InterfaceOutputFeature(iface, isInside, false) 217 } 218 return h.handleNat44Interface(iface, isInside, false) 219 } 220 221 // AddNat44AddressPool adds new IPV4 address pool into the NAT pools. 222 func (h *NatVppHandler) AddNat44AddressPool(vrf uint32, firstIP, lastIP string, twiceNat bool) error { 223 return h.handleNat44AddressPool(vrf, firstIP, lastIP, twiceNat, true) 224 } 225 226 // DelNat44AddressPool removes existing IPv4 address pool from the NAT pools. 227 func (h *NatVppHandler) DelNat44AddressPool(vrf uint32, firstIP, lastIP string, twiceNat bool) error { 228 return h.handleNat44AddressPool(vrf, firstIP, lastIP, twiceNat, false) 229 } 230 231 // SetVirtualReassemblyIPv4 configures NAT virtual reassembly for IPv4 packets. 232 func (h *NatVppHandler) SetVirtualReassemblyIPv4(vrCfg *nat.VirtualReassembly) error { 233 return h.handleNatVirtualReassembly(vrCfg, false) 234 } 235 236 // SetVirtualReassemblyIPv6 configures NAT virtual reassembly for IPv6 packets. 237 func (h *NatVppHandler) SetVirtualReassemblyIPv6(vrCfg *nat.VirtualReassembly) error { 238 return h.handleNatVirtualReassembly(vrCfg, true) 239 } 240 241 // AddNat44IdentityMapping adds new NAT44 identity mapping 242 func (h *NatVppHandler) AddNat44IdentityMapping(mapping *nat.DNat44_IdentityMapping, dnatLabel string) error { 243 return h.handleNat44IdentityMapping(mapping, dnatLabel, true) 244 } 245 246 // DelNat44IdentityMapping removes existing NAT44 identity mapping 247 func (h *NatVppHandler) DelNat44IdentityMapping(mapping *nat.DNat44_IdentityMapping, dnatLabel string) error { 248 return h.handleNat44IdentityMapping(mapping, dnatLabel, false) 249 } 250 251 // AddNat44StaticMapping creates new NAT44 static mapping entry. 252 func (h *NatVppHandler) AddNat44StaticMapping(mapping *nat.DNat44_StaticMapping, dnatLabel string) error { 253 if len(mapping.LocalIps) == 0 { 254 return errors.Errorf("cannot configure static mapping for DNAT %s: no local address provided", dnatLabel) 255 } 256 if len(mapping.LocalIps) == 1 { 257 return h.handleNat44StaticMapping(mapping, dnatLabel, true) 258 } 259 return h.handleNat44StaticMappingLb(mapping, dnatLabel, true) 260 } 261 262 // DelNat44StaticMapping removes existing NAT44 static mapping entry. 263 func (h *NatVppHandler) DelNat44StaticMapping(mapping *nat.DNat44_StaticMapping, dnatLabel string) error { 264 if len(mapping.LocalIps) == 0 { 265 return errors.Errorf("cannot un-configure static mapping from DNAT %s: no local address provided", dnatLabel) 266 } 267 if len(mapping.LocalIps) == 1 { 268 return h.handleNat44StaticMapping(mapping, dnatLabel, false) 269 } 270 return h.handleNat44StaticMappingLb(mapping, dnatLabel, false) 271 } 272 273 func (h *NatVppHandler) handleNatEd44Interface(iface string, isInside, isAdd bool) error { 274 // get interface metadata 275 ifaceMeta, found := h.ifIndexes.LookupByName(iface) 276 if !found { 277 return errors.New("failed to get interface metadata") 278 } 279 280 req := &vpp_nat_ed.Nat44InterfaceAddDelFeature{ 281 SwIfIndex: interface_types.InterfaceIndex(ifaceMeta.SwIfIndex), 282 Flags: setNat44EdFlags(&nat44EdFlags{isInside: isInside}), 283 IsAdd: isAdd, 284 } 285 reply := &vpp_nat_ed.Nat44InterfaceAddDelFeatureReply{} 286 287 if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { 288 return err 289 } 290 291 return nil 292 } 293 294 func (h *NatVppHandler) handleNat44EiInterface(iface string, isInside, isAdd bool) error { 295 // get interface metadata 296 ifaceMeta, found := h.ifIndexes.LookupByName(iface) 297 if !found { 298 return errors.New("failed to get interface metadata") 299 } 300 301 req := &vpp_nat_ei.Nat44EiInterfaceAddDelFeature{ 302 SwIfIndex: interface_types.InterfaceIndex(ifaceMeta.SwIfIndex), 303 Flags: setNat44EiFlags(&nat44EiFlags{eiIfInside: isInside}), 304 IsAdd: isAdd, 305 } 306 reply := &vpp_nat_ei.Nat44EiInterfaceAddDelFeatureReply{} 307 308 if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { 309 return err 310 } 311 312 return nil 313 } 314 315 // Calls VPP binary API to set/unset interface NAT44 feature. 316 func (h *NatVppHandler) handleNat44Interface(iface string, isInside, isAdd bool) error { 317 if h.ed { 318 return h.handleNatEd44Interface(iface, isInside, isAdd) 319 } else { 320 return h.handleNat44EiInterface(iface, isInside, isAdd) 321 } 322 } 323 324 func (h *NatVppHandler) handleNat44EdInterfaceOutputFeature(iface string, isInside, isAdd bool) error { 325 // get interface metadata 326 ifaceMeta, found := h.ifIndexes.LookupByName(iface) 327 if !found { 328 return errors.New("failed to get interface metadata") 329 } 330 331 req := &vpp_nat_ed.Nat44EdAddDelOutputInterface{ 332 SwIfIndex: interface_types.InterfaceIndex(ifaceMeta.SwIfIndex), 333 IsAdd: isAdd, 334 } 335 reply := &vpp_nat_ed.Nat44EdAddDelOutputInterfaceReply{} 336 337 if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { 338 return err 339 } 340 341 return nil 342 } 343 344 func (h *NatVppHandler) handleNat44EiInterfaceOutputFeature(iface string, isInside, isAdd bool) error { 345 // get interface metadata 346 ifaceMeta, found := h.ifIndexes.LookupByName(iface) 347 if !found { 348 return errors.New("failed to get interface metadata") 349 } 350 351 req := &vpp_nat_ei.Nat44EiAddDelOutputInterface{ 352 SwIfIndex: interface_types.InterfaceIndex(ifaceMeta.SwIfIndex), 353 IsAdd: isAdd, 354 } 355 reply := &vpp_nat_ei.Nat44EiAddDelOutputInterfaceReply{} 356 357 if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { 358 return err 359 } 360 361 return nil 362 } 363 364 // Calls VPP binary API to set/unset interface NAT44 output feature 365 func (h *NatVppHandler) handleNat44InterfaceOutputFeature(iface string, isInside, isAdd bool) error { 366 if h.ed { 367 return h.handleNat44EdInterfaceOutputFeature(iface, isInside, isAdd) 368 } else { 369 return h.handleNat44EiInterfaceOutputFeature(iface, isInside, isAdd) 370 } 371 } 372 373 func (h *NatVppHandler) handleNat44EdAddressPool(vrf uint32, firstIP, lastIP string, twiceNat, isAdd bool) error { 374 firstAddr, err := ipTo4Address(firstIP) 375 if err != nil { 376 return errors.Errorf("unable to parse address %s from the NAT pool: %v", firstIP, err) 377 } 378 lastAddr := firstAddr 379 if lastIP != "" { 380 lastAddr, err = ipTo4Address(lastIP) 381 if err != nil { 382 return errors.Errorf("unable to parse address %s from the NAT pool: %v", lastIP, err) 383 } 384 } 385 386 req := &vpp_nat_ed.Nat44AddDelAddressRange{ 387 FirstIPAddress: firstAddr, 388 LastIPAddress: lastAddr, 389 VrfID: vrf, 390 Flags: setNat44EdFlags(&nat44EdFlags{isTwiceNat: twiceNat}), 391 IsAdd: isAdd, 392 } 393 reply := &vpp_nat_ed.Nat44AddDelAddressRangeReply{} 394 395 if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { 396 return err 397 } 398 399 return nil 400 } 401 402 func (h *NatVppHandler) handleNat44EiAddressPool(vrf uint32, firstIP, lastIP string, twiceNat, isAdd bool) error { 403 firstAddr, err := ipTo4Address(firstIP) 404 if err != nil { 405 return errors.Errorf("unable to parse address %s from the NAT pool: %v", firstIP, err) 406 } 407 lastAddr := firstAddr 408 if lastIP != "" { 409 lastAddr, err = ipTo4Address(lastIP) 410 if err != nil { 411 return errors.Errorf("unable to parse address %s from the NAT pool: %v", lastIP, err) 412 } 413 } 414 415 req := &vpp_nat_ei.Nat44EiAddDelAddressRange{ 416 FirstIPAddress: firstAddr, 417 LastIPAddress: lastAddr, 418 VrfID: vrf, 419 IsAdd: isAdd, 420 } 421 reply := &vpp_nat_ei.Nat44EiAddDelAddressRangeReply{} 422 423 if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { 424 return err 425 } 426 427 return nil 428 } 429 430 // Calls VPP binary API to add/remove addresses to/from the NAT44 pool. 431 func (h *NatVppHandler) handleNat44AddressPool(vrf uint32, firstIP, lastIP string, twiceNat, isAdd bool) error { 432 if h.ed { 433 return h.handleNat44EdAddressPool(vrf, firstIP, lastIP, twiceNat, isAdd) 434 } else { 435 return h.handleNat44EiAddressPool(vrf, firstIP, lastIP, twiceNat, isAdd) 436 } 437 } 438 439 // Calls VPP binary API to setup NAT virtual reassembly 440 func (h *NatVppHandler) handleNatVirtualReassembly(vrCfg *nat.VirtualReassembly, isIpv6 bool) error { 441 // Virtual Reassembly has been removed from NAT API in VPP (moved to IP API) 442 // TODO: define IPReassembly model in L3 plugin 443 return nil 444 /*req := &vpp_nat.NatSetReass{ 445 Timeout: vrCfg.Timeout, 446 MaxReass: uint16(vrCfg.MaxReassemblies), 447 MaxFrag: uint8(vrCfg.MaxFragments), 448 DropFrag: boolToUint(vrCfg.DropFragments), 449 IsIP6: isIpv6, 450 } 451 reply := &vpp_nat.NatSetReassReply{} 452 if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { 453 return err 454 }*/ 455 } 456 457 // Calls VPP binary API to add/remove NAT44 static mapping 458 func (h *NatVppHandler) handleNat44StaticMapping(mapping *nat.DNat44_StaticMapping, dnatLabel string, isAdd bool) error { 459 var ifIdx interface_types.InterfaceIndex // NOTE: This is a workaround, because NoInterface crashes VPP 22.10 460 var exIPAddr ip_types.IP4Address 461 462 // check tag length limit 463 if err := checkTagLength(dnatLabel); err != nil { 464 return err 465 } 466 467 // parse local endpoint 468 lcIPAddr, err := ipTo4Address(mapping.LocalIps[0].LocalIp) 469 if err != nil { 470 return errors.Errorf("cannot configure DNAT static mapping %s: unable to parse local IP %s: %v", 471 dnatLabel, mapping.LocalIps[0].LocalIp, err) 472 } 473 lcPort := uint16(mapping.LocalIps[0].LocalPort) 474 lcVrf := mapping.LocalIps[0].VrfId 475 476 // Check external interface (prioritized over external IP) 477 if mapping.ExternalInterface != "" { 478 // Check external interface 479 ifMeta, found := h.ifIndexes.LookupByName(mapping.ExternalInterface) 480 if !found { 481 return errors.Errorf("cannot configure static mapping for DNAT %s: required external interface %s is missing", 482 dnatLabel, mapping.ExternalInterface) 483 } 484 ifIdx = interface_types.InterfaceIndex(ifMeta.SwIfIndex) 485 } else { 486 // Parse external IP address 487 exIPAddr, err = ipTo4Address(mapping.ExternalIp) 488 if err != nil { 489 return errors.Errorf("cannot configure static mapping for DNAT %s: unable to parse external IP %s: %v", 490 dnatLabel, mapping.ExternalIp, err) 491 } 492 } 493 494 // Resolve mapping (address only or address and port) 495 var addrOnly bool 496 if lcPort == 0 || mapping.ExternalPort == 0 { 497 addrOnly = true 498 } 499 500 if h.ed { 501 req := &vpp_nat_ed.Nat44AddDelStaticMappingV2{ 502 Tag: dnatLabel, 503 LocalIPAddress: lcIPAddr, 504 ExternalIPAddress: exIPAddr, 505 Protocol: h.protocolNBValueToNumber(mapping.Protocol), 506 ExternalSwIfIndex: ifIdx, 507 VrfID: lcVrf, 508 Flags: setNat44EdFlags(&nat44EdFlags{ 509 isTwiceNat: mapping.TwiceNat == nat.DNat44_StaticMapping_ENABLED, 510 isSelfTwiceNat: mapping.TwiceNat == nat.DNat44_StaticMapping_SELF, 511 isOut2In: true, 512 isAddrOnly: addrOnly, 513 }), 514 IsAdd: isAdd, 515 } 516 517 if !addrOnly { 518 req.LocalPort = lcPort 519 req.ExternalPort = uint16(mapping.ExternalPort) 520 } 521 522 // Applying(if needed) the override of IP address picking from twice-NAT address pool 523 if mapping.TwiceNatPoolIp != "" { 524 req.MatchPool = true 525 req.PoolIPAddress, err = ipTo4Address(mapping.TwiceNatPoolIp) 526 if err != nil { 527 return errors.Errorf("cannot configure static mapping for DNAT %s: unable to parse "+ 528 "twice-NAT pool IP %s: %v", dnatLabel, mapping.TwiceNatPoolIp, err) 529 } 530 } 531 532 reply := &vpp_nat_ed.Nat44AddDelStaticMappingV2Reply{} 533 534 if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { 535 return err 536 } 537 } else { 538 req := &vpp_nat_ei.Nat44EiAddDelStaticMapping{ 539 Tag: dnatLabel, 540 LocalIPAddress: lcIPAddr, 541 ExternalIPAddress: exIPAddr, 542 Protocol: h.protocolNBValueToNumber(mapping.Protocol), 543 ExternalSwIfIndex: ifIdx, 544 VrfID: lcVrf, 545 Flags: setNat44EiFlags(&nat44EiFlags{ 546 eiAddrOnlyMapping: addrOnly, 547 }), 548 IsAdd: isAdd, 549 } 550 551 if !addrOnly { 552 req.LocalPort = lcPort 553 req.ExternalPort = uint16(mapping.ExternalPort) 554 } 555 556 reply := &vpp_nat_ei.Nat44EiAddDelStaticMappingReply{} 557 558 if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { 559 return err 560 } 561 } 562 563 return nil 564 } 565 566 func (h *NatVppHandler) handleNat44EdStaticMappingLb(mapping *nat.DNat44_StaticMapping, dnatLabel string, isAdd bool) error { 567 // check tag length limit 568 if err := checkTagLength(dnatLabel); err != nil { 569 return err 570 } 571 572 // parse external IP address 573 exIPAddrByte, err := ipTo4Address(mapping.ExternalIp) 574 if err != nil { 575 return errors.Errorf("cannot configure LB static mapping for DNAT %s: unable to parse external IP %s: %v", 576 dnatLabel, mapping.ExternalIp, err) 577 } 578 579 // In this case, external port is required 580 if mapping.ExternalPort == 0 { 581 return errors.Errorf("cannot configure LB static mapping for DNAT %s: external port is not set", dnatLabel) 582 } 583 584 // Transform local IP/Ports 585 var locals []vpp_nat_ed.Nat44LbAddrPort 586 for _, local := range mapping.LocalIps { 587 if local.LocalPort == 0 { 588 return errors.Errorf("cannot set local IP/Port for DNAT mapping %s: port is missing", 589 dnatLabel) 590 } 591 592 localIP, err := ipTo4Address(local.LocalIp) 593 if err != nil { 594 return errors.Errorf("cannot set local IP/Port for DNAT mapping %s: unable to parse local IP %v: %v", 595 dnatLabel, local.LocalIp, err) 596 } 597 598 locals = append(locals, vpp_nat_ed.Nat44LbAddrPort{ 599 Addr: localIP, 600 Port: uint16(local.LocalPort), 601 Probability: uint8(local.Probability), 602 VrfID: local.VrfId, 603 }) 604 } 605 606 req := &vpp_nat_ed.Nat44AddDelLbStaticMapping{ 607 Tag: dnatLabel, 608 Locals: locals, 609 // LocalNum: uint32(len(locals)), // should not be needed (will be set by struc) 610 ExternalAddr: exIPAddrByte, 611 ExternalPort: uint16(mapping.ExternalPort), 612 Protocol: h.protocolNBValueToNumber(mapping.Protocol), 613 Flags: setNat44EdFlags(&nat44EdFlags{ 614 isTwiceNat: mapping.TwiceNat == nat.DNat44_StaticMapping_ENABLED, 615 isSelfTwiceNat: mapping.TwiceNat == nat.DNat44_StaticMapping_SELF, 616 isOut2In: true, 617 }), 618 IsAdd: isAdd, 619 Affinity: mapping.SessionAffinity, 620 } 621 622 reply := &vpp_nat_ed.Nat44AddDelLbStaticMappingReply{} 623 624 if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { 625 return err 626 } 627 628 return nil 629 } 630 631 // Calls VPP binary API to add/remove NAT44 static mapping with load balancing. 632 func (h *NatVppHandler) handleNat44StaticMappingLb(mapping *nat.DNat44_StaticMapping, dnatLabel string, isAdd bool) error { 633 if h.ed { 634 return h.handleNat44EdStaticMappingLb(mapping, dnatLabel, isAdd) 635 } else { 636 // no static mapping with load balancing implemented for EI nat yet 637 return nil 638 } 639 } 640 641 // Calls VPP binary API to add/remove NAT44 identity mapping. 642 func (h *NatVppHandler) handleNat44IdentityMapping(mapping *nat.DNat44_IdentityMapping, dnatLabel string, isAdd bool) (err error) { 643 var ifIdx = NoInterface 644 var ipAddr ip_types.IP4Address 645 646 // check tag length limit 647 if err := checkTagLength(dnatLabel); err != nil { 648 return err 649 } 650 651 // get interface index 652 if mapping.Interface != "" { 653 ifMeta, found := h.ifIndexes.LookupByName(mapping.Interface) 654 if !found { 655 return errors.Errorf("failed to configure identity mapping for DNAT %s: addressed interface %s does not exist", 656 dnatLabel, mapping.Interface) 657 } 658 ifIdx = interface_types.InterfaceIndex(ifMeta.SwIfIndex) 659 } 660 661 if ifIdx == NoInterface { 662 // Case with IP (optionally port). Verify and parse input IP/port 663 ipAddr, err = ipTo4Address(mapping.IpAddress) 664 if err != nil { 665 return errors.Errorf("failed to configure identity mapping for DNAT %s: unable to parse IP address %s: %v", 666 dnatLabel, mapping.IpAddress, err) 667 } 668 } 669 670 var addrOnly bool 671 if mapping.Port == 0 { 672 addrOnly = true 673 } 674 675 if h.ed { 676 req := &vpp_nat_ed.Nat44AddDelIdentityMapping{ 677 Tag: dnatLabel, 678 Flags: setNat44EdFlags(&nat44EdFlags{isAddrOnly: addrOnly}), 679 IPAddress: ipAddr, 680 Port: uint16(mapping.Port), 681 Protocol: h.protocolNBValueToNumber(mapping.Protocol), 682 SwIfIndex: ifIdx, 683 VrfID: mapping.VrfId, 684 IsAdd: isAdd, 685 } 686 687 reply := &vpp_nat_ed.Nat44AddDelIdentityMappingReply{} 688 689 if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { 690 return err 691 } 692 } else { 693 req := &vpp_nat_ei.Nat44EiAddDelIdentityMapping{ 694 Tag: dnatLabel, 695 Flags: setNat44EiFlags(&nat44EiFlags{eiAddrOnlyMapping: addrOnly}), 696 IPAddress: ipAddr, 697 Port: uint16(mapping.Port), 698 Protocol: h.protocolNBValueToNumber(mapping.Protocol), 699 SwIfIndex: ifIdx, 700 VrfID: mapping.VrfId, 701 IsAdd: isAdd, 702 } 703 704 reply := &vpp_nat_ei.Nat44EiAddDelIdentityMappingReply{} 705 706 if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { 707 return err 708 } 709 } 710 711 return nil 712 } 713 714 func setNat44EdFlags(flags *nat44EdFlags) nat_types.NatConfigFlags { 715 var flagsCfg nat_types.NatConfigFlags 716 if flags.isTwiceNat { 717 flagsCfg |= nat_types.NAT_IS_TWICE_NAT 718 } 719 if flags.isSelfTwiceNat { 720 flagsCfg |= nat_types.NAT_IS_SELF_TWICE_NAT 721 } 722 if flags.isOut2In { 723 flagsCfg |= nat_types.NAT_IS_OUT2IN_ONLY 724 } 725 if flags.isAddrOnly { 726 flagsCfg |= nat_types.NAT_IS_ADDR_ONLY 727 } 728 if flags.isOutside { 729 flagsCfg |= nat_types.NAT_IS_OUTSIDE 730 } 731 if flags.isInside { 732 flagsCfg |= nat_types.NAT_IS_INSIDE 733 } 734 if flags.isStatic { 735 flagsCfg |= nat_types.NAT_IS_STATIC 736 } 737 if flags.isExtHostValid { 738 flagsCfg |= nat_types.NAT_IS_EXT_HOST_VALID 739 } 740 return flagsCfg 741 } 742 743 func setNat44EiFlags(flags *nat44EiFlags) vpp_nat_ei.Nat44EiConfigFlags { 744 var flagsCfg vpp_nat_ei.Nat44EiConfigFlags 745 if flags.eiStaticMappingOnly { 746 flagsCfg |= vpp_nat_ei.NAT44_EI_STATIC_MAPPING_ONLY 747 } 748 if flags.eiConnectionTracking { 749 flagsCfg |= vpp_nat_ei.NAT44_EI_CONNECTION_TRACKING 750 } 751 if flags.eiOut2InDpo { 752 flagsCfg |= vpp_nat_ei.NAT44_EI_OUT2IN_DPO 753 } 754 if flags.eiAddrOnlyMapping { 755 flagsCfg |= vpp_nat_ei.NAT44_EI_ADDR_ONLY_MAPPING 756 } 757 if flags.eiIfInside { 758 flagsCfg |= vpp_nat_ei.NAT44_EI_IF_INSIDE 759 } 760 if flags.eiIfOutside { 761 flagsCfg |= vpp_nat_ei.NAT44_EI_IF_OUTSIDE 762 } 763 if flags.eiStaticMapping { 764 flagsCfg |= vpp_nat_ei.NAT44_EI_STATIC_MAPPING 765 } 766 return flagsCfg 767 } 768 769 func ipTo4Address(ipStr string) (addr ip_types.IP4Address, err error) { 770 netIP := net.ParseIP(ipStr) 771 if netIP == nil { 772 return ip_types.IP4Address{}, fmt.Errorf("invalid IP: %q", ipStr) 773 } 774 if ip4 := netIP.To4(); ip4 != nil { 775 var ip4Addr ip_types.IP4Address 776 copy(ip4Addr[:], netIP.To4()) 777 addr = ip4Addr 778 } else { 779 return ip_types.IP4Address{}, fmt.Errorf("required IPv4, provided: %q", ipStr) 780 } 781 return 782 } 783 784 // checkTagLength serves as a validator for static/identity mapping tag length 785 func checkTagLength(tag string) error { 786 if len(tag) > maxTagLen { 787 return errors.Errorf("DNAT label '%s' has %d bytes, max allowed is %d", 788 tag, len(tag), maxTagLen) 789 } 790 return nil 791 }