go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/natplugin/vppcalls/vpp2106/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  
    21  	"go.fd.io/govpp/api"
    22  
    23  	"github.com/pkg/errors"
    24  
    25  	"go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2106/interface_types"
    26  	"go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2106/ip_types"
    27  	vpp_nat_ed "go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2106/nat44_ed"
    28  	vpp_nat_ei "go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2106/nat44_ei"
    29  	"go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2106/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.Nat44InterfaceAddDelOutputFeature{
   332  		SwIfIndex: interface_types.InterfaceIndex(ifaceMeta.SwIfIndex),
   333  		Flags:     setNat44EdFlags(&nat44EdFlags{isInside: isInside}),
   334  		IsAdd:     isAdd,
   335  	}
   336  	reply := &vpp_nat_ed.Nat44InterfaceAddDelOutputFeatureReply{}
   337  
   338  	if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   339  		return err
   340  	}
   341  
   342  	return nil
   343  }
   344  
   345  func (h *NatVppHandler) handleNat44EiInterfaceOutputFeature(iface string, isInside, isAdd bool) error {
   346  	// get interface metadata
   347  	ifaceMeta, found := h.ifIndexes.LookupByName(iface)
   348  	if !found {
   349  		return errors.New("failed to get interface metadata")
   350  	}
   351  
   352  	req := &vpp_nat_ei.Nat44EiInterfaceAddDelFeature{
   353  		SwIfIndex: interface_types.InterfaceIndex(ifaceMeta.SwIfIndex),
   354  		Flags:     setNat44EiFlags(&nat44EiFlags{eiIfInside: isInside}),
   355  		IsAdd:     isAdd,
   356  	}
   357  	reply := &vpp_nat_ei.Nat44EiInterfaceAddDelFeatureReply{}
   358  
   359  	if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   360  		return err
   361  	}
   362  
   363  	return nil
   364  }
   365  
   366  // Calls VPP binary API to set/unset interface NAT44 output feature
   367  func (h *NatVppHandler) handleNat44InterfaceOutputFeature(iface string, isInside, isAdd bool) error {
   368  	if h.ed {
   369  		return h.handleNat44EdInterfaceOutputFeature(iface, isInside, isAdd)
   370  	} else {
   371  		return h.handleNat44EiInterfaceOutputFeature(iface, isInside, isAdd)
   372  	}
   373  }
   374  
   375  func (h *NatVppHandler) handleNat44EdAddressPool(vrf uint32, firstIP, lastIP string, twiceNat, isAdd bool) error {
   376  	firstAddr, err := ipTo4Address(firstIP)
   377  	if err != nil {
   378  		return errors.Errorf("unable to parse address %s from the NAT pool: %v", firstIP, err)
   379  	}
   380  	lastAddr := firstAddr
   381  	if lastIP != "" {
   382  		lastAddr, err = ipTo4Address(lastIP)
   383  		if err != nil {
   384  			return errors.Errorf("unable to parse address %s from the NAT pool: %v", lastIP, err)
   385  		}
   386  	}
   387  
   388  	req := &vpp_nat_ed.Nat44AddDelAddressRange{
   389  		FirstIPAddress: firstAddr,
   390  		LastIPAddress:  lastAddr,
   391  		VrfID:          vrf,
   392  		Flags:          setNat44EdFlags(&nat44EdFlags{isTwiceNat: twiceNat}),
   393  		IsAdd:          isAdd,
   394  	}
   395  	reply := &vpp_nat_ed.Nat44AddDelAddressRangeReply{}
   396  
   397  	if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   398  		return err
   399  	}
   400  
   401  	return nil
   402  }
   403  
   404  func (h *NatVppHandler) handleNat44EiAddressPool(vrf uint32, firstIP, lastIP string, twiceNat, isAdd bool) error {
   405  	firstAddr, err := ipTo4Address(firstIP)
   406  	if err != nil {
   407  		return errors.Errorf("unable to parse address %s from the NAT pool: %v", firstIP, err)
   408  	}
   409  	lastAddr := firstAddr
   410  	if lastIP != "" {
   411  		lastAddr, err = ipTo4Address(lastIP)
   412  		if err != nil {
   413  			return errors.Errorf("unable to parse address %s from the NAT pool: %v", lastIP, err)
   414  		}
   415  	}
   416  
   417  	req := &vpp_nat_ei.Nat44EiAddDelAddressRange{
   418  		FirstIPAddress: firstAddr,
   419  		LastIPAddress:  lastAddr,
   420  		VrfID:          vrf,
   421  		IsAdd:          isAdd,
   422  	}
   423  	reply := &vpp_nat_ei.Nat44EiAddDelAddressRangeReply{}
   424  
   425  	if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   426  		return err
   427  	}
   428  
   429  	return nil
   430  }
   431  
   432  // Calls VPP binary API to add/remove addresses to/from the NAT44 pool.
   433  func (h *NatVppHandler) handleNat44AddressPool(vrf uint32, firstIP, lastIP string, twiceNat, isAdd bool) error {
   434  	if h.ed {
   435  		return h.handleNat44EdAddressPool(vrf, firstIP, lastIP, twiceNat, isAdd)
   436  	} else {
   437  		return h.handleNat44EiAddressPool(vrf, firstIP, lastIP, twiceNat, isAdd)
   438  	}
   439  }
   440  
   441  // Calls VPP binary API to setup NAT virtual reassembly
   442  func (h *NatVppHandler) handleNatVirtualReassembly(vrCfg *nat.VirtualReassembly, isIpv6 bool) error {
   443  	// Virtual Reassembly has been removed from NAT API in VPP (moved to IP API)
   444  	// TODO: define IPReassembly model in L3 plugin
   445  	return nil
   446  	/*req := &vpp_nat.NatSetReass{
   447  	  	Timeout:  vrCfg.Timeout,
   448  	  	MaxReass: uint16(vrCfg.MaxReassemblies),
   449  	  	MaxFrag:  uint8(vrCfg.MaxFragments),
   450  	  	DropFrag: boolToUint(vrCfg.DropFragments),
   451  	  	IsIP6:    isIpv6,
   452  	  }
   453  	  reply := &vpp_nat.NatSetReassReply{}
   454  	  if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   455  	  	return err
   456  	  }*/
   457  }
   458  
   459  // Calls VPP binary API to add/remove NAT44 static mapping
   460  func (h *NatVppHandler) handleNat44StaticMapping(mapping *nat.DNat44_StaticMapping, dnatLabel string, isAdd bool) error {
   461  	var ifIdx = NoInterface
   462  	var exIPAddr ip_types.IP4Address
   463  
   464  	// check tag length limit
   465  	if err := checkTagLength(dnatLabel); err != nil {
   466  		return err
   467  	}
   468  
   469  	// parse local endpoint
   470  	lcIPAddr, err := ipTo4Address(mapping.LocalIps[0].LocalIp)
   471  	if err != nil {
   472  		return errors.Errorf("cannot configure DNAT static mapping %s: unable to parse local IP %s: %v",
   473  			dnatLabel, mapping.LocalIps[0].LocalIp, err)
   474  	}
   475  	lcPort := uint16(mapping.LocalIps[0].LocalPort)
   476  	lcVrf := mapping.LocalIps[0].VrfId
   477  
   478  	// Check external interface (prioritized over external IP)
   479  	if mapping.ExternalInterface != "" {
   480  		// Check external interface
   481  		ifMeta, found := h.ifIndexes.LookupByName(mapping.ExternalInterface)
   482  		if !found {
   483  			return errors.Errorf("cannot configure static mapping for DNAT %s: required external interface %s is missing",
   484  				dnatLabel, mapping.ExternalInterface)
   485  		}
   486  		ifIdx = interface_types.InterfaceIndex(ifMeta.SwIfIndex)
   487  	} else {
   488  		// Parse external IP address
   489  		exIPAddr, err = ipTo4Address(mapping.ExternalIp)
   490  		if err != nil {
   491  			return errors.Errorf("cannot configure static mapping for DNAT %s: unable to parse external IP %s: %v",
   492  				dnatLabel, mapping.ExternalIp, err)
   493  		}
   494  	}
   495  
   496  	// Resolve mapping (address only or address and port)
   497  	var addrOnly bool
   498  	if lcPort == 0 || mapping.ExternalPort == 0 {
   499  		addrOnly = true
   500  	}
   501  
   502  	if h.ed {
   503  		req := &vpp_nat_ed.Nat44AddDelStaticMappingV2{
   504  			Tag:               dnatLabel,
   505  			LocalIPAddress:    lcIPAddr,
   506  			ExternalIPAddress: exIPAddr,
   507  			Protocol:          h.protocolNBValueToNumber(mapping.Protocol),
   508  			ExternalSwIfIndex: ifIdx,
   509  			VrfID:             lcVrf,
   510  			Flags: setNat44EdFlags(&nat44EdFlags{
   511  				isTwiceNat:     mapping.TwiceNat == nat.DNat44_StaticMapping_ENABLED,
   512  				isSelfTwiceNat: mapping.TwiceNat == nat.DNat44_StaticMapping_SELF,
   513  				isOut2In:       true,
   514  				isAddrOnly:     addrOnly,
   515  			}),
   516  			IsAdd: isAdd,
   517  		}
   518  
   519  		if !addrOnly {
   520  			req.LocalPort = lcPort
   521  			req.ExternalPort = uint16(mapping.ExternalPort)
   522  		}
   523  
   524  		// Applying(if needed) the override of IP address picking from twice-NAT address pool
   525  		if mapping.TwiceNatPoolIp != "" {
   526  			req.MatchPool = true
   527  			req.PoolIPAddress, err = ipTo4Address(mapping.TwiceNatPoolIp)
   528  			if err != nil {
   529  				return errors.Errorf("cannot configure static mapping for DNAT %s: unable to parse "+
   530  					"twice-NAT pool IP %s: %v", dnatLabel, mapping.TwiceNatPoolIp, err)
   531  			}
   532  		}
   533  
   534  		reply := &vpp_nat_ed.Nat44AddDelStaticMappingV2Reply{}
   535  
   536  		if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   537  			return err
   538  		}
   539  	} else {
   540  		req := &vpp_nat_ei.Nat44EiAddDelStaticMapping{
   541  			Tag:               dnatLabel,
   542  			LocalIPAddress:    lcIPAddr,
   543  			ExternalIPAddress: exIPAddr,
   544  			Protocol:          h.protocolNBValueToNumber(mapping.Protocol),
   545  			ExternalSwIfIndex: ifIdx,
   546  			VrfID:             lcVrf,
   547  			Flags: setNat44EiFlags(&nat44EiFlags{
   548  				eiAddrOnlyMapping: addrOnly,
   549  			}),
   550  			IsAdd: isAdd,
   551  		}
   552  
   553  		if !addrOnly {
   554  			req.LocalPort = lcPort
   555  			req.ExternalPort = uint16(mapping.ExternalPort)
   556  		}
   557  
   558  		reply := &vpp_nat_ei.Nat44EiAddDelStaticMappingReply{}
   559  
   560  		if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   561  			return err
   562  		}
   563  	}
   564  
   565  	return nil
   566  }
   567  
   568  func (h *NatVppHandler) handleNat44EdStaticMappingLb(mapping *nat.DNat44_StaticMapping, dnatLabel string, isAdd bool) error {
   569  	// check tag length limit
   570  	if err := checkTagLength(dnatLabel); err != nil {
   571  		return err
   572  	}
   573  
   574  	// parse external IP address
   575  	exIPAddrByte, err := ipTo4Address(mapping.ExternalIp)
   576  	if err != nil {
   577  		return errors.Errorf("cannot configure LB static mapping for DNAT %s: unable to parse external IP %s: %v",
   578  			dnatLabel, mapping.ExternalIp, err)
   579  	}
   580  
   581  	// In this case, external port is required
   582  	if mapping.ExternalPort == 0 {
   583  		return errors.Errorf("cannot configure LB static mapping for DNAT %s: external port is not set", dnatLabel)
   584  	}
   585  
   586  	// Transform local IP/Ports
   587  	var locals []vpp_nat_ed.Nat44LbAddrPort
   588  	for _, local := range mapping.LocalIps {
   589  		if local.LocalPort == 0 {
   590  			return errors.Errorf("cannot set local IP/Port for DNAT mapping %s: port is missing",
   591  				dnatLabel)
   592  		}
   593  
   594  		localIP, err := ipTo4Address(local.LocalIp)
   595  		if err != nil {
   596  			return errors.Errorf("cannot set local IP/Port for DNAT mapping %s: unable to parse local IP %v: %v",
   597  				dnatLabel, local.LocalIp, err)
   598  		}
   599  
   600  		locals = append(locals, vpp_nat_ed.Nat44LbAddrPort{
   601  			Addr:        localIP,
   602  			Port:        uint16(local.LocalPort),
   603  			Probability: uint8(local.Probability),
   604  			VrfID:       local.VrfId,
   605  		})
   606  	}
   607  
   608  	req := &vpp_nat_ed.Nat44AddDelLbStaticMapping{
   609  		Tag:    dnatLabel,
   610  		Locals: locals,
   611  		// LocalNum:     uint32(len(locals)), // should not be needed (will be set by struc)
   612  		ExternalAddr: exIPAddrByte,
   613  		ExternalPort: uint16(mapping.ExternalPort),
   614  		Protocol:     h.protocolNBValueToNumber(mapping.Protocol),
   615  		Flags: setNat44EdFlags(&nat44EdFlags{
   616  			isTwiceNat:     mapping.TwiceNat == nat.DNat44_StaticMapping_ENABLED,
   617  			isSelfTwiceNat: mapping.TwiceNat == nat.DNat44_StaticMapping_SELF,
   618  			isOut2In:       true,
   619  		}),
   620  		IsAdd:    isAdd,
   621  		Affinity: mapping.SessionAffinity,
   622  	}
   623  
   624  	reply := &vpp_nat_ed.Nat44AddDelLbStaticMappingReply{}
   625  
   626  	if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   627  		return err
   628  	}
   629  
   630  	return nil
   631  }
   632  
   633  // Calls VPP binary API to add/remove NAT44 static mapping with load balancing.
   634  func (h *NatVppHandler) handleNat44StaticMappingLb(mapping *nat.DNat44_StaticMapping, dnatLabel string, isAdd bool) error {
   635  	if h.ed {
   636  		return h.handleNat44EdStaticMappingLb(mapping, dnatLabel, isAdd)
   637  	} else {
   638  		// no static mapping with load balancing implemented for EI nat yet
   639  		return nil
   640  	}
   641  }
   642  
   643  // Calls VPP binary API to add/remove NAT44 identity mapping.
   644  func (h *NatVppHandler) handleNat44IdentityMapping(mapping *nat.DNat44_IdentityMapping, dnatLabel string, isAdd bool) (err error) {
   645  	var ifIdx = NoInterface
   646  	var ipAddr ip_types.IP4Address
   647  
   648  	// check tag length limit
   649  	if err := checkTagLength(dnatLabel); err != nil {
   650  		return err
   651  	}
   652  
   653  	// get interface index
   654  	if mapping.Interface != "" {
   655  		ifMeta, found := h.ifIndexes.LookupByName(mapping.Interface)
   656  		if !found {
   657  			return errors.Errorf("failed to configure identity mapping for DNAT %s: addressed interface %s does not exist",
   658  				dnatLabel, mapping.Interface)
   659  		}
   660  		ifIdx = interface_types.InterfaceIndex(ifMeta.SwIfIndex)
   661  	}
   662  
   663  	if ifIdx == NoInterface {
   664  		// Case with IP (optionally port). Verify and parse input IP/port
   665  		ipAddr, err = ipTo4Address(mapping.IpAddress)
   666  		if err != nil {
   667  			return errors.Errorf("failed to configure identity mapping for DNAT %s: unable to parse IP address %s: %v",
   668  				dnatLabel, mapping.IpAddress, err)
   669  		}
   670  	}
   671  
   672  	var addrOnly bool
   673  	if mapping.Port == 0 {
   674  		addrOnly = true
   675  	}
   676  
   677  	if h.ed {
   678  		req := &vpp_nat_ed.Nat44AddDelIdentityMapping{
   679  			Tag:       dnatLabel,
   680  			Flags:     setNat44EdFlags(&nat44EdFlags{isAddrOnly: addrOnly}),
   681  			IPAddress: ipAddr,
   682  			Port:      uint16(mapping.Port),
   683  			Protocol:  h.protocolNBValueToNumber(mapping.Protocol),
   684  			SwIfIndex: ifIdx,
   685  			VrfID:     mapping.VrfId,
   686  			IsAdd:     isAdd,
   687  		}
   688  
   689  		reply := &vpp_nat_ed.Nat44AddDelIdentityMappingReply{}
   690  
   691  		if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   692  			return err
   693  		}
   694  	} else {
   695  		req := &vpp_nat_ei.Nat44EiAddDelIdentityMapping{
   696  			Tag:       dnatLabel,
   697  			Flags:     setNat44EiFlags(&nat44EiFlags{eiAddrOnlyMapping: addrOnly}),
   698  			IPAddress: ipAddr,
   699  			Port:      uint16(mapping.Port),
   700  			Protocol:  h.protocolNBValueToNumber(mapping.Protocol),
   701  			SwIfIndex: ifIdx,
   702  			VrfID:     mapping.VrfId,
   703  			IsAdd:     isAdd,
   704  		}
   705  
   706  		reply := &vpp_nat_ei.Nat44EiAddDelIdentityMappingReply{}
   707  
   708  		if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   709  			return err
   710  		}
   711  	}
   712  
   713  	return nil
   714  }
   715  
   716  func setNat44EdFlags(flags *nat44EdFlags) nat_types.NatConfigFlags {
   717  	var flagsCfg nat_types.NatConfigFlags
   718  	if flags.isTwiceNat {
   719  		flagsCfg |= nat_types.NAT_IS_TWICE_NAT
   720  	}
   721  	if flags.isSelfTwiceNat {
   722  		flagsCfg |= nat_types.NAT_IS_SELF_TWICE_NAT
   723  	}
   724  	if flags.isOut2In {
   725  		flagsCfg |= nat_types.NAT_IS_OUT2IN_ONLY
   726  	}
   727  	if flags.isAddrOnly {
   728  		flagsCfg |= nat_types.NAT_IS_ADDR_ONLY
   729  	}
   730  	if flags.isOutside {
   731  		flagsCfg |= nat_types.NAT_IS_OUTSIDE
   732  	}
   733  	if flags.isInside {
   734  		flagsCfg |= nat_types.NAT_IS_INSIDE
   735  	}
   736  	if flags.isStatic {
   737  		flagsCfg |= nat_types.NAT_IS_STATIC
   738  	}
   739  	if flags.isExtHostValid {
   740  		flagsCfg |= nat_types.NAT_IS_EXT_HOST_VALID
   741  	}
   742  	return flagsCfg
   743  }
   744  
   745  func setNat44EiFlags(flags *nat44EiFlags) vpp_nat_ei.Nat44EiConfigFlags {
   746  	var flagsCfg vpp_nat_ei.Nat44EiConfigFlags
   747  	if flags.eiStaticMappingOnly {
   748  		flagsCfg |= vpp_nat_ei.NAT44_EI_STATIC_MAPPING_ONLY
   749  	}
   750  	if flags.eiConnectionTracking {
   751  		flagsCfg |= vpp_nat_ei.NAT44_EI_CONNECTION_TRACKING
   752  	}
   753  	if flags.eiOut2InDpo {
   754  		flagsCfg |= vpp_nat_ei.NAT44_EI_OUT2IN_DPO
   755  	}
   756  	if flags.eiAddrOnlyMapping {
   757  		flagsCfg |= vpp_nat_ei.NAT44_EI_ADDR_ONLY_MAPPING
   758  	}
   759  	if flags.eiIfInside {
   760  		flagsCfg |= vpp_nat_ei.NAT44_EI_IF_INSIDE
   761  	}
   762  	if flags.eiIfOutside {
   763  		flagsCfg |= vpp_nat_ei.NAT44_EI_IF_OUTSIDE
   764  	}
   765  	if flags.eiStaticMapping {
   766  		flagsCfg |= vpp_nat_ei.NAT44_EI_STATIC_MAPPING
   767  	}
   768  	return flagsCfg
   769  }
   770  
   771  func ipTo4Address(ipStr string) (addr ip_types.IP4Address, err error) {
   772  	netIP := net.ParseIP(ipStr)
   773  	if netIP == nil {
   774  		return ip_types.IP4Address{}, fmt.Errorf("invalid IP: %q", ipStr)
   775  	}
   776  	if ip4 := netIP.To4(); ip4 != nil {
   777  		var ip4Addr ip_types.IP4Address
   778  		copy(ip4Addr[:], netIP.To4())
   779  		addr = ip4Addr
   780  	} else {
   781  		return ip_types.IP4Address{}, fmt.Errorf("required IPv4, provided: %q", ipStr)
   782  	}
   783  	return
   784  }
   785  
   786  // checkTagLength serves as a validator for static/identity mapping tag length
   787  func checkTagLength(tag string) error {
   788  	if len(tag) > maxTagLen {
   789  		return errors.Errorf("DNAT label '%s' has %d bytes, max allowed is %d",
   790  			tag, len(tag), maxTagLen)
   791  	}
   792  	return nil
   793  }