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 }