2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-31 14:25:26 +00:00

ofp-util: Revise OpenFlow 1.0 ofp_match normalization.

For a long time, Open vSwitch has "normalized" OpenFlow 1.0 flows in a
funny way: it tries to change fields that are wildcarded into fields
that are exact-match.  For example, the normalize_match() function
knows that if dl_type is wildcarded, then all of the L3 and L4 fields
will always be extracted as 0, so it sets those fields to exact-match
and their values to 0.

The reason for this was originally that exact-match flows were much
cheaper for Open vSwitch to implement, because they could be implemented
with a hash table, whereas other kinds of flows had to be implemented
with an expensive linear search.  But these days Open vSwitch has a
smarter classifier in which wildcarded flows have minimal cost.  Also,
it is no longer possible for OpenFlow 1.0 to specify truly exact-match
flows, because Open vSwitch supports fields for which OpenFlow 1.0
cannot specify values and therefore will always be wildcarded.

Now, it no longer makes sense to do this transformation, so this commit
removes it.  Presumably, this will be less surprising for users.

Reviewed-by: Simon Horman <horms@verge.net.au>
This commit is contained in:
Ben Pfaff
2011-05-02 11:04:33 -07:00
parent 34276a5790
commit 1c0b7503af
3 changed files with 53 additions and 84 deletions

View File

@@ -36,7 +36,7 @@
VLOG_DEFINE_THIS_MODULE(ofp_util);
static void normalize_match(struct ofp_match *);
static ovs_be32 normalize_wildcards(const struct ofp_match *);
/* Rate limit for OpenFlow message parse errors. These always indicate a bug
* in the peer and so there's not much point in showing a lot of them. */
@@ -870,8 +870,9 @@ ofputil_decode_flow_mod(struct flow_mod *fm, const struct ofp_header *oh)
ofputil_decode_msg_type(oh, &type);
if (ofputil_msg_type_code(type) == OFPUTIL_OFPT_FLOW_MOD) {
/* Standard OpenFlow flow_mod. */
struct ofp_match match, orig_match;
const struct ofp_flow_mod *ofm;
uint16_t priority;
ovs_be32 wc;
int error;
/* Dissect the message. */
@@ -881,26 +882,37 @@ ofputil_decode_flow_mod(struct flow_mod *fm, const struct ofp_header *oh)
return error;
}
/* Set priority based on original wildcards. Normally we'd allow
* ofputil_cls_rule_from_match() to do this for us, but
* normalize_wildcards() can put wildcards where the original flow
* didn't have them. */
priority = ntohs(ofm->priority);
if (!(ofm->match.wildcards & htonl(OFPFW_ALL))) {
priority = UINT16_MAX;
}
/* Normalize ofm->match. If normalization actually changes anything,
* then log the differences. */
match = ofm->match;
match.pad1[0] = match.pad2[0] = 0;
orig_match = match;
normalize_match(&match);
if (memcmp(&match, &orig_match, sizeof orig_match)) {
wc = normalize_wildcards(&ofm->match);
if (wc == ofm->match.wildcards) {
ofputil_cls_rule_from_match(&ofm->match, priority, &fm->cr);
} else {
struct ofp_match match = ofm->match;
match.wildcards = wc;
ofputil_cls_rule_from_match(&match, priority, &fm->cr);
if (!VLOG_DROP_INFO(&bad_ofmsg_rl)) {
char *old = ofp_match_to_literal_string(&orig_match);
char *new = ofp_match_to_literal_string(&match);
char *pre = ofp_match_to_string(&ofm->match, 1);
char *post = ofp_match_to_string(&match, 1);
VLOG_INFO("normalization changed ofp_match, details:");
VLOG_INFO(" pre: %s", old);
VLOG_INFO("post: %s", new);
free(old);
free(new);
VLOG_INFO(" pre: %s", pre);
VLOG_INFO("post: %s", post);
free(pre);
free(post);
}
}
/* Translate the message. */
ofputil_cls_rule_from_match(&match, ntohs(ofm->priority), &fm->cr);
fm->cookie = ofm->cookie;
fm->command = ntohs(ofm->command);
fm->idle_timeout = ntohs(ofm->idle_timeout);
@@ -2030,79 +2042,29 @@ actions_next(struct actions_iterator *iter)
}
}
static void
normalize_match(struct ofp_match *m)
static ovs_be32
normalize_wildcards(const struct ofp_match *m)
{
enum { OFPFW_NW = (OFPFW_NW_SRC_ALL | OFPFW_NW_DST_ALL | OFPFW_NW_PROTO
| OFPFW_NW_TOS) };
enum { OFPFW_TP = OFPFW_TP_SRC | OFPFW_TP_DST };
uint32_t wc;
ovs_be32 wc;
wc = ntohl(m->wildcards) & OFPFW_ALL;
if (wc & OFPFW_DL_TYPE) {
/* Can't sensibly match on network or transport headers if the
* data link type is unknown. */
wc |= OFPFW_NW | OFPFW_TP;
wc = m->wildcards;
if (wc & htonl(OFPFW_DL_TYPE)) {
wc |= htonl(OFPFW_NW | OFPFW_TP);
} else if (m->dl_type == htons(ETH_TYPE_IP)) {
if (wc & OFPFW_NW_PROTO) {
/* Can't sensibly match on transport headers if the network
* protocol is unknown. */
wc |= OFPFW_TP;
} else if (m->nw_proto != IPPROTO_TCP &&
m->nw_proto != IPPROTO_UDP &&
m->nw_proto != IPPROTO_ICMP) {
/* Transport layer fields will always be extracted as zeros, so we
* can do an exact-match on those values. */
wc &= ~OFPFW_TP;
m->tp_src = m->tp_dst = 0;
if (wc & htonl(OFPFW_NW_PROTO) || (m->nw_proto != IPPROTO_TCP &&
m->nw_proto != IPPROTO_UDP &&
m->nw_proto != IPPROTO_ICMP)) {
wc |= htonl(OFPFW_TP);
}
} else if (m->dl_type != htons(ETH_TYPE_ARP) &&
m->dl_type != htons(ETH_TYPE_IPV6)) {
/* Network and transport layer fields will always be extracted as
* zeros, so we can do an exact-match on those values. */
wc &= ~(OFPFW_NW | OFPFW_TP);
m->nw_proto = m->nw_src = m->nw_dst = m->nw_tos = 0;
m->tp_src = m->tp_dst = 0;
} else if (m->dl_type == htons(ETH_TYPE_ARP)) {
wc |= htonl(OFPFW_TP);
} else {
wc |= htonl(OFPFW_NW | OFPFW_TP);
}
m->wildcards = htonl(wc);
}
/* Returns a string that describes 'match' in a very literal way, without
* interpreting its contents except in a very basic fashion. The returned
* string is intended to be fixed-length, so that it is easy to see differences
* between two such strings if one is put above another. This is useful for
* describing changes made by normalize_match().
*
* The caller must free the returned string (with free()). */
char *
ofp_match_to_literal_string(const struct ofp_match *match)
{
return xasprintf("wildcards=%#10"PRIx32" "
" in_port=%5"PRId16" "
" dl_src="ETH_ADDR_FMT" "
" dl_dst="ETH_ADDR_FMT" "
" dl_vlan=%5"PRId16" "
" dl_vlan_pcp=%3"PRId8" "
" dl_type=%#6"PRIx16" "
" nw_tos=%#4"PRIx8" "
" nw_proto=%#4"PRIx16" "
" nw_src=%#10"PRIx32" "
" nw_dst=%#10"PRIx32" "
" tp_src=%5"PRId16" "
" tp_dst=%5"PRId16,
ntohl(match->wildcards),
ntohs(match->in_port),
ETH_ADDR_ARGS(match->dl_src),
ETH_ADDR_ARGS(match->dl_dst),
ntohs(match->dl_vlan),
match->dl_vlan_pcp,
ntohs(match->dl_type),
match->nw_tos,
match->nw_proto,
ntohl(match->nw_src),
ntohl(match->nw_dst),
ntohs(match->tp_src),
ntohs(match->tp_dst));
return wc;
}
static uint32_t