X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Fodp-util.c;h=9b0876c45982af700bce46b4607cfece67bbb51c;hb=a088a1ffeba3d113c1be918969085a5c0de0d518;hp=85c9d1f5024cdc73ba5273c2d333eced1dffacc7;hpb=36fc5f183328c538eba3744bec3302972891f3e7;p=openvswitch diff --git a/lib/odp-util.c b/lib/odp-util.c index 85c9d1f5..9b0876c4 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -14,8 +14,8 @@ * limitations under the License. */ -#include #include +#include #include "odp-util.h" #include #include @@ -30,9 +30,8 @@ #include "flow.h" #include "netlink.h" #include "ofpbuf.h" -#include "openvswitch/tunnel.h" #include "packets.h" -#include "shash.h" +#include "simap.h" #include "timeval.h" #include "util.h" #include "vlog.h" @@ -49,7 +48,7 @@ VLOG_DEFINE_THIS_MODULE(odp_util); * from another. */ static const char *delimiters = ", \t\r\n"; -static int parse_odp_key_attr(const char *, const struct shash *port_names, +static int parse_odp_key_attr(const char *, const struct simap *port_names, struct ofpbuf *); static void format_odp_key_attr(const struct nlattr *a, struct ds *ds); @@ -94,6 +93,8 @@ ovs_key_attr_to_string(enum ovs_key_attr attr) case OVS_KEY_ATTR_UNSPEC: return "unspec"; case OVS_KEY_ATTR_ENCAP: return "encap"; case OVS_KEY_ATTR_PRIORITY: return "priority"; + case OVS_KEY_ATTR_TUN_ID: return "tun_id"; + case OVS_KEY_ATTR_IPV4_TUNNEL: return "ipv4_tunnel"; case OVS_KEY_ATTR_IN_PORT: return "in_port"; case OVS_KEY_ATTR_ETHERNET: return "eth"; case OVS_KEY_ATTR_VLAN: return "vlan"; @@ -106,7 +107,6 @@ 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_TUN_ID: return "tun_id"; case __OVS_KEY_ATTR_MAX: default: @@ -166,6 +166,111 @@ format_odp_sample_action(struct ds *ds, const struct nlattr *attr) ds_put_format(ds, "))"); } +static const char * +slow_path_reason_to_string(uint32_t data) +{ + enum slow_path_reason bit = (enum slow_path_reason) data; + + switch (bit) { + case SLOW_CFM: + return "cfm"; + case SLOW_LACP: + return "lacp"; + case SLOW_STP: + return "stp"; + case SLOW_IN_BAND: + return "in_band"; + case SLOW_CONTROLLER: + return "controller"; + case SLOW_MATCH: + return "match"; + default: + return NULL; + } +} + +static void +format_flags(struct ds *ds, const char *(*bit_to_string)(uint32_t), + uint32_t flags) +{ + uint32_t bad = 0; + + ds_put_format(ds, "("); + if (!flags) { + goto out; + } + while (flags) { + uint32_t bit = rightmost_1bit(flags); + const char *s; + + s = bit_to_string(bit); + if (s) { + ds_put_format(ds, "%s,", s); + } else { + bad |= bit; + } + + flags &= ~bit; + } + + if (bad) { + ds_put_format(ds, "0x%"PRIx32",", bad); + } + ds_chomp(ds, ','); +out: + ds_put_format(ds, ")"); +} + +static int +parse_flags(const char *s, const char *(*bit_to_string)(uint32_t), + uint32_t *res) +{ + uint32_t result = 0; + int n = 0; + + if (s[n] != '(') { + return -EINVAL; + } + n++; + + while (s[n] != ')') { + unsigned long long int flags; + uint32_t bit; + int n0; + + if (sscanf(&s[n], "%lli%n", &flags, &n0) > 0 && n0 > 0) { + n += n0 + (s[n + n0] == ','); + result |= flags; + continue; + } + + for (bit = 1; bit; bit <<= 1) { + const char *name = bit_to_string(bit); + size_t len; + + if (!name) { + continue; + } + + len = strlen(name); + if (!strncmp(s + n, name, len) && + (s[n + len] == ',' || s[n + len] == ')')) { + result |= bit; + n += len + (s[n + len] == ','); + break; + } + } + + if (!bit) { + return -EINVAL; + } + } + n++; + + *res = result; + return n; +} + static void format_odp_userspace_action(struct ds *ds, const struct nlattr *attr) { @@ -185,16 +290,22 @@ format_odp_userspace_action(struct ds *ds, const struct nlattr *attr) if (a[OVS_USERSPACE_ATTR_USERDATA]) { uint64_t userdata = nl_attr_get_u64(a[OVS_USERSPACE_ATTR_USERDATA]); - struct user_action_cookie cookie; + union user_action_cookie cookie; memcpy(&cookie, &userdata, sizeof cookie); switch (cookie.type) { case USER_ACTION_COOKIE_SFLOW: - ds_put_format(ds, ",sFlow," - "vid=%"PRIu16",pcp=%"PRIu8",output=%"PRIu32, - vlan_tci_to_vid(cookie.vlan_tci), - vlan_tci_to_pcp(cookie.vlan_tci), cookie.output); + ds_put_format(ds, ",sFlow(" + "vid=%"PRIu16",pcp=%"PRIu8",output=%"PRIu32")", + vlan_tci_to_vid(cookie.sflow.vlan_tci), + vlan_tci_to_pcp(cookie.sflow.vlan_tci), + cookie.sflow.output); + break; + + case USER_ACTION_COOKIE_SLOW_PATH: + ds_put_cstr(ds, ",slow_path"); + format_flags(ds, slow_path_reason_to_string, cookie.slow_path.reason); break; case USER_ACTION_COOKIE_UNSPEC: @@ -300,7 +411,7 @@ format_odp_actions(struct ds *ds, const struct nlattr *actions, } static int -parse_odp_action(const char *s, const struct shash *port_names, +parse_odp_action(const char *s, const struct simap *port_names, struct ofpbuf *actions) { /* Many of the sscanf calls in this function use oversized destination @@ -325,12 +436,11 @@ parse_odp_action(const char *s, const struct shash *port_names, if (port_names) { int len = strcspn(s, delimiters); - struct shash_node *node; + struct simap_node *node; - node = shash_find_len(port_names, s, len); + node = simap_find_len(port_names, s, len); if (node) { - nl_msg_put_u32(actions, OVS_ACTION_ATTR_OUTPUT, - (uintptr_t) node->data); + nl_msg_put_u32(actions, OVS_ACTION_ATTR_OUTPUT, node->data); return len; } } @@ -345,10 +455,10 @@ parse_odp_action(const char *s, const struct shash *port_names, if (sscanf(s, "userspace(pid=%lli)%n", &pid, &n) > 0 && n > 0) { odp_put_userspace_action(pid, NULL, actions); return n; - } else if (sscanf(s, "userspace(pid=%lli,sFlow,vid=%i," - "pcp=%i,output=%lli)%n", + } else if (sscanf(s, "userspace(pid=%lli,sFlow(vid=%i," + "pcp=%i,output=%lli))%n", &pid, &vid, &pcp, &output, &n) > 0 && n > 0) { - struct user_action_cookie cookie; + union user_action_cookie cookie; uint16_t tci; tci = vid | (pcp << VLAN_PCP_SHIFT); @@ -357,14 +467,36 @@ parse_odp_action(const char *s, const struct shash *port_names, } cookie.type = USER_ACTION_COOKIE_SFLOW; - cookie.vlan_tci = htons(tci); - cookie.output = output; + cookie.sflow.vlan_tci = htons(tci); + cookie.sflow.output = output; + odp_put_userspace_action(pid, &cookie, actions); + return n; + } else if (sscanf(s, "userspace(pid=%lli,slow_path%n", &pid, &n) > 0 + && n > 0) { + union user_action_cookie cookie; + int res; + + cookie.type = USER_ACTION_COOKIE_SLOW_PATH; + cookie.slow_path.unused = 0; + cookie.slow_path.reason = 0; + + res = parse_flags(&s[n], slow_path_reason_to_string, + &cookie.slow_path.reason); + if (res < 0) { + return res; + } + n += res; + if (s[n] != ')') { + return -EINVAL; + } + n++; + odp_put_userspace_action(pid, &cookie, actions); return n; } else if (sscanf(s, "userspace(pid=%lli,userdata=" "%31[x0123456789abcdefABCDEF])%n", &pid, userdata_s, &n) > 0 && n > 0) { - struct user_action_cookie cookie; + union user_action_cookie cookie; uint64_t userdata; userdata = strtoull(userdata_s, NULL, 0); @@ -443,7 +575,7 @@ parse_odp_action(const char *s, const struct shash *port_names, for (;;) { int retval; - s += strspn(s, delimiters); + n += strspn(s + n, delimiters); if (s[n] == ')') { break; } @@ -453,7 +585,6 @@ parse_odp_action(const char *s, const struct shash *port_names, return retval; } n += retval; - } nl_msg_end_nested(actions, actions_ofs); nl_msg_end_nested(actions, sample_ofs); @@ -471,7 +602,7 @@ parse_odp_action(const char *s, const struct shash *port_names, * Netlink attributes. On failure, no data is appended to 'actions'. Either * way, 'actions''s data might be reallocated. */ int -odp_actions_from_string(const char *s, const struct shash *port_names, +odp_actions_from_string(const char *s, const struct simap *port_names, struct ofpbuf *actions) { size_t old_size; @@ -514,6 +645,7 @@ odp_flow_key_attr_len(uint16_t type) case OVS_KEY_ATTR_ENCAP: return -2; case OVS_KEY_ATTR_PRIORITY: return 4; case OVS_KEY_ATTR_TUN_ID: return 8; + case OVS_KEY_ATTR_IPV4_TUNNEL: return sizeof(struct ovs_key_ipv4_tunnel); case OVS_KEY_ATTR_IN_PORT: return 4; case OVS_KEY_ATTR_ETHERNET: return sizeof(struct ovs_key_ethernet); case OVS_KEY_ATTR_VLAN: return sizeof(ovs_be16); @@ -568,6 +700,21 @@ ovs_frag_type_to_string(enum ovs_frag_type type) } } +static const char * +tun_flag_to_string(uint32_t flags) +{ + switch (flags) { + case OVS_TNL_F_DONT_FRAGMENT: + return "df"; + case OVS_TNL_F_CSUM: + return "csum"; + case OVS_TNL_F_KEY: + return "key"; + default: + return NULL; + } +} + static void format_odp_key_attr(const struct nlattr *a, struct ds *ds) { @@ -580,6 +727,7 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds) const struct ovs_key_icmpv6 *icmpv6_key; const struct ovs_key_arp *arp_key; const struct ovs_key_nd *nd_key; + const struct ovs_key_ipv4_tunnel *ipv4_tun_key; enum ovs_key_attr attr = nl_attr_type(a); int expected_len; @@ -610,6 +758,19 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds) ds_put_format(ds, "(%#"PRIx64")", ntohll(nl_attr_get_be64(a))); break; + case OVS_KEY_ATTR_IPV4_TUNNEL: + ipv4_tun_key = nl_attr_get(a); + ds_put_format(ds, "(tun_id=0x%"PRIx64",src="IP_FMT",dst="IP_FMT"," + "tos=0x%"PRIx8",ttl=%"PRIu8",flags", + ntohll(ipv4_tun_key->tun_id), + IP_ARGS(&ipv4_tun_key->ipv4_src), + IP_ARGS(&ipv4_tun_key->ipv4_dst), + ipv4_tun_key->ipv4_tos, ipv4_tun_key->ipv4_ttl); + + format_flags(ds, tun_flag_to_string, ipv4_tun_key->tun_flags); + ds_put_format(ds, ")"); + break; + case OVS_KEY_ATTR_IN_PORT: ds_put_format(ds, "(%"PRIu32")", nl_attr_get_u32(a)); break; @@ -788,7 +949,7 @@ ovs_frag_type_from_string(const char *s, enum ovs_frag_type *type) } static int -parse_odp_key_attr(const char *s, const struct shash *port_names, +parse_odp_key_attr(const char *s, const struct simap *port_names, struct ofpbuf *key) { /* Many of the sscanf calls in this function use oversized destination @@ -823,6 +984,41 @@ parse_odp_key_attr(const char *s, const struct shash *port_names, } } + { + char tun_id_s[32]; + int tos, ttl; + struct ovs_key_ipv4_tunnel tun_key; + int n = -1; + + if (sscanf(s, "ipv4_tunnel(tun_id=%31[x0123456789abcdefABCDEF]," + "src="IP_SCAN_FMT",dst="IP_SCAN_FMT + ",tos=%i,ttl=%i,flags%n", tun_id_s, + IP_SCAN_ARGS(&tun_key.ipv4_src), + IP_SCAN_ARGS(&tun_key.ipv4_dst), &tos, &ttl, + &n) > 0 && n > 0) { + int res; + + tun_key.tun_id = htonll(strtoull(tun_id_s, NULL, 0)); + tun_key.ipv4_tos = tos; + tun_key.ipv4_ttl = ttl; + + res = parse_flags(&s[n], tun_flag_to_string, &tun_key.tun_flags); + if (res < 0) { + return res; + } + n += res; + if (s[n] != ')') { + return -EINVAL; + } + n++; + + memset(&tun_key.pad, 0, sizeof tun_key.pad); + nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV4_TUNNEL, &tun_key, + sizeof tun_key); + return n; + } + } + { unsigned long long int in_port; int n = -1; @@ -835,14 +1031,14 @@ parse_odp_key_attr(const char *s, const struct shash *port_names, if (port_names && !strncmp(s, "in_port(", 8)) { const char *name; - const struct shash_node *node; + const struct simap_node *node; int name_len; name = s + 8; name_len = strcspn(s, ")"); - node = shash_find_len(port_names, name, name_len); + node = simap_find_len(port_names, name, name_len); if (node) { - nl_msg_put_u32(key, OVS_KEY_ATTR_IN_PORT, (uintptr_t) node->data); + nl_msg_put_u32(key, OVS_KEY_ATTR_IN_PORT, node->data); return 8 + name_len + 1; } } @@ -1119,15 +1315,15 @@ parse_odp_key_attr(const char *s, const struct shash *port_names, * data is appended to 'key'. Either way, 'key''s data might be * reallocated. * - * If 'port_names' is nonnull, it points to an shash that maps from a port name - * to a port number cast to void *. (Port names may be used instead of port - * numbers in in_port.) + * If 'port_names' is nonnull, it points to an simap that maps from a port name + * to a port number. (Port names may be used instead of port numbers in + * in_port.) * * On success, the attributes appended to 'key' are individually syntactically * valid, but they may not be valid as a sequence. 'key' might, for example, * have duplicated keys. odp_flow_key_to_flow() will detect those errors. */ int -odp_flow_key_from_string(const char *s, const struct shash *port_names, +odp_flow_key_from_string(const char *s, const struct simap *port_names, struct ofpbuf *key) { const size_t old_size = key->size; @@ -1158,9 +1354,17 @@ ovs_to_odp_frag(uint8_t nw_frag) : OVS_FRAG_TYPE_LATER); } -/* Appends a representation of 'flow' as OVS_KEY_ATTR_* attributes to 'buf'. */ +/* Appends a representation of 'flow' as OVS_KEY_ATTR_* attributes to 'buf'. + * 'flow->in_port' is ignored (since it is likely to be an OpenFlow port + * number rather than a datapath port number). Instead, if 'odp_in_port' + * is anything other than OVSP_NONE, it is included in 'buf' as the input + * port. + * + * 'buf' must have at least ODPUTIL_FLOW_KEY_BYTES bytes of space, or be + * capable of being expanded to allow for that much space. */ void -odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow) +odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow, + uint32_t odp_in_port) { struct ovs_key_ethernet *eth_key; size_t encap; @@ -1169,13 +1373,12 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow) nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, flow->skb_priority); } - if (flow->tun_id != htonll(0)) { - nl_msg_put_be64(buf, OVS_KEY_ATTR_TUN_ID, flow->tun_id); + if (flow->tunnel.tun_id != htonll(0)) { + nl_msg_put_be64(buf, OVS_KEY_ATTR_TUN_ID, flow->tunnel.tun_id); } - if (flow->in_port != OFPP_NONE && flow->in_port != OFPP_CONTROLLER) { - nl_msg_put_u32(buf, OVS_KEY_ATTR_IN_PORT, - ofp_port_to_odp_port(flow->in_port)); + if (odp_in_port != OVSP_NONE) { + nl_msg_put_u32(buf, OVS_KEY_ATTR_IN_PORT, odp_in_port); } eth_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ETHERNET, @@ -1223,7 +1426,8 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow) ipv6_key->ipv6_tclass = flow->nw_tos; ipv6_key->ipv6_hlimit = flow->nw_ttl; ipv6_key->ipv6_frag = ovs_to_odp_frag(flow->nw_frag); - } else if (flow->dl_type == htons(ETH_TYPE_ARP)) { + } else if (flow->dl_type == htons(ETH_TYPE_ARP) || + flow->dl_type == htons(ETH_TYPE_RARP)) { struct ovs_key_arp *arp_key; arp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ARP, @@ -1479,7 +1683,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)) { + } else if (flow->dl_type == htons(ETH_TYPE_ARP) || + flow->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; @@ -1629,6 +1834,10 @@ parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1], * structure in 'flow'. Returns an ODP_FIT_* value that indicates how well * 'key' fits our expectations for what a flow key should contain. * + * The 'in_port' will be the datapath's understanding of the port. The + * caller will need to translate with odp_port_to_ofp_port() if the + * OpenFlow port is needed. + * * This function doesn't take the packet itself as an argument because none of * the currently understood OVS_KEY_ATTR_* attributes require it. Currently, * it is always possible to infer which additional attribute(s) should appear @@ -1640,7 +1849,6 @@ enum odp_key_fitness odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, struct flow *flow) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1]; uint64_t expected_attrs; uint64_t present_attrs; @@ -1662,21 +1870,15 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, } if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TUN_ID)) { - flow->tun_id = nl_attr_get_be64(attrs[OVS_KEY_ATTR_TUN_ID]); + flow->tunnel.tun_id = nl_attr_get_be64(attrs[OVS_KEY_ATTR_TUN_ID]); expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TUN_ID; } if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IN_PORT)) { - uint32_t in_port = nl_attr_get_u32(attrs[OVS_KEY_ATTR_IN_PORT]); - if (in_port >= UINT16_MAX || in_port >= OFPP_MAX) { - VLOG_ERR_RL(&rl, "in_port %"PRIu32" out of supported range", - in_port); - return ODP_FIT_ERROR; - } - flow->in_port = odp_port_to_ofp_port(in_port); + flow->in_port = nl_attr_get_u32(attrs[OVS_KEY_ATTR_IN_PORT]); expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IN_PORT; } else { - flow->in_port = OFPP_NONE; + flow->in_port = OVSP_NONE; } /* Ethernet header. */ @@ -1726,7 +1928,7 @@ odp_key_fitness_to_string(enum odp_key_fitness fitness) * the start of the cookie. (If 'cookie' is null, then the return value is not * meaningful.) */ size_t -odp_put_userspace_action(uint32_t pid, const struct user_action_cookie *cookie, +odp_put_userspace_action(uint32_t pid, const union user_action_cookie *cookie, struct ofpbuf *odp_actions) { size_t offset; @@ -1757,13 +1959,13 @@ static void commit_set_tun_id_action(const struct flow *flow, struct flow *base, struct ofpbuf *odp_actions) { - if (base->tun_id == flow->tun_id) { + if (base->tunnel.tun_id == flow->tunnel.tun_id) { return; } - base->tun_id = flow->tun_id; + base->tunnel.tun_id = flow->tunnel.tun_id; commit_set_action(odp_actions, OVS_KEY_ATTR_TUN_ID, - &base->tun_id, sizeof(base->tun_id)); + &base->tunnel.tun_id, sizeof(base->tunnel.tun_id)); } static void @@ -1885,7 +2087,7 @@ static void commit_set_port_action(const struct flow *flow, struct flow *base, struct ofpbuf *odp_actions) { - if (!base->tp_src || !base->tp_dst) { + if (!base->tp_src && !base->tp_dst) { return; }