Skip to content

Commit

Permalink
userspace: Add OXM field MFF_PACKET_TYPE
Browse files Browse the repository at this point in the history
Allow packet type namespace OFPHTN_ETHERTYPE as alternative pre-requisite
for matching L3 protocols (MPLS, IP, IPv6, ARP etc).

Change the meta-flow definition of packet_type field to use the new
custom format MFS_PACKET_TYPE representing "(NS,NS_TYPE)".

Parsing routine for MFS_PACKET_TYPE added to meta-flow.c. Formatting
routine for field packet_type extracted from match_format() and moved to
flow.c to be used from meta-flow.c for formatting MFS_PACKET_TYPE.

Updated the ovs-fields man page source meta-flow.xml with documentation
for packet-type-aware bridges and added documentation for field packet_type.

Added packet_type to the matching properties in tests/ofproto.at.

If dl_type is unwildcarded due to later packet modification, make sure it
is cleared again if the original packet_type was not PT_ETH.

Signed-off-by: Jan Scheurich <[email protected]>
Signed-off-by: Ben Pfaff <[email protected]>
  • Loading branch information
JanScheurich authored and blp committed Jun 27, 2017
1 parent be7ac2f commit 3d4b2e6
Show file tree
Hide file tree
Showing 24 changed files with 657 additions and 285 deletions.
3 changes: 2 additions & 1 deletion build-aux/extract-ofp-fields
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ FORMATTING = {"decimal": ("MFS_DECIMAL", 1, 8),
"OpenFlow 1.1+ port": ("MFS_OFP_PORT_OXM", 4, 4),
"frag": ("MFS_FRAG", 1, 1),
"tunnel flags": ("MFS_TNL_FLAGS", 2, 2),
"TCP flags": ("MFS_TCP_FLAGS", 2, 2)}
"TCP flags": ("MFS_TCP_FLAGS", 2, 2),
"packet type": ("MFS_PACKET_TYPE", 4, 4)}

PREREQS = {"none": "MFP_NONE",
"Ethernet": "MFP_ETHERNET",
Expand Down
5 changes: 5 additions & 0 deletions include/openvswitch/match.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

struct ds;
struct ofputil_port_map;
struct mf_field;

/* A flow classification match.
*
Expand Down Expand Up @@ -119,6 +120,10 @@ void match_set_ct_ipv6_dst_masked(struct match *, const struct in6_addr *,
const struct in6_addr *);

void match_set_packet_type(struct match *, ovs_be32 packet_type);
void match_set_default_packet_type(struct match *);
bool match_has_default_packet_type(const struct match *);
void match_add_ethernet_prereq(struct match *, const struct mf_field *);

void match_set_skb_priority(struct match *, uint32_t skb_priority);
void match_set_dl_type(struct match *, ovs_be16);
void match_set_dl_src(struct match *, const struct eth_addr );
Expand Down
20 changes: 20 additions & 0 deletions include/openvswitch/meta-flow.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ struct ofputil_tlv_table_mod;
*
* TCP flags: See the description of tcp_flags in ovs-ofctl(8).
*
* packet type: A pair of packet type namespace NS and NS_TYPE within
* that namespace "(NS,NS_TYPE)". NS and NS_TYPE are formatted in
* decimal or hexadecimal as and accept decimal and hexadecimal (with
* 0x prefix) at parsing.
*
* Prerequisites:
*
* The field's prerequisites. The values should be straightfoward.
Expand Down Expand Up @@ -248,6 +253,20 @@ enum OVS_PACKED_ENUM mf_field_id {
*/
MFF_RECIRC_ID,

/* "packet_type".
*
* Define the packet type in OpenFlow 1.5+.
*
* Type: be32.
* Maskable: no.
* Formatting: packet type.
* Prerequisites: none.
* Access: read-only.
* NXM: none.
* OXM: OXM_OF_PACKET_TYPE(44) since OF1.5 and v2.8.
*/
MFF_PACKET_TYPE,

/* "conj_id".
*
* ID for "conjunction" actions. Please refer to ovs-ofctl(8)
Expand Down Expand Up @@ -1860,6 +1879,7 @@ enum OVS_PACKED_ENUM mf_string {
MFS_FRAG, /* no, yes, first, later, not_later */
MFS_TNL_FLAGS, /* FLOW_TNL_F_* flags */
MFS_TCP_FLAGS, /* TCP_* flags */
MFS_PACKET_TYPE, /* "(NS,NS_TYPE)" */
};

struct mf_field {
Expand Down
34 changes: 33 additions & 1 deletion lib/flow.c
Original file line number Diff line number Diff line change
Expand Up @@ -1153,6 +1153,38 @@ format_flags_masked(struct ds *ds, const char *name,
}
}

static void
put_u16_masked(struct ds *s, uint16_t value, uint16_t mask)
{
if (!mask) {
ds_put_char(s, '*');
} else {
if (value > 9) {
ds_put_format(s, "0x%"PRIx16, value);
} else {
ds_put_format(s, "%"PRIu16, value);
}

if (mask != UINT16_MAX) {
ds_put_format(s, "/0x%"PRIx16, mask);
}
}
}

void
format_packet_type_masked(struct ds *s, ovs_be32 value, ovs_be32 mask)
{
if (value == htonl(PT_ETH) && mask == OVS_BE32_MAX) {
ds_put_cstr(s, "eth");
} else {
ds_put_cstr(s, "packet_type=(");
put_u16_masked(s, pt_ns(value), pt_ns(mask));
ds_put_char(s, ',');
put_u16_masked(s, pt_ns_type(value), pt_ns_type(mask));
ds_put_char(s, ')');
}
}

/* Scans a string 's' of flags to determine their numerical value and
* returns the number of characters parsed using 'bit_to_string' to
* lookup flag names. Scanning continues until the character 'end' is
Expand Down Expand Up @@ -1425,7 +1457,6 @@ flow_wildcards_init_for_packet(struct flow_wildcards *wc,
WC_MASK_FIELD(wc, tunnel.tp_dst);
WC_MASK_FIELD(wc, tunnel.gbp_id);
WC_MASK_FIELD(wc, tunnel.gbp_flags);
WC_MASK_FIELD(wc, packet_type);

if (!(flow->tunnel.flags & FLOW_TNL_F_UDPIF)) {
if (flow->tunnel.metadata.present.map) {
Expand Down Expand Up @@ -1457,6 +1488,7 @@ flow_wildcards_init_for_packet(struct flow_wildcards *wc,

/* actset_output wildcarded. */

WC_MASK_FIELD(wc, packet_type);
WC_MASK_FIELD(wc, dl_dst);
WC_MASK_FIELD(wc, dl_src);
WC_MASK_FIELD(wc, dl_type);
Expand Down
27 changes: 21 additions & 6 deletions lib/flow.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ void format_flags(struct ds *ds, const char *(*bit_to_string)(uint32_t),
void format_flags_masked(struct ds *ds, const char *name,
const char *(*bit_to_string)(uint32_t),
uint32_t flags, uint32_t mask, uint32_t max_mask);
void format_packet_type_masked(struct ds *, ovs_be32 value, ovs_be32 mask);
int parse_flags(const char *s, const char *(*bit_to_string)(uint32_t),
char end, const char *field_name, char **res_string,
uint32_t *res_flags, uint32_t allowed, uint32_t *res_mask);
Expand Down Expand Up @@ -964,9 +965,23 @@ static inline bool is_ethernet(const struct flow *flow,
return flow->packet_type == htonl(PT_ETH);
}

static inline ovs_be16 get_dl_type(const struct flow *flow)
{
if (flow->packet_type == htonl(PT_ETH)) {
return flow->dl_type;
} else if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE) {
return pt_ns_type_be(flow->packet_type);
} else {
return htons(FLOW_DL_TYPE_NONE);
}
}

static inline bool is_vlan(const struct flow *flow,
struct flow_wildcards *wc)
{
if (!is_ethernet(flow, wc)) {
return false;
}
if (wc) {
WC_MASK_FIELD_MASK(wc, vlans[0].tci, htons(VLAN_CFI));
}
Expand All @@ -975,7 +990,7 @@ static inline bool is_vlan(const struct flow *flow,

static inline bool is_ip_any(const struct flow *flow)
{
return dl_type_is_ip_any(flow->dl_type);
return dl_type_is_ip_any(get_dl_type(flow));
}

static inline bool is_ip_proto(const struct flow *flow, uint8_t ip_proto,
Expand Down Expand Up @@ -1011,7 +1026,7 @@ static inline bool is_sctp(const struct flow *flow,
static inline bool is_icmpv4(const struct flow *flow,
struct flow_wildcards *wc)
{
if (flow->dl_type == htons(ETH_TYPE_IP)) {
if (get_dl_type(flow) == htons(ETH_TYPE_IP)) {
if (wc) {
memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
}
Expand All @@ -1023,7 +1038,7 @@ static inline bool is_icmpv4(const struct flow *flow,
static inline bool is_icmpv6(const struct flow *flow,
struct flow_wildcards *wc)
{
if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
if (get_dl_type(flow) == htons(ETH_TYPE_IPV6)) {
if (wc) {
memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
}
Expand Down Expand Up @@ -1054,7 +1069,7 @@ static inline bool is_nd(const struct flow *flow,

static inline bool is_igmp(const struct flow *flow, struct flow_wildcards *wc)
{
if (flow->dl_type == htons(ETH_TYPE_IP)) {
if (get_dl_type(flow) == htons(ETH_TYPE_IP)) {
if (wc) {
memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
}
Expand Down Expand Up @@ -1098,8 +1113,8 @@ static inline bool is_mld_report(const struct flow *flow,

static inline bool is_stp(const struct flow *flow)
{
return (eth_addr_equals(flow->dl_dst, eth_addr_stp)
&& flow->dl_type == htons(FLOW_DL_TYPE_NONE));
return (flow->dl_type == htons(FLOW_DL_TYPE_NONE)
&& eth_addr_equals(flow->dl_dst, eth_addr_stp));
}

#endif /* flow.h */
1 change: 1 addition & 0 deletions lib/learn.c
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ learn_execute(const struct ofpact_learn *learn, const struct flow *flow,
switch (spec->dst_type) {
case NX_LEARN_DST_MATCH:
mf_write_subfield(&spec->dst, &value, &fm->match);
match_add_ethernet_prereq(&fm->match, spec->dst.field);
mf_vl_mff_set_tlv_bitmap(
spec->dst.field, &fm->match.flow.tunnel.metadata.present.map);
break;
Expand Down
98 changes: 67 additions & 31 deletions lib/match.c
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,49 @@ match_set_packet_type(struct match *match, ovs_be32 packet_type)
match->wc.masks.packet_type = OVS_BE32_MAX;
}

/* If 'match' does not match on any packet type, make it match on Ethernet
* packets (the default packet type, as specified by OpenFlow). */
void
match_set_default_packet_type(struct match *match)
{
if (!match->wc.masks.packet_type) {
match_set_packet_type(match, htonl(PT_ETH));
}
}

/* Returns true if 'match' matches only Ethernet packets (the default packet
* type, as specified by OpenFlow). */
bool
match_has_default_packet_type(const struct match *match)
{
return (match->flow.packet_type == htonl(PT_ETH)
&& match->wc.masks.packet_type == OVS_BE32_MAX);
}

/* A match on 'field' is being added to or has been added to 'match'. If
* 'field' is a data field, and 'match' does not already match on packet_type,
* this function make it match on the Ethernet packet_type.
*
* This function is useful because OpenFlow implicitly applies to Ethernet
* packets when there's no explicit packet_type, but matching on a metadata
* field doesn't imply anything about the packet_type and falsely inferring
* that it does can cause harm. A flow that matches only on metadata fields,
* for example, should be able to match more than just Ethernet flows. There
* are also important reasons that a catch-all match (one with no field matches
* at all) should not imply a packet_type(0,0) match. For example, a "flow
* dump" request that matches on no fields should return every flow in the
* switch, not just the flows that match on Ethernet. As a second example,
* OpenFlow 1.2+ special-cases "table miss" flows, that is catch-all flows with
* priority 0, and inferring a match on packet_type(0,0) causes such a flow not
* to be a table miss flow. */
void
match_add_ethernet_prereq(struct match *match, const struct mf_field *field)
{
if (field->prereqs != MFP_NONE) {
match_set_default_packet_type(match);
}
}

void
match_set_dl_type(struct match *match, ovs_be16 dl_type)
{
Expand Down Expand Up @@ -1187,8 +1230,8 @@ match_format(const struct match *match,
size_t start_len = s->length;
const struct flow *f = &match->flow;
bool skip_type = false;

bool skip_proto = false;
ovs_be16 dl_type = f->dl_type;

int i;

Expand Down Expand Up @@ -1269,25 +1312,18 @@ match_format(const struct match *match,
format_be16_masked(s, "ct_tp_dst", f->ct_tp_dst, wc->masks.ct_tp_dst);
}

if (wc->masks.packet_type) {
if (pt_ns_type_be(wc->masks.packet_type) == 0) {
ds_put_format(s, "packet_type=(%u,*),",
pt_ns(f->packet_type));
} else if (pt_ns_type_be(wc->masks.packet_type) == OVS_BE16_MAX) {
ds_put_format(s, "packet_type=(%u,%#"PRIx16"),",
pt_ns(f->packet_type),
pt_ns_type(f->packet_type));
} else {
ds_put_format(s, "packet_type=(%u,%#"PRIx16"/%#"PRIx16"),",
pt_ns(f->packet_type),
pt_ns_type(f->packet_type),
pt_ns_type(wc->masks.packet_type));
if (wc->masks.packet_type && !match_has_default_packet_type(match)) {
format_packet_type_masked(s, f->packet_type, wc->masks.packet_type);
ds_put_char(s, ',');
if (pt_ns(f->packet_type) == OFPHTN_ETHERTYPE) {
dl_type = pt_ns_type_be(f->packet_type);
}
}

if (wc->masks.dl_type) {
dl_type = f->dl_type;
skip_type = true;
if (f->dl_type == htons(ETH_TYPE_IP)) {
if (dl_type == htons(ETH_TYPE_IP)) {
if (wc->masks.nw_proto) {
skip_proto = true;
if (f->nw_proto == IPPROTO_ICMP) {
Expand All @@ -1307,7 +1343,7 @@ match_format(const struct match *match,
} else {
ds_put_format(s, "%sip%s,", colors.value, colors.end);
}
} else if (f->dl_type == htons(ETH_TYPE_IPV6)) {
} else if (dl_type == htons(ETH_TYPE_IPV6)) {
if (wc->masks.nw_proto) {
skip_proto = true;
if (f->nw_proto == IPPROTO_ICMPV6) {
Expand All @@ -1325,13 +1361,13 @@ match_format(const struct match *match,
} else {
ds_put_format(s, "%sipv6%s,", colors.value, colors.end);
}
} else if (f->dl_type == htons(ETH_TYPE_ARP)) {
} else if (dl_type == htons(ETH_TYPE_ARP)) {
ds_put_format(s, "%sarp%s,", colors.value, colors.end);
} else if (f->dl_type == htons(ETH_TYPE_RARP)) {
} else if (dl_type == htons(ETH_TYPE_RARP)) {
ds_put_format(s, "%srarp%s,", colors.value, colors.end);
} else if (f->dl_type == htons(ETH_TYPE_MPLS)) {
} else if (dl_type == htons(ETH_TYPE_MPLS)) {
ds_put_format(s, "%smpls%s,", colors.value, colors.end);
} else if (f->dl_type == htons(ETH_TYPE_MPLS_MCAST)) {
} else if (dl_type == htons(ETH_TYPE_MPLS_MCAST)) {
ds_put_format(s, "%smplsm%s,", colors.value, colors.end);
} else {
skip_type = false;
Expand Down Expand Up @@ -1403,9 +1439,9 @@ match_format(const struct match *match,

if (!skip_type && wc->masks.dl_type) {
ds_put_format(s, "%sdl_type=%s0x%04"PRIx16",",
colors.param, colors.end, ntohs(f->dl_type));
colors.param, colors.end, ntohs(dl_type));
}
if (f->dl_type == htons(ETH_TYPE_IPV6)) {
if (dl_type == htons(ETH_TYPE_IPV6)) {
format_ipv6_netmask(s, "ipv6_src", &f->ipv6_src, &wc->masks.ipv6_src);
format_ipv6_netmask(s, "ipv6_dst", &f->ipv6_dst, &wc->masks.ipv6_dst);
if (wc->masks.ipv6_label) {
Expand All @@ -1419,26 +1455,26 @@ match_format(const struct match *match,
ntohl(wc->masks.ipv6_label));
}
}
} else if (f->dl_type == htons(ETH_TYPE_ARP) ||
f->dl_type == htons(ETH_TYPE_RARP)) {
} else if (dl_type == htons(ETH_TYPE_ARP) ||
dl_type == htons(ETH_TYPE_RARP)) {
format_ip_netmask(s, "arp_spa", f->nw_src, wc->masks.nw_src);
format_ip_netmask(s, "arp_tpa", f->nw_dst, wc->masks.nw_dst);
} else {
format_ip_netmask(s, "nw_src", f->nw_src, wc->masks.nw_src);
format_ip_netmask(s, "nw_dst", f->nw_dst, wc->masks.nw_dst);
}
if (!skip_proto && wc->masks.nw_proto) {
if (f->dl_type == htons(ETH_TYPE_ARP) ||
f->dl_type == htons(ETH_TYPE_RARP)) {
if (dl_type == htons(ETH_TYPE_ARP) ||
dl_type == htons(ETH_TYPE_RARP)) {
ds_put_format(s, "%sarp_op=%s%"PRIu8",",
colors.param, colors.end, f->nw_proto);
} else {
ds_put_format(s, "%snw_proto=%s%"PRIu8",",
colors.param, colors.end, f->nw_proto);
}
}
if (f->dl_type == htons(ETH_TYPE_ARP) ||
f->dl_type == htons(ETH_TYPE_RARP)) {
if (dl_type == htons(ETH_TYPE_ARP) ||
dl_type == htons(ETH_TYPE_RARP)) {
format_eth_masked(s, "arp_sha", f->arp_sha, wc->masks.arp_sha);
format_eth_masked(s, "arp_tha", f->arp_tha, wc->masks.arp_tha);
}
Expand Down Expand Up @@ -1491,15 +1527,15 @@ match_format(const struct match *match,
f->nw_frag & FLOW_NW_FRAG_LATER ? "later" : "not_later");
break;
}
if (f->dl_type == htons(ETH_TYPE_IP) &&
if (dl_type == htons(ETH_TYPE_IP) &&
f->nw_proto == IPPROTO_ICMP) {
format_be16_masked(s, "icmp_type", f->tp_src, wc->masks.tp_src);
format_be16_masked(s, "icmp_code", f->tp_dst, wc->masks.tp_dst);
} else if (f->dl_type == htons(ETH_TYPE_IP) &&
} else if (dl_type == htons(ETH_TYPE_IP) &&
f->nw_proto == IPPROTO_IGMP) {
format_be16_masked(s, "igmp_type", f->tp_src, wc->masks.tp_src);
format_be16_masked(s, "igmp_code", f->tp_dst, wc->masks.tp_dst);
} else if (f->dl_type == htons(ETH_TYPE_IPV6) &&
} else if (dl_type == htons(ETH_TYPE_IPV6) &&
f->nw_proto == IPPROTO_ICMPV6) {
format_be16_masked(s, "icmp_type", f->tp_src, wc->masks.tp_src);
format_be16_masked(s, "icmp_code", f->tp_dst, wc->masks.tp_dst);
Expand Down
Loading

0 comments on commit 3d4b2e6

Please sign in to comment.