How to generate MPLS packets using Ostinato User Script

5 minute read

Ostinato provides two ways to generate packets for protocols that are not available out of the box.

In the last post, we saw how to create a MPLS packet in Ostinato using hexdump. In this post we will see how to do the same using a user script instead.

Although this post talks about MPLS, the same method is applicable for any protocol - MPLS is used just as an example here

First we start with the MPLS packet format. Let’s assume we need to generate a IP/UDP packet encapsulated inside a MPLS tunnel. So the packet would consist of the following protocol headers in this order -

+----------+------+----+-----+------+
| Ethernet | MPLS | IP | UDP | Data |
+----------+------+----+-----+------+

All the protocols in the above frame except MPLS are already supported in Ostinato.

To create this packet in Ostinato, we create a stream and select the already available protocols - Ethernet II, IPv4, UDP, Pattern and then switch to the advanced protocol selection mode -

Simple Protocols

Select the :{Script}[EXPERIMENTAL] protocol in the Available Protocols list on the left, IPv4 in the Selected Protocols list on the right and click on the right arrow to insert userscript in between Eth II and IPv4 -

Advanced Protocols

You can use the up/down arrows to change the order of the selected protocols at any time

Now that we have the user script header at the correct place in the packet, let’s switch to the Protocol Data tab and click on UserScript -

Protocol Data

We need to write a script here to generate the MPLS header. To do that, let’s review the MPLS header format.

The MPLS header consists of one or more MPLS tags comprising a tag stack -

+-------+-------+-------+-----+
| Tag 1 | Tag 2 | Tag 3 | ... |
+-------+-------+-------+-----+

Each tag is 32-bit wide and is composed of the following fields -

+------------+-----+-----+-----+
| MPLS Label | Exp |  S  | TTL |
|    (20)    | (3) | (1) | (8) |
+------------+-----+-----+-----+

The numbers in () represents the bit-width of each field. The S bit is set to 1 for the last (innermost) tag and is 0 for all the other tags

Let’s create a single tag MPLS header with the following values -

Label = 12701
EXP = 5
S = 1
TTL = 64

We start the script with defining the protocol name -

protocol.name = 'MPLS';

followed by the size of the header -

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

Note: protocolFrameSize must be a function returning an integer, not a simple integer

and finally a function that returns an array of integers where the size of the array is the number of bytes in the header and each element of the array is the byte at that offset (in other words, it’s a byte array!) -

protocol.protocolFrameValue = function()
{
    var fv = Array(4);
    // TODO: fill fv with header bytes

    return fv;
}

We first create an object with the values that we want in the MPLS fields and then use some bit arithmetic to convert it to a 32-bit integer value using the header format encoding that we saw earlier -

protocol.protocolFrameValue = function()
{
    var tag = { label: 12701, exp: 5, s: 1, ttl: 64 };
    var tagVal = tag.label << 12 | tag.exp << 9 | tag.s << 8 | tag.ttl;

    var fv = Array(4);
    // TODO: convert tagVal to header bytes and fill fv 

    return fv;
}

We complete the function by extracting each byte out of the tagVal integer and populating fv with it -

protocol.protocolFrameValue = function()
{
    var tag = { label: 12701, exp: 5, s: 1, ttl: 64 };
    var tagVal = tag.label << 12 | tag.exp << 9 | tag.s << 8 | tag.ttl;

    var fv = Array(4);
    fv[0] = (tagVal >> 24) & 0xff;
    fv[1] = (tagVal >> 16) & 0xff;
    fv[2] = (tagVal >> 8) & 0xff;
    fv[3] = tagVal & 0xff;

    return fv;
}

We need one last thing - tell our preceding protocol Eth II (of which we are a payload) what our ethertype is -

protocol.protocolId = function() { return 0x8847; }

Putting all that together, here’s the full script -

protocol.name = 'MPLS';
protocol.protocolFrameSize = function() { return 4; }
protocol.protocolFrameValue = function()
{
    var tag = { label: 12701, exp: 5, s: 1, ttl: 64 };
    var tagVal = tag.label << 12 | tag.exp << 9 | tag.s << 8 | tag.ttl;

    var fv = Array(4);
    fv[0] = (tagVal >> 24) & 0xff;
    fv[1] = (tagVal >> 16) & 0xff;
    fv[2] = (tagVal >> 8) & 0xff;
    fv[3] = tagVal & 0xff;

    return fv;
}
protocol.protocolId = function() { return 0x8847; }

Copy paste the above script into the user script editor, and hit Compile (make sure the compilation results in Success) -

UserScript

To preview the result of our script, go to the Packet View tab and click on the MPLS header in the top pane - the MPLS header bytes will be highlighted in the bottom pane -

Packet View

The generated 32-bit value is 0x0319DB40 which is the same value that we calculated manually in the previous post

To confirm that that the MPLS header is indeed correct, we apply our changes, transmit the stream, capture the transmitted packet and view it in Wireshark -

Wireshark MPLS single tag

Voila, Wireshark decodes that perfectly!

What if you need more than one tag?

Simple -

  1. Change protocolFrameSize to return n*4 where n is the number of tags that we want
  2. Convert the single tag object to a list of tag objects
  3. Size the fv array to n*4
  4. Do the tagVal and fv calculation in a loop

Here’s an example for 3 tags -

protocol.name = 'MPLS';
protocol.protocolFrameSize = function() { return 3*4; }
protocol.protocolFrameValue = function()
{
    var tags = [
        { label: 12701, exp: 5, s: 0, ttl: 64 },
        { label: 972, exp: 1, s: 0, ttl: 128 },
        { label: 19, exp: 3, s: 1, ttl: 255 }
    ];

    var fv = Array(tags.length*4);
    for (i = 0; i < tags.length; i++) {
        var tag = tags[i];
	var tagVal = tag.label << 12 | tag.exp << 9 | tag.s << 8 | tag.ttl;

	fv[i*4+0] = (tagVal >> 24) & 0xff;
	fv[i*4+1] = (tagVal >> 16) & 0xff;
	fv[i*4+2] = (tagVal >> 8) & 0xff;
	fv[i*4+3] = tagVal & 0xff;
    }

    return fv;
}
protocol.protocolId = function() { return 0x8847; }

Here’s the 3-tag stack mpls frame in Wireshark -

Wireshark MPLS tag stack

Ostinato userscript has much more capabilities than we saw in this post. See the UserScript Reference Documentation for details.

Interested in more Ostinato tips and tricks? Subscribe to receive email updates!

Leave a Comment