diff options
| author | Ben Pfaff <blp@nicira.com> | 2011-01-23 18:44:44 -0800 |
|---|---|---|
| committer | Ben Pfaff <blp@nicira.com> | 2011-01-27 21:08:35 -0800 |
| commit | 36956a7d33c9ee204fcb184484a5aaacbd9ecef8 (patch) | |
| tree | f62a4d17e6941697bf59520a2068176d1ea3b904 /lib/odp-util.c | |
| parent | 704a1e09e9b31ea39ca41c028c7c6aaf2482283a (diff) | |
| download | openvswitch-36956a7d33c9ee204fcb184484a5aaacbd9ecef8.tar.gz | |
datapath: Convert odp_flow_key to use Netlink attributes instead.
One of the goals for Open vSwitch is to decouple kernel and userspace
software, so that either one can be upgraded or rolled back independent of
the other. To do this in full generality, it must be possible to change
the kernel's idea of the flow key separately from the userspace version.
In turn, that means that flow keys must become variable-length. This
commit makes that change using Netlink attribute sequences.
This commit does not actually make userspace flexible enough to handle
changes in the kernel flow key structure, because userspace doesn't yet
have enough information to do that intelligently. Upcoming commits will
fix that.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Jesse Gross <jesse@nicira.com>
Diffstat (limited to 'lib/odp-util.c')
| -rw-r--r-- | lib/odp-util.c | 462 |
1 files changed, 413 insertions, 49 deletions
diff --git a/lib/odp-util.c b/lib/odp-util.c index dc6843fb5..193f97be0 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -16,6 +16,7 @@ #include <config.h> #include "odp-util.h" +#include <errno.h> #include <inttypes.h> #include <stdlib.h> #include <string.h> @@ -29,26 +30,6 @@ #include "timeval.h" #include "util.h" -void -format_odp_flow_key(struct ds *ds, const struct odp_flow_key *key) -{ - ds_put_format(ds, "tun_id%#"PRIx64" in_port%d tci(", - ntohll(key->tun_id), key->in_port); - if (key->dl_tci) { - ds_put_format(ds, "vlan%"PRIu16",pcp%d", - vlan_tci_to_vid(key->dl_tci), - vlan_tci_to_pcp(key->dl_tci)); - } else { - ds_put_char(ds, '0'); - } - ds_put_format(ds, ") mac"ETH_ADDR_FMT"->"ETH_ADDR_FMT" type%04x " - "proto%"PRId8" tos%"PRIu8" ip"IP_FMT"->"IP_FMT" port%d->%d", - ETH_ADDR_ARGS(key->dl_src), ETH_ADDR_ARGS(key->dl_dst), - ntohs(key->dl_type), key->nw_proto, key->nw_tos, - IP_ARGS(&key->nw_src), IP_ARGS(&key->nw_dst), - ntohs(key->tp_src), ntohs(key->tp_dst)); -} - int odp_action_len(uint16_t type) { @@ -190,7 +171,7 @@ format_odp_actions(struct ds *ds, const struct nlattr *actions, if (left == actions_len) { ds_put_cstr(ds, "<empty>"); } - ds_put_format(ds, " ***%u leftover bytes***", left); + ds_put_format(ds, ",***%u leftover bytes***", left); } } else { ds_put_cstr(ds, "drop"); @@ -214,7 +195,7 @@ format_odp_flow_stats(struct ds *ds, const struct odp_flow_stats *s) void format_odp_flow(struct ds *ds, const struct odp_flow *f) { - format_odp_flow_key(ds, &f->key); + odp_flow_key_format(f->key, f->key_len, ds); ds_put_cstr(ds, ", "); format_odp_flow_stats(ds, &f->stats); ds_put_cstr(ds, ", actions:"); @@ -250,37 +231,420 @@ format_odp_port_type(struct ds *ds, const struct odp_port *p) } } +/* Returns the correct length of the payload for a flow key attribute of the + * specified 'type', or -1 if 'type' is unknown. */ +static int +odp_flow_key_attr_len(uint16_t type) +{ + if (type > ODP_KEY_ATTR_MAX) { + return -1; + } + + switch ((enum odp_key_type) type) { + case ODP_KEY_ATTR_TUN_ID: return 8; + case ODP_KEY_ATTR_IN_PORT: return 4; + case ODP_KEY_ATTR_ETHERNET: return sizeof(struct odp_key_ethernet); + case ODP_KEY_ATTR_8021Q: return sizeof(struct odp_key_8021q); + case ODP_KEY_ATTR_ETHERTYPE: return 2; + case ODP_KEY_ATTR_IPV4: return sizeof(struct odp_key_ipv4); + case ODP_KEY_ATTR_TCP: return sizeof(struct odp_key_tcp); + case ODP_KEY_ATTR_UDP: return sizeof(struct odp_key_udp); + case ODP_KEY_ATTR_ICMP: return sizeof(struct odp_key_icmp); + case ODP_KEY_ATTR_ARP: return sizeof(struct odp_key_arp); + + case ODP_KEY_ATTR_UNSPEC: + case __ODP_KEY_ATTR_MAX: + return -1; + } + + return -1; +} + + +static void +format_generic_odp_key(const struct nlattr *a, struct ds *ds) +{ + size_t len = nl_attr_get_size(a); + + ds_put_format(ds, "key%"PRId16, nl_attr_type(a)); + if (len) { + const uint8_t *unspec; + unsigned int i; + + unspec = nl_attr_get(a); + for (i = 0; i < len; i++) { + ds_put_char(ds, i ? ' ': '('); + ds_put_format(ds, "%02x", unspec[i]); + } + ds_put_char(ds, ')'); + } +} + +static void +format_odp_key_attr(const struct nlattr *a, struct ds *ds) +{ + const struct odp_key_ethernet *eth_key; + const struct odp_key_8021q *q_key; + const struct odp_key_ipv4 *ipv4_key; + const struct odp_key_tcp *tcp_key; + const struct odp_key_udp *udp_key; + const struct odp_key_icmp *icmp_key; + const struct odp_key_arp *arp_key; + + if (nl_attr_get_size(a) != odp_flow_key_attr_len(nl_attr_type(a))) { + ds_put_format(ds, "bad length %zu, expected %d for: ", + nl_attr_get_size(a), + odp_flow_key_attr_len(nl_attr_type(a))); + format_generic_odp_key(a, ds); + return; + } + + switch (nl_attr_type(a)) { + case ODP_KEY_ATTR_TUN_ID: + ds_put_format(ds, "tun_id(%#"PRIx64")", nl_attr_get_be64(a)); + break; + + case ODP_KEY_ATTR_IN_PORT: + ds_put_format(ds, "in_port(%"PRIu32")", nl_attr_get_u32(a)); + break; + + case ODP_KEY_ATTR_ETHERNET: + eth_key = nl_attr_get(a); + ds_put_format(ds, "eth(src="ETH_ADDR_FMT",dst="ETH_ADDR_FMT")", + ETH_ADDR_ARGS(eth_key->eth_src), + ETH_ADDR_ARGS(eth_key->eth_dst)); + break; + + case ODP_KEY_ATTR_8021Q: + q_key = nl_attr_get(a); + ds_put_cstr(ds, "vlan("); + if (q_key->q_tpid != htons(ETH_TYPE_VLAN)) { + ds_put_format(ds, "tpid=%#"PRIx16",", ntohs(q_key->q_tpid)); + } + ds_put_format(ds, "vid%"PRIu16",pcp%d)", + vlan_tci_to_vid(q_key->q_tci), + vlan_tci_to_pcp(q_key->q_tci)); + break; + + case ODP_KEY_ATTR_ETHERTYPE: + ds_put_format(ds, "eth_type(%#04"PRIx16")", + ntohs(nl_attr_get_be16(a))); + break; + + case ODP_KEY_ATTR_IPV4: + ipv4_key = nl_attr_get(a); + ds_put_format(ds, "ipv4(src="IP_FMT",dst="IP_FMT"," + "proto=%"PRId8",tos=%"PRIu8")", + IP_ARGS(&ipv4_key->ipv4_src), + IP_ARGS(&ipv4_key->ipv4_dst), + ipv4_key->ipv4_proto, ipv4_key->ipv4_tos); + break; + + case ODP_KEY_ATTR_TCP: + tcp_key = nl_attr_get(a); + ds_put_format(ds, "tcp(src=%"PRIu16",dst=%"PRIu16")", + ntohs(tcp_key->tcp_src), ntohs(tcp_key->tcp_dst)); + break; + + case ODP_KEY_ATTR_UDP: + udp_key = nl_attr_get(a); + ds_put_format(ds, "udp(src=%"PRIu16",dst=%"PRIu16")", + ntohs(udp_key->udp_src), ntohs(udp_key->udp_dst)); + break; + + case ODP_KEY_ATTR_ICMP: + icmp_key = nl_attr_get(a); + ds_put_format(ds, "icmp(type=%"PRIu8",code=%"PRIu8")", + icmp_key->icmp_type, icmp_key->icmp_code); + break; + + case ODP_KEY_ATTR_ARP: + arp_key = nl_attr_get(a); + ds_put_format(ds, "arp(sip="IP_FMT",tip="IP_FMT",op=%"PRIu16")", + IP_ARGS(&arp_key->arp_sip), IP_ARGS(&arp_key->arp_tip), + ntohs(arp_key->arp_op)); + break; + + default: + format_generic_odp_key(a, ds); + break; + } +} + +/* Appends to 'ds' a string representation of the 'key_len' bytes of + * ODP_KEY_ATTR_* attributes in 'key'. */ void -odp_flow_key_from_flow(struct odp_flow_key *key, const struct flow *flow) +odp_flow_key_format(const struct nlattr *key, size_t key_len, struct ds *ds) { - key->tun_id = flow->tun_id; - key->nw_src = flow->nw_src; - key->nw_dst = flow->nw_dst; - key->in_port = flow->in_port; - key->dl_tci = flow->vlan_tci; - key->dl_type = flow->dl_type; - key->tp_src = flow->tp_src; - key->tp_dst = flow->tp_dst; - memcpy(key->dl_src, flow->dl_src, ETH_ADDR_LEN); - memcpy(key->dl_dst, flow->dl_dst, ETH_ADDR_LEN); - key->nw_proto = flow->nw_proto; - key->nw_tos = flow->nw_tos; + if (key_len) { + const struct nlattr *a; + unsigned int left; + + NL_ATTR_FOR_EACH (a, left, key, key_len) { + if (a != key) { + ds_put_char(ds, ','); + } + format_odp_key_attr(a, ds); + } + if (left) { + if (left == key_len) { + ds_put_cstr(ds, "<empty>"); + } + ds_put_format(ds, ",***%u leftover bytes***", left); + } + } else { + ds_put_cstr(ds, "<empty>"); + } } +/* Appends a representation of 'flow' as ODP_KEY_ATTR_* attributes to 'buf'. */ void -odp_flow_key_to_flow(const struct odp_flow_key *key, struct flow *flow) +odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow) { - memset(flow->regs, 0, sizeof flow->regs); - flow->tun_id = key->tun_id; - flow->nw_src = key->nw_src; - flow->nw_dst = key->nw_dst; - flow->in_port = key->in_port; - flow->vlan_tci = key->dl_tci; - flow->dl_type = key->dl_type; - flow->tp_src = key->tp_src; - flow->tp_dst = key->tp_dst; - memcpy(flow->dl_src, key->dl_src, ETH_ADDR_LEN); - memcpy(flow->dl_dst, key->dl_dst, ETH_ADDR_LEN); - flow->nw_proto = key->nw_proto; - flow->nw_tos = key->nw_tos; + struct odp_key_ethernet *eth_key; + + if (flow->tun_id != htonll(0)) { + nl_msg_put_be64(buf, ODP_KEY_ATTR_TUN_ID, flow->tun_id); + } + + nl_msg_put_u32(buf, ODP_KEY_ATTR_IN_PORT, flow->in_port); + + eth_key = nl_msg_put_unspec_uninit(buf, ODP_KEY_ATTR_ETHERNET, + sizeof *eth_key); + memcpy(eth_key->eth_src, flow->dl_src, ETH_ADDR_LEN); + memcpy(eth_key->eth_dst, flow->dl_dst, ETH_ADDR_LEN); + + if (flow->vlan_tci != htons(0)) { + struct odp_key_8021q *q_key; + + q_key = nl_msg_put_unspec_uninit(buf, ODP_KEY_ATTR_8021Q, + sizeof *q_key); + q_key->q_tpid = htons(ETH_TYPE_VLAN); + q_key->q_tci = flow->vlan_tci & ~htons(VLAN_CFI); + } + + if (ntohs(flow->dl_type) < ETH_TYPE_MIN) { + return; + } + + nl_msg_put_be16(buf, ODP_KEY_ATTR_ETHERTYPE, flow->dl_type); + + if (flow->dl_type == htons(ETH_TYPE_IP)) { + struct odp_key_ipv4 *ipv4_key; + + ipv4_key = nl_msg_put_unspec_uninit(buf, ODP_KEY_ATTR_IPV4, + sizeof *ipv4_key); + ipv4_key->ipv4_src = flow->nw_src; + ipv4_key->ipv4_dst = flow->nw_dst; + ipv4_key->ipv4_proto = flow->nw_proto; + ipv4_key->ipv4_tos = flow->nw_tos; + + if (flow->nw_proto == IP_TYPE_TCP) { + struct odp_key_tcp *tcp_key; + + tcp_key = nl_msg_put_unspec_uninit(buf, ODP_KEY_ATTR_TCP, + sizeof *tcp_key); + tcp_key->tcp_src = flow->tp_src; + tcp_key->tcp_dst = flow->tp_dst; + } else if (flow->nw_proto == IP_TYPE_UDP) { + struct odp_key_udp *udp_key; + + udp_key = nl_msg_put_unspec_uninit(buf, ODP_KEY_ATTR_UDP, + sizeof *udp_key); + udp_key->udp_src = flow->tp_src; + udp_key->udp_dst = flow->tp_dst; + } else if (flow->nw_proto == IP_TYPE_ICMP) { + struct odp_key_icmp *icmp_key; + + icmp_key = nl_msg_put_unspec_uninit(buf, ODP_KEY_ATTR_ICMP, + sizeof *icmp_key); + icmp_key->icmp_type = ntohs(flow->tp_src); + icmp_key->icmp_code = ntohs(flow->tp_dst); + } + } else if (flow->dl_type == htons(ETH_TYPE_ARP)) { + struct odp_key_arp *arp_key; + + arp_key = nl_msg_put_unspec_uninit(buf, ODP_KEY_ATTR_ARP, + sizeof *arp_key); + arp_key->arp_sip = flow->nw_src; + arp_key->arp_tip = flow->nw_dst; + arp_key->arp_op = htons(flow->nw_proto); + } +} + +/* Converts the 'key_len' bytes of ODP_KEY_ATTR_* attributes in 'key' to a flow + * structure in 'flow'. Returns 0 if successful, otherwise EINVAL. */ +int +odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, + struct flow *flow) +{ + const struct nlattr *nla; + enum odp_key_type prev_type; + size_t left; + + memset(flow, 0, sizeof *flow); + flow->dl_type = htons(FLOW_DL_TYPE_NONE); + + prev_type = ODP_KEY_ATTR_UNSPEC; + NL_ATTR_FOR_EACH (nla, left, key, key_len) { + const struct odp_key_ethernet *eth_key; + const struct odp_key_8021q *q_key; + const struct odp_key_ipv4 *ipv4_key; + const struct odp_key_tcp *tcp_key; + const struct odp_key_udp *udp_key; + const struct odp_key_icmp *icmp_key; + const struct odp_key_arp *arp_key; + + uint16_t type = nl_attr_type(nla); + int len = odp_flow_key_attr_len(type); + + if (nl_attr_get_size(nla) != len && len != -1) { + return EINVAL; + } + +#define TRANSITION(PREV_TYPE, TYPE) (((PREV_TYPE) << 16) | (TYPE)) + switch (TRANSITION(prev_type, type)) { + case TRANSITION(ODP_KEY_ATTR_UNSPEC, ODP_KEY_ATTR_TUN_ID): + flow->tun_id = nl_attr_get_be64(nla); + break; + + case TRANSITION(ODP_KEY_ATTR_UNSPEC, ODP_KEY_ATTR_IN_PORT): + case TRANSITION(ODP_KEY_ATTR_TUN_ID, ODP_KEY_ATTR_IN_PORT): + if (nl_attr_get_u32(nla) >= UINT16_MAX) { + return EINVAL; + } + flow->in_port = nl_attr_get_u32(nla); + break; + + case TRANSITION(ODP_KEY_ATTR_IN_PORT, ODP_KEY_ATTR_ETHERNET): + eth_key = nl_attr_get(nla); + memcpy(flow->dl_src, eth_key->eth_src, ETH_ADDR_LEN); + memcpy(flow->dl_dst, eth_key->eth_dst, ETH_ADDR_LEN); + break; + + case TRANSITION(ODP_KEY_ATTR_ETHERNET, ODP_KEY_ATTR_8021Q): + q_key = nl_attr_get(nla); + if (q_key->q_tpid != htons(ETH_TYPE_VLAN)) { + /* Only standard 0x8100 VLANs currently supported. */ + return EINVAL; + } + if (q_key->q_tci & htons(VLAN_CFI)) { + return EINVAL; + } + flow->vlan_tci = q_key->q_tci | htons(VLAN_CFI); + break; + + case TRANSITION(ODP_KEY_ATTR_8021Q, ODP_KEY_ATTR_ETHERTYPE): + case TRANSITION(ODP_KEY_ATTR_ETHERNET, ODP_KEY_ATTR_ETHERTYPE): + flow->dl_type = nl_attr_get_be16(nla); + if (ntohs(flow->dl_type) < 1536) { + return EINVAL; + } + break; + + case TRANSITION(ODP_KEY_ATTR_ETHERTYPE, ODP_KEY_ATTR_IPV4): + if (flow->dl_type != htons(ETH_TYPE_IP)) { + return EINVAL; + } + ipv4_key = nl_attr_get(nla); + flow->nw_src = ipv4_key->ipv4_src; + flow->nw_dst = ipv4_key->ipv4_dst; + flow->nw_proto = ipv4_key->ipv4_proto; + flow->nw_tos = ipv4_key->ipv4_tos; + if (flow->nw_tos & IP_ECN_MASK) { + return EINVAL; + } + break; + + case TRANSITION(ODP_KEY_ATTR_IPV4, ODP_KEY_ATTR_TCP): + if (flow->nw_proto != IP_TYPE_TCP) { + return EINVAL; + } + tcp_key = nl_attr_get(nla); + flow->tp_src = tcp_key->tcp_src; + flow->tp_dst = tcp_key->tcp_dst; + break; + + case TRANSITION(ODP_KEY_ATTR_IPV4, ODP_KEY_ATTR_UDP): + if (flow->nw_proto != IP_TYPE_UDP) { + return EINVAL; + } + udp_key = nl_attr_get(nla); + flow->tp_src = udp_key->udp_src; + flow->tp_dst = udp_key->udp_dst; + break; + + case TRANSITION(ODP_KEY_ATTR_IPV4, ODP_KEY_ATTR_ICMP): + if (flow->nw_proto != IP_TYPE_ICMP) { + return EINVAL; + } + icmp_key = nl_attr_get(nla); + flow->tp_src = htons(icmp_key->icmp_type); + flow->tp_dst = htons(icmp_key->icmp_code); + break; + + case TRANSITION(ODP_KEY_ATTR_ETHERTYPE, ODP_KEY_ATTR_ARP): + if (flow->dl_type != htons(ETH_TYPE_ARP)) { + return EINVAL; + } + arp_key = nl_attr_get(nla); + flow->nw_src = arp_key->arp_sip; + flow->nw_dst = arp_key->arp_tip; + if (arp_key->arp_op & htons(0xff00)) { + return EINVAL; + } + flow->nw_proto = ntohs(arp_key->arp_op); + break; + + default: + if (type == ODP_KEY_ATTR_UNSPEC + || prev_type == ODP_KEY_ATTR_UNSPEC) { + return EINVAL; + } + return EINVAL; + } + + prev_type = type; + } + if (left) { + return EINVAL; + } + + switch (prev_type) { + case ODP_KEY_ATTR_UNSPEC: + return EINVAL; + + case ODP_KEY_ATTR_TUN_ID: + case ODP_KEY_ATTR_IN_PORT: + return EINVAL; + + case ODP_KEY_ATTR_ETHERNET: + case ODP_KEY_ATTR_8021Q: + return 0; + + case ODP_KEY_ATTR_ETHERTYPE: + if (flow->dl_type == htons(ETH_TYPE_IP) + || flow->dl_type == htons(ETH_TYPE_ARP)) { + return EINVAL; + } + return 0; + + case ODP_KEY_ATTR_IPV4: + if (flow->nw_proto == IP_TYPE_TCP + || flow->nw_proto == IP_TYPE_UDP + || flow->nw_proto == IP_TYPE_ICMP) { + return EINVAL; + } + return 0; + + case ODP_KEY_ATTR_TCP: + case ODP_KEY_ATTR_UDP: + case ODP_KEY_ATTR_ICMP: + case ODP_KEY_ATTR_ARP: + return 0; + + case __ODP_KEY_ATTR_MAX: + default: + NOT_REACHED(); + } } |
