-
Notifications
You must be signed in to change notification settings - Fork 82
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
Checksums #421
Checksums #421
Conversation
I would think that Restricting Changing the direction of the headers parameter to the deparser from 'in' to 'inout' is a straightforward way to allow the checksum fields to be modified in place. I understand that has the down side that the compiler would then permit arbitrary header fields to be modified in the deparser, without issuing any errors for doing so. An alternative is to require the checksums to be updated at the end of the ingress or egress control blocks, but the down side there is that it makes P4_16 statements like 'return' and 'exit' harder or impossible to use, without also skipping the checksum updating. |
You could also consider having two types of checksum blocks: |
// BEGIN:Compute_New_IPv4_Checksum_Example | ||
control DeparserImpl(packet_out packet, in headers hdr_) { | ||
Checksum<bit<16>>(HashAlgorithm_t.ONES_COMPLEMENT16) ck; | ||
headers hdr = hdr_; //TBD: or better, change declaration to inout? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are the implications of changing the argument to inout
? Are we then going to rely on convention that certain operations are not allowed?
Resolved at 9/18 meeting:
I will take these marching orders and update the PR. |
I believe I've made the changes we agreed on.
|
Given that it is used for IPv4 headers, and TCP and UDP, Internet Checksum seems as good a name as any. I am not aware of any other checksums that are incrementally updatable and commonly used in networking protocols. If such a thing arises, it seems reasonable to add a new extern to P4_16 for that purpose. Thus fixing InternetChecksum to input that is multiples of 16 bits wide, and output that is 16 bits wide, seems reasonable. |
missing the declaration of type parameter `T`.
* Add comments to Checksum extern clarifying that `remove` is only supported for CRC hash functions.
deparser. * Update PSA.mdk text, adding a note on when incremental computation is (not) supported. * Still pending: example of incremental TCP checksum?
* Add `InternetChecksum` with support for `remove`.
Remove extraneous whitespace
I've made the following changes:
Specifically I added the user metadata as an
|
@jnfoster what's the conclusion on adding the EDIT by @jafingerhut after this PR was already merged in -- @cc10512 My conclusion is that while we could have a separate init() method that we recommend people call when doing incremental checksum updates, but that the implementation of init() would be exactly the same as remove(). I don't mind having a separate init() method if we think that is a more understandable or cleaner API, somehow. |
ck.clear(); | ||
ck.remove(hdr.tcp.checksum); | ||
ck.remove(user_meta.fwd_metadata.old_srcAddr); | ||
ck.remove(hdr.ipv4.hdrChecksum); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The TCP pseudo-header includes the IPv4 source and dest address, but not the IPv4 header checksum, so this remove call, and the ck.update(hdrChecksum) call below, should be removed. RFC 793 is a good reference for the contents of the TCP pseudo-header.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am going to go ahead and approve and merge in these changes, and then make the small fix suggested above on top of that.
hdrChecksum = ck.get(); | ||
// Update TCP checksum | ||
ck.clear(); | ||
ck.remove(hdr.tcp.checksum); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nate, you asked me in email earlier whether remove() is correct to do here, and at the time I wasn't sure. I am pretty darned sure now that this is a correct way to do an incremental TCP checksum, primarily due to RFC 1624 and my own writing and thinking on the properties of the one's complement checksum.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While this example is totally valid, it is probably not the way incremental checksums are computed in practice.
Instead, the following happens:
- In the parser you compute the value of payload checksum by removing all the fields that TCP checksum is computed over from hdr.tcp.checksum and store it in the metadata, e.g. meta.tcpPayloadChecksum.
- In the control, some of the fields that participate in tcp checksum are modified (which is the whole reason to recompute the checksum in the first place)
- In the deparser, the TCP checksum is initialized with meta.tcpPayloadChecksum and then all the fields that TCP checksum is computed over are added to that checksum. Finally you do ck.get() and store it in hdr.tcp.checksum and then hdr.tcp is emit()ed.
As an optimization, one can remove/add only the fields that are actually modified in step 2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vgurevich So a separate parser state for parsing a TCP header after IPv4 vs. parsing a TCP header after IPv6, if both IPv4 and IPv6 are supported in a P4 program, then? Either that, or you need a single parser state that has an 'if' condition that distinguishes those two cases, and I'm not sure whether you'd be interested in mandating support for if statements inside of a parser state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jafingerhut , Yes, something along those lines (unless we want to revisit the separate ChecksumVerification control :) ). If() statement is more readable, but it is way more dangerous.
Another option is to instantiate the extern outside (of any parser state) and then remove() IPv4/IPv6 fields in the corresponding states whether or not there is a TCP/UDP header afterwards. Then in the TCP parsing state, you add() tcp.CheckSum and remove() the rest of tcp fields. Same for UDP.
It's all not very pretty, but can probably be partially blamed on the quirkiness of the original (i.e. TCP/UDP) design.....
We can try to write a couple of examples and see which one is more palatable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will make a couple of minor fixes after merging this in.
Ooh, that's nice. Shall we change the example?
…-N
On Sat, Oct 14, 2017 at 2:37 PM, Vladimir Gurevich ***@***.*** > wrote:
***@***.**** commented on this pull request.
------------------------------
In p4-16/psa/examples/psa-example-incremental-checksum.p4
<#421 (comment)>:
> + ck.update({ hdr.ipv4.version,
+ hdr.ipv4.ihl,
+ hdr.ipv4.diffserv,
+ hdr.ipv4.totalLen,
+ hdr.ipv4.identification,
+ hdr.ipv4.flags,
+ hdr.ipv4.fragOffset,
+ hdr.ipv4.ttl,
+ hdr.ipv4.protocol,
+ //hdr.ipv4.hdrChecksum, // intentionally leave this out
+ hdr.ipv4.srcAddr,
+ hdr.ipv4.dstAddr });
+ hdrChecksum = ck.get();
+ // Update TCP checksum
+ ck.clear();
+ ck.remove(hdr.tcp.checksum);
@jafingerhut <https://github.com/jafingerhut> ,
While this example is totally valid, it is probably not the way
incremental checksums are computed in practice.
Instead, the following happens:
1. In the parser you compute the value of payload checksum by removing
all the fields that TCP checksum is computed over from hdr.tcp.checksum and
store it in the metadata, e.g. meta.tcpPayloadChecksum.
2. In the control, some of the fields that participate in tcp checksum
are modified (which is the whole reason to recompute the checksum in the
first place)
3. In the deparser, the TCP checksum is initialized with
meta.tcpPayloadChecksum and then all the fields that TCP checksum is
computed over are added to that checksum. Finally you do ck.get() and store
it in hdr.tcp.checksum and then hdr.tcp is emit()ed.
As an optimization, one can remove/add only the fields that are actually
modified in step 2.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#421 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/ABwi0iwitujWu-ZCWugllsyQCXXqb8Zkks5ssP9ugaJpZM4PaZbn>
.
|
apply { | ||
// Update IPv4 checksum | ||
ck.clear(); | ||
ck.update({ hdr.ipv4.version, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, maybe for clarity we should format this like so:
ck.update({
/* 16-bit word 0 */ hdr.ipv4.version, hdr.ipv4.ihl, hdr.ipv4.diffserv,
/* 16-bit word 1 */ hdr.ipv4.totalLen,
/* 16-bit word 2 */ hdr.ipv4.identification,
/* 16-bit word 3 */ hdr.ipv4.flags, hdr.ipv4.fragOffset,
/* 16-bit word 4 */ hdr.ipv4.ttl, hdr.ipv4.protocol,
/* 16-bit word 5 skip hdr.ipv4.hdrChecksum, */
/* 16-bit words 6-7 */ hdr.ipv4.srcAddr,
/* 16-bit words 8-9 */ hdr,ipv4.dstAddr
});
The point I am trying to make is that the update()
method of the InternetChecksum
extern can only take data that is short-sized (i.e. 16*N bits wide).
Obviously, we do not have a way to express it syntactically, but I think it should be documented and the compiler should enforce that bit-width of T is a multiple of 16.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed that the update() and remove() methods should restrict their inputs to be multiples of 16 bits long, and this ought to be documented. That change is included in this PR: #440
It does not update the format as shown above, though -- certainly an easy change to make, and no objections from me.
This PR implements changes discussed in preceding working group meetings. Note that I missed the last meeting, so I am going from the notes and discussions with those who attended -- i.e., it possible something got lost in translation.
The major changes include:
Remove
ComputeChecksum
control and require checksum computation and update to be done in deparser.Clarifying that incremental checksumming is only supported for CRC{16,32}.
To discuss:
Should we indeed move checksum updates to deparser?
What do to with deparser direction annotations? Can copy the headers to a new variable, but it's a shame that we have to do that.
Is my undersatnding of the intended restrictions on incrmental checksums correct? In particular, calling
update
many times is fine, but callingremove
is only okay for CRC{16,32}.Add an example of incremental TCP checksum?