How to generate Geneve encapsulated packets

2 minute read

A decade or so ago, virtualization took the world by storm and that introduced multiple L2 tunneling encapsulations such as VxLAN, NVGRE and STT.

In 2020, IETF also standardized GENEVE (Generic Network Virtualization Encapsulation). See RFC 8926 for details. Before you dismiss this as yet another L2 tunnel encapsulation, know that AWS already uses Geneve!

Similary to VxLAN, Geneve is carried as a UDP payload and encapsulates an inner Ethernet or IP packet.

+=====+====+=====+========+================+=================+
| Eth | IP | UDP | GENEVE | (Inner) Eth/IP | (Inner) payload |
+=====+====+=====+========+================+=================+

Here’s the Geneve packet format.

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Ver|  Opt Len  |O|C|    Rsvd.  |          Protocol Type        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|        Virtual Network Identifier (VNI)       |    Reserved   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
~                    Variable-Length Options                    ~
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

To generate Geneve packets using Ostinato, we need to write a protocol builder userscript. Since the frame format is similar to VxLAN, I used the VxLAN userscript and made modifications required for Geneve, all of which was fairly straightforward.

Here’s the userscript you need to use for Geneve -

protocol.name = "Geneve"

protocol.protocolFrameSize = function() {
  return 8;
}

protocol.protocolFrameValue = function(index) {
    var ver = 0;	    // 0 to 3
    var opt_len = 0;	    // 0 - 63
    var control_pkt = 0;    // 0 or 1
    var critical_option = 0;// 0 or 1
    var vni = 0x749127;	    // example VNI value
    var proto = protocol.payloadProtocolId(Protocol.ProtocolIdEth);

    if (proto == 0)
        proto = 0x6558      // Assume ethernet aka Transparent Bridging

    var pfv = new Array(8);

    pfv[0] = (ver << 6 | opt_len) & 0xFF;
    pfv[1] = (control_pkt << 7 | critical_option << 6) & 0xFF;
    pfv[2] = (proto >> 8) & 0xFF;
    pfv[3] = proto & 0xFF;

    pfv[4] = (vni >> 16) & 0xFF;
    pfv[5] = (vni >> 8) & 0xFF;
    pfv[6] = vni & 0xFF;
    pfv[7] = 0;

    return pfv;
}

protocol.protocolId = function(id_type) {
    if (id_type == Protocol.ProtocolIdTcpUdp)
        return 6081;
    return 0;
}

Let’s verify the script by capturing our Geneve packet in Wireshark (click to enlarge) -

Geneve packet in Wireshark

Some limitations -

  • The above userscript doesn’t generate Geneve options
  • For the outer UDP, override the checksum field to 0 - this is to avoid an Ostinato TCP/UDP checksum bug when you have 2 (or more) IP headers in a packet.

If you need details on how to create all the protocols headers (outer, inner) for a Geneve packet, see Crafting VxLAN packets using Ostinato - it’s exactly the same, just replace the VxLAN userscript with the Geneve userscript above.

Or just download this sample Ostinato stream and open it in Ostinato directly.

For more Ostinato related content, subscribe for email updates.

Leave a Comment