DOCA Documentation v3.2.0

PSP Encryption

This DPL program implements the PSP security protocol to provide line-rate cryptographic protection and encapsulation for IPv4 packets. By leveraging BlueField’s PSP hardware primitives, the program offloads encryption and decryption tasks, establishing secure, transparent tunnels between endpoints. The pipeline dynamically distinguishes between outbound (TX) encryption and inbound (RX) decryption paths based on the ingress port, incorporating integrated error handling for cryptographic failures. Flow identifiers are used within match-action tables to select the appropriate keys, tunnel parameters, or remediation actions. Notably, PSP key exchange is out of scope for this DPL program and is assumed to have been completed prior to pipeline execution.

Direction Selection Stage

  • Purpose: Directs each packet through either encryption or decryption logic.

  • Table direction_table: Matches ingress port against configured “wire port” numbers, distinguishing between RX (decap) and TX (encap) path.

  • Logic:

    • Miss: Packet is from the host/network stack, send it through encapsulation/encryption logic.

    • Hit: Packet is from the wire, run through decapsulation/decryption logic.

PSP Encapsulation/Encrypt Stage

  • Purpose: Used for TX/network-bound packets, encapsulates and encrypts matching flows.

  • Table encap_table: Matches outgoing IPv4 flows using source and destination address. For matched flows, triggers encapsulation with given MACs, tunnel IPs, and Security Association—applies DOCA PSP tunnel header and performs hardware encryption.

  • Actions: The encap action builds the outer encapsulation (Ethernet, IPv4, UDP, PSP), sets keys/crypt parameters, and calls the hardware to encrypt payload.

  • Packet Output: After encapsulation, packets are sent to the wire (external port) if matched, or to a default “other” port if not.

PSP Decrypt/Decapsulation Stage

  • Purpose: Used for RX/wire-to-host direction, decompresses, decrypts, and validates PSP-encrypted packets.

  • Decryption: If PSP header is present, calls hardware decryption with flow state defined in the hardware.

  • Table decap_table: Matches on PSP SPI field. For matched packets, runs the decap action which strips outer tunnel and restores the original packet.

  • Syndrome/Error Handling: Table syndrome matches on decryption error syndromes (ICV, bad trailer/context, general error). For recognized errors, counts and drops the packet; otherwise, processing continues.

  • Packet Output: Successfully decrypted/decapsulated packets go to a designated internal port; others are sent to a fallback port.

See below for the complete DPL example.

Copy
Copied!
            

/* * SPDX-FileCopyrightText: Copyright (c) 2025 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 <doca_model.p4> #include <doca_headers.p4> #include <doca_externs.p4> #include <doca_parser.p4>   const bit<32> WIRE_PORT = 0; const bit<16> PSP_UDP_SRC_PORT = 1234;   NvPSP(1024) psp_crypto;   control psp_encap( inout nv_headers_t headers, in nv_standard_metadata_t std_meta, inout nv_empty_metadata_t user_meta, inout nv_empty_metadata_t pkt_out_meta ) { action encap_tunnel( nv_mac_addr_t dst_addr, nv_mac_addr_t src_addr, nv_ipv4_addr_t src_ip, nv_ipv4_addr_t dst_ip, bit<32> spi ) { nv_encap_psp_v4_tunnel(headers, dst_addr, src_addr, src_ip, dst_ip, PSP_UDP_SRC_PORT, // udp_src_port, NvIPEncapProtocol.IPV4, // next header 6w0, // crypt_offset 0, // needs_sampling 0, // drop NvPSPVersion.V1_AES_GCM_256, spi, 0 // init_vector ); } action encrypt_tunnel( nv_mac_addr_t dst_addr, nv_mac_addr_t src_addr, nv_ipv4_addr_t src_ip, nv_ipv4_addr_t dst_ip, bit<32> spi, bit<32> sa_index ) { encap_tunnel(dst_addr, src_addr, src_ip, dst_ip, spi); psp_crypto.encrypt(headers, sa_index); } action encap_transport(bit<32> spi ) { nv_encap_psp_transport(headers, PSP_UDP_SRC_PORT, // udp_src_port, 6w0, // crypt_offset 0, // needs_sampling 0, // drop NvPSPVersion.V1_AES_GCM_256, spi, 0 // init_vector ); } action encrypt_transport( bit<32> spi, bit<32> sa_index ) { encap_transport(spi); psp_crypto.encrypt(headers, sa_index); } table encap_table { key = { headers.ipv4.src_addr : exact; headers.ipv4.dst_addr : exact; } actions = { encrypt_tunnel; encap_tunnel; encrypt_transport; encap_transport; NoAction; } default_action = NoAction(); } apply { if (encap_table.apply().hit) { nv_send_to_port(WIRE_PORT); } else { // This packet is not PSP nv_send_to_port(3); }   } }   control psp_decap( inout nv_headers_t headers, in nv_standard_metadata_t std_meta, inout nv_empty_metadata_t user_meta, inout nv_empty_metadata_t pkt_out_meta ) { NvCounter(8, NvCounterType.PACKETS_AND_BYTES) syndrome_counter;   action decrypt() { psp_crypto.decrypt(headers); }   action decap( nv_mac_addr_t dst_addr, nv_mac_addr_t src_addr ) { nv_decap_psp_tunnel(headers, dst_addr, src_addr, NvPspInnerProtocolType.IPV4); }   table decap_table { key = { headers.psp.security_parameters_index : exact; } actions = { decap; NoAction; } default_action = NoAction(); }   action drop(nv_psp_status_t status) { syndrome_counter.count(status); nv_drop(); }   table syndrome { key = { std_meta.psp_syndrome : exact; } actions = { drop; NoAction; } default_action = NoAction(); const entries = { (NvPSPStatus.ICV_FAIL) : drop(NvPSPStatus.ICV_FAIL); (NvPSPStatus.BAD_TRAILER) : drop(NvPSPStatus.BAD_TRAILER); (NvPSPStatus.BAD_CONTEXT) : drop(NvPSPStatus.BAD_CONTEXT); (NvPSPStatus.GENERAL_ERROR) : drop(NvPSPStatus.GENERAL_ERROR); } }   apply { if (headers.psp.isValid()) { decrypt(); if (syndrome.apply().miss) { decap_table.apply(); nv_send_to_port(1); } } // it is not PSP nv_send_to_port(2); } }   control psp( inout nv_headers_t headers, in nv_standard_metadata_t std_meta, inout nv_empty_metadata_t user_meta, inout nv_empty_metadata_t pkt_out_meta ) { psp_decap() decap; psp_encap() encap;   /* 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) { // TX direction encap.apply(headers, std_meta, user_meta, pkt_out_meta); } else { // RX direction decap.apply(headers, std_meta, user_meta, pkt_out_meta); } } }   NvDocaPipeline( nv_fixed_parser(), psp() ) main;


© Copyright 2025, NVIDIA. Last updated on Nov 20, 2025