Skip to content

Add/Parse Geneve Option for Multicast Tag #760

@zeeshanlakhani

Description

@zeeshanlakhani

With the additional Geneve option added as part of Multicast replication in oxidecomputer/dendrite#14, we need to update the Geneve engine in OPTE to parse and set this option.

Here's the P4 code showing the additions:

const bit<7> GENEVE_OPT_OXIDE_MCAST	= 0x01;
/* Geneve option for an `mcast_tag`.
 * This is a 2-bit field that indicates the type of
 * multicast traffic:
 * 0 - Replicate packets to ports set for external multicast traffic
 * 1 - Replicate packets to ports set for underlay multicast traffic
 * 2 - Replicate packets to ports set for underlay and external multicast
       traffic (bifurcated)
 *
 * The rest of the option is reserved.
*/
header geneve_opt_mcast_h {
	bit<2> mcast_tag;
	bit<30> reserved;
}
struct geneve_opt_headers_t {
	geneve_opt_h	ox_external_tag;
	geneve_opt_h ox_external_tag;
	// Multicast-specific options
	geneve_opt_mcast_h ox_mcast_tag;
}

Here's a test helper used in packet testing to generate the option as part of a Geneve (encapsulated) packet:

/// Build a Geneve packet with a possible multicast tag.
pub fn gen_geneve_packet_with_mcast_tag(
    src: Endpoint,
    dst: Endpoint,
    inner_type: u16,
    vni: u32,
    tag_ingress: bool,
    mcast_tag: Option<u8>, // New parameter for multicast tag
    payload: &[u8],
) -> Packet {
    let udp_stack = match src.get_ip("src").unwrap() {
        IpAddr::V4(_) => {
            vec![inner_type, ipv4::IPPROTO_UDP.into(), eth::ETHER_IPV4]
        }
        IpAddr::V6(_) => {
            vec![inner_type, ipv6::IPPROTO_UDP.into(), eth::ETHER_IPV6]
        }
    };

    let mut pkt = Packet::gen(src, dst, udp_stack, Some(payload)).unwrap();
    let geneve = pkt.hdrs.geneve_hdr.as_mut().unwrap();
    geneve.vni = vni;

    match (tag_ingress, mcast_tag) {
        (true, Some(tag)) if tag < 3 => {
            geneve.opt_len = 2;
            // Multicast tag option
            #[rustfmt::skip]
            geneve.options.extend_from_slice(&[
                // First 2 bytes: Geneve option class (0x0129)
                // The OXIDE vendor-specific class identifier
                0x01, 0x29,
                // Third byte: Critical bit (0) + Option type (1)
                // Type 1 represents multicast tagged packets
                0x01,
                // Fourth byte: Option(s) length
                0x01,
                // Fifth byte: Tag value (encoded in the data)
                (tag & 0x03) << 6,
                // Sixth byte: reserved
                0x00,
                // Seventh byte
                0x00,
                // Eighth byte
                0x00,
            ]);

            let extra_bytes = geneve.options.len() as u16;

            match src.get_ip("src").unwrap() {
                IpAddr::V4(_) => {
                    pkt.hdrs.ipv4_hdr.as_mut().unwrap().ipv4_total_len +=
                        extra_bytes
                }
                IpAddr::V6(_) => {
                    pkt.hdrs.ipv6_hdr.as_mut().unwrap().ipv6_payload_len +=
                        extra_bytes
                }
            }

            pkt.hdrs.udp_hdr.as_mut().unwrap().udp_len += extra_bytes;
        }
        (true, Some(_)) => {
            // Multicast tag is not valid
            panic!("Multicast tag must be less than 3");
        }
        (true, None) => {
            // External packet option
            geneve.opt_len = 1;
            #[rustfmt::skip]
            geneve.options.extend_from_slice(&[
                // First 2 bytes: Geneve option class (0x0129)
                // The OXIDE vendor-specific class identifier
                0x01, 0x29,
                // Third byte: Critical bit (0) + Option type (1)
                0x00,
                // reserved + body len
                0x00,
            ]);

            let extra_bytes = geneve.options.len() as u16;

            match src.get_ip("src").unwrap() {
                IpAddr::V4(_) => {
                    pkt.hdrs.ipv4_hdr.as_mut().unwrap().ipv4_total_len +=
                        extra_bytes
                }
                IpAddr::V6(_) => {
                    pkt.hdrs.ipv6_hdr.as_mut().unwrap().ipv6_payload_len +=
                        extra_bytes
                }
            }

            pkt.hdrs.udp_hdr.as_mut().unwrap().udp_len += extra_bytes;
        }
        _ => {}
    }

    pkt
}

Metadata

Metadata

Labels

multicastMulticast-related feature/impl

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions