1use anyhow::{bail, ensure, Context, Result};
6use std::net::Ipv4Addr;
7
8pub const VXLAN_PORT: u16 = 4789;
9pub const GENEVE_PORT: u16 = 6081;
10pub const GRE_IN_UDP_PORT: u16 = 4754;
12const ETH_BRIDGING: u16 = 0x6558;
14const GRE_CHECKSUM: u8 = 0x80;
16const GRE_KEY: u8 = 0x20;
17const GRE_SEQUENCE: u8 = 0x10;
18const GRE_RFC1701_ONLY: u8 = 0x40 | 0x08 | 0x04;
22pub const ETHERTYPE_IPV4: u16 = 0x0800;
23pub const ETHERTYPE_ARP: u16 = 0x0806;
24pub const MAX_DATAGRAM: usize = 65_507;
26pub const MAX_UDP_PAYLOAD: usize = 1400;
29pub const DEFAULT_MAC: &str = "02:4e:47:00:00:01";
31
32pub type Mac = [u8; 6];
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum Encap {
36 Vxlan,
37 Geneve,
38 Gretap,
40}
41
42impl Encap {
43 pub fn parse(s: &str) -> Result<Self> {
44 match s {
45 "vxlan" => Ok(Encap::Vxlan),
46 "geneve" => Ok(Encap::Geneve),
47 "gretap" => Ok(Encap::Gretap),
48 other => bail!("encapsulation {other:?} is not vxlan, geneve or gretap"),
49 }
50 }
51 pub fn name(self) -> &'static str {
52 match self {
53 Encap::Vxlan => "vxlan",
54 Encap::Geneve => "geneve",
55 Encap::Gretap => "gretap",
56 }
57 }
58 pub fn port(self) -> u16 {
59 match self {
60 Encap::Vxlan => VXLAN_PORT,
61 Encap::Geneve => GENEVE_PORT,
62 Encap::Gretap => GRE_IN_UDP_PORT,
63 }
64 }
65 pub fn max_vni(self) -> u32 {
67 match self {
68 Encap::Vxlan | Encap::Geneve => MAX_VNI,
69 Encap::Gretap => u32::MAX,
70 }
71 }
72}
73
74pub const MAX_VNI: u32 = 0xff_ffff;
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
78pub struct Header {
79 pub vni: Option<u32>,
81 pub checksum: bool,
83 pub sequence: Option<u32>,
85}
86
87impl Header {
88 pub fn vni(vni: u32) -> Self {
89 Header {
90 vni: Some(vni),
91 ..Default::default()
92 }
93 }
94}
95
96pub fn vni_str(encap: Encap, vni: Option<u32>) -> String {
98 match (encap, vni) {
99 (Encap::Gretap, Some(k)) => format!("key {k}"),
100 (Encap::Gretap, None) => "no key".into(),
101 (_, v) => format!("VNI {}", v.unwrap_or_default()),
102 }
103}
104
105pub fn decap(encap: Encap, b: &[u8]) -> Result<(Header, &[u8])> {
107 if encap == Encap::Gretap {
108 return decap_gre(b);
109 }
110 ensure!(
111 b.len() >= 8,
112 "a {}-byte datagram has no {} header",
113 b.len(),
114 encap.name()
115 );
116 let vni = u32::from_be_bytes([0, b[4], b[5], b[6]]);
117 match encap {
118 Encap::Vxlan => {
119 ensure!(b[0] & 0x08 != 0, "the VXLAN I flag is clear: no valid VNI");
120 Ok((Header::vni(vni), &b[8..]))
121 }
122 Encap::Geneve => {
123 let version = b[0] >> 6;
124 ensure!(version == 0, "Geneve version {version}");
125 ensure!(b[1] & 0x80 == 0, "a Geneve OAM packet");
126 let options = (b[0] & 0x3f) as usize * 4;
127 let protocol = u16::from_be_bytes([b[2], b[3]]);
128 ensure!(
129 protocol == ETH_BRIDGING,
130 "Geneve protocol type {protocol:#06x}, not Ethernet"
131 );
132 ensure!(
133 b.len() >= 8 + options,
134 "Geneve options run past the datagram"
135 );
136 Ok((Header::vni(vni), &b[8 + options..]))
137 }
138 Encap::Gretap => unreachable!("decap_gre"),
139 }
140}
141
142fn decap_gre(b: &[u8]) -> Result<(Header, &[u8])> {
145 ensure!(
146 b.len() >= 4,
147 "a {}-byte datagram has no GRE header",
148 b.len()
149 );
150 let version = b[1] & 0x07;
151 ensure!(
152 version == 0,
153 "GRE version {version} (1 is PPTP's enhanced GRE)"
154 );
155 ensure!(
156 b[0] & GRE_RFC1701_ONLY == 0,
157 "GRE flags {:#04x}: RFC 1701 routing, strict source route or recursion bits are set",
158 b[0]
159 );
160 let protocol = u16::from_be_bytes([b[2], b[3]]);
161 ensure!(
162 protocol == ETH_BRIDGING,
163 "GRE protocol type {protocol:#06x}, not Transparent Ethernet Bridging"
164 );
165 let checksum = b[0] & GRE_CHECKSUM != 0;
166 let mut at = 4;
167 let mut field = |present: bool, what: &str| -> Result<Option<u32>> {
168 if !present {
169 return Ok(None);
170 }
171 ensure!(
172 b.len() >= at + 4,
173 "a {}-byte datagram ends inside the GRE {what}",
174 b.len()
175 );
176 let v = u32::from_be_bytes([b[at], b[at + 1], b[at + 2], b[at + 3]]);
177 at += 4;
178 Ok(Some(v))
179 };
180 field(checksum, "checksum")?;
181 let key = field(b[0] & GRE_KEY != 0, "key")?;
182 let sequence = field(b[0] & GRE_SEQUENCE != 0, "sequence number")?;
183 ensure!(!checksum || self::checksum(&[b]) == 0, "bad GRE checksum");
186 Ok((
187 Header {
188 vni: key,
189 checksum,
190 sequence,
191 },
192 &b[at..],
193 ))
194}
195
196pub fn encap(encap: Encap, header: &Header, inner: &[u8]) -> Vec<u8> {
199 let mut out = Vec::with_capacity(16 + inner.len());
200 let v = header.vni.unwrap_or_default().to_be_bytes();
201 match encap {
202 Encap::Vxlan => out.extend_from_slice(&[0x08, 0, 0, 0, v[1], v[2], v[3], 0]),
203 Encap::Geneve => {
204 out.extend_from_slice(&[0, 0]);
205 out.extend_from_slice(Ð_BRIDGING.to_be_bytes());
206 out.extend_from_slice(&[v[1], v[2], v[3], 0]);
207 }
208 Encap::Gretap => {
209 let flags = if header.checksum { GRE_CHECKSUM } else { 0 }
210 | if header.vni.is_some() { GRE_KEY } else { 0 }
211 | if header.sequence.is_some() {
212 GRE_SEQUENCE
213 } else {
214 0
215 };
216 out.extend_from_slice(&[flags, 0]);
217 out.extend_from_slice(Ð_BRIDGING.to_be_bytes());
218 if header.checksum {
219 out.extend_from_slice(&[0; 4]);
220 }
221 if header.vni.is_some() {
222 out.extend_from_slice(&v);
223 }
224 if let Some(s) = header.sequence {
225 out.extend_from_slice(&s.to_be_bytes());
226 }
227 out.extend_from_slice(inner);
228 if header.checksum {
229 let sum = checksum(&[&out]);
230 out[4..6].copy_from_slice(&sum.to_be_bytes());
231 }
232 return out;
233 }
234 }
235 out.extend_from_slice(inner);
236 out
237}
238
239pub fn stamp_sequence(encap: Encap, datagram: &mut [u8], sequence: u32) -> bool {
244 if encap != Encap::Gretap || datagram.len() < 4 || datagram[0] & GRE_SEQUENCE == 0 {
245 return false;
246 }
247 let has_checksum = datagram[0] & GRE_CHECKSUM != 0;
248 let at = 4 + 4 * usize::from(has_checksum) + 4 * usize::from(datagram[0] & GRE_KEY != 0);
249 if datagram.len() < at + 4 {
250 return false;
251 }
252 datagram[at..at + 4].copy_from_slice(&sequence.to_be_bytes());
253 if has_checksum {
254 datagram[4..6].copy_from_slice(&[0, 0]);
255 let sum = checksum(&[datagram]);
256 datagram[4..6].copy_from_slice(&sum.to_be_bytes());
257 }
258 true
259}
260
261pub fn parse_mac(s: &str) -> Result<Mac> {
262 let parts: Vec<&str> = s.split([':', '-']).collect();
263 ensure!(
264 parts.len() == 6,
265 "{s:?} is not a MAC address like 02:00:00:00:00:01"
266 );
267 let mut mac = [0u8; 6];
268 for (i, p) in parts.iter().enumerate() {
269 mac[i] =
270 u8::from_str_radix(p, 16).with_context(|| format!("{s:?} is not a MAC address"))?;
271 }
272 ensure!(mac[0] & 1 == 0, "{s} is a multicast address, not a host's");
273 Ok(mac)
274}
275
276pub fn mac_str(m: &Mac) -> String {
277 m.iter()
278 .map(|b| format!("{b:02x}"))
279 .collect::<Vec<_>>()
280 .join(":")
281}
282
283#[derive(Debug, Clone, PartialEq, Eq)]
284pub struct Arp {
285 pub request: bool,
286 pub sender_mac: Mac,
287 pub sender_ip: Ipv4Addr,
288 pub target_mac: Mac,
289 pub target_ip: Ipv4Addr,
290}
291
292#[derive(Debug, Clone, PartialEq, Eq)]
293pub enum Payload {
294 Arp(Arp),
295 Echo {
296 request: bool,
297 identifier: u16,
298 sequence: u16,
299 data: Vec<u8>,
300 },
301 IcmpError {
303 kind: u8,
304 code: u8,
305 original_protocol: u8,
306 original_dst: Ipv4Addr,
307 original_dst_port: Option<u16>,
308 },
309 Udp {
310 src_port: u16,
311 dst_port: u16,
312 data: Vec<u8>,
313 },
314 Other {
316 what: String,
317 },
318}
319
320#[derive(Debug, Clone, PartialEq, Eq)]
321pub struct Frame {
322 pub dst_mac: Mac,
323 pub src_mac: Mac,
324 pub src_ip: Option<Ipv4Addr>,
326 pub dst_ip: Option<Ipv4Addr>,
327 pub payload: Payload,
328}
329
330pub fn checksum(parts: &[&[u8]]) -> u16 {
332 let mut sum: u32 = 0;
333 let mut odd: Option<u8> = None;
334 for part in parts {
335 for &b in *part {
336 match odd.take() {
337 Some(hi) => sum += u32::from(u16::from_be_bytes([hi, b])),
338 None => odd = Some(b),
339 }
340 }
341 }
342 if let Some(hi) = odd {
343 sum += u32::from(u16::from_be_bytes([hi, 0]));
344 }
345 while sum >> 16 != 0 {
346 sum = (sum & 0xffff) + (sum >> 16);
347 }
348 !(sum as u16)
349}
350
351fn ip(b: &[u8]) -> Ipv4Addr {
352 Ipv4Addr::new(b[0], b[1], b[2], b[3])
353}
354
355fn mac(b: &[u8]) -> Mac {
356 let mut m = [0u8; 6];
357 m.copy_from_slice(&b[..6]);
358 m
359}
360
361pub fn parse_frame(b: &[u8]) -> Result<Frame> {
364 ensure!(
365 b.len() >= 14,
366 "a {}-byte inner frame is shorter than Ethernet",
367 b.len()
368 );
369 let (dst_mac, src_mac) = (mac(&b[0..6]), mac(&b[6..12]));
370 let ethertype = u16::from_be_bytes([b[12], b[13]]);
371 let body = &b[14..];
372 let other = |what: String| Frame {
373 dst_mac,
374 src_mac,
375 src_ip: None,
376 dst_ip: None,
377 payload: Payload::Other { what },
378 };
379 match ethertype {
380 ETHERTYPE_ARP => {
381 ensure!(body.len() >= 28, "a truncated ARP packet");
382 ensure!(
383 body[0..6] == [0, 1, 8, 0, 6, 4],
384 "ARP for something other than Ethernet/IPv4"
385 );
386 let op = u16::from_be_bytes([body[6], body[7]]);
387 ensure!(op == 1 || op == 2, "ARP operation {op}");
388 Ok(Frame {
389 dst_mac,
390 src_mac,
391 src_ip: None,
392 dst_ip: None,
393 payload: Payload::Arp(Arp {
394 request: op == 1,
395 sender_mac: mac(&body[8..14]),
396 sender_ip: ip(&body[14..18]),
397 target_mac: mac(&body[18..24]),
398 target_ip: ip(&body[24..28]),
399 }),
400 })
401 }
402 ETHERTYPE_IPV4 => parse_ipv4(dst_mac, src_mac, body),
403 other_type => Ok(other(format!("ethertype {other_type:#06x}"))),
404 }
405}
406
407fn parse_ipv4(dst_mac: Mac, src_mac: Mac, b: &[u8]) -> Result<Frame> {
408 ensure!(b.len() >= 20, "a truncated IPv4 header");
409 ensure!(b[0] >> 4 == 4, "IP version {}", b[0] >> 4);
410 let ihl = (b[0] & 0x0f) as usize * 4;
411 ensure!(ihl >= 20 && b.len() >= ihl, "IPv4 header length {ihl}");
412 let total = u16::from_be_bytes([b[2], b[3]]) as usize;
413 ensure!(
414 total >= ihl && total <= b.len(),
415 "IPv4 total length {total} against a {}-byte payload",
416 b.len()
417 );
418 ensure!(checksum(&[&b[..ihl]]) == 0, "bad IPv4 header checksum");
419 let (src, dst) = (ip(&b[12..16]), ip(&b[16..20]));
420 let protocol = b[9];
421 let flags_frag = u16::from_be_bytes([b[6], b[7]]);
422 let body = &b[ihl..total];
423 let frame = |payload| Frame {
424 dst_mac,
425 src_mac,
426 src_ip: Some(src),
427 dst_ip: Some(dst),
428 payload,
429 };
430 if flags_frag & 0x3fff != 0 {
431 return Ok(frame(Payload::Other {
432 what: "an IPv4 fragment".into(),
433 }));
434 }
435 match protocol {
436 1 => {
437 ensure!(body.len() >= 8, "a truncated ICMP message");
438 ensure!(checksum(&[body]) == 0, "bad ICMP checksum");
439 let (kind, code) = (body[0], body[1]);
440 match kind {
441 0 | 8 => Ok(frame(Payload::Echo {
442 request: kind == 8,
443 identifier: u16::from_be_bytes([body[4], body[5]]),
444 sequence: u16::from_be_bytes([body[6], body[7]]),
445 data: body[8..].to_vec(),
446 })),
447 3 | 11 if body.len() >= 8 + 20 => {
448 let inner = &body[8..];
449 let inner_ihl = (inner[0] & 0x0f) as usize * 4;
450 let original_protocol = inner[9];
451 let original_dst_port = (original_protocol == 17
452 && inner.len() >= inner_ihl + 4)
453 .then(|| u16::from_be_bytes([inner[inner_ihl + 2], inner[inner_ihl + 3]]));
454 Ok(frame(Payload::IcmpError {
455 kind,
456 code,
457 original_protocol,
458 original_dst: ip(&inner[16..20]),
459 original_dst_port,
460 }))
461 }
462 _ => Ok(frame(Payload::Other {
463 what: format!("ICMP type {kind} code {code}"),
464 })),
465 }
466 }
467 17 => {
468 ensure!(body.len() >= 8, "a truncated UDP header");
469 let len = u16::from_be_bytes([body[4], body[5]]) as usize;
470 ensure!(len >= 8 && len <= body.len(), "UDP length {len}");
471 let sum = u16::from_be_bytes([body[6], body[7]]);
472 if sum != 0 {
473 ensure!(
474 checksum(&[&pseudo_header(src, dst, 17, len), &body[..len]]) == 0,
475 "bad UDP checksum"
476 );
477 }
478 Ok(frame(Payload::Udp {
479 src_port: u16::from_be_bytes([body[0], body[1]]),
480 dst_port: u16::from_be_bytes([body[2], body[3]]),
481 data: body[8..len].to_vec(),
482 }))
483 }
484 6 => Ok(frame(Payload::Other { what: "TCP".into() })),
485 p => Ok(frame(Payload::Other {
486 what: format!("IP protocol {p}"),
487 })),
488 }
489}
490
491fn pseudo_header(src: Ipv4Addr, dst: Ipv4Addr, protocol: u8, len: usize) -> Vec<u8> {
492 let mut p = Vec::with_capacity(12);
493 p.extend_from_slice(&src.octets());
494 p.extend_from_slice(&dst.octets());
495 p.extend_from_slice(&[0, protocol]);
496 p.extend_from_slice(&(len as u16).to_be_bytes());
497 p
498}
499
500pub fn ethernet(dst: Mac, src: Mac, ethertype: u16, payload: &[u8]) -> Vec<u8> {
501 let mut out = Vec::with_capacity(14 + payload.len());
502 out.extend_from_slice(&dst);
503 out.extend_from_slice(&src);
504 out.extend_from_slice(ðertype.to_be_bytes());
505 out.extend_from_slice(payload);
506 out
507}
508
509pub fn arp_frame(
511 request: bool,
512 sender_mac: Mac,
513 sender_ip: Ipv4Addr,
514 target_mac: Mac,
515 target_ip: Ipv4Addr,
516) -> Vec<u8> {
517 let mut a = vec![0, 1, 8, 0, 6, 4, 0, if request { 1 } else { 2 }];
518 a.extend_from_slice(&sender_mac);
519 a.extend_from_slice(&sender_ip.octets());
520 a.extend_from_slice(&if request { [0; 6] } else { target_mac });
521 a.extend_from_slice(&target_ip.octets());
522 let dst = if request { [0xff; 6] } else { target_mac };
523 ethernet(dst, sender_mac, ETHERTYPE_ARP, &a)
524}
525
526pub fn ipv4(src: Ipv4Addr, dst: Ipv4Addr, protocol: u8, id: u16, payload: &[u8]) -> Vec<u8> {
528 let total = (20 + payload.len()) as u16;
529 let mut h = vec![0x45, 0];
530 h.extend_from_slice(&total.to_be_bytes());
531 h.extend_from_slice(&id.to_be_bytes());
532 h.extend_from_slice(&[0x40, 0, 64, protocol, 0, 0]);
533 h.extend_from_slice(&src.octets());
534 h.extend_from_slice(&dst.octets());
535 let sum = checksum(&[&h]);
536 h[10..12].copy_from_slice(&sum.to_be_bytes());
537 h.extend_from_slice(payload);
538 h
539}
540
541pub fn icmp_echo(request: bool, identifier: u16, sequence: u16, data: &[u8]) -> Vec<u8> {
542 let mut m = vec![if request { 8 } else { 0 }, 0, 0, 0];
543 m.extend_from_slice(&identifier.to_be_bytes());
544 m.extend_from_slice(&sequence.to_be_bytes());
545 m.extend_from_slice(data);
546 let sum = checksum(&[&m]);
547 m[2..4].copy_from_slice(&sum.to_be_bytes());
548 m
549}
550
551pub fn udp(src: Ipv4Addr, dst: Ipv4Addr, src_port: u16, dst_port: u16, data: &[u8]) -> Vec<u8> {
552 let len = 8 + data.len();
553 let mut u = Vec::with_capacity(len);
554 u.extend_from_slice(&src_port.to_be_bytes());
555 u.extend_from_slice(&dst_port.to_be_bytes());
556 u.extend_from_slice(&(len as u16).to_be_bytes());
557 u.extend_from_slice(&[0, 0]);
558 u.extend_from_slice(data);
559 let mut sum = checksum(&[&pseudo_header(src, dst, 17, len), &u]);
560 if sum == 0 {
561 sum = 0xffff;
562 }
563 u[6..8].copy_from_slice(&sum.to_be_bytes());
564 u
565}
566
567pub fn data_json(data: &[u8]) -> (String, &'static str) {
570 match std::str::from_utf8(data) {
571 Ok(s)
572 if s.chars()
573 .all(|c| !c.is_control() || matches!(c, '\n' | '\r' | '\t')) =>
574 {
575 (s.to_string(), "utf8")
576 }
577 _ => (hex::encode(data), "hex"),
578 }
579}
580
581pub fn data_bytes(v: &serde_json::Value) -> Result<Vec<u8>> {
583 let data = v["data"].as_str().unwrap_or_default();
584 let bytes = match v["encoding"].as_str().unwrap_or("utf8") {
585 "utf8" => data.as_bytes().to_vec(),
586 "hex" => hex::decode(data).context("data is not hex")?,
587 other => bail!("encoding {other:?} is not utf8 or hex"),
588 };
589 ensure!(
590 bytes.len() <= MAX_UDP_PAYLOAD,
591 "{} bytes is more than one frame carries ({MAX_UDP_PAYLOAD})",
592 bytes.len()
593 );
594 Ok(bytes)
595}