X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Fofp-util.c;h=99ef722332830726405f2b197ae509a7ba8d4f9a;hb=772ec52b896380f23b587bea4a5bf18edd22449d;hp=5c4336a812feadadce42cef4e1feb6918ea06fb8;hpb=44381c1b4e01d051a8cc9ffccb8c37a0c2d4c808;p=openvswitch diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 5c4336a8..99ef7223 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -19,6 +19,7 @@ #include #include #include "byte-order.h" +#include "classifier.h" #include "nx-match.h" #include "ofp-util.h" #include "ofpbuf.h" @@ -32,6 +33,236 @@ VLOG_DEFINE_THIS_MODULE(ofp_util); * 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); +/* Given the wildcard bit count in the least-significant 6 of 'wcbits', returns + * an IP netmask with a 1 in each bit that must match and a 0 in each bit that + * is wildcarded. + * + * The bits in 'wcbits' are in the format used in enum ofp_flow_wildcards: 0 + * is exact match, 1 ignores the LSB, 2 ignores the 2 least-significant bits, + * ..., 32 and higher wildcard the entire field. This is the *opposite* of the + * usual convention where e.g. /24 indicates that 8 bits (not 24 bits) are + * wildcarded. */ +ovs_be32 +ofputil_wcbits_to_netmask(int wcbits) +{ + wcbits &= 0x3f; + return wcbits < 32 ? htonl(~((1u << wcbits) - 1)) : 0; +} + +/* Given the IP netmask 'netmask', returns the number of bits of the IP address + * that it wildcards. 'netmask' must be a CIDR netmask (see ip_is_cidr()). */ +int +ofputil_netmask_to_wcbits(ovs_be32 netmask) +{ + assert(ip_is_cidr(netmask)); +#if __GNUC__ >= 4 + return netmask == htonl(0) ? 32 : __builtin_ctz(ntohl(netmask)); +#else + int wcbits; + + for (wcbits = 32; netmask; wcbits--) { + netmask &= netmask - 1; + } + + return wcbits; +#endif +} + +/* A list of the FWW_* and OFPFW_ bits that have the same value, meaning, and + * name. */ +#define WC_INVARIANT_LIST \ + WC_INVARIANT_BIT(IN_PORT) \ + WC_INVARIANT_BIT(DL_SRC) \ + WC_INVARIANT_BIT(DL_DST) \ + WC_INVARIANT_BIT(DL_TYPE) \ + WC_INVARIANT_BIT(NW_PROTO) \ + WC_INVARIANT_BIT(TP_SRC) \ + WC_INVARIANT_BIT(TP_DST) + +/* Verify that all of the invariant bits (as defined on WC_INVARIANT_LIST) + * actually have the same names and values. */ +#define WC_INVARIANT_BIT(NAME) BUILD_ASSERT_DECL(FWW_##NAME == OFPFW_##NAME); + WC_INVARIANT_LIST +#undef WC_INVARIANT_BIT + +/* WC_INVARIANTS is the invariant bits (as defined on WC_INVARIANT_LIST) all + * OR'd together. */ +enum { + 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'. + * + * 'flow_format' must either NXFF_OPENFLOW10 or NXFF_TUN_ID_FROM_COOKIE. In + * the latter case only, 'flow''s tun_id field will be taken from the high bits + * of 'cookie', if 'match''s wildcards do not indicate that tun_id is + * wildcarded. */ +void +ofputil_cls_rule_from_match(const struct ofp_match *match, + unsigned int priority, + enum nx_flow_format flow_format, + uint64_t cookie, struct cls_rule *rule) +{ + struct flow_wildcards *wc = &rule->wc; + unsigned int ofpfw; + ovs_be16 vid, pcp; + + /* Initialize rule->priority. */ + ofpfw = ntohl(match->wildcards); + ofpfw &= flow_format == NXFF_TUN_ID_FROM_COOKIE ? OVSFW_ALL : OFPFW_ALL; + rule->priority = !ofpfw ? UINT16_MAX : priority; + + /* Initialize most of rule->wc. */ + wc->wildcards = ofpfw & WC_INVARIANTS; + if (ofpfw & OFPFW_NW_TOS) { + wc->wildcards |= FWW_NW_TOS; + } + memset(wc->reg_masks, 0, sizeof wc->reg_masks); + wc->nw_src_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW_NW_SRC_SHIFT); + wc->nw_dst_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW_NW_DST_SHIFT); + + if (flow_format == NXFF_TUN_ID_FROM_COOKIE && !(ofpfw & NXFW_TUN_ID)) { + rule->flow.tun_id = htonl(ntohll(cookie) >> 32); + } else { + wc->wildcards |= FWW_TUN_ID; + rule->flow.tun_id = 0; + } + + if (ofpfw & OFPFW_DL_DST) { + /* OpenFlow 1.0 OFPFW_DL_DST covers the whole Ethernet destination, but + * Open vSwitch breaks the Ethernet destination into bits as FWW_DL_DST + * and FWW_ETH_MCAST. */ + wc->wildcards |= FWW_ETH_MCAST; + } + + /* 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.dl_type = match->dl_type; + rule->flow.tp_src = match->tp_src; + rule->flow.tp_dst = match->tp_dst; + memcpy(rule->flow.dl_src, match->dl_src, ETH_ADDR_LEN); + memcpy(rule->flow.dl_dst, match->dl_dst, ETH_ADDR_LEN); + rule->flow.nw_tos = match->nw_tos; + rule->flow.nw_proto = match->nw_proto; + + /* Translate VLANs. */ + vid = match->dl_vlan & htons(VLAN_VID_MASK); + pcp = htons((match->dl_vlan_pcp << VLAN_PCP_SHIFT) & VLAN_PCP_MASK); + switch (ofpfw & (OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP)) { + case OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP: + /* Wildcard everything. */ + rule->flow.vlan_tci = htons(0); + rule->wc.vlan_tci_mask = htons(0); + break; + + case OFPFW_DL_VLAN_PCP: + if (match->dl_vlan == htons(OFP_VLAN_NONE)) { + /* Match only packets without 802.1Q header. */ + rule->flow.vlan_tci = htons(0); + rule->wc.vlan_tci_mask = htons(0xffff); + } else { + /* Wildcard PCP, specific VID. */ + rule->flow.vlan_tci = vid | htons(VLAN_CFI); + rule->wc.vlan_tci_mask = htons(VLAN_VID_MASK | VLAN_CFI); + } + break; + + case OFPFW_DL_VLAN: + /* Wildcard VID, specific PCP. */ + rule->flow.vlan_tci = pcp | htons(VLAN_CFI); + rule->wc.vlan_tci_mask = htons(VLAN_PCP_MASK | VLAN_CFI); + break; + + case 0: + if (match->dl_vlan == htons(OFP_VLAN_NONE)) { + /* This case is odd, since we can't have a specific PCP without an + * 802.1Q header. However, older versions of OVS treated this as + * matching packets withut an 802.1Q header, so we do here too. */ + rule->flow.vlan_tci = htons(0); + rule->wc.vlan_tci_mask = htons(0xffff); + } else { + /* Specific VID and PCP. */ + rule->flow.vlan_tci = vid | pcp | htons(VLAN_CFI); + rule->wc.vlan_tci_mask = htons(0xffff); + } + break; + } + + /* Clean up. */ + cls_rule_zero_wildcarded_fields(rule); +} + +/* Extract 'flow' with 'wildcards' into the OpenFlow match structure + * 'match'. + * + * 'flow_format' must either NXFF_OPENFLOW10 or NXFF_TUN_ID_FROM_COOKIE. In + * the latter case only, 'match''s NXFW_TUN_ID bit will be filled in; otherwise + * it is always set to 0. */ +void +ofputil_cls_rule_to_match(const struct cls_rule *rule, + enum nx_flow_format flow_format, + struct ofp_match *match) +{ + const struct flow_wildcards *wc = &rule->wc; + unsigned int ofpfw; + + /* Figure out most OpenFlow wildcards. */ + ofpfw = 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) { + ofpfw |= OFPFW_NW_TOS; + } + if (flow_format == NXFF_TUN_ID_FROM_COOKIE && wc->wildcards & FWW_TUN_ID) { + ofpfw |= NXFW_TUN_ID; + } + + /* Translate VLANs. */ + match->dl_vlan = htons(0); + match->dl_vlan_pcp = 0; + if (rule->wc.vlan_tci_mask == htons(0)) { + ofpfw |= OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP; + } else if (rule->wc.vlan_tci_mask & htons(VLAN_CFI) + && !(rule->flow.vlan_tci & htons(VLAN_CFI))) { + match->dl_vlan = htons(OFP_VLAN_NONE); + } else { + if (!(rule->wc.vlan_tci_mask & htons(VLAN_VID_MASK))) { + ofpfw |= OFPFW_DL_VLAN; + } else { + match->dl_vlan = htons(vlan_tci_to_vid(rule->flow.vlan_tci)); + } + + if (!(rule->wc.vlan_tci_mask & htons(VLAN_PCP_MASK))) { + ofpfw |= OFPFW_DL_VLAN_PCP; + } else { + match->dl_vlan_pcp = vlan_tci_to_pcp(rule->flow.vlan_tci); + } + } + + /* 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); + 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 = rule->flow.dl_type; + match->nw_src = rule->flow.nw_src; + match->nw_dst = rule->flow.nw_dst; + match->nw_tos = rule->flow.nw_tos; + match->nw_proto = rule->flow.nw_proto; + match->tp_src = rule->flow.tp_src; + match->tp_dst = rule->flow.tp_dst; + memset(match->pad1, '\0', sizeof match->pad1); + memset(match->pad2, '\0', sizeof match->pad2); +} + /* Returns a transaction ID to use for an outgoing OpenFlow message. */ static ovs_be32 alloc_xid(void) @@ -154,7 +385,8 @@ update_openflow_length(struct ofpbuf *buffer) } struct ofpbuf * -make_flow_mod(uint16_t command, const struct flow *flow, size_t actions_len) +make_flow_mod(uint16_t command, const struct cls_rule *rule, + size_t actions_len) { struct ofp_flow_mod *ofm; size_t size = sizeof *ofm + actions_len; @@ -164,29 +396,17 @@ make_flow_mod(uint16_t command, const struct flow *flow, size_t actions_len) ofm->header.type = OFPT_FLOW_MOD; ofm->header.length = htons(size); ofm->cookie = 0; - ofm->match.wildcards = htonl(0); - ofm->match.in_port = htons(flow->in_port == ODPP_LOCAL ? OFPP_LOCAL - : flow->in_port); - memcpy(ofm->match.dl_src, flow->dl_src, sizeof ofm->match.dl_src); - memcpy(ofm->match.dl_dst, flow->dl_dst, sizeof ofm->match.dl_dst); - ofm->match.dl_vlan = flow->dl_vlan; - ofm->match.dl_vlan_pcp = flow->dl_vlan_pcp; - ofm->match.dl_type = flow->dl_type; - ofm->match.nw_src = flow->nw_src; - ofm->match.nw_dst = flow->nw_dst; - ofm->match.nw_proto = flow->nw_proto; - ofm->match.nw_tos = flow->nw_tos; - ofm->match.tp_src = flow->tp_src; - ofm->match.tp_dst = flow->tp_dst; + ofm->priority = htons(MIN(rule->priority, UINT16_MAX)); + ofputil_cls_rule_to_match(rule, NXFF_OPENFLOW10, &ofm->match); ofm->command = htons(command); return out; } struct ofpbuf * -make_add_flow(const struct flow *flow, uint32_t buffer_id, +make_add_flow(const struct cls_rule *rule, uint32_t buffer_id, uint16_t idle_timeout, size_t actions_len) { - struct ofpbuf *out = make_flow_mod(OFPFC_ADD, flow, actions_len); + struct ofpbuf *out = make_flow_mod(OFPFC_ADD, rule, actions_len); struct ofp_flow_mod *ofm = out->data; ofm->idle_timeout = htons(idle_timeout); ofm->hard_timeout = htons(OFP_FLOW_PERMANENT); @@ -195,16 +415,16 @@ make_add_flow(const struct flow *flow, uint32_t buffer_id, } struct ofpbuf * -make_del_flow(const struct flow *flow) +make_del_flow(const struct cls_rule *rule) { - struct ofpbuf *out = make_flow_mod(OFPFC_DELETE_STRICT, flow, 0); + struct ofpbuf *out = make_flow_mod(OFPFC_DELETE_STRICT, rule, 0); struct ofp_flow_mod *ofm = out->data; ofm->out_port = htons(OFPP_NONE); return out; } struct ofpbuf * -make_add_simple_flow(const struct flow *flow, +make_add_simple_flow(const struct cls_rule *rule, uint32_t buffer_id, uint16_t out_port, uint16_t idle_timeout) { @@ -212,14 +432,14 @@ make_add_simple_flow(const struct flow *flow, struct ofp_action_output *oao; struct ofpbuf *buffer; - buffer = make_add_flow(flow, buffer_id, idle_timeout, sizeof *oao); + buffer = make_add_flow(rule, buffer_id, idle_timeout, sizeof *oao); oao = ofpbuf_put_zeros(buffer, sizeof *oao); oao->type = htons(OFPAT_OUTPUT); oao->len = htons(sizeof *oao); oao->port = htons(out_port); return buffer; } else { - return make_add_flow(flow, buffer_id, idle_timeout, 0); + return make_add_flow(rule, buffer_id, idle_timeout, 0); } } @@ -747,10 +967,10 @@ normalize_match(struct ofp_match *m) m->tp_src = m->tp_dst = 0; } if (wc & OFPFW_NW_SRC_MASK) { - m->nw_src &= flow_nw_bits_to_mask(wc, OFPFW_NW_SRC_SHIFT); + m->nw_src &= ofputil_wcbits_to_netmask(wc >> OFPFW_NW_SRC_SHIFT); } if (wc & OFPFW_NW_DST_MASK) { - m->nw_dst &= flow_nw_bits_to_mask(wc, OFPFW_NW_DST_SHIFT); + m->nw_dst &= ofputil_wcbits_to_netmask(wc >> OFPFW_NW_DST_SHIFT); } if (wc & OFPFW_NW_TOS) { m->nw_tos = 0; @@ -762,10 +982,10 @@ normalize_match(struct ofp_match *m) m->nw_proto = 0; } if (wc & OFPFW_NW_SRC_MASK) { - m->nw_src &= flow_nw_bits_to_mask(wc, OFPFW_NW_SRC_SHIFT); + m->nw_src &= ofputil_wcbits_to_netmask(wc >> OFPFW_NW_SRC_SHIFT); } if (wc & OFPFW_NW_DST_MASK) { - m->nw_dst &= flow_nw_bits_to_mask(wc, OFPFW_NW_DST_SHIFT); + m->nw_dst &= ofputil_wcbits_to_netmask(wc >> OFPFW_NW_DST_SHIFT); } m->tp_src = m->tp_dst = m->nw_tos = 0; } else {