go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/natplugin/vppcalls/vpp2210/dump_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 "context" 19 "fmt" 20 "io" 21 "net" 22 "sort" 23 "strings" 24 25 "google.golang.org/protobuf/proto" 26 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/ifplugin/ifaceidx" 31 ifs "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/interfaces" 32 nat "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/nat" 33 ) 34 35 // DNATs sorted by tags 36 type dnatMap map[string]*nat.DNat44 37 38 // static mappings sorted by tags 39 type stMappingMap map[string][]*nat.DNat44_StaticMapping 40 41 // identity mappings sorted by tags 42 type idMappingMap map[string][]*nat.DNat44_IdentityMapping 43 44 // WithLegacyStartupConf returns true if the loaded VPP NAT plugin is still using 45 // the legacy startup NAT configuration (this is the case for VPP <= 20.09). 46 func (h *NatVppHandler) WithLegacyStartupConf() bool { 47 return false 48 } 49 50 func (h *NatVppHandler) DefaultNat44GlobalConfig() *nat.Nat44Global { 51 return &nat.Nat44Global{ 52 Forwarding: false, 53 EndpointIndependent: false, 54 NatInterfaces: nil, 55 AddressPool: nil, 56 VirtualReassembly: nil, // VirtualReassembly is not part of NAT API in VPP 20.01+ anymore 57 } 58 } 59 60 func (h *NatVppHandler) nat44EiGlobalConfigDump(dumpDeprecated bool) (cfg *nat.Nat44Global, err error) { 61 cfg = &nat.Nat44Global{} 62 req := &vpp_nat_ei.Nat44EiShowRunningConfig{} 63 reply := &vpp_nat_ei.Nat44EiShowRunningConfigReply{} 64 if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { 65 return nil, err 66 } 67 cfg.EndpointIndependent = true 68 cfg.Forwarding = reply.ForwardingEnabled 69 cfg.VirtualReassembly, _, err = h.virtualReassemblyDump() 70 if err != nil { 71 return nil, err 72 } 73 if dumpDeprecated { 74 cfg.NatInterfaces, err = h.nat44InterfaceDump() 75 if err != nil { 76 return nil, err 77 } 78 cfg.AddressPool, err = h.nat44AddressDump() 79 if err != nil { 80 return nil, err 81 } 82 } 83 return 84 } 85 86 func (h *NatVppHandler) nat44EdGlobalConfigDump(dumpDeprecated bool) (cfg *nat.Nat44Global, err error) { 87 cfg = &nat.Nat44Global{} 88 req := &vpp_nat_ed.Nat44ShowRunningConfig{} 89 reply := &vpp_nat_ed.Nat44ShowRunningConfigReply{} 90 if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil { 91 return nil, err 92 } 93 cfg.EndpointIndependent = false 94 cfg.Forwarding = reply.ForwardingEnabled 95 cfg.VirtualReassembly, _, err = h.virtualReassemblyDump() 96 if err != nil { 97 return nil, err 98 } 99 if dumpDeprecated { 100 cfg.NatInterfaces, err = h.nat44InterfaceDump() 101 if err != nil { 102 return nil, err 103 } 104 cfg.AddressPool, err = h.nat44AddressDump() 105 if err != nil { 106 return nil, err 107 } 108 } 109 return 110 } 111 112 // Nat44GlobalConfigDump dumps global NAT44 config in NB format. 113 func (h *NatVppHandler) Nat44GlobalConfigDump(dumpDeprecated bool) (cfg *nat.Nat44Global, err error) { 114 if h.ed { 115 return h.nat44EdGlobalConfigDump(dumpDeprecated) 116 } else { 117 return h.nat44EiGlobalConfigDump(dumpDeprecated) 118 } 119 120 } 121 122 // DNat44Dump dumps all configured DNAT-44 configurations ordered by label. 123 func (h *NatVppHandler) DNat44Dump() (dnats []*nat.DNat44, err error) { 124 dnatMap := make(dnatMap) 125 126 // Static mappings 127 natStMappings, err := h.nat44StaticMappingDump() 128 if err != nil { 129 return nil, fmt.Errorf("failed to dump NAT44 static mappings: %v", err) 130 } 131 for label, mappings := range natStMappings { 132 dnat := getOrCreateDNAT(dnatMap, label) 133 dnat.StMappings = append(dnat.StMappings, mappings...) 134 } 135 136 // Static mappings with load balancer 137 natStLbMappings, err := h.nat44StaticMappingLbDump() 138 if err != nil { 139 return nil, fmt.Errorf("failed to dump NAT44 static mappings with load balancer: %v", err) 140 } 141 for label, mappings := range natStLbMappings { 142 dnat := getOrCreateDNAT(dnatMap, label) 143 dnat.StMappings = append(dnat.StMappings, mappings...) 144 } 145 146 // Identity mappings 147 natIDMappings, err := h.nat44IdentityMappingDump() 148 if err != nil { 149 return nil, fmt.Errorf("failed to dump NAT44 identity mappings: %v", err) 150 } 151 for label, mappings := range natIDMappings { 152 dnat := getOrCreateDNAT(dnatMap, label) 153 dnat.IdMappings = append(dnat.IdMappings, mappings...) 154 } 155 156 // Convert map of DNAT configurations into a list. 157 for _, dnat := range dnatMap { 158 dnats = append(dnats, dnat) 159 } 160 161 // sort to simplify testing 162 sort.Slice(dnats, func(i, j int) bool { return dnats[i].Label < dnats[j].Label }) 163 164 return dnats, nil 165 } 166 167 func (h *NatVppHandler) nat44EiInterfacesDump() ([]*nat.Nat44Interface, error) { 168 var natIfs []*nat.Nat44Interface 169 170 // dump non-output NAT interfaces 171 req1 := &vpp_nat_ei.Nat44EiInterfaceDump{} 172 reqContext := h.callsChannel.SendMultiRequest(req1) 173 for { 174 msg := &vpp_nat_ei.Nat44EiInterfaceDetails{} 175 stop, err := reqContext.ReceiveReply(msg) 176 if err != nil { 177 return nil, fmt.Errorf("failed to dump NAT44 interface: %v", err) 178 } 179 if stop { 180 break 181 } 182 ifName, _, found := h.ifIndexes.LookupBySwIfIndex(uint32(msg.SwIfIndex)) 183 if !found { 184 h.log.Warnf("Interface with index %d not found in the mapping", msg.SwIfIndex) 185 continue 186 } 187 flags := getNat44EiFlags(msg.Flags) 188 natIf := &nat.Nat44Interface{ 189 Name: ifName, 190 NatInside: flags.eiIfInside, 191 NatOutside: flags.eiIfOutside, 192 OutputFeature: false, 193 } 194 natIfs = append(natIfs, natIf) 195 } 196 197 // dump output NAT interfaces 198 var cursor uint32 199 for { 200 rpcServ, err := h.natEi.Nat44EiOutputInterfaceGet(context.Background(), &vpp_nat_ei.Nat44EiOutputInterfaceGet{ 201 Cursor: cursor, 202 }) 203 if err != nil { 204 return nil, fmt.Errorf("failed to dump NAT44 interface output feature: %v", err) 205 } 206 RecvLoop: 207 for { 208 details, reply, err := rpcServ.Recv() 209 if err != nil && err != io.EOF { 210 return nil, fmt.Errorf("failed to dump NAT44 interface output feature: %v", err) 211 } 212 if reply != nil { 213 // TODO: Possible bug in VPP? When VPP contains no NAT interfaces this 214 // reply often returns cursor value of 0 repeatedly even though it should return 215 // ^uint32(0). So if the reply contains cursor that is the same as cursor 216 // from previous reply, return. 217 if reply.Cursor == cursor || reply.Cursor == ^uint32(0) { 218 return natIfs, nil 219 } 220 cursor = reply.Cursor 221 break RecvLoop 222 } 223 if details != nil { 224 ifName, _, found := h.ifIndexes.LookupBySwIfIndex(uint32(details.SwIfIndex)) 225 if !found { 226 h.log.Warnf("Interface with index %d not found in the mapping", details.SwIfIndex) 227 } 228 natIfs = append(natIfs, &nat.Nat44Interface{ 229 Name: ifName, 230 OutputFeature: true, 231 }) 232 } 233 } 234 } 235 } 236 237 func (h *NatVppHandler) nat44EdInterfacesDump() ([]*nat.Nat44Interface, error) { 238 var natIfs []*nat.Nat44Interface 239 240 // dump non-output NAT interfaces 241 req1 := &vpp_nat_ed.Nat44InterfaceDump{} 242 reqContext := h.callsChannel.SendMultiRequest(req1) 243 for { 244 msg := &vpp_nat_ed.Nat44InterfaceDetails{} 245 stop, err := reqContext.ReceiveReply(msg) 246 if err != nil { 247 return nil, fmt.Errorf("failed to dump NAT44 interface: %v", err) 248 } 249 if stop { 250 break 251 } 252 ifName, _, found := h.ifIndexes.LookupBySwIfIndex(uint32(msg.SwIfIndex)) 253 if !found { 254 h.log.Warnf("Interface with index %d not found in the mapping", msg.SwIfIndex) 255 continue 256 } 257 flags := getNat44Flags(msg.Flags) 258 natIf := &nat.Nat44Interface{ 259 Name: ifName, 260 NatInside: flags.isInside, 261 NatOutside: flags.isOutside, 262 OutputFeature: false, 263 } 264 natIfs = append(natIfs, natIf) 265 } 266 267 // dump output NAT interfaces 268 var cursor uint32 269 for { 270 rpcServ, err := h.natEd.Nat44EdOutputInterfaceGet(context.Background(), &vpp_nat_ed.Nat44EdOutputInterfaceGet{ 271 Cursor: cursor, 272 }) 273 if err != nil { 274 return nil, fmt.Errorf("failed to dump NAT44 interface output feature: %v", err) 275 } 276 RecvLoop: 277 for { 278 details, reply, err := rpcServ.Recv() 279 if err != nil && err != io.EOF { 280 return nil, fmt.Errorf("failed to dump NAT44 interface output feature: %v", err) 281 } 282 if reply != nil { 283 // TODO: Possible bug in VPP? When VPP contains no NAT interfaces this 284 // reply often returns cursor value of 0 repeatedly even though it should return 285 // ^uint32(0). So if the reply contains cursor that is the same as cursor 286 // from previous reply, return. 287 if reply.Cursor == cursor || reply.Cursor == ^uint32(0) { 288 return natIfs, nil 289 } 290 cursor = reply.Cursor 291 break RecvLoop 292 } 293 if details != nil { 294 ifName, _, found := h.ifIndexes.LookupBySwIfIndex(uint32(details.SwIfIndex)) 295 if !found { 296 h.log.Warnf("Interface with index %d not found in the mapping", details.SwIfIndex) 297 } 298 natIfs = append(natIfs, &nat.Nat44Interface{ 299 Name: ifName, 300 OutputFeature: true, 301 }) 302 } 303 } 304 } 305 } 306 307 // Nat44InterfacesDump dumps NAT44 config of all NAT44-enabled interfaces. 308 func (h *NatVppHandler) Nat44InterfacesDump() (natIfs []*nat.Nat44Interface, err error) { 309 if h.ed { 310 return h.nat44EdInterfacesDump() 311 } else { 312 return h.nat44EiInterfacesDump() 313 } 314 } 315 316 func (h *NatVppHandler) nat44EiAddressPoolsDump() (natPools []*nat.Nat44AddressPool, err error) { 317 var curPool *nat.Nat44AddressPool 318 var lastIP net.IP 319 320 req := &vpp_nat_ei.Nat44EiAddressDump{} 321 reqContext := h.callsChannel.SendMultiRequest(req) 322 323 for { 324 msg := &vpp_nat_ei.Nat44EiAddressDetails{} 325 stop, err := reqContext.ReceiveReply(msg) 326 if err != nil { 327 return nil, fmt.Errorf("failed to dump NAT44 Address pool: %v", err) 328 } 329 if stop { 330 break 331 } 332 ip := net.IP(msg.IPAddress[:]) 333 // merge subsequent IPs into a single pool 334 if curPool != nil && curPool.VrfId == msg.VrfID && ip.Equal(incIP(lastIP)) { 335 // update current pool 336 curPool.LastIp = ip.String() 337 } else { 338 // start a new pool 339 pool := &nat.Nat44AddressPool{ 340 FirstIp: ip.String(), 341 VrfId: msg.VrfID, 342 TwiceNat: false, 343 } 344 curPool = pool 345 natPools = append(natPools, pool) 346 } 347 lastIP = ip 348 } 349 return 350 } 351 352 func (h *NatVppHandler) nat44EdAddressPoolsDump() (natPools []*nat.Nat44AddressPool, err error) { 353 var curPool *nat.Nat44AddressPool 354 var lastIP net.IP 355 356 req := &vpp_nat_ed.Nat44AddressDump{} 357 reqContext := h.callsChannel.SendMultiRequest(req) 358 359 for { 360 msg := &vpp_nat_ed.Nat44AddressDetails{} 361 stop, err := reqContext.ReceiveReply(msg) 362 if err != nil { 363 return nil, fmt.Errorf("failed to dump NAT44 Address pool: %v", err) 364 } 365 if stop { 366 break 367 } 368 ip := net.IP(msg.IPAddress[:]) 369 isTwiceNat := getNat44Flags(msg.Flags).isTwiceNat 370 // merge subsequent IPs into a single pool 371 if curPool != nil && curPool.VrfId == msg.VrfID && curPool.TwiceNat == isTwiceNat && ip.Equal(incIP(lastIP)) { 372 // update current pool 373 curPool.LastIp = ip.String() 374 } else { 375 // start a new pool 376 pool := &nat.Nat44AddressPool{ 377 FirstIp: ip.String(), 378 VrfId: msg.VrfID, 379 TwiceNat: isTwiceNat, 380 } 381 curPool = pool 382 natPools = append(natPools, pool) 383 } 384 lastIP = ip 385 } 386 return 387 } 388 389 // Nat44AddressPoolsDump dumps all configured NAT44 address pools. 390 func (h *NatVppHandler) Nat44AddressPoolsDump() (natPools []*nat.Nat44AddressPool, err error) { 391 if h.ed { 392 return h.nat44EdAddressPoolsDump() 393 } else { 394 return h.nat44EiAddressPoolsDump() 395 } 396 } 397 398 func (h *NatVppHandler) nat44EiAddressDump() (addressPool []*nat.Nat44Global_Address, err error) { 399 req := &vpp_nat_ei.Nat44EiAddressDump{} 400 reqContext := h.callsChannel.SendMultiRequest(req) 401 402 for { 403 msg := &vpp_nat_ei.Nat44EiAddressDetails{} 404 stop, err := reqContext.ReceiveReply(msg) 405 if err != nil { 406 return nil, fmt.Errorf("failed to dump NAT44 Address pool: %v", err) 407 } 408 if stop { 409 break 410 } 411 412 addressPool = append(addressPool, &nat.Nat44Global_Address{ 413 Address: net.IP(msg.IPAddress[:]).String(), 414 VrfId: msg.VrfID, 415 TwiceNat: false, 416 }) 417 } 418 419 return 420 } 421 422 func (h *NatVppHandler) nat44EdAddressDump() (addressPool []*nat.Nat44Global_Address, err error) { 423 req := &vpp_nat_ed.Nat44AddressDump{} 424 reqContext := h.callsChannel.SendMultiRequest(req) 425 426 for { 427 msg := &vpp_nat_ed.Nat44AddressDetails{} 428 stop, err := reqContext.ReceiveReply(msg) 429 if err != nil { 430 return nil, fmt.Errorf("failed to dump NAT44 Address pool: %v", err) 431 } 432 if stop { 433 break 434 } 435 436 addressPool = append(addressPool, &nat.Nat44Global_Address{ 437 Address: net.IP(msg.IPAddress[:]).String(), 438 VrfId: msg.VrfID, 439 TwiceNat: getNat44Flags(msg.Flags).isTwiceNat, 440 }) 441 } 442 443 return 444 } 445 446 // nat44AddressDump returns NAT44 address pool configured in the VPP. 447 // Deprecated. Functionality moved to Nat44AddressPoolsDump. Kept for backward compatibility. 448 func (h *NatVppHandler) nat44AddressDump() (addressPool []*nat.Nat44Global_Address, err error) { 449 if h.ed { 450 return h.nat44EdAddressDump() 451 } else { 452 return h.nat44EiAddressDump() 453 } 454 } 455 456 // virtualReassemblyDump returns current NAT virtual-reassembly configuration. 457 func (h *NatVppHandler) virtualReassemblyDump() (vrIPv4 *nat.VirtualReassembly, vrIPv6 *nat.VirtualReassembly, err error) { 458 /*ipv4vr, err := h.ip.IPReassemblyGet(context.TODO(), &vpp_ip.IPReassemblyGet{IsIP6: false}) 459 if err != nil { 460 return nil, nil, fmt.Errorf("getting virtual reassembly IPv4 config failed: %w", err) 461 } 462 h.log.Debugf("IP Reassembly config IPv4: %+v\n", ipv4vr) 463 ipv6vr, err := h.ip.IPReassemblyGet(context.TODO(), &vpp_ip.IPReassemblyGet{IsIP6: true}) 464 if err != nil { 465 return nil, nil, fmt.Errorf("getting virtual reassembly IPv6 config failed: %w", err) 466 } 467 h.log.Debugf("IP Reassembly config IPv6: %+v\n", ipv6vr)*/ 468 469 // Virtual Reassembly has been removed from NAT API in VPP (moved to IP API) 470 // TODO: define IPReassembly model in L3 plugin 471 return nil, nil, nil 472 /*vrIPv4 = &nat.VirtualReassembly{ 473 Timeout: reply.IP4Timeout, 474 MaxReassemblies: uint32(reply.IP4MaxReass), 475 MaxFragments: uint32(reply.IP4MaxFrag), 476 DropFragments: uintToBool(reply.IP4DropFrag), 477 } 478 vrIPv6 = &nat.VirtualReassembly{ 479 Timeout: reply.IP6Timeout, 480 MaxReassemblies: uint32(reply.IP6MaxReass), 481 MaxFragments: uint32(reply.IP6MaxFrag), 482 DropFragments: uintToBool(reply.IP6DropFrag), 483 } 484 return*/ 485 } 486 487 func (h *NatVppHandler) nat44EiStaticMappingDump() (entries stMappingMap, err error) { 488 entries = make(stMappingMap) 489 childMappings := make(stMappingMap) 490 req := &vpp_nat_ei.Nat44EiStaticMappingDump{} 491 reqContext := h.callsChannel.SendMultiRequest(req) 492 493 for { 494 msg := &vpp_nat_ei.Nat44EiStaticMappingDetails{} 495 stop, err := reqContext.ReceiveReply(msg) 496 if err != nil { 497 return nil, fmt.Errorf("failed to dump NAT44 static mapping: %v", err) 498 } 499 if stop { 500 break 501 } 502 lcIPAddress := net.IP(msg.LocalIPAddress[:]).String() 503 exIPAddress := net.IP(msg.ExternalIPAddress[:]).String() 504 505 // Parse tag (DNAT label) 506 tag := strings.TrimRight(msg.Tag, "\x00") 507 if _, hasTag := entries[tag]; !hasTag { 508 entries[tag] = []*nat.DNat44_StaticMapping{} 509 childMappings[tag] = []*nat.DNat44_StaticMapping{} 510 } 511 512 // resolve interface name 513 var ( 514 found bool 515 extIfaceName string 516 extIfaceMeta *ifaceidx.IfaceMetadata 517 ) 518 if msg.ExternalSwIfIndex != NoInterface { 519 extIfaceName, extIfaceMeta, found = h.ifIndexes.LookupBySwIfIndex(uint32(msg.ExternalSwIfIndex)) 520 if !found { 521 h.log.Warnf("Interface with index %v not found in the mapping", msg.ExternalSwIfIndex) 522 continue 523 } 524 } 525 526 // Add mapping into the map. 527 mapping := &nat.DNat44_StaticMapping{ 528 ExternalInterface: extIfaceName, 529 ExternalIp: exIPAddress, 530 ExternalPort: uint32(msg.ExternalPort), 531 LocalIps: []*nat.DNat44_StaticMapping_LocalIP{ // single-value 532 { 533 VrfId: msg.VrfID, 534 LocalIp: lcIPAddress, 535 LocalPort: uint32(msg.LocalPort), 536 }, 537 }, 538 Protocol: h.protocolNumberToNBValue(msg.Protocol), 539 TwiceNat: h.getTwiceNatMode(false, false), 540 // if there is only one backend the affinity can not be set 541 SessionAffinity: 0, 542 } 543 entries[tag] = append(entries[tag], mapping) 544 545 if msg.ExternalSwIfIndex != NoInterface { 546 // collect auto-generated "child" mappings (interface replaced with every assigned IP address) 547 for _, ipAddr := range h.getInterfaceIPAddresses(extIfaceName, extIfaceMeta) { 548 childMapping := proto.Clone(mapping).(*nat.DNat44_StaticMapping) 549 childMapping.ExternalIp = ipAddr 550 childMapping.ExternalInterface = "" 551 childMappings[tag] = append(childMappings[tag], childMapping) 552 } 553 } 554 } 555 556 // do not dump auto-generated child mappings 557 for tag, mappings := range entries { 558 var filtered []*nat.DNat44_StaticMapping 559 for _, mapping := range mappings { 560 isChild := false 561 for _, child := range childMappings[tag] { 562 if proto.Equal(mapping, child) { 563 isChild = true 564 break 565 } 566 } 567 if !isChild { 568 filtered = append(filtered, mapping) 569 } 570 } 571 entries[tag] = filtered 572 } 573 return entries, nil 574 } 575 576 func (h *NatVppHandler) nat44EdStaticMappingDump() (entries stMappingMap, err error) { 577 entries = make(stMappingMap) 578 childMappings := make(stMappingMap) 579 req := &vpp_nat_ed.Nat44StaticMappingDump{} 580 reqContext := h.callsChannel.SendMultiRequest(req) 581 582 for { 583 msg := &vpp_nat_ed.Nat44StaticMappingDetails{} 584 stop, err := reqContext.ReceiveReply(msg) 585 if err != nil { 586 return nil, fmt.Errorf("failed to dump NAT44 static mapping: %v", err) 587 } 588 if stop { 589 break 590 } 591 lcIPAddress := net.IP(msg.LocalIPAddress[:]).String() 592 exIPAddress := net.IP(msg.ExternalIPAddress[:]).String() 593 594 // Parse tag (DNAT label) 595 tag := strings.TrimRight(msg.Tag, "\x00") 596 if _, hasTag := entries[tag]; !hasTag { 597 entries[tag] = []*nat.DNat44_StaticMapping{} 598 childMappings[tag] = []*nat.DNat44_StaticMapping{} 599 } 600 601 // resolve interface name 602 var ( 603 found bool 604 extIfaceName string 605 extIfaceMeta *ifaceidx.IfaceMetadata 606 ) 607 if msg.ExternalSwIfIndex != NoInterface { 608 extIfaceName, extIfaceMeta, found = h.ifIndexes.LookupBySwIfIndex(uint32(msg.ExternalSwIfIndex)) 609 if !found { 610 h.log.Warnf("Interface with index %v not found in the mapping", msg.ExternalSwIfIndex) 611 continue 612 } 613 } 614 615 flags := getNat44Flags(msg.Flags) 616 617 // Add mapping into the map. 618 mapping := &nat.DNat44_StaticMapping{ 619 ExternalInterface: extIfaceName, 620 ExternalIp: exIPAddress, 621 ExternalPort: uint32(msg.ExternalPort), 622 LocalIps: []*nat.DNat44_StaticMapping_LocalIP{ // single-value 623 { 624 VrfId: msg.VrfID, 625 LocalIp: lcIPAddress, 626 LocalPort: uint32(msg.LocalPort), 627 }, 628 }, 629 Protocol: h.protocolNumberToNBValue(msg.Protocol), 630 TwiceNat: h.getTwiceNatMode(flags.isTwiceNat, flags.isSelfTwiceNat), 631 // if there is only one backend the affinity can not be set 632 SessionAffinity: 0, 633 } 634 entries[tag] = append(entries[tag], mapping) 635 636 if msg.ExternalSwIfIndex != NoInterface { 637 // collect auto-generated "child" mappings (interface replaced with every assigned IP address) 638 for _, ipAddr := range h.getInterfaceIPAddresses(extIfaceName, extIfaceMeta) { 639 childMapping := proto.Clone(mapping).(*nat.DNat44_StaticMapping) 640 childMapping.ExternalIp = ipAddr 641 childMapping.ExternalInterface = "" 642 childMappings[tag] = append(childMappings[tag], childMapping) 643 } 644 } 645 } 646 647 // do not dump auto-generated child mappings 648 for tag, mappings := range entries { 649 var filtered []*nat.DNat44_StaticMapping 650 for _, mapping := range mappings { 651 isChild := false 652 for _, child := range childMappings[tag] { 653 if proto.Equal(mapping, child) { 654 isChild = true 655 break 656 } 657 } 658 if !isChild { 659 filtered = append(filtered, mapping) 660 } 661 } 662 entries[tag] = filtered 663 } 664 return entries, nil 665 } 666 667 // nat44StaticMappingDump returns a map of NAT44 static mappings sorted by tags 668 func (h *NatVppHandler) nat44StaticMappingDump() (entries stMappingMap, err error) { 669 if h.ed { 670 return h.nat44EdStaticMappingDump() 671 } else { 672 return h.nat44EiStaticMappingDump() 673 } 674 } 675 676 func (h *NatVppHandler) nat44EiStaticMappingLbDump() (entries stMappingMap, err error) { 677 return make(stMappingMap), nil 678 } 679 680 func (h *NatVppHandler) nat44EdStaticMappingLbDump() (entries stMappingMap, err error) { 681 entries = make(stMappingMap) 682 req := &vpp_nat_ed.Nat44LbStaticMappingDump{} 683 reqContext := h.callsChannel.SendMultiRequest(req) 684 685 for { 686 msg := &vpp_nat_ed.Nat44LbStaticMappingDetails{} 687 stop, err := reqContext.ReceiveReply(msg) 688 if err != nil { 689 return nil, fmt.Errorf("failed to dump NAT44 lb-static mapping: %v", err) 690 } 691 if stop { 692 break 693 } 694 695 // Parse tag (DNAT label) 696 tag := strings.TrimRight(msg.Tag, "\x00") 697 if _, hasTag := entries[tag]; !hasTag { 698 entries[tag] = []*nat.DNat44_StaticMapping{} 699 } 700 701 // Prepare localIPs 702 var locals []*nat.DNat44_StaticMapping_LocalIP 703 for _, localIPVal := range msg.Locals { 704 locals = append(locals, &nat.DNat44_StaticMapping_LocalIP{ 705 VrfId: localIPVal.VrfID, 706 LocalIp: net.IP(localIPVal.Addr[:]).String(), 707 LocalPort: uint32(localIPVal.Port), 708 Probability: uint32(localIPVal.Probability), 709 }) 710 } 711 exIPAddress := net.IP(msg.ExternalAddr[:]).String() 712 713 flags := getNat44Flags(msg.Flags) 714 715 // Add mapping into the map. 716 mapping := &nat.DNat44_StaticMapping{ 717 ExternalIp: exIPAddress, 718 ExternalPort: uint32(msg.ExternalPort), 719 LocalIps: locals, 720 Protocol: h.protocolNumberToNBValue(msg.Protocol), 721 TwiceNat: h.getTwiceNatMode(flags.isTwiceNat, flags.isSelfTwiceNat), 722 SessionAffinity: msg.Affinity, 723 } 724 entries[tag] = append(entries[tag], mapping) 725 } 726 727 return entries, nil 728 } 729 730 // nat44StaticMappingLbDump returns a map of NAT44 static mapping with load balancing sorted by tags. 731 func (h *NatVppHandler) nat44StaticMappingLbDump() (entries stMappingMap, err error) { 732 if h.ed { 733 return h.nat44EdStaticMappingLbDump() 734 } else { 735 return h.nat44EiStaticMappingLbDump() 736 } 737 } 738 739 func (h *NatVppHandler) nat44EiIdentityMappingDump() (entries idMappingMap, err error) { 740 entries = make(idMappingMap) 741 childMappings := make(idMappingMap) 742 req := &vpp_nat_ei.Nat44EiIdentityMappingDump{} 743 reqContext := h.callsChannel.SendMultiRequest(req) 744 745 for { 746 msg := &vpp_nat_ei.Nat44EiIdentityMappingDetails{} 747 stop, err := reqContext.ReceiveReply(msg) 748 if err != nil { 749 return nil, fmt.Errorf("failed to dump NAT44 identity mapping: %v", err) 750 } 751 if stop { 752 break 753 } 754 755 // Parse tag (DNAT label) 756 tag := strings.TrimRight(msg.Tag, "\x00") 757 if _, hasTag := entries[tag]; !hasTag { 758 entries[tag] = []*nat.DNat44_IdentityMapping{} 759 childMappings[tag] = []*nat.DNat44_IdentityMapping{} 760 } 761 762 // resolve interface name 763 var ( 764 found bool 765 ifaceName string 766 ifaceMeta *ifaceidx.IfaceMetadata 767 ) 768 if msg.SwIfIndex != NoInterface { 769 ifaceName, ifaceMeta, found = h.ifIndexes.LookupBySwIfIndex(uint32(msg.SwIfIndex)) 770 if !found { 771 h.log.Warnf("Interface with index %v not found in the mapping", msg.SwIfIndex) 772 continue 773 } 774 } 775 776 // Add mapping into the map. 777 mapping := &nat.DNat44_IdentityMapping{ 778 IpAddress: net.IP(msg.IPAddress[:]).String(), 779 VrfId: msg.VrfID, 780 Interface: ifaceName, 781 Port: uint32(msg.Port), 782 Protocol: h.protocolNumberToNBValue(msg.Protocol), 783 } 784 entries[tag] = append(entries[tag], mapping) 785 786 if msg.SwIfIndex != NoInterface { 787 // collect auto-generated "child" mappings (interface replaced with every assigned IP address) 788 for _, ipAddr := range h.getInterfaceIPAddresses(ifaceName, ifaceMeta) { 789 childMapping := proto.Clone(mapping).(*nat.DNat44_IdentityMapping) 790 childMapping.IpAddress = ipAddr 791 childMapping.Interface = "" 792 childMappings[tag] = append(childMappings[tag], childMapping) 793 } 794 } 795 } 796 797 // do not dump auto-generated child mappings 798 for tag, mappings := range entries { 799 var filtered []*nat.DNat44_IdentityMapping 800 for _, mapping := range mappings { 801 isChild := false 802 for _, child := range childMappings[tag] { 803 if proto.Equal(mapping, child) { 804 isChild = true 805 break 806 } 807 } 808 if !isChild { 809 filtered = append(filtered, mapping) 810 } 811 } 812 entries[tag] = filtered 813 } 814 815 return entries, nil 816 } 817 818 func (h *NatVppHandler) nat44EdIdentityMappingDump() (entries idMappingMap, err error) { 819 entries = make(idMappingMap) 820 childMappings := make(idMappingMap) 821 req := &vpp_nat_ed.Nat44IdentityMappingDump{} 822 reqContext := h.callsChannel.SendMultiRequest(req) 823 824 for { 825 msg := &vpp_nat_ed.Nat44IdentityMappingDetails{} 826 stop, err := reqContext.ReceiveReply(msg) 827 if err != nil { 828 return nil, fmt.Errorf("failed to dump NAT44 identity mapping: %v", err) 829 } 830 if stop { 831 break 832 } 833 834 // Parse tag (DNAT label) 835 tag := strings.TrimRight(msg.Tag, "\x00") 836 if _, hasTag := entries[tag]; !hasTag { 837 entries[tag] = []*nat.DNat44_IdentityMapping{} 838 childMappings[tag] = []*nat.DNat44_IdentityMapping{} 839 } 840 841 // resolve interface name 842 var ( 843 found bool 844 ifaceName string 845 ifaceMeta *ifaceidx.IfaceMetadata 846 ) 847 if msg.SwIfIndex != NoInterface { 848 ifaceName, ifaceMeta, found = h.ifIndexes.LookupBySwIfIndex(uint32(msg.SwIfIndex)) 849 if !found { 850 h.log.Warnf("Interface with index %v not found in the mapping", msg.SwIfIndex) 851 continue 852 } 853 } 854 855 // Add mapping into the map. 856 mapping := &nat.DNat44_IdentityMapping{ 857 IpAddress: net.IP(msg.IPAddress[:]).String(), 858 VrfId: msg.VrfID, 859 Interface: ifaceName, 860 Port: uint32(msg.Port), 861 Protocol: h.protocolNumberToNBValue(msg.Protocol), 862 } 863 entries[tag] = append(entries[tag], mapping) 864 865 if msg.SwIfIndex != NoInterface { 866 // collect auto-generated "child" mappings (interface replaced with every assigned IP address) 867 for _, ipAddr := range h.getInterfaceIPAddresses(ifaceName, ifaceMeta) { 868 childMapping := proto.Clone(mapping).(*nat.DNat44_IdentityMapping) 869 childMapping.IpAddress = ipAddr 870 childMapping.Interface = "" 871 childMappings[tag] = append(childMappings[tag], childMapping) 872 } 873 } 874 } 875 876 // do not dump auto-generated child mappings 877 for tag, mappings := range entries { 878 var filtered []*nat.DNat44_IdentityMapping 879 for _, mapping := range mappings { 880 isChild := false 881 for _, child := range childMappings[tag] { 882 if proto.Equal(mapping, child) { 883 isChild = true 884 break 885 } 886 } 887 if !isChild { 888 filtered = append(filtered, mapping) 889 } 890 } 891 entries[tag] = filtered 892 } 893 894 return entries, nil 895 } 896 897 // nat44IdentityMappingDump returns a map of NAT44 identity mappings sorted by tags. 898 func (h *NatVppHandler) nat44IdentityMappingDump() (entries idMappingMap, err error) { 899 if h.ed { 900 return h.nat44EdIdentityMappingDump() 901 } else { 902 return h.nat44EiIdentityMappingDump() 903 } 904 } 905 906 func (h *NatVppHandler) nat44EiInterfaceDump() ([]*nat.Nat44Global_Interface, error) { 907 var natIfs []*nat.Nat44Global_Interface 908 909 // dump non-output NAT interfaces 910 req1 := &vpp_nat_ei.Nat44EiInterfaceDump{} 911 reqContext := h.callsChannel.SendMultiRequest(req1) 912 913 for { 914 msg := &vpp_nat_ei.Nat44EiInterfaceDetails{} 915 stop, err := reqContext.ReceiveReply(msg) 916 if err != nil { 917 return nil, fmt.Errorf("failed to dump NAT44 interface: %v", err) 918 } 919 if stop { 920 break 921 } 922 923 // Find interface name 924 ifName, _, found := h.ifIndexes.LookupBySwIfIndex(uint32(msg.SwIfIndex)) 925 if !found { 926 h.log.Warnf("Interface with index %d not found in the mapping", msg.SwIfIndex) 927 continue 928 } 929 930 flags := getNat44EiFlags(msg.Flags) 931 932 if flags.eiIfInside { 933 natIfs = append(natIfs, &nat.Nat44Global_Interface{ 934 Name: ifName, 935 IsInside: true, 936 }) 937 } else { 938 natIfs = append(natIfs, &nat.Nat44Global_Interface{ 939 Name: ifName, 940 IsInside: false, 941 }) 942 } 943 } 944 945 // dump output NAT interfaces 946 var cursor uint32 947 for { 948 rpcServ, err := h.natEi.Nat44EiOutputInterfaceGet(context.Background(), &vpp_nat_ei.Nat44EiOutputInterfaceGet{ 949 Cursor: cursor, 950 }) 951 if err != nil { 952 return nil, fmt.Errorf("failed to dump NAT44 interface output feature: %v", err) 953 } 954 RecvLoop: 955 for { 956 details, reply, err := rpcServ.Recv() 957 if err != nil && err != io.EOF { 958 return nil, fmt.Errorf("failed to dump NAT44 interface output feature: %v", err) 959 } 960 if reply != nil { 961 // TODO: Possible bug in VPP? When VPP contains no NAT interfaces this 962 // reply often returns cursor value of 0 repeatedly even though it should return 963 // ^uint32(0). So if the reply contains cursor that is the same as cursor 964 // from previous reply, return. 965 if reply.Cursor == cursor || reply.Cursor == ^uint32(0) { 966 return natIfs, nil 967 } 968 cursor = reply.Cursor 969 break RecvLoop 970 } 971 if details != nil { 972 ifName, _, found := h.ifIndexes.LookupBySwIfIndex(uint32(details.SwIfIndex)) 973 if !found { 974 h.log.Warnf("Interface with index %d not found in the mapping", details.SwIfIndex) 975 } 976 natIfs = append(natIfs, &nat.Nat44Global_Interface{ 977 Name: ifName, 978 OutputFeature: true, 979 }) 980 } 981 } 982 } 983 } 984 985 func (h *NatVppHandler) nat44EdInterfaceDump() ([]*nat.Nat44Global_Interface, error) { 986 var natIfs []*nat.Nat44Global_Interface 987 988 // dump non-output NAT interfaces 989 req1 := &vpp_nat_ed.Nat44InterfaceDump{} 990 reqContext := h.callsChannel.SendMultiRequest(req1) 991 992 for { 993 msg := &vpp_nat_ed.Nat44InterfaceDetails{} 994 stop, err := reqContext.ReceiveReply(msg) 995 if err != nil { 996 return nil, fmt.Errorf("failed to dump NAT44 interface: %v", err) 997 } 998 if stop { 999 break 1000 } 1001 1002 // Find interface name 1003 ifName, _, found := h.ifIndexes.LookupBySwIfIndex(uint32(msg.SwIfIndex)) 1004 if !found { 1005 h.log.Warnf("Interface with index %d not found in the mapping", msg.SwIfIndex) 1006 continue 1007 } 1008 1009 flags := getNat44Flags(msg.Flags) 1010 1011 if flags.isInside { 1012 natIfs = append(natIfs, &nat.Nat44Global_Interface{ 1013 Name: ifName, 1014 IsInside: true, 1015 }) 1016 } else { 1017 natIfs = append(natIfs, &nat.Nat44Global_Interface{ 1018 Name: ifName, 1019 IsInside: false, 1020 }) 1021 } 1022 } 1023 1024 // dump output NAT interfaces 1025 var cursor uint32 1026 for { 1027 rpcServ, err := h.natEd.Nat44EdOutputInterfaceGet(context.Background(), &vpp_nat_ed.Nat44EdOutputInterfaceGet{ 1028 Cursor: cursor, 1029 }) 1030 if err != nil { 1031 return nil, fmt.Errorf("failed to dump NAT44 interface output feature: %v", err) 1032 } 1033 RecvLoop: 1034 for { 1035 details, reply, err := rpcServ.Recv() 1036 if err != nil && err != io.EOF { 1037 return nil, fmt.Errorf("failed to dump NAT44 interface output feature: %v", err) 1038 } 1039 if reply != nil { 1040 // TODO: Possible bug in VPP? When VPP contains no NAT interfaces this 1041 // reply often returns cursor value of 0 repeatedly even though it should return 1042 // ^uint32(0). So if the reply contains cursor that is the same as cursor 1043 // from previous reply, return. 1044 if reply.Cursor == cursor || reply.Cursor == ^uint32(0) { 1045 return natIfs, nil 1046 } 1047 cursor = reply.Cursor 1048 break RecvLoop 1049 } 1050 if details != nil { 1051 ifName, _, found := h.ifIndexes.LookupBySwIfIndex(uint32(details.SwIfIndex)) 1052 if !found { 1053 h.log.Warnf("Interface with index %d not found in the mapping", details.SwIfIndex) 1054 } 1055 natIfs = append(natIfs, &nat.Nat44Global_Interface{ 1056 Name: ifName, 1057 OutputFeature: true, 1058 }) 1059 } 1060 } 1061 } 1062 } 1063 1064 // nat44InterfaceDump dumps NAT44 interface features. 1065 // Deprecated. Functionality moved to Nat44Nat44InterfacesDump. Kept for backward compatibility. 1066 func (h *NatVppHandler) nat44InterfaceDump() ([]*nat.Nat44Global_Interface, error) { 1067 if h.ed { 1068 return h.nat44EdInterfaceDump() 1069 } else { 1070 return h.nat44EiInterfaceDump() 1071 } 1072 } 1073 1074 func (h *NatVppHandler) getInterfaceIPAddresses(ifaceName string, ifaceMeta *ifaceidx.IfaceMetadata) (ipAddrs []string) { 1075 ipAddrNets := ifaceMeta.IPAddresses 1076 dhcpLease, hasDHCPLease := h.dhcpIndex.GetValue(ifaceName) 1077 if hasDHCPLease { 1078 lease := dhcpLease.(*ifs.DHCPLease) 1079 ipAddrNets = append(ipAddrNets, lease.HostIpAddress) 1080 } 1081 for _, ipAddrNet := range ipAddrNets { 1082 ipAddr := strings.Split(ipAddrNet, "/")[0] 1083 ipAddrs = append(ipAddrs, ipAddr) 1084 } 1085 return ipAddrs 1086 } 1087 1088 // protocolNumberToNBValue converts protocol numeric representation into the corresponding enum 1089 // enum value from the NB model. 1090 func (h *NatVppHandler) protocolNumberToNBValue(protocol uint8) (proto nat.DNat44_Protocol) { 1091 switch protocol { 1092 case TCP: 1093 return nat.DNat44_TCP 1094 case UDP: 1095 return nat.DNat44_UDP 1096 case ICMP: 1097 return nat.DNat44_ICMP 1098 default: 1099 h.log.Warnf("Unknown protocol %v", protocol) 1100 return 0 1101 } 1102 } 1103 1104 // protocolNBValueToNumber converts protocol enum value from the NB model into the 1105 // corresponding numeric representation. 1106 func (h *NatVppHandler) protocolNBValueToNumber(protocol nat.DNat44_Protocol) (proto uint8) { 1107 switch protocol { 1108 case nat.DNat44_TCP: 1109 return TCP 1110 case nat.DNat44_UDP: 1111 return UDP 1112 case nat.DNat44_ICMP: 1113 return ICMP 1114 default: 1115 h.log.Warnf("Unknown protocol %v, defaulting to TCP", protocol) 1116 return TCP 1117 } 1118 } 1119 1120 func (h *NatVppHandler) getTwiceNatMode(twiceNat, selfTwiceNat bool) nat.DNat44_StaticMapping_TwiceNatMode { 1121 if twiceNat { 1122 if selfTwiceNat { 1123 h.log.Warnf("Both TwiceNAT and self-TwiceNAT are enabled") 1124 return 0 1125 } 1126 return nat.DNat44_StaticMapping_ENABLED 1127 } 1128 if selfTwiceNat { 1129 return nat.DNat44_StaticMapping_SELF 1130 } 1131 return nat.DNat44_StaticMapping_DISABLED 1132 } 1133 1134 func getOrCreateDNAT(dnats dnatMap, label string) *nat.DNat44 { 1135 if _, created := dnats[label]; !created { 1136 dnats[label] = &nat.DNat44{Label: label} 1137 } 1138 return dnats[label] 1139 } 1140 1141 func getNat44Flags(flags nat_types.NatConfigFlags) *nat44EdFlags { 1142 natFlags := &nat44EdFlags{} 1143 if flags&nat_types.NAT_IS_EXT_HOST_VALID != 0 { 1144 natFlags.isExtHostValid = true 1145 } 1146 if flags&nat_types.NAT_IS_STATIC != 0 { 1147 natFlags.isStatic = true 1148 } 1149 if flags&nat_types.NAT_IS_INSIDE != 0 { 1150 natFlags.isInside = true 1151 } 1152 if flags&nat_types.NAT_IS_OUTSIDE != 0 { 1153 natFlags.isOutside = true 1154 } 1155 if flags&nat_types.NAT_IS_ADDR_ONLY != 0 { 1156 natFlags.isAddrOnly = true 1157 } 1158 if flags&nat_types.NAT_IS_OUT2IN_ONLY != 0 { 1159 natFlags.isOut2In = true 1160 } 1161 if flags&nat_types.NAT_IS_SELF_TWICE_NAT != 0 { 1162 natFlags.isSelfTwiceNat = true 1163 } 1164 if flags&nat_types.NAT_IS_TWICE_NAT != 0 { 1165 natFlags.isTwiceNat = true 1166 } 1167 return natFlags 1168 } 1169 1170 func getNat44EiFlags(flags vpp_nat_ei.Nat44EiConfigFlags) *nat44EiFlags { 1171 natFlags := &nat44EiFlags{} 1172 if flags&vpp_nat_ei.NAT44_EI_STATIC_MAPPING_ONLY != 0 { 1173 natFlags.eiStaticMappingOnly = true 1174 } 1175 if flags&vpp_nat_ei.NAT44_EI_CONNECTION_TRACKING != 0 { 1176 natFlags.eiConnectionTracking = true 1177 } 1178 if flags&vpp_nat_ei.NAT44_EI_OUT2IN_DPO != 0 { 1179 natFlags.eiOut2InDpo = true 1180 } 1181 if flags&vpp_nat_ei.NAT44_EI_ADDR_ONLY_MAPPING != 0 { 1182 natFlags.eiAddrOnlyMapping = true 1183 } 1184 if flags&vpp_nat_ei.NAT44_EI_IF_INSIDE != 0 { 1185 natFlags.eiIfInside = true 1186 } 1187 if flags&vpp_nat_ei.NAT44_EI_IF_OUTSIDE != 0 { 1188 natFlags.eiIfOutside = true 1189 } 1190 if flags&vpp_nat_ei.NAT44_EI_STATIC_MAPPING != 0 { 1191 natFlags.eiStaticMapping = true 1192 } 1193 return natFlags 1194 } 1195 1196 // incIP increments IP address and returns it. 1197 // Based on: https://play.golang.org/p/m8TNTtygK0 1198 func incIP(ip net.IP) net.IP { 1199 retIP := make(net.IP, len(ip)) 1200 copy(retIP, ip) 1201 for j := len(retIP) - 1; j >= 0; j-- { 1202 retIP[j]++ 1203 if retIP[j] > 0 { 1204 break 1205 } 1206 } 1207 return retIP 1208 }