Skip to content

Commit a22db5c

Browse files
committed
Format MAC/IP addresses, TCP flags, ICMP types
Adds formatters to make life easier when using DTrace to track softnpu. Before: ``` ipv4: version 4 ihl 5 diffserv 0 total_len 34 identification 15 flags 2 frag_offset 0 ttl 40 protocol 6 hdr_checksum e444 src c6336502 dst c6336501 ``` After: ``` ethernet: dst 33:33:00:00:00:dd src a8:40:25:00:00:06 ether_type 0x0901 (Sidecar) sidecar: sc_code 1 sc_ingress 3 sc_egress 3 sc_ether_type 0x86dd (IPv6) sc_payload 1701d arp: ∅ ipv4: ∅ ipv6: version 6 traffic_class 0 flow_label 0 payload_len e next_hdr 0x11 (UDP) hop_limit 1 src fe80::aa40:25ff:fe00:6 dst ff02::dd ``` Signed-off-by: Trey Aspelund <trey@oxidecomputer.com>
1 parent 132cdc3 commit a22db5c

2 files changed

Lines changed: 275 additions & 2 deletions

File tree

codegen/rust/src/header.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,39 @@ impl<'a> HeaderGenerator<'a> {
112112
checksum_statements.push(quote! {
113113
csum = p4rs::bitmath::add_le(csum.clone(), self.#name.csum())
114114
});
115+
let dump_call = if size == 32
116+
&& (name_s == "src"
117+
|| name_s == "dst"
118+
|| name_s.ends_with("_ip")
119+
|| name_s.ends_with("_addr"))
120+
{
121+
quote! { p4rs::dump_ip4(&self.#name) }
122+
} else if size == 128 && (name_s == "src" || name_s == "dst") {
123+
quote! { p4rs::dump_ip6(&self.#name) }
124+
} else if size == 48
125+
&& (name_s == "src" || name_s == "dst" || name_s.ends_with("_mac"))
126+
{
127+
quote! { p4rs::dump_mac(&self.#name) }
128+
} else if size == 16
129+
&& (name_s.ends_with("ether_type")
130+
|| name_s == "proto_type"
131+
|| name_s == "protocol")
132+
{
133+
quote! { p4rs::dump_ether_type(&self.#name) }
134+
} else if size == 8 && (name_s == "protocol" || name_s == "next_hdr") {
135+
quote! { p4rs::dump_ip_proto(&self.#name) }
136+
} else if size == 8 && name_s == "flags" {
137+
quote! { p4rs::dump_tcp_flags(&self.#name) }
138+
} else if size == 16 && name_s == "opcode" {
139+
quote! { p4rs::dump_arp_op(&self.#name) }
140+
} else if size == 8 && name_s == "type" {
141+
quote! { p4rs::dump_icmp_type(&self.#name) }
142+
} else {
143+
quote! { p4rs::dump_bv(&self.#name) }
144+
};
115145
dump_statements.push(quote! {
116146
#name_s.cyan(),
117-
p4rs::dump_bv(&self.#name)
147+
#dump_call
118148
});
119149

120150
offset += size;

lang/p4rs/src/lib.rs

Lines changed: 244 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
#![allow(non_camel_case_types)]
6262

6363
use std::fmt;
64-
use std::net::IpAddr;
64+
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
6565

6666
pub use error::TryFromSliceError;
6767
use serde::{Deserialize, Serialize};
@@ -276,6 +276,156 @@ pub fn dump_bv(x: &BitVec<u8, Msb0>) -> String {
276276
}
277277
}
278278

279+
pub fn dump_ip4(x: &BitVec<u8, Msb0>) -> String {
280+
if x.len() != 32 {
281+
return dump_bv(x);
282+
}
283+
let v: u32 = x.load_le();
284+
Ipv4Addr::from(v).to_string()
285+
}
286+
287+
pub fn dump_ip6(x: &BitVec<u8, Msb0>) -> String {
288+
if x.len() != 128 {
289+
return dump_bv(x);
290+
}
291+
let v: u128 = x.load_le();
292+
Ipv6Addr::from(v).to_string()
293+
}
294+
295+
pub fn dump_mac(x: &BitVec<u8, Msb0>) -> String {
296+
if x.len() != 48 {
297+
return dump_bv(x);
298+
}
299+
let v: u128 = x.load_le();
300+
format!(
301+
"{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
302+
(v >> 40) & 0xff,
303+
(v >> 32) & 0xff,
304+
(v >> 24) & 0xff,
305+
(v >> 16) & 0xff,
306+
(v >> 8) & 0xff,
307+
v & 0xff,
308+
)
309+
}
310+
311+
pub fn dump_ether_type(x: &BitVec<u8, Msb0>) -> String {
312+
if x.len() != 16 {
313+
return dump_bv(x);
314+
}
315+
let v: u16 = x.load_le();
316+
let name = match v {
317+
0x0800 => Some("IPv4"),
318+
0x0806 => Some("ARP"),
319+
0x0901 => Some("Sidecar"),
320+
0x8100 => Some("VLAN"),
321+
0x86dd => Some("IPv6"),
322+
0x8847 => Some("MPLS unicast"),
323+
0x8848 => Some("MPLS multicast"),
324+
0x88a8 => Some("QinQ"),
325+
_ => None,
326+
};
327+
match name {
328+
Some(n) => format!("0x{:04x} ({})", v, n),
329+
None => format!("0x{:04x}", v),
330+
}
331+
}
332+
333+
pub fn dump_ip_proto(x: &BitVec<u8, Msb0>) -> String {
334+
if x.len() != 8 {
335+
return dump_bv(x);
336+
}
337+
let v: u8 = x.load_le();
338+
let name = match v {
339+
1 => Some("ICMPv4"),
340+
6 => Some("TCP"),
341+
17 => Some("UDP"),
342+
41 => Some("IPv6-in-IPv4"),
343+
43 => Some("IPv6-Route"),
344+
44 => Some("IPv6-Frag"),
345+
58 => Some("ICMPv6"),
346+
59 => Some("IPv6-NoNxt"),
347+
60 => Some("IPv6-Opts"),
348+
132 => Some("SCTP"),
349+
_ => None,
350+
};
351+
match name {
352+
Some(n) => format!("0x{:02x} ({})", v, n),
353+
None => format!("0x{:02x}", v),
354+
}
355+
}
356+
357+
pub fn dump_tcp_flags(x: &BitVec<u8, Msb0>) -> String {
358+
if x.len() != 8 {
359+
return dump_bv(x);
360+
}
361+
let v: u8 = x.load_le();
362+
if v == 0 {
363+
return "none".into();
364+
}
365+
let mut flags = Vec::new();
366+
if v & 0x01 != 0 { flags.push("FIN"); }
367+
if v & 0x02 != 0 { flags.push("SYN"); }
368+
if v & 0x04 != 0 { flags.push("RST"); }
369+
if v & 0x08 != 0 { flags.push("PSH"); }
370+
if v & 0x10 != 0 { flags.push("ACK"); }
371+
if v & 0x20 != 0 { flags.push("URG"); }
372+
if v & 0x40 != 0 { flags.push("ECE"); }
373+
if v & 0x80 != 0 { flags.push("CWR"); }
374+
flags.join("|")
375+
}
376+
377+
pub fn dump_arp_op(x: &BitVec<u8, Msb0>) -> String {
378+
if x.len() != 16 {
379+
return dump_bv(x);
380+
}
381+
let v: u16 = x.load_le();
382+
let name = match v {
383+
1 => Some("Request"),
384+
2 => Some("Reply"),
385+
3 => Some("RARP Request"),
386+
4 => Some("RARP Reply"),
387+
_ => None,
388+
};
389+
match name {
390+
Some(n) => format!("0x{:04x} ({})", v, n),
391+
None => format!("0x{:04x}", v),
392+
}
393+
}
394+
395+
pub fn dump_icmp_type(x: &BitVec<u8, Msb0>) -> String {
396+
if x.len() != 8 {
397+
return dump_bv(x);
398+
}
399+
let v: u8 = x.load_le();
400+
// Types 0-127 use ICMPv4 semantics; types 128-255 are exclusively ICMPv6.
401+
let name = match v {
402+
// ICMPv4
403+
0 => Some("Echo Reply"),
404+
3 => Some("Dest Unreachable"),
405+
5 => Some("Redirect"),
406+
8 => Some("Echo Request"),
407+
11 => Some("Time Exceeded"),
408+
12 => Some("Parameter Problem"),
409+
// ICMPv6 (128+ are unambiguously ICMPv6)
410+
128 => Some("Echo Request"),
411+
129 => Some("Echo Reply"),
412+
130 => Some("MLD Query"),
413+
131 => Some("MLD Report"),
414+
132 => Some("MLD Done"),
415+
133 => Some("RS"),
416+
134 => Some("RA"),
417+
135 => Some("NS"),
418+
136 => Some("NA"),
419+
137 => Some("Redirect"),
420+
143 => Some("MLDv2 Report"),
421+
_ => None,
422+
};
423+
match name {
424+
Some(n) => format!("0x{:02x} ({})", v, n),
425+
None => format!("0x{:02x}", v),
426+
}
427+
}
428+
279429
pub fn extract_exact_key(
280430
keyset_data: &[u8],
281431
offset: usize,
@@ -357,6 +507,99 @@ pub fn extract_lpm_key(
357507
table::Key::Lpm(table::Prefix { addr, len })
358508
}
359509

510+
#[cfg(test)]
511+
mod tests {
512+
use super::*;
513+
514+
// Construct a bitvec as it would appear after header::set() — bytes are
515+
// reversed for multi-byte fields (>8 bits), unchanged for single-byte fields.
516+
fn net_bv(bytes: &[u8]) -> BitVec<u8, Msb0> {
517+
let mut v = bytes.to_vec();
518+
if v.len() > 1 {
519+
v.reverse();
520+
}
521+
BitVec::<u8, Msb0>::from_vec(v)
522+
}
523+
524+
#[test]
525+
fn test_dump_ip4() {
526+
assert_eq!(dump_ip4(&net_bv(&[192, 168, 1, 1])), "192.168.1.1");
527+
assert_eq!(dump_ip4(&net_bv(&[10, 0, 0, 1])), "10.0.0.1");
528+
assert_eq!(dump_ip4(&net_bv(&[0, 0, 0, 0])), "0.0.0.0");
529+
assert_eq!(dump_ip4(&net_bv(&[255, 255, 255, 255])), "255.255.255.255");
530+
}
531+
532+
#[test]
533+
fn test_dump_ip6() {
534+
let mut fd00_1 = [0u8; 16];
535+
fd00_1[0] = 0xfd;
536+
fd00_1[15] = 0x01;
537+
assert_eq!(dump_ip6(&net_bv(&fd00_1)), "fd00::1");
538+
539+
let mut loopback = [0u8; 16];
540+
loopback[15] = 1;
541+
assert_eq!(dump_ip6(&net_bv(&loopback)), "::1");
542+
}
543+
544+
#[test]
545+
fn test_dump_mac() {
546+
assert_eq!(
547+
dump_mac(&net_bv(&[0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff])),
548+
"aa:bb:cc:dd:ee:ff"
549+
);
550+
assert_eq!(
551+
dump_mac(&net_bv(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00])),
552+
"00:00:00:00:00:00"
553+
);
554+
}
555+
556+
#[test]
557+
fn test_dump_ether_type() {
558+
assert_eq!(dump_ether_type(&net_bv(&[0x08, 0x00])), "0x0800 (IPv4)");
559+
assert_eq!(dump_ether_type(&net_bv(&[0x08, 0x06])), "0x0806 (ARP)");
560+
assert_eq!(dump_ether_type(&net_bv(&[0x86, 0xdd])), "0x86dd (IPv6)");
561+
assert_eq!(dump_ether_type(&net_bv(&[0x09, 0x01])), "0x0901 (Sidecar)");
562+
assert_eq!(dump_ether_type(&net_bv(&[0x12, 0x34])), "0x1234");
563+
}
564+
565+
#[test]
566+
fn test_dump_ip_proto() {
567+
assert_eq!(dump_ip_proto(&net_bv(&[1])), "0x01 (ICMPv4)");
568+
assert_eq!(dump_ip_proto(&net_bv(&[6])), "0x06 (TCP)");
569+
assert_eq!(dump_ip_proto(&net_bv(&[17])), "0x11 (UDP)");
570+
assert_eq!(dump_ip_proto(&net_bv(&[58])), "0x3a (ICMPv6)");
571+
assert_eq!(dump_ip_proto(&net_bv(&[99])), "0x63");
572+
}
573+
574+
#[test]
575+
fn test_dump_tcp_flags() {
576+
assert_eq!(dump_tcp_flags(&net_bv(&[0x00])), "none");
577+
assert_eq!(dump_tcp_flags(&net_bv(&[0x02])), "SYN");
578+
assert_eq!(dump_tcp_flags(&net_bv(&[0x12])), "SYN|ACK");
579+
assert_eq!(dump_tcp_flags(&net_bv(&[0x01])), "FIN");
580+
assert_eq!(dump_tcp_flags(&net_bv(&[0x04])), "RST");
581+
assert_eq!(dump_tcp_flags(&net_bv(&[0xff])), "FIN|SYN|RST|PSH|ACK|URG|ECE|CWR");
582+
}
583+
584+
#[test]
585+
fn test_dump_arp_op() {
586+
assert_eq!(dump_arp_op(&net_bv(&[0x00, 0x01])), "0x0001 (Request)");
587+
assert_eq!(dump_arp_op(&net_bv(&[0x00, 0x02])), "0x0002 (Reply)");
588+
assert_eq!(dump_arp_op(&net_bv(&[0x00, 0x05])), "0x0005");
589+
}
590+
591+
#[test]
592+
fn test_dump_icmp_type() {
593+
assert_eq!(dump_icmp_type(&net_bv(&[0])), "0x00 (Echo Reply)");
594+
assert_eq!(dump_icmp_type(&net_bv(&[8])), "0x08 (Echo Request)");
595+
assert_eq!(dump_icmp_type(&net_bv(&[133])), "0x85 (RS)");
596+
assert_eq!(dump_icmp_type(&net_bv(&[134])), "0x86 (RA)");
597+
assert_eq!(dump_icmp_type(&net_bv(&[135])), "0x87 (NS)");
598+
assert_eq!(dump_icmp_type(&net_bv(&[136])), "0x88 (NA)");
599+
assert_eq!(dump_icmp_type(&net_bv(&[50])), "0x32");
600+
}
601+
}
602+
360603
pub fn extract_bool_action_parameter(
361604
parameter_data: &[u8],
362605
offset: usize,

0 commit comments

Comments
 (0)