Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dpdk Backend: Add IPSec support #3858

Merged
merged 8 commits into from
Feb 2, 2023
Merged

Conversation

usha1830
Copy link
Contributor

PNA extern for IPsec:
	// IPsec accelerator result for the current packet
	enum ipsec_status {
	    IPSEC_SUCCESS,
	    IPSEC_ERROR
	}
	 
	// IPsec accelerator extern definition
	extern ipsec_accelerator {
	                // IPsec constructor.
	                ipsec_accelerator();
	 
	                // Set the security association (SA) index for the current packet.
	                //
	                // When not invoked, the SA index to be used is undefined, leading to target dependent
	                // behavior.
	                void set_sa_index<T>(in T sa_index);
	 
	                // Set the offset to the IPv4/IPv6 header for the current packet.
	                //
	                // When not invoked, the default value set for the IPv4/IPv6 header offset is 0.
	                void set_ip_header_offset<T>(in T offset);
	 
	                // Enable the IPsec operation to be performed on the current packet.
	                //
	                // The type of the operation (i.e. encrypt/decrypt) and its associated parameters (such as
	                // the crypto cipher and/or authentication parameters, the tunnel/transport mode headers,
	                // etc) are completely specified by the SA that was set for the current packet.
	                //
	                // When enabled, this operation takes place once the deparser operation is completed.
	                void enable();
	 
	                // Disable any IPsec operation that might have been previously enabled for the current
	                // packet.
	                void disable();
	 
	                // Returns true when the current packet has been processed by the IPsec accelerator and
	                // reinjected back into the pipeline, and false otherwise.
	                bool from_ipsec(out ipsec_status status);
	}

This extern is to be included in dpdk specific pna.p4

  1. Pseudo Header for IPsec accelerator (platform_hdr_t)
    This a pseudo header inserted by the compiler with one field for security association index. This header needs to be instantiated in the main encapsulating header struct which is passed as argument to MainParser and MainControl
			T is a bit type 
			header platform_hdr_t {
			    T sa_id;
			}
			
			struct headers_t {
			    ... existing headers
				platform_hdr_t ipsec_hdr;
			}

Insert the pseudo header only if ipsec_accelerator is instantiated and enable is called.
3. IPSec needs 4 location registers. Size of these registers is 1 and these are set by the control plane.

  • ipsec_port_out_inbound
  • ipsec_port_out_outbound
  • ipsec_port_in_inbound
  • ipsec_port_in_outbound
  1. Method “set_sa_index”: sets the ipsec_hdr.sa_id field.

  2. Method “enable”:
    a. Needs to validate the ipsec_instrisic header: ipsec_hdr.setValid()
    b. Needs to set the output port to the IPsec output port. This is done in the beginning of deparser, so that if there are multiple calls to enable and disable, the setting of output port for ipsec is done just once.

	1. if (ipsec_hdr.isValid()) 
		port_out = (direction == NET_TO_HOST) ? ipsec_inbound_port_out : ipsec_outbound_port_out ;
	2. //Deparser original start
	3. emit(ipsec_hdr)
	4. emit(rest of the headers)
  1. Method “disable”:
    a. Needs to invalidate the ipsec_hdr: ipsec_hdr.setInvalid()
  2. Method “from_ipsec”:
    a. Needs to test if the input port is equal to the IPsec accelerator port
    b. Currently, for P4-DPDK, the status is always set to “success”.
status = SUCCESS
from_ipsec = (port_in == ipsec_port_in_inbound) || (port_in == ipsec_port_in_outbound)

newHeaderName);
return program;
}
ipsecHeader = new IR::Type_Header(IR::ID(newHeaderName), newHeaderFields);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this header is always to going to present for all dpdk programs, perhaps it should be part of dpdk/pna.p4?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be only added if ipsec_accelerators enable method is called.

ipsec_status status;

state start {
from_ipsec = ipsec.from_ipsec(status);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you show how 'status' might be used in the parser?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be tested in "if" or "verify" statements.
The from_ipsec method can well be called outside the parser and the status can tested in if.

backends/dpdk/dpdkArch.cpp Outdated Show resolved Hide resolved
backends/dpdk/dpdkArch.cpp Show resolved Hide resolved
// containing headerinstances for all headers must be present
BUG_CHECK(structure->header_type != "",
"Encapsulating struct containing all headers used in the program is missing");
for (auto obj : program->objects) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not use the visitor methods for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Visitors for Type_Struct and P4Parser? This is for adding the new header and register declarations to the program. Only for placing these new declarations better in the program, I have added check for Type_Struct and Parser.

auto portOutInBound = new IR::AssignmentStatement(
c->body->srcInfo,
new IR::Member(new IR::PathExpression(IR::ID("m")),
IR::ID("pna_main_output_metadata_output_port")),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one of these two assignments must be wrong, since they write to the same field

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These assignments are placed in true and false blocks of "if-else"
corresponds to

if (direction == NET_TO_HOST)
      m.pna_main_output_metadata_output_port = ipsec_inbound_port_out
  else
      m.pna_main_output_metadata_output_port = ipsec_outbound_port_out

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be clearer to factor out the common subexpression. But it makes sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated comments and refactored common subexpression.

return stmt;
};

for (auto kv : structure->deparsers) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could be also done in a visitor

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, we need to insert this set of statements (from lambda function) in deparser. We have the information about which control blocks is/are deparser(s) stored in the "structure". This information is collected in the initial passes while inspecting the program as per specified architecture. Hence, I am checking that while visiting P4Control. I am not able to think of any other way to achieve this here. Please suggest if I am missing something.

backends/dpdk/dpdkArch.h Outdated Show resolved Hide resolved
passes.push_back(new P4::ClearTypeMap(typeMap));
passes.push_back(new P4::ResolveReferences(refMap));
passes.push_back(new P4::TypeInference(refMap, typeMap, false));
passes.push_back(new P4::TypeChecking(refMap, typeMap));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually prefer to call typechecking before a pass if needed.
This way a pass will build whatever data structures it needs.
Here you assume that the next pass needs a fresh typemap.
This is perhaps more reasonable in a backend - but probably the backend should document the fact that every pass expects a correct typemap.
I wonder whether this can mess up with the structure you have built earlier.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have removed TypeChecking from here as it is done later when needed. RefMap and TypeMap needs to be updated as we are adding new declarations and using those, hence rest of the passes are still needed.

backends/dpdk/dpdkHelpers.cpp Outdated Show resolved Hide resolved
backends/dpdk/dpdkUtils.h Show resolved Hide resolved
backends/dpdk/dpdkUtils.h Outdated Show resolved Hide resolved
@usha1830
Copy link
Contributor Author

usha1830 commented Feb 1, 2023

Thanks for the review @hanw @mbudiu-vmw
I will update the code as per comments

@mihaibudiu
Copy link
Contributor

I have already approved

@usha1830 usha1830 merged commit aa24300 into p4lang:main Feb 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants