Hello everyone! I’m trying to implement incremental checksum in P4 PSA eBPF and I’m using NIKSS switch. My program changes source IP and source port of packet. The problem is that my checksum update implementation seems to not work, I can see in wireshark that TCP checksum and IPv4 checksum is the same as before entering the switch. I followed the example program- p4-spec/p4-16/psa/examples/psa-example-incremental-checksum2.p4 at main · p4lang/p4-spec · GitHub and added some changes in parser and deparser. Do you recognize mabe some bugs in code below which may cause this issue? Or it should be implemented in completely different way?
Ingress Parser:
parser IngressParserImpl(packet_in buffer,
out headers hdr,
inout metadata user_meta,
in psa_ingress_parser_input_metadata_t istd,
in empty_metadata_t resubmit_meta,
in empty_metadata_t recirculate_meta) {
InternetChecksum() ck;
state start {
transition parse_ethernet;
}
state parse_ethernet {
buffer.extract(hdr.ethernet);
transition select(hdr.ethernet.etherType) {
TYPE_MPLS: parse_mpls;
TYPE_IPV4: parse_ipv4;
default: accept;
}
}
state parse_mpls {
buffer.extract(hdr.mpls);
transition parse_ipv4;
}
state parse_ipv4 {
buffer.extract(hdr.ipv4);
ck.clear();
ck.add({
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.srcAddr,
hdr.ipv4.dstAddr
});
verify(ck.get() == hdr.ipv4.hdrChecksum, error.BadIPv4HeaderChecksum);
ck.subtract({
hdr.ipv4.srcAddr,
hdr.ipv4.dstAddr
});
transition select(hdr.ipv4.protocol) {
TYPE_TCP: parse_trans;
TYPE_UDP: parse_trans;
default: accept;
}
}
state parse_trans {
buffer.extract(hdr.trans);
transition select(hdr.ipv4.protocol) {
TYPE_TCP: parse_tcp;
TYPE_UDP: parse_udp;
default: accept;
}
}
state parse_tcp {
buffer.extract(hdr.tcp);
ck.subtract({
hdr.trans.srcPort,
hdr.trans.dstPort,
hdr.tcp.seqNo,
hdr.tcp.ackNo,
hdr.tcp.dataOffset, hdr.tcp.res,
hdr.tcp.ecn, hdr.tcp.ctrl,
hdr.tcp.window,
hdr.tcp.checksum,
hdr.tcp.urgentPtr
});
user_meta.fwd_metadata.checksum_state = ck.get_state();
transition accept;
}
state parse_udp {
buffer.extract(hdr.udp);
ck.subtract({
hdr.trans.srcPort,
hdr.trans.dstPort,
hdr.udp.length_,
hdr.udp.checksum
});
user_meta.fwd_metadata.checksum_state = ck.get_state();
transition accept;
}
}
Egress Deparser:
control EgressDeparserImpl(packet_out packet,
out empty_metadata_t clone_e2e_meta,
out empty_metadata_t recirculate_meta,
inout headers hdr,
in metadata user_meta,
in psa_egress_output_metadata_t istd,
in psa_egress_deparser_input_metadata_t edstd){
InternetChecksum() ck;
apply {
if (hdr.ipv4.isValid()) {
ck.clear();
ck.add({
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.srcAddr,
hdr.ipv4.dstAddr
});
hdr.ipv4.hdrChecksum = ck.get();
}
ck.set_state(user_meta.fwd_metadata.checksum_state);
if (hdr.ipv4.isValid()) {
ck.add({
hdr.ipv4.srcAddr,
hdr.ipv4.dstAddr
});
if (hdr.tcp.isValid()) {
ck.add({
hdr.trans.srcPort,
hdr.trans.dstPort,
hdr.tcp.seqNo,
hdr.tcp.ackNo,
hdr.tcp.dataOffset, hdr.tcp.res,
hdr.tcp.ecn, hdr.tcp.ctrl,
hdr.tcp.urgentPtr
});
hdr.tcp.checksum = ck.get();
}
if (hdr.udp.isValid()) {
ck.add({
hdr.trans.srcPort,
hdr.trans.dstPort,
hdr.udp.length_
});
if (hdr.udp.checksum != 0) {
hdr.udp.checksum = ck.get();
if (hdr.udp.checksum == 0) {
hdr.udp.checksum = 0xffff;
}
}
}
packet.emit(hdr.ethernet);
packet.emit(hdr.ipv4);
packet.emit(hdr.trans);
packet.emit(hdr.tcp);
packet.emit(hdr.udp);
}
}
}