Views
OpenFlowMPLS OpenFlow Specification
From OpenFlow Wiki
This page is no longer maintained. For up to date information on the OpenFlow MPLS Extension, please visit the main page and the pages linked from there: OpenFlowMPLS.
NOTE: These are simply the changes proposed by Ericsson Research and this is in no way related to the offical OpenFlow specification!
Contents |
Flow Table Changes
In order to support MPLS, the switch must be able to match on an MPLS label at the flow table. In fact, there are many common scenarios, such as egressing an MPLS tunnel, where a packet will need to match on two or more labels. One way to support this would be to match on only one label at a time and to require packets to be sent back to the flow table to match on additional labels. Since the vast majority of use cases in MPLS involve matching on only one or two labels, we have chosen instead to allow matching of up to two labels at once at the flow table. This requires extending the 10-tuple flow table entry to a 12-tuple by adding support for the top two MPLS labels on the packet's label stack.
The modified flow matching structure in OpenFlow thus looks like:
/* Fields to match against flows */
struct ofp_match
{
uint32_t wildcards; /* Wildcard fields. */
uint16_t in_port; /* Input switch port. */
uint8_t dl_src[OFP_ETH_ALEN]; /* Ethernet source address. */
uint8_t dl_dst[OFP_ETH_ALEN]; /* Ethernet destination address. */
uint16_t dl_vlan; /* Input VLAN. */
uint16_t dl_type; /* Ethernet frame type. */
uint8_t nw_proto; /* IP protocol. */
uint8_t pad; /* Align to 32-bits. */
uint32_t nw_src; /* IP source address. */
uint32_t nw_dst; /* IP destination address. */
uint16_t tp_src; /* TCP/UDP source port. */
uint16_t tp_dst; /* TCP/UDP destination port. */
uint32_t mpls_label1; /* Top MPLS label on stack. */
uint32_t mpls_label2; /* Second MPLS label (if available). */
};
Each MPLS label has 1-bit in the OpenFlow wildcards (ofp_flow_wildcards) so that it can be wildcarded:
OFPFW_MPLS_L1 = 1 << 21, /* MPLS Label 1. */ OFPFW_MPLS_L2 = 1 << 22, /* MPLS label 2. */
Flow Table Actions
Output Action
The output action is used to send packets out a particular physical port or to one of the virtual ports already in OpenFlow (e.g. OFPP_FLOOD, OFPP_CONTROLLER, OFPP_NONE, etc). This action has been expanded so that it can be used as the mechanism to send packets to the virtual port table as well. The original output action used a 16-bit port field. We expanded this field to 32-bits to support the new port space as shown in this figure.
The new output action:
struct ofp_action_output {
uint16_t type; /* OFPAT_OUTPUT. */
uint16_t len; /* Length is 8. */
// port space is now 32-bits to accomodate virtual ports
uint32_t port; /* Output port. */
uint16_t max_len; /* Max length to send to controller. */
uint8_t pad[6]; /* Padding. */
};
Rewrite MPLS Label Action
Since the current version of OpenFlow includes a separate action to rewrite each of the fields that can be matched, we have also included an action to rewrite the MPLS label field. This action will replace only the 20-bit label for the top label on the label stack with the value specified. It could be used to implement a swap action but this action will, of course, not handle decrementing the MPLS ttl field.
struct ofp_action_mpls_label {
uint16_t type; /* OFPPAT_SET_MPLS_LABEL. */
uint16_t len; /* Length is 8. */
uint32_t label_out; /* outgoing mpls label. */
};
Rewrite MPLS EXP Action
OpenFlow also supports an action to rewrite the VLAN PCP bits even though a switch cannot match on this field. Similarly, we have included an action to rewrite the MPLS exp bits even though the switch does not match on this field.
struct ofp_action_mpls_exp {
uint16_t type; /* OFPPAT_SET_MPLS_EXP. */
uint16_t len; /* Length is 8. */
uint8_t exp; /* experimental/class of service bits */
uint8_t pad[3];
};
Ericsson Extensions
In order to support MPLS, the only requirement at the flow table is the ability to forward to a virtual port. However, we expect that for efficiency, some switch implementations will want to perform some of the MPLS related actions at the flow table. As a result, all of the MPLS related actions available at the virtual port table will also be available at the flow table. Since the flow table in OpenFlow was not intended to support these type of actions, they should be completely optional and we have included them in our implementation as Ericsson extensions.
We defined a new file for ericsson extensions ericsson-ext.h that defines the following:
Ericsson Vendor ID:
#define ER_VENDOR_ID 0x000001EC
Header for ericsson flow table actions:
/* Header for Ericsson-defined actions
* Note that this is effectively an extension/subclass
* of ofpat_action_vendor_header defined in include/openflow/openflow.h */
struct er_action_header
{
uint16_t type; /* OFPAT_VENDOR. */
uint16_t len; /* Length is 16. */
uint32_t vendor; /* ER_VENDOR_ID. */
uint16_t subtype; /* Ericsson action type (er_action_type) */
uint8_t pad[6]; /* align to 64-bit boundary */
};
The currently defined ericsson actions:
enum er_action_type
{
ERXT_POP_MPLS, /* Pop an MLPS label. */
ERXT_PUSH_MPLS, /* Push an MPLS label. */
};
Header for the MPLS pop action:
/* Action structure for ERXT_POP_MPLS. */
struct er_action_pop_mpls
{
struct er_action_header erah;
struct action_pop_mpls apm;
};
Header for the MPLS pop action:
/* Action structure for ERXT_PUSH_MPLS. */
struct er_action_push_mpls
{
struct er_action_header erah;
struct action_push_mpls apm;
};
Note that the action_pop_mpls and action_push_mpls structs are defined in the virtual port table actions section.
Virtual Port Table
The most important change to OpenFlow is the addition of a virtual port table which we described in the Virtual Ports Section.
Here we describe the changes to the OpenFlow header to add and remove virtual port table entries.
/* Add or remove a virtual port */
struct ofp_vport_mod
{
struct ofp_header header;
uint32_t vport; /* virtual port number. */
uint32_t parent_port; /* parent port number */
/* Flow actions. */
uint16_t command; /* One of OFPFC_*. */
uint8_t pad[6]; /* Align to 32-bits. */
//uint32_t reserved; /* Reserved for future use. */
struct ofp_action_header actions[0]; /* Uses the same action header
as the flow-table.
The action length is inferred
from the length field in the
header. */
};
The above structure defines the message to add or remove a virtual port table entry. It is somewhat similar to how a flow table entry is added, a virtual port must be specified along with a command (add or remove) and then a list of actions to associate with the virtual port table entry. The parent_port, as the name implies, indicates the parent port of this virtual port table entry as described in Virtual Ports Section.
enum ofp_vport_mod_command
{
OFPVP_ADD, /* New virtual port. */
OFPVP_DELETE /* Delete virtual port. */
};
Virtual Port Table Actions
Supported virtual port table actions:
enum ofp_vport_action_type {
OFPPAT_OUTPUT, /* Output to switch port. */
OFPPAT_POP_MPLS, /* Pop MLPS label. */
OFPPAT_PUSH_MPLS, /* Push MPLS label. */
OFPPAT_SET_MPLS_LABEL, /* Rewrite MPLS label. */
OFPPAT_SET_MPLS_EXP, /* Rewrite MPLS exp bits. */
OFPPAT_VENDOR = 0xffff /* not yet supported. */
};
Pop MPLS action:
Header for pop MPLS action:
struct ofp_vport_action_pop_mpls
{
uint16_t type; /* OFPPAT_POP_MPLS. */
uint16_t len; /* Length is 8. */
struct action_pop_mpls apm;
uint8_t pad[4];
};
Action structure with parameters for pop mpls:
struct action_pop_mpls
{
uint16_t eth_type; /* eth_type of packet */
uint8_t flags; /* MPLS_POP_* flags */
uint8_t pad[5]; /* align to 64-bits */
};
The flags for the MPLS pop action are defined below:
/* flag values for mpls_pop action. */
enum {
MPLS_POP_DONT_POP = 1 << 0, /* Don't pop. */
MPLS_POP_DECREMENT_TTL = 1 << 1, /* Decrement the ttl. */
MPLS_POP_COPY_TTL = 1 << 2, /* Copy the ttl bits to the next header. */
MPLS_POP_COPY_EXP = 1 << 3 /* Copy the exp bits to the next header. */
};
The DECREMENT_TTL flag indicates that the time to live value of the label to be popped should be decremented. Note that this action will not deal with ttl expired exceptions. The COPY_TTL flag causes the ttl value of the popped label to be copied to the next label in the label stack or back to the IP header if the stack is empty. Similarly the COPY_EXP flag causes the exp bits to be copied to the next MPLS label or over the ToS bits in the IP Header. Finally, if the DONT_POP flag is set, the label is not actually popped. This can be useful to handle the ttl decrementing in the case where a label is swapped using the rewrite MPLS label action since that action does not decrement the ttl.
Push MPLS action:
Header for push MPLS action:
struct ofp_vport_action_push_mpls
{
uint16_t type; /* OFPAT_OFPPAT_PUSH_MPLS. */
uint16_t len; /* Length is 8. */
struct action_push_mpls apm;
uint8_t pad[4];
};
Action structure with parameters for push mpls:
struct action_push_mpls
{
uint32_t label_out; /* outgoing mpls label. */
uint8_t exp; /* exp/cos bits. */
uint8_t ttl; /* time to live. */
uint8_t flags; /* MPLS_PUSH_* flags */
uint8_t pad[1]; /* align to 64-bits */
};
/* flag values for mpls_push action. */
enum {
MPLS_PUSH_DECREMENT_TTL = 1 << 0, /* Decrement the ttl. */
MPLS_PUSH_TTL_NEXT = 1 << 1, /* Copy the ttl bits from the next header. */
MPLS_PUSH_EXP_NEXT = 1 << 2, /* Copy the exp bits from the next header. */
MPLS_PUSH_TTL_PREV = 1 << 3, /* Copy the ttl bits from the previous label.*/
MPLS_PUSH_EXP_PREV = 1 << 4 /* Copy the exp bits from the previous label. */
};
The DECREMENT_TTL flag allows the ttl field to be decremented on the label being pushed. When the TTL_NEXT field is set the ttl bits for the label to be pushed are taken from the next MPLS label in the label stack or from the IP header if the label stack is empty. The TTL_PREV flag, by contrast, causes the ttl bits for the label to be pushed to come from the last label that was popped (useful when implementing a swap using a pop followed by a push). The same two cases apply for the exp bits where the EXP_NEXT and EXP_PREV are used.
Rewrite MPLS Label Action
We support the MPLS-related rewrite actions as virtual port table actions as well.
Header for the rewrite MPLS label action:
struct ofp_vport_action_set_mpls_label
{
uint16_t type; /* OFPPAT_SET_MPLS_LABEL. */
uint16_t len; /* Length is 8. */
uint32_t label_out; /* outgoing mpls label. */
};
Rewrite MPLS Exp Bits Action
Header for rewrite MPLS exp bits action:
struct ofp_vport_action_set_mpls_exp
{
uint16_t type; /* OFPPAT_SET_MPLS_EXP. */
uint16_t len; /* Length is 8. */
uint8_t exp; /* experimental/class of service bits */
uint8_t pad[3];
};
TTL Exceptions
Handling the TTL field in the MPLS label is non-trivial with OpenFlow. While the IPv4 header also has a TTL field, there is currently no way to decrement the TTL field or manage the TTL expired exception in OpenFlow. While the Push & Pop MPLS actions include the functionality to decrement the TTL field, handling the TTL exception is non-trivial as it requires the actions to support conditional logic. We have chosen to assume that the handling of the TTL expired exception as part of the implicit logic of an MPLS-enabled OpenFlow switch. We have, however, added a mpls_ttl0_dropped counter to the ofp_port_stats message to keep track of the number of packets that have been dropped due to the MPLS TTL expired exception.
Stats
Statistics for virtual ports are requested in the same way as statistics for physical ports, that is by using an ofp_stats_req message with the type field set to OFPST_PORT. Virtual ports use the same ofp_port_stats structure as physical ports. Only the tx_packets and tx_bytes fields are returned for virtual ports, however, and the rest of the fields are unsupported.
Requesting port stats has been changed. Previously when requesting statistics for a port (OFPST_PORT), the body of the message was empty and there was no way to specify which port or ports to return statistics for. Now the body of the message contains a list of 32-bit port numbers that the switch should return statistics for. The number of port numbers requested is inferred from the length of the message.
Statistics can also be requested for the virtual port table using the ofp_stats_req message. The body will be empty and a new type field has been defined for this:
enum ofp_stats_types {
...
OFPST_PORT_TABLE,
...
};
The return message for the virtual port table statistics will use the following structure. The lookup_count field is the number of packets that have been looked up against the virtual port table. The port_match_count is the number of virtual port entries that have matched the lookups. When a virtual port has a parent, accessing the parent virtual port entry increments the chain_match_count.
struct ofp_vport_table_stats {
uint32_t max_vports; /* Max number of entries supported */
uint32_t active_vports; /* Number of active entries */
uint64_t lookup_count; /* Number of lookups performed */
uint64_t port_match_count; /* Number of entries matched in vport table */
uint64_t chain_match_count; /* Number of entries accessed by chaining */
};
Handshake Messages
When the switch responds to the features request message, the reply includes a bitmap of capabilities. This bitmap has been extended to include two new capabilities. The OFPC_VPORT_TABLE, indicates the switch supports a virtual port table. The OFPC_MPLS_ENABLED indicates the switch is MPLS enabled which means that it can match 2 MPLS labels at the flow table and supports the rewrite MPLS label and MPLS exp bits as actions at the flow table. The updated list of possible capabilities is shown below:
enum ofp_capabilities {
OFPC_FLOW_STATS = 1 << 0, /* Flow statistics. */
OFPC_TABLE_STATS = 1 << 1, /* Table statistics. */
OFPC_PORT_STATS = 1 << 2, /* Port statistics. */
OFPC_STP = 1 << 3, /* 802.1d spanning tree. */
OFPC_MULTI_PHY_TX = 1 << 4, /* Supports transmitting through multiple
physical interfaces */
OFPC_IP_REASM = 1 << 5, /* Can reassemble IP fragments. */
OFPC_VPORT_TABLE = 1 << 6, /* Supports a virtual port table
rest of virtual port table attributes
specified in ofp_vport_table_features */
OFPC_MPLS_ENABLED = 1 << 7 /* Switch is MPLS-enabled. */
};
If the switch supports a virtual port table, the controller will send a separate message to the switch to determine which actions are supported at the virtual port table. This message simply consists of the ofp_header with the type set to OFPT_FEATURES_REQUEST. The switch replies with the ofp_vport_table_features message which is defined below:
struct ofp_vport_table_features {
struct ofp_header header;
uint32_t max_vports; /* Max number of entries supported */
uint32_t actions; /* Bitmap of supported port table actions */
uint8_t mixed_chaining; /* 0 = false, 1 = true */
uint8_t pad[5]; /* align to 64-bits */
};
The actions field is a bitmap of supported actions at the virtual port table and these are currently defined to be:
enum ofp_vport_action_type {
OFPPAT_OUTPUT, /* Output to switch port . */
OFPPAT_POP_MPLS, /* Pop MLPS label. */
OFPPAT_PUSH_MPLS, /* Push MPLS label. */
OFPPAT_REWRITE_MPLS_LABEL, /* Rewrite MPLS label. */
OFPPAT_REWRITE_MPLS_EXP, /* Rewrite MPLS exp bits. */
};
The max_vports field indicates the maximum allowed number of virtual port table entries. This can also be found in the vport_table_stats message. The mixed_chaining field is used to indicate whether virtual ports with different actions can be mixed together. This is needed because the NetFPGA implementation cannot support chaining a virtual port table entry that performs a pop action to one that performs a push.
Note that at present these are mostly related to supporting MPLS. In the future this would need to be replaced with a more general list where protocol/vendor specific actions could be specified in a fashion similar to vendor extensions for actions at the flow table. Also note that a supported action is OFPPAT_OUTPUT which allows a copy of the packet to be sent to a virtual or physical port. The original copy of the packet will be processed through the chain of virtual ports until the parent_port at the virtual port table entry is a physical port or one of the originally defined virtual ports (e.g. OFPP_IN_PORT, OFPP_NONE, etc).
