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