go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/natplugin/vppcalls/vpp2101/dump_nat_vppcalls.go (about)

     1  //  Copyright (c) 2019 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 vpp2101
    16  
    17  import (
    18  	"fmt"
    19  	"net"
    20  	"sort"
    21  	"strings"
    22  
    23  	"google.golang.org/protobuf/proto"
    24  
    25  	vpp_nat "go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2101/nat44"
    26  	"go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2101/nat_types"
    27  	"go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/ifaceidx"
    28  	ifs "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/interfaces"
    29  	nat "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/nat"
    30  )
    31  
    32  // DNATs sorted by tags
    33  type dnatMap map[string]*nat.DNat44
    34  
    35  // static mappings sorted by tags
    36  type stMappingMap map[string][]*nat.DNat44_StaticMapping
    37  
    38  // identity mappings sorted by tags
    39  type idMappingMap map[string][]*nat.DNat44_IdentityMapping
    40  
    41  // WithLegacyStartupConf returns true if the loaded VPP NAT plugin is still using
    42  // the legacy startup NAT configuration (this is the case for VPP <= 20.09).
    43  func (h *NatVppHandler) WithLegacyStartupConf() bool {
    44  	return false
    45  }
    46  
    47  func (h *NatVppHandler) DefaultNat44GlobalConfig() *nat.Nat44Global {
    48  	return &nat.Nat44Global{
    49  		Forwarding:          false,
    50  		EndpointIndependent: true,
    51  		NatInterfaces:       nil,
    52  		AddressPool:         nil,
    53  		VirtualReassembly:   nil, // VirtualReassembly is not part of NAT API in VPP 20.01+ anymore
    54  	}
    55  }
    56  
    57  // Nat44GlobalConfigDump dumps global NAT44 config in NB format.
    58  func (h *NatVppHandler) Nat44GlobalConfigDump(dumpDeprecated bool) (cfg *nat.Nat44Global, err error) {
    59  	cfg = &nat.Nat44Global{}
    60  	req := &vpp_nat.Nat44ShowRunningConfig{}
    61  	reply := &vpp_nat.Nat44ShowRunningConfigReply{}
    62  	if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
    63  		return nil, err
    64  	}
    65  	cfg.EndpointIndependent = reply.Flags&vpp_nat.NAT44_IS_ENDPOINT_DEPENDENT == 0
    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  // DNat44Dump dumps all configured DNAT-44 configurations ordered by label.
    85  func (h *NatVppHandler) DNat44Dump() (dnats []*nat.DNat44, err error) {
    86  	dnatMap := make(dnatMap)
    87  
    88  	// Static mappings
    89  	natStMappings, err := h.nat44StaticMappingDump()
    90  	if err != nil {
    91  		return nil, fmt.Errorf("failed to dump NAT44 static mappings: %v", err)
    92  	}
    93  	for label, mappings := range natStMappings {
    94  		dnat := getOrCreateDNAT(dnatMap, label)
    95  		dnat.StMappings = append(dnat.StMappings, mappings...)
    96  	}
    97  
    98  	// Static mappings with load balancer
    99  	natStLbMappings, err := h.nat44StaticMappingLbDump()
   100  	if err != nil {
   101  		return nil, fmt.Errorf("failed to dump NAT44 static mappings with load balancer: %v", err)
   102  	}
   103  	for label, mappings := range natStLbMappings {
   104  		dnat := getOrCreateDNAT(dnatMap, label)
   105  		dnat.StMappings = append(dnat.StMappings, mappings...)
   106  	}
   107  
   108  	// Identity mappings
   109  	natIDMappings, err := h.nat44IdentityMappingDump()
   110  	if err != nil {
   111  		return nil, fmt.Errorf("failed to dump NAT44 identity mappings: %v", err)
   112  	}
   113  	for label, mappings := range natIDMappings {
   114  		dnat := getOrCreateDNAT(dnatMap, label)
   115  		dnat.IdMappings = append(dnat.IdMappings, mappings...)
   116  	}
   117  
   118  	// Convert map of DNAT configurations into a list.
   119  	for _, dnat := range dnatMap {
   120  		dnats = append(dnats, dnat)
   121  	}
   122  
   123  	// sort to simplify testing
   124  	sort.Slice(dnats, func(i, j int) bool { return dnats[i].Label < dnats[j].Label })
   125  
   126  	return dnats, nil
   127  }
   128  
   129  // Nat44InterfacesDump dumps NAT44 config of all NAT44-enabled interfaces.
   130  func (h *NatVppHandler) Nat44InterfacesDump() (natIfs []*nat.Nat44Interface, err error) {
   131  
   132  	// dump NAT interfaces without output feature enabled
   133  	req1 := &vpp_nat.Nat44InterfaceDump{}
   134  	reqContext := h.callsChannel.SendMultiRequest(req1)
   135  	for {
   136  		msg := &vpp_nat.Nat44InterfaceDetails{}
   137  		stop, err := reqContext.ReceiveReply(msg)
   138  		if err != nil {
   139  			return nil, fmt.Errorf("failed to dump NAT44 interface: %v", err)
   140  		}
   141  		if stop {
   142  			break
   143  		}
   144  		ifName, _, found := h.ifIndexes.LookupBySwIfIndex(uint32(msg.SwIfIndex))
   145  		if !found {
   146  			h.log.Warnf("Interface with index %d not found in the mapping", msg.SwIfIndex)
   147  			continue
   148  		}
   149  		flags := getNat44Flags(msg.Flags)
   150  		natIf := &nat.Nat44Interface{
   151  			Name:          ifName,
   152  			NatInside:     flags.isInside,
   153  			NatOutside:    flags.isOutside,
   154  			OutputFeature: false,
   155  		}
   156  		natIfs = append(natIfs, natIf)
   157  	}
   158  
   159  	// dump interfaces with output feature enabled
   160  	req2 := &vpp_nat.Nat44InterfaceOutputFeatureDump{}
   161  	reqContext = h.callsChannel.SendMultiRequest(req2)
   162  	for {
   163  		msg := &vpp_nat.Nat44InterfaceOutputFeatureDetails{}
   164  		stop, err := reqContext.ReceiveReply(msg)
   165  		if err != nil {
   166  			return nil, fmt.Errorf("failed to dump NAT44 interface output feature: %v", err)
   167  		}
   168  		if stop {
   169  			break
   170  		}
   171  		ifName, _, found := h.ifIndexes.LookupBySwIfIndex(uint32(msg.SwIfIndex))
   172  		if !found {
   173  			h.log.Warnf("Interface with index %d not found in the mapping", msg.SwIfIndex)
   174  			continue
   175  		}
   176  		flags := getNat44Flags(msg.Flags)
   177  		natIf := &nat.Nat44Interface{
   178  			Name:          ifName,
   179  			NatInside:     flags.isInside,
   180  			NatOutside:    flags.isOutside,
   181  			OutputFeature: true,
   182  		}
   183  		if !natIf.NatInside && !natIf.NatOutside {
   184  			natIf.NatOutside = true
   185  		}
   186  		natIfs = append(natIfs, natIf)
   187  	}
   188  	return
   189  }
   190  
   191  // Nat44AddressPoolsDump dumps all configured NAT44 address pools.
   192  func (h *NatVppHandler) Nat44AddressPoolsDump() (natPools []*nat.Nat44AddressPool, err error) {
   193  	var curPool *nat.Nat44AddressPool
   194  	var lastIP net.IP
   195  
   196  	req := &vpp_nat.Nat44AddressDump{}
   197  	reqContext := h.callsChannel.SendMultiRequest(req)
   198  
   199  	for {
   200  		msg := &vpp_nat.Nat44AddressDetails{}
   201  		stop, err := reqContext.ReceiveReply(msg)
   202  		if err != nil {
   203  			return nil, fmt.Errorf("failed to dump NAT44 Address pool: %v", err)
   204  		}
   205  		if stop {
   206  			break
   207  		}
   208  		ip := net.IP(msg.IPAddress[:])
   209  		isTwiceNat := getNat44Flags(msg.Flags).isTwiceNat
   210  		// merge subsequent IPs into a single pool
   211  		if curPool != nil && curPool.VrfId == msg.VrfID && curPool.TwiceNat == isTwiceNat && ip.Equal(incIP(lastIP)) {
   212  			// update current pool
   213  			curPool.LastIp = ip.String()
   214  		} else {
   215  			// start a new pool
   216  			pool := &nat.Nat44AddressPool{
   217  				FirstIp:  ip.String(),
   218  				VrfId:    msg.VrfID,
   219  				TwiceNat: isTwiceNat,
   220  			}
   221  			curPool = pool
   222  			natPools = append(natPools, pool)
   223  		}
   224  		lastIP = ip
   225  	}
   226  	return
   227  }
   228  
   229  // nat44AddressDump returns NAT44 address pool configured in the VPP.
   230  // Deprecated. Functionality moved to Nat44AddressPoolsDump. Kept for backward compatibility.
   231  func (h *NatVppHandler) nat44AddressDump() (addressPool []*nat.Nat44Global_Address, err error) {
   232  	req := &vpp_nat.Nat44AddressDump{}
   233  	reqContext := h.callsChannel.SendMultiRequest(req)
   234  
   235  	for {
   236  		msg := &vpp_nat.Nat44AddressDetails{}
   237  		stop, err := reqContext.ReceiveReply(msg)
   238  		if err != nil {
   239  			return nil, fmt.Errorf("failed to dump NAT44 Address pool: %v", err)
   240  		}
   241  		if stop {
   242  			break
   243  		}
   244  
   245  		addressPool = append(addressPool, &nat.Nat44Global_Address{
   246  			Address:  net.IP(msg.IPAddress[:]).String(),
   247  			VrfId:    msg.VrfID,
   248  			TwiceNat: getNat44Flags(msg.Flags).isTwiceNat,
   249  		})
   250  	}
   251  
   252  	return
   253  }
   254  
   255  // virtualReassemblyDump returns current NAT virtual-reassembly configuration.
   256  func (h *NatVppHandler) virtualReassemblyDump() (vrIPv4 *nat.VirtualReassembly, vrIPv6 *nat.VirtualReassembly, err error) {
   257  	/*ipv4vr, err := h.ip.IPReassemblyGet(context.TODO(), &vpp_ip.IPReassemblyGet{IsIP6: false})
   258  	if err != nil {
   259  		return nil, nil, fmt.Errorf("getting virtual reassembly IPv4 config failed: %w", err)
   260  	}
   261  	h.log.Debugf("IP Reassembly config IPv4: %+v\n", ipv4vr)
   262  	ipv6vr, err := h.ip.IPReassemblyGet(context.TODO(), &vpp_ip.IPReassemblyGet{IsIP6: true})
   263  	if err != nil {
   264  		return nil, nil, fmt.Errorf("getting virtual reassembly IPv6 config failed: %w", err)
   265  	}
   266  	h.log.Debugf("IP Reassembly config IPv6: %+v\n", ipv6vr)*/
   267  
   268  	// Virtual Reassembly has been removed from NAT API in VPP (moved to IP API)
   269  	// TODO: define IPReassembly model in L3 plugin
   270  	return nil, nil, nil
   271  	/*vrIPv4 = &nat.VirtualReassembly{
   272  		Timeout:         reply.IP4Timeout,
   273  		MaxReassemblies: uint32(reply.IP4MaxReass),
   274  		MaxFragments:    uint32(reply.IP4MaxFrag),
   275  		DropFragments:   uintToBool(reply.IP4DropFrag),
   276  	}
   277  	vrIPv6 = &nat.VirtualReassembly{
   278  		Timeout:         reply.IP6Timeout,
   279  		MaxReassemblies: uint32(reply.IP6MaxReass),
   280  		MaxFragments:    uint32(reply.IP6MaxFrag),
   281  		DropFragments:   uintToBool(reply.IP6DropFrag),
   282  	}
   283  	return*/
   284  }
   285  
   286  // nat44StaticMappingDump returns a map of NAT44 static mappings sorted by tags
   287  func (h *NatVppHandler) nat44StaticMappingDump() (entries stMappingMap, err error) {
   288  	entries = make(stMappingMap)
   289  	childMappings := make(stMappingMap)
   290  	req := &vpp_nat.Nat44StaticMappingDump{}
   291  	reqContext := h.callsChannel.SendMultiRequest(req)
   292  
   293  	for {
   294  		msg := &vpp_nat.Nat44StaticMappingDetails{}
   295  		stop, err := reqContext.ReceiveReply(msg)
   296  		if err != nil {
   297  			return nil, fmt.Errorf("failed to dump NAT44 static mapping: %v", err)
   298  		}
   299  		if stop {
   300  			break
   301  		}
   302  		lcIPAddress := net.IP(msg.LocalIPAddress[:]).String()
   303  		exIPAddress := net.IP(msg.ExternalIPAddress[:]).String()
   304  
   305  		// Parse tag (DNAT label)
   306  		tag := strings.TrimRight(msg.Tag, "\x00")
   307  		if _, hasTag := entries[tag]; !hasTag {
   308  			entries[tag] = []*nat.DNat44_StaticMapping{}
   309  			childMappings[tag] = []*nat.DNat44_StaticMapping{}
   310  		}
   311  
   312  		// resolve interface name
   313  		var (
   314  			found        bool
   315  			extIfaceName string
   316  			extIfaceMeta *ifaceidx.IfaceMetadata
   317  		)
   318  		if msg.ExternalSwIfIndex != NoInterface {
   319  			extIfaceName, extIfaceMeta, found = h.ifIndexes.LookupBySwIfIndex(uint32(msg.ExternalSwIfIndex))
   320  			if !found {
   321  				h.log.Warnf("Interface with index %v not found in the mapping", msg.ExternalSwIfIndex)
   322  				continue
   323  			}
   324  		}
   325  
   326  		flags := getNat44Flags(msg.Flags)
   327  
   328  		// Add mapping into the map.
   329  		mapping := &nat.DNat44_StaticMapping{
   330  			ExternalInterface: extIfaceName,
   331  			ExternalIp:        exIPAddress,
   332  			ExternalPort:      uint32(msg.ExternalPort),
   333  			LocalIps: []*nat.DNat44_StaticMapping_LocalIP{ // single-value
   334  				{
   335  					VrfId:     msg.VrfID,
   336  					LocalIp:   lcIPAddress,
   337  					LocalPort: uint32(msg.LocalPort),
   338  				},
   339  			},
   340  			Protocol: h.protocolNumberToNBValue(msg.Protocol),
   341  			TwiceNat: h.getTwiceNatMode(flags.isTwiceNat, flags.isSelfTwiceNat),
   342  			// if there is only one backend the affinity can not be set
   343  			SessionAffinity: 0,
   344  		}
   345  		entries[tag] = append(entries[tag], mapping)
   346  
   347  		if msg.ExternalSwIfIndex != NoInterface {
   348  			// collect auto-generated "child" mappings (interface replaced with every assigned IP address)
   349  			for _, ipAddr := range h.getInterfaceIPAddresses(extIfaceName, extIfaceMeta) {
   350  				childMapping := proto.Clone(mapping).(*nat.DNat44_StaticMapping)
   351  				childMapping.ExternalIp = ipAddr
   352  				childMapping.ExternalInterface = ""
   353  				childMappings[tag] = append(childMappings[tag], childMapping)
   354  			}
   355  		}
   356  	}
   357  
   358  	// do not dump auto-generated child mappings
   359  	for tag, mappings := range entries {
   360  		var filtered []*nat.DNat44_StaticMapping
   361  		for _, mapping := range mappings {
   362  			isChild := false
   363  			for _, child := range childMappings[tag] {
   364  				if proto.Equal(mapping, child) {
   365  					isChild = true
   366  					break
   367  				}
   368  			}
   369  			if !isChild {
   370  				filtered = append(filtered, mapping)
   371  			}
   372  		}
   373  		entries[tag] = filtered
   374  	}
   375  	return entries, nil
   376  }
   377  
   378  // nat44StaticMappingLbDump returns a map of NAT44 static mapping with load balancing sorted by tags.
   379  func (h *NatVppHandler) nat44StaticMappingLbDump() (entries stMappingMap, err error) {
   380  	entries = make(stMappingMap)
   381  	req := &vpp_nat.Nat44LbStaticMappingDump{}
   382  	reqContext := h.callsChannel.SendMultiRequest(req)
   383  
   384  	for {
   385  		msg := &vpp_nat.Nat44LbStaticMappingDetails{}
   386  		stop, err := reqContext.ReceiveReply(msg)
   387  		if err != nil {
   388  			return nil, fmt.Errorf("failed to dump NAT44 lb-static mapping: %v", err)
   389  		}
   390  		if stop {
   391  			break
   392  		}
   393  
   394  		// Parse tag (DNAT label)
   395  		tag := strings.TrimRight(msg.Tag, "\x00")
   396  		if _, hasTag := entries[tag]; !hasTag {
   397  			entries[tag] = []*nat.DNat44_StaticMapping{}
   398  		}
   399  
   400  		// Prepare localIPs
   401  		var locals []*nat.DNat44_StaticMapping_LocalIP
   402  		for _, localIPVal := range msg.Locals {
   403  			locals = append(locals, &nat.DNat44_StaticMapping_LocalIP{
   404  				VrfId:       localIPVal.VrfID,
   405  				LocalIp:     net.IP(localIPVal.Addr[:]).String(),
   406  				LocalPort:   uint32(localIPVal.Port),
   407  				Probability: uint32(localIPVal.Probability),
   408  			})
   409  		}
   410  		exIPAddress := net.IP(msg.ExternalAddr[:]).String()
   411  
   412  		flags := getNat44Flags(msg.Flags)
   413  
   414  		// Add mapping into the map.
   415  		mapping := &nat.DNat44_StaticMapping{
   416  			ExternalIp:      exIPAddress,
   417  			ExternalPort:    uint32(msg.ExternalPort),
   418  			LocalIps:        locals,
   419  			Protocol:        h.protocolNumberToNBValue(msg.Protocol),
   420  			TwiceNat:        h.getTwiceNatMode(flags.isTwiceNat, flags.isSelfTwiceNat),
   421  			SessionAffinity: msg.Affinity,
   422  		}
   423  		entries[tag] = append(entries[tag], mapping)
   424  	}
   425  
   426  	return entries, nil
   427  }
   428  
   429  // nat44IdentityMappingDump returns a map of NAT44 identity mappings sorted by tags.
   430  func (h *NatVppHandler) nat44IdentityMappingDump() (entries idMappingMap, err error) {
   431  	entries = make(idMappingMap)
   432  	childMappings := make(idMappingMap)
   433  	req := &vpp_nat.Nat44IdentityMappingDump{}
   434  	reqContext := h.callsChannel.SendMultiRequest(req)
   435  
   436  	for {
   437  		msg := &vpp_nat.Nat44IdentityMappingDetails{}
   438  		stop, err := reqContext.ReceiveReply(msg)
   439  		if err != nil {
   440  			return nil, fmt.Errorf("failed to dump NAT44 identity mapping: %v", err)
   441  		}
   442  		if stop {
   443  			break
   444  		}
   445  
   446  		// Parse tag (DNAT label)
   447  		tag := strings.TrimRight(msg.Tag, "\x00")
   448  		if _, hasTag := entries[tag]; !hasTag {
   449  			entries[tag] = []*nat.DNat44_IdentityMapping{}
   450  			childMappings[tag] = []*nat.DNat44_IdentityMapping{}
   451  		}
   452  
   453  		// resolve interface name
   454  		var (
   455  			found     bool
   456  			ifaceName string
   457  			ifaceMeta *ifaceidx.IfaceMetadata
   458  		)
   459  		if msg.SwIfIndex != NoInterface {
   460  			ifaceName, ifaceMeta, found = h.ifIndexes.LookupBySwIfIndex(uint32(msg.SwIfIndex))
   461  			if !found {
   462  				h.log.Warnf("Interface with index %v not found in the mapping", msg.SwIfIndex)
   463  				continue
   464  			}
   465  		}
   466  
   467  		// Add mapping into the map.
   468  		mapping := &nat.DNat44_IdentityMapping{
   469  			IpAddress: net.IP(msg.IPAddress[:]).String(),
   470  			VrfId:     msg.VrfID,
   471  			Interface: ifaceName,
   472  			Port:      uint32(msg.Port),
   473  			Protocol:  h.protocolNumberToNBValue(msg.Protocol),
   474  		}
   475  		entries[tag] = append(entries[tag], mapping)
   476  
   477  		if msg.SwIfIndex != NoInterface {
   478  			// collect auto-generated "child" mappings (interface replaced with every assigned IP address)
   479  			for _, ipAddr := range h.getInterfaceIPAddresses(ifaceName, ifaceMeta) {
   480  				childMapping := proto.Clone(mapping).(*nat.DNat44_IdentityMapping)
   481  				childMapping.IpAddress = ipAddr
   482  				childMapping.Interface = ""
   483  				childMappings[tag] = append(childMappings[tag], childMapping)
   484  			}
   485  		}
   486  	}
   487  
   488  	// do not dump auto-generated child mappings
   489  	for tag, mappings := range entries {
   490  		var filtered []*nat.DNat44_IdentityMapping
   491  		for _, mapping := range mappings {
   492  			isChild := false
   493  			for _, child := range childMappings[tag] {
   494  				if proto.Equal(mapping, child) {
   495  					isChild = true
   496  					break
   497  				}
   498  			}
   499  			if !isChild {
   500  				filtered = append(filtered, mapping)
   501  			}
   502  		}
   503  		entries[tag] = filtered
   504  	}
   505  
   506  	return entries, nil
   507  }
   508  
   509  // nat44InterfaceDump dumps NAT44 interface features.
   510  // Deprecated. Functionality moved to Nat44Nat44InterfacesDump. Kept for backward compatibility.
   511  func (h *NatVppHandler) nat44InterfaceDump() (interfaces []*nat.Nat44Global_Interface, err error) {
   512  
   513  	/* dump non-Output interfaces first */
   514  	req1 := &vpp_nat.Nat44InterfaceDump{}
   515  	reqContext := h.callsChannel.SendMultiRequest(req1)
   516  
   517  	for {
   518  		msg := &vpp_nat.Nat44InterfaceDetails{}
   519  		stop, err := reqContext.ReceiveReply(msg)
   520  		if err != nil {
   521  			return nil, fmt.Errorf("failed to dump NAT44 interface: %v", err)
   522  		}
   523  		if stop {
   524  			break
   525  		}
   526  
   527  		// Find interface name
   528  		ifName, _, found := h.ifIndexes.LookupBySwIfIndex(uint32(msg.SwIfIndex))
   529  		if !found {
   530  			h.log.Warnf("Interface with index %d not found in the mapping", msg.SwIfIndex)
   531  			continue
   532  		}
   533  
   534  		flags := getNat44Flags(msg.Flags)
   535  
   536  		if flags.isInside {
   537  			interfaces = append(interfaces, &nat.Nat44Global_Interface{
   538  				Name:     ifName,
   539  				IsInside: true,
   540  			})
   541  		} else {
   542  			interfaces = append(interfaces, &nat.Nat44Global_Interface{
   543  				Name:     ifName,
   544  				IsInside: false,
   545  			})
   546  		}
   547  	}
   548  
   549  	/* dump Output interfaces next */
   550  	req2 := &vpp_nat.Nat44InterfaceOutputFeatureDump{}
   551  	reqContext = h.callsChannel.SendMultiRequest(req2)
   552  
   553  	for {
   554  		msg := &vpp_nat.Nat44InterfaceOutputFeatureDetails{}
   555  		stop, err := reqContext.ReceiveReply(msg)
   556  		if err != nil {
   557  			return nil, fmt.Errorf("failed to dump NAT44 interface output feature: %v", err)
   558  		}
   559  		if stop {
   560  			break
   561  		}
   562  
   563  		// Find interface name
   564  		ifName, _, found := h.ifIndexes.LookupBySwIfIndex(uint32(msg.SwIfIndex))
   565  		if !found {
   566  			h.log.Warnf("Interface with index %d not found in the mapping", msg.SwIfIndex)
   567  			continue
   568  		}
   569  
   570  		flags := getNat44Flags(msg.Flags)
   571  
   572  		interfaces = append(interfaces, &nat.Nat44Global_Interface{
   573  			Name:          ifName,
   574  			IsInside:      flags.isInside,
   575  			OutputFeature: true,
   576  		})
   577  	}
   578  
   579  	return interfaces, nil
   580  }
   581  
   582  func (h *NatVppHandler) getInterfaceIPAddresses(ifaceName string, ifaceMeta *ifaceidx.IfaceMetadata) (ipAddrs []string) {
   583  	ipAddrNets := ifaceMeta.IPAddresses
   584  	dhcpLease, hasDHCPLease := h.dhcpIndex.GetValue(ifaceName)
   585  	if hasDHCPLease {
   586  		lease := dhcpLease.(*ifs.DHCPLease)
   587  		ipAddrNets = append(ipAddrNets, lease.HostIpAddress)
   588  	}
   589  	for _, ipAddrNet := range ipAddrNets {
   590  		ipAddr := strings.Split(ipAddrNet, "/")[0]
   591  		ipAddrs = append(ipAddrs, ipAddr)
   592  	}
   593  	return ipAddrs
   594  }
   595  
   596  // protocolNumberToNBValue converts protocol numeric representation into the corresponding enum
   597  // enum value from the NB model.
   598  func (h *NatVppHandler) protocolNumberToNBValue(protocol uint8) (proto nat.DNat44_Protocol) {
   599  	switch protocol {
   600  	case TCP:
   601  		return nat.DNat44_TCP
   602  	case UDP:
   603  		return nat.DNat44_UDP
   604  	case ICMP:
   605  		return nat.DNat44_ICMP
   606  	default:
   607  		h.log.Warnf("Unknown protocol %v", protocol)
   608  		return 0
   609  	}
   610  }
   611  
   612  // protocolNBValueToNumber converts protocol enum value from the NB model into the
   613  // corresponding numeric representation.
   614  func (h *NatVppHandler) protocolNBValueToNumber(protocol nat.DNat44_Protocol) (proto uint8) {
   615  	switch protocol {
   616  	case nat.DNat44_TCP:
   617  		return TCP
   618  	case nat.DNat44_UDP:
   619  		return UDP
   620  	case nat.DNat44_ICMP:
   621  		return ICMP
   622  	default:
   623  		h.log.Warnf("Unknown protocol %v, defaulting to TCP", protocol)
   624  		return TCP
   625  	}
   626  }
   627  
   628  func (h *NatVppHandler) getTwiceNatMode(twiceNat, selfTwiceNat bool) nat.DNat44_StaticMapping_TwiceNatMode {
   629  	if twiceNat {
   630  		if selfTwiceNat {
   631  			h.log.Warnf("Both TwiceNAT and self-TwiceNAT are enabled")
   632  			return 0
   633  		}
   634  		return nat.DNat44_StaticMapping_ENABLED
   635  	}
   636  	if selfTwiceNat {
   637  		return nat.DNat44_StaticMapping_SELF
   638  	}
   639  	return nat.DNat44_StaticMapping_DISABLED
   640  }
   641  
   642  func getOrCreateDNAT(dnats dnatMap, label string) *nat.DNat44 {
   643  	if _, created := dnats[label]; !created {
   644  		dnats[label] = &nat.DNat44{Label: label}
   645  	}
   646  	return dnats[label]
   647  }
   648  
   649  func getNat44Flags(flags nat_types.NatConfigFlags) *nat44Flags {
   650  	natFlags := &nat44Flags{}
   651  	if flags&nat_types.NAT_IS_EXT_HOST_VALID != 0 {
   652  		natFlags.isExtHostValid = true
   653  	}
   654  	if flags&nat_types.NAT_IS_STATIC != 0 {
   655  		natFlags.isStatic = true
   656  	}
   657  	if flags&nat_types.NAT_IS_INSIDE != 0 {
   658  		natFlags.isInside = true
   659  	}
   660  	if flags&nat_types.NAT_IS_OUTSIDE != 0 {
   661  		natFlags.isOutside = true
   662  	}
   663  	if flags&nat_types.NAT_IS_ADDR_ONLY != 0 {
   664  		natFlags.isAddrOnly = true
   665  	}
   666  	if flags&nat_types.NAT_IS_OUT2IN_ONLY != 0 {
   667  		natFlags.isOut2In = true
   668  	}
   669  	if flags&nat_types.NAT_IS_SELF_TWICE_NAT != 0 {
   670  		natFlags.isSelfTwiceNat = true
   671  	}
   672  	if flags&nat_types.NAT_IS_TWICE_NAT != 0 {
   673  		natFlags.isTwiceNat = true
   674  	}
   675  	return natFlags
   676  }
   677  
   678  // incIP increments IP address and returns it.
   679  // Based on: https://play.golang.org/p/m8TNTtygK0
   680  func incIP(ip net.IP) net.IP {
   681  	retIP := make(net.IP, len(ip))
   682  	copy(retIP, ip)
   683  	for j := len(retIP) - 1; j >= 0; j-- {
   684  		retIP[j]++
   685  		if retIP[j] > 0 {
   686  			break
   687  		}
   688  	}
   689  	return retIP
   690  }