/* * SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: LicenseRef-NvidiaProprietary * * NVIDIA CORPORATION, its affiliates and licensors retain all intellectual * property and proprietary rights in and to this material, related * documentation and any modifications thereto. Any use, reproduction, * disclosure or distribution of this material and related documentation * without an express license agreement from NVIDIA CORPORATION or * its affiliates is strictly prohibited. */ #include #include #include #include /* * Stretched L2 tenant’s network over provider routed network * - Underlay router * - Overlay L2 encapsulated in VxLAN header, multi tenant’s per server (FID to VF mapping) * - Policy engine for tenant’s 5 tuple + underlay IP * - Bridge + policy counter */ const nv_mac_addr_t DEFAULT_SRC_MAC = 48w0x1070fd658c88; const nv_mac_addr_t DEFAULT_DST_MAC = 48w0x9c05911f2a00; const nv_ipv4_addr_t DEFAULT_SRC_IPV4 = 32w0x01020304; const nv_ipv4_addr_t DEFAULT_DST_IPV4 = 32w0x11223344; const nv_ipv6_addr_t DEFAULT_SRC_IPV6 = 128w0x00010002000300040005000600070008; const nv_ipv6_addr_t DEFAULT_DST_IPV6 = 128w0x11112222333344445555666677778888; const bit<24> DEFAULT_VNI = 24w0xa; /* * Table sizes. */ const bit<32> FID_TABLE_SIZE = 1024; const bit<32> DECAP_TABLE_SIZE = 1048576; const bit<32> ADMIT_TABLE_SIZE = 1048576; const bit<32> BRIDGE_TABLE_SIZE = 32768; const bit<32> ENCAP_TABLE_SIZE = 32768; const bit<32> ROUTE_TABLE_SIZE = 32768; enum bit<32> AdmissionCounter_t { DENY = 0, ALLOW = 1, VXLAN_V4_ENCAP = 2, VXLAN_V6_ENCAP = 3 } enum bit<32> DecapFlowCounter_t { DENY = 0, ALLOW = 1, } /* The directionality is based on network to host * The user will configure the P4 port IDs in OVS */ const bit<32> WIRE_PORT = 32w0; /* fid is 32 bits */ typedef bit<32> fid_t; struct metadata_t { fid_t fid; } struct headers_t { NV_FIXED_HEADERS } parser packet_parser(packet_in packet, out headers_t headers) { NV_FIXED_PARSER(packet, headers) } /** * This control performs the overlay policy including L2 encap with VxLAN */ control overlay_encap( inout headers_t headers, in nv_standard_metadata_t std_meta, inout metadata_t user_meta, inout nv_empty_metadata_t pkt_out_meta ) { NvDirectCounter(NvCounterType.PACKETS_AND_BYTES) fid_counter; NvCounter(4, NvCounterType.PACKETS_AND_BYTES) admission_counter; action deny() { admission_counter.count(AdmissionCounter_t.DENY); nv_drop(); } action allow() { admission_counter.count(AdmissionCounter_t.ALLOW); } action vxlan_v4_encap(nv_mac_addr_t underlay_src_mac, nv_mac_addr_t underlay_dst_mac, nv_ipv4_addr_t underlay_sip, nv_ipv4_addr_t underlay_dip, bit<24> vni) { nv_set_vxlan_v4_underlay(headers, false, underlay_dst_mac, underlay_src_mac, 0, underlay_sip, underlay_dip, vni); admission_counter.count(AdmissionCounter_t.VXLAN_V4_ENCAP); } action vxlan_v6_encap(nv_mac_addr_t underlay_src_mac, nv_mac_addr_t underlay_dst_mac, nv_ipv6_addr_t underlay_sip, nv_ipv6_addr_t underlay_dip, bit<24> vni) { nv_set_vxlan_v6_underlay(headers, false, underlay_dst_mac, underlay_src_mac, 0, underlay_sip, underlay_dip, vni); admission_counter.count(AdmissionCounter_t.VXLAN_V6_ENCAP); } action set_fid_fid_table(fid_t fid) { fid_counter.count(); user_meta.fid = fid; } action no_action_fid_table() { fid_counter.count(); } // A FID could be as simple as ingress port to FID mapping // e.g. ports 1 and 2 are in FID 1, ports 3 and 4 are in FID 2 table fid_table { key = { std_meta.ingress_port : exact; } actions = { set_fid_fid_table; no_action_fid_table; } size = FID_TABLE_SIZE; default_action = no_action_fid_table; direct_counter = fid_counter; } table admit_v4_table { key = { headers.ipv4.src_addr : exact; headers.ipv4.dst_addr : exact; headers.ipv4.protocol : exact; headers.tcp.src_port : exact; headers.tcp.dst_port : exact; } actions = { allow; deny; NoAction; } size = ADMIT_TABLE_SIZE; default_action = NoAction; } table admit_v6_table { key = { headers.ipv6.src_addr : exact; headers.ipv6.dst_addr : exact; headers.ipv6.next_header : exact; headers.tcp.src_port : exact; headers.tcp.dst_port : exact; } actions = { allow; deny; NoAction; } size = ADMIT_TABLE_SIZE; default_action = NoAction; } table bridge { key = { headers.ethernet.dst_addr : exact; user_meta.fid : exact; } actions = { NoAction; } size = BRIDGE_TABLE_SIZE; default_action = NoAction; } table encap_v4_table { key = { headers.ipv4.dst_addr : lpm; } actions = { vxlan_v4_encap; } size = ENCAP_TABLE_SIZE; default_action = vxlan_v4_encap(DEFAULT_DST_MAC, DEFAULT_SRC_MAC, DEFAULT_SRC_IPV4, DEFAULT_DST_IPV4, DEFAULT_VNI); } table encap_v6_table { key = { headers.ipv6.dst_addr : lpm; } actions = { vxlan_v6_encap; } size = ENCAP_TABLE_SIZE; default_action = vxlan_v6_encap(DEFAULT_DST_MAC, DEFAULT_SRC_MAC, DEFAULT_SRC_IPV6, DEFAULT_DST_IPV6, DEFAULT_VNI); } apply { user_meta.fid = 0; fid_table.apply(); if (headers.ipv4.isValid()) { admit_v4_table.apply(); if (bridge.apply().hit) { encap_v4_table.apply(); } } else if (headers.ipv6.isValid()) { admit_v6_table.apply(); if (bridge.apply().hit) { encap_v6_table.apply(); } } } } /** * This control performs the underlay policy with routing */ control underlay_route( inout headers_t headers, in nv_standard_metadata_t std_meta, inout metadata_t user_meta, inout nv_empty_metadata_t pkt_out_meta ) { action set_port_and_route(nv_logical_port_t port, nv_mac_addr_t src_mac, nv_mac_addr_t dst_mac) { headers.ethernet.src_addr = src_mac; headers.ethernet.dst_addr = dst_mac; nv_dec_ip_ttl(headers, 1); nv_send_to_port(port); } table exact_v4_route { key = { headers.ipv4.dst_addr : exact; } actions = { set_port_and_route; NoAction; } size = ROUTE_TABLE_SIZE; default_action = NoAction; } table lpm_v4_route { key = { headers.ipv4.dst_addr : lpm; } actions = { set_port_and_route; NoAction; } size = ROUTE_TABLE_SIZE; default_action = NoAction; } table exact_v6_route { key = { headers.ipv6.dst_addr : exact; } actions = { set_port_and_route; NoAction; } size = ROUTE_TABLE_SIZE; default_action = NoAction; } table lpm_v6_route { key = { headers.ipv6.dst_addr : lpm; } actions = { set_port_and_route; NoAction; } size = ROUTE_TABLE_SIZE; default_action = NoAction; } apply { if (headers.ipv4.isValid()) { if (exact_v4_route.apply().miss) { lpm_v4_route.apply(); } } else if (headers.ipv6.isValid()) { if (exact_v6_route.apply().miss) { lpm_v6_route.apply(); } } } } /** * This control is for packets from wire to host (RX) * and includes policy for L2 decap */ control decap_flow( inout headers_t headers, in nv_standard_metadata_t std_meta, inout metadata_t user_meta, inout nv_empty_metadata_t pkt_out_meta ) { NvDirectCounter(NvCounterType.PACKETS_AND_BYTES) fid_counter; NvCounter(2, NvCounterType.PACKETS_AND_BYTES) decap_counter; action deny() { decap_counter.count(DecapFlowCounter_t.DENY); nv_drop(); } action decap_l2_and_send(nv_logical_port_t port) { decap_counter.count(DecapFlowCounter_t.ALLOW); nv_l2_decap(headers); nv_send_to_port(port); } action decap_ipv4_and_send(nv_logical_port_t port) { decap_counter.count(DecapFlowCounter_t.ALLOW); nv_l3_decap(headers, false, 0xffffffffff, 0x112233445566,NV_TYPE_IPV4,0); nv_send_to_port(port); } action decap_ipv6_and_send(nv_logical_port_t port) { decap_counter.count(DecapFlowCounter_t.ALLOW); nv_l3_decap(headers, false, 0xffffffffff, 0x112233445566,NV_TYPE_IPV6,0); nv_send_to_port(port); } action set_fid_fid_table(fid_t fid) { fid_counter.count(); user_meta.fid = fid; } action no_action_fid_table() { fid_counter.count(); } table fid_table { key = { std_meta.ingress_port : exact; } actions = { set_fid_fid_table; no_action_fid_table; } size = FID_TABLE_SIZE; default_action = no_action_fid_table; direct_counter = fid_counter; } table bridge { key = { headers.ethernet.dst_addr : exact; user_meta.fid : exact; } actions = { deny; NoAction; } size = BRIDGE_TABLE_SIZE; default_action = deny; } table decap_v4_table { key = { headers.ipv4.src_addr : exact; headers.ipv4.dst_addr : exact; headers.ipv4.protocol : exact; headers.udp.src_port : exact; headers.udp.dst_port : exact; } actions = { decap_l2_and_send; decap_ipv4_and_send; deny; } size = DECAP_TABLE_SIZE; default_action = deny; } table decap_v6_table { key = { headers.ipv6.src_addr : exact; headers.ipv6.dst_addr : exact; headers.ipv6.next_header : exact; headers.udp.src_port : exact; headers.udp.dst_port : exact; } actions = { decap_l2_and_send; decap_ipv6_and_send; deny; } size = DECAP_TABLE_SIZE; default_action = deny; } apply { user_meta.fid = (fid_t) 0; fid_table.apply(); if (bridge.apply().hit) { if (headers.ipv4.isValid()) { decap_v4_table.apply(); } else if (headers.ipv6.isValid()) { decap_v6_table.apply(); } } } } control host_based_networking( inout headers_t headers, in nv_standard_metadata_t std_meta, inout metadata_t user_meta, inout nv_empty_metadata_t pkt_out_meta ) { overlay_encap() overlay; underlay_route() route; decap_flow() decap; /* user should add entries that correspond to the wire ports * A hit means this is an RX packet, miss means a TX packet */ table direction_table { key = { std_meta.ingress_port : exact; } actions = { NoAction; } default_action = NoAction; const entries = { (WIRE_PORT) : NoAction(); } } apply { if (direction_table.apply().miss) { overlay.apply(headers, std_meta, user_meta, pkt_out_meta); route.apply(headers, std_meta, user_meta, pkt_out_meta); } else { decap.apply(headers, std_meta, user_meta, pkt_out_meta); } } } NvDocaPipeline( packet_parser(), host_based_networking() ) main;