From: Simon Horman Date: Fri, 25 Jan 2013 07:22:07 +0000 (+0900) Subject: User-Space MPLS actions and matches X-Git-Tag: sliver-openvswitch-1.10.90-1~11^2~78 X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;h=b02475c53b3ca857c45eb5e17d12fdf233a9dac8;hp=d224e3501437af4a8480d734a99ca371a1eafa11;p=sliver-openvswitch.git User-Space MPLS actions and matches This patch implements use-space datapath and non-datapath code to match and use the datapath API set out in Leo Alterman's patch "user-space datapath: Add basic MPLS support to kernel". The resulting MPLS implementation supports: * Pushing a single MPLS label * Poping a single MPLS label * Modifying an MPLS lable using set-field or load actions that act on the label value, tc and bos bit. * There is no support for manipulating the TTL this is considered future work. The single-level push pop limitation is implemented by processing push, pop and set-field/load actions in order and discarding information that would require multiple levels of push/pop to be supported. e.g. push,push -> the first push is discarded pop,pop -> the first pop is discarded This patch is based heavily on work by Ravi K. Cc: Ravi K Reviewed-by: Isaku Yamahata Signed-off-by: Simon Horman Signed-off-by: Ben Pfaff --- diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h index b12bf0c83..7c6e3abc6 100644 --- a/include/linux/openvswitch.h +++ b/include/linux/openvswitch.h @@ -283,6 +283,8 @@ enum ovs_key_attr { #ifdef __KERNEL__ OVS_KEY_ATTR_IPV4_TUNNEL, /* struct ovs_key_ipv4_tunnel */ #endif + + OVS_KEY_ATTR_MPLS = 62, /* struct ovs_key_mpls */ OVS_KEY_ATTR_TUN_ID = 63, /* be64 tunnel ID */ __OVS_KEY_ATTR_MAX }; @@ -325,6 +327,10 @@ struct ovs_key_ethernet { __u8 eth_dst[6]; }; +struct ovs_key_mpls { + __be32 mpls_top_lse; +}; + struct ovs_key_ipv4 { __be32 ipv4_src; __be32 ipv4_dst; @@ -454,6 +460,19 @@ enum ovs_userspace_attr { #define OVS_USERSPACE_ATTR_MAX (__OVS_USERSPACE_ATTR_MAX - 1) +/** + * struct ovs_action_push_mpls - %OVS_ACTION_ATTR_PUSH_MPLS action argument. + * @mpls_lse: MPLS label stack entry to push. + * @mpls_ethertype: Ethertype to set in the encapsulating ethernet frame. + * + * The only values @mpls_ethertype should ever be given are %ETH_P_MPLS_UC and + * %ETH_P_MPLS_MC, indicating MPLS unicast or multicast. Other are rejected. + */ +struct ovs_action_push_mpls { + __be32 mpls_lse; + __be16 mpls_ethertype; /* Either %ETH_P_MPLS_UC or %ETH_P_MPLS_MC */ +}; + /** * struct ovs_action_push_vlan - %OVS_ACTION_ATTR_PUSH_VLAN action argument. * @vlan_tpid: Tag protocol identifier (TPID) to push. @@ -476,14 +495,23 @@ struct ovs_action_push_vlan { * @OVS_ACTION_ATTR_OUTPUT: Output packet to port. * @OVS_ACTION_ATTR_USERSPACE: Send packet to userspace according to nested * %OVS_USERSPACE_ATTR_* attributes. - * @OVS_ACTION_ATTR_SET: Replaces the contents of an existing header. The - * single nested %OVS_KEY_ATTR_* attribute specifies a header to modify and its - * value. * @OVS_ACTION_ATTR_PUSH_VLAN: Push a new outermost 802.1Q header onto the * packet. * @OVS_ACTION_ATTR_POP_VLAN: Pop the outermost 802.1Q header off the packet. * @OVS_ACTION_ATTR_SAMPLE: Probabilitically executes actions, as specified in * the nested %OVS_SAMPLE_ATTR_* attributes. + * @OVS_ACTION_ATTR_SET: Replaces the contents of an existing header. The + * single nested %OVS_KEY_ATTR_* attribute specifies a header to modify and its + * value. + * @OVS_ACTION_ATTR_PUSH_MPLS: Push a new MPLS label stack entry onto the + * top of the packets MPLS label stack. Set the ethertype of the + * encapsulating frame to either %ETH_P_MPLS_UC or %ETH_P_MPLS_MC to + * indicate the new packet contents. + * @OVS_ACTION_ATTR_POP_MPLS: Pop an MPLS label stack entry off of the + * packet's MPLS label stack. Set the encapsulating frame's ethertype to + * indicate the new packet contents This could potentially still be + * %ETH_P_MPLS_* if the resulting MPLS label stack is not empty. If there + * is no MPLS label stack, as determined by ethertype, no action is taken. * * Only a single header can be set with a single %OVS_ACTION_ATTR_SET. Not all * fields within a header are modifiable, e.g. the IPv4 protocol and fragment @@ -498,6 +526,8 @@ enum ovs_action_attr { OVS_ACTION_ATTR_PUSH_VLAN, /* struct ovs_action_push_vlan. */ OVS_ACTION_ATTR_POP_VLAN, /* No argument. */ OVS_ACTION_ATTR_SAMPLE, /* Nested OVS_SAMPLE_ATTR_*. */ + OVS_ACTION_ATTR_PUSH_MPLS, /* struct ovs_action_push_mpls. */ + OVS_ACTION_ATTR_POP_MPLS, /* __be16 ethertype. */ __OVS_ACTION_ATTR_MAX }; diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h index 91c96b36a..afc2e612e 100644 --- a/include/openflow/nicira-ext.h +++ b/include/openflow/nicira-ext.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc. + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -304,6 +304,8 @@ enum nx_action_subtype { NXAST_CONTROLLER, /* struct nx_action_controller */ NXAST_DEC_TTL_CNT_IDS, /* struct nx_action_cnt_ids */ NXAST_WRITE_METADATA, /* struct nx_action_write_metadata */ + NXAST_PUSH_MPLS, /* struct nx_action_push_mpls */ + NXAST_POP_MPLS, /* struct nx_action_pop_mpls */ }; /* Header for Nicira-defined actions. */ @@ -2212,4 +2214,26 @@ struct nx_action_write_metadata { }; OFP_ASSERT(sizeof(struct nx_action_write_metadata) == 32); +/* Action structure for NXAST_PUSH_MPLS. */ +struct nx_action_push_mpls { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* Length is 8. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_PUSH_MPLS. */ + ovs_be16 ethertype; /* Ethertype */ + uint8_t pad[4]; +}; +OFP_ASSERT(sizeof(struct nx_action_push_mpls) == 16); + +/* Action structure for NXAST_POP_MPLS. */ +struct nx_action_pop_mpls { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* Length is 8. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_POP_MPLS. */ + ovs_be16 ethertype; /* Ethertype */ + uint8_t pad[4]; +}; +OFP_ASSERT(sizeof(struct nx_action_pop_mpls) == 16); + #endif /* openflow/nicira-ext.h */ diff --git a/include/openflow/openflow-1.1.h b/include/openflow/openflow-1.1.h index 8dfd795c6..ec94cee79 100644 --- a/include/openflow/openflow-1.1.h +++ b/include/openflow/openflow-1.1.h @@ -204,8 +204,8 @@ enum ofp11_action_type { OFPAT11_PUSH_VLAN, /* Push a new VLAN tag */ OFPAT11_POP_VLAN, /* Pop the outer VLAN tag */ - OFPAT11_PUSH_MPLS, /* Push a new MPLS tag */ - OFPAT11_POP_MPLS, /* Pop the outer MPLS tag */ + OFPAT11_PUSH_MPLS, /* Push a new MPLS Label Stack Entry */ + OFPAT11_POP_MPLS, /* Pop the outer MPLS Label Stack Entry */ OFPAT11_SET_QUEUE, /* Set queue id when outputting to a port */ OFPAT11_GROUP, /* Apply group. */ OFPAT11_SET_NW_TTL, /* IP TTL. */ diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index 038dfdc9a..d315b5941 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -1233,6 +1233,10 @@ execute_set_action(struct ofpbuf *packet, const struct nlattr *a) packet_set_udp_port(packet, udp_key->udp_src, udp_key->udp_dst); break; + case OVS_KEY_ATTR_MPLS: + set_mpls_lse(packet, nl_attr_get_be32(a)); + break; + case OVS_KEY_ATTR_UNSPEC: case OVS_KEY_ATTR_ENCAP: case OVS_KEY_ATTR_ETHERTYPE: @@ -1279,6 +1283,16 @@ dp_netdev_execute_actions(struct dp_netdev *dp, eth_pop_vlan(packet); break; + case OVS_ACTION_ATTR_PUSH_MPLS: { + const struct ovs_action_push_mpls *mpls = nl_attr_get(a); + push_mpls(packet, mpls->mpls_ethertype, mpls->mpls_lse); + break; + } + + case OVS_ACTION_ATTR_POP_MPLS: + pop_mpls(packet, nl_attr_get_be16(a)); + break; + case OVS_ACTION_ATTR_SET: execute_set_action(packet, nl_attr_get(a)); break; diff --git a/lib/flow.c b/lib/flow.c index 2a3dd3d93..5e7d1d47d 100644 --- a/lib/flow.c +++ b/lib/flow.c @@ -93,6 +93,21 @@ pull_icmpv6(struct ofpbuf *packet) return ofpbuf_try_pull(packet, sizeof(struct icmp6_hdr)); } +static void +parse_mpls(struct ofpbuf *b, struct flow *flow) +{ + struct mpls_hdr *mh; + + while ((mh = ofpbuf_try_pull(b, sizeof *mh))) { + if (flow->mpls_depth++ == 0) { + flow->mpls_lse = mh->mpls_lse; + } + if (mh->mpls_lse & htonl(MPLS_BOS_MASK)) { + break; + } + } +} + static void parse_vlan(struct ofpbuf *b, struct flow *flow) { @@ -324,6 +339,8 @@ invalid: * * - packet->l2 to the start of the Ethernet header. * + * - packet->l2_5 to the start of the MPLS shim header. + * * - packet->l3 to just past the Ethernet header, or just past the * vlan_header if one is present, to the first byte of the payload of the * Ethernet frame. @@ -354,10 +371,11 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority, uint32_t skb_mark, flow->skb_priority = skb_priority; flow->skb_mark = skb_mark; - packet->l2 = b.data; - packet->l3 = NULL; - packet->l4 = NULL; - packet->l7 = NULL; + packet->l2 = b.data; + packet->l2_5 = NULL; + packet->l3 = NULL; + packet->l4 = NULL; + packet->l7 = NULL; if (b.size < sizeof *eth) { return; @@ -375,6 +393,12 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority, uint32_t skb_mark, } flow->dl_type = parse_ethertype(&b); + /* Parse mpls, copy l3 ttl. */ + if (eth_type_mpls(flow->dl_type)) { + packet->l2_5 = b.data; + parse_mpls(&b, flow); + } + packet->l3 = b.data; flow_extract_l3_onwards(packet, flow, flow->dl_type); } @@ -487,7 +511,7 @@ flow_zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards) void flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd) { - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 19); fmd->tun_id = flow->tunnel.tun_id; fmd->metadata = flow->metadata; @@ -811,6 +835,29 @@ flow_set_vlan_pcp(struct flow *flow, uint8_t pcp) flow->vlan_tci |= htons((pcp << VLAN_PCP_SHIFT) | VLAN_CFI); } +/* Sets the MPLS Label that 'flow' matches to 'label', which is interpreted + * as an OpenFlow 1.1 "mpls_label" value. */ +void +flow_set_mpls_label(struct flow *flow, ovs_be32 label) +{ + set_mpls_lse_label(&flow->mpls_lse, label); +} + +/* Sets the MPLS TC that 'flow' matches to 'tc', which should be in the + * range 0...7. */ +void +flow_set_mpls_tc(struct flow *flow, uint8_t tc) +{ + set_mpls_lse_tc(&flow->mpls_lse, tc); +} + +/* Sets the MPLS BOS bit that 'flow' matches to which should be 0 or 1. */ +void +flow_set_mpls_bos(struct flow *flow, uint8_t bos) +{ + set_mpls_lse_bos(&flow->mpls_lse, bos); +} + /* Puts into 'b' a packet that flow_extract() would parse as having the given * 'flow'. * @@ -820,6 +867,9 @@ flow_set_vlan_pcp(struct flow *flow, uint8_t pcp) void flow_compose(struct ofpbuf *b, const struct flow *flow) { + ovs_be16 inner_dl_type; + + inner_dl_type = flow_innermost_dl_type(flow); eth_compose(b, flow->dl_dst, flow->dl_src, ntohs(flow->dl_type), 0); if (flow->dl_type == htons(FLOW_DL_TYPE_NONE)) { struct eth_header *eth = b->l2; @@ -831,7 +881,7 @@ flow_compose(struct ofpbuf *b, const struct flow *flow) eth_push_vlan(b, flow->vlan_tci); } - if (flow->dl_type == htons(ETH_TYPE_IP)) { + if (inner_dl_type == htons(ETH_TYPE_IP)) { struct ip_header *ip; b->l3 = ip = ofpbuf_put_zeros(b, sizeof *ip); @@ -877,10 +927,10 @@ flow_compose(struct ofpbuf *b, const struct flow *flow) ip->ip_tot_len = htons((uint8_t *) b->data + b->size - (uint8_t *) b->l3); ip->ip_csum = csum(ip, sizeof *ip); - } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) { + } else if (inner_dl_type == htons(ETH_TYPE_IPV6)) { /* XXX */ - } else if (flow->dl_type == htons(ETH_TYPE_ARP) || - flow->dl_type == htons(ETH_TYPE_RARP)) { + } else if (inner_dl_type == htons(ETH_TYPE_ARP) || + inner_dl_type == htons(ETH_TYPE_RARP)) { struct arp_eth_header *arp; b->l3 = arp = ofpbuf_put_zeros(b, sizeof *arp); @@ -898,6 +948,11 @@ flow_compose(struct ofpbuf *b, const struct flow *flow) memcpy(arp->ar_tha, flow->arp_tha, ETH_ADDR_LEN); } } + + if (eth_type_mpls(flow->dl_type)) { + b->l2_5 = b->l3; + push_mpls(b, flow->dl_type, flow->mpls_lse); + } } /* Compressed flow. */ diff --git a/lib/flow.h b/lib/flow.h index 8e79e62f5..e6da48050 100644 --- a/lib/flow.h +++ b/lib/flow.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc. + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +36,7 @@ struct ofpbuf; /* This sequence number should be incremented whenever anything involving flows * or the wildcarding of flows changes. This will cause build assertion * failures in places which likely need to be updated. */ -#define FLOW_WC_SEQ 18 +#define FLOW_WC_SEQ 19 #define FLOW_N_REGS 8 BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS); @@ -91,8 +91,11 @@ struct flow { unless in DPIF code, in which case it is the datapath port number. */ uint32_t skb_mark; /* Packet mark. */ + ovs_be32 mpls_lse; /* MPLS label stack entry. */ + uint16_t mpls_depth; /* Depth of MPLS stack. */ ovs_be16 vlan_tci; /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */ ovs_be16 dl_type; /* Ethernet frame type. */ + ovs_be16 encap_dl_type; /* MPLS encapsulated Ethernet frame type */ ovs_be16 tp_src; /* TCP/UDP source port. */ ovs_be16 tp_dst; /* TCP/UDP destination port. */ uint8_t dl_src[6]; /* Ethernet source address. */ @@ -110,8 +113,8 @@ BUILD_ASSERT_DECL(sizeof(struct flow) % 4 == 0); #define FLOW_U32S (sizeof(struct flow) / 4) /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */ -BUILD_ASSERT_DECL(sizeof(struct flow) == sizeof(struct flow_tnl) + 152 && - FLOW_WC_SEQ == 18); +BUILD_ASSERT_DECL(sizeof(struct flow) == sizeof(struct flow_tnl) + 160 && + FLOW_WC_SEQ == 19); /* Represents the metadata fields of struct flow. */ struct flow_metadata { @@ -125,6 +128,18 @@ void flow_extract(struct ofpbuf *, uint32_t priority, uint32_t mark, const struct flow_tnl *, uint16_t in_port, struct flow *); void flow_extract_l3_onwards(struct ofpbuf *, struct flow *, ovs_be16 dl_type); + +/* Returns the innermost dl_type. + * If there's an outer and an inner type then the inner type is returned. + * Otherwise, if there is only one type then it is returned. */ +static inline ovs_be16 +flow_innermost_dl_type(const struct flow *flow) +{ + return (flow->encap_dl_type == htons(0) + ? flow->dl_type + : flow->encap_dl_type); +} + void flow_zero_wildcards(struct flow *, const struct flow_wildcards *); void flow_get_metadata(const struct flow *, struct flow_metadata *); @@ -142,6 +157,11 @@ void flow_set_dl_vlan(struct flow *, ovs_be16 vid); void flow_set_vlan_vid(struct flow *, ovs_be16 vid); void flow_set_vlan_pcp(struct flow *, uint8_t pcp); +void flow_set_mpls_label(struct flow *flow, ovs_be32 label); +void flow_set_mpls_ttl(struct flow *flow, uint8_t ttl); +void flow_set_mpls_tc(struct flow *flow, uint8_t tc); +void flow_set_mpls_bos(struct flow *flow, uint8_t stack); + void flow_compose(struct ofpbuf *, const struct flow *); static inline int diff --git a/lib/match.c b/lib/match.c index bedb1a1d2..f4b0a6c14 100644 --- a/lib/match.c +++ b/lib/match.c @@ -460,6 +460,57 @@ match_set_dl_vlan_pcp(struct match *match, uint8_t dl_vlan_pcp) match->wc.masks.vlan_tci |= htons(VLAN_CFI | VLAN_PCP_MASK); } +/* Modifies 'match' so that the MPLS label is wildcarded. */ +void +match_set_any_mpls_label(struct match *match) +{ + match->wc.masks.mpls_lse &= ~htonl(MPLS_LABEL_MASK); + flow_set_mpls_label(&match->flow, htonl(0)); +} + +/* Modifies 'match' so that it matches only packets with an MPLS header whose + * label equals the low 20 bits of 'mpls_label'. */ +void +match_set_mpls_label(struct match *match, ovs_be32 mpls_label) +{ + match->wc.masks.mpls_lse |= htonl(MPLS_LABEL_MASK); + flow_set_mpls_label(&match->flow, mpls_label); +} + +/* Modifies 'match' so that the MPLS TC is wildcarded. */ +void +match_set_any_mpls_tc(struct match *match) +{ + match->wc.masks.mpls_lse &= ~htonl(MPLS_TC_MASK); + flow_set_mpls_tc(&match->flow, 0); +} + +/* Modifies 'match' so that it matches only packets with an MPLS header whose + * Traffic Class equals the low 3 bits of 'mpls_tc'. */ +void +match_set_mpls_tc(struct match *match, uint8_t mpls_tc) +{ + match->wc.masks.mpls_lse |= htonl(MPLS_TC_MASK); + flow_set_mpls_tc(&match->flow, mpls_tc); +} + +/* Modifies 'match' so that the MPLS stack flag is wildcarded. */ +void +match_set_any_mpls_bos(struct match *match) +{ + match->wc.masks.mpls_lse &= ~htonl(MPLS_BOS_MASK); + flow_set_mpls_bos(&match->flow, 0); +} + +/* Modifies 'match' so that it matches only packets with an MPLS header whose + * Stack Flag equals the lower bit of 'mpls_bos' */ +void +match_set_mpls_bos(struct match *match, uint8_t mpls_bos) +{ + match->wc.masks.mpls_lse |= htonl(MPLS_BOS_MASK); + flow_set_mpls_bos(&match->flow, mpls_bos); +} + void match_set_tp_src(struct match *match, ovs_be16 tp_src) { @@ -767,6 +818,24 @@ format_flow_tunnel(struct ds *s, const struct match *match) } } +static void +flow_format_mpls(const struct flow *flow, struct ds *s) +{ + if (flow->dl_type == htons(ETH_TYPE_MPLS)) { + ds_put_cstr(s, "mpls"); + } else if (flow->dl_type == htons(ETH_TYPE_MPLS_MCAST)) { + ds_put_cstr(s, "mplsm"); + } else { + return; + } + + ds_put_format(s, "(label:%"PRIu32",tc:%d,ttl:%d,bos:%d),", + mpls_lse_to_label(flow->mpls_lse), + mpls_lse_to_tc(flow->mpls_lse), + mpls_lse_to_ttl(flow->mpls_lse), + mpls_lse_to_bos(flow->mpls_lse)); +} + /* Appends a string representation of 'match' to 's'. If 'priority' is * different from OFP_DEFAULT_PRIORITY, includes it in 's'. */ void @@ -780,7 +849,7 @@ match_format(const struct match *match, struct ds *s, unsigned int priority) int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 19); if (priority != OFP_DEFAULT_PRIORITY) { ds_put_format(s, "priority=%u,", priority); @@ -832,6 +901,8 @@ match_format(const struct match *match, struct ds *s, unsigned int priority) ds_put_cstr(s, "arp,"); } else if (f->dl_type == htons(ETH_TYPE_RARP)) { ds_put_cstr(s, "rarp,"); + } else if (f->mpls_depth) { + flow_format_mpls(f, s); } else { skip_type = false; } @@ -940,6 +1011,18 @@ match_format(const struct match *match, struct ds *s, unsigned int priority) if (wc->masks.nw_ttl) { ds_put_format(s, "nw_ttl=%"PRIu8",", f->nw_ttl); } + if (wc->masks.mpls_lse & htonl(MPLS_LABEL_MASK)) { + ds_put_format(s, "mpls_label=%"PRIu32",", + mpls_lse_to_label(f->mpls_lse)); + } + if (wc->masks.mpls_lse & htonl(MPLS_TC_MASK)) { + ds_put_format(s, "mpls_tc=%"PRIu8",", + mpls_lse_to_tc(f->mpls_lse)); + } + if (wc->masks.mpls_lse & htonl(MPLS_BOS_MASK)) { + ds_put_format(s, "mpls_bos=%"PRIu8",", + mpls_lse_to_bos(f->mpls_lse)); + } switch (wc->masks.nw_frag) { case FLOW_NW_FRAG_ANY | FLOW_NW_FRAG_LATER: ds_put_format(s, "nw_frag=%s,", diff --git a/lib/match.h b/lib/match.h index ff0b5f2ad..d435aa4ec 100644 --- a/lib/match.h +++ b/lib/match.h @@ -78,6 +78,12 @@ void match_set_vlan_vid(struct match *, ovs_be16); void match_set_vlan_vid_masked(struct match *, ovs_be16 vid, ovs_be16 mask); void match_set_any_pcp(struct match *); void match_set_dl_vlan_pcp(struct match *, uint8_t); +void match_set_any_mpls_label(struct match *); +void match_set_mpls_label(struct match *, ovs_be32); +void match_set_any_mpls_tc(struct match *); +void match_set_mpls_tc(struct match *, uint8_t); +void match_set_any_mpls_bos(struct match *); +void match_set_mpls_bos(struct match *, uint8_t); void match_set_tp_src(struct match *, ovs_be16); void match_set_tp_src_masked(struct match *, ovs_be16 port, ovs_be16 mask); void match_set_tp_dst(struct match *, ovs_be16); diff --git a/lib/meta-flow.c b/lib/meta-flow.c index b53465c74..6f7a3aac4 100644 --- a/lib/meta-flow.c +++ b/lib/meta-flow.c @@ -256,6 +256,38 @@ static const struct mf_field mf_fields[MFF_N_IDS] = { OXM_OF_VLAN_PCP, "OXM_OF_VLAN_PCP", }, + /* ## ---- ## */ + /* ## L2.5 ## */ + /* ## ---- ## */ + { + MFF_MPLS_LABEL, "mpls_label", NULL, + 4, 20, + MFM_NONE, + MFS_DECIMAL, + MFP_MPLS, + true, + OXM_OF_MPLS_LABEL, "OXM_OF_MPLS_LABEL", + OXM_OF_MPLS_LABEL, "OXM_OF_MPLS_LABEL", + }, { + MFF_MPLS_TC, "mpls_tc", NULL, + 1, 3, + MFM_NONE, + MFS_DECIMAL, + MFP_MPLS, + true, + OXM_OF_MPLS_TC, "OXM_OF_MPLS_TC", + OXM_OF_MPLS_TC, "OXM_OF_MPLS_TC", + }, { + MFF_MPLS_BOS, "mpls_bos", NULL, + 1, 1, + MFM_NONE, + MFS_DECIMAL, + MFP_MPLS, + false, + OXM_OF_MPLS_BOS, "OXM_OF_MPLS_BOS", + OXM_OF_MPLS_BOS, "OXM_OF_MPLS_BOS", + }, + /* ## -- ## */ /* ## L3 ## */ /* ## -- ## */ @@ -678,6 +710,13 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc) case MFF_VLAN_PCP: return !(wc->masks.vlan_tci & htons(VLAN_PCP_MASK)); + case MFF_MPLS_LABEL: + return !(wc->masks.mpls_lse & htonl(MPLS_LABEL_MASK)); + case MFF_MPLS_TC: + return !(wc->masks.mpls_lse & htonl(MPLS_TC_MASK)); + case MFF_MPLS_BOS: + return !(wc->masks.mpls_lse & htonl(MPLS_BOS_MASK)); + case MFF_IPV4_SRC: return !wc->masks.nw_src; case MFF_IPV4_DST: @@ -791,6 +830,8 @@ mf_are_prereqs_ok(const struct mf_field *mf, const struct flow *flow) return flow->dl_type == htons(ETH_TYPE_IPV6); case MFP_VLAN_VID: return (flow->vlan_tci & htons(VLAN_CFI)) != 0; + case MFP_MPLS: + return eth_type_mpls(flow->dl_type); case MFP_IP_ANY: return is_ip_any(flow); @@ -895,6 +936,15 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value) case MFF_IPV6_LABEL: return !(value->be32 & ~htonl(IPV6_LABEL_MASK)); + case MFF_MPLS_LABEL: + return !(value->be32 & ~htonl(MPLS_LABEL_MASK >> MPLS_LABEL_SHIFT)); + + case MFF_MPLS_TC: + return !(value->u8 & ~(MPLS_TC_MASK >> MPLS_TC_SHIFT)); + + case MFF_MPLS_BOS: + return !(value->u8 & ~(MPLS_BOS_MASK >> MPLS_BOS_SHIFT)); + case MFF_N_IDS: default: NOT_REACHED(); @@ -975,6 +1025,18 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow, value->u8 = vlan_tci_to_pcp(flow->vlan_tci); break; + case MFF_MPLS_LABEL: + value->be32 = htonl(mpls_lse_to_label(flow->mpls_lse)); + break; + + case MFF_MPLS_TC: + value->u8 = mpls_lse_to_tc(flow->mpls_lse); + break; + + case MFF_MPLS_BOS: + value->u8 = mpls_lse_to_bos(flow->mpls_lse); + break; + case MFF_IPV4_SRC: value->be32 = flow->nw_src; break; @@ -1142,6 +1204,18 @@ mf_set_value(const struct mf_field *mf, match_set_dl_vlan_pcp(match, value->u8); break; + case MFF_MPLS_LABEL: + match_set_mpls_label(match, value->be32); + break; + + case MFF_MPLS_TC: + match_set_mpls_tc(match, value->u8); + break; + + case MFF_MPLS_BOS: + match_set_mpls_bos(match, value->u8); + break; + case MFF_IPV4_SRC: match_set_nw_src(match, value->be32); break; @@ -1309,6 +1383,18 @@ mf_set_flow_value(const struct mf_field *mf, flow_set_vlan_pcp(flow, value->u8); break; + case MFF_MPLS_LABEL: + flow_set_mpls_label(flow, value->be32); + break; + + case MFF_MPLS_TC: + flow_set_mpls_tc(flow, value->u8); + break; + + case MFF_MPLS_BOS: + flow_set_mpls_bos(flow, value->u8); + break; + case MFF_IPV4_SRC: flow->nw_src = value->be32; break; @@ -1495,6 +1581,18 @@ mf_set_wild(const struct mf_field *mf, struct match *match) match_set_any_pcp(match); break; + case MFF_MPLS_LABEL: + match_set_any_mpls_label(match); + break; + + case MFF_MPLS_TC: + match_set_any_mpls_tc(match); + break; + + case MFF_MPLS_BOS: + match_set_any_mpls_bos(match); + break; + case MFF_IPV4_SRC: case MFF_ARP_SPA: match_set_nw_src_masked(match, htonl(0), htonl(0)); @@ -1622,6 +1720,9 @@ mf_set(const struct mf_field *mf, case MFF_DL_VLAN: case MFF_DL_VLAN_PCP: case MFF_VLAN_PCP: + case MFF_MPLS_LABEL: + case MFF_MPLS_TC: + case MFF_MPLS_BOS: case MFF_IP_PROTO: case MFF_IP_TTL: case MFF_IP_DSCP: @@ -1879,6 +1980,18 @@ mf_random_value(const struct mf_field *mf, union mf_value *value) value->u8 &= 0x07; break; + case MFF_MPLS_LABEL: + value->be32 &= htonl(MPLS_LABEL_MASK >> MPLS_LABEL_SHIFT); + break; + + case MFF_MPLS_TC: + value->u8 &= MPLS_TC_MASK >> MPLS_TC_SHIFT; + break; + + case MFF_MPLS_BOS: + value->u8 &= MPLS_BOS_MASK >> MPLS_BOS_SHIFT; + break; + case MFF_N_IDS: default: NOT_REACHED(); diff --git a/lib/meta-flow.h b/lib/meta-flow.h index 367588364..57f6df57f 100644 --- a/lib/meta-flow.h +++ b/lib/meta-flow.h @@ -78,6 +78,11 @@ enum mf_field_id { MFF_DL_VLAN_PCP, /* u8 (OpenFlow 1.0 compatibility) */ MFF_VLAN_PCP, /* be16 (OpenFlow 1.2 compatibility) */ + /* L2.5 */ + MFF_MPLS_LABEL, /* be32 */ + MFF_MPLS_TC, /* u8 */ + MFF_MPLS_BOS, /* u8 */ + /* L3. */ MFF_IPV4_SRC, /* be32 */ MFF_IPV4_DST, /* be32 */ @@ -168,6 +173,9 @@ enum mf_prereqs { MFP_IPV6, MFP_IP_ANY, + /* L2.5 requirements. */ + MFP_MPLS, + /* L2+L3 requirements. */ MFP_TCP, /* On IPv4 or IPv6. */ MFP_UDP, /* On IPv4 or IPv6. */ @@ -220,6 +228,9 @@ struct mf_field { * - "dl_vlan_pcp" is 1 byte but only 3 bits. * - "is_frag" is 1 byte but only 2 bits. * - "ipv6_label" is 4 bytes but only 20 bits. + * - "mpls_label" is 4 bytes but only 20 bits. + * - "mpls_tc" is 1 byte but only 3 bits. + * - "mpls_bos" is 1 byte but only 1 bit. */ unsigned int n_bytes; /* Width of the field in bytes. */ unsigned int n_bits; /* Number of significant bits in field. */ diff --git a/lib/nx-match.c b/lib/nx-match.c index 4d7fcd6cd..4ff516ee4 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -573,7 +573,7 @@ nx_put_raw(struct ofpbuf *b, bool oxm, const struct match *match, int match_len; int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 19); /* Metadata. */ if (match->wc.masks.in_port) { @@ -615,6 +615,22 @@ nx_put_raw(struct ofpbuf *b, bool oxm, const struct match *match, match->wc.masks.vlan_tci); } + /* MPLS. */ + if (eth_type_mpls(flow->dl_type)) { + if (match->wc.masks.mpls_lse & htonl(MPLS_TC_MASK)) { + nxm_put_8(b, OXM_OF_MPLS_TC, mpls_lse_to_tc(flow->mpls_lse)); + } + + if (match->wc.masks.mpls_lse & htonl(MPLS_BOS_MASK)) { + nxm_put_8(b, OXM_OF_MPLS_BOS, mpls_lse_to_bos(flow->mpls_lse)); + } + + if (match->wc.masks.mpls_lse & htonl(MPLS_LABEL_MASK)) { + nxm_put_32(b, OXM_OF_MPLS_LABEL, + htonl(mpls_lse_to_label(flow->mpls_lse))); + } + } + /* L3. */ if (flow->dl_type == htons(ETH_TYPE_IP)) { /* IP. */ diff --git a/lib/odp-util.c b/lib/odp-util.c index 7e4898147..355a3afe0 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -73,6 +73,8 @@ odp_action_len(uint16_t type) case OVS_ACTION_ATTR_USERSPACE: return -2; case OVS_ACTION_ATTR_PUSH_VLAN: return sizeof(struct ovs_action_push_vlan); case OVS_ACTION_ATTR_POP_VLAN: return 0; + case OVS_ACTION_ATTR_PUSH_MPLS: return sizeof(struct ovs_action_push_mpls); + case OVS_ACTION_ATTR_POP_MPLS: return sizeof(ovs_be16); case OVS_ACTION_ATTR_SET: return -2; case OVS_ACTION_ATTR_SAMPLE: return -2; @@ -108,6 +110,7 @@ ovs_key_attr_to_string(enum ovs_key_attr attr) case OVS_KEY_ATTR_ICMPV6: return "icmpv6"; case OVS_KEY_ATTR_ARP: return "arp"; case OVS_KEY_ATTR_ND: return "nd"; + case OVS_KEY_ATTR_MPLS: return "mpls"; case __OVS_KEY_ATTR_MAX: default: @@ -300,6 +303,16 @@ format_vlan_tci(struct ds *ds, ovs_be16 vlan_tci) } } +static void +format_mpls_lse(struct ds *ds, ovs_be32 mpls_lse) +{ + ds_put_format(ds, "label=%"PRIu32",tc=%d,ttl=%d,bos=%d", + mpls_lse_to_label(mpls_lse), + mpls_lse_to_tc(mpls_lse), + mpls_lse_to_ttl(mpls_lse), + mpls_lse_to_bos(mpls_lse)); +} + static void format_odp_action(struct ds *ds, const struct nlattr *a) { @@ -339,6 +352,18 @@ format_odp_action(struct ds *ds, const struct nlattr *a) case OVS_ACTION_ATTR_POP_VLAN: ds_put_cstr(ds, "pop_vlan"); break; + case OVS_ACTION_ATTR_PUSH_MPLS: { + const struct ovs_action_push_mpls *mpls = nl_attr_get(a); + ds_put_cstr(ds, "push_mpls("); + format_mpls_lse(ds, mpls->mpls_lse); + ds_put_format(ds, ",eth_type=0x%"PRIx16")", ntohs(mpls->mpls_ethertype)); + break; + } + case OVS_ACTION_ATTR_POP_MPLS: { + ovs_be16 ethertype = nl_attr_get_be16(a); + ds_put_format(ds, "pop_mpls(eth_type=0x%"PRIx16")", ntohs(ethertype)); + break; + } case OVS_ACTION_ATTR_SAMPLE: format_odp_sample_action(ds, a); break; @@ -622,6 +647,7 @@ odp_flow_key_attr_len(uint16_t type) case OVS_KEY_ATTR_ETHERNET: return sizeof(struct ovs_key_ethernet); case OVS_KEY_ATTR_VLAN: return sizeof(ovs_be16); case OVS_KEY_ATTR_ETHERTYPE: return 2; + case OVS_KEY_ATTR_MPLS: return sizeof(struct ovs_key_mpls); case OVS_KEY_ATTR_IPV4: return sizeof(struct ovs_key_ipv4); case OVS_KEY_ATTR_IPV6: return sizeof(struct ovs_key_ipv6); case OVS_KEY_ATTR_TCP: return sizeof(struct ovs_key_tcp); @@ -859,6 +885,14 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds) ds_put_char(ds, ')'); break; + case OVS_KEY_ATTR_MPLS: { + const struct ovs_key_mpls *mpls_key = nl_attr_get(a); + ds_put_char(ds, '('); + format_mpls_lse(ds, mpls_key->mpls_top_lse); + ds_put_char(ds, ')'); + break; + } + case OVS_KEY_ATTR_ETHERTYPE: ds_put_format(ds, "(0x%04"PRIx16")", ntohs(nl_attr_get_be16(a))); @@ -1019,6 +1053,15 @@ ovs_frag_type_from_string(const char *s, enum ovs_frag_type *type) return true; } +static ovs_be32 +mpls_lse_from_components(int mpls_label, int mpls_tc, int mpls_ttl, int mpls_bos) +{ + return (htonl((mpls_label << MPLS_LABEL_SHIFT) | + (mpls_tc << MPLS_TC_SHIFT) | + (mpls_ttl << MPLS_TTL_SHIFT) | + (mpls_bos << MPLS_BOS_SHIFT))); +} + static int parse_odp_key_attr(const char *s, const struct simap *port_names, struct ofpbuf *key) @@ -1171,6 +1214,22 @@ parse_odp_key_attr(const char *s, const struct simap *port_names, } } + { + int label, tc, ttl, bos; + int n = -1; + + if (sscanf(s, "mpls(label=%"SCNi32",tc=%i,ttl=%i,bos=%i)%n", + &label, &tc, &ttl, &bos, &n) > 0 && + n > 0) { + struct ovs_key_mpls *mpls; + + mpls = nl_msg_put_unspec_uninit(key, OVS_KEY_ATTR_MPLS, + sizeof *mpls); + mpls->mpls_top_lse = mpls_lse_from_components(label, tc, ttl, bos); + return n; + } + } + { ovs_be32 ipv4_src; ovs_be32 ipv4_dst; @@ -1526,6 +1585,14 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow, memcpy(arp_key->arp_tha, flow->arp_tha, ETH_ADDR_LEN); } + if (flow->mpls_depth) { + struct ovs_key_mpls *mpls_key; + + mpls_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_MPLS, + sizeof *mpls_key); + mpls_key->mpls_top_lse = flow->mpls_lse; + } + if (is_ip_any(flow) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) { if (flow->nw_proto == IPPROTO_TCP) { struct ovs_key_tcp *tcp_key; @@ -1728,14 +1795,38 @@ parse_ethertype(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1], } static enum odp_key_fitness -parse_l3_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1], - uint64_t present_attrs, int out_of_range_attr, - uint64_t expected_attrs, struct flow *flow, - const struct nlattr *key, size_t key_len) +parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1], + uint64_t present_attrs, int out_of_range_attr, + uint64_t expected_attrs, struct flow *flow, + const struct nlattr *key, size_t key_len) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + ovs_be16 dl_type; - if (flow->dl_type == htons(ETH_TYPE_IP)) { + /* Parse MPLS label stack entry */ + if (eth_type_mpls(flow->dl_type)) { + /* Calculate fitness of outer attributes. */ + expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_MPLS); + + /* Get the MPLS LSE value. */ + if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS))) { + return ODP_FIT_TOO_LITTLE; + } + flow->mpls_lse = nl_attr_get_be32(attrs[OVS_KEY_ATTR_MPLS]); + flow->mpls_depth++; + + if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV4)) { + flow->encap_dl_type = htons(ETH_TYPE_IP); + } else if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV6)) { + flow->encap_dl_type = htons(ETH_TYPE_IPV6); + } else if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ARP)) { + flow->encap_dl_type = htons(ETH_TYPE_ARP); + } + } + + dl_type = flow_innermost_dl_type(flow); + + if (dl_type == htons(ETH_TYPE_IP)) { expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV4; if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV4)) { const struct ovs_key_ipv4 *ipv4_key; @@ -1750,7 +1841,7 @@ parse_l3_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1], return ODP_FIT_ERROR; } } - } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) { + } else if (dl_type == htons(ETH_TYPE_IPV6)) { expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV6; if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV6)) { const struct ovs_key_ipv6 *ipv6_key; @@ -1766,8 +1857,8 @@ parse_l3_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1], return ODP_FIT_ERROR; } } - } else if (flow->dl_type == htons(ETH_TYPE_ARP) || - flow->dl_type == htons(ETH_TYPE_RARP)) { + } else if (dl_type == htons(ETH_TYPE_ARP) || + dl_type == htons(ETH_TYPE_RARP)) { expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ARP; if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ARP)) { const struct ovs_key_arp *arp_key; @@ -1787,7 +1878,8 @@ parse_l3_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1], } if (flow->nw_proto == IPPROTO_TCP - && is_ip_any(flow) + && (dl_type == htons(ETH_TYPE_IP) || + dl_type == htons(ETH_TYPE_IPV6)) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) { expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TCP; if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TCP)) { @@ -1798,7 +1890,8 @@ parse_l3_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1], flow->tp_dst = tcp_key->tcp_dst; } } else if (flow->nw_proto == IPPROTO_UDP - && is_ip_any(flow) + && (dl_type == htons(ETH_TYPE_IP) || + dl_type == htons(ETH_TYPE_IPV6)) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) { expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_UDP; if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_UDP)) { @@ -1809,7 +1902,7 @@ parse_l3_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1], flow->tp_dst = udp_key->udp_dst; } } else if (flow->nw_proto == IPPROTO_ICMP - && flow->dl_type == htons(ETH_TYPE_IP) + && dl_type == htons(ETH_TYPE_IP) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) { expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ICMP; if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ICMP)) { @@ -1820,7 +1913,7 @@ parse_l3_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1], flow->tp_dst = htons(icmp_key->icmp_code); } } else if (flow->nw_proto == IPPROTO_ICMPV6 - && flow->dl_type == htons(ETH_TYPE_IPV6) + && dl_type == htons(ETH_TYPE_IPV6) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) { expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ICMPV6; if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ICMPV6)) { @@ -1904,8 +1997,8 @@ parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1], if (!parse_ethertype(attrs, present_attrs, &expected_attrs, flow)) { return ODP_FIT_ERROR; } - encap_fitness = parse_l3_onward(attrs, present_attrs, out_of_range_attr, - expected_attrs, flow, key, key_len); + encap_fitness = parse_l2_5_onward(attrs, present_attrs, out_of_range_attr, + expected_attrs, flow, key, key_len); /* The overall fitness is the worse of the outer and inner attributes. */ return MAX(fitness, encap_fitness); @@ -1997,8 +2090,8 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, return parse_8021q_onward(attrs, present_attrs, out_of_range_attr, expected_attrs, flow, key, key_len); } - return parse_l3_onward(attrs, present_attrs, out_of_range_attr, - expected_attrs, flow, key, key_len); + return parse_l2_5_onward(attrs, present_attrs, out_of_range_attr, + expected_attrs, flow, key, key_len); } /* Returns 'fitness' as a string, for use in debug messages. */ @@ -2129,6 +2222,50 @@ commit_vlan_action(const struct flow *flow, struct flow *base, base->vlan_tci = flow->vlan_tci; } +static void +commit_mpls_action(const struct flow *flow, struct flow *base, + struct ofpbuf *odp_actions) +{ + if (flow->mpls_lse == base->mpls_lse && + flow->mpls_depth == base->mpls_depth) { + return; + } + + if (flow->mpls_depth < base->mpls_depth) { + if (base->mpls_depth - flow->mpls_depth > 1) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10); + VLOG_WARN_RL(&rl, "Multiple mpls_pop actions reduced to " + " a single mpls_pop action"); + } + + nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_POP_MPLS, flow->dl_type); + } else if (flow->mpls_depth > base->mpls_depth) { + struct ovs_action_push_mpls *mpls; + + if (flow->mpls_depth - base->mpls_depth > 1) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10); + VLOG_WARN_RL(&rl, "Multiple mpls_push actions reduced to " + " a single mpls_push action"); + } + + mpls = nl_msg_put_unspec_uninit(odp_actions, OVS_ACTION_ATTR_PUSH_MPLS, + sizeof *mpls); + memset(mpls, 0, sizeof *mpls); + mpls->mpls_ethertype = flow->dl_type; + mpls->mpls_lse = flow->mpls_lse; + } else { + struct ovs_key_mpls mpls_key; + + mpls_key.mpls_top_lse = flow->mpls_lse; + commit_set_action(odp_actions, OVS_KEY_ATTR_MPLS, + &mpls_key, sizeof(mpls_key)); + } + + base->dl_type = flow->dl_type; + base->mpls_lse = flow->mpls_lse; + base->mpls_depth = flow->mpls_depth; +} + static void commit_set_ipv4_action(const struct flow *flow, struct flow *base, struct ofpbuf *odp_actions) @@ -2269,6 +2406,7 @@ commit_odp_actions(const struct flow *flow, struct flow *base, { commit_set_ether_addr_action(flow, base, odp_actions); commit_vlan_action(flow, base, odp_actions); + commit_mpls_action(flow, base, odp_actions); commit_set_nw_action(flow, base, odp_actions); commit_set_port_action(flow, base, odp_actions); commit_set_priority_action(flow, base, odp_actions); diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c index a439d1382..8c0585d89 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -402,6 +402,24 @@ ofpact_from_nxast(const union ofp_action *a, enum ofputil_action_code code, case OFPUTIL_NXAST_CONTROLLER: controller_from_openflow((const struct nx_action_controller *) a, out); break; + + case OFPUTIL_NXAST_PUSH_MPLS: { + struct nx_action_push_mpls *nxapm = (struct nx_action_push_mpls *)a; + if (!eth_type_mpls(nxapm->ethertype)) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + ofpact_put_PUSH_MPLS(out)->ethertype = nxapm->ethertype; + break; + } + + case OFPUTIL_NXAST_POP_MPLS: { + struct nx_action_pop_mpls *nxapm = (struct nx_action_pop_mpls *)a; + if (eth_type_mpls(nxapm->ethertype)) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + ofpact_put_POP_MPLS(out)->ethertype = nxapm->ethertype; + break; + } } return error; @@ -774,6 +792,24 @@ ofpact_from_openflow11(const union ofp_action *a, struct ofpbuf *out) return nxm_reg_load_from_openflow12_set_field( (const struct ofp12_action_set_field *)a, out); + case OFPUTIL_OFPAT11_PUSH_MPLS: { + struct ofp11_action_push *oap = (struct ofp11_action_push *)a; + if (!eth_type_mpls(oap->ethertype)) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + ofpact_put_PUSH_MPLS(out)->ethertype = oap->ethertype; + break; + } + + case OFPUTIL_OFPAT11_POP_MPLS: { + struct ofp11_action_pop_mpls *oapm = (struct ofp11_action_pop_mpls *)a; + if (eth_type_mpls(oapm->ethertype)) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + ofpact_put_POP_MPLS(out)->ethertype = oapm->ethertype; + break; + } + #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM: #include "ofp-util.def" return ofpact_from_nxast(a, code, out); @@ -1053,7 +1089,8 @@ exit: } static enum ofperr -ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports) +ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports, + ovs_be16 *dl_type) { const struct ofpact_enqueue *enqueue; @@ -1096,7 +1133,13 @@ ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports) return nxm_reg_move_check(ofpact_get_REG_MOVE(a), flow); case OFPACT_REG_LOAD: - return nxm_reg_load_check(ofpact_get_REG_LOAD(a), flow); + if (*dl_type != flow->dl_type) { + struct flow updated_flow = *flow; + updated_flow.dl_type = *dl_type; + return nxm_reg_load_check(ofpact_get_REG_LOAD(a), &updated_flow); + } else { + return nxm_reg_load_check(ofpact_get_REG_LOAD(a), flow); + } case OFPACT_DEC_TTL: case OFPACT_SET_TUNNEL: @@ -1119,6 +1162,14 @@ ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports) case OFPACT_EXIT: return 0; + case OFPACT_PUSH_MPLS: + *dl_type = ofpact_get_PUSH_MPLS(a)->ethertype; + return 0; + + case OFPACT_POP_MPLS: + *dl_type = ofpact_get_POP_MPLS(a)->ethertype; + return 0; + case OFPACT_CLEAR_ACTIONS: case OFPACT_WRITE_METADATA: case OFPACT_GOTO_TABLE: @@ -1137,9 +1188,10 @@ ofpacts_check(const struct ofpact ofpacts[], size_t ofpacts_len, const struct flow *flow, int max_ports) { const struct ofpact *a; + ovs_be16 dl_type = flow->dl_type; OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { - enum ofperr error = ofpact_check__(a, flow, max_ports); + enum ofperr error = ofpact_check__(a, flow, max_ports, &dl_type); if (error) { return error; } @@ -1385,6 +1437,16 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out) ofputil_put_NXAST_EXIT(out); break; + case OFPACT_PUSH_MPLS: + ofputil_put_NXAST_PUSH_MPLS(out)->ethertype = + ofpact_get_PUSH_MPLS(a)->ethertype; + break; + + case OFPACT_POP_MPLS: + ofputil_put_NXAST_POP_MPLS(out)->ethertype = + ofpact_get_POP_MPLS(a)->ethertype; + break; + case OFPACT_OUTPUT: case OFPACT_ENQUEUE: case OFPACT_SET_VLAN_VID: @@ -1512,6 +1574,8 @@ ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out) case OFPACT_AUTOPATH: case OFPACT_NOTE: case OFPACT_EXIT: + case OFPACT_PUSH_MPLS: + case OFPACT_POP_MPLS: ofpact_to_nxast(a, out); break; } @@ -1636,6 +1700,17 @@ ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out) /* OpenFlow 1.1 uses OFPIT_WRITE_METADATA to express this action. */ break; + case OFPACT_PUSH_MPLS: + ofputil_put_OFPAT11_PUSH_MPLS(out)->ethertype = + ofpact_get_PUSH_MPLS(a)->ethertype; + break; + + case OFPACT_POP_MPLS: + ofputil_put_OFPAT11_POP_MPLS(out)->ethertype = + ofpact_get_POP_MPLS(a)->ethertype; + + break; + case OFPACT_CLEAR_ACTIONS: case OFPACT_GOTO_TABLE: NOT_REACHED(); @@ -1777,6 +1852,8 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, uint16_t port) case OFPACT_AUTOPATH: case OFPACT_NOTE: case OFPACT_EXIT: + case OFPACT_PUSH_MPLS: + case OFPACT_POP_MPLS: case OFPACT_CLEAR_ACTIONS: case OFPACT_GOTO_TABLE: default: @@ -2048,6 +2125,16 @@ ofpact_format(const struct ofpact *a, struct ds *s) print_note(ofpact_get_NOTE(a), s); break; + case OFPACT_PUSH_MPLS: + ds_put_format(s, "push_mpls:0x%04"PRIx16, + ntohs(ofpact_get_PUSH_MPLS(a)->ethertype)); + break; + + case OFPACT_POP_MPLS: + ds_put_format(s, "pop_mpls:0x%04"PRIx16, + ntohs(ofpact_get_POP_MPLS(a)->ethertype)); + break; + case OFPACT_EXIT: ds_put_cstr(s, "exit"); break; diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h index e930986b0..2a9463689 100644 --- a/lib/ofp-actions.h +++ b/lib/ofp-actions.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Nicira, Inc. + * Copyright (c) 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -71,6 +71,8 @@ DEFINE_OFPACT(REG_MOVE, ofpact_reg_move, ofpact) \ DEFINE_OFPACT(REG_LOAD, ofpact_reg_load, ofpact) \ DEFINE_OFPACT(DEC_TTL, ofpact_cnt_ids, cnt_ids) \ + DEFINE_OFPACT(PUSH_MPLS, ofpact_push_mpls, ofpact) \ + DEFINE_OFPACT(POP_MPLS, ofpact_pop_mpls, ofpact) \ \ /* Metadata. */ \ DEFINE_OFPACT(SET_TUNNEL, ofpact_tunnel, ofpact) \ @@ -310,6 +312,22 @@ struct ofpact_reg_load { union mf_subvalue subvalue; /* Least-significant bits are used. */ }; +/* OFPACT_PUSH_VLAN/MPLS/PBB + * + * Used for NXAST_PUSH_MPLS, OFPAT11_PUSH_MPLS. */ +struct ofpact_push_mpls { + struct ofpact ofpact; + ovs_be16 ethertype; +}; + +/* OFPACT_POP_MPLS + * + * Used for NXAST_POP_MPLS, OFPAT11_POP_MPLS.. */ +struct ofpact_pop_mpls { + struct ofpact ofpact; + ovs_be16 ethertype; +}; + /* OFPACT_SET_TUNNEL. * * Used for NXAST_SET_TUNNEL, NXAST_SET_TUNNEL64. */ diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c index cfcf5bf0c..dd435779d 100644 --- a/lib/ofp-parse.c +++ b/lib/ofp-parse.c @@ -562,6 +562,18 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow, case OFPUTIL_NXAST_CONTROLLER: parse_controller(ofpacts, arg); break; + + case OFPUTIL_OFPAT11_PUSH_MPLS: + case OFPUTIL_NXAST_PUSH_MPLS: + ofpact_put_PUSH_MPLS(ofpacts)->ethertype = + htons(str_to_u16(arg, "push_mpls")); + break; + + case OFPUTIL_OFPAT11_POP_MPLS: + case OFPUTIL_NXAST_POP_MPLS: + ofpact_put_POP_MPLS(ofpacts)->ethertype = + htons(str_to_u16(arg, "pop_mpls")); + break; } } @@ -726,7 +738,9 @@ parse_protocol(const char *name, const struct protocol **p_out) { "tcp6", ETH_TYPE_IPV6, IPPROTO_TCP }, { "udp6", ETH_TYPE_IPV6, IPPROTO_UDP }, { "rarp", ETH_TYPE_RARP, 0}, -}; + { "mpls", ETH_TYPE_MPLS, 0 }, + { "mplsm", ETH_TYPE_MPLS_MCAST, 0 }, + }; const struct protocol *p; for (p = protocols; p < &protocols[ARRAY_SIZE(protocols)]; p++) { diff --git a/lib/ofp-print.c b/lib/ofp-print.c index b97180e66..1a9ca0e17 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -642,6 +642,10 @@ ofp10_match_to_string(const struct ofp10_match *om, int verbosity) ds_put_cstr(&f, "arp,"); } else if (om->dl_type == htons(ETH_TYPE_RARP)){ ds_put_cstr(&f, "rarp,"); + } else if (om->dl_type == htons(ETH_TYPE_MPLS)) { + ds_put_cstr(&f, "mpls,"); + } else if (om->dl_type == htons(ETH_TYPE_MPLS_MCAST)) { + ds_put_cstr(&f, "mplsm,"); } else { skip_type = false; } diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 40983504e..6d885127c 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -85,7 +85,7 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask) void ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc) { - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 19); /* Initialize most of wc. */ flow_wildcards_init_catchall(wc); @@ -440,8 +440,7 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch, } } - if (match->flow.dl_type == htons(ETH_TYPE_MPLS) || - match->flow.dl_type == htons(ETH_TYPE_MPLS_MCAST)) { + if (eth_type_mpls(match->flow.dl_type)) { enum { OFPFW11_MPLS_ALL = OFPFW11_MPLS_LABEL | OFPFW11_MPLS_TC }; if ((wc & OFPFW11_MPLS_ALL) != OFPFW11_MPLS_ALL) { @@ -1060,7 +1059,7 @@ ofputil_usable_protocols(const struct match *match) { const struct flow_wildcards *wc = &match->wc; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 19); /* tunnel params other than tun_id can't be sent in a flow_mod */ if (!tun_parms_fully_wildcarded(wc)) { @@ -1152,6 +1151,26 @@ ofputil_usable_protocols(const struct match *match) | OFPUTIL_P_OF13_OXM; } + /* NXM and OF1.1+ support matching MPLS label */ + if (wc->masks.mpls_lse & htonl(MPLS_LABEL_MASK)) { + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM + | OFPUTIL_P_OF13_OXM; + } + + /* NXM and OF1.1+ support matching MPLS TC */ + if (wc->masks.mpls_lse & htonl(MPLS_TC_MASK)) { + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM + | OFPUTIL_P_OF13_OXM; + } + + /* NXM and OF1.3+ support matching MPLS stack flag */ + /* Allow for OF1.2 as there doesn't seem to be a + * particularly good reason not to */ + if (wc->masks.mpls_lse & htonl(MPLS_BOS_MASK)) { + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM + | OFPUTIL_P_OF13_OXM; + } + /* Other formats can express this rule. */ return OFPUTIL_P_ANY; } @@ -4307,7 +4326,8 @@ ofputil_normalize_match__(struct match *match, bool may_log) MAY_ARP_SHA = 1 << 4, /* arp_sha */ MAY_ARP_THA = 1 << 5, /* arp_tha */ MAY_IPV6 = 1 << 6, /* ipv6_src, ipv6_dst, ipv6_label */ - MAY_ND_TARGET = 1 << 7 /* nd_target */ + MAY_ND_TARGET = 1 << 7, /* nd_target */ + MAY_MPLS = 1 << 8, /* mpls label and tc */ } may_match; struct flow_wildcards wc; @@ -4336,6 +4356,8 @@ ofputil_normalize_match__(struct match *match, bool may_log) } else if (match->flow.dl_type == htons(ETH_TYPE_ARP) || match->flow.dl_type == htons(ETH_TYPE_RARP)) { may_match = MAY_NW_PROTO | MAY_NW_ADDR | MAY_ARP_SHA | MAY_ARP_THA; + } else if (eth_type_mpls(match->flow.dl_type)) { + may_match = MAY_MPLS; } else { may_match = 0; } @@ -4368,6 +4390,10 @@ ofputil_normalize_match__(struct match *match, bool may_log) if (!(may_match & MAY_ND_TARGET)) { wc.masks.nd_target = in6addr_any; } + if (!(may_match & MAY_MPLS)) { + wc.masks.mpls_lse = htonl(0); + wc.masks.mpls_depth = 0; + } /* Log any changes. */ if (!flow_wildcards_equal(&wc, &match->wc)) { diff --git a/lib/ofp-util.def b/lib/ofp-util.def index 6d08d8a38..1a60194e1 100644 --- a/lib/ofp-util.def +++ b/lib/ofp-util.def @@ -32,6 +32,8 @@ OFPAT11_ACTION(OFPAT11_SET_TP_SRC, ofp_action_tp_port, 0, "mod_tp_src") OFPAT11_ACTION(OFPAT11_SET_TP_DST, ofp_action_tp_port, 0, "mod_tp_dst") OFPAT11_ACTION(OFPAT11_PUSH_VLAN, ofp11_action_push, 0, "push_vlan") OFPAT11_ACTION(OFPAT11_POP_VLAN, ofp_action_header, 0, "pop_vlan") +OFPAT11_ACTION(OFPAT11_PUSH_MPLS, ofp11_action_push, 0, "push_mpls") +OFPAT11_ACTION(OFPAT11_POP_MPLS, ofp11_action_pop_mpls, 0, "pop_mpls") OFPAT11_ACTION(OFPAT11_SET_QUEUE, ofp11_action_set_queue, 0, "set_queue") //OFPAT11_ACTION(OFPAT11_SET_NW_TTL, ofp11_action_nw_ttl, 0, "set_nw_ttl") OFPAT11_ACTION(OFPAT11_DEC_NW_TTL, ofp_action_header, 0, NULL) @@ -62,6 +64,8 @@ NXAST_ACTION(NXAST_CONTROLLER, nx_action_controller, 0, "controller") NXAST_ACTION(NXAST_DEC_TTL_CNT_IDS, nx_action_cnt_ids, 1, NULL) NXAST_ACTION(NXAST_WRITE_METADATA, nx_action_write_metadata, 0, "write_metadata") +NXAST_ACTION(NXAST_PUSH_MPLS, nx_action_push_mpls, 0, "push_mpls") +NXAST_ACTION(NXAST_POP_MPLS, nx_action_pop_mpls, 0, "pop_mpls") #undef OFPAT10_ACTION #undef OFPAT11_ACTION diff --git a/lib/ofpbuf.c b/lib/ofpbuf.c index 6484ab3b8..fd10da361 100644 --- a/lib/ofpbuf.c +++ b/lib/ofpbuf.c @@ -29,7 +29,7 @@ ofpbuf_use__(struct ofpbuf *b, void *base, size_t allocated, b->allocated = allocated; b->source = source; b->size = 0; - b->l2 = b->l3 = b->l4 = b->l7 = NULL; + b->l2 = b->l2_5 = b->l3 = b->l4 = b->l7 = NULL; list_poison(&b->list_node); b->private_p = NULL; } @@ -176,6 +176,9 @@ ofpbuf_clone_with_headroom(const struct ofpbuf *buffer, size_t headroom) if (buffer->l2) { new_buffer->l2 = (char *) buffer->l2 + data_delta; } + if (buffer->l2_5) { + new_buffer->l2_5 = (char *) buffer->l2_5 + data_delta; + } if (buffer->l3) { new_buffer->l3 = (char *) buffer->l3 + data_delta; } @@ -295,6 +298,9 @@ ofpbuf_resize__(struct ofpbuf *b, size_t new_headroom, size_t new_tailroom) if (b->l2) { b->l2 = (char *) b->l2 + data_delta; } + if (b->l2_5) { + b->l2_5 = (char *) b->l2_5 + data_delta; + } if (b->l3) { b->l3 = (char *) b->l3 + data_delta; } diff --git a/lib/ofpbuf.h b/lib/ofpbuf.h index 520455dc3..bae3c0aee 100644 --- a/lib/ofpbuf.h +++ b/lib/ofpbuf.h @@ -43,6 +43,7 @@ struct ofpbuf { size_t size; /* Number of bytes in use. */ void *l2; /* Link-level header. */ + void *l2_5; /* MPLS label stack */ void *l3; /* Network-level header. */ void *l4; /* Transport-level header. */ void *l7; /* Application data. */ diff --git a/lib/packets.c b/lib/packets.c index fa7328237..4f57d1666 100644 --- a/lib/packets.c +++ b/lib/packets.c @@ -192,7 +192,8 @@ eth_push_vlan(struct ofpbuf *packet, ovs_be16 tci) /* Removes outermost VLAN header (if any is present) from 'packet'. * - * 'packet->l2' must initially point to 'packet''s Ethernet header. */ + * 'packet->l2_5' should initially point to 'packet''s outer-most MPLS header + * or may be NULL if there are no MPLS headers. */ void eth_pop_vlan(struct ofpbuf *packet) { @@ -211,6 +212,182 @@ eth_pop_vlan(struct ofpbuf *packet) } } +/* Return depth of mpls stack. + * + * 'packet->l2_5' should initially point to 'packet''s outer-most MPLS header + * or may be NULL if there are no MPLS headers. */ +uint16_t +eth_mpls_depth(const struct ofpbuf *packet) +{ + struct mpls_hdr *mh = packet->l2_5; + uint16_t depth; + + if (!mh) { + return 0; + } + + depth = 0; + while (packet->size >= ((char *)mh - (char *)packet->data) + sizeof *mh) { + depth++; + if (mh->mpls_lse & htonl(MPLS_BOS_MASK)) { + break; + } + mh++; + } + + return depth; +} + +/* Set ethertype of the packet. */ +void +set_ethertype(struct ofpbuf *packet, ovs_be16 eth_type) +{ + struct eth_header *eh = packet->data; + + if (eh->eth_type == htons(ETH_TYPE_VLAN)) { + ovs_be16 *p; + p = (ovs_be16 *)((char *)(packet->l2_5 ? packet->l2_5 : packet->l3) - 2); + *p = eth_type; + } else { + eh->eth_type = eth_type; + } +} + +static bool is_mpls(struct ofpbuf *packet) +{ + return packet->l2_5 != NULL; +} + +/* Set time to live (TTL) of an MPLS label stack entry (LSE). */ +static void +set_mpls_lse_ttl(ovs_be32 *lse, uint8_t ttl) +{ + *lse &= ~htonl(MPLS_TTL_MASK); + *lse |= htonl((ttl << MPLS_TTL_SHIFT) & MPLS_TTL_MASK); +} + +/* Set traffic class (TC) of an MPLS label stack entry (LSE). */ +void +set_mpls_lse_tc(ovs_be32 *lse, uint8_t tc) +{ + *lse &= ~htonl(MPLS_TC_MASK); + *lse |= htonl((tc << MPLS_TC_SHIFT) & MPLS_TC_MASK); +} + +/* Set label of an MPLS label stack entry (LSE). */ +void +set_mpls_lse_label(ovs_be32 *lse, ovs_be32 label) +{ + *lse &= ~htonl(MPLS_LABEL_MASK); + *lse |= htonl((ntohl(label) << MPLS_LABEL_SHIFT) & MPLS_LABEL_MASK); +} + +/* Set bottom of stack (BoS) bit of an MPLS label stack entry (LSE). */ +void +set_mpls_lse_bos(ovs_be32 *lse, uint8_t bos) +{ + *lse &= ~htonl(MPLS_BOS_MASK); + *lse |= htonl((bos << MPLS_BOS_SHIFT) & MPLS_BOS_MASK); +} + +/* Compose an MPLS label stack entry (LSE) from its components: + * label, traffic class (TC), time to live (TTL) and + * bottom of stack (BoS) bit. */ +ovs_be32 +set_mpls_lse_values(uint8_t ttl, uint8_t tc, uint8_t bos, ovs_be32 label) +{ + ovs_be32 lse = htonl(0); + set_mpls_lse_ttl(&lse, ttl); + set_mpls_lse_tc(&lse, tc); + set_mpls_lse_bos(&lse, bos); + set_mpls_lse_label(&lse, label); + return lse; +} + +/* Push an new MPLS stack entry onto the MPLS stack and adjust 'packet->l2' and + * 'packet->l2_5' accordingly. The new entry will be the outermost entry on + * the stack. + * + * Previous to calling this function, 'packet->l2_5' must be set; if the MPLS + * label to be pushed will be the first label in 'packet', then it should be + * the same as 'packet->l3'. */ +static void +push_mpls_lse(struct ofpbuf *packet, struct mpls_hdr *mh) +{ + char * header; + size_t len; + header = ofpbuf_push_uninit(packet, MPLS_HLEN); + len = (char *)packet->l2_5 - (char *)packet->l2; + memmove(header, packet->l2, len); + memcpy(header + len, mh, sizeof *mh); + packet->l2 = (char*)packet->l2 - MPLS_HLEN; + packet->l2_5 = (char*)packet->l2_5 - MPLS_HLEN; +} + +/* Set MPLS label stack entry to outermost MPLS header.*/ +void +set_mpls_lse(struct ofpbuf *packet, ovs_be32 mpls_lse) +{ + struct mpls_hdr *mh = packet->l2_5; + + /* Packet type should be MPLS to set label stack entry. */ + if (is_mpls(packet)) { + /* Update mpls label stack entry. */ + mh->mpls_lse = mpls_lse; + } +} + +/* Push MPLS label stack entry 'lse' onto 'packet' as the the outermost MPLS + * header. If 'packet' does not already have any MPLS labels, then its + * Ethertype is changed to 'ethtype' (which must be an MPLS Ethertype). */ +void +push_mpls(struct ofpbuf *packet, ovs_be16 ethtype, ovs_be32 lse) +{ + struct mpls_hdr mh; + + if (!eth_type_mpls(ethtype)) { + return; + } + + if (!is_mpls(packet)) { + /* Set ethtype and MPLS label stack entry. */ + set_ethertype(packet, ethtype); + packet->l2_5 = packet->l3; + } + + /* Push new MPLS shim header onto packet. */ + mh.mpls_lse = lse; + push_mpls_lse(packet, &mh); +} + +/* If 'packet' is an MPLS packet, removes its outermost MPLS label stack entry. + * If the label that was removed was the only MPLS label, changes 'packet''s + * Ethertype to 'ethtype' (which ordinarily should not be an MPLS + * Ethertype). */ +void +pop_mpls(struct ofpbuf *packet, ovs_be16 ethtype) +{ + struct mpls_hdr *mh = NULL; + + if (is_mpls(packet)) { + size_t len; + mh = packet->l2_5; + len = (char*)packet->l2_5 - (char*)packet->l2; + /* If bottom of the stack set ethertype. */ + if (mh->mpls_lse & htonl(MPLS_BOS_MASK)) { + packet->l2_5 = NULL; + set_ethertype(packet, ethtype); + } else { + packet->l2_5 = (char*)packet->l2_5 + MPLS_HLEN; + } + /* Shift the l2 header forward. */ + memmove((char*)packet->data + MPLS_HLEN, packet->data, len); + packet->size -= MPLS_HLEN; + packet->data = (char*)packet->data + MPLS_HLEN; + packet->l2 = (char*)packet->l2 + MPLS_HLEN; + } +} + /* Converts hex digits in 'hex' to an Ethernet packet in '*packetp'. The * caller must free '*packetp'. On success, returns NULL. On failure, returns * an error message and stores NULL in '*packetp'. */ diff --git a/lib/packets.h b/lib/packets.h index 8dd3ebff2..973f3e878 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -140,6 +140,10 @@ void compose_rarp(struct ofpbuf *, const uint8_t eth_src[ETH_ADDR_LEN]); void eth_push_vlan(struct ofpbuf *, ovs_be16 tci); void eth_pop_vlan(struct ofpbuf *); +uint16_t eth_mpls_depth(const struct ofpbuf *packet); + +void set_ethertype(struct ofpbuf *packet, ovs_be16 eth_type); + const char *eth_from_hex(const char *hex, struct ofpbuf **packetp); void eth_format_masked(const uint8_t eth[ETH_ADDR_LEN], const uint8_t mask[ETH_ADDR_LEN], struct ds *s); @@ -147,6 +151,16 @@ void eth_addr_bitand(const uint8_t src[ETH_ADDR_LEN], const uint8_t mask[ETH_ADDR_LEN], uint8_t dst[ETH_ADDR_LEN]); +void set_mpls_lse(struct ofpbuf *, ovs_be32 label); +void push_mpls(struct ofpbuf *packet, ovs_be16 ethtype, ovs_be32 lse); +void pop_mpls(struct ofpbuf *, ovs_be16 ethtype); + +void set_mpls_lse_tc(ovs_be32 *lse, uint8_t tc); +void set_mpls_lse_label(ovs_be32 *lse, ovs_be32 label); +void set_mpls_lse_bos(ovs_be32 *lse, uint8_t bos); +ovs_be32 set_mpls_lse_values(uint8_t ttl, uint8_t tc, uint8_t bos, + ovs_be32 label); + /* Example: * * uint8_t mac[ETH_ADDR_LEN]; @@ -186,6 +200,12 @@ void eth_addr_bitand(const uint8_t src[ETH_ADDR_LEN], #define ETH_TYPE_MPLS 0x8847 #define ETH_TYPE_MPLS_MCAST 0x8848 +static inline bool eth_type_mpls(ovs_be16 eth_type) +{ + return eth_type == htons(ETH_TYPE_MPLS) || + eth_type == htons(ETH_TYPE_MPLS_MCAST); +} + /* Minimum value for an Ethernet type. Values below this are IEEE 802.2 frame * lengths. */ #define ETH_TYPE_MIN 0x600 @@ -272,6 +292,66 @@ struct vlan_eth_header { } __attribute__((packed)); BUILD_ASSERT_DECL(VLAN_ETH_HEADER_LEN == sizeof(struct vlan_eth_header)); +/* MPLS related definitions */ +#define MPLS_TTL_MASK 0x000000ff +#define MPLS_TTL_SHIFT 0 + +#define MPLS_BOS_MASK 0x00000100 +#define MPLS_BOS_SHIFT 8 + +#define MPLS_TC_MASK 0x00000e00 +#define MPLS_TC_SHIFT 9 + +#define MPLS_LABEL_MASK 0xfffff000 +#define MPLS_LABEL_SHIFT 12 + +#define MPLS_HLEN 4 + +struct mpls_hdr { + ovs_be32 mpls_lse; +}; +BUILD_ASSERT_DECL(MPLS_HLEN == sizeof(struct mpls_hdr)); + +/* Given a mpls label stack entry in network byte order + * return mpls label in host byte order */ +static inline uint32_t +mpls_lse_to_label(ovs_be32 mpls_lse) +{ + return (ntohl(mpls_lse) & MPLS_LABEL_MASK) >> MPLS_LABEL_SHIFT; +} + +/* Given a mpls label stack entry in network byte order + * return mpls tc */ +static inline uint8_t +mpls_lse_to_tc(ovs_be32 mpls_lse) +{ + return (ntohl(mpls_lse) & MPLS_TC_MASK) >> MPLS_TC_SHIFT; +} + +/* Given a mpls label stack entry in network byte order + * return mpls ttl */ +static inline uint8_t +mpls_lse_to_ttl(ovs_be32 mpls_lse) +{ + return (ntohl(mpls_lse) & MPLS_TTL_MASK) >> MPLS_TTL_SHIFT; +} + +/* Set TTL in mpls lse. */ +static inline void +flow_set_mpls_lse_ttl(ovs_be32 *mpls_lse, uint8_t ttl) +{ + *mpls_lse &= ~htonl(MPLS_TTL_MASK); + *mpls_lse |= htonl(ttl << MPLS_TTL_SHIFT); +} + +/* Given a mpls label stack entry in network byte order + * return mpls BoS bit */ +static inline uint8_t +mpls_lse_to_bos(ovs_be32 mpls_lse) +{ + return (mpls_lse & htonl(MPLS_BOS_MASK)) != 0; +} + #define IP_FMT "%"PRIu32".%"PRIu32".%"PRIu32".%"PRIu32 #define IP_ARGS(ip) \ ntohl(ip) >> 24, \ diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index b1ec3fb13..f91d3c336 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -5716,7 +5716,7 @@ compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port, /* If 'struct flow' gets additional metadata, we'll need to zero it out * before traversing a patch port. */ - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 19); if (!ofport) { xlate_report(ctx, "Nonexistent output port"); @@ -5938,16 +5938,11 @@ execute_controller_action(struct action_xlate_ctx *ctx, int len, if (packet->l2 && packet->l3) { struct eth_header *eh; + uint16_t mpls_depth; eth_pop_vlan(packet); eh = packet->l2; - /* If the Ethernet type is less than ETH_TYPE_MIN, it's likely an 802.2 - * LLC frame. Calculating the Ethernet type of these frames is more - * trouble than seems appropriate for a simple assertion. */ - ovs_assert(ntohs(eh->eth_type) < ETH_TYPE_MIN - || eh->eth_type == ctx->flow.dl_type); - memcpy(eh->eth_src, ctx->flow.dl_src, sizeof eh->eth_src); memcpy(eh->eth_dst, ctx->flow.dl_dst, sizeof eh->eth_dst); @@ -5955,6 +5950,16 @@ execute_controller_action(struct action_xlate_ctx *ctx, int len, eth_push_vlan(packet, ctx->flow.vlan_tci); } + mpls_depth = eth_mpls_depth(packet); + + if (mpls_depth < ctx->flow.mpls_depth) { + push_mpls(packet, ctx->flow.dl_type, ctx->flow.mpls_lse); + } else if (mpls_depth > ctx->flow.mpls_depth) { + pop_mpls(packet, ctx->flow.dl_type); + } else if (mpls_depth) { + set_mpls_lse(packet, ctx->flow.mpls_lse); + } + if (packet->l4) { if (ctx->flow.dl_type == htons(ETH_TYPE_IP)) { packet_set_ipv4(packet, ctx->flow.nw_src, ctx->flow.nw_dst, @@ -5987,6 +5992,48 @@ execute_controller_action(struct action_xlate_ctx *ctx, int len, ofpbuf_delete(packet); } +static void +execute_mpls_push_action(struct action_xlate_ctx *ctx, ovs_be16 eth_type) +{ + ovs_assert(eth_type_mpls(eth_type)); + + if (ctx->base_flow.mpls_depth) { + ctx->flow.mpls_lse &= ~htonl(MPLS_BOS_MASK); + ctx->flow.mpls_depth++; + } else { + ovs_be32 label; + uint8_t tc, ttl; + + if (ctx->flow.dl_type == htons(ETH_TYPE_IPV6)) { + label = htonl(0x2); /* IPV6 Explicit Null. */ + } else { + label = htonl(0x0); /* IPV4 Explicit Null. */ + } + tc = (ctx->flow.nw_tos & IP_DSCP_MASK) >> 2; + ttl = ctx->flow.nw_ttl ? ctx->flow.nw_ttl : 0x40; + ctx->flow.mpls_lse = set_mpls_lse_values(ttl, tc, 1, label); + ctx->flow.encap_dl_type = ctx->flow.dl_type; + ctx->flow.mpls_depth = 1; + } + ctx->flow.dl_type = eth_type; +} + +static void +execute_mpls_pop_action(struct action_xlate_ctx *ctx, ovs_be16 eth_type) +{ + ovs_assert(eth_type_mpls(ctx->flow.dl_type)); + ovs_assert(!eth_type_mpls(eth_type)); + + if (ctx->flow.mpls_depth) { + ctx->flow.mpls_depth--; + ctx->flow.mpls_lse = htonl(0); + if (!ctx->flow.mpls_depth) { + ctx->flow.dl_type = eth_type; + ctx->flow.encap_dl_type = htons(0); + } + } +} + static bool compose_dec_ttl(struct action_xlate_ctx *ctx, struct ofpact_cnt_ids *ids) { @@ -6373,6 +6420,14 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, nxm_execute_reg_load(ofpact_get_REG_LOAD(a), &ctx->flow); break; + case OFPACT_PUSH_MPLS: + execute_mpls_push_action(ctx, ofpact_get_PUSH_MPLS(a)->ethertype); + break; + + case OFPACT_POP_MPLS: + execute_mpls_pop_action(ctx, ofpact_get_POP_MPLS(a)->ethertype); + break; + case OFPACT_DEC_TTL: if (compose_dec_ttl(ctx, ofpact_get_DEC_TTL(a))) { goto out; diff --git a/tests/odp.at b/tests/odp.at index 687f9c914..bd45cf706 100644 --- a/tests/odp.at +++ b/tests/odp.at @@ -25,6 +25,11 @@ in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=136,code=0),nd(target=::3,sll=00:05:06:07:08:09,tll=00:0a:0b:0c:0d:0e) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0806),arp(sip=1.2.3.4,tip=5.6.7.8,op=1,sha=00:0f:10:11:12:13,tha=00:14:15:16:17:18) skb_mark(0x1234),in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=136,code=0),nd(target=::3,sll=00:05:06:07:08:09,tll=00:0a:0b:0c:0d:0e) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8847),mpls(label=100,tc=3,ttl=64,bos=1) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8847),mpls(label=100,tc=7,ttl=100,bos=1) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8847),mpls(label=100,tc=7,ttl=100,bos=0) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8848),mpls(label=1000,tc=4,ttl=200,bos=1) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8848),mpls(label=1000,tc=4,ttl=200,bos=0) ]) (echo '# Valid forms without tun_id or VLAN header.' @@ -39,6 +44,14 @@ skb_mark(0x1234),in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth sed 's/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/ s/$/)/' odp-base.txt + echo + echo '# Valid forms with MPLS header.' + sed 's/\(eth([[^)]]*),?\)/\1,eth_type(0x8847),mpls(label=100,tc=7,ttl=64,bos=1)/' odp-base.txt + + echo + echo '# Valid forms with MPLS multicast header.' + sed 's/\(eth([[^)]]*),?\)/\1,eth_type(0x8848),mpls(label=100,tc=7,ttl=64,bos=1)/' odp-base.txt + echo echo '# Valid forms with QoS priority.' sed 's/^/skb_priority(0x1234),/' odp-base.txt diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index 3eec9477a..08645cc8a 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -230,6 +230,12 @@ cookie=0x6 table=4 in_port=83 actions=load:4->NXM_NX_REG3[[]],mod_nw_src:83.83.8 cookie=0x7 table=5 in_port=84 actions=load:5->NXM_NX_REG4[[]],load:6->NXM_NX_TUN_ID[[]],mod_nw_dst:84.84.84.84,controller,resubmit(85,6) cookie=0x8 table=6 in_port=85 actions=mod_tp_src:85,controller,resubmit(86,7) cookie=0x9 table=7 in_port=86 actions=mod_tp_dst:86,controller,controller +cookie=0xa dl_src=40:44:44:44:44:42 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],controller +cookie=0xa dl_src=40:44:44:44:44:43 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],controller +cookie=0xa dl_src=40:44:44:44:44:44 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],controller +cookie=0xb dl_src=50:55:55:55:55:55 dl_type=0x8847 actions=load:1000->OXM_OF_MPLS_LABEL[[]],controller +cookie=0xd dl_src=60:66:66:66:66:66 actions=pop_mpls:0x0800,controller +cookie=0xc dl_src=70:77:77:77:77:77 actions=push_mpls:0x8848,load:1000->OXM_OF_MPLS_LABEL[[]],load:7->OXM_OF_MPLS_TC[[]],controller ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) @@ -290,6 +296,123 @@ OFPT_PACKET_IN (xid=0x0): total_len=64 in_port=1 (via action) data_len=64 (unbuf tcp,metadata=0,in_port=0,dl_vlan=15,dl_vlan_pcp=0,dl_src=30:33:33:33:33:33,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10 tcp_csum:0 ]) +dnl Modified MPLS controller action. +AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log]) + +for i in 1 2 3; do + ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:42,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)' +done + +OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) +AT_CHECK([cat ofctl_monitor.log], [0], [dnl +NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered) +mpls(label:10,tc:3,ttl:64,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:42,dl_dst=50:54:00:00:00:07 +dnl +NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered) +mpls(label:10,tc:3,ttl:64,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:42,dl_dst=50:54:00:00:00:07 +dnl +NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered) +mpls(label:10,tc:3,ttl:64,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:42,dl_dst=50:54:00:00:00:07 +]) + +dnl Modified MPLS controller action. +AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log]) + +dnl in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8847),mpls(label=100,tc=3,ttl=64,bos=1) + +for i in 1 2 3; do + ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:43,dst=50:54:00:00:00:07),eth_type(0x8847),mpls(label=11,tc=3,ttl=64,bos=1)' +done + +OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) +AT_CHECK([cat ofctl_monitor.log], [0], [dnl +NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered) +mpls(label:10,tc:3,ttl:64,bos:0),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:43,dl_dst=50:54:00:00:00:07 +dnl +NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered) +mpls(label:10,tc:3,ttl:64,bos:0),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:43,dl_dst=50:54:00:00:00:07 +dnl +NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered) +mpls(label:10,tc:3,ttl:64,bos:0),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:43,dl_dst=50:54:00:00:00:07 +]) + +dnl Modified MPLS controller action. +AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log]) + +for i in 1 2 3; do + ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:44,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no))' +done + +OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) +AT_CHECK([cat ofctl_monitor.log], [0], [dnl +NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered) +mpls(label:10,tc:3,ttl:64,bos:1),metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=7,dl_src=40:44:44:44:44:44,dl_dst=50:54:00:00:00:07 +dnl +NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered) +mpls(label:10,tc:3,ttl:64,bos:1),metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=7,dl_src=40:44:44:44:44:44,dl_dst=50:54:00:00:00:07 +dnl +NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered) +mpls(label:10,tc:3,ttl:64,bos:1),metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=7,dl_src=40:44:44:44:44:44,dl_dst=50:54:00:00:00:07 +]) + +dnl Modified MPLS actions. +AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log]) + +for i in 1 2 3; do + ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:55:55:55:55:55,dst=50:54:00:00:00:07),eth_type(0x8847),mpls(label=100,tc=7,ttl=64,bos=1),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)' +done + +OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) +AT_CHECK([cat ofctl_monitor.log], [0], [dnl +NXT_PACKET_IN (xid=0x0): cookie=0xb total_len=60 in_port=1 (via action) data_len=60 (unbuffered) +mpls(label:1000,tc:7,ttl:64,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:55:55:55:55:55,dl_dst=50:54:00:00:00:07 +dnl +NXT_PACKET_IN (xid=0x0): cookie=0xb total_len=60 in_port=1 (via action) data_len=60 (unbuffered) +mpls(label:1000,tc:7,ttl:64,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:55:55:55:55:55,dl_dst=50:54:00:00:00:07 +dnl +NXT_PACKET_IN (xid=0x0): cookie=0xb total_len=60 in_port=1 (via action) data_len=60 (unbuffered) +mpls(label:1000,tc:7,ttl:64,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:55:55:55:55:55,dl_dst=50:54:00:00:00:07 +]) + +dnl Modified MPLS ipv6 controller action. +AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log]) + +for i in 1 2 3; do + ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=70:77:77:77:77:77,dst=50:54:00:00:00:07),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=no)' +done + +OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) +AT_CHECK([cat ofctl_monitor.log], [0], [dnl +NXT_PACKET_IN (xid=0x0): cookie=0xc total_len=64 in_port=1 (via action) data_len=64 (unbuffered) +mplsm(label:1000,tc:7,ttl:64,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=70:77:77:77:77:77,dl_dst=50:54:00:00:00:07 +dnl +NXT_PACKET_IN (xid=0x0): cookie=0xc total_len=64 in_port=1 (via action) data_len=64 (unbuffered) +mplsm(label:1000,tc:7,ttl:64,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=70:77:77:77:77:77,dl_dst=50:54:00:00:00:07 +dnl +NXT_PACKET_IN (xid=0x0): cookie=0xc total_len=64 in_port=1 (via action) data_len=64 (unbuffered) +mplsm(label:1000,tc:7,ttl:64,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=70:77:77:77:77:77,dl_dst=50:54:00:00:00:07 +]) + + +dnl Modified MPLS pop action. +AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log]) + +for i in 1 2 3; do + ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=60:66:66:66:66:66,dst=50:54:00:00:00:07),eth_type(0x8847),mpls(label=10,tc=3,ttl=100,bos=1),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)' +done + +OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit]) +AT_CHECK([cat ofctl_monitor.log], [0], [dnl +NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=56 in_port=1 (via action) data_len=56 (unbuffered) +tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=60:66:66:66:66:66,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64 tcp_csum:0 +dnl +NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=56 in_port=1 (via action) data_len=56 (unbuffered) +tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=60:66:66:66:66:66,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64 tcp_csum:0 +dnl +NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=56 in_port=1 (via action) data_len=56 (unbuffered) +tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=60:66:66:66:66:66,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64 tcp_csum:0 +]) + dnl Checksum TCP. AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --no-chdir --pidfile 2> ofctl_monitor.log]) @@ -374,6 +497,12 @@ AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0x7, table=5, n_packets=2, n_bytes=120, in_port=84 actions=load:0x5->NXM_NX_REG4[[]],load:0x6->NXM_NX_TUN_ID[[]],mod_nw_dst:84.84.84.84,CONTROLLER:65535,resubmit(85,6) cookie=0x8, table=6, n_packets=2, n_bytes=120, in_port=85 actions=mod_tp_src:85,CONTROLLER:65535,resubmit(86,7) cookie=0x9, table=7, n_packets=2, n_bytes=120, in_port=86 actions=mod_tp_dst:86,CONTROLLER:65535,CONTROLLER:65535 + cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:44:42 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],CONTROLLER:65535 + cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:44:43 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],CONTROLLER:65535 + cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:44:44 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],CONTROLLER:65535 + cookie=0xb, n_packets=3, n_bytes=180, dl_src=50:55:55:55:55:55,dl_type=0x8847 actions=load:0x3e8->OXM_OF_MPLS_LABEL[[]],CONTROLLER:65535 + cookie=0xc, n_packets=3, n_bytes=180, dl_src=70:77:77:77:77:77 actions=push_mpls:0x8848,load:0x3e8->OXM_OF_MPLS_LABEL[[]],load:0x7->OXM_OF_MPLS_TC[[]],CONTROLLER:65535 + cookie=0xd, n_packets=3, n_bytes=180, dl_src=60:66:66:66:66:66 actions=pop_mpls:0x0800,CONTROLLER:65535 n_packets=3, n_bytes=180, dl_src=10:11:11:11:11:11 actions=CONTROLLER:65535 NXST_FLOW reply: ]) diff --git a/tests/test-bundle.c b/tests/test-bundle.c index aa8b6f0f4..f5b24b4a3 100644 --- a/tests/test-bundle.c +++ b/tests/test-bundle.c @@ -137,6 +137,7 @@ main(int argc, char *argv[]) for (i = 0; i < N_FLOWS; i++) { random_bytes(&flows[i], sizeof flows[i]); memset(flows[i].zeros, 0, sizeof flows[i].zeros); + flows[i].mpls_depth = 0; flows[i].regs[0] = OFPP_NONE; } diff --git a/tests/test-multipath.c b/tests/test-multipath.c index b990c1338..8442bc2e9 100644 --- a/tests/test-multipath.c +++ b/tests/test-multipath.c @@ -61,6 +61,7 @@ main(int argc, char *argv[]) random_bytes(&flow, sizeof flow); memset(flow.zeros, 0, sizeof flow.zeros); + flow.mpls_depth = 0; mp.max_link = n - 1; multipath_execute(&mp, &flow); diff --git a/utilities/ovs-dpctl.c b/utilities/ovs-dpctl.c index b48b349b4..363485bed 100644 --- a/utilities/ovs-dpctl.c +++ b/utilities/ovs-dpctl.c @@ -939,14 +939,13 @@ dpctl_normalize_actions(int argc, char *argv[]) hmap_init(&actions_per_flow); NL_ATTR_FOR_EACH (a, left, odp_actions.data, odp_actions.size) { - if (nl_attr_type(a) == OVS_ACTION_ATTR_POP_VLAN) { + const struct ovs_action_push_vlan *push; + switch(nl_attr_type(a)) { + case OVS_ACTION_ATTR_POP_VLAN: flow.vlan_tci = htons(0); continue; - } - - if (nl_attr_type(a) == OVS_ACTION_ATTR_PUSH_VLAN) { - const struct ovs_action_push_vlan *push; + case OVS_ACTION_ATTR_PUSH_VLAN: push = nl_attr_get_unspec(a, sizeof *push); flow.vlan_tci = push->vlan_tci; continue; @@ -980,6 +979,15 @@ dpctl_normalize_actions(int argc, char *argv[]) printf("no vlan: "); } + if (af->flow.mpls_depth) { + printf("mpls(label=%"PRIu32",tc=%d,ttl=%d): ", + mpls_lse_to_label(af->flow.mpls_lse), + mpls_lse_to_tc(af->flow.mpls_lse), + mpls_lse_to_ttl(af->flow.mpls_lse)); + } else { + printf("no mpls: "); + } + ds_clear(&s); format_odp_actions(&s, af->actions.data, af->actions.size); puts(ds_cstr(&s)); diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in index c48645af4..c865bbc1c 100644 --- a/utilities/ovs-ofctl.8.in +++ b/utilities/ovs-ofctl.8.in @@ -915,6 +915,34 @@ for the tag. Only ethertype 0x8100 should be used. (0x88a8 which the spec allows isn't supported at the moment.) A priority of zero and the tag of zero are used for the new tag. . +.IP \fBpush_mpls\fR:\fIethertype\fR +If the packet does not already contain any MPLS labels, changes the +packet's Ethertype to \fIethertype\fR, which must be either the MPLS +unicast Ethertype \fB0x8847\fR or the MPLS multicast Ethertype +\fB0x8848\fR, and then pushes an initial label stack entry. The label +stack entry's default label is 2 if the packet contains IPv6 and 0 +otherwise, its default traffic control value is the low 3 bits of the +packet's DSCP value (0 if the packet is not IP), and its TTL is copied +from the IP TTL (64 if the packet is not IP). +.IP +If the packet does already contain an MPLS label, pushes a new +outermost label as a copy of the existing outermost label. +.IP +There are some limitations in the implementation. \fBpush_mpls\fR +followed by another \fBpush_mpls\fR will result in the first +\fBpush_mpls\fR being discarded. +. +.IP \fBpop_mpls\fR:\fIethertype\fR +Strips the outermost MPLS label stack entry. If the MPLS label +stripped was the only one, changes the ethertype of a packet to +\fIethertype\fR, which should not ordinarily be an MPLS Ethertype. +. +.IP +There are some limitations in the implementation. \fBpop_mpls\fR +followed by another \fBpush_mpls\fR without an intermediate +\fBpush_mpls\fR will result in the first \fBpush_mpls\fR being +discarded. +. .IP \fBmod_dl_src\fB:\fImac\fR Sets the source Ethernet address to \fImac\fR. .