go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/natplugin/vppcalls/vpp2101/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  
    21  	"github.com/pkg/errors"
    22  
    23  	"go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2101/interface_types"
    24  	"go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2101/ip_types"
    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/natplugin/vppcalls"
    28  	nat "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/nat"
    29  )
    30  
    31  // Num protocol representation
    32  const (
    33  	ICMP uint8 = 1
    34  	TCP  uint8 = 6
    35  	UDP  uint8 = 17
    36  )
    37  
    38  const (
    39  	// NoInterface is sw-if-idx which means 'no interface'
    40  	NoInterface = interface_types.InterfaceIndex(^uint32(0))
    41  	// Maximal length of tag
    42  	maxTagLen = 64
    43  )
    44  
    45  // holds a list of NAT44 flags set
    46  type nat44Flags struct {
    47  	isTwiceNat     bool
    48  	isSelfTwiceNat bool
    49  	isOut2In       bool
    50  	isAddrOnly     bool
    51  	isOutside      bool
    52  	isInside       bool
    53  	isStatic       bool
    54  	isExtHostValid bool
    55  }
    56  
    57  // Enable NAT44 plugin and apply the given set of options.
    58  func (h *NatVppHandler) EnableNAT44Plugin(opts vppcalls.Nat44InitOpts) error {
    59  	var flags vpp_nat.Nat44ConfigFlags
    60  	if opts.EndpointDependent {
    61  		flags |= vpp_nat.NAT44_IS_ENDPOINT_DEPENDENT
    62  	}
    63  	if opts.ConnectionTracking {
    64  		flags |= vpp_nat.NAT44_IS_CONNECTION_TRACKING
    65  	}
    66  	if opts.StaticMappingOnly {
    67  		flags |= vpp_nat.NAT44_IS_STATIC_MAPPING_ONLY
    68  	}
    69  	if opts.OutToInDPO {
    70  		flags |= vpp_nat.NAT44_IS_OUT2IN_DPO
    71  	}
    72  	req := &vpp_nat.Nat44PluginEnableDisable{
    73  		Enable: true,
    74  		Flags:  flags,
    75  	}
    76  	reply := &vpp_nat.Nat44PluginEnableDisableReply{}
    77  	if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
    78  		return err
    79  	}
    80  	return nil
    81  }
    82  
    83  // DisableNAT44Plugin disables NAT44 plugin.
    84  func (h *NatVppHandler) DisableNAT44Plugin() error {
    85  	req := &vpp_nat.Nat44PluginEnableDisable{
    86  		Enable: false,
    87  	}
    88  	reply := &vpp_nat.Nat44PluginEnableDisableReply{}
    89  	if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
    90  		return err
    91  	}
    92  	return nil
    93  }
    94  
    95  // SetNat44Forwarding configures NAT44 forwarding.
    96  func (h *NatVppHandler) SetNat44Forwarding(enableFwd bool) error {
    97  	req := &vpp_nat.Nat44ForwardingEnableDisable{
    98  		Enable: enableFwd,
    99  	}
   100  	reply := &vpp_nat.Nat44ForwardingEnableDisableReply{}
   101  
   102  	if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   103  		return err
   104  	}
   105  
   106  	return nil
   107  }
   108  
   109  // EnableNat44Interface enables NAT44 feature for provided interface.
   110  func (h *NatVppHandler) EnableNat44Interface(iface string, isInside, isOutput bool) error {
   111  	if isOutput {
   112  		return h.handleNat44InterfaceOutputFeature(iface, isInside, true)
   113  	}
   114  	return h.handleNat44Interface(iface, isInside, true)
   115  }
   116  
   117  // DisableNat44Interface disables NAT44 feature for provided interface.
   118  func (h *NatVppHandler) DisableNat44Interface(iface string, isInside, isOutput bool) error {
   119  	if isOutput {
   120  		return h.handleNat44InterfaceOutputFeature(iface, isInside, false)
   121  	}
   122  	return h.handleNat44Interface(iface, isInside, false)
   123  }
   124  
   125  // AddNat44AddressPool adds new IPV4 address pool into the NAT pools.
   126  func (h *NatVppHandler) AddNat44AddressPool(vrf uint32, firstIP, lastIP string, twiceNat bool) error {
   127  	return h.handleNat44AddressPool(vrf, firstIP, lastIP, twiceNat, true)
   128  }
   129  
   130  // DelNat44AddressPool removes existing IPv4 address pool from the NAT pools.
   131  func (h *NatVppHandler) DelNat44AddressPool(vrf uint32, firstIP, lastIP string, twiceNat bool) error {
   132  	return h.handleNat44AddressPool(vrf, firstIP, lastIP, twiceNat, false)
   133  }
   134  
   135  // SetVirtualReassemblyIPv4 configures NAT virtual reassembly for IPv4 packets.
   136  func (h *NatVppHandler) SetVirtualReassemblyIPv4(vrCfg *nat.VirtualReassembly) error {
   137  	return h.handleNatVirtualReassembly(vrCfg, false)
   138  }
   139  
   140  // SetVirtualReassemblyIPv6 configures NAT virtual reassembly for IPv6 packets.
   141  func (h *NatVppHandler) SetVirtualReassemblyIPv6(vrCfg *nat.VirtualReassembly) error {
   142  	return h.handleNatVirtualReassembly(vrCfg, true)
   143  }
   144  
   145  // AddNat44IdentityMapping adds new NAT44 identity mapping
   146  func (h *NatVppHandler) AddNat44IdentityMapping(mapping *nat.DNat44_IdentityMapping, dnatLabel string) error {
   147  	return h.handleNat44IdentityMapping(mapping, dnatLabel, true)
   148  }
   149  
   150  // DelNat44IdentityMapping removes existing NAT44 identity mapping
   151  func (h *NatVppHandler) DelNat44IdentityMapping(mapping *nat.DNat44_IdentityMapping, dnatLabel string) error {
   152  	return h.handleNat44IdentityMapping(mapping, dnatLabel, false)
   153  }
   154  
   155  // AddNat44StaticMapping creates new NAT44 static mapping entry.
   156  func (h *NatVppHandler) AddNat44StaticMapping(mapping *nat.DNat44_StaticMapping, dnatLabel string) error {
   157  	if len(mapping.LocalIps) == 0 {
   158  		return errors.Errorf("cannot configure static mapping for DNAT %s: no local address provided", dnatLabel)
   159  	}
   160  	if len(mapping.LocalIps) == 1 {
   161  		return h.handleNat44StaticMapping(mapping, dnatLabel, true)
   162  	}
   163  	return h.handleNat44StaticMappingLb(mapping, dnatLabel, true)
   164  }
   165  
   166  // DelNat44StaticMapping removes existing NAT44 static mapping entry.
   167  func (h *NatVppHandler) DelNat44StaticMapping(mapping *nat.DNat44_StaticMapping, dnatLabel string) error {
   168  	if len(mapping.LocalIps) == 0 {
   169  		return errors.Errorf("cannot un-configure static mapping from DNAT %s: no local address provided", dnatLabel)
   170  	}
   171  	if len(mapping.LocalIps) == 1 {
   172  		return h.handleNat44StaticMapping(mapping, dnatLabel, false)
   173  	}
   174  	return h.handleNat44StaticMappingLb(mapping, dnatLabel, false)
   175  }
   176  
   177  // Calls VPP binary API to set/unset interface NAT44 feature.
   178  func (h *NatVppHandler) handleNat44Interface(iface string, isInside, isAdd bool) error {
   179  	// get interface metadata
   180  	ifaceMeta, found := h.ifIndexes.LookupByName(iface)
   181  	if !found {
   182  		return errors.New("failed to get interface metadata")
   183  	}
   184  
   185  	req := &vpp_nat.Nat44InterfaceAddDelFeature{
   186  		SwIfIndex: interface_types.InterfaceIndex(ifaceMeta.SwIfIndex),
   187  		Flags:     setNat44Flags(&nat44Flags{isInside: isInside}),
   188  		IsAdd:     isAdd,
   189  	}
   190  	reply := &vpp_nat.Nat44InterfaceAddDelFeatureReply{}
   191  
   192  	if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   193  		return err
   194  	}
   195  
   196  	return nil
   197  }
   198  
   199  // Calls VPP binary API to set/unset interface NAT44 output feature
   200  func (h *NatVppHandler) handleNat44InterfaceOutputFeature(iface string, isInside, isAdd bool) error {
   201  	// get interface metadata
   202  	ifaceMeta, found := h.ifIndexes.LookupByName(iface)
   203  	if !found {
   204  		return errors.New("failed to get interface metadata")
   205  	}
   206  
   207  	req := &vpp_nat.Nat44InterfaceAddDelOutputFeature{
   208  		SwIfIndex: interface_types.InterfaceIndex(ifaceMeta.SwIfIndex),
   209  		Flags:     setNat44Flags(&nat44Flags{isInside: isInside}),
   210  		IsAdd:     isAdd,
   211  	}
   212  	reply := &vpp_nat.Nat44InterfaceAddDelOutputFeatureReply{}
   213  
   214  	if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   215  		return err
   216  	}
   217  
   218  	return nil
   219  }
   220  
   221  // Calls VPP binary API to add/remove addresses to/from the NAT44 pool.
   222  func (h *NatVppHandler) handleNat44AddressPool(vrf uint32, firstIP, lastIP string, twiceNat, isAdd bool) error {
   223  	firstAddr, err := ipTo4Address(firstIP)
   224  	if err != nil {
   225  		return errors.Errorf("unable to parse address %s from the NAT pool: %v", firstIP, err)
   226  	}
   227  	lastAddr := firstAddr
   228  	if lastIP != "" {
   229  		lastAddr, err = ipTo4Address(lastIP)
   230  		if err != nil {
   231  			return errors.Errorf("unable to parse address %s from the NAT pool: %v", lastIP, err)
   232  		}
   233  	}
   234  
   235  	req := &vpp_nat.Nat44AddDelAddressRange{
   236  		FirstIPAddress: firstAddr,
   237  		LastIPAddress:  lastAddr,
   238  		VrfID:          vrf,
   239  		Flags:          setNat44Flags(&nat44Flags{isTwiceNat: twiceNat}),
   240  		IsAdd:          isAdd,
   241  	}
   242  	reply := &vpp_nat.Nat44AddDelAddressRangeReply{}
   243  
   244  	if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   245  		return err
   246  	}
   247  
   248  	return nil
   249  }
   250  
   251  // Calls VPP binary API to setup NAT virtual reassembly
   252  func (h *NatVppHandler) handleNatVirtualReassembly(vrCfg *nat.VirtualReassembly, isIpv6 bool) error {
   253  	// Virtual Reassembly has been removed from NAT API in VPP (moved to IP API)
   254  	// TODO: define IPReassembly model in L3 plugin
   255  	return nil
   256  	/*req := &vpp_nat.NatSetReass{
   257  		Timeout:  vrCfg.Timeout,
   258  		MaxReass: uint16(vrCfg.MaxReassemblies),
   259  		MaxFrag:  uint8(vrCfg.MaxFragments),
   260  		DropFrag: boolToUint(vrCfg.DropFragments),
   261  		IsIP6:    isIpv6,
   262  	}
   263  	reply := &vpp_nat.NatSetReassReply{}
   264  	if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   265  		return err
   266  	}*/
   267  }
   268  
   269  // Calls VPP binary API to add/remove NAT44 static mapping
   270  func (h *NatVppHandler) handleNat44StaticMapping(mapping *nat.DNat44_StaticMapping, dnatLabel string, isAdd bool) error {
   271  	var ifIdx = NoInterface
   272  	var exIPAddr ip_types.IP4Address
   273  
   274  	// check tag length limit
   275  	if err := checkTagLength(dnatLabel); err != nil {
   276  		return err
   277  	}
   278  
   279  	// parse local endpoint
   280  	lcIPAddr, err := ipTo4Address(mapping.LocalIps[0].LocalIp)
   281  	if err != nil {
   282  		return errors.Errorf("cannot configure DNAT static mapping %s: unable to parse local IP %s: %v",
   283  			dnatLabel, mapping.LocalIps[0].LocalIp, err)
   284  	}
   285  	lcPort := uint16(mapping.LocalIps[0].LocalPort)
   286  	lcVrf := mapping.LocalIps[0].VrfId
   287  
   288  	// Check external interface (prioritized over external IP)
   289  	if mapping.ExternalInterface != "" {
   290  		// Check external interface
   291  		ifMeta, found := h.ifIndexes.LookupByName(mapping.ExternalInterface)
   292  		if !found {
   293  			return errors.Errorf("cannot configure static mapping for DNAT %s: required external interface %s is missing",
   294  				dnatLabel, mapping.ExternalInterface)
   295  		}
   296  		ifIdx = interface_types.InterfaceIndex(ifMeta.SwIfIndex)
   297  	} else {
   298  		// Parse external IP address
   299  		exIPAddr, err = ipTo4Address(mapping.ExternalIp)
   300  		if err != nil {
   301  			return errors.Errorf("cannot configure static mapping for DNAT %s: unable to parse external IP %s: %v",
   302  				dnatLabel, mapping.ExternalIp, err)
   303  		}
   304  	}
   305  
   306  	// Resolve mapping (address only or address and port)
   307  	var addrOnly bool
   308  	if lcPort == 0 || mapping.ExternalPort == 0 {
   309  		addrOnly = true
   310  	}
   311  
   312  	req := &vpp_nat.Nat44AddDelStaticMappingV2{
   313  		Tag:               dnatLabel,
   314  		LocalIPAddress:    lcIPAddr,
   315  		ExternalIPAddress: exIPAddr,
   316  		Protocol:          h.protocolNBValueToNumber(mapping.Protocol),
   317  		ExternalSwIfIndex: ifIdx,
   318  		VrfID:             lcVrf,
   319  		Flags: setNat44Flags(&nat44Flags{
   320  			isTwiceNat:     mapping.TwiceNat == nat.DNat44_StaticMapping_ENABLED,
   321  			isSelfTwiceNat: mapping.TwiceNat == nat.DNat44_StaticMapping_SELF,
   322  			isOut2In:       true,
   323  			isAddrOnly:     addrOnly,
   324  		}),
   325  		IsAdd: isAdd,
   326  	}
   327  
   328  	if !addrOnly {
   329  		req.LocalPort = lcPort
   330  		req.ExternalPort = uint16(mapping.ExternalPort)
   331  	}
   332  
   333  	// Applying(if needed) the override of IP address picking from twice-NAT address pool
   334  	if mapping.TwiceNatPoolIp != "" {
   335  		req.MatchPool = true
   336  		req.PoolIPAddress, err = ipTo4Address(mapping.TwiceNatPoolIp)
   337  		if err != nil {
   338  			return errors.Errorf("cannot configure static mapping for DNAT %s: unable to parse "+
   339  				"twice-NAT pool IP %s: %v", dnatLabel, mapping.TwiceNatPoolIp, err)
   340  		}
   341  	}
   342  
   343  	reply := &vpp_nat.Nat44AddDelStaticMappingV2Reply{}
   344  
   345  	if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   346  		return err
   347  	}
   348  
   349  	return nil
   350  }
   351  
   352  // Calls VPP binary API to add/remove NAT44 static mapping with load balancing.
   353  func (h *NatVppHandler) handleNat44StaticMappingLb(mapping *nat.DNat44_StaticMapping, dnatLabel string, isAdd bool) error {
   354  	// check tag length limit
   355  	if err := checkTagLength(dnatLabel); err != nil {
   356  		return err
   357  	}
   358  
   359  	// parse external IP address
   360  	exIPAddrByte, err := ipTo4Address(mapping.ExternalIp)
   361  	if err != nil {
   362  		return errors.Errorf("cannot configure LB static mapping for DNAT %s: unable to parse external IP %s: %v",
   363  			dnatLabel, mapping.ExternalIp, err)
   364  	}
   365  
   366  	// In this case, external port is required
   367  	if mapping.ExternalPort == 0 {
   368  		return errors.Errorf("cannot configure LB static mapping for DNAT %s: external port is not set", dnatLabel)
   369  	}
   370  
   371  	// Transform local IP/Ports
   372  	var locals []vpp_nat.Nat44LbAddrPort
   373  	for _, local := range mapping.LocalIps {
   374  		if local.LocalPort == 0 {
   375  			return errors.Errorf("cannot set local IP/Port for DNAT mapping %s: port is missing",
   376  				dnatLabel)
   377  		}
   378  
   379  		localIP, err := ipTo4Address(local.LocalIp)
   380  		if err != nil {
   381  			return errors.Errorf("cannot set local IP/Port for DNAT mapping %s: unable to parse local IP %v: %v",
   382  				dnatLabel, local.LocalIp, err)
   383  		}
   384  
   385  		locals = append(locals, vpp_nat.Nat44LbAddrPort{
   386  			Addr:        localIP,
   387  			Port:        uint16(local.LocalPort),
   388  			Probability: uint8(local.Probability),
   389  			VrfID:       local.VrfId,
   390  		})
   391  	}
   392  
   393  	req := &vpp_nat.Nat44AddDelLbStaticMapping{
   394  		Tag:    dnatLabel,
   395  		Locals: locals,
   396  		//LocalNum:     uint32(len(locals)), // should not be needed (will be set by struc)
   397  		ExternalAddr: exIPAddrByte,
   398  		ExternalPort: uint16(mapping.ExternalPort),
   399  		Protocol:     h.protocolNBValueToNumber(mapping.Protocol),
   400  		Flags: setNat44Flags(&nat44Flags{
   401  			isTwiceNat:     mapping.TwiceNat == nat.DNat44_StaticMapping_ENABLED,
   402  			isSelfTwiceNat: mapping.TwiceNat == nat.DNat44_StaticMapping_SELF,
   403  			isOut2In:       true,
   404  		}),
   405  		IsAdd:    isAdd,
   406  		Affinity: mapping.SessionAffinity,
   407  	}
   408  
   409  	reply := &vpp_nat.Nat44AddDelLbStaticMappingReply{}
   410  
   411  	if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   412  		return err
   413  	}
   414  
   415  	return nil
   416  }
   417  
   418  // Calls VPP binary API to add/remove NAT44 identity mapping.
   419  func (h *NatVppHandler) handleNat44IdentityMapping(mapping *nat.DNat44_IdentityMapping, dnatLabel string, isAdd bool) (err error) {
   420  	var ifIdx = NoInterface
   421  	var ipAddr ip_types.IP4Address
   422  
   423  	// check tag length limit
   424  	if err := checkTagLength(dnatLabel); err != nil {
   425  		return err
   426  	}
   427  
   428  	// get interface index
   429  	if mapping.Interface != "" {
   430  		ifMeta, found := h.ifIndexes.LookupByName(mapping.Interface)
   431  		if !found {
   432  			return errors.Errorf("failed to configure identity mapping for DNAT %s: addressed interface %s does not exist",
   433  				dnatLabel, mapping.Interface)
   434  		}
   435  		ifIdx = interface_types.InterfaceIndex(ifMeta.SwIfIndex)
   436  	}
   437  
   438  	if ifIdx == NoInterface {
   439  		// Case with IP (optionally port). Verify and parse input IP/port
   440  		ipAddr, err = ipTo4Address(mapping.IpAddress)
   441  		if err != nil {
   442  			return errors.Errorf("failed to configure identity mapping for DNAT %s: unable to parse IP address %s: %v",
   443  				dnatLabel, mapping.IpAddress, err)
   444  		}
   445  	}
   446  
   447  	var addrOnly bool
   448  	if mapping.Port == 0 {
   449  		addrOnly = true
   450  	}
   451  
   452  	req := &vpp_nat.Nat44AddDelIdentityMapping{
   453  		Tag:       dnatLabel,
   454  		Flags:     setNat44Flags(&nat44Flags{isAddrOnly: addrOnly}),
   455  		IPAddress: ipAddr,
   456  		Port:      uint16(mapping.Port),
   457  		Protocol:  h.protocolNBValueToNumber(mapping.Protocol),
   458  		SwIfIndex: ifIdx,
   459  		VrfID:     mapping.VrfId,
   460  		IsAdd:     isAdd,
   461  	}
   462  
   463  	reply := &vpp_nat.Nat44AddDelIdentityMappingReply{}
   464  
   465  	if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   466  		return err
   467  	}
   468  
   469  	return nil
   470  }
   471  
   472  func setNat44Flags(flags *nat44Flags) nat_types.NatConfigFlags {
   473  	var flagsCfg nat_types.NatConfigFlags
   474  	if flags.isTwiceNat {
   475  		flagsCfg |= nat_types.NAT_IS_TWICE_NAT
   476  	}
   477  	if flags.isSelfTwiceNat {
   478  		flagsCfg |= nat_types.NAT_IS_SELF_TWICE_NAT
   479  	}
   480  	if flags.isOut2In {
   481  		flagsCfg |= nat_types.NAT_IS_OUT2IN_ONLY
   482  	}
   483  	if flags.isAddrOnly {
   484  		flagsCfg |= nat_types.NAT_IS_ADDR_ONLY
   485  	}
   486  	if flags.isOutside {
   487  		flagsCfg |= nat_types.NAT_IS_OUTSIDE
   488  	}
   489  	if flags.isInside {
   490  		flagsCfg |= nat_types.NAT_IS_INSIDE
   491  	}
   492  	if flags.isStatic {
   493  		flagsCfg |= nat_types.NAT_IS_STATIC
   494  	}
   495  	if flags.isExtHostValid {
   496  		flagsCfg |= nat_types.NAT_IS_EXT_HOST_VALID
   497  	}
   498  	return flagsCfg
   499  }
   500  
   501  func ipTo4Address(ipStr string) (addr ip_types.IP4Address, err error) {
   502  	netIP := net.ParseIP(ipStr)
   503  	if netIP == nil {
   504  		return ip_types.IP4Address{}, fmt.Errorf("invalid IP: %q", ipStr)
   505  	}
   506  	if ip4 := netIP.To4(); ip4 != nil {
   507  		var ip4Addr ip_types.IP4Address
   508  		copy(ip4Addr[:], netIP.To4())
   509  		addr = ip4Addr
   510  	} else {
   511  		return ip_types.IP4Address{}, fmt.Errorf("required IPv4, provided: %q", ipStr)
   512  	}
   513  	return
   514  }
   515  
   516  // checkTagLength serves as a validator for static/identity mapping tag length
   517  func checkTagLength(tag string) error {
   518  	if len(tag) > maxTagLen {
   519  		return errors.Errorf("DNAT label '%s' has %d bytes, max allowed is %d",
   520  			tag, len(tag), maxTagLen)
   521  	}
   522  	return nil
   523  }