The MPLS network based on P4

5 minute read

The MPLS network based on P4

In the previous post I described the implementation of the IP router in the P4 language. Now, I would like to move to a little bit more advanced technology - MPLS (Multi Protocol Label Switching). This post describes the demo of MPLS implemented in P4. The demo is avaiable on my GitHub. Note that the demo is just a Proof of Concept and the scenario may differ from the way how MPLS works in a real-world network.

Introduction to MPLS

MPLS is widely used in the Wide Area Networks (WANs) to provide reliable connections. An explanation of the MPLS protocol is out of the scope of this post. However, I was basing on this presentation.

The demo scenario

You can find the user guide and the source code of this demo on my GitHub

The test network (presented below) is composed of three routers and two end hosts - each associated to the different network. Hosts are attached to the devices (R1 and R3), which play the role of the edge MPLS router. The R2 router is the core MPLS router - it forwards packets based on the MPLS label. Packets from hosts are encapsulated to MPLS (and decapsulated from MPLS) by the edge routers.

mpls-network.png

The design of the MPLS router in P4

MPLS is a standard to simplify IP forwarding. Devices implementing MPLS are called Label Switching Router (LSR). LSRs use label to set up MPLS tunnel. The basic set of LSR’s functionalities consists of:

  • FEC (Forwarding Equivalence Class) classifier, which classifies packets into different classes and binds a label to particular class of packets.
  • Label operations:
    • Push a label - add the MPLS header to a packet
    • Swap a label - change a value of MPLS label
    • Pop a label - remove the MPLS header from a packet
  • Label-based forwarding - LSR determines the output port for a packet based on the input port and MPLS label.

Besides, LSR must implement also IP lookup to forward non-MPLS packets (or when the MPLS header is stripped out) and MAC-level switching.

The P4 program implementing MPLS is composed of 5 Match-Action tables:

  • fec_table - it implements a functionality of FEC classifier. We assume classification based on destination IP address (LPM), but the classification could be more granular. If a packet is classified, the push_mpls() method is invoked to add the MPLS header.
  • mpls_table - this table is used by transit or egress LSR. It determines to swap or pop the MPLS label based on the input port and MPLS label.
  • mplslookup_table - it forwards a packet to an output port based on the MPLS label (if exists).
  • iplookup_table - if the MPLS label doesn’t exists (it’s pure IP packet or MPLS label has been stripped out in the mpls_table) it performs IP lookup to determine the output port.
  • switching_table - it rewrites source and destination MAC addresses (per-hop behaviour).

Such design of the MPLS program aggregates all MPLS functionalities in the single P4 program. It means that the one subset of functionalities will be used by the core MPLS router and the different subset will be used by the edge MPLS router. The set of functionalities used by the MPLS router is configured by installing table entries.

The P4 code

In this section I will go through the P4 code implementing MPLS.

Let’s start from a definition of the MPLS header:

header_type mpls_t {
    fields {
        label : 20;
        tc : 3; // traffic class field
        bos : 1; // indicates if it's bottom of MPLS label's stack
        ttl: 8;
    }
}

It is composed of four fields. The most important is the label field, which is dedicated to store the tunnel identifier.

The MPLS header is extracted based on etherType by the P4 parser:

#define ETHERTYPE_MPLS 0x8847
(...)
parser parse_ethernet {
    extract(ethernet);
    return select(latest.etherType) {
        ETHERTYPE_IPV4 : parse_ipv4;
        ETHERTYPE_MPLS : parse_mpls;
        default: ingress;
    }
}

When extracted, packets are passed to the ingress pipeline. The pipeline defines the order of Match-Action tables that will handle packets.

control ingress {
    apply(fec_table);
    apply(mpls_table);
    apply(mplslookup_table);
    if (standard_metadata.egress_spec == 0) {
        apply(iplookup_table);
    }
    apply(switching_table);
}

Match-Action tables make use of the pre-defined MPLS actions: push_mpls(), pop_mpls() and swap_mpls():

action push_mpls(label) {
    add_header(mpls);
    modify_field(mpls.label, label);
    modify_field(mpls.tc, 7);
    modify_field(mpls.bos, 0x1);
    modify_field(mpls.ttl, 32);
    modify_field(ethernet.etherType, ETHERTYPE_MPLS);
}

action pop_mpls() {
    remove_header(mpls);
    modify_field(ethernet.etherType, ETHERTYPE_IPV4);
}

action swap_mpls(label) {
   modify_field(mpls.label, label);
   subtract_from_field(mpls.ttl, 1);
}

The push_mpls() action adds the MPLS header to a packet and sets a value of MPLS fields. Moreover, it modifies etherType to indicate the MPLS protocol. The pop_mpls() action removes the MPLS header and reverts a value of etherType to indicate the IPv4 protocol. The swap_mpls() action just changes a value of the MPLS label and decrements a value of TTL.

The first table that handles incoming packets is the fec_table. It classifies packets (based on the destination IP address) to the MPLS class (tunnel). If classified, a packet may be encapsulated by using the push_mpls() action.

table fec_table {

    reads {
        ipv4.dstAddr : lpm;
    }

    actions {
        push_mpls;
        _drop;
    }
}

The mpls_table is used to handle MPLS packets. It reads an input port and the MPLS label and decides to pop or swap the MPLS label.

table mpls_table {

    reads {
        standard_metadata.ingress_port: exact;
        mpls.label : exact;
    }

    actions {
        pop_mpls;
        swap_mpls;
        _drop;
    }
}

When a label is set packets enter mplslookup_table, which determines an output port based on the MPLS label.

table mplslookup_table {

    reads {
        mpls.label : exact;
    }

    actions {
        forward;
        _drop;
    }
}

The last two tables - iplookup_table and switching_table - implement IP routing and MAC rewriting, respectively. The former is used to determine an output port for IP packets (e.g. when packets are decapsulated from MPLS). The latter rewrites MAC addresses hop-by-hop.

Summary

In this post I described how to implement MPLS in the P4 language. The MPLS implementation is fairly straightforward and is much easier than writing the code in C, what’s a big advantage of the P4 language! In order to reproduce an experiment follow the steps listed in the user guide.

Leave a comment