X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;ds=sidebyside;f=lib%2Fofp-util.c;h=c1e98ba224a1125e1dc5b1c183b0261571557926;hb=df2fa9b50e1c0e93c6be1d31a5d69eb4f077add3;hp=4cb4b7026a67f5ab4147659cee2874c61666dee6;hpb=118c46769fce3be2c70dae494b02753c1db1780d;p=openvswitch diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 4cb4b702..c1e98ba2 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -18,6 +18,7 @@ #include "ofp-print.h" #include #include +#include #include #include "autopath.h" #include "byte-order.h" @@ -36,8 +37,6 @@ VLOG_DEFINE_THIS_MODULE(ofp_util); -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. */ static struct vlog_rate_limit bad_ofmsg_rl = VLOG_RATE_LIMIT_INIT(1, 5); @@ -96,12 +95,11 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask) /* WC_INVARIANTS is the invariant bits (as defined on WC_INVARIANT_LIST) all * OR'd together. */ -enum { - WC_INVARIANTS = 0 +static const flow_wildcards_t WC_INVARIANTS = 0 #define WC_INVARIANT_BIT(NAME) | FWW_##NAME WC_INVARIANT_LIST #undef WC_INVARIANT_BIT -}; +; /* Converts the ofp_match in 'match' into a cls_rule in 'rule', with the given * 'priority'. */ @@ -110,7 +108,7 @@ ofputil_cls_rule_from_match(const struct ofp_match *match, unsigned int priority, struct cls_rule *rule) { struct flow_wildcards *wc = &rule->wc; - unsigned int ofpfw; + uint32_t ofpfw; ovs_be16 vid, pcp; /* Initialize rule->priority. */ @@ -119,7 +117,7 @@ ofputil_cls_rule_from_match(const struct ofp_match *match, /* Initialize most of rule->wc. */ flow_wildcards_init_catchall(wc); - wc->wildcards = ofpfw & WC_INVARIANTS; + wc->wildcards = (OVS_FORCE flow_wildcards_t) ofpfw & WC_INVARIANTS; /* Wildcard fields that aren't defined by ofp_match or tun_id. */ wc->wildcards |= (FWW_ARP_SHA | FWW_ARP_THA | FWW_ND_TARGET); @@ -140,8 +138,7 @@ ofputil_cls_rule_from_match(const struct ofp_match *match, /* Initialize most of rule->flow. */ rule->flow.nw_src = match->nw_src; rule->flow.nw_dst = match->nw_dst; - rule->flow.in_port = (match->in_port == htons(OFPP_LOCAL) ? ODPP_LOCAL - : ntohs(match->in_port)); + rule->flow.in_port = ntohs(match->in_port); rule->flow.dl_type = ofputil_dl_type_from_openflow(match->dl_type); rule->flow.tp_src = match->tp_src; rule->flow.tp_dst = match->tp_dst; @@ -202,10 +199,10 @@ void ofputil_cls_rule_to_match(const struct cls_rule *rule, struct ofp_match *match) { const struct flow_wildcards *wc = &rule->wc; - unsigned int ofpfw; + uint32_t ofpfw; /* Figure out most OpenFlow wildcards. */ - ofpfw = wc->wildcards & WC_INVARIANTS; + ofpfw = (OVS_FORCE uint32_t) (wc->wildcards & WC_INVARIANTS); ofpfw |= ofputil_netmask_to_wcbits(wc->nw_src_mask) << OFPFW_NW_SRC_SHIFT; ofpfw |= ofputil_netmask_to_wcbits(wc->nw_dst_mask) << OFPFW_NW_DST_SHIFT; if (wc->wildcards & FWW_NW_TOS) { @@ -236,8 +233,7 @@ ofputil_cls_rule_to_match(const struct cls_rule *rule, struct ofp_match *match) /* Compose most of the match structure. */ match->wildcards = htonl(ofpfw); - match->in_port = htons(rule->flow.in_port == ODPP_LOCAL ? OFPP_LOCAL - : rule->flow.in_port); + match->in_port = htons(rule->flow.in_port); memcpy(match->dl_src, rule->flow.dl_src, ETH_ADDR_LEN); memcpy(match->dl_dst, rule->flow.dl_dst, ETH_ADDR_LEN); match->dl_type = ofputil_dl_type_to_openflow(rule->flow.dl_type); @@ -363,6 +359,9 @@ static int ofputil_decode_vendor(const struct ofp_header *oh, const struct ofputil_msg_type **typep) { + BUILD_ASSERT_DECL(sizeof(struct nxt_set_flow_format) + != sizeof(struct nxt_flow_mod_table_id)); + static const struct ofputil_msg_type nxt_messages[] = { { OFPUTIL_NXT_ROLE_REQUEST, NXT_ROLE_REQUEST, "NXT_ROLE_REQUEST", @@ -409,6 +408,21 @@ ofputil_decode_vendor(const struct ofp_header *oh, } nh = (const struct nicira_header *) oh; + + if (nh->subtype == htonl(NXT_FLOW_MOD_TABLE_ID) + && oh->length == htons(sizeof(struct nxt_flow_mod_table_id))) { + /* NXT_SET_FLOW_FORMAT and NXT_FLOW_MOD_TABLE_ID accidentally have the + * same value but different lengths. ofputil_lookup_openflow_message() + * doesn't support this case, so special case it here. */ + static const struct ofputil_msg_type nxt_flow_mod_table_id = + { OFPUTIL_NXT_FLOW_MOD_TABLE_ID, + NXT_FLOW_MOD_TABLE_ID, "NXT_FLOW_MOD_TABLE_ID", + sizeof(struct nxt_flow_mod_table_id), 0 }; + + *typep = &nxt_flow_mod_table_id; + return 0; + } + return ofputil_lookup_openflow_message(&nxt_category, ntohl(nh->subtype), ntohs(oh->length), typep); } @@ -854,15 +868,33 @@ ofputil_make_set_flow_format(enum nx_flow_format flow_format) return msg; } +/* Returns an OpenFlow message that can be used to turn the flow_mod_table_id + * extension on or off (according to 'flow_mod_table_id'). */ +struct ofpbuf * +ofputil_make_flow_mod_table_id(bool flow_mod_table_id) +{ + struct nxt_flow_mod_table_id *nfmti; + struct ofpbuf *msg; + + nfmti = make_nxmsg(sizeof *nfmti, NXT_FLOW_MOD_TABLE_ID, &msg); + nfmti->set = flow_mod_table_id; + return msg; +} + /* Converts an OFPT_FLOW_MOD or NXT_FLOW_MOD message 'oh' into an abstract * flow_mod in 'fm'. Returns 0 if successful, otherwise an OpenFlow error * code. * + * 'flow_mod_table_id' should be true if the NXT_FLOW_MOD_TABLE_ID extension is + * enabled, false otherwise. + * * Does not validate the flow_mod actions. */ int -ofputil_decode_flow_mod(struct flow_mod *fm, const struct ofp_header *oh) +ofputil_decode_flow_mod(struct flow_mod *fm, const struct ofp_header *oh, + bool flow_mod_table_id) { const struct ofputil_msg_type *type; + uint16_t command; struct ofpbuf b; ofpbuf_use_const(&b, oh, ntohs(oh->length)); @@ -872,7 +904,6 @@ ofputil_decode_flow_mod(struct flow_mod *fm, const struct ofp_header *oh) /* Standard OpenFlow flow_mod. */ const struct ofp_flow_mod *ofm; uint16_t priority; - ovs_be32 wc; int error; /* Dissect the message. */ @@ -884,37 +915,20 @@ ofputil_decode_flow_mod(struct flow_mod *fm, const struct ofp_header *oh) /* 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 + * ofputil_normalize_rule() 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. */ - 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 *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", pre); - VLOG_INFO("post: %s", post); - free(pre); - free(post); - } - } + /* Translate the rule. */ + ofputil_cls_rule_from_match(&ofm->match, priority, &fm->cr); + ofputil_normalize_rule(&fm->cr, NXFF_OPENFLOW10); /* Translate the message. */ fm->cookie = ofm->cookie; - fm->command = ntohs(ofm->command); + command = ntohs(ofm->command); fm->idle_timeout = ntohs(ofm->idle_timeout); fm->hard_timeout = ntohs(ofm->hard_timeout); fm->buffer_id = ntohl(ofm->buffer_id); @@ -939,7 +953,7 @@ ofputil_decode_flow_mod(struct flow_mod *fm, const struct ofp_header *oh) /* Translate the message. */ fm->cookie = nfm->cookie; - fm->command = ntohs(nfm->command); + command = ntohs(nfm->command); fm->idle_timeout = ntohs(nfm->idle_timeout); fm->hard_timeout = ntohs(nfm->hard_timeout); fm->buffer_id = ntohl(nfm->buffer_id); @@ -949,17 +963,34 @@ ofputil_decode_flow_mod(struct flow_mod *fm, const struct ofp_header *oh) NOT_REACHED(); } + if (flow_mod_table_id) { + fm->command = command & 0xff; + fm->table_id = command >> 8; + } else { + fm->command = command; + fm->table_id = 0xff; + } + return 0; } /* Converts 'fm' into an OFPT_FLOW_MOD or NXT_FLOW_MOD message according to - * 'flow_format' and returns the message. */ + * 'flow_format' and returns the message. + * + * 'flow_mod_table_id' should be true if the NXT_FLOW_MOD_TABLE_ID extension is + * enabled, false otherwise. */ struct ofpbuf * ofputil_encode_flow_mod(const struct flow_mod *fm, - enum nx_flow_format flow_format) + enum nx_flow_format flow_format, + bool flow_mod_table_id) { size_t actions_len = fm->n_actions * sizeof *fm->actions; struct ofpbuf *msg; + uint16_t command; + + command = (flow_mod_table_id + ? (fm->command & 0xff) | (fm->table_id << 8) + : fm->command); if (flow_format == NXFF_OPENFLOW10) { struct ofp_flow_mod *ofm; @@ -985,7 +1016,7 @@ ofputil_encode_flow_mod(const struct flow_mod *fm, nfm = msg->data; nfm->cookie = fm->cookie; - nfm->command = htons(fm->command); + nfm->command = htons(command); nfm->idle_timeout = htons(fm->idle_timeout); nfm->hard_timeout = htons(fm->hard_timeout); nfm->priority = htons(fm->cr.priority); @@ -1298,6 +1329,7 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr, ofr = make_openflow_xid(sizeof *ofr, OFPT_FLOW_REMOVED, htonl(0), &msg); ofputil_cls_rule_to_match(&fr->rule, &ofr->match); + ofr->cookie = fr->cookie; ofr->priority = htons(fr->rule.priority); ofr->reason = fr->reason; ofr->duration_sec = htonl(fr->duration_sec); @@ -1990,10 +2022,9 @@ validate_actions(const union ofp_action *actions, size_t n_actions, return 0; } -/* Returns true if 'action' outputs to 'port' (which must be in network byte - * order), false otherwise. */ +/* Returns true if 'action' outputs to 'port', false otherwise. */ bool -action_outputs_to_port(const union ofp_action *action, uint16_t port) +action_outputs_to_port(const union ofp_action *action, ovs_be16 port) { switch (ntohs(action->type)) { case OFPAT_OUTPUT: @@ -2029,29 +2060,115 @@ actions_next(struct actions_iterator *iter) } } -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 }; - ovs_be32 wc; - - 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 & htonl(OFPFW_NW_PROTO) || (m->nw_proto != IPPROTO_TCP && - m->nw_proto != IPPROTO_UDP && - m->nw_proto != IPPROTO_ICMP)) { - wc |= htonl(OFPFW_TP); +/* "Normalizes" the wildcards in 'rule'. That means: + * + * 1. If the type of level N is known, then only the valid fields for that + * level may be specified. For example, ARP does not have a TOS field, + * so nw_tos must be wildcarded if 'rule' specifies an ARP flow. + * Similarly, IPv4 does not have any IPv6 addresses, so ipv6_src and + * ipv6_dst (and other fields) must be wildcarded if 'rule' specifies an + * IPv4 flow. + * + * 2. If the type of level N is not known (or not understood by Open + * vSwitch), then no fields at all for that level may be specified. For + * example, Open vSwitch does not understand SCTP, an L4 protocol, so the + * L4 fields tp_src and tp_dst must be wildcarded if 'rule' specifies an + * SCTP flow. + * + * 'flow_format' specifies the format of the flow as received or as intended to + * be sent. This is important for IPv6 and ARP, for which NXM supports more + * detailed matching. */ +void +ofputil_normalize_rule(struct cls_rule *rule, enum nx_flow_format flow_format) +{ + enum { + MAY_NW_ADDR = 1 << 0, /* nw_src, nw_dst */ + MAY_TP_ADDR = 1 << 1, /* tp_src, tp_dst */ + MAY_NW_PROTO = 1 << 2, /* nw_proto */ + MAY_NW_TOS = 1 << 3, /* nw_tos */ + MAY_ARP_SHA = 1 << 4, /* arp_sha */ + MAY_ARP_THA = 1 << 5, /* arp_tha */ + MAY_IPV6_ADDR = 1 << 6, /* ipv6_src, ipv6_dst */ + MAY_ND_TARGET = 1 << 7 /* nd_target */ + } may_match; + + struct flow_wildcards wc; + + /* Figure out what fields may be matched. */ + if (rule->flow.dl_type == htons(ETH_TYPE_IP)) { + may_match = MAY_NW_PROTO | MAY_NW_TOS | MAY_NW_ADDR; + if (rule->flow.nw_proto == IPPROTO_TCP || + rule->flow.nw_proto == IPPROTO_UDP || + rule->flow.nw_proto == IPPROTO_ICMP) { + may_match |= MAY_TP_ADDR; + } + } else if (rule->flow.dl_type == htons(ETH_TYPE_IPV6) + && flow_format == NXFF_NXM) { + may_match = MAY_NW_PROTO | MAY_NW_TOS | MAY_IPV6_ADDR; + if (rule->flow.nw_proto == IPPROTO_TCP || + rule->flow.nw_proto == IPPROTO_UDP) { + may_match |= MAY_TP_ADDR; + } else if (rule->flow.nw_proto == IPPROTO_ICMPV6) { + may_match |= MAY_TP_ADDR; + if (rule->flow.tp_src == htons(ND_NEIGHBOR_SOLICIT)) { + may_match |= MAY_ND_TARGET | MAY_ARP_SHA; + } else if (rule->flow.tp_src == htons(ND_NEIGHBOR_ADVERT)) { + may_match |= MAY_ND_TARGET | MAY_ARP_THA; + } + } + } else if (rule->flow.dl_type == htons(ETH_TYPE_ARP)) { + may_match = MAY_NW_PROTO | MAY_NW_ADDR; + if (flow_format == NXFF_NXM) { + may_match |= MAY_ARP_SHA | MAY_ARP_THA; } - } else if (m->dl_type == htons(ETH_TYPE_ARP)) { - wc |= htonl(OFPFW_TP); } else { - wc |= htonl(OFPFW_NW | OFPFW_TP); + may_match = 0; + } + + /* Clear the fields that may not be matched. */ + wc = rule->wc; + if (!(may_match & MAY_NW_ADDR)) { + wc.nw_src_mask = wc.nw_dst_mask = htonl(0); + } + if (!(may_match & MAY_TP_ADDR)) { + wc.wildcards |= FWW_TP_SRC | FWW_TP_DST; + } + if (!(may_match & MAY_NW_PROTO)) { + wc.wildcards |= FWW_NW_PROTO; + } + if (!(may_match & MAY_NW_TOS)) { + wc.wildcards |= FWW_NW_TOS; + } + if (!(may_match & MAY_ARP_SHA)) { + wc.wildcards |= FWW_ARP_SHA; + } + if (!(may_match & MAY_ARP_THA)) { + wc.wildcards |= FWW_ARP_THA; + } + if (!(may_match & MAY_IPV6_ADDR)) { + wc.ipv6_src_mask = wc.ipv6_dst_mask = in6addr_any; + } + if (!(may_match & MAY_ND_TARGET)) { + wc.wildcards |= FWW_ND_TARGET; + } + + /* Log any changes. */ + if (!flow_wildcards_equal(&wc, &rule->wc)) { + bool log = !VLOG_DROP_INFO(&bad_ofmsg_rl); + char *pre = log ? cls_rule_to_string(rule) : NULL; + + rule->wc = wc; + cls_rule_zero_wildcarded_fields(rule); + + if (log) { + char *post = cls_rule_to_string(rule); + VLOG_INFO("normalization changed ofp_match, details:"); + VLOG_INFO(" pre: %s", pre); + VLOG_INFO("post: %s", post); + free(pre); + free(post); + } } - return wc; } static uint32_t