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

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