From f393f81e425658cdfddf29ca23086bf66d6b733c Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Thu, 9 Dec 2010 11:03:35 -0800 Subject: [PATCH] ofp-print, ofp-parse: Add support for NXAST_REG_MOVE and NXAST_REG_LOAD. --- lib/nx-match.c | 183 +++++++++++++++++++++++++++++++++++++-- lib/nx-match.h | 7 ++ lib/ofp-parse.c | 9 ++ lib/ofp-print.c | 36 +++++++- tests/ovs-ofctl.at | 12 +-- utilities/ovs-ofctl.8.in | 23 +++++ 6 files changed, 256 insertions(+), 14 deletions(-) diff --git a/lib/nx-match.c b/lib/nx-match.c index 4821c62b4..bcb6482aa 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -662,6 +662,8 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr) /* nx_match_to_string() and helpers. */ +static void format_nxm_field_name(struct ds *, uint32_t header); + char * nx_match_to_string(const uint8_t *p, unsigned int match_len) { @@ -678,20 +680,13 @@ nx_match_to_string(const uint8_t *p, unsigned int match_len) unsigned int value_len = nxm_field_bytes(header); const uint8_t *value = p + 4; const uint8_t *mask = value + value_len; - const struct nxm_field *f; unsigned int i; if (s.length) { ds_put_cstr(&s, ", "); } - f = nxm_field_lookup(header); - if (f) { - ds_put_cstr(&s, f->name); - } else { - ds_put_format(&s, "%d:%d", NXM_VENDOR(header), NXM_FIELD(header)); - } - + format_nxm_field_name(&s, header); ds_put_char(&s, '('); for (i = 0; i < value_len; i++) { @@ -720,6 +715,17 @@ nx_match_to_string(const uint8_t *p, unsigned int match_len) return ds_steal_cstr(&s); } +static void +format_nxm_field_name(struct ds *s, uint32_t header) +{ + const struct nxm_field *f = nxm_field_lookup(header); + if (f) { + ds_put_cstr(s, f->name); + } else { + ds_put_format(s, "%d:%d", NXM_VENDOR(header), NXM_FIELD(header)); + } +} + static uint32_t parse_nxm_field_name(const char *name, int name_len) { @@ -820,6 +826,167 @@ nx_match_from_string(const char *s, struct ofpbuf *b) return match_len; } +static const char * +parse_nxm_field_bits(const char *s, uint32_t *headerp, int *ofsp, int *n_bitsp) +{ + const char *full_s = s; + const char *name; + uint32_t header; + int start, end; + int name_len; + int width; + + name = s; + name_len = strcspn(s, "["); + if (s[name_len] != '[') { + ovs_fatal(0, "%s: missing [ looking for field name", full_s); + } + + header = parse_nxm_field_name(name, name_len); + if (!header) { + ovs_fatal(0, "%s: unknown field `%.*s'", full_s, name_len, s); + } + width = nxm_field_bits(header); + + s += name_len; + if (sscanf(s, "[%d..%d]", &start, &end) == 2) { + /* Nothing to do. */ + } else if (sscanf(s, "[%d]", &start) == 1) { + end = start; + } else if (!strncmp(s, "[]", 2)) { + start = 0; + end = width - 1; + } else { + ovs_fatal(0, "%s: syntax error expecting [] or [] or " + "[..]", full_s); + } + s = strchr(s, ']') + 1; + + if (start > end) { + ovs_fatal(0, "%s: starting bit %d is after ending bit %d", + full_s, start, end); + } else if (start >= width) { + ovs_fatal(0, "%s: starting bit %d is not valid because field is only " + "%d bits wide", full_s, start, width); + } else if (end >= width){ + ovs_fatal(0, "%s: ending bit %d is not valid because field is only " + "%d bits wide", full_s, end, width); + } + + *headerp = header; + *ofsp = start; + *n_bitsp = end - start + 1; + + return s; +} + +void +nxm_parse_reg_move(struct nx_action_reg_move *move, const char *s) +{ + const char *full_s = s; + uint32_t src, dst; + int src_ofs, dst_ofs; + int src_n_bits, dst_n_bits; + + s = parse_nxm_field_bits(s, &src, &src_ofs, &src_n_bits); + if (strncmp(s, "->", 2)) { + ovs_fatal(0, "%s: missing `->' following source", full_s); + } + s += 2; + s = parse_nxm_field_bits(s, &dst, &dst_ofs, &dst_n_bits); + if (*s != '\0') { + ovs_fatal(0, "%s: trailing garbage following destination", full_s); + } + + if (src_n_bits != dst_n_bits) { + ovs_fatal(0, "%s: source field is %d bits wide but destination is " + "%d bits wide", full_s, src_n_bits, dst_n_bits); + } + + move->type = htons(OFPAT_VENDOR); + move->len = htons(sizeof *move); + move->vendor = htonl(NX_VENDOR_ID); + move->subtype = htons(NXAST_REG_MOVE); + move->n_bits = htons(src_n_bits); + move->src_ofs = htons(src_ofs); + move->dst_ofs = htons(dst_ofs); + move->src = htonl(src); + move->dst = htonl(dst); +} + +void +nxm_parse_reg_load(struct nx_action_reg_load *load, const char *s) +{ + const char *full_s = s; + uint32_t dst; + int ofs, n_bits; + uint64_t value; + + value = strtoull(s, (char **) &s, 0); + if (strncmp(s, "->", 2)) { + ovs_fatal(0, "%s: missing `->' following value", full_s); + } + s += 2; + s = parse_nxm_field_bits(s, &dst, &ofs, &n_bits); + if (*s != '\0') { + ovs_fatal(0, "%s: trailing garbage following destination", full_s); + } + + if (n_bits < 64 && (value >> n_bits) != 0) { + ovs_fatal(0, "%s: value %llu does not fit into %d bits", + full_s, value, n_bits); + } + + load->type = htons(OFPAT_VENDOR); + load->len = htons(sizeof *load); + load->vendor = htonl(NX_VENDOR_ID); + load->subtype = htons(NXAST_REG_LOAD); + load->ofs_nbits = htons((ofs << 6) | (n_bits - 1)); + load->dst = htonl(dst); + load->value = htonll(value); +} + +/* nxm_format_reg_move(), nxm_format_reg_load(). */ + +static void +format_nxm_field_bits(struct ds *s, uint32_t header, int ofs, int n_bits) +{ + format_nxm_field_name(s, header); + if (n_bits != 1) { + ds_put_format(s, "[%d..%d]", ofs, ofs + n_bits - 1); + } else { + ds_put_format(s, "[%d]", ofs); + } +} + +void +nxm_format_reg_move(const struct nx_action_reg_move *move, struct ds *s) +{ + int n_bits = ntohs(move->n_bits); + int src_ofs = ntohs(move->src_ofs); + int dst_ofs = ntohs(move->dst_ofs); + uint32_t src = ntohl(move->src); + uint32_t dst = ntohl(move->dst); + + ds_put_format(s, "move:"); + format_nxm_field_bits(s, src, src_ofs, n_bits); + ds_put_cstr(s, "->"); + format_nxm_field_bits(s, dst, dst_ofs, n_bits); +} + +void +nxm_format_reg_load(const struct nx_action_reg_load *load, struct ds *s) +{ + uint16_t ofs_nbits = ntohs(load->ofs_nbits); + int ofs = ofs_nbits >> 6; + int n_bits = (ofs_nbits & 0x3f) + 1; + uint32_t dst = ntohl(load->dst); + uint64_t value = ntohll(load->value); + + ds_put_format(s, "load:%"PRIu64"->", value); + format_nxm_field_bits(s, dst, ofs, n_bits); +} + /* nxm_check_reg_move(), nxm_check_reg_load(). */ static bool diff --git a/lib/nx-match.h b/lib/nx-match.h index ba57f8139..c790333d1 100644 --- a/lib/nx-match.h +++ b/lib/nx-match.h @@ -20,6 +20,7 @@ #include struct cls_rule; +struct ds; struct flow; struct ofpbuf; struct nx_action_reg_load; @@ -37,6 +38,12 @@ int nx_put_match(struct ofpbuf *, const struct cls_rule *); char *nx_match_to_string(const uint8_t *, unsigned int match_len); int nx_match_from_string(const char *, struct ofpbuf *); +void nxm_parse_reg_move(struct nx_action_reg_move *, const char *); +void nxm_parse_reg_load(struct nx_action_reg_load *, const char *); + +void nxm_format_reg_move(const struct nx_action_reg_move *, struct ds *); +void nxm_format_reg_load(const struct nx_action_reg_load *, struct ds *); + int nxm_check_reg_move(const struct nx_action_reg_move *, const struct flow *); int nxm_check_reg_load(const struct nx_action_reg_load *, const struct flow *); diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c index 1ccbcd02c..1c7ce4fcf 100644 --- a/lib/ofp-parse.c +++ b/lib/ofp-parse.c @@ -25,6 +25,7 @@ #include "byte-order.h" #include "dynamic-string.h" #include "netdev.h" +#include "nx-match.h" #include "ofp-util.h" #include "ofpbuf.h" #include "openflow/openflow.h" @@ -315,6 +316,14 @@ str_to_action(char *str, struct ofpbuf *b) ofpbuf_put_zeros(b, OFP_ACTION_ALIGN - remainder); } nan->len = htons(b->size - start_ofs); + } else if (!strcasecmp(act, "move")) { + struct nx_action_reg_move *move; + move = ofpbuf_put_uninit(b, sizeof *move); + nxm_parse_reg_move(move, arg); + } else if (!strcasecmp(act, "load")) { + struct nx_action_reg_load *load; + load = ofpbuf_put_uninit(b, sizeof *load); + nxm_parse_reg_load(load, arg); } else if (!strcasecmp(act, "output")) { put_output_action(b, str_to_u32(arg)); } else if (!strcasecmp(act, "enqueue")) { diff --git a/lib/ofp-print.c b/lib/ofp-print.c index 26d11489f..a49c3854c 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -184,6 +184,7 @@ static void ofp_print_port_name(struct ds *string, uint16_t port) ds_put_cstr(string, name); } + static void print_note(struct ds *string, const struct nx_action_note *nan) { @@ -200,15 +201,43 @@ print_note(struct ds *string, const struct nx_action_note *nan) } } +static int +nx_action_len(enum nx_action_subtype subtype) +{ + switch (subtype) { + case NXAST_SNAT__OBSOLETE: return -1; + case NXAST_RESUBMIT: return sizeof(struct nx_action_resubmit); + case NXAST_SET_TUNNEL: return sizeof(struct nx_action_set_tunnel); + case NXAST_DROP_SPOOFED_ARP: + return sizeof(struct nx_action_drop_spoofed_arp); + case NXAST_SET_QUEUE: return sizeof(struct nx_action_set_queue); + case NXAST_POP_QUEUE: return sizeof(struct nx_action_pop_queue); + case NXAST_REG_MOVE: return sizeof(struct nx_action_reg_move); + case NXAST_REG_LOAD: return sizeof(struct nx_action_reg_load); + case NXAST_NOTE: return -1; + default: return -1; + } +} + static void ofp_print_nx_action(struct ds *string, const struct nx_action_header *nah) { uint16_t subtype = ntohs(nah->subtype); + int required_len = nx_action_len(subtype); + int len = ntohs(nah->len); + + if (required_len != -1 && required_len != len) { + ds_put_format(string, "***Nicira action %"PRIu16" wrong length: %d***", + subtype, len); + return; + } if (subtype <= TYPE_MAXIMUM(enum nx_action_subtype)) { const struct nx_action_set_tunnel *nast; const struct nx_action_set_queue *nasq; const struct nx_action_resubmit *nar; + const struct nx_action_reg_move *move; + const struct nx_action_reg_load *load; switch ((enum nx_action_subtype) subtype) { case NXAST_RESUBMIT: @@ -240,8 +269,13 @@ ofp_print_nx_action(struct ds *string, const struct nx_action_header *nah) return; case NXAST_REG_MOVE: + move = (const struct nx_action_reg_move *) nah; + nxm_format_reg_move(move, string); + return; + case NXAST_REG_LOAD: - /* XXX */ + load = (const struct nx_action_reg_load *) nah; + nxm_format_reg_load(load, string); return; case NXAST_SNAT__OBSOLETE: diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at index 58e88113c..85d191d03 100644 --- a/tests/ovs-ofctl.at +++ b/tests/ovs-ofctl.at @@ -77,7 +77,7 @@ NXT_FLOW_MOD: ADD actions=drop AT_CLEANUP AT_SETUP([ovs-ofctl -F nxm -mmm parse-flows]) -AT_DATA([flows.txt], [ +AT_DATA([flows.txt], [[ # comment tcp,tp_src=123,actions=flood in_port=LOCAL dl_vlan=9 dl_src=00:0A:E4:25:6B:B0 actions=drop @@ -89,10 +89,11 @@ cookie=0x123456789abcdef hard_timeout=10 priority=60000 actions=controller actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note tun_id=0x1234,cookie=0x5678,actions=flood actions=drop -]) +reg0=123,actions=move:NXM_NX_REG0[0..5]->NXM_NX_REG1[26..31],load:55->NXM_NX_REG2[0..31],move:NXM_NX_REG0[0..31]->NXM_NX_TUN_ID[0..31],move:NXM_NX_REG0[0..15]->NXM_OF_VLAN_TCI[] +]]) AT_CHECK([ovs-ofctl -F nxm -mmm parse-flows flows.txt], [0], [stdout]) -AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0], [dnl -NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_SRC(007b) actions=FLOOD +AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0], +[[NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_SRC(007b) actions=FLOOD NXT_FLOW_MOD: ADD NXM_OF_IN_PORT(fffe), NXM_OF_ETH_SRC(000ae4256bb0), NXM_OF_VLAN_TCI_W(1009/1fff) actions=drop NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_SPA(c0a80001) actions=drop_spoofed_arp,NORMAL NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_VLAN_TCI_W(f000/f000), NXM_OF_IP_PROTO(11) idle:5 actions=strip_vlan,output:0 @@ -102,7 +103,8 @@ NXT_FLOW_MOD: ADD cookie:0x123456789abcdef hard:10 pri:60000 actions=CONTR NXT_FLOW_MOD: ADD actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00 NXT_FLOW_MOD: ADD NXM_NX_TUN_ID(0000000000001234) cookie:0x5678 actions=FLOOD NXT_FLOW_MOD: ADD actions=drop -]) +NXT_FLOW_MOD: ADD NXM_NX_REG0(0000007b) actions=move:NXM_NX_REG0[0..5]->NXM_NX_REG1[26..31],load:55->NXM_NX_REG2[0..31],move:NXM_NX_REG0[0..31]->NXM_NX_TUN_ID[0..31],move:NXM_NX_REG0[0..15]->NXM_OF_VLAN_TCI[0..15] +]]) AT_CLEANUP AT_SETUP([ovs-ofctl parse-nx-match]) diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in index bd2a37f74..30a7a4357 100644 --- a/utilities/ovs-ofctl.8.in +++ b/utilities/ovs-ofctl.8.in @@ -521,6 +521,29 @@ actions were applied. Does nothing at all. Any number of bytes represented as hex digits \fIhh\fR may be included. Pairs of hex digits may be separated by periods for readability. +. +.IP "\fBmove:\fIsrc\fB[\fIstart\fB..\fIend\fB]->\fIdst\fB[\fIstart\fB..\fIend\fB]\fR" +Copies the named bits from field \fIsrc\fR to field \fIdst\fR. +\fIsrc\fR and \fIdst\fR must be NXM field names as defined in +\fBnicira\-ext.h\fR, e.g. \fBNXM_OF_UDP_SRC\fR or \fBNXM_NX_REG0\fR. +Each \fIstart\fR and \fIend\fR pair, which are inclusive, must specify +the same number of bits and must fit within its respective field. +Shorthands for \fB[\fIstart\fB..\fIend\fB]\fR exist: use +\fB[\fIbit\fB]\fR to specify a single bit or \fB[]\fR to specify an +entire field. +.IP +Examples: \fBmove:NXM_NX_REG0[0..5]\->NXM_NX_REG1[26..31]\fR copies the +six bits numbered 0 through 5, inclusive, in register 0 into bits 26 +through 31, inclusive; +\fBmove:NXM_NX_REG0[0..15]->NXM_OF_VLAN_TCI[]\fR copies the least +significant 16 bits of register 0 into the VLAN TCI field. +. +.IP "\fBload:\fIvalue\fB\->\fIdst\fB[\fIstart\fB..\fIend\fB]" +Writes \fIvalue\fR to bits \fIstart\fR through \fIend\fR, inclusive, +in field \fBdst\fR. +.IP +Example: \fBload:55\->NXM_NX_REG2[0..5]\fR loads value 55 (bit pattern +\fB110111\fR) into bits 0 through 5, inclusive, in register 2. .RE . .IP -- 2.20.1