diff options
author | Ben Pfaff <blp@nicira.com> | 2014-07-28 10:58:09 -0700 |
---|---|---|
committer | Ben Pfaff <blp@nicira.com> | 2014-07-28 15:04:58 -0700 |
commit | bed0d7e7cb6d045dbc4522d441e43d0a0ea5023a (patch) | |
tree | 0f9acd152e2b9db7f019e31c26d32c849e21dc5f | |
parent | 63df4c505d7ec49acac1fe30764343af00c1655d (diff) | |
download | openvswitch-ext-320.tar.gz |
workext-320
31 files changed, 4140 insertions, 4382 deletions
diff --git a/build-aux/extract-ofp-actions b/build-aux/extract-ofp-actions new file mode 100755 index 000000000..880fdd82c --- /dev/null +++ b/build-aux/extract-ofp-actions @@ -0,0 +1,394 @@ +#! /usr/bin/python + +import sys +import os.path +import re + +macros = {} + +OFP_ACTION_ALIGN = 8 + +# Map from OpenFlow version number to version ID used in ofp_header. +version_map = {"1.0": 0x01, + "1.1": 0x02, + "1.2": 0x03, + "1.3": 0x04, + "1.4": 0x05, + "1.5": 0x06} +version_reverse_map = dict((v, k) for (k, v) in version_map.iteritems()) + +# Map from vendor name to the length of the action header. +vendor_map = {"OF": (0x00000000, 4), + "NX": (0x00002320, 10)} + +# Basic types used in action arguments. +types = {} +types['uint8_t'] = {"size": 1, "alignment": 1, "ntoh": None} +types['ovs_be16'] = {"size": 2, "alignment": 2, "ntoh": "ntohs"} +types['ovs_be32'] = {"size": 4, "alignment": 4, "ntoh": "ntohl"} +types['ovs_be64'] = {"size": 8, "alignment": 8, "ntoh": "ntohll"} +types['uint16_t'] = {"size": 2, "alignment": 2, "ntoh": None} +types['uint32_t'] = {"size": 4, "alignment": 4, "ntoh": None} +types['uint64_t'] = {"size": 8, "alignment": 8, "ntoh": None} + +token = None +line = "" +idRe = "[a-zA-Z_][a-zA-Z_0-9]*" +tokenRe = "#?" + idRe + "|[0-9]+|." +inComment = False +inDirective = False + +arg_structs = set() + +def round_up(x, y): + return (x + (y - 1)) / y * y + +def open_file(fn): + global fileName + global inputFile + global lineNumber + fileName = fn + inputFile = open(fileName) + lineNumber = 0 + +def tryGetLine(): + global inputFile + global line + global lineNumber + line = inputFile.readline() + lineNumber += 1 + return line != "" + +def getLine(): + if not tryGetLine(): + fatal("unexpected end of input") + +def getToken(): + global token + global line + global inComment + global inDirective + while True: + line = line.lstrip() + if line != "": + if line.startswith("/*"): + inComment = True + line = line[2:] + elif inComment: + commentEnd = line.find("*/") + if commentEnd < 0: + line = "" + else: + inComment = False + line = line[commentEnd + 2:] + else: + match = re.match(tokenRe, line) + token = match.group(0) + line = line[len(token):] + if token.startswith('#'): + inDirective = True + elif token in macros and not inDirective: + line = macros[token] + line + continue + return True + elif inDirective: + token = "$" + inDirective = False + return True + else: + global lineNumber + line = inputFile.readline() + lineNumber += 1 + while line.endswith("\\\n"): + line = line[:-2] + inputFile.readline() + lineNumber += 1 + if line == "": + if token == None: + fatal("unexpected end of input") + token = None + return False + +n_errors = 0 +def error(msg): + global n_errors + sys.stderr.write("%s:%d: %s\n" % (fileName, lineNumber, msg)) + n_errors += 1 + +def fatal(msg): + error(msg) + sys.exit(1) + +def skipDirective(): + getToken() + while token != '$': + getToken() + +def isId(s): + return re.match(idRe + "$", s) != None + +def forceId(): + if not isId(token): + fatal("identifier expected") + +def forceInteger(): + if not re.match('[0-9]+$', token): + fatal("integer expected") + +def match(t): + if token == t: + getToken() + return True + else: + return False + +def forceMatch(t): + if not match(t): + fatal("%s expected" % t) + +def parseTaggedName(): + assert token in ('struct', 'union') + name = token + getToken() + forceId() + name = "%s %s" % (name, token) + getToken() + return name + +def usage(): + argv0 = os.path.basename(sys.argv[0]) + print ('''\ +%(argv0)s, for extracting OpenFlow action data +usage: %(argv0)s OFP_ACTIONS.C + +This program reads ofp-actions.c to obtain information about OpenFlow +actions. It outputs a C source file to be #included into ofp-actions.c + +OFP_ACTIONS.C should point to lib/ofp-actions.c. +The output is suitable for use as lib/ofp-actions.inc.\ +''' % {"argv0": argv0}) + sys.exit(0) + +def extract_ofp_actions(fn): + error_types = {} + + comments = [] + names = [] + domain = {} + for code, size in vendor_map.values(): + domain[code] = {} + enums = {} + + n_errors = 0 + + open_file(fn) + + while True: + getLine() + if re.match('enum ofp_raw_action_type {', line): + break + + while True: + getLine() + if line.startswith('/*') or not line or line.isspace(): + continue + elif re.match('}', line): + break + + if not line.lstrip().startswith('/*'): + fatal("unexpected syntax between actions") + + comment = line.lstrip()[2:].strip() + while not comment.endswith('*/'): + getLine() + if line.startswith('/*') or not line or line.isspace(): + fatal("unexpected syntax within action") + comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n') + comment = re.sub('\[[^]]*\]', '', comment) + comment = comment[:-2].rstrip() + + m = re.match('([^:]+):\s+(.*)$', comment) + if not m: + fatal("unexpected syntax between actions") + + dsts = m.group(1) + argtype = m.group(2).strip().replace('.', '', 1) + + getLine() + m = re.match(r'\s+(([A-Z]+)_RAW([0-9]*)_([A-Z0-9_]+)),?', line) + if not m: + fatal("syntax error expecting enum value") + + enum = m.group(1) + if enum in names: + fatal("%s specified twice" % enum) + + names.append(enum) + + for dst in dsts.split(', '): + m = re.match(r'([A-Z]+)([0-9.]+)(\+|-[0-9.]+)?(?:\((\d+)\))(?: is deprecated \(([^)]+)\))?$', dst) + if not m: + fatal("%r: syntax error in destination" % dst) + vendor_name = m.group(1) + version1_name = m.group(2) + version2_name = m.group(3) + type_ = int(m.group(4)) + explanation = m.group(5) + + if vendor_name not in vendor_map: + fatal("%s: unknown vendor" % vendor_name) + vendor = vendor_map[vendor_name][0] + + if version1_name not in version_map: + fatal("%s: unknown OpenFlow version" % version1_name) + v1 = version_map[version1_name] + + if version2_name is None: + v2 = v1 + elif version2_name == "+": + v2 = max(version_map.values()) + elif version2_name[1:] not in version_map: + fatal("%s: unknown OpenFlow version" % version2_name[1:]) + else: + v2 = version_map[version2_name[1:]] + + if v2 < v1: + fatal("%s%s: %s precedes %s" + % (version1_name, version2_name, + version2_name, version1_name)) + + for version in range(v1, v2 + 1): + domain[vendor].setdefault(type_, {}) + if version in domain[vendor][type_]: + msg = "%#x,%d in OF%s means both %s and %s" % ( + vendor, type_, version_reverse_map[version], + domain[vendor][type_][version][0], enum) + error("%s: %s." % (dst, msg)) + sys.stderr.write("%s:%d: %s: Here is the location " + "of the previous definition.\n" + % (domain[vendor][type_][version][2], + domain[vendor][type_][version][3], + dst)) + n_errors += 1 + else: + header_len = vendor_map[vendor_name][1] + + base_argtype = argtype.replace(', ..', '', 1) + if base_argtype in types: + arg_align = types[base_argtype]['alignment'] + arg_size = types[base_argtype]['size'] + arg_ofs = round_up(header_len, arg_align) + min_length = round_up(arg_ofs + arg_size, + OFP_ACTION_ALIGN) + elif base_argtype == 'void': + min_length = round_up(header_len, OFP_ACTION_ALIGN) + arg_size = 0 + arg_ofs = 0 + elif re.match(r'struct [a-zA-Z0-9_]+$', base_argtype): + min_length = 'sizeof(%s)' % base_argtype + arg_structs.add(base_argtype) + arg_size = 0 + arg_ofs = 0 + # should also emit OFP_ACTION_ALIGN assertion + else: + fatal("bad argument type %s" % argtype) + + ellipsis = argtype != base_argtype + if ellipsis: + max_length = '65536 - OFP_ACTION_ALIGN' + else: + max_length = min_length + + info = (enum, explanation, fileName, lineNumber, min_length, max_length, arg_ofs, arg_size, base_argtype, version, type_) + domain[vendor][type_][version] = info + + enums.setdefault(enum, []) + enums[enum].append(info) + + inputFile.close() + + if n_errors: + sys.exit(1) + + print """\ +/* Generated automatically; do not modify! -*- buffer-read-only: t -*- */ +""" + + print "/* Verify that structs used as actions are reasonable sizes. */" + for s in sorted(arg_structs): + print "BUILD_ASSERT_DECL(sizeof(%s) %% OFP_ACTION_ALIGN == 0);" % s + + print "\nstatic struct ofpact_raw_instance all_raw_instances[] = {" + for vendor in domain: + for type_ in domain[vendor]: + for version in domain[vendor][type_]: + d = domain[vendor][type_][version] + print " { { 0x%08x, %2d, 0x%02x }, " % ( + vendor, type_, version) + print " %s," % d[0] + print " HMAP_NODE_NULL_INITIALIZER," + print " HMAP_NODE_NULL_INITIALIZER," + print " %s," % d[4] + print " %s," % d[5] + print " %s," % d[6] + print " %s," % d[7] + print " \"%s\"," % re.sub('_RAW[0-9]*', '', d[0], 1) + if d[1]: + print " \"%s\"," % re.sub(r'(["\\])', r'\\\1', d[1]) + else: + print " NULL," + print " }," + print "};"; + + for versions in enums.values(): + need_ofp_version = False + for v in versions: + assert v[7] == versions[0][7] + assert v[8] == versions[0][8] + if (v[4] != versions[0][4] or v[6] != versions[0][6] or + v[10] != versions[0][10]): + need_ofp_version = True + base_argtype = versions[0][8] + + prototype = "static inline " + if base_argtype.startswith('struct'): + prototype += "%s *" %base_argtype + else: + prototype += "void" + prototype += "\nput_%s(struct ofpbuf *openflow" % versions[0][0].replace('_RAW', '', 1) + if need_ofp_version: + prototype += ", enum ofp_version version" + if base_argtype != 'void' and not base_argtype.startswith('struct'): + prototype += ", %s arg" % base_argtype + prototype += ")\n" + prototype += "{\n" + prototype += " " + if base_argtype.startswith('struct'): + prototype += "return " + prototype += "ofpact_put_raw(openflow, " + if need_ofp_version: + prototype += "version" + else: + prototype += "%s" % versions[0][9] + prototype += ", %s, " % versions[0][0] + if base_argtype.startswith('struct') or base_argtype == 'void': + prototype += "0" + else: + ntoh = types[base_argtype]['ntoh'] + if ntoh: + prototype += "%s(arg)" % ntoh + else: + prototype += "arg" + prototype += ");\n" + prototype += "}" + print prototype + print + +if __name__ == '__main__': + if '--help' in sys.argv: + usage() + elif len(sys.argv) != 2: + sys.stderr.write("exactly one non-option arguments required; " + "use --help for help\n") + sys.exit(1) + else: + extract_ofp_actions(sys.argv[1]) diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h index 3bd2b15ec..bbf3388fa 100644 --- a/include/openflow/nicira-ext.h +++ b/include/openflow/nicira-ext.h @@ -279,902 +279,6 @@ struct nx_async_config { }; OFP_ASSERT(sizeof(struct nx_async_config) == 24); -/* Nicira vendor flow actions. */ - -enum nx_action_subtype { - NXAST_SNAT__OBSOLETE, /* No longer used. */ - NXAST_RESUBMIT, /* struct nx_action_resubmit */ - NXAST_SET_TUNNEL, /* struct nx_action_set_tunnel */ - NXAST_DROP_SPOOFED_ARP__OBSOLETE, - NXAST_SET_QUEUE, /* struct nx_action_set_queue */ - NXAST_POP_QUEUE, /* struct nx_action_pop_queue */ - NXAST_REG_MOVE, /* struct nx_action_reg_move */ - NXAST_REG_LOAD, /* struct nx_action_reg_load */ - NXAST_NOTE, /* struct nx_action_note */ - NXAST_SET_TUNNEL64, /* struct nx_action_set_tunnel64 */ - NXAST_MULTIPATH, /* struct nx_action_multipath */ - NXAST_AUTOPATH__OBSOLETE, /* No longer used. */ - NXAST_BUNDLE, /* struct nx_action_bundle */ - NXAST_BUNDLE_LOAD, /* struct nx_action_bundle */ - NXAST_RESUBMIT_TABLE, /* struct nx_action_resubmit */ - NXAST_OUTPUT_REG, /* struct nx_action_output_reg */ - NXAST_LEARN, /* struct nx_action_learn */ - NXAST_EXIT, /* struct nx_action_header */ - NXAST_DEC_TTL, /* struct nx_action_header */ - NXAST_FIN_TIMEOUT, /* struct nx_action_fin_timeout */ - NXAST_CONTROLLER, /* struct nx_action_controller */ - NXAST_DEC_TTL_CNT_IDS, /* struct nx_action_cnt_ids */ - NXAST_WRITE_METADATA, /* struct nx_action_write_metadata */ - NXAST_PUSH_MPLS, /* struct nx_action_push_mpls */ - NXAST_POP_MPLS, /* struct nx_action_pop_mpls */ - NXAST_SET_MPLS_TTL, /* struct nx_action_ttl */ - NXAST_DEC_MPLS_TTL, /* struct nx_action_header */ - NXAST_STACK_PUSH, /* struct nx_action_stack */ - NXAST_STACK_POP, /* struct nx_action_stack */ - NXAST_SAMPLE, /* struct nx_action_sample */ - NXAST_SET_MPLS_LABEL, /* struct nx_action_ttl */ - NXAST_SET_MPLS_TC, /* struct nx_action_ttl */ -}; - -/* Header for Nicira-defined actions. */ -struct nx_action_header { - ovs_be16 type; /* OFPAT_VENDOR. */ - ovs_be16 len; /* Length is 16. */ - ovs_be32 vendor; /* NX_VENDOR_ID. */ - ovs_be16 subtype; /* NXAST_*. */ - uint8_t pad[6]; -}; -OFP_ASSERT(sizeof(struct nx_action_header) == 16); - -/* Action structures for NXAST_RESUBMIT and NXAST_RESUBMIT_TABLE. - * - * These actions search one of the switch's flow tables: - * - * - For NXAST_RESUBMIT_TABLE only, if the 'table' member is not 255, then - * it specifies the table to search. - * - * - Otherwise (for NXAST_RESUBMIT_TABLE with a 'table' of 255, or for - * NXAST_RESUBMIT regardless of 'table'), it searches the current flow - * table, that is, the OpenFlow flow table that contains the flow from - * which this action was obtained. If this action did not come from a - * flow table (e.g. it came from an OFPT_PACKET_OUT message), then table 0 - * is the current table. - * - * The flow table lookup uses a flow that may be slightly modified from the - * original lookup: - * - * - For NXAST_RESUBMIT, the 'in_port' member of struct nx_action_resubmit - * is used as the flow's in_port. - * - * - For NXAST_RESUBMIT_TABLE, if the 'in_port' member is not OFPP_IN_PORT, - * then its value is used as the flow's in_port. Otherwise, the original - * in_port is used. - * - * - If actions that modify the flow (e.g. OFPAT_SET_VLAN_VID) precede the - * resubmit action, then the flow is updated with the new values. - * - * Following the lookup, the original in_port is restored. - * - * If the modified flow matched in the flow table, then the corresponding - * actions are executed. Afterward, actions following the resubmit in the - * original set of actions, if any, are executed; any changes made to the - * packet (e.g. changes to VLAN) by secondary actions persist when those - * actions are executed, although the original in_port is restored. - * - * Resubmit actions may be used any number of times within a set of actions. - * - * Resubmit actions may nest to an implementation-defined depth. Beyond this - * implementation-defined depth, further resubmit actions are simply ignored. - * - * NXAST_RESUBMIT ignores 'table' and 'pad'. NXAST_RESUBMIT_TABLE requires - * 'pad' to be all-bits-zero. - * - * Open vSwitch 1.0.1 and earlier did not support recursion. Open vSwitch - * before 1.2.90 did not support NXAST_RESUBMIT_TABLE. - */ -struct nx_action_resubmit { - ovs_be16 type; /* OFPAT_VENDOR. */ - ovs_be16 len; /* Length is 16. */ - ovs_be32 vendor; /* NX_VENDOR_ID. */ - ovs_be16 subtype; /* NXAST_RESUBMIT. */ - ovs_be16 in_port; /* New in_port for checking flow table. */ - uint8_t table; /* NXAST_RESUBMIT_TABLE: table to use. */ - uint8_t pad[3]; -}; -OFP_ASSERT(sizeof(struct nx_action_resubmit) == 16); - -/* Action structure for NXAST_SET_TUNNEL. - * - * Sets the encapsulating tunnel ID to a 32-bit value. The most-significant 32 - * bits of the tunnel ID are set to 0. */ -struct nx_action_set_tunnel { - ovs_be16 type; /* OFPAT_VENDOR. */ - ovs_be16 len; /* Length is 16. */ - ovs_be32 vendor; /* NX_VENDOR_ID. */ - ovs_be16 subtype; /* NXAST_SET_TUNNEL. */ - uint8_t pad[2]; - ovs_be32 tun_id; /* Tunnel ID. */ -}; -OFP_ASSERT(sizeof(struct nx_action_set_tunnel) == 16); - -/* Action structure for NXAST_SET_TUNNEL64. - * - * Sets the encapsulating tunnel ID to a 64-bit value. */ -struct nx_action_set_tunnel64 { - ovs_be16 type; /* OFPAT_VENDOR. */ - ovs_be16 len; /* Length is 24. */ - ovs_be32 vendor; /* NX_VENDOR_ID. */ - ovs_be16 subtype; /* NXAST_SET_TUNNEL64. */ - uint8_t pad[6]; - ovs_be64 tun_id; /* Tunnel ID. */ -}; -OFP_ASSERT(sizeof(struct nx_action_set_tunnel64) == 24); - -/* Action structure for NXAST_SET_QUEUE. - * - * Set the queue that should be used when packets are output. This is similar - * to the OpenFlow OFPAT_ENQUEUE action, but does not take the output port as - * an argument. This allows the queue to be defined before the port is - * known. */ -struct nx_action_set_queue { - ovs_be16 type; /* OFPAT_VENDOR. */ - ovs_be16 len; /* Length is 16. */ - ovs_be32 vendor; /* NX_VENDOR_ID. */ - ovs_be16 subtype; /* NXAST_SET_QUEUE. */ - uint8_t pad[2]; - ovs_be32 queue_id; /* Where to enqueue packets. */ -}; -OFP_ASSERT(sizeof(struct nx_action_set_queue) == 16); - -/* Action structure for NXAST_POP_QUEUE. - * - * Restores the queue to the value it was before any NXAST_SET_QUEUE actions - * were used. Only the original queue can be restored this way; no stack is - * maintained. */ -struct nx_action_pop_queue { - ovs_be16 type; /* OFPAT_VENDOR. */ - ovs_be16 len; /* Length is 16. */ - ovs_be32 vendor; /* NX_VENDOR_ID. */ - ovs_be16 subtype; /* NXAST_POP_QUEUE. */ - uint8_t pad[6]; -}; -OFP_ASSERT(sizeof(struct nx_action_pop_queue) == 16); - -/* Action structure for NXAST_REG_MOVE. - * - * Copies src[src_ofs:src_ofs+n_bits] to dst[dst_ofs:dst_ofs+n_bits], where - * a[b:c] denotes the bits within 'a' numbered 'b' through 'c' (not including - * bit 'c'). Bit numbering starts at 0 for the least-significant bit, 1 for - * the next most significant bit, and so on. - * - * 'src' and 'dst' are nxm_header values with nxm_hasmask=0. (It doesn't make - * sense to use nxm_hasmask=1 because the action does not do any kind of - * matching; it uses the actual value of a field.) - * - * The following nxm_header values are potentially acceptable as 'src': - * - * - NXM_OF_IN_PORT - * - NXM_OF_ETH_DST - * - NXM_OF_ETH_SRC - * - NXM_OF_ETH_TYPE - * - NXM_OF_VLAN_TCI - * - NXM_OF_IP_TOS - * - NXM_OF_IP_PROTO - * - NXM_OF_IP_SRC - * - NXM_OF_IP_DST - * - NXM_OF_TCP_SRC - * - NXM_OF_TCP_DST - * - NXM_OF_UDP_SRC - * - NXM_OF_UDP_DST - * - NXM_OF_ICMP_TYPE - * - NXM_OF_ICMP_CODE - * - NXM_OF_ARP_OP - * - NXM_OF_ARP_SPA - * - NXM_OF_ARP_TPA - * - NXM_NX_TUN_ID - * - NXM_NX_ARP_SHA - * - NXM_NX_ARP_THA - * - NXM_NX_ICMPV6_TYPE - * - NXM_NX_ICMPV6_CODE - * - NXM_NX_ND_SLL - * - NXM_NX_ND_TLL - * - NXM_NX_REG(idx) for idx in the switch's accepted range. - * - NXM_NX_PKT_MARK - * - NXM_NX_TUN_IPV4_SRC - * - NXM_NX_TUN_IPV4_DST - * - * The following nxm_header values are potentially acceptable as 'dst': - * - * - NXM_OF_ETH_DST - * - NXM_OF_ETH_SRC - * - NXM_OF_IP_TOS - * - NXM_OF_IP_SRC - * - NXM_OF_IP_DST - * - NXM_OF_TCP_SRC - * - NXM_OF_TCP_DST - * - NXM_OF_UDP_SRC - * - NXM_OF_UDP_DST - * - NXM_NX_ARP_SHA - * - NXM_NX_ARP_THA - * - NXM_OF_ARP_OP - * - NXM_OF_ARP_SPA - * - NXM_OF_ARP_TPA - * Modifying any of the above fields changes the corresponding packet - * header. - * - * - NXM_OF_IN_PORT - * - * - NXM_NX_REG(idx) for idx in the switch's accepted range. - * - * - NXM_NX_PKT_MARK - * - * - NXM_OF_VLAN_TCI. Modifying this field's value has side effects on the - * packet's 802.1Q header. Setting a value with CFI=0 removes the 802.1Q - * header (if any), ignoring the other bits. Setting a value with CFI=1 - * adds or modifies the 802.1Q header appropriately, setting the TCI field - * to the field's new value (with the CFI bit masked out). - * - * - NXM_NX_TUN_ID, NXM_NX_TUN_IPV4_SRC, NXM_NX_TUN_IPV4_DST. Modifying - * any of these values modifies the corresponding tunnel header field used - * for the packet's next tunnel encapsulation, if allowed by the - * configuration of the output tunnel port. - * - * A given nxm_header value may be used as 'src' or 'dst' only on a flow whose - * nx_match satisfies its prerequisites. For example, NXM_OF_IP_TOS may be - * used only if the flow's nx_match includes an nxm_entry that specifies - * nxm_type=NXM_OF_ETH_TYPE, nxm_hasmask=0, and nxm_value=0x0800. - * - * The switch will reject actions for which src_ofs+n_bits is greater than the - * width of 'src' or dst_ofs+n_bits is greater than the width of 'dst' with - * error type OFPET_BAD_ACTION, code OFPBAC_BAD_ARGUMENT. - * - * This action behaves properly when 'src' overlaps with 'dst', that is, it - * behaves as if 'src' were copied out to a temporary buffer, then the - * temporary buffer copied to 'dst'. - */ -struct nx_action_reg_move { - ovs_be16 type; /* OFPAT_VENDOR. */ - ovs_be16 len; /* Length is 24. */ - ovs_be32 vendor; /* NX_VENDOR_ID. */ - ovs_be16 subtype; /* NXAST_REG_MOVE. */ - ovs_be16 n_bits; /* Number of bits. */ - ovs_be16 src_ofs; /* Starting bit offset in source. */ - ovs_be16 dst_ofs; /* Starting bit offset in destination. */ - ovs_be32 src; /* Source register. */ - ovs_be32 dst; /* Destination register. */ -}; -OFP_ASSERT(sizeof(struct nx_action_reg_move) == 24); - -/* Action structure for NXAST_REG_LOAD. - * - * Copies value[0:n_bits] to dst[ofs:ofs+n_bits], where a[b:c] denotes the bits - * within 'a' numbered 'b' through 'c' (not including bit 'c'). Bit numbering - * starts at 0 for the least-significant bit, 1 for the next most significant - * bit, and so on. - * - * 'dst' is an nxm_header with nxm_hasmask=0. See the documentation for - * NXAST_REG_MOVE, above, for the permitted fields and for the side effects of - * loading them. - * - * The 'ofs' and 'n_bits' fields are combined into a single 'ofs_nbits' field - * to avoid enlarging the structure by another 8 bytes. To allow 'n_bits' to - * take a value between 1 and 64 (inclusive) while taking up only 6 bits, it is - * also stored as one less than its true value: - * - * 15 6 5 0 - * +------------------------------+------------------+ - * | ofs | n_bits - 1 | - * +------------------------------+------------------+ - * - * The switch will reject actions for which ofs+n_bits is greater than the - * width of 'dst', or in which any bits in 'value' with value 2**n_bits or - * greater are set to 1, with error type OFPET_BAD_ACTION, code - * OFPBAC_BAD_ARGUMENT. - */ -struct nx_action_reg_load { - ovs_be16 type; /* OFPAT_VENDOR. */ - ovs_be16 len; /* Length is 24. */ - ovs_be32 vendor; /* NX_VENDOR_ID. */ - ovs_be16 subtype; /* NXAST_REG_LOAD. */ - ovs_be16 ofs_nbits; /* (ofs << 6) | (n_bits - 1). */ - ovs_be32 dst; /* Destination register. */ - ovs_be64 value; /* Immediate value. */ -}; -OFP_ASSERT(sizeof(struct nx_action_reg_load) == 24); - -/* Action structure for NXAST_STACK_PUSH and NXAST_STACK_POP. - * - * Pushes (or pops) field[offset: offset + n_bits] to (or from) - * top of the stack. - */ -struct nx_action_stack { - ovs_be16 type; /* OFPAT_VENDOR. */ - ovs_be16 len; /* Length is 16. */ - ovs_be32 vendor; /* NX_VENDOR_ID. */ - ovs_be16 subtype; /* NXAST_STACK_PUSH or NXAST_STACK_POP. */ - ovs_be16 offset; /* Bit offset into the field. */ - ovs_be32 field; /* The field used for push or pop. */ - ovs_be16 n_bits; /* (n_bits + 1) bits of the field. */ - uint8_t zero[6]; /* Reserved, must be zero. */ -}; -OFP_ASSERT(sizeof(struct nx_action_stack) == 24); - -/* Action structure for NXAST_NOTE. - * - * This action has no effect. It is variable length. The switch does not - * attempt to interpret the user-defined 'note' data in any way. A controller - * can use this action to attach arbitrary metadata to a flow. - * - * This action might go away in the future. - */ -struct nx_action_note { - ovs_be16 type; /* OFPAT_VENDOR. */ - ovs_be16 len; /* A multiple of 8, but at least 16. */ - ovs_be32 vendor; /* NX_VENDOR_ID. */ - ovs_be16 subtype; /* NXAST_NOTE. */ - uint8_t note[6]; /* Start of user-defined data. */ - /* Possibly followed by additional user-defined data. */ -}; -OFP_ASSERT(sizeof(struct nx_action_note) == 16); - -/* Action structure for NXAST_MULTIPATH. - * - * This action performs the following steps in sequence: - * - * 1. Hashes the fields designated by 'fields', one of NX_HASH_FIELDS_*. - * Refer to the definition of "enum nx_mp_fields" for details. - * - * The 'basis' value is used as a universal hash parameter, that is, - * different values of 'basis' yield different hash functions. The - * particular universal hash function used is implementation-defined. - * - * The hashed fields' values are drawn from the current state of the - * flow, including all modifications that have been made by actions up to - * this point. - * - * 2. Applies the multipath link choice algorithm specified by 'algorithm', - * one of NX_MP_ALG_*. Refer to the definition of "enum nx_mp_algorithm" - * for details. - * - * The output of the algorithm is 'link', an unsigned integer less than - * or equal to 'max_link'. - * - * Some algorithms use 'arg' as an additional argument. - * - * 3. Stores 'link' in dst[ofs:ofs+n_bits]. The format and semantics of - * 'dst' and 'ofs_nbits' are similar to those for the NXAST_REG_LOAD - * action. - * - * The switch will reject actions that have an unknown 'fields', or an unknown - * 'algorithm', or in which ofs+n_bits is greater than the width of 'dst', or - * in which 'max_link' is greater than or equal to 2**n_bits, with error type - * OFPET_BAD_ACTION, code OFPBAC_BAD_ARGUMENT. - */ -struct nx_action_multipath { - ovs_be16 type; /* OFPAT_VENDOR. */ - ovs_be16 len; /* Length is 32. */ - ovs_be32 vendor; /* NX_VENDOR_ID. */ - ovs_be16 subtype; /* NXAST_MULTIPATH. */ - - /* What fields to hash and how. */ - ovs_be16 fields; /* One of NX_HASH_FIELDS_*. */ - ovs_be16 basis; /* Universal hash parameter. */ - ovs_be16 pad0; - - /* Multipath link choice algorithm to apply to hash value. */ - ovs_be16 algorithm; /* One of NX_MP_ALG_*. */ - ovs_be16 max_link; /* Number of output links, minus 1. */ - ovs_be32 arg; /* Algorithm-specific argument. */ - ovs_be16 pad1; - - /* Where to store the result. */ - ovs_be16 ofs_nbits; /* (ofs << 6) | (n_bits - 1). */ - ovs_be32 dst; /* Destination. */ -}; -OFP_ASSERT(sizeof(struct nx_action_multipath) == 32); - -/* NXAST_MULTIPATH: Multipath link choice algorithm to apply. - * - * In the descriptions below, 'n_links' is max_link + 1. */ -enum nx_mp_algorithm { - /* link = hash(flow) % n_links. - * - * Redistributes all traffic when n_links changes. O(1) performance. See - * RFC 2992. - * - * Use UINT16_MAX for max_link to get a raw hash value. */ - NX_MP_ALG_MODULO_N, - - /* link = hash(flow) / (MAX_HASH / n_links). - * - * Redistributes between one-quarter and one-half of traffic when n_links - * changes. O(1) performance. See RFC 2992. - */ - NX_MP_ALG_HASH_THRESHOLD, - - /* for i in [0,n_links): - * weights[i] = hash(flow, i) - * link = { i such that weights[i] >= weights[j] for all j != i } - * - * Redistributes 1/n_links of traffic when n_links changes. O(n_links) - * performance. If n_links is greater than a threshold (currently 64, but - * subject to change), Open vSwitch will substitute another algorithm - * automatically. See RFC 2992. */ - NX_MP_ALG_HRW, /* Highest Random Weight. */ - - /* i = 0 - * repeat: - * i = i + 1 - * link = hash(flow, i) % arg - * while link > max_link - * - * Redistributes 1/n_links of traffic when n_links changes. O(1) - * performance when arg/max_link is bounded by a constant. - * - * Redistributes all traffic when arg changes. - * - * arg must be greater than max_link and for best performance should be no - * more than approximately max_link * 2. If arg is outside the acceptable - * range, Open vSwitch will automatically substitute the least power of 2 - * greater than max_link. - * - * This algorithm is specific to Open vSwitch. - */ - NX_MP_ALG_ITER_HASH /* Iterative Hash. */ -}; - -/* Action structure for NXAST_LEARN. - * - * This action adds or modifies a flow in an OpenFlow table, similar to - * OFPT_FLOW_MOD with OFPFC_MODIFY_STRICT as 'command'. The new flow has the - * specified idle timeout, hard timeout, priority, cookie, and flags. The new - * flow's match criteria and actions are built by applying each of the series - * of flow_mod_spec elements included as part of the action. - * - * A flow_mod_spec starts with a 16-bit header. A header that is all-bits-0 is - * a no-op used for padding the action as a whole to a multiple of 8 bytes in - * length. Otherwise, the flow_mod_spec can be thought of as copying 'n_bits' - * bits from a source to a destination. In this case, the header contains - * multiple fields: - * - * 15 14 13 12 11 10 0 - * +------+---+------+---------------------------------+ - * | 0 |src| dst | n_bits | - * +------+---+------+---------------------------------+ - * - * The meaning and format of a flow_mod_spec depends on 'src' and 'dst'. The - * following table summarizes the meaning of each possible combination. - * Details follow the table: - * - * src dst meaning - * --- --- ---------------------------------------------------------- - * 0 0 Add match criteria based on value in a field. - * 1 0 Add match criteria based on an immediate value. - * 0 1 Add NXAST_REG_LOAD action to copy field into a different field. - * 1 1 Add NXAST_REG_LOAD action to load immediate value into a field. - * 0 2 Add OFPAT_OUTPUT action to output to port from specified field. - * All other combinations are undefined and not allowed. - * - * The flow_mod_spec header is followed by a source specification and a - * destination specification. The format and meaning of the source - * specification depends on 'src': - * - * - If 'src' is 0, the source bits are taken from a field in the flow to - * which this action is attached. (This should be a wildcarded field. If - * its value is fully specified then the source bits being copied have - * constant values.) - * - * The source specification is an ovs_be32 'field' and an ovs_be16 'ofs'. - * 'field' is an nxm_header with nxm_hasmask=0, and 'ofs' the starting bit - * offset within that field. The source bits are field[ofs:ofs+n_bits-1]. - * 'field' and 'ofs' are subject to the same restrictions as the source - * field in NXAST_REG_MOVE. - * - * - If 'src' is 1, the source bits are a constant value. The source - * specification is (n_bits+15)/16*2 bytes long. Taking those bytes as a - * number in network order, the source bits are the 'n_bits' - * least-significant bits. The switch will report an error if other bits - * in the constant are nonzero. - * - * The flow_mod_spec destination specification, for 'dst' of 0 or 1, is an - * ovs_be32 'field' and an ovs_be16 'ofs'. 'field' is an nxm_header with - * nxm_hasmask=0 and 'ofs' is a starting bit offset within that field. The - * meaning of the flow_mod_spec depends on 'dst': - * - * - If 'dst' is 0, the flow_mod_spec specifies match criteria for the new - * flow. The new flow matches only if bits field[ofs:ofs+n_bits-1] in a - * packet equal the source bits. 'field' may be any nxm_header with - * nxm_hasmask=0 that is allowed in NXT_FLOW_MOD. - * - * Order is significant. Earlier flow_mod_specs must satisfy any - * prerequisites for matching fields specified later, by copying constant - * values into prerequisite fields. - * - * The switch will reject flow_mod_specs that do not satisfy NXM masking - * restrictions. - * - * - If 'dst' is 1, the flow_mod_spec specifies an NXAST_REG_LOAD action for - * the new flow. The new flow copies the source bits into - * field[ofs:ofs+n_bits-1]. Actions are executed in the same order as the - * flow_mod_specs. - * - * A single NXAST_REG_LOAD action writes no more than 64 bits, so n_bits - * greater than 64 yields multiple NXAST_REG_LOAD actions. - * - * The flow_mod_spec destination spec for 'dst' of 2 (when 'src' is 0) is - * empty. It has the following meaning: - * - * - The flow_mod_spec specifies an OFPAT_OUTPUT action for the new flow. - * The new flow outputs to the OpenFlow port specified by the source field. - * Of the special output ports with value OFPP_MAX or larger, OFPP_IN_PORT, - * OFPP_FLOOD, OFPP_LOCAL, and OFPP_ALL are supported. Other special ports - * may not be used. - * - * Resource Management - * ------------------- - * - * A switch has a finite amount of flow table space available for learning. - * When this space is exhausted, no new learning table entries will be learned - * until some existing flow table entries expire. The controller should be - * prepared to handle this by flooding (which can be implemented as a - * low-priority flow). - * - * If a learned flow matches a single TCP stream with a relatively long - * timeout, one may make the best of resource constraints by setting - * 'fin_idle_timeout' or 'fin_hard_timeout' (both measured in seconds), or - * both, to shorter timeouts. When either of these is specified as a nonzero - * value, OVS adds a NXAST_FIN_TIMEOUT action, with the specified timeouts, to - * the learned flow. - * - * Examples - * -------- - * - * The following examples give a prose description of the flow_mod_specs along - * with informal notation for how those would be represented and a hex dump of - * the bytes that would be required. - * - * These examples could work with various nx_action_learn parameters. Typical - * values would be idle_timeout=OFP_FLOW_PERMANENT, hard_timeout=60, - * priority=OFP_DEFAULT_PRIORITY, flags=0, table_id=10. - * - * 1. Learn input port based on the source MAC, with lookup into - * NXM_NX_REG1[16:31] by resubmit to in_port=99: - * - * Match on in_port=99: - * ovs_be16(src=1, dst=0, n_bits=16), 20 10 - * ovs_be16(99), 00 63 - * ovs_be32(NXM_OF_IN_PORT), ovs_be16(0) 00 00 00 02 00 00 - * - * Match Ethernet destination on Ethernet source from packet: - * ovs_be16(src=0, dst=0, n_bits=48), 00 30 - * ovs_be32(NXM_OF_ETH_SRC), ovs_be16(0) 00 00 04 06 00 00 - * ovs_be32(NXM_OF_ETH_DST), ovs_be16(0) 00 00 02 06 00 00 - * - * Set NXM_NX_REG1[16:31] to the packet's input port: - * ovs_be16(src=0, dst=1, n_bits=16), 08 10 - * ovs_be32(NXM_OF_IN_PORT), ovs_be16(0) 00 00 00 02 00 00 - * ovs_be32(NXM_NX_REG1), ovs_be16(16) 00 01 02 04 00 10 - * - * Given a packet that arrived on port A with Ethernet source address B, - * this would set up the flow "in_port=99, dl_dst=B, - * actions=load:A->NXM_NX_REG1[16..31]". - * - * In syntax accepted by ovs-ofctl, this action is: learn(in_port=99, - * NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], - * load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31]) - * - * 2. Output to input port based on the source MAC and VLAN VID, with lookup - * into NXM_NX_REG1[16:31]: - * - * Match on same VLAN ID as packet: - * ovs_be16(src=0, dst=0, n_bits=12), 00 0c - * ovs_be32(NXM_OF_VLAN_TCI), ovs_be16(0) 00 00 08 02 00 00 - * ovs_be32(NXM_OF_VLAN_TCI), ovs_be16(0) 00 00 08 02 00 00 - * - * Match Ethernet destination on Ethernet source from packet: - * ovs_be16(src=0, dst=0, n_bits=48), 00 30 - * ovs_be32(NXM_OF_ETH_SRC), ovs_be16(0) 00 00 04 06 00 00 - * ovs_be32(NXM_OF_ETH_DST), ovs_be16(0) 00 00 02 06 00 00 - * - * Output to the packet's input port: - * ovs_be16(src=0, dst=2, n_bits=16), 10 10 - * ovs_be32(NXM_OF_IN_PORT), ovs_be16(0) 00 00 00 02 00 00 - * - * Given a packet that arrived on port A with Ethernet source address B in - * VLAN C, this would set up the flow "dl_dst=B, vlan_vid=C, - * actions=output:A". - * - * In syntax accepted by ovs-ofctl, this action is: - * learn(NXM_OF_VLAN_TCI[0..11], NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], - * output:NXM_OF_IN_PORT[]) - * - * 3. Here's a recipe for a very simple-minded MAC learning switch. It uses a - * 10-second MAC expiration time to make it easier to see what's going on - * - * ovs-vsctl del-controller br0 - * ovs-ofctl del-flows br0 - * ovs-ofctl add-flow br0 "table=0 actions=learn(table=1, \ - hard_timeout=10, NXM_OF_VLAN_TCI[0..11], \ - NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], \ - output:NXM_OF_IN_PORT[]), resubmit(,1)" - * ovs-ofctl add-flow br0 "table=1 priority=0 actions=flood" - * - * You can then dump the MAC learning table with: - * - * ovs-ofctl dump-flows br0 table=1 - * - * Usage Advice - * ------------ - * - * For best performance, segregate learned flows into a table that is not used - * for any other flows except possibly for a lowest-priority "catch-all" flow - * (a flow with no match criteria). If different learning actions specify - * different match criteria, use different tables for the learned flows. - * - * The meaning of 'hard_timeout' and 'idle_timeout' can be counterintuitive. - * These timeouts apply to the flow that is added, which means that a flow with - * an idle timeout will expire when no traffic has been sent *to* the learned - * address. This is not usually the intent in MAC learning; instead, we want - * the MAC learn entry to expire when no traffic has been sent *from* the - * learned address. Use a hard timeout for that. - */ -struct nx_action_learn { - ovs_be16 type; /* OFPAT_VENDOR. */ - ovs_be16 len; /* At least 24. */ - ovs_be32 vendor; /* NX_VENDOR_ID. */ - ovs_be16 subtype; /* NXAST_LEARN. */ - ovs_be16 idle_timeout; /* Idle time before discarding (seconds). */ - ovs_be16 hard_timeout; /* Max time before discarding (seconds). */ - ovs_be16 priority; /* Priority level of flow entry. */ - ovs_be64 cookie; /* Cookie for new flow. */ - ovs_be16 flags; /* NX_LEARN_F_*. */ - uint8_t table_id; /* Table to insert flow entry. */ - uint8_t pad; /* Must be zero. */ - ovs_be16 fin_idle_timeout; /* Idle timeout after FIN, if nonzero. */ - ovs_be16 fin_hard_timeout; /* Hard timeout after FIN, if nonzero. */ - /* Followed by a sequence of flow_mod_spec elements, as described above, - * until the end of the action is reached. */ -}; -OFP_ASSERT(sizeof(struct nx_action_learn) == 32); - -/* Bits for 'flags' in struct nx_action_learn. - * - * If NX_LEARN_F_SEND_FLOW_REM is set, then the learned flows will have their - * OFPFF_SEND_FLOW_REM flag set. - * - * If NX_LEARN_F_DELETE_LEARNED is set, then removing this action will delete - * all the flows from the learn action's 'table_id' that have the learn - * action's 'cookie'. Important points: - * - * - The deleted flows include those created by this action, those created - * by other learn actions with the same 'table_id' and 'cookie', those - * created by flow_mod requests by a controller in the specified table - * with the specified cookie, and those created through any other - * means. - * - * - If multiple flows specify "learn" actions with - * NX_LEARN_F_DELETE_LEARNED with the same 'table_id' and 'cookie', then - * no deletion occurs until all of those "learn" actions are deleted. - * - * - Deleting a flow that contains a learn action is the most obvious way - * to delete a learn action. Modifying a flow's actions, or replacing it - * by a new flow, can also delete a learn action. Finally, replacing a - * learn action with NX_LEARN_F_DELETE_LEARNED with a learn action - * without that flag also effectively deletes the learn action and can - * trigger flow deletion. - * - * NX_LEARN_F_DELETE_LEARNED was added in Open vSwitch 2.4. */ -enum nx_learn_flags { - NX_LEARN_F_SEND_FLOW_REM = 1 << 0, - NX_LEARN_F_DELETE_LEARNED = 1 << 1, -}; - -#define NX_LEARN_N_BITS_MASK 0x3ff - -#define NX_LEARN_SRC_FIELD (0 << 13) /* Copy from field. */ -#define NX_LEARN_SRC_IMMEDIATE (1 << 13) /* Copy from immediate value. */ -#define NX_LEARN_SRC_MASK (1 << 13) - -#define NX_LEARN_DST_MATCH (0 << 11) /* Add match criterion. */ -#define NX_LEARN_DST_LOAD (1 << 11) /* Add NXAST_REG_LOAD action. */ -#define NX_LEARN_DST_OUTPUT (2 << 11) /* Add OFPAT_OUTPUT action. */ -#define NX_LEARN_DST_RESERVED (3 << 11) /* Not yet defined. */ -#define NX_LEARN_DST_MASK (3 << 11) - -/* Action structure for NXAST_FIN_TIMEOUT. - * - * This action changes the idle timeout or hard timeout, or both, of this - * OpenFlow rule when the rule matches a TCP packet with the FIN or RST flag. - * When such a packet is observed, the action reduces the rule's idle timeout - * to 'fin_idle_timeout' and its hard timeout to 'fin_hard_timeout'. This - * action has no effect on an existing timeout that is already shorter than the - * one that the action specifies. A 'fin_idle_timeout' or 'fin_hard_timeout' - * of zero has no effect on the respective timeout. - * - * 'fin_idle_timeout' and 'fin_hard_timeout' are measured in seconds. - * 'fin_hard_timeout' specifies time since the flow's creation, not since the - * receipt of the FIN or RST. - * - * This is useful for quickly discarding learned TCP flows that otherwise will - * take a long time to expire. - * - * This action is intended for use with an OpenFlow rule that matches only a - * single TCP flow. If the rule matches multiple TCP flows (e.g. it wildcards - * all TCP traffic, or all TCP traffic to a particular port), then any FIN or - * RST in any of those flows will cause the entire OpenFlow rule to expire - * early, which is not normally desirable. - */ -struct nx_action_fin_timeout { - ovs_be16 type; /* OFPAT_VENDOR. */ - ovs_be16 len; /* 16. */ - ovs_be32 vendor; /* NX_VENDOR_ID. */ - ovs_be16 subtype; /* NXAST_FIN_TIMEOUT. */ - ovs_be16 fin_idle_timeout; /* New idle timeout, if nonzero. */ - ovs_be16 fin_hard_timeout; /* New hard timeout, if nonzero. */ - ovs_be16 pad; /* Must be zero. */ -}; -OFP_ASSERT(sizeof(struct nx_action_fin_timeout) == 16); - -/* Action structure for NXAST_BUNDLE and NXAST_BUNDLE_LOAD. - * - * The bundle actions choose a slave from a supplied list of options. - * NXAST_BUNDLE outputs to its selection. NXAST_BUNDLE_LOAD writes its - * selection to a register. - * - * The list of possible slaves follows the nx_action_bundle structure. The size - * of each slave is governed by its type as indicated by the 'slave_type' - * parameter. The list of slaves should be padded at its end with zeros to make - * the total length of the action a multiple of 8. - * - * Switches infer from the 'slave_type' parameter the size of each slave. All - * implementations must support the NXM_OF_IN_PORT 'slave_type' which indicates - * that the slaves are OpenFlow port numbers with NXM_LENGTH(NXM_OF_IN_PORT) == - * 2 byte width. Switches should reject actions which indicate unknown or - * unsupported slave types. - * - * Switches use a strategy dictated by the 'algorithm' parameter to choose a - * slave. If the switch does not support the specified 'algorithm' parameter, - * it should reject the action. - * - * Several algorithms take into account liveness when selecting slaves. The - * liveness of a slave is implementation defined (with one exception), but will - * generally take into account things like its carrier status and the results - * of any link monitoring protocols which happen to be running on it. In order - * to give controllers a place-holder value, the OFPP_NONE port is always - * considered live. - * - * Some slave selection strategies require the use of a hash function, in which - * case the 'fields' and 'basis' parameters should be populated. The 'fields' - * parameter (one of NX_HASH_FIELDS_*) designates which parts of the flow to - * hash. Refer to the definition of "enum nx_hash_fields" for details. The - * 'basis' parameter is used as a universal hash parameter. Different values - * of 'basis' yield different hash results. - * - * The 'zero' parameter at the end of the action structure is reserved for - * future use. Switches are required to reject actions which have nonzero - * bytes in the 'zero' field. - * - * NXAST_BUNDLE actions should have 'ofs_nbits' and 'dst' zeroed. Switches - * should reject actions which have nonzero bytes in either of these fields. - * - * NXAST_BUNDLE_LOAD stores the OpenFlow port number of the selected slave in - * dst[ofs:ofs+n_bits]. The format and semantics of 'dst' and 'ofs_nbits' are - * similar to those for the NXAST_REG_LOAD action. */ -struct nx_action_bundle { - ovs_be16 type; /* OFPAT_VENDOR. */ - ovs_be16 len; /* Length including slaves. */ - ovs_be32 vendor; /* NX_VENDOR_ID. */ - ovs_be16 subtype; /* NXAST_BUNDLE or NXAST_BUNDLE_LOAD. */ - - /* Slave choice algorithm to apply to hash value. */ - ovs_be16 algorithm; /* One of NX_BD_ALG_*. */ - - /* What fields to hash and how. */ - ovs_be16 fields; /* One of NX_HASH_FIELDS_*. */ - ovs_be16 basis; /* Universal hash parameter. */ - - ovs_be32 slave_type; /* NXM_OF_IN_PORT. */ - ovs_be16 n_slaves; /* Number of slaves. */ - - ovs_be16 ofs_nbits; /* (ofs << 6) | (n_bits - 1). */ - ovs_be32 dst; /* Destination. */ - - uint8_t zero[4]; /* Reserved. Must be zero. */ -}; -OFP_ASSERT(sizeof(struct nx_action_bundle) == 32); - -/* NXAST_BUNDLE: Bundle slave choice algorithm to apply. - * - * In the descriptions below, 'slaves' is the list of possible slaves in the - * order they appear in the OpenFlow action. */ -enum nx_bd_algorithm { - /* Chooses the first live slave listed in the bundle. - * - * O(n_slaves) performance. */ - NX_BD_ALG_ACTIVE_BACKUP, - - /* for i in [0,n_slaves): - * weights[i] = hash(flow, i) - * slave = { slaves[i] such that weights[i] >= weights[j] for all j != i } - * - * Redistributes 1/n_slaves of traffic when a slave's liveness changes. - * O(n_slaves) performance. - * - * Uses the 'fields' and 'basis' parameters. */ - NX_BD_ALG_HRW /* Highest Random Weight. */ -}; - - -/* Action structure for NXAST_DEC_TTL_CNT_IDS. - * - * If the packet is not IPv4 or IPv6, does nothing. For IPv4 or IPv6, if the - * TTL or hop limit is at least 2, decrements it by 1. Otherwise, if TTL or - * hop limit is 0 or 1, sends a packet-in to the controllers with each of the - * 'n_controllers' controller IDs specified in 'cnt_ids'. - * - * (This differs from NXAST_DEC_TTL in that for NXAST_DEC_TTL the packet-in is - * sent only to controllers with id 0.) - */ -struct nx_action_cnt_ids { - ovs_be16 type; /* OFPAT_VENDOR. */ - ovs_be16 len; /* Length including slaves. */ - ovs_be32 vendor; /* NX_VENDOR_ID. */ - ovs_be16 subtype; /* NXAST_DEC_TTL_CNT_IDS. */ - - ovs_be16 n_controllers; /* Number of controllers. */ - uint8_t zeros[4]; /* Must be zero. */ - - /* Followed by 1 or more controller ids. - * - * uint16_t cnt_ids[]; // Controller ids. - * uint8_t pad[]; // Must be 0 to 8-byte align cnt_ids[]. - */ -}; -OFP_ASSERT(sizeof(struct nx_action_cnt_ids) == 16); - - -/* Action structure for NXAST_OUTPUT_REG. - * - * Outputs to the OpenFlow port number written to src[ofs:ofs+nbits]. - * - * The format and semantics of 'src' and 'ofs_nbits' are similar to those for - * the NXAST_REG_LOAD action. - * - * The acceptable nxm_header values for 'src' are the same as the acceptable - * nxm_header values for the 'src' field of NXAST_REG_MOVE. - * - * The 'max_len' field indicates the number of bytes to send when the chosen - * port is OFPP_CONTROLLER. Its semantics are equivalent to the 'max_len' - * field of OFPAT_OUTPUT. - * - * The 'zero' field is required to be zeroed for forward compatibility. */ -struct nx_action_output_reg { - ovs_be16 type; /* OFPAT_VENDOR. */ - ovs_be16 len; /* 24. */ - ovs_be32 vendor; /* NX_VENDOR_ID. */ - ovs_be16 subtype; /* NXAST_OUTPUT_REG. */ - - ovs_be16 ofs_nbits; /* (ofs << 6) | (n_bits - 1). */ - ovs_be32 src; /* Source. */ - - ovs_be16 max_len; /* Max length to send to controller. */ - - uint8_t zero[6]; /* Reserved, must be zero. */ -}; -OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24); - -/* NXAST_EXIT - * - * Discontinues action processing. - * - * The NXAST_EXIT action causes the switch to immediately halt processing - * actions for the flow. Any actions which have already been processed are - * executed by the switch. However, any further actions, including those which - * may be in different tables, or different levels of the NXAST_RESUBMIT - * hierarchy, will be ignored. - * - * Uses the nx_action_header structure. */ - /* Flexible flow specifications (aka NXM = Nicira Extended Match). * * OpenFlow 1.0 has "struct ofp10_match" for specifying flow matches. This @@ -2044,30 +1148,6 @@ struct nx_controller_id { ovs_be16 controller_id; /* New controller connection ID. */ }; OFP_ASSERT(sizeof(struct nx_controller_id) == 8); - -/* Action structure for NXAST_CONTROLLER. - * - * This generalizes using OFPAT_OUTPUT to send a packet to OFPP_CONTROLLER. In - * addition to the 'max_len' that OFPAT_OUTPUT supports, it also allows - * specifying: - * - * - 'reason': The reason code to use in the ofp_packet_in or nx_packet_in. - * - * - 'controller_id': The ID of the controller connection to which the - * ofp_packet_in should be sent. The ofp_packet_in or nx_packet_in is - * sent only to controllers that have the specified controller connection - * ID. See "struct nx_controller_id" for more information. */ -struct nx_action_controller { - ovs_be16 type; /* OFPAT_VENDOR. */ - ovs_be16 len; /* Length is 16. */ - ovs_be32 vendor; /* NX_VENDOR_ID. */ - ovs_be16 subtype; /* NXAST_CONTROLLER. */ - ovs_be16 max_len; /* Maximum length to send to controller. */ - ovs_be16 controller_id; /* Controller ID to send packet-in. */ - uint8_t reason; /* enum ofp_packet_in_reason (OFPR_*). */ - uint8_t zero; /* Must be zero. */ -}; -OFP_ASSERT(sizeof(struct nx_action_controller) == 16); /* Flow Table Monitoring * ===================== @@ -2309,98 +1389,4 @@ struct nx_flow_monitor_cancel { }; OFP_ASSERT(sizeof(struct nx_flow_monitor_cancel) == 4); -/* Action structure for NXAST_WRITE_METADATA. - * - * Modifies the 'mask' bits of the metadata value. */ -struct nx_action_write_metadata { - ovs_be16 type; /* OFPAT_VENDOR. */ - ovs_be16 len; /* Length is 32. */ - ovs_be32 vendor; /* NX_VENDOR_ID. */ - ovs_be16 subtype; /* NXAST_WRITE_METADATA. */ - uint8_t zeros[6]; /* Must be zero. */ - ovs_be64 metadata; /* Metadata register. */ - ovs_be64 mask; /* Metadata mask. */ -}; -OFP_ASSERT(sizeof(struct nx_action_write_metadata) == 32); - -/* Action structure for NXAST_PUSH_MPLS. */ -struct nx_action_push_mpls { - ovs_be16 type; /* OFPAT_VENDOR. */ - ovs_be16 len; /* Length is 8. */ - ovs_be32 vendor; /* NX_VENDOR_ID. */ - ovs_be16 subtype; /* NXAST_PUSH_MPLS. */ - ovs_be16 ethertype; /* Ethertype */ - uint8_t pad[4]; -}; -OFP_ASSERT(sizeof(struct nx_action_push_mpls) == 16); - -/* Action structure for NXAST_POP_MPLS. */ -struct nx_action_pop_mpls { - ovs_be16 type; /* OFPAT_VENDOR. */ - ovs_be16 len; /* Length is 8. */ - ovs_be32 vendor; /* NX_VENDOR_ID. */ - ovs_be16 subtype; /* NXAST_POP_MPLS. */ - ovs_be16 ethertype; /* Ethertype */ - uint8_t pad[4]; -}; -OFP_ASSERT(sizeof(struct nx_action_pop_mpls) == 16); - -/* Action structure for NXAST_SET_MPLS_LABEL. */ -struct nx_action_mpls_label { - ovs_be16 type; /* OFPAT_VENDOR. */ - ovs_be16 len; /* Length is 8. */ - ovs_be32 vendor; /* NX_VENDOR_ID. */ - ovs_be16 subtype; /* NXAST_SET_MPLS_LABEL. */ - uint8_t zeros[2]; /* Must be zero. */ - ovs_be32 label; /* LABEL */ -}; -OFP_ASSERT(sizeof(struct nx_action_mpls_label) == 16); - -/* Action structure for NXAST_SET_MPLS_TC. */ -struct nx_action_mpls_tc { - ovs_be16 type; /* OFPAT_VENDOR. */ - ovs_be16 len; /* Length is 8. */ - ovs_be32 vendor; /* NX_VENDOR_ID. */ - ovs_be16 subtype; /* NXAST_SET_MPLS_TC. */ - uint8_t tc; /* TC */ - uint8_t pad[5]; -}; -OFP_ASSERT(sizeof(struct nx_action_mpls_tc) == 16); - -/* Action structure for NXAST_SET_MPLS_TTL. */ -struct nx_action_mpls_ttl { - ovs_be16 type; /* OFPAT_VENDOR. */ - ovs_be16 len; /* Length is 8. */ - ovs_be32 vendor; /* NX_VENDOR_ID. */ - ovs_be16 subtype; /* NXAST_SET_MPLS_TTL. */ - uint8_t ttl; /* TTL */ - uint8_t pad[5]; -}; -OFP_ASSERT(sizeof(struct nx_action_mpls_ttl) == 16); - -/* Action structure for NXAST_SAMPLE. - * - * Samples matching packets with the given probability and sends them - * each to the set of collectors identified with the given ID. The - * probability is expressed as a number of packets to be sampled out - * of USHRT_MAX packets, and must be >0. - * - * When sending packet samples to IPFIX collectors, the IPFIX flow - * record sent for each sampled packet is associated with the given - * observation domain ID and observation point ID. Each IPFIX flow - * record contain the sampled packet's headers when executing this - * rule. If a sampled packet's headers are modified by previous - * actions in the flow, those modified headers are sent. */ -struct nx_action_sample { - ovs_be16 type; /* OFPAT_VENDOR. */ - ovs_be16 len; /* Length is 24. */ - ovs_be32 vendor; /* NX_VENDOR_ID. */ - ovs_be16 subtype; /* NXAST_SAMPLE. */ - ovs_be16 probability; /* Fraction of packets to sample. */ - ovs_be32 collector_set_id; /* ID of collector set in OVSDB. */ - ovs_be32 obs_domain_id; /* ID of sampling observation domain. */ - ovs_be32 obs_point_id; /* ID of sampling observation point. */ -}; -OFP_ASSERT(sizeof(struct nx_action_sample) == 24); - #endif /* openflow/nicira-ext.h */ diff --git a/include/openflow/openflow-1.0.h b/include/openflow/openflow-1.0.h index 002c75d46..c67edd97c 100644 --- a/include/openflow/openflow-1.0.h +++ b/include/openflow/openflow-1.0.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -170,46 +170,6 @@ struct ofp10_packet_in { }; OFP_ASSERT(sizeof(struct ofp10_packet_in) == 12); -enum ofp10_action_type { - OFPAT10_OUTPUT, /* Output to switch port. */ - OFPAT10_SET_VLAN_VID, /* Set the 802.1q VLAN id. */ - OFPAT10_SET_VLAN_PCP, /* Set the 802.1q priority. */ - OFPAT10_STRIP_VLAN, /* Strip the 802.1q header. */ - OFPAT10_SET_DL_SRC, /* Ethernet source address. */ - OFPAT10_SET_DL_DST, /* Ethernet destination address. */ - OFPAT10_SET_NW_SRC, /* IP source address. */ - OFPAT10_SET_NW_DST, /* IP destination address. */ - OFPAT10_SET_NW_TOS, /* IP ToS (DSCP field, 6 bits). */ - OFPAT10_SET_TP_SRC, /* TCP/UDP source port. */ - OFPAT10_SET_TP_DST, /* TCP/UDP destination port. */ - OFPAT10_ENQUEUE, /* Output to queue. */ - OFPAT10_VENDOR = 0xffff -}; - -/* Action structure for OFPAT10_OUTPUT, which sends packets out 'port'. - * When the 'port' is the OFPP_CONTROLLER, 'max_len' indicates the max - * number of bytes to send. A 'max_len' of zero means no bytes of the - * packet should be sent. */ -struct ofp10_action_output { - ovs_be16 type; /* OFPAT10_OUTPUT. */ - ovs_be16 len; /* Length is 8. */ - ovs_be16 port; /* Output port. */ - ovs_be16 max_len; /* Max length to send to controller. */ -}; -OFP_ASSERT(sizeof(struct ofp10_action_output) == 8); - -/* OFPAT10_ENQUEUE action struct: send packets to given queue on port. */ -struct ofp10_action_enqueue { - ovs_be16 type; /* OFPAT10_ENQUEUE. */ - ovs_be16 len; /* Len is 16. */ - ovs_be16 port; /* Port that queue belongs. Should - refer to a valid physical port - (i.e. < OFPP_MAX) or OFPP_IN_PORT. */ - uint8_t pad[6]; /* Pad for 64-bit alignment. */ - ovs_be32 queue_id; /* Where to enqueue the packets. */ -}; -OFP_ASSERT(sizeof(struct ofp10_action_enqueue) == 16); - /* Send packet (controller -> datapath). */ struct ofp10_packet_out { ovs_be32 buffer_id; /* ID assigned by datapath or UINT32_MAX. */ @@ -311,9 +271,9 @@ struct ofp10_flow_mod { output port. A value of OFPP_NONE indicates no restriction. */ ovs_be16 flags; /* One of OFPFF_*. */ - struct ofp_action_header actions[0]; /* The action length is inferred - from the length field in the - header. */ + + /* Followed by OpenFlow actions whose length is inferred from the length + * field in the OpenFlow header. */ }; OFP_ASSERT(sizeof(struct ofp10_flow_mod) == 64); @@ -374,7 +334,7 @@ struct ofp10_flow_stats { ovs_32aligned_be64 cookie; /* Opaque controller-issued identifier. */ ovs_32aligned_be64 packet_count; /* Number of packets in flow. */ ovs_32aligned_be64 byte_count; /* Number of bytes in flow. */ - struct ofp_action_header actions[0]; /* Actions. */ + /* Followed by OpenFlow actions whose length is inferred from 'length'. */ }; OFP_ASSERT(sizeof(struct ofp10_flow_stats) == 88); diff --git a/include/openflow/openflow-1.1.h b/include/openflow/openflow-1.1.h index b51600b17..4d76f08eb 100644 --- a/include/openflow/openflow-1.1.h +++ b/include/openflow/openflow-1.1.h @@ -181,38 +181,6 @@ enum ofp11_capabilities { OFPC11_GROUP_STATS = 1 << 3, /* Group statistics. */ }; -enum ofp11_action_type { - OFPAT11_OUTPUT, /* Output to switch port. */ - OFPAT11_SET_VLAN_VID, /* Set the 802.1q VLAN id. */ - OFPAT11_SET_VLAN_PCP, /* Set the 802.1q priority. */ - OFPAT11_SET_DL_SRC, /* Ethernet source address. */ - OFPAT11_SET_DL_DST, /* Ethernet destination address. */ - OFPAT11_SET_NW_SRC, /* IP source address. */ - OFPAT11_SET_NW_DST, /* IP destination address. */ - OFPAT11_SET_NW_TOS, /* IP ToS (DSCP field, 6 bits). */ - OFPAT11_SET_NW_ECN, /* IP ECN (2 bits). */ - OFPAT11_SET_TP_SRC, /* TCP/UDP/SCTP source port. */ - OFPAT11_SET_TP_DST, /* TCP/UDP/SCTP destination port. */ - OFPAT11_COPY_TTL_OUT, /* Copy TTL "outwards" -- from next-to-outermost - to outermost */ - OFPAT11_COPY_TTL_IN, /* Copy TTL "inwards" -- from outermost to - next-to-outermost */ - OFPAT11_SET_MPLS_LABEL, /* MPLS label */ - OFPAT11_SET_MPLS_TC, /* MPLS TC */ - OFPAT11_SET_MPLS_TTL, /* MPLS TTL */ - OFPAT11_DEC_MPLS_TTL, /* Decrement MPLS TTL */ - - OFPAT11_PUSH_VLAN, /* Push a new VLAN tag */ - OFPAT11_POP_VLAN, /* Pop the outer VLAN tag */ - OFPAT11_PUSH_MPLS, /* Push a new MPLS Label Stack Entry */ - OFPAT11_POP_MPLS, /* Pop the outer MPLS Label Stack Entry */ - OFPAT11_SET_QUEUE, /* Set queue id when outputting to a port */ - OFPAT11_GROUP, /* Apply group. */ - OFPAT11_SET_NW_TTL, /* IP TTL. */ - OFPAT11_DEC_NW_TTL, /* Decrement IP TTL. */ - OFPAT11_EXPERIMENTER = 0xffff -}; - #define OFPMT11_STANDARD_LENGTH 88 struct ofp11_match_header { @@ -346,97 +314,6 @@ struct ofp11_instruction_experimenter { }; OFP_ASSERT(sizeof(struct ofp11_instruction_experimenter) == 8); -/* Action structure for OFPAT_OUTPUT, which sends packets out 'port'. - * When the 'port' is the OFPP_CONTROLLER, 'max_len' indicates the max - * number of bytes to send. A 'max_len' of zero means no bytes of the - * packet should be sent.*/ -struct ofp11_action_output { - ovs_be16 type; /* OFPAT11_OUTPUT. */ - ovs_be16 len; /* Length is 16. */ - ovs_be32 port; /* Output port. */ - ovs_be16 max_len; /* Max length to send to controller. */ - uint8_t pad[6]; /* Pad to 64 bits. */ -}; -OFP_ASSERT(sizeof(struct ofp11_action_output) == 16); - -/* Action structure for OFPAT_GROUP. */ -struct ofp11_action_group { - ovs_be16 type; /* OFPAT11_GROUP. */ - ovs_be16 len; /* Length is 8. */ - ovs_be32 group_id; /* Group identifier. */ -}; -OFP_ASSERT(sizeof(struct ofp11_action_group) == 8); - -/* OFPAT_SET_QUEUE action struct: send packets to given queue on port. */ -struct ofp11_action_set_queue { - ovs_be16 type; /* OFPAT11_SET_QUEUE. */ - ovs_be16 len; /* Len is 8. */ - ovs_be32 queue_id; /* Queue id for the packets. */ -}; -OFP_ASSERT(sizeof(struct ofp11_action_set_queue) == 8); - -/* Action structure for OFPAT11_SET_MPLS_LABEL. */ -struct ofp11_action_mpls_label { - ovs_be16 type; /* OFPAT11_SET_MPLS_LABEL. */ - ovs_be16 len; /* Length is 8. */ - ovs_be32 mpls_label; /* MPLS label */ -}; -OFP_ASSERT(sizeof(struct ofp11_action_mpls_label) == 8); - -/* Action structure for OFPAT11_SET_MPLS_TC. */ -struct ofp11_action_mpls_tc { - ovs_be16 type; /* OFPAT11_SET_MPLS_TC. */ - ovs_be16 len; /* Length is 8. */ - uint8_t mpls_tc; /* MPLS TC */ - uint8_t pad[3]; -}; -OFP_ASSERT(sizeof(struct ofp11_action_mpls_tc) == 8); - -/* Action structure for OFPAT11_SET_MPLS_TTL. */ -struct ofp11_action_mpls_ttl { - ovs_be16 type; /* OFPAT11_SET_MPLS_TTL. */ - ovs_be16 len; /* Length is 8. */ - uint8_t mpls_ttl; /* MPLS TTL */ - uint8_t pad[3]; -}; -OFP_ASSERT(sizeof(struct ofp11_action_mpls_ttl) == 8); - -/* Action structure for OFPAT11_SET_NW_ECN. */ -struct ofp11_action_nw_ecn { - ovs_be16 type; /* OFPAT11_SET_TW_SRC/DST. */ - ovs_be16 len; /* Length is 8. */ - uint8_t nw_ecn; /* IP ECN (2 bits). */ - uint8_t pad[3]; -}; -OFP_ASSERT(sizeof(struct ofp11_action_nw_ecn) == 8); - -/* Action structure for OFPAT11_SET_NW_TTL. */ -struct ofp11_action_nw_ttl { - ovs_be16 type; /* OFPAT11_SET_NW_TTL. */ - ovs_be16 len; /* Length is 8. */ - uint8_t nw_ttl; /* IP TTL */ - uint8_t pad[3]; -}; -OFP_ASSERT(sizeof(struct ofp11_action_nw_ttl) == 8); - -/* Action structure for OFPAT11_PUSH_VLAN/MPLS. */ -struct ofp11_action_push { - ovs_be16 type; /* OFPAT11_PUSH_VLAN/MPLS. */ - ovs_be16 len; /* Length is 8. */ - ovs_be16 ethertype; /* Ethertype */ - uint8_t pad[2]; -}; -OFP_ASSERT(sizeof(struct ofp11_action_push) == 8); - -/* Action structure for OFPAT11_POP_MPLS. */ -struct ofp11_action_pop_mpls { - ovs_be16 type; /* OFPAT11_POP_MPLS. */ - ovs_be16 len; /* Length is 8. */ - ovs_be16 ethertype; /* Ethertype */ - uint8_t pad[2]; -}; -OFP_ASSERT(sizeof(struct ofp11_action_pop_mpls) == 8); - /* Configure/Modify behavior of a flow table */ struct ofp11_table_mod { uint8_t table_id; /* ID of the table, 0xFF indicates all tables */ diff --git a/include/openflow/openflow-1.2.h b/include/openflow/openflow-1.2.h index 1069f4e0e..4183464c8 100644 --- a/include/openflow/openflow-1.2.h +++ b/include/openflow/openflow-1.2.h @@ -110,22 +110,18 @@ enum oxm12_ofb_match_fields { OFPXMT12_OFB_IPV6_ND_TLL, /* Target link-layer for ND. */ OFPXMT12_OFB_MPLS_LABEL, /* MPLS label. */ OFPXMT12_OFB_MPLS_TC, /* MPLS TC. */ -#define OFPXMT12_MASK ((1ULL << (OFPXMT12_OFB_MPLS_TC + 1)) - 1) /* Following added in OpenFlow 1.3 */ OFPXMT13_OFB_MPLS_BOS, /* MPLS BoS bit. */ OFPXMT13_OFB_PBB_ISID, /* PBB I-SID. */ OFPXMT13_OFB_TUNNEL_ID, /* Logical Port Metadata */ OFPXMT13_OFB_IPV6_EXTHDR, /* IPv6 Extension Header pseudo-field */ -#define OFPXMT13_MASK ((1ULL << (OFPXMT13_OFB_IPV6_EXTHDR + 1)) - 1) /* Following added in OpenFlow 1.4. */ OFPXMT14_OFB_PBB_UCA = 41, /* PBB UCA header field. */ -#define OFPXMT14_MASK (1ULL << OFPXMT14_OFB_PBB_UCA) /* Following added in OpenFlow 1.5. */ OFPXMT15_OFB_TCP_FLAGS = 42, /* TCP flags. */ -#define OFPXMT15_MASK (1ULL << OFPXMT15_OFB_TCP_FLAGS) }; /* OXM implementation makes use of NXM as they are the same format @@ -230,10 +226,6 @@ struct ofp12_oxm_experimenter_header { }; OFP_ASSERT(sizeof(struct ofp12_oxm_experimenter_header) == 8); -enum ofp12_action_type { - OFPAT12_SET_FIELD = 25, /* Set a header field using OXM TLV format. */ -}; - enum ofp12_controller_max_len { OFPCML12_MAX = 0xffe5, /* maximum max_len value which can be used * to request a specific byte length. */ @@ -242,18 +234,6 @@ enum ofp12_controller_max_len { * sent to the controller. */ }; -/* Action structure for OFPAT12_SET_FIELD. */ -struct ofp12_action_set_field { - ovs_be16 type; /* OFPAT12_SET_FIELD. */ - ovs_be16 len; /* Length is padded to 64 bits. */ - ovs_be32 dst; /* OXM TLV header */ - /* Followed by: - * - Exactly ((oxm_len + 4) + 7)/8*8 - (oxm_len + 4) (between 0 and 7) - * bytes of all-zero bytes - */ -}; -OFP_ASSERT(sizeof(struct ofp12_action_set_field) == 8); - /* OpenFlow 1.2 specific flags * (struct ofp12_flow_mod, member flags). */ enum ofp12_flow_mod_flags { diff --git a/include/openflow/openflow-1.3.h b/include/openflow/openflow-1.3.h index a722a4537..5cc8ddd7e 100644 --- a/include/openflow/openflow-1.3.h +++ b/include/openflow/openflow-1.3.h @@ -101,27 +101,6 @@ struct ofp13_instruction_meter { }; OFP_ASSERT(sizeof(struct ofp13_instruction_meter) == 8); -enum ofp13_action_type { - OFPAT13_OUTPUT = 0, /* Output to switch port. */ - OFPAT13_COPY_TTL_OUT = 11, /* Copy TTL "outwards" -- from next-to-outermost - to outermost */ - OFPAT13_COPY_TTL_IN = 12, /* Copy TTL "inwards" -- from outermost to - next-to-outermost */ - OFPAT13_SET_MPLS_TTL = 15, /* MPLS TTL */ - OFPAT13_DEC_MPLS_TTL = 16, /* Decrement MPLS TTL */ - OFPAT13_PUSH_VLAN = 17, /* Push a new VLAN tag */ - OFPAT13_POP_VLAN = 18, /* Pop the outer VLAN tag */ - OFPAT13_PUSH_MPLS = 19, /* Push a new MPLS Label Stack Entry */ - OFPAT13_POP_MPLS = 20, /* Pop the outer MPLS Label Stack Entry */ - OFPAT13_SET_QUEUE = 21, /* Set queue id when outputting to a port */ - OFPAT13_GROUP = 22, /* Apply group. */ - OFPAT13_SET_NW_TTL = 23, /* IP TTL. */ - OFPAT13_DEC_NW_TTL = 24, /* Decrement IP TTL. */ - OFPAT13_SET_FIELD = 25, /* Set a header field using OXM TLV format. */ - OFPAT13_PUSH_PBB = 26, /* Push a new PBB service tag (I-TAG) */ - OFPAT13_POP_PBB = 27 /* Pop the outer PBB service tag (I-TAG) */ -}; - /* enum ofp_config_flags value OFPC_INVALID_TTL_TO_CONTROLLER * is deprecated in OpenFlow 1.3 */ diff --git a/include/openflow/openflow-common.h b/include/openflow/openflow-common.h index 18ca3b1f6..cd0aebf92 100644 --- a/include/openflow/openflow-common.h +++ b/include/openflow/openflow-common.h @@ -286,82 +286,6 @@ enum ofp_flow_mod_flags { OFPFF_CHECK_OVERLAP = 1 << 1, /* Check for overlapping entries first. */ }; -/* Action header for OFPAT10_VENDOR and OFPAT11_EXPERIMEMNTER. - * The rest of the body is vendor-defined. */ -struct ofp_action_vendor_header { - ovs_be16 type; /* OFPAT10_VENDOR. */ - ovs_be16 len; /* Length is a multiple of 8. */ - ovs_be32 vendor; /* Vendor ID, which takes the same form - as in "struct ofp_vendor_header". */ -}; -OFP_ASSERT(sizeof(struct ofp_action_vendor_header) == 8); - -/* Action header that is common to all actions. The length includes the - * header and any padding used to make the action 64-bit aligned. - * NB: The length of an action *must* always be a multiple of eight. */ -struct ofp_action_header { - ovs_be16 type; /* One of OFPAT*. */ - ovs_be16 len; /* Length of action, including this - header. This is the length of action, - including any padding to make it - 64-bit aligned. */ - uint8_t pad[4]; -}; -OFP_ASSERT(sizeof(struct ofp_action_header) == 8); - -/* Action structure for OFPAT10_SET_VLAN_VID and OFPAT11_SET_VLAN_VID. */ -struct ofp_action_vlan_vid { - ovs_be16 type; /* Type. */ - ovs_be16 len; /* Length is 8. */ - ovs_be16 vlan_vid; /* VLAN id. */ - uint8_t pad[2]; -}; -OFP_ASSERT(sizeof(struct ofp_action_vlan_vid) == 8); - -/* Action structure for OFPAT10_SET_VLAN_PCP and OFPAT11_SET_VLAN_PCP. */ -struct ofp_action_vlan_pcp { - ovs_be16 type; /* Type. */ - ovs_be16 len; /* Length is 8. */ - uint8_t vlan_pcp; /* VLAN priority. */ - uint8_t pad[3]; -}; -OFP_ASSERT(sizeof(struct ofp_action_vlan_pcp) == 8); - -/* Action structure for OFPAT10_SET_DL_SRC/DST and OFPAT11_SET_DL_SRC/DST. */ -struct ofp_action_dl_addr { - ovs_be16 type; /* Type. */ - ovs_be16 len; /* Length is 16. */ - uint8_t dl_addr[OFP_ETH_ALEN]; /* Ethernet address. */ - uint8_t pad[6]; -}; -OFP_ASSERT(sizeof(struct ofp_action_dl_addr) == 16); - -/* Action structure for OFPAT10_SET_NW_SRC/DST and OFPAT11_SET_NW_SRC/DST. */ -struct ofp_action_nw_addr { - ovs_be16 type; /* Type. */ - ovs_be16 len; /* Length is 8. */ - ovs_be32 nw_addr; /* IP address. */ -}; -OFP_ASSERT(sizeof(struct ofp_action_nw_addr) == 8); - -/* Action structure for OFPAT10_SET_NW_TOS and OFPAT11_SET_NW_TOS. */ -struct ofp_action_nw_tos { - ovs_be16 type; /* Type.. */ - ovs_be16 len; /* Length is 8. */ - uint8_t nw_tos; /* DSCP in high 6 bits, rest ignored. */ - uint8_t pad[3]; -}; -OFP_ASSERT(sizeof(struct ofp_action_nw_tos) == 8); - -/* Action structure for OFPAT10_SET_TP_SRC/DST and OFPAT11_SET_TP_SRC/DST. */ -struct ofp_action_tp_port { - ovs_be16 type; /* Type. */ - ovs_be16 len; /* Length is 8. */ - ovs_be16 tp_port; /* TCP/UDP port. */ - uint8_t pad[2]; -}; -OFP_ASSERT(sizeof(struct ofp_action_tp_port) == 8); - /* Why was this flow removed? */ enum ofp_flow_removed_reason { OFPRR_IDLE_TIMEOUT, /* Flow idle time exceeded idle_timeout. */ diff --git a/lib/automake.mk b/lib/automake.mk index 0997df565..9c4f49779 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -137,7 +137,6 @@ lib_libopenvswitch_la_SOURCES = \ lib/ofp-print.c \ lib/ofp-print.h \ lib/ofp-util.c \ - lib/ofp-util.def \ lib/ofp-util.h \ lib/ofp-version-opt.h \ lib/ofp-version-opt.c \ @@ -420,6 +419,12 @@ lib/dirs.c: lib/dirs.c.in Makefile > lib/dirs.c.tmp mv lib/dirs.c.tmp lib/dirs.c +lib/ofp-actions.inc: lib/ofp-actions.c $(srcdir)/build-aux/extract-ofp-actions + $(run_python) $(srcdir)/build-aux/extract-ofp-actions $< > $@.tmp + mv $@.tmp $@ +lib/ofp-actions.lo: lib/ofp-actions.inc +EXTRA_DIST += build-aux/extract-ofp-actions lib/ofp-errors.inc + $(srcdir)/lib/ofp-errors.inc: \ lib/ofp-errors.h include/openflow/openflow-common.h \ $(srcdir)/build-aux/extract-ofp-errors diff --git a/lib/bundle.c b/lib/bundle.c index 60a360e1d..620782b73 100644 --- a/lib/bundle.c +++ b/lib/bundle.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011, 2012, 2013 Nicira, Inc. +/* Copyright (c) 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,8 +31,6 @@ #include "openflow/nicira-ext.h" #include "vlog.h" -#define BUNDLE_MAX_SLAVES 2048 - VLOG_DEFINE_THIS_MODULE(bundle); static ofp_port_t @@ -103,89 +101,6 @@ bundle_execute(const struct ofpact_bundle *bundle, } } -/* Checks that 'nab' specifies a bundle action which is supported by this - * bundle module. Uses the 'max_ports' parameter to validate each port using - * ofputil_check_output_port(). Returns 0 if 'nab' is supported, otherwise an - * OFPERR_* error code. */ -enum ofperr -bundle_from_openflow(const struct nx_action_bundle *nab, - struct ofpbuf *ofpacts) -{ - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); - struct ofpact_bundle *bundle; - uint16_t subtype; - uint32_t slave_type; - size_t slaves_size, i; - enum ofperr error; - - bundle = ofpact_put_BUNDLE(ofpacts); - - subtype = ntohs(nab->subtype); - bundle->n_slaves = ntohs(nab->n_slaves); - bundle->basis = ntohs(nab->basis); - bundle->fields = ntohs(nab->fields); - bundle->algorithm = ntohs(nab->algorithm); - slave_type = ntohl(nab->slave_type); - slaves_size = ntohs(nab->len) - sizeof *nab; - - error = OFPERR_OFPBAC_BAD_ARGUMENT; - if (!flow_hash_fields_valid(bundle->fields)) { - VLOG_WARN_RL(&rl, "unsupported fields %d", (int) bundle->fields); - } else if (bundle->n_slaves > BUNDLE_MAX_SLAVES) { - VLOG_WARN_RL(&rl, "too may slaves"); - } else if (bundle->algorithm != NX_BD_ALG_HRW - && bundle->algorithm != NX_BD_ALG_ACTIVE_BACKUP) { - VLOG_WARN_RL(&rl, "unsupported algorithm %d", (int) bundle->algorithm); - } else if (slave_type != NXM_OF_IN_PORT) { - VLOG_WARN_RL(&rl, "unsupported slave type %"PRIu16, slave_type); - } else { - error = 0; - } - - if (!is_all_zeros(nab->zero, sizeof nab->zero)) { - VLOG_WARN_RL(&rl, "reserved field is nonzero"); - error = OFPERR_OFPBAC_BAD_ARGUMENT; - } - - if (subtype == NXAST_BUNDLE && (nab->ofs_nbits || nab->dst)) { - VLOG_WARN_RL(&rl, "bundle action has nonzero reserved fields"); - error = OFPERR_OFPBAC_BAD_ARGUMENT; - } - - if (subtype == NXAST_BUNDLE_LOAD) { - bundle->dst.field = mf_from_nxm_header(ntohl(nab->dst)); - bundle->dst.ofs = nxm_decode_ofs(nab->ofs_nbits); - bundle->dst.n_bits = nxm_decode_n_bits(nab->ofs_nbits); - - if (bundle->dst.n_bits < 16) { - VLOG_WARN_RL(&rl, "bundle_load action requires at least 16 bit " - "destination."); - error = OFPERR_OFPBAC_BAD_ARGUMENT; - } - } - - if (slaves_size < bundle->n_slaves * sizeof(ovs_be16)) { - VLOG_WARN_RL(&rl, "Nicira action %"PRIu16" only has %"PRIuSIZE" bytes " - "allocated for slaves. %"PRIuSIZE" bytes are required for " - "%"PRIu16" slaves.", subtype, slaves_size, - bundle->n_slaves * sizeof(ovs_be16), bundle->n_slaves); - error = OFPERR_OFPBAC_BAD_LEN; - } - - for (i = 0; i < bundle->n_slaves; i++) { - uint16_t ofp_port = ntohs(((ovs_be16 *)(nab + 1))[i]); - ofpbuf_put(ofpacts, &ofp_port, sizeof ofp_port); - } - - bundle = ofpacts->frame; - ofpact_update_len(ofpacts, &bundle->ofpact); - - if (!error) { - error = bundle_check(bundle, OFPP_MAX, NULL); - } - return error; -} - enum ofperr bundle_check(const struct ofpact_bundle *bundle, ofp_port_t max_ports, const struct flow *flow) @@ -222,164 +137,6 @@ bundle_check(const struct ofpact_bundle *bundle, ofp_port_t max_ports, return 0; } -void -bundle_to_nxast(const struct ofpact_bundle *bundle, struct ofpbuf *openflow) -{ - int slaves_len = ROUND_UP(2 * bundle->n_slaves, OFP_ACTION_ALIGN); - struct nx_action_bundle *nab; - ovs_be16 *slaves; - size_t i; - - nab = (bundle->dst.field - ? ofputil_put_NXAST_BUNDLE_LOAD(openflow) - : ofputil_put_NXAST_BUNDLE(openflow)); - nab->len = htons(ntohs(nab->len) + slaves_len); - nab->algorithm = htons(bundle->algorithm); - nab->fields = htons(bundle->fields); - nab->basis = htons(bundle->basis); - nab->slave_type = htonl(NXM_OF_IN_PORT); - nab->n_slaves = htons(bundle->n_slaves); - if (bundle->dst.field) { - nab->ofs_nbits = nxm_encode_ofs_nbits(bundle->dst.ofs, - bundle->dst.n_bits); - nab->dst = htonl(bundle->dst.field->nxm_header); - } - - slaves = ofpbuf_put_zeros(openflow, slaves_len); - for (i = 0; i < bundle->n_slaves; i++) { - slaves[i] = htons(ofp_to_u16(bundle->slaves[i])); - } -} - -/* Helper for bundle_parse and bundle_parse_load. - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string.*/ -static char * WARN_UNUSED_RESULT -bundle_parse__(const char *s, char **save_ptr, - const char *fields, const char *basis, const char *algorithm, - const char *slave_type, const char *dst, - const char *slave_delim, struct ofpbuf *ofpacts) -{ - struct ofpact_bundle *bundle; - - if (!slave_delim) { - return xasprintf("%s: not enough arguments to bundle action", s); - } - - if (strcasecmp(slave_delim, "slaves")) { - return xasprintf("%s: missing slave delimiter, expected `slaves' " - "got `%s'", s, slave_delim); - } - - bundle = ofpact_put_BUNDLE(ofpacts); - - for (;;) { - ofp_port_t slave_port; - char *slave; - - slave = strtok_r(NULL, ", []", save_ptr); - if (!slave || bundle->n_slaves >= BUNDLE_MAX_SLAVES) { - break; - } - - if (!ofputil_port_from_string(slave, &slave_port)) { - return xasprintf("%s: bad port number", slave); - } - ofpbuf_put(ofpacts, &slave_port, sizeof slave_port); - - bundle = ofpacts->frame; - bundle->n_slaves++; - } - ofpact_update_len(ofpacts, &bundle->ofpact); - - bundle->basis = atoi(basis); - - if (!strcasecmp(fields, "eth_src")) { - bundle->fields = NX_HASH_FIELDS_ETH_SRC; - } else if (!strcasecmp(fields, "symmetric_l4")) { - bundle->fields = NX_HASH_FIELDS_SYMMETRIC_L4; - } else { - return xasprintf("%s: unknown fields `%s'", s, fields); - } - - if (!strcasecmp(algorithm, "active_backup")) { - bundle->algorithm = NX_BD_ALG_ACTIVE_BACKUP; - } else if (!strcasecmp(algorithm, "hrw")) { - bundle->algorithm = NX_BD_ALG_HRW; - } else { - return xasprintf("%s: unknown algorithm `%s'", s, algorithm); - } - - if (strcasecmp(slave_type, "ofport")) { - return xasprintf("%s: unknown slave_type `%s'", s, slave_type); - } - - if (dst) { - char *error = mf_parse_subfield(&bundle->dst, dst); - if (error) { - return error; - } - } - - return NULL; -} - -/* Converts a bundle action string contained in 's' to an nx_action_bundle and - * stores it in 'b'. Sets 'b''s l2 pointer to NULL. - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string. */ -char * WARN_UNUSED_RESULT -bundle_parse(const char *s, struct ofpbuf *ofpacts) -{ - char *fields, *basis, *algorithm, *slave_type, *slave_delim; - char *tokstr, *save_ptr; - char *error; - - save_ptr = NULL; - tokstr = xstrdup(s); - fields = strtok_r(tokstr, ", ", &save_ptr); - basis = strtok_r(NULL, ", ", &save_ptr); - algorithm = strtok_r(NULL, ", ", &save_ptr); - slave_type = strtok_r(NULL, ", ", &save_ptr); - slave_delim = strtok_r(NULL, ": ", &save_ptr); - - error = bundle_parse__(s, &save_ptr, fields, basis, algorithm, slave_type, - NULL, slave_delim, ofpacts); - free(tokstr); - - return error; -} - -/* Converts a bundle_load action string contained in 's' to an nx_action_bundle - * and stores it in 'b'. Sets 'b''s l2 pointer to NULL. - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string.*/ -char * WARN_UNUSED_RESULT -bundle_parse_load(const char *s, struct ofpbuf *ofpacts) -{ - char *fields, *basis, *algorithm, *slave_type, *dst, *slave_delim; - char *tokstr, *save_ptr; - char *error; - - save_ptr = NULL; - tokstr = xstrdup(s); - fields = strtok_r(tokstr, ", ", &save_ptr); - basis = strtok_r(NULL, ", ", &save_ptr); - algorithm = strtok_r(NULL, ", ", &save_ptr); - slave_type = strtok_r(NULL, ", ", &save_ptr); - dst = strtok_r(NULL, ", ", &save_ptr); - slave_delim = strtok_r(NULL, ": ", &save_ptr); - - error = bundle_parse__(s, &save_ptr, fields, basis, algorithm, slave_type, - dst, slave_delim, ofpacts); - - free(tokstr); - - return error; -} /* Appends a human-readable representation of 'nab' to 's'. */ void diff --git a/lib/bundle.h b/lib/bundle.h index dceb6e583..c2a22f08c 100644 --- a/lib/bundle.h +++ b/lib/bundle.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011, 2012, 2013 Nicira, Inc. +/* Copyright (c) 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,15 +36,14 @@ struct ofpbuf; * * See include/openflow/nicira-ext.h for NXAST_BUNDLE specification. */ +#define BUNDLE_MAX_SLAVES 2048 + ofp_port_t bundle_execute(const struct ofpact_bundle *, const struct flow *, struct flow_wildcards *wc, bool (*slave_enabled)(ofp_port_t ofp_port, void *aux), void *aux); -enum ofperr bundle_from_openflow(const struct nx_action_bundle *, - struct ofpbuf *ofpact); enum ofperr bundle_check(const struct ofpact_bundle *, ofp_port_t max_ports, const struct flow *); -void bundle_to_nxast(const struct ofpact_bundle *, struct ofpbuf *of10); char *bundle_parse(const char *, struct ofpbuf *ofpacts) WARN_UNUSED_RESULT; char *bundle_parse_load(const char *, struct ofpbuf *ofpacts) WARN_UNUSED_RESULT; diff --git a/lib/learn.c b/lib/learn.c index c5797ebba..e1c73cb50 100644 --- a/lib/learn.c +++ b/lib/learn.c @@ -30,145 +30,6 @@ #include "openflow/openflow.h" #include "unaligned.h" -static ovs_be16 -get_be16(const void **pp) -{ - const ovs_be16 *p = *pp; - ovs_be16 value = *p; - *pp = p + 1; - return value; -} - -static ovs_be32 -get_be32(const void **pp) -{ - const ovs_be32 *p = *pp; - ovs_be32 value = get_unaligned_be32(p); - *pp = p + 1; - return value; -} - -static void -get_subfield(int n_bits, const void **p, struct mf_subfield *sf) -{ - sf->field = mf_from_nxm_header(ntohl(get_be32(p))); - sf->ofs = ntohs(get_be16(p)); - sf->n_bits = n_bits; -} - -static unsigned int -learn_min_len(uint16_t header) -{ - int n_bits = header & NX_LEARN_N_BITS_MASK; - int src_type = header & NX_LEARN_SRC_MASK; - int dst_type = header & NX_LEARN_DST_MASK; - unsigned int min_len; - - min_len = 0; - if (src_type == NX_LEARN_SRC_FIELD) { - min_len += sizeof(ovs_be32); /* src_field */ - min_len += sizeof(ovs_be16); /* src_ofs */ - } else { - min_len += DIV_ROUND_UP(n_bits, 16); - } - if (dst_type == NX_LEARN_DST_MATCH || - dst_type == NX_LEARN_DST_LOAD) { - min_len += sizeof(ovs_be32); /* dst_field */ - min_len += sizeof(ovs_be16); /* dst_ofs */ - } - return min_len; -} - -/* Converts 'nal' into a "struct ofpact_learn" and appends that struct to - * 'ofpacts'. Returns 0 if successful, otherwise an OFPERR_*. */ -enum ofperr -learn_from_openflow(const struct nx_action_learn *nal, struct ofpbuf *ofpacts) -{ - struct ofpact_learn *learn; - const void *p, *end; - - if (nal->pad) { - return OFPERR_OFPBAC_BAD_ARGUMENT; - } - - learn = ofpact_put_LEARN(ofpacts); - - learn->idle_timeout = ntohs(nal->idle_timeout); - learn->hard_timeout = ntohs(nal->hard_timeout); - learn->priority = ntohs(nal->priority); - learn->cookie = nal->cookie; - learn->table_id = nal->table_id; - learn->fin_idle_timeout = ntohs(nal->fin_idle_timeout); - learn->fin_hard_timeout = ntohs(nal->fin_hard_timeout); - - learn->flags = ntohs(nal->flags); - if (learn->flags & ~(NX_LEARN_F_SEND_FLOW_REM | - NX_LEARN_F_DELETE_LEARNED)) { - return OFPERR_OFPBAC_BAD_ARGUMENT; - } - - if (learn->table_id == 0xff) { - return OFPERR_OFPBAC_BAD_ARGUMENT; - } - - end = (char *) nal + ntohs(nal->len); - for (p = nal + 1; p != end; ) { - struct ofpact_learn_spec *spec; - uint16_t header = ntohs(get_be16(&p)); - - if (!header) { - break; - } - - spec = ofpbuf_put_zeros(ofpacts, sizeof *spec); - learn = ofpacts->frame; - learn->n_specs++; - - spec->src_type = header & NX_LEARN_SRC_MASK; - spec->dst_type = header & NX_LEARN_DST_MASK; - spec->n_bits = header & NX_LEARN_N_BITS_MASK; - - /* Check for valid src and dst type combination. */ - if (spec->dst_type == NX_LEARN_DST_MATCH || - spec->dst_type == NX_LEARN_DST_LOAD || - (spec->dst_type == NX_LEARN_DST_OUTPUT && - spec->src_type == NX_LEARN_SRC_FIELD)) { - /* OK. */ - } else { - return OFPERR_OFPBAC_BAD_ARGUMENT; - } - - /* Check that the arguments don't overrun the end of the action. */ - if ((char *) end - (char *) p < learn_min_len(header)) { - return OFPERR_OFPBAC_BAD_LEN; - } - - /* Get the source. */ - if (spec->src_type == NX_LEARN_SRC_FIELD) { - get_subfield(spec->n_bits, &p, &spec->src); - } else { - int p_bytes = 2 * DIV_ROUND_UP(spec->n_bits, 16); - - bitwise_copy(p, p_bytes, 0, - &spec->src_imm, sizeof spec->src_imm, 0, - spec->n_bits); - p = (const uint8_t *) p + p_bytes; - } - - /* Get the destination. */ - if (spec->dst_type == NX_LEARN_DST_MATCH || - spec->dst_type == NX_LEARN_DST_LOAD) { - get_subfield(spec->n_bits, &p, &spec->dst); - } - } - ofpact_update_len(ofpacts, &learn->ofpact); - - if (!is_all_zeros(p, (char *) end - (char *) p)) { - return OFPERR_OFPBAC_BAD_ARGUMENT; - } - - return 0; -} /* Checks that 'learn' is a valid action on 'flow'. Returns 0 if it is valid, * otherwise an OFPERR_*. */ @@ -216,79 +77,6 @@ learn_check(const struct ofpact_learn *learn, const struct flow *flow) return 0; } -static void -put_be16(struct ofpbuf *b, ovs_be16 x) -{ - ofpbuf_put(b, &x, sizeof x); -} - -static void -put_be32(struct ofpbuf *b, ovs_be32 x) -{ - ofpbuf_put(b, &x, sizeof x); -} - -static void -put_u16(struct ofpbuf *b, uint16_t x) -{ - put_be16(b, htons(x)); -} - -static void -put_u32(struct ofpbuf *b, uint32_t x) -{ - put_be32(b, htonl(x)); -} - -/* Converts 'learn' into a "struct nx_action_learn" and appends that action to - * 'ofpacts'. */ -void -learn_to_nxast(const struct ofpact_learn *learn, struct ofpbuf *openflow) -{ - const struct ofpact_learn_spec *spec; - struct nx_action_learn *nal; - size_t start_ofs; - - start_ofs = ofpbuf_size(openflow); - nal = ofputil_put_NXAST_LEARN(openflow); - nal->idle_timeout = htons(learn->idle_timeout); - nal->hard_timeout = htons(learn->hard_timeout); - nal->fin_idle_timeout = htons(learn->fin_idle_timeout); - nal->fin_hard_timeout = htons(learn->fin_hard_timeout); - nal->priority = htons(learn->priority); - nal->cookie = learn->cookie; - nal->flags = htons(learn->flags); - nal->table_id = learn->table_id; - - for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) { - put_u16(openflow, spec->n_bits | spec->dst_type | spec->src_type); - - if (spec->src_type == NX_LEARN_SRC_FIELD) { - put_u32(openflow, spec->src.field->nxm_header); - put_u16(openflow, spec->src.ofs); - } else { - size_t n_dst_bytes = 2 * DIV_ROUND_UP(spec->n_bits, 16); - uint8_t *bits = ofpbuf_put_zeros(openflow, n_dst_bytes); - bitwise_copy(&spec->src_imm, sizeof spec->src_imm, 0, - bits, n_dst_bytes, 0, - spec->n_bits); - } - - if (spec->dst_type == NX_LEARN_DST_MATCH || - spec->dst_type == NX_LEARN_DST_LOAD) { - put_u32(openflow, spec->dst.field->nxm_header); - put_u16(openflow, spec->dst.ofs); - } - } - - if ((ofpbuf_size(openflow) - start_ofs) % 8) { - ofpbuf_put_zeros(openflow, 8 - (ofpbuf_size(openflow) - start_ofs) % 8); - } - - nal = ofpbuf_at_assert(openflow, start_ofs, sizeof *nal); - nal->len = htons(ofpbuf_size(openflow) - start_ofs); -} - /* Composes 'fm' so that executing it will implement 'learn' given that the * packet being processed has 'flow' as its flow. * diff --git a/lib/learn.h b/lib/learn.h index 0e676fe98..bedd2c701 100644 --- a/lib/learn.h +++ b/lib/learn.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, 2013 Nicira, Inc. + * Copyright (c) 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,11 +33,7 @@ struct nx_action_learn; * See include/openflow/nicira-ext.h for NXAST_LEARN specification. */ -enum ofperr learn_from_openflow(const struct nx_action_learn *, - struct ofpbuf *ofpacts); enum ofperr learn_check(const struct ofpact_learn *, const struct flow *); -void learn_to_nxast(const struct ofpact_learn *, struct ofpbuf *openflow); - void learn_execute(const struct ofpact_learn *, const struct flow *, struct ofputil_flow_mod *, struct ofpbuf *ofpacts); void learn_mask(const struct ofpact_learn *, struct flow_wildcards *); diff --git a/lib/multipath.c b/lib/multipath.c index a6f549ca6..96d6898ca 100644 --- a/lib/multipath.c +++ b/lib/multipath.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2011, 2012, 2013 Nicira, Inc. + * Copyright (c) 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,49 +28,7 @@ #include "ofp-util.h" #include "openflow/nicira-ext.h" #include "packets.h" -#include "vlog.h" - -VLOG_DEFINE_THIS_MODULE(multipath); - -static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); -/* Converts 'nam' into 'mp'. Returns 0 if successful, otherwise an - * OFPERR_*. */ -enum ofperr -multipath_from_openflow(const struct nx_action_multipath *nam, - struct ofpact_multipath *mp) -{ - uint32_t n_links = ntohs(nam->max_link) + 1; - size_t min_n_bits = log_2_ceil(n_links); - - ofpact_init_MULTIPATH(mp); - mp->fields = ntohs(nam->fields); - mp->basis = ntohs(nam->basis); - mp->algorithm = ntohs(nam->algorithm); - mp->max_link = ntohs(nam->max_link); - mp->arg = ntohl(nam->arg); - mp->dst.field = mf_from_nxm_header(ntohl(nam->dst)); - mp->dst.ofs = nxm_decode_ofs(nam->ofs_nbits); - mp->dst.n_bits = nxm_decode_n_bits(nam->ofs_nbits); - - if (!flow_hash_fields_valid(mp->fields)) { - VLOG_WARN_RL(&rl, "unsupported fields %d", (int) mp->fields); - return OFPERR_OFPBAC_BAD_ARGUMENT; - } else if (mp->algorithm != NX_MP_ALG_MODULO_N - && mp->algorithm != NX_MP_ALG_HASH_THRESHOLD - && mp->algorithm != NX_MP_ALG_HRW - && mp->algorithm != NX_MP_ALG_ITER_HASH) { - VLOG_WARN_RL(&rl, "unsupported algorithm %d", (int) mp->algorithm); - return OFPERR_OFPBAC_BAD_ARGUMENT; - } else if (mp->dst.n_bits < min_n_bits) { - VLOG_WARN_RL(&rl, "multipath action requires at least %"PRIuSIZE" bits for " - "%"PRIu32" links", min_n_bits, n_links); - return OFPERR_OFPBAC_BAD_ARGUMENT; - } - - return multipath_check(mp, NULL); -} - /* Checks that 'mp' is valid on flow. Returns 0 if it is valid, otherwise an * OFPERR_*. */ enum ofperr @@ -79,22 +37,6 @@ multipath_check(const struct ofpact_multipath *mp, { return mf_check_dst(&mp->dst, flow); } - -/* Converts 'mp' into an OpenFlow NXAST_MULTIPATH action, which it appends to - * 'openflow'. */ -void -multipath_to_nxast(const struct ofpact_multipath *mp, struct ofpbuf *openflow) -{ - struct nx_action_multipath *nam = ofputil_put_NXAST_MULTIPATH(openflow); - - nam->fields = htons(mp->fields); - nam->basis = htons(mp->basis); - nam->algorithm = htons(mp->algorithm); - nam->max_link = htons(mp->max_link); - nam->arg = htonl(mp->arg); - nam->ofs_nbits = nxm_encode_ofs_nbits(mp->dst.ofs, mp->dst.n_bits); - nam->dst = htonl(mp->dst.field->nxm_header); -} /* multipath_execute(). */ @@ -192,84 +134,6 @@ multipath_algorithm(uint32_t hash, enum nx_mp_algorithm algorithm, OVS_NOT_REACHED(); } -/* Parses 's_' as a set of arguments to the "multipath" action and initializes - * 'mp' accordingly. ovs-ofctl(8) describes the format parsed. - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string.*/ -static char * WARN_UNUSED_RESULT -multipath_parse__(struct ofpact_multipath *mp, const char *s_, char *s) -{ - char *save_ptr = NULL; - char *fields, *basis, *algorithm, *n_links_str, *arg, *dst; - char *error; - int n_links; - - fields = strtok_r(s, ", ", &save_ptr); - basis = strtok_r(NULL, ", ", &save_ptr); - algorithm = strtok_r(NULL, ", ", &save_ptr); - n_links_str = strtok_r(NULL, ", ", &save_ptr); - arg = strtok_r(NULL, ", ", &save_ptr); - dst = strtok_r(NULL, ", ", &save_ptr); - if (!dst) { - return xasprintf("%s: not enough arguments to multipath action", s_); - } - - ofpact_init_MULTIPATH(mp); - if (!strcasecmp(fields, "eth_src")) { - mp->fields = NX_HASH_FIELDS_ETH_SRC; - } else if (!strcasecmp(fields, "symmetric_l4")) { - mp->fields = NX_HASH_FIELDS_SYMMETRIC_L4; - } else { - return xasprintf("%s: unknown fields `%s'", s_, fields); - } - mp->basis = atoi(basis); - if (!strcasecmp(algorithm, "modulo_n")) { - mp->algorithm = NX_MP_ALG_MODULO_N; - } else if (!strcasecmp(algorithm, "hash_threshold")) { - mp->algorithm = NX_MP_ALG_HASH_THRESHOLD; - } else if (!strcasecmp(algorithm, "hrw")) { - mp->algorithm = NX_MP_ALG_HRW; - } else if (!strcasecmp(algorithm, "iter_hash")) { - mp->algorithm = NX_MP_ALG_ITER_HASH; - } else { - return xasprintf("%s: unknown algorithm `%s'", s_, algorithm); - } - n_links = atoi(n_links_str); - if (n_links < 1 || n_links > 65536) { - return xasprintf("%s: n_links %d is not in valid range 1 to 65536", - s_, n_links); - } - mp->max_link = n_links - 1; - mp->arg = atoi(arg); - - error = mf_parse_subfield(&mp->dst, dst); - if (error) { - return error; - } - if (mp->dst.n_bits < 16 && n_links > (1u << mp->dst.n_bits)) { - return xasprintf("%s: %d-bit destination field has %u possible " - "values, less than specified n_links %d", - s_, mp->dst.n_bits, 1u << mp->dst.n_bits, n_links); - } - - return NULL; -} - -/* Parses 's_' as a set of arguments to the "multipath" action and initializes - * 'mp' accordingly. ovs-ofctl(8) describes the format parsed. - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string. */ -char * WARN_UNUSED_RESULT -multipath_parse(struct ofpact_multipath *mp, const char *s_) -{ - char *s = xstrdup(s_); - char *error = multipath_parse__(mp, s_, s); - free(s); - return error; -} - /* Appends a description of 'mp' to 's', in the format that ovs-ofctl(8) * describes. */ void diff --git a/lib/multipath.h b/lib/multipath.h index 067305025..e1e2e2fff 100644 --- a/lib/multipath.h +++ b/lib/multipath.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2011, 2012, 2013 Nicira, Inc. + * Copyright (c) 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,17 +28,10 @@ struct nx_action_multipath; struct ofpact_multipath; struct ofpbuf; -/* NXAST_MULTIPATH helper functions. - * - * See include/openflow/nicira-ext.h for NXAST_MULTIPATH specification. - */ +/* NXAST_MULTIPATH helper functions. */ -enum ofperr multipath_from_openflow(const struct nx_action_multipath *, - struct ofpact_multipath *); enum ofperr multipath_check(const struct ofpact_multipath *, const struct flow *); -void multipath_to_nxast(const struct ofpact_multipath *, - struct ofpbuf *openflow); void multipath_execute(const struct ofpact_multipath *, struct flow *, struct flow_wildcards *); diff --git a/lib/nx-match.c b/lib/nx-match.c index 678e6f33a..e2846cd9f 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -1080,37 +1080,6 @@ nxm_parse_reg_move(struct ofpact_reg_move *move, const char *s) } return NULL; } - -/* Parses 's' as a "load" action, in the form described in ovs-ofctl(8), into - * '*load'. - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string. */ -char * WARN_UNUSED_RESULT -nxm_parse_reg_load(struct ofpact_reg_load *load, const char *s) -{ - const char *full_s = s; - uint64_t value = strtoull(s, (char **) &s, 0); - char *error; - - if (strncmp(s, "->", 2)) { - return xasprintf("%s: missing `->' following value", full_s); - } - s += 2; - error = mf_parse_subfield(&load->dst, s); - if (error) { - return error; - } - - if (load->dst.n_bits < 64 && (value >> load->dst.n_bits) != 0) { - return xasprintf("%s: value %"PRIu64" does not fit into %d bits", - full_s, value, load->dst.n_bits); - } - - load->subvalue.be64[0] = htonll(0); - load->subvalue.be64[1] = htonll(value); - return NULL; -} /* nxm_format_reg_move(), nxm_format_reg_load(). */ @@ -1133,45 +1102,6 @@ nxm_format_reg_load(const struct ofpact_reg_load *load, struct ds *s) } enum ofperr -nxm_reg_move_from_openflow(const struct nx_action_reg_move *narm, - struct ofpbuf *ofpacts) -{ - struct ofpact_reg_move *move; - - move = ofpact_put_REG_MOVE(ofpacts); - move->src.field = mf_from_nxm_header(ntohl(narm->src)); - move->src.ofs = ntohs(narm->src_ofs); - move->src.n_bits = ntohs(narm->n_bits); - move->dst.field = mf_from_nxm_header(ntohl(narm->dst)); - move->dst.ofs = ntohs(narm->dst_ofs); - move->dst.n_bits = ntohs(narm->n_bits); - - return nxm_reg_move_check(move, NULL); -} - -enum ofperr -nxm_reg_load_from_openflow(const struct nx_action_reg_load *narl, - struct ofpbuf *ofpacts) -{ - struct ofpact_reg_load *load; - - load = ofpact_put_REG_LOAD(ofpacts); - load->dst.field = mf_from_nxm_header(ntohl(narl->dst)); - load->dst.ofs = nxm_decode_ofs(narl->ofs_nbits); - load->dst.n_bits = nxm_decode_n_bits(narl->ofs_nbits); - load->subvalue.be64[1] = narl->value; - - /* Reject 'narl' if a bit numbered 'n_bits' or higher is set to 1 in - * narl->value. */ - if (load->dst.n_bits < 64 && - ntohll(narl->value) >> load->dst.n_bits) { - return OFPERR_OFPBAC_BAD_ARGUMENT; - } - - return nxm_reg_load_check(load, NULL); -} - -enum ofperr nxm_reg_move_check(const struct ofpact_reg_move *move, const struct flow *flow) { enum ofperr error; @@ -1190,31 +1120,6 @@ nxm_reg_load_check(const struct ofpact_reg_load *load, const struct flow *flow) return mf_check_dst(&load->dst, flow); } -void -nxm_reg_move_to_nxast(const struct ofpact_reg_move *move, - struct ofpbuf *openflow) -{ - struct nx_action_reg_move *narm; - - narm = ofputil_put_NXAST_REG_MOVE(openflow); - narm->n_bits = htons(move->dst.n_bits); - narm->src_ofs = htons(move->src.ofs); - narm->dst_ofs = htons(move->dst.ofs); - narm->src = htonl(move->src.field->nxm_header); - narm->dst = htonl(move->dst.field->nxm_header); -} - -void -nxm_reg_load_to_nxast(const struct ofpact_reg_load *load, - struct ofpbuf *openflow) -{ - struct nx_action_reg_load *narl; - - narl = ofputil_put_NXAST_REG_LOAD(openflow); - narl->ofs_nbits = nxm_encode_ofs_nbits(load->dst.ofs, load->dst.n_bits); - narl->dst = htonl(load->dst.field->nxm_header); - narl->value = load->subvalue.be64[1]; -} /* nxm_execute_reg_move(), nxm_execute_reg_load(). */ @@ -1322,49 +1227,6 @@ nxm_format_stack_pop(const struct ofpact_stack *pop, struct ds *s) mf_format_subfield(&pop->subfield, s); } -/* Common set for both push and pop actions. */ -static void -stack_action_from_openflow__(const struct nx_action_stack *nasp, - struct ofpact_stack *stack_action) -{ - stack_action->subfield.field = mf_from_nxm_header(ntohl(nasp->field)); - stack_action->subfield.ofs = ntohs(nasp->offset); - stack_action->subfield.n_bits = ntohs(nasp->n_bits); -} - -static void -nxm_stack_to_nxast__(const struct ofpact_stack *stack_action, - struct nx_action_stack *nasp) -{ - nasp->offset = htons(stack_action->subfield.ofs); - nasp->n_bits = htons(stack_action->subfield.n_bits); - nasp->field = htonl(stack_action->subfield.field->nxm_header); -} - -enum ofperr -nxm_stack_push_from_openflow(const struct nx_action_stack *nasp, - struct ofpbuf *ofpacts) -{ - struct ofpact_stack *push; - - push = ofpact_put_STACK_PUSH(ofpacts); - stack_action_from_openflow__(nasp, push); - - return nxm_stack_push_check(push, NULL); -} - -enum ofperr -nxm_stack_pop_from_openflow(const struct nx_action_stack *nasp, - struct ofpbuf *ofpacts) -{ - struct ofpact_stack *pop; - - pop = ofpact_put_STACK_POP(ofpacts); - stack_action_from_openflow__(nasp, pop); - - return nxm_stack_pop_check(pop, NULL); -} - enum ofperr nxm_stack_push_check(const struct ofpact_stack *push, const struct flow *flow) @@ -1379,20 +1241,6 @@ nxm_stack_pop_check(const struct ofpact_stack *pop, return mf_check_dst(&pop->subfield, flow); } -void -nxm_stack_push_to_nxast(const struct ofpact_stack *stack, - struct ofpbuf *openflow) -{ - nxm_stack_to_nxast__(stack, ofputil_put_NXAST_STACK_PUSH(openflow)); -} - -void -nxm_stack_pop_to_nxast(const struct ofpact_stack *stack, - struct ofpbuf *openflow) -{ - nxm_stack_to_nxast__(stack, ofputil_put_NXAST_STACK_POP(openflow)); -} - /* nxm_execute_stack_push(), nxm_execute_stack_pop(). */ static void nx_stack_push(struct ofpbuf *stack, union mf_subvalue *v) diff --git a/lib/nx-match.h b/lib/nx-match.h index 077f299b8..34adbcb0e 100644 --- a/lib/nx-match.h +++ b/lib/nx-match.h @@ -61,27 +61,15 @@ int oxm_match_from_string(const char *, struct ofpbuf *); char *nxm_parse_reg_move(struct ofpact_reg_move *, const char *) WARN_UNUSED_RESULT; -char *nxm_parse_reg_load(struct ofpact_reg_load *, const char *) - WARN_UNUSED_RESULT; void nxm_format_reg_move(const struct ofpact_reg_move *, struct ds *); void nxm_format_reg_load(const struct ofpact_reg_load *, struct ds *); -enum ofperr nxm_reg_move_from_openflow(const struct nx_action_reg_move *, - struct ofpbuf *ofpacts); -enum ofperr nxm_reg_load_from_openflow(const struct nx_action_reg_load *, - struct ofpbuf *ofpacts); - enum ofperr nxm_reg_move_check(const struct ofpact_reg_move *, const struct flow *); enum ofperr nxm_reg_load_check(const struct ofpact_reg_load *, const struct flow *); -void nxm_reg_move_to_nxast(const struct ofpact_reg_move *, - struct ofpbuf *openflow); -void nxm_reg_load_to_nxast(const struct ofpact_reg_load *, - struct ofpbuf *openflow); - void nxm_execute_reg_move(const struct ofpact_reg_move *, struct flow *, struct flow_wildcards *); void nxm_execute_reg_load(const struct ofpact_reg_load *, struct flow *, @@ -95,20 +83,11 @@ char *nxm_parse_stack_action(struct ofpact_stack *, const char *) void nxm_format_stack_push(const struct ofpact_stack *, struct ds *); void nxm_format_stack_pop(const struct ofpact_stack *, struct ds *); -enum ofperr nxm_stack_push_from_openflow(const struct nx_action_stack *, - struct ofpbuf *ofpacts); -enum ofperr nxm_stack_pop_from_openflow(const struct nx_action_stack *, - struct ofpbuf *ofpacts); enum ofperr nxm_stack_push_check(const struct ofpact_stack *, const struct flow *); enum ofperr nxm_stack_pop_check(const struct ofpact_stack *, const struct flow *); -void nxm_stack_push_to_nxast(const struct ofpact_stack *, - struct ofpbuf *openflow); -void nxm_stack_pop_to_nxast(const struct ofpact_stack *, - struct ofpbuf *openflow); - void nxm_execute_stack_push(const struct ofpact_stack *, const struct flow *, struct flow_wildcards *, struct ofpbuf *); diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c index 5270b9441..fbf16ebda 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -20,18 +20,1281 @@ #include "byte-order.h" #include "compiler.h" #include "dynamic-string.h" +#include "hmap.h" #include "learn.h" #include "meta-flow.h" #include "multipath.h" #include "nx-match.h" +#include "ofp-parse.h" #include "ofp-util.h" #include "ofpbuf.h" +#include "unaligned.h" #include "util.h" #include "vlog.h" VLOG_DEFINE_THIS_MODULE(ofp_actions); static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + +#define OFP_ACTION_ALIGN 8 + +#define OFPAT_VENDOR 0xffff + +/* Action header for OFPAT10_VENDOR and OFPAT11_EXPERIMEMNTER. + * The rest of the body is vendor-defined. */ +struct ofp_action_vendor_header { + ovs_be16 type; /* OFPAT10_VENDOR. */ + ovs_be16 len; /* Length is a multiple of 8. */ + ovs_be32 vendor; /* Vendor ID, which takes the same form + as in "struct ofp_vendor_header". */ +}; +OFP_ASSERT(sizeof(struct ofp_action_vendor_header) == 8); + +/* Action header that is common to all actions. The length includes the + * header and any padding used to make the action 64-bit aligned. + * NB: The length of an action *must* always be a multiple of eight. */ +struct ofp_action_header { + ovs_be16 type; /* One of OFPAT*. */ + ovs_be16 len; /* Length of action, including this + header. This is the length of action, + including any padding to make it + 64-bit aligned. */ + uint8_t pad[4]; +}; +OFP_ASSERT(sizeof(struct ofp_action_header) == 8); + +/* Header for Nicira-defined actions. */ +struct nx_action_header { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* Length is 16. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_*. */ + uint8_t pad[6]; +}; +OFP_ASSERT(sizeof(struct nx_action_header) == 16); + +/* Action structure for OFPAT10_OUTPUT, which sends packets out 'port'. + * When the 'port' is the OFPP_CONTROLLER, 'max_len' indicates the max + * number of bytes to send. A 'max_len' of zero means no bytes of the + * packet should be sent. */ +struct ofp10_action_output { + ovs_be16 type; /* OFPAT10_OUTPUT. */ + ovs_be16 len; /* Length is 8. */ + ovs_be16 port; /* Output port. */ + ovs_be16 max_len; /* Max length to send to controller. */ +}; +OFP_ASSERT(sizeof(struct ofp10_action_output) == 8); + +/* Action structure for OFPAT_OUTPUT, which sends packets out 'port'. + * When the 'port' is the OFPP_CONTROLLER, 'max_len' indicates the max + * number of bytes to send. A 'max_len' of zero means no bytes of the + * packet should be sent.*/ +struct ofp11_action_output { + ovs_be16 type; /* OFPAT11_OUTPUT. */ + ovs_be16 len; /* Length is 16. */ + ovs_be32 port; /* Output port. */ + ovs_be16 max_len; /* Max length to send to controller. */ + uint8_t pad[6]; /* Pad to 64 bits. */ +}; +OFP_ASSERT(sizeof(struct ofp11_action_output) == 16); + +/* OFPAT10_ENQUEUE action struct: send packets to given queue on port. */ +struct ofp10_action_enqueue { + ovs_be16 type; /* OFPAT10_ENQUEUE. */ + ovs_be16 len; /* Len is 16. */ + ovs_be16 port; /* Port that queue belongs. Should + refer to a valid physical port + (i.e. < OFPP_MAX) or OFPP_IN_PORT. */ + uint8_t pad[6]; /* Pad for 64-bit alignment. */ + ovs_be32 queue_id; /* Where to enqueue the packets. */ +}; +OFP_ASSERT(sizeof(struct ofp10_action_enqueue) == 16); + +/* Action structure for OFPAT10_SET_DL_SRC/DST and OFPAT11_SET_DL_SRC/DST. */ +struct ofp_action_dl_addr { + ovs_be16 type; /* Type. */ + ovs_be16 len; /* Length is 16. */ + uint8_t dl_addr[OFP_ETH_ALEN]; /* Ethernet address. */ + uint8_t pad[6]; +}; +OFP_ASSERT(sizeof(struct ofp_action_dl_addr) == 16); + +/* Action structure for NXAST_DEC_TTL_CNT_IDS. + * + * If the packet is not IPv4 or IPv6, does nothing. For IPv4 or IPv6, if the + * TTL or hop limit is at least 2, decrements it by 1. Otherwise, if TTL or + * hop limit is 0 or 1, sends a packet-in to the controllers with each of the + * 'n_controllers' controller IDs specified in 'cnt_ids'. + * + * (This differs from NXAST_DEC_TTL in that for NXAST_DEC_TTL the packet-in is + * sent only to controllers with id 0.) + */ +struct nx_action_cnt_ids { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* Length including slaves. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_DEC_TTL_CNT_IDS. */ + + ovs_be16 n_controllers; /* Number of controllers. */ + uint8_t zeros[4]; /* Must be zero. */ + + /* Followed by 1 or more controller ids. + * + * uint16_t cnt_ids[]; // Controller ids. + * uint8_t pad[]; // Must be 0 to 8-byte align cnt_ids[]. + */ +}; +OFP_ASSERT(sizeof(struct nx_action_cnt_ids) == 16); + +/* Action structure for OFPAT12_SET_FIELD. */ +struct ofp12_action_set_field { + ovs_be16 type; /* OFPAT12_SET_FIELD. */ + ovs_be16 len; /* Length is padded to 64 bits. */ + ovs_be32 dst; /* OXM TLV header */ + /* Followed by: + * - Exactly ((oxm_len + 4) + 7)/8*8 - (oxm_len + 4) (between 0 and 7) + * bytes of all-zero bytes + */ +}; +OFP_ASSERT(sizeof(struct ofp12_action_set_field) == 8); + +/* Action structure for OFPAT_COPY_FIELD. */ +struct ofp15_action_copy_field { + ovs_be16 type; /* OFPAT_COPY_FIELD. */ + ovs_be16 len; /* Length is padded to 64 bits. */ + ovs_be16 n_bits; /* Number of bits to copy. */ + ovs_be16 src_offset; /* Starting bit offset in source. */ + ovs_be16 dst_offset; /* Starting bit offset in destination. */ + ovs_be16 oxm_id_len; /* Length of oxm_ids. */ + + /* OpenFlow allows for experimenter OXM fields whose expression is longer + * than a standard 32-bit OXM. Thus, in the OpenFlow specification, the + * following is variable-length. Open vSwitch does not yet support + * experimenter OXM fields, so until it does we leave these as fixed + * size. */ + ovs_be32 src; /* OXM for source field. */ + ovs_be32 dst; /* OXM for destination field. */ + uint8_t pad[4]; /* Must be zero. */ +}; +OFP_ASSERT(sizeof(struct ofp15_action_copy_field) == 24); + +/* Action structure for NXAST_REG_LOAD. + * + * Copies value[0:n_bits] to dst[ofs:ofs+n_bits], where a[b:c] denotes the bits + * within 'a' numbered 'b' through 'c' (not including bit 'c'). Bit numbering + * starts at 0 for the least-significant bit, 1 for the next most significant + * bit, and so on. + * + * 'dst' is an nxm_header with nxm_hasmask=0. See the documentation for + * NXAST_REG_MOVE, above, for the permitted fields and for the side effects of + * loading them. + * + * The 'ofs' and 'n_bits' fields are combined into a single 'ofs_nbits' field + * to avoid enlarging the structure by another 8 bytes. To allow 'n_bits' to + * take a value between 1 and 64 (inclusive) while taking up only 6 bits, it is + * also stored as one less than its true value: + * + * 15 6 5 0 + * +------------------------------+------------------+ + * | ofs | n_bits - 1 | + * +------------------------------+------------------+ + * + * The switch will reject actions for which ofs+n_bits is greater than the + * width of 'dst', or in which any bits in 'value' with value 2**n_bits or + * greater are set to 1, with error type OFPET_BAD_ACTION, code + * OFPBAC_BAD_ARGUMENT. + */ +struct nx_action_reg_load { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* Length is 24. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_REG_LOAD. */ + ovs_be16 ofs_nbits; /* (ofs << 6) | (n_bits - 1). */ + ovs_be32 dst; /* Destination register. */ + ovs_be64 value; /* Immediate value. */ +}; +OFP_ASSERT(sizeof(struct nx_action_reg_load) == 24); + +/* Action structure for NXAST_REG_MOVE. + * + * Copies src[src_ofs:src_ofs+n_bits] to dst[dst_ofs:dst_ofs+n_bits], where + * a[b:c] denotes the bits within 'a' numbered 'b' through 'c' (not including + * bit 'c'). Bit numbering starts at 0 for the least-significant bit, 1 for + * the next most significant bit, and so on. + * + * 'src' and 'dst' are nxm_header values with nxm_hasmask=0. (It doesn't make + * sense to use nxm_hasmask=1 because the action does not do any kind of + * matching; it uses the actual value of a field.) + * + * The following nxm_header values are potentially acceptable as 'src': + * + * - NXM_OF_IN_PORT + * - NXM_OF_ETH_DST + * - NXM_OF_ETH_SRC + * - NXM_OF_ETH_TYPE + * - NXM_OF_VLAN_TCI + * - NXM_OF_IP_TOS + * - NXM_OF_IP_PROTO + * - NXM_OF_IP_SRC + * - NXM_OF_IP_DST + * - NXM_OF_TCP_SRC + * - NXM_OF_TCP_DST + * - NXM_OF_UDP_SRC + * - NXM_OF_UDP_DST + * - NXM_OF_ICMP_TYPE + * - NXM_OF_ICMP_CODE + * - NXM_OF_ARP_OP + * - NXM_OF_ARP_SPA + * - NXM_OF_ARP_TPA + * - NXM_NX_TUN_ID + * - NXM_NX_ARP_SHA + * - NXM_NX_ARP_THA + * - NXM_NX_ICMPV6_TYPE + * - NXM_NX_ICMPV6_CODE + * - NXM_NX_ND_SLL + * - NXM_NX_ND_TLL + * - NXM_NX_REG(idx) for idx in the switch's accepted range. + * - NXM_NX_PKT_MARK + * - NXM_NX_TUN_IPV4_SRC + * - NXM_NX_TUN_IPV4_DST + * + * The following nxm_header values are potentially acceptable as 'dst': + * + * - NXM_OF_ETH_DST + * - NXM_OF_ETH_SRC + * - NXM_OF_IP_TOS + * - NXM_OF_IP_SRC + * - NXM_OF_IP_DST + * - NXM_OF_TCP_SRC + * - NXM_OF_TCP_DST + * - NXM_OF_UDP_SRC + * - NXM_OF_UDP_DST + * - NXM_NX_ARP_SHA + * - NXM_NX_ARP_THA + * - NXM_OF_ARP_OP + * - NXM_OF_ARP_SPA + * - NXM_OF_ARP_TPA + * Modifying any of the above fields changes the corresponding packet + * header. + * + * - NXM_OF_IN_PORT + * + * - NXM_NX_REG(idx) for idx in the switch's accepted range. + * + * - NXM_NX_PKT_MARK + * + * - NXM_OF_VLAN_TCI. Modifying this field's value has side effects on the + * packet's 802.1Q header. Setting a value with CFI=0 removes the 802.1Q + * header (if any), ignoring the other bits. Setting a value with CFI=1 + * adds or modifies the 802.1Q header appropriately, setting the TCI field + * to the field's new value (with the CFI bit masked out). + * + * - NXM_NX_TUN_ID, NXM_NX_TUN_IPV4_SRC, NXM_NX_TUN_IPV4_DST. Modifying + * any of these values modifies the corresponding tunnel header field used + * for the packet's next tunnel encapsulation, if allowed by the + * configuration of the output tunnel port. + * + * A given nxm_header value may be used as 'src' or 'dst' only on a flow whose + * nx_match satisfies its prerequisites. For example, NXM_OF_IP_TOS may be + * used only if the flow's nx_match includes an nxm_entry that specifies + * nxm_type=NXM_OF_ETH_TYPE, nxm_hasmask=0, and nxm_value=0x0800. + * + * The switch will reject actions for which src_ofs+n_bits is greater than the + * width of 'src' or dst_ofs+n_bits is greater than the width of 'dst' with + * error type OFPET_BAD_ACTION, code OFPBAC_BAD_ARGUMENT. + * + * This action behaves properly when 'src' overlaps with 'dst', that is, it + * behaves as if 'src' were copied out to a temporary buffer, then the + * temporary buffer copied to 'dst'. + */ +struct nx_action_reg_move { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* Length is 24. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_REG_MOVE. */ + ovs_be16 n_bits; /* Number of bits. */ + ovs_be16 src_ofs; /* Starting bit offset in source. */ + ovs_be16 dst_ofs; /* Starting bit offset in destination. */ + ovs_be32 src; /* Source register. */ + ovs_be32 dst; /* Destination register. */ +}; +OFP_ASSERT(sizeof(struct nx_action_reg_move) == 24); + +/* Action structures for NXAST_RESUBMIT and NXAST_RESUBMIT_TABLE. + * + * These actions search one of the switch's flow tables: + * + * - For NXAST_RESUBMIT_TABLE only, if the 'table' member is not 255, then + * it specifies the table to search. + * + * - Otherwise (for NXAST_RESUBMIT_TABLE with a 'table' of 255, or for + * NXAST_RESUBMIT regardless of 'table'), it searches the current flow + * table, that is, the OpenFlow flow table that contains the flow from + * which this action was obtained. If this action did not come from a + * flow table (e.g. it came from an OFPT_PACKET_OUT message), then table 0 + * is the current table. + * + * The flow table lookup uses a flow that may be slightly modified from the + * original lookup: + * + * - For NXAST_RESUBMIT, the 'in_port' member of struct nx_action_resubmit + * is used as the flow's in_port. + * + * - For NXAST_RESUBMIT_TABLE, if the 'in_port' member is not OFPP_IN_PORT, + * then its value is used as the flow's in_port. Otherwise, the original + * in_port is used. + * + * - If actions that modify the flow (e.g. OFPAT_SET_VLAN_VID) precede the + * resubmit action, then the flow is updated with the new values. + * + * Following the lookup, the original in_port is restored. + * + * If the modified flow matched in the flow table, then the corresponding + * actions are executed. Afterward, actions following the resubmit in the + * original set of actions, if any, are executed; any changes made to the + * packet (e.g. changes to VLAN) by secondary actions persist when those + * actions are executed, although the original in_port is restored. + * + * Resubmit actions may be used any number of times within a set of actions. + * + * Resubmit actions may nest to an implementation-defined depth. Beyond this + * implementation-defined depth, further resubmit actions are simply ignored. + * + * NXAST_RESUBMIT ignores 'table' and 'pad'. NXAST_RESUBMIT_TABLE requires + * 'pad' to be all-bits-zero. + * + * Open vSwitch 1.0.1 and earlier did not support recursion. Open vSwitch + * before 1.2.90 did not support NXAST_RESUBMIT_TABLE. + */ +struct nx_action_resubmit { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* Length is 16. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_RESUBMIT. */ + ovs_be16 in_port; /* New in_port for checking flow table. */ + uint8_t table; /* NXAST_RESUBMIT_TABLE: table to use. */ + uint8_t pad[3]; +}; +OFP_ASSERT(sizeof(struct nx_action_resubmit) == 16); + +/* Action structure for NXAST_NOTE. + * + * This action has no effect. It is variable length. The switch does not + * attempt to interpret the user-defined 'note' data in any way. A controller + * can use this action to attach arbitrary metadata to a flow. + * + * This action might go away in the future. + */ +struct nx_action_note { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* A multiple of 8, but at least 16. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_NOTE. */ + uint8_t note[6]; /* Start of user-defined data. */ + /* Possibly followed by additional user-defined data. */ +}; +OFP_ASSERT(sizeof(struct nx_action_note) == 16); + +/* Action structure for NXAST_MULTIPATH. + * + * This action performs the following steps in sequence: + * + * 1. Hashes the fields designated by 'fields', one of NX_HASH_FIELDS_*. + * Refer to the definition of "enum nx_mp_fields" for details. + * + * The 'basis' value is used as a universal hash parameter, that is, + * different values of 'basis' yield different hash functions. The + * particular universal hash function used is implementation-defined. + * + * The hashed fields' values are drawn from the current state of the + * flow, including all modifications that have been made by actions up to + * this point. + * + * 2. Applies the multipath link choice algorithm specified by 'algorithm', + * one of NX_MP_ALG_*. Refer to the definition of "enum nx_mp_algorithm" + * for details. + * + * The output of the algorithm is 'link', an unsigned integer less than + * or equal to 'max_link'. + * + * Some algorithms use 'arg' as an additional argument. + * + * 3. Stores 'link' in dst[ofs:ofs+n_bits]. The format and semantics of + * 'dst' and 'ofs_nbits' are similar to those for the NXAST_REG_LOAD + * action. + * + * The switch will reject actions that have an unknown 'fields', or an unknown + * 'algorithm', or in which ofs+n_bits is greater than the width of 'dst', or + * in which 'max_link' is greater than or equal to 2**n_bits, with error type + * OFPET_BAD_ACTION, code OFPBAC_BAD_ARGUMENT. + */ +struct nx_action_multipath { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* Length is 32. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_MULTIPATH. */ + + /* What fields to hash and how. */ + ovs_be16 fields; /* One of NX_HASH_FIELDS_*. */ + ovs_be16 basis; /* Universal hash parameter. */ + ovs_be16 pad0; + + /* Multipath link choice algorithm to apply to hash value. */ + ovs_be16 algorithm; /* One of NX_MP_ALG_*. */ + ovs_be16 max_link; /* Number of output links, minus 1. */ + ovs_be32 arg; /* Algorithm-specific argument. */ + ovs_be16 pad1; + + /* Where to store the result. */ + ovs_be16 ofs_nbits; /* (ofs << 6) | (n_bits - 1). */ + ovs_be32 dst; /* Destination. */ +}; +OFP_ASSERT(sizeof(struct nx_action_multipath) == 32); + + +/* Action structure for NXAST_BUNDLE and NXAST_BUNDLE_LOAD. + * + * The bundle actions choose a slave from a supplied list of options. + * NXAST_BUNDLE outputs to its selection. NXAST_BUNDLE_LOAD writes its + * selection to a register. + * + * The list of possible slaves follows the nx_action_bundle structure. The size + * of each slave is governed by its type as indicated by the 'slave_type' + * parameter. The list of slaves should be padded at its end with zeros to make + * the total length of the action a multiple of 8. + * + * Switches infer from the 'slave_type' parameter the size of each slave. All + * implementations must support the NXM_OF_IN_PORT 'slave_type' which indicates + * that the slaves are OpenFlow port numbers with NXM_LENGTH(NXM_OF_IN_PORT) == + * 2 byte width. Switches should reject actions which indicate unknown or + * unsupported slave types. + * + * Switches use a strategy dictated by the 'algorithm' parameter to choose a + * slave. If the switch does not support the specified 'algorithm' parameter, + * it should reject the action. + * + * Several algorithms take into account liveness when selecting slaves. The + * liveness of a slave is implementation defined (with one exception), but will + * generally take into account things like its carrier status and the results + * of any link monitoring protocols which happen to be running on it. In order + * to give controllers a place-holder value, the OFPP_NONE port is always + * considered live. + * + * Some slave selection strategies require the use of a hash function, in which + * case the 'fields' and 'basis' parameters should be populated. The 'fields' + * parameter (one of NX_HASH_FIELDS_*) designates which parts of the flow to + * hash. Refer to the definition of "enum nx_hash_fields" for details. The + * 'basis' parameter is used as a universal hash parameter. Different values + * of 'basis' yield different hash results. + * + * The 'zero' parameter at the end of the action structure is reserved for + * future use. Switches are required to reject actions which have nonzero + * bytes in the 'zero' field. + * + * NXAST_BUNDLE actions should have 'ofs_nbits' and 'dst' zeroed. Switches + * should reject actions which have nonzero bytes in either of these fields. + * + * NXAST_BUNDLE_LOAD stores the OpenFlow port number of the selected slave in + * dst[ofs:ofs+n_bits]. The format and semantics of 'dst' and 'ofs_nbits' are + * similar to those for the NXAST_REG_LOAD action. */ +struct nx_action_bundle { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* Length including slaves. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_BUNDLE or NXAST_BUNDLE_LOAD. */ + + /* Slave choice algorithm to apply to hash value. */ + ovs_be16 algorithm; /* One of NX_BD_ALG_*. */ + + /* What fields to hash and how. */ + ovs_be16 fields; /* One of NX_HASH_FIELDS_*. */ + ovs_be16 basis; /* Universal hash parameter. */ + + ovs_be32 slave_type; /* NXM_OF_IN_PORT. */ + ovs_be16 n_slaves; /* Number of slaves. */ + + ovs_be16 ofs_nbits; /* (ofs << 6) | (n_bits - 1). */ + ovs_be32 dst; /* Destination. */ + + uint8_t zero[4]; /* Reserved. Must be zero. */ +}; +OFP_ASSERT(sizeof(struct nx_action_bundle) == 32); + +/* Action structure for NXAST_OUTPUT_REG. + * + * Outputs to the OpenFlow port number written to src[ofs:ofs+nbits]. + * + * The format and semantics of 'src' and 'ofs_nbits' are similar to those for + * the NXAST_REG_LOAD action. + * + * The acceptable nxm_header values for 'src' are the same as the acceptable + * nxm_header values for the 'src' field of NXAST_REG_MOVE. + * + * The 'max_len' field indicates the number of bytes to send when the chosen + * port is OFPP_CONTROLLER. Its semantics are equivalent to the 'max_len' + * field of OFPAT_OUTPUT. + * + * The 'zero' field is required to be zeroed for forward compatibility. */ +struct nx_action_output_reg { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* 24. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_OUTPUT_REG. */ + + ovs_be16 ofs_nbits; /* (ofs << 6) | (n_bits - 1). */ + ovs_be32 src; /* Source. */ + + ovs_be16 max_len; /* Max length to send to controller. */ + + uint8_t zero[6]; /* Reserved, must be zero. */ +}; +OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24); +/* Action structure for NXAST_LEARN. + * + * This action adds or modifies a flow in an OpenFlow table, similar to + * OFPT_FLOW_MOD with OFPFC_MODIFY_STRICT as 'command'. The new flow has the + * specified idle timeout, hard timeout, priority, cookie, and flags. The new + * flow's match criteria and actions are built by applying each of the series + * of flow_mod_spec elements included as part of the action. + * + * A flow_mod_spec starts with a 16-bit header. A header that is all-bits-0 is + * a no-op used for padding the action as a whole to a multiple of 8 bytes in + * length. Otherwise, the flow_mod_spec can be thought of as copying 'n_bits' + * bits from a source to a destination. In this case, the header contains + * multiple fields: + * + * 15 14 13 12 11 10 0 + * +------+---+------+---------------------------------+ + * | 0 |src| dst | n_bits | + * +------+---+------+---------------------------------+ + * + * The meaning and format of a flow_mod_spec depends on 'src' and 'dst'. The + * following table summarizes the meaning of each possible combination. + * Details follow the table: + * + * src dst meaning + * --- --- ---------------------------------------------------------- + * 0 0 Add match criteria based on value in a field. + * 1 0 Add match criteria based on an immediate value. + * 0 1 Add NXAST_REG_LOAD action to copy field into a different field. + * 1 1 Add NXAST_REG_LOAD action to load immediate value into a field. + * 0 2 Add OFPAT_OUTPUT action to output to port from specified field. + * All other combinations are undefined and not allowed. + * + * The flow_mod_spec header is followed by a source specification and a + * destination specification. The format and meaning of the source + * specification depends on 'src': + * + * - If 'src' is 0, the source bits are taken from a field in the flow to + * which this action is attached. (This should be a wildcarded field. If + * its value is fully specified then the source bits being copied have + * constant values.) + * + * The source specification is an ovs_be32 'field' and an ovs_be16 'ofs'. + * 'field' is an nxm_header with nxm_hasmask=0, and 'ofs' the starting bit + * offset within that field. The source bits are field[ofs:ofs+n_bits-1]. + * 'field' and 'ofs' are subject to the same restrictions as the source + * field in NXAST_REG_MOVE. + * + * - If 'src' is 1, the source bits are a constant value. The source + * specification is (n_bits+15)/16*2 bytes long. Taking those bytes as a + * number in network order, the source bits are the 'n_bits' + * least-significant bits. The switch will report an error if other bits + * in the constant are nonzero. + * + * The flow_mod_spec destination specification, for 'dst' of 0 or 1, is an + * ovs_be32 'field' and an ovs_be16 'ofs'. 'field' is an nxm_header with + * nxm_hasmask=0 and 'ofs' is a starting bit offset within that field. The + * meaning of the flow_mod_spec depends on 'dst': + * + * - If 'dst' is 0, the flow_mod_spec specifies match criteria for the new + * flow. The new flow matches only if bits field[ofs:ofs+n_bits-1] in a + * packet equal the source bits. 'field' may be any nxm_header with + * nxm_hasmask=0 that is allowed in NXT_FLOW_MOD. + * + * Order is significant. Earlier flow_mod_specs must satisfy any + * prerequisites for matching fields specified later, by copying constant + * values into prerequisite fields. + * + * The switch will reject flow_mod_specs that do not satisfy NXM masking + * restrictions. + * + * - If 'dst' is 1, the flow_mod_spec specifies an NXAST_REG_LOAD action for + * the new flow. The new flow copies the source bits into + * field[ofs:ofs+n_bits-1]. Actions are executed in the same order as the + * flow_mod_specs. + * + * A single NXAST_REG_LOAD action writes no more than 64 bits, so n_bits + * greater than 64 yields multiple NXAST_REG_LOAD actions. + * + * The flow_mod_spec destination spec for 'dst' of 2 (when 'src' is 0) is + * empty. It has the following meaning: + * + * - The flow_mod_spec specifies an OFPAT_OUTPUT action for the new flow. + * The new flow outputs to the OpenFlow port specified by the source field. + * Of the special output ports with value OFPP_MAX or larger, OFPP_IN_PORT, + * OFPP_FLOOD, OFPP_LOCAL, and OFPP_ALL are supported. Other special ports + * may not be used. + * + * Resource Management + * ------------------- + * + * A switch has a finite amount of flow table space available for learning. + * When this space is exhausted, no new learning table entries will be learned + * until some existing flow table entries expire. The controller should be + * prepared to handle this by flooding (which can be implemented as a + * low-priority flow). + * + * If a learned flow matches a single TCP stream with a relatively long + * timeout, one may make the best of resource constraints by setting + * 'fin_idle_timeout' or 'fin_hard_timeout' (both measured in seconds), or + * both, to shorter timeouts. When either of these is specified as a nonzero + * value, OVS adds a NXAST_FIN_TIMEOUT action, with the specified timeouts, to + * the learned flow. + * + * Examples + * -------- + * + * The following examples give a prose description of the flow_mod_specs along + * with informal notation for how those would be represented and a hex dump of + * the bytes that would be required. + * + * These examples could work with various nx_action_learn parameters. Typical + * values would be idle_timeout=OFP_FLOW_PERMANENT, hard_timeout=60, + * priority=OFP_DEFAULT_PRIORITY, flags=0, table_id=10. + * + * 1. Learn input port based on the source MAC, with lookup into + * NXM_NX_REG1[16:31] by resubmit to in_port=99: + * + * Match on in_port=99: + * ovs_be16(src=1, dst=0, n_bits=16), 20 10 + * ovs_be16(99), 00 63 + * ovs_be32(NXM_OF_IN_PORT), ovs_be16(0) 00 00 00 02 00 00 + * + * Match Ethernet destination on Ethernet source from packet: + * ovs_be16(src=0, dst=0, n_bits=48), 00 30 + * ovs_be32(NXM_OF_ETH_SRC), ovs_be16(0) 00 00 04 06 00 00 + * ovs_be32(NXM_OF_ETH_DST), ovs_be16(0) 00 00 02 06 00 00 + * + * Set NXM_NX_REG1[16:31] to the packet's input port: + * ovs_be16(src=0, dst=1, n_bits=16), 08 10 + * ovs_be32(NXM_OF_IN_PORT), ovs_be16(0) 00 00 00 02 00 00 + * ovs_be32(NXM_NX_REG1), ovs_be16(16) 00 01 02 04 00 10 + * + * Given a packet that arrived on port A with Ethernet source address B, + * this would set up the flow "in_port=99, dl_dst=B, + * actions=load:A->NXM_NX_REG1[16..31]". + * + * In syntax accepted by ovs-ofctl, this action is: learn(in_port=99, + * NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], + * load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31]) + * + * 2. Output to input port based on the source MAC and VLAN VID, with lookup + * into NXM_NX_REG1[16:31]: + * + * Match on same VLAN ID as packet: + * ovs_be16(src=0, dst=0, n_bits=12), 00 0c + * ovs_be32(NXM_OF_VLAN_TCI), ovs_be16(0) 00 00 08 02 00 00 + * ovs_be32(NXM_OF_VLAN_TCI), ovs_be16(0) 00 00 08 02 00 00 + * + * Match Ethernet destination on Ethernet source from packet: + * ovs_be16(src=0, dst=0, n_bits=48), 00 30 + * ovs_be32(NXM_OF_ETH_SRC), ovs_be16(0) 00 00 04 06 00 00 + * ovs_be32(NXM_OF_ETH_DST), ovs_be16(0) 00 00 02 06 00 00 + * + * Output to the packet's input port: + * ovs_be16(src=0, dst=2, n_bits=16), 10 10 + * ovs_be32(NXM_OF_IN_PORT), ovs_be16(0) 00 00 00 02 00 00 + * + * Given a packet that arrived on port A with Ethernet source address B in + * VLAN C, this would set up the flow "dl_dst=B, vlan_vid=C, + * actions=output:A". + * + * In syntax accepted by ovs-ofctl, this action is: + * learn(NXM_OF_VLAN_TCI[0..11], NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], + * output:NXM_OF_IN_PORT[]) + * + * 3. Here's a recipe for a very simple-minded MAC learning switch. It uses a + * 10-second MAC expiration time to make it easier to see what's going on + * + * ovs-vsctl del-controller br0 + * ovs-ofctl del-flows br0 + * ovs-ofctl add-flow br0 "table=0 actions=learn(table=1, \ + hard_timeout=10, NXM_OF_VLAN_TCI[0..11], \ + NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], \ + output:NXM_OF_IN_PORT[]), resubmit(,1)" + * ovs-ofctl add-flow br0 "table=1 priority=0 actions=flood" + * + * You can then dump the MAC learning table with: + * + * ovs-ofctl dump-flows br0 table=1 + * + * Usage Advice + * ------------ + * + * For best performance, segregate learned flows into a table that is not used + * for any other flows except possibly for a lowest-priority "catch-all" flow + * (a flow with no match criteria). If different learning actions specify + * different match criteria, use different tables for the learned flows. + * + * The meaning of 'hard_timeout' and 'idle_timeout' can be counterintuitive. + * These timeouts apply to the flow that is added, which means that a flow with + * an idle timeout will expire when no traffic has been sent *to* the learned + * address. This is not usually the intent in MAC learning; instead, we want + * the MAC learn entry to expire when no traffic has been sent *from* the + * learned address. Use a hard timeout for that. + */ +struct nx_action_learn { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* At least 24. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_LEARN. */ + ovs_be16 idle_timeout; /* Idle time before discarding (seconds). */ + ovs_be16 hard_timeout; /* Max time before discarding (seconds). */ + ovs_be16 priority; /* Priority level of flow entry. */ + ovs_be64 cookie; /* Cookie for new flow. */ + ovs_be16 flags; /* NX_LEARN_F_*. */ + uint8_t table_id; /* Table to insert flow entry. */ + uint8_t pad; /* Must be zero. */ + ovs_be16 fin_idle_timeout; /* Idle timeout after FIN, if nonzero. */ + ovs_be16 fin_hard_timeout; /* Hard timeout after FIN, if nonzero. */ + /* Followed by a sequence of flow_mod_spec elements, as described above, + * until the end of the action is reached. */ +}; +OFP_ASSERT(sizeof(struct nx_action_learn) == 32); + +/* Action structure for NXAST_FIN_TIMEOUT. + * + * This action changes the idle timeout or hard timeout, or both, of this + * OpenFlow rule when the rule matches a TCP packet with the FIN or RST flag. + * When such a packet is observed, the action reduces the rule's idle timeout + * to 'fin_idle_timeout' and its hard timeout to 'fin_hard_timeout'. This + * action has no effect on an existing timeout that is already shorter than the + * one that the action specifies. A 'fin_idle_timeout' or 'fin_hard_timeout' + * of zero has no effect on the respective timeout. + * + * 'fin_idle_timeout' and 'fin_hard_timeout' are measured in seconds. + * 'fin_hard_timeout' specifies time since the flow's creation, not since the + * receipt of the FIN or RST. + * + * This is useful for quickly discarding learned TCP flows that otherwise will + * take a long time to expire. + * + * This action is intended for use with an OpenFlow rule that matches only a + * single TCP flow. If the rule matches multiple TCP flows (e.g. it wildcards + * all TCP traffic, or all TCP traffic to a particular port), then any FIN or + * RST in any of those flows will cause the entire OpenFlow rule to expire + * early, which is not normally desirable. + */ +struct nx_action_fin_timeout { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* 16. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_FIN_TIMEOUT. */ + ovs_be16 fin_idle_timeout; /* New idle timeout, if nonzero. */ + ovs_be16 fin_hard_timeout; /* New hard timeout, if nonzero. */ + ovs_be16 pad; /* Must be zero. */ +}; +OFP_ASSERT(sizeof(struct nx_action_fin_timeout) == 16); + +/* Action structure for NXAST_CONTROLLER. + * + * This generalizes using OFPAT_OUTPUT to send a packet to OFPP_CONTROLLER. In + * addition to the 'max_len' that OFPAT_OUTPUT supports, it also allows + * specifying: + * + * - 'reason': The reason code to use in the ofp_packet_in or nx_packet_in. + * + * - 'controller_id': The ID of the controller connection to which the + * ofp_packet_in should be sent. The ofp_packet_in or nx_packet_in is + * sent only to controllers that have the specified controller connection + * ID. See "struct nx_controller_id" for more information. */ +struct nx_action_controller { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* Length is 16. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_CONTROLLER. */ + ovs_be16 max_len; /* Maximum length to send to controller. */ + ovs_be16 controller_id; /* Controller ID to send packet-in. */ + uint8_t reason; /* enum ofp_packet_in_reason (OFPR_*). */ + uint8_t zero; /* Must be zero. */ +}; +OFP_ASSERT(sizeof(struct nx_action_controller) == 16); + +/* Action structure for NXAST_WRITE_METADATA. + * + * Modifies the 'mask' bits of the metadata value. */ +struct nx_action_write_metadata { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* Length is 32. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_WRITE_METADATA. */ + uint8_t zeros[6]; /* Must be zero. */ + ovs_be64 metadata; /* Metadata register. */ + ovs_be64 mask; /* Metadata mask. */ +}; +OFP_ASSERT(sizeof(struct nx_action_write_metadata) == 32); + +/* Action structure for NXAST_STACK_PUSH and NXAST_STACK_POP. + * + * Pushes (or pops) field[offset: offset + n_bits] to (or from) + * top of the stack. + */ +struct nx_action_stack { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* Length is 16. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_STACK_PUSH or NXAST_STACK_POP. */ + ovs_be16 offset; /* Bit offset into the field. */ + ovs_be32 field; /* The field used for push or pop. */ + ovs_be16 n_bits; /* (n_bits + 1) bits of the field. */ + uint8_t zero[6]; /* Reserved, must be zero. */ +}; +OFP_ASSERT(sizeof(struct nx_action_stack) == 24); + +/* Action structure for NXAST_SAMPLE. + * + * Samples matching packets with the given probability and sends them + * each to the set of collectors identified with the given ID. The + * probability is expressed as a number of packets to be sampled out + * of USHRT_MAX packets, and must be >0. + * + * When sending packet samples to IPFIX collectors, the IPFIX flow + * record sent for each sampled packet is associated with the given + * observation domain ID and observation point ID. Each IPFIX flow + * record contain the sampled packet's headers when executing this + * rule. If a sampled packet's headers are modified by previous + * actions in the flow, those modified headers are sent. */ +struct nx_action_sample { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* Length is 24. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_SAMPLE. */ + ovs_be16 probability; /* Fraction of packets to sample. */ + ovs_be32 collector_set_id; /* ID of collector set in OVSDB. */ + ovs_be32 obs_domain_id; /* ID of sampling observation domain. */ + ovs_be32 obs_point_id; /* ID of sampling observation point. */ +}; +OFP_ASSERT(sizeof(struct nx_action_sample) == 24); + +enum ofp_raw_action_type { +/* ## ----------------- ## */ +/* ## Standard actions. ## */ +/* ## ----------------- ## */ + + /* OF1.0(0): struct ofp10_action_output. */ + OFPAT_RAW10_OUTPUT, + /* OF1.1+(0): struct ofp11_action_output. */ + OFPAT_RAW11_OUTPUT, + + /* OF1.0(1): uint16_t. */ + OFPAT_RAW10_SET_VLAN_VID, + /* OF1.0(2): uint8_t. */ + OFPAT_RAW10_SET_VLAN_PCP, + + /* OF1.1(1), OF1.2+(1) is deprecated (use Set-Field): uint16_t. + * + * [Semantics differ slightly between the 1.0 and 1.1 versions of the VLAN + * modification actions: the 1.0 versions push a VLAN header if none is + * present, but the 1.1 versions do not. That is the only reason that we + * distinguish their raw action types.] */ + OFPAT_RAW11_SET_VLAN_VID, + /* OF1.1(2), OF1.2+(2) is deprecated (use Set-Field): uint8_t. */ + OFPAT_RAW11_SET_VLAN_PCP, + + /* OF1.1+(17): ovs_be16. + * + * [The argument is the Ethertype, e.g. ETH_TYPE_VLAN_8021Q, not the VID or + * TCI.] */ + OFPAT_RAW11_PUSH_VLAN, + + /* OF1.0(3): void. */ + OFPAT_RAW10_STRIP_VLAN, + /* OF1.1+(18): void. */ + OFPAT_RAW11_POP_VLAN, + + /* OF1.0(4), OF1.1(3), OF1.2+(3) is deprecated (use Set-Field): struct + * ofp_action_dl_addr. */ + OFPAT_RAW_SET_DL_SRC, + + /* OF1.0(5), OF1.1(4), OF1.2+(4) is deprecated (use Set-Field): struct + * ofp_action_dl_addr. */ + OFPAT_RAW_SET_DL_DST, + + /* OF1.0(6), OF1.1(5), OF1.2+(5) is deprecated (use Set-Field): + * ovs_be32. */ + OFPAT_RAW_SET_NW_SRC, + + /* OF1.0(7), OF1.1(6), OF1.2+(6) is deprecated (use Set-Field): + * ovs_be32. */ + OFPAT_RAW_SET_NW_DST, + + /* OF1.0(8), OF1.1(7), OF1.2+(7) is deprecated (use Set-Field): uint8_t. */ + OFPAT_RAW_SET_NW_TOS, + + /* OF1.1(8), OF1.2+(8) is deprecated (use Set-Field): uint8_t. */ + OFPAT_RAW11_SET_NW_ECN, + + /* OF1.0(9), OF1.1(9), OF1.2+(9) is deprecated (use Set-Field): + * ovs_be16. */ + OFPAT_RAW_SET_TP_SRC, + + /* OF1.0(10), OF1.1(10), OF1.2+(10) is deprecated (use Set-Field): + * ovs_be16. */ + OFPAT_RAW_SET_TP_DST, + + /* OF1.0(11): struct ofp10_action_enqueue. */ + OFPAT_RAW10_ENQUEUE, + + /* OF1.1+(11): void. */ + OFPAT_RAW11_COPY_TTL_OUT, + + /* OF1.1+(12): void. */ + OFPAT_RAW11_COPY_TTL_IN, + + /* NX1.0(30), OF1.1(13), OF1.2+(13) is deprecated (use Set-Field): + * ovs_be32. */ + OFPAT_RAW_SET_MPLS_LABEL, + + /* NX1.0(31), OF1.1(14), OF1.2+(14) is deprecated (use Set-Field): + * uint8_t. */ + OFPAT_RAW_SET_MPLS_TC, + + /* NX1.0(25), OF1.1(15), OF1.2+(15) is deprecated (use Set-Field): + * uint8_t. */ + OFPAT_RAW_SET_MPLS_TTL, + + /* NX1.0(26), OF1.1+(16): void. */ + OFPAT_RAW_DEC_MPLS_TTL, + + /* NX1.0(23), OF1.1+(19): ovs_be16. + * + * [The argument is the Ethertype, e.g. ETH_TYPE_MPLS, not the label.] */ + OFPAT_RAW_PUSH_MPLS, + + /* NX1.0(24), OF1.1+(20): ovs_be16. + * + * [The argument is the Ethertype, e.g. ETH_TYPE_IPV4 if at BoS or + * ETH_TYPE_MPLS otherwise, not the label.] */ + OFPAT_RAW_POP_MPLS, + + /* NX1.0(4), OF1.1+(21): uint32_t. */ + OFPAT_RAW_SET_QUEUE, + + /* OF1.1+(22): uint32_t. */ + OFPAT_RAW11_GROUP, + + /* OF1.1+(23): uint8_t. */ + OFPAT_RAW11_SET_NW_TTL, + + /* NX1.0(18), OF1.1+(24): void. */ + OFPAT_RAW_DEC_NW_TTL, + /* NX1.0+(21): struct nx_action_cnt_ids, ... */ + NXAST_RAW_DEC_TTL_CNT_IDS, + + /* OF1.2+(25): struct ofp12_action_set_field, ... */ + OFPAT_RAW12_SET_FIELD, + /* NX1.0+(7): struct nx_action_reg_load. */ + NXAST_RAW_REG_LOAD, + + /* OF1.5+(28): struct ofp15_action_copy_field. */ + OFPAT_RAW15_COPY_FIELD, + /* NX1.0+(6): struct nx_action_reg_move. */ + NXAST_RAW_REG_MOVE, + +/* ## ------------------------- ## */ +/* ## Nicira extension actions. ## */ +/* ## ------------------------- ## */ + +/* Actions similar to standard actions are listed with the standard actions. */ + + /* NX1.0+(1): uint16_t. */ + NXAST_RAW_RESUBMIT, + /* NX1.0+(14): struct nx_action_resubmit. */ + NXAST_RAW_RESUBMIT_TABLE, + + /* NX1.0+(2): uint32_t. */ + NXAST_RAW_SET_TUNNEL, + /* NX1.0+(9): uint64_t. */ + NXAST_RAW_SET_TUNNEL64, + + /* NX1.0+(5): void. */ + NXAST_RAW_POP_QUEUE, + + /* NX1.0+(8): struct nx_action_note, ... */ + NXAST_RAW_NOTE, + + /* NX1.0+(10): struct nx_action_multipath. */ + NXAST_RAW_MULTIPATH, + + /* NX1.0+(12): struct nx_action_bundle, ... */ + NXAST_RAW_BUNDLE, + /* NX1.0+(13): struct nx_action_bundle, ... */ + NXAST_RAW_BUNDLE_LOAD, + + /* NX1.0+(15): struct nx_action_output_reg. */ + NXAST_RAW_OUTPUT_REG, + + /* NX1.0+(16): struct nx_action_learn, ... */ + NXAST_RAW_LEARN, + + /* NX1.0+(17): void. */ + NXAST_RAW_EXIT, + + /* NX1.0+(19): struct nx_action_fin_timeout. */ + NXAST_RAW_FIN_TIMEOUT, + + /* NX1.0+(20): struct nx_action_controller. */ + NXAST_RAW_CONTROLLER, + + /* NX1.0+(22): struct nx_action_write_metadata. */ + NXAST_RAW_WRITE_METADATA, + + /* NX1.0+(27): struct nx_action_stack. */ + NXAST_RAW_STACK_PUSH, + + /* NX1.0+(28): struct nx_action_stack. */ + NXAST_RAW_STACK_POP, + + /* NX1.0+(29): struct nx_action_sample. */ + NXAST_RAW_SAMPLE, +}; + +struct ofpact_hdrs { + uint32_t vendor; /* 0 if standard, otherwise a vendor code. */ + uint16_t type; /* Type if standard, otherwise subtype. */ + uint8_t ofp_version; /* From ofp_header. */ +}; + +static bool +ofpact_hdrs_equal(const struct ofpact_hdrs *a, + const struct ofpact_hdrs *b) +{ + return (a->vendor == b->vendor + && a->type == b->type + && a->ofp_version == b->ofp_version); +} + +static uint32_t +ofpact_hdrs_hash(const struct ofpact_hdrs *hdrs) +{ + return hash_2words(hdrs->vendor, (hdrs->type << 16) | hdrs->ofp_version); +} + +struct ofpact_raw_instance { + struct ofpact_hdrs hdrs; + enum ofp_raw_action_type raw; + + struct hmap_node decode_node; + struct hmap_node encode_node; + + unsigned short int min_length; + unsigned short int max_length; + + unsigned short int arg_ofs; + unsigned short int arg_len; + + const char *name; + const char *deprecation; +}; + +static struct hmap ofpact_decode_hmap; +static struct hmap ofpact_encode_hmap; + +static void *ofpact_put_raw(struct ofpbuf *buf, enum ofp_version ofp_version, + enum ofp_raw_action_type raw, uint64_t arg); + +static char * WARN_UNUSED_RESULT +ofpacts_parse(char *str, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols, + bool allow_instructions); + +#include "ofp-actions.inc" + +static void +ofpact_init_hmaps(void) +{ + static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; + + if (ovsthread_once_start(&once)) { + struct ofpact_raw_instance *inst; + + hmap_init(&ofpact_decode_hmap); + hmap_init(&ofpact_encode_hmap); + for (inst = all_raw_instances; + inst < &all_raw_instances[ARRAY_SIZE(all_raw_instances)]; + inst++) { + hmap_insert(&ofpact_decode_hmap, &inst->decode_node, + ofpact_hdrs_hash(&inst->hdrs)); + hmap_insert(&ofpact_encode_hmap, &inst->encode_node, + hash_2words(inst->raw, inst->hdrs.ofp_version)); + } + ovsthread_once_done(&once); + } +} + +static enum ofperr +ofpact_decode_raw(enum ofp_version ofp_version, + const struct ofp_action_header *oah, size_t length, + const struct ofpact_raw_instance **instp) +{ + const struct ofpact_raw_instance *inst; + struct ofpact_hdrs hdrs; + + *instp = NULL; + if (length < sizeof *oah) { + return OFPERR_OFPBAC_BAD_LEN; + } + + /* Get base action type. */ + if (oah->type == htons(OFPAT_VENDOR)) { + /* Get vendor. */ + const struct ofp_action_vendor_header *oavh; + + oavh = ALIGNED_CAST(struct ofp_action_vendor_header *, oah); + hdrs.vendor = ntohl(oavh->vendor); + if (hdrs.vendor == NX_VENDOR_ID) { + /* Get Nicira action type. */ + const struct nx_action_header *nah; + + nah = ALIGNED_CAST(const struct nx_action_header *, oah); + if (length < sizeof *nah) { + return OFPERR_OFPBAC_BAD_LEN; + } + hdrs.type = ntohs(nah->subtype); + } else { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); + + VLOG_WARN_RL(&rl, "OpenFlow action has unknown vendor %#"PRIx32, + hdrs.vendor); + return OFPERR_OFPBAC_BAD_VENDOR; + } + } else { + hdrs.vendor = 0; + hdrs.type = ntohs(oah->type); + } + + /* XXX complain about deprecation */ + + ofpact_init_hmaps(); + hdrs.ofp_version = ofp_version; + HMAP_FOR_EACH_WITH_HASH (inst, decode_node, ofpact_hdrs_hash(&hdrs), + &ofpact_decode_hmap) { + if (ofpact_hdrs_equal(&hdrs, &inst->hdrs)) { + *instp = inst; + return 0; + } + } + + return (hdrs.vendor + ? OFPERR_OFPBAC_BAD_VENDOR_TYPE + : OFPERR_OFPBAC_BAD_TYPE); +} + +static enum ofperr +ofpact_pull_raw(struct ofpbuf *buf, enum ofp_version ofp_version, + enum ofp_raw_action_type *raw, uint64_t *arg) +{ + const struct ofp_action_header *oah = ofpbuf_data(buf); + const struct ofpact_raw_instance *action; + unsigned int length; + enum ofperr error; + + *raw = *arg = 0; + error = ofpact_decode_raw(ofp_version, oah, ofpbuf_size(buf), &action); + if (error) { + return error; + } + + length = ntohs(oah->len); + if (length < action->min_length || length > action->max_length) { + VLOG_WARN_RL(&rl, "OpenFlow action %s length %u not in valid range " + "[%hu,%hu]", action->name, length, + action->min_length, action->max_length); + return OFPERR_OFPBAC_BAD_LEN; + } + if (length % 8) { + VLOG_WARN_RL(&rl, "OpenFlow action %s length %u is not a multiple " + "of 8", action->name, length); + return OFPERR_OFPBAC_BAD_LEN; + } + + *raw = action->raw; + *arg = 0; + if (action->arg_len) { + const uint8_t *p; + int i; + + p = ofpbuf_at_assert(buf, action->arg_ofs, action->arg_len); + for (i = 0; i < action->arg_len; i++) { + *arg = (*arg << 8) | p[i]; + } + } + + ofpbuf_pull(buf, length); + + return 0; +} + +static const struct ofpact_raw_instance * +ofpact_raw_lookup(enum ofp_version ofp_version, enum ofp_raw_action_type raw) +{ + const struct ofpact_raw_instance *inst; + + ofpact_init_hmaps(); + HMAP_FOR_EACH_WITH_HASH (inst, encode_node, hash_2words(raw, ofp_version), + &ofpact_encode_hmap) { + if (inst->raw == raw && inst->hdrs.ofp_version == ofp_version) { + return inst; + } + } + OVS_NOT_REACHED(); +} + +static void * +ofpact_put_raw(struct ofpbuf *buf, enum ofp_version ofp_version, + enum ofp_raw_action_type raw, uint64_t arg) +{ + const struct ofpact_raw_instance *inst; + struct ofp_action_vendor_header *oavh; + const struct ofpact_hdrs *hdrs; + + inst = ofpact_raw_lookup(ofp_version, raw); + hdrs = &inst->hdrs; + + oavh = ofpbuf_put_zeros(buf, inst->min_length); + oavh->type = htons(hdrs->vendor ? OFPAT_VENDOR : hdrs->type); + oavh->len = htons(inst->min_length); + oavh->vendor = htonl(hdrs->vendor); + + switch (hdrs->vendor) { + case 0: + break; + + case NX_VENDOR_ID: { + struct nx_action_header *nah = (struct nx_action_header *) oavh; + nah->subtype = htons(hdrs->type); + break; + } + + default: + OVS_NOT_REACHED(); + } + + if (inst->arg_len) { + uint8_t *p = (uint8_t *) oavh + inst->arg_ofs + inst->arg_len; + int i; + + for (i = 0; i < inst->arg_len; i++) { + *--p = arg; + arg >>= 8; + } + } else { + ovs_assert(!arg); + } + + return oavh; +} /* Converting OpenFlow 1.0 to ofpacts. */ @@ -40,30 +1303,14 @@ union ofp_action { struct ofp_action_header header; struct ofp_action_vendor_header vendor; struct ofp10_action_output output10; - struct ofp_action_vlan_vid vlan_vid; - struct ofp_action_vlan_pcp vlan_pcp; - struct ofp_action_nw_addr nw_addr; - struct ofp_action_nw_tos nw_tos; - struct ofp11_action_nw_ecn nw_ecn; - struct ofp11_action_nw_ttl nw_ttl; - struct ofp_action_tp_port tp_port; struct ofp_action_dl_addr dl_addr; struct ofp10_action_enqueue enqueue; struct ofp11_action_output ofp11_output; - struct ofp11_action_push push; - struct ofp11_action_pop_mpls ofp11_pop_mpls; - struct ofp11_action_set_queue ofp11_set_queue; - struct ofp11_action_mpls_label ofp11_mpls_label; - struct ofp11_action_mpls_tc ofp11_mpls_tc; - struct ofp11_action_mpls_ttl ofp11_mpls_ttl; - struct ofp11_action_group group; struct ofp12_action_set_field set_field; + struct ofp15_action_copy_field copy_field; struct nx_action_header nxa_header; struct nx_action_resubmit resubmit; - struct nx_action_set_tunnel set_tunnel; - struct nx_action_set_tunnel64 set_tunnel64; struct nx_action_write_metadata write_metadata; - struct nx_action_set_queue set_queue; struct nx_action_reg_move reg_move; struct nx_action_reg_load reg_load; struct nx_action_stack stack; @@ -74,13 +1321,8 @@ union ofp_action { struct nx_action_cnt_ids cnt_ids; struct nx_action_fin_timeout fin_timeout; struct nx_action_controller controller; - struct nx_action_push_mpls push_mpls; - struct nx_action_mpls_ttl mpls_ttl; - struct nx_action_pop_mpls pop_mpls; struct nx_action_sample sample; struct nx_action_learn learn; - struct nx_action_mpls_label mpls_label; - struct nx_action_mpls_tc mpls_tc; }; static enum ofperr @@ -114,14 +1356,13 @@ enqueue_from_openflow10(const struct ofp10_action_enqueue *oae, } static void -resubmit_from_openflow(const struct nx_action_resubmit *nar, - struct ofpbuf *out) +resubmit_from_openflow(uint16_t port, struct ofpbuf *out) { struct ofpact_resubmit *resubmit; resubmit = ofpact_put_RESUBMIT(out); - resubmit->ofpact.compat = OFPUTIL_NXAST_RESUBMIT; - resubmit->in_port = u16_to_ofp(ntohs(nar->in_port)); + resubmit->ofpact.raw = NXAST_RAW_RESUBMIT; + resubmit->in_port = u16_to_ofp(port); resubmit->table_id = 0xff; } @@ -136,7 +1377,7 @@ resubmit_table_from_openflow(const struct nx_action_resubmit *nar, } resubmit = ofpact_put_RESUBMIT(out); - resubmit->ofpact.compat = OFPUTIL_NXAST_RESUBMIT_TABLE; + resubmit->ofpact.raw = NXAST_RAW_RESUBMIT_TABLE; resubmit->in_port = u16_to_ofp(ntohs(nar->in_port)); resubmit->table_id = nar->table; return 0; @@ -201,6 +1442,76 @@ metadata_from_nxast(const struct nx_action_write_metadata *nawm, return 0; } +static enum ofperr +copy_field_from_openflow(const struct ofp15_action_copy_field *oacf, + struct ofpbuf *ofpacts) +{ + struct ofpact_reg_move *move; + + move = ofpact_put_REG_MOVE(ofpacts); + move->ofpact.raw = OFPAT_RAW15_COPY_FIELD; + move->src.field = mf_from_nxm_header(ntohl(oacf->src)); + move->src.ofs = ntohs(oacf->src_offset); + move->src.n_bits = ntohs(oacf->n_bits); + move->dst.field = mf_from_nxm_header(ntohl(oacf->dst)); + move->dst.ofs = ntohs(oacf->dst_offset); + move->dst.n_bits = ntohs(oacf->n_bits); + + return nxm_reg_move_check(move, NULL); +} + +static enum ofperr +nxm_reg_move_from_openflow(const struct nx_action_reg_move *narm, + struct ofpbuf *ofpacts) +{ + struct ofpact_reg_move *move; + + move = ofpact_put_REG_MOVE(ofpacts); + move->ofpact.raw = NXAST_RAW_REG_MOVE; + move->src.field = mf_from_nxm_header(ntohl(narm->src)); + move->src.ofs = ntohs(narm->src_ofs); + move->src.n_bits = ntohs(narm->n_bits); + move->dst.field = mf_from_nxm_header(ntohl(narm->dst)); + move->dst.ofs = ntohs(narm->dst_ofs); + move->dst.n_bits = ntohs(narm->n_bits); + + return nxm_reg_move_check(move, NULL); +} + +static enum ofperr +nxm_reg_load_from_openflow(const struct nx_action_reg_load *narl, + struct ofpbuf *ofpacts) +{ + struct ofpact_reg_load *load; + + load = ofpact_put_REG_LOAD(ofpacts); + load->dst.field = mf_from_nxm_header(ntohl(narl->dst)); + load->dst.ofs = nxm_decode_ofs(narl->ofs_nbits); + load->dst.n_bits = nxm_decode_n_bits(narl->ofs_nbits); + load->subvalue.be64[1] = narl->value; + + /* Reject 'narl' if a bit numbered 'n_bits' or higher is set to 1 in + * narl->value. */ + if (load->dst.n_bits < 64 && + ntohll(narl->value) >> load->dst.n_bits) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + + return nxm_reg_load_check(load, NULL); +} + +static void +nxm_reg_load_to_nxast(const struct ofpact_reg_load *load, + struct ofpbuf *openflow) +{ + struct nx_action_reg_load *narl; + + narl = put_NXAST_REG_LOAD(openflow); + narl->ofs_nbits = nxm_encode_ofs_nbits(load->dst.ofs, load->dst.n_bits); + narl->dst = htonl(load->dst.field->nxm_header); + narl->value = load->subvalue.be64[1]; +} + static void note_from_openflow(const struct nx_action_note *nan, struct ofpbuf *out) { @@ -215,14 +1526,13 @@ note_from_openflow(const struct nx_action_note *nan, struct ofpbuf *out) } static enum ofperr -dec_ttl_from_openflow(struct ofpbuf *out, enum ofputil_action_code compat) +dec_ttl_from_openflow(struct ofpbuf *out) { uint16_t id = 0; struct ofpact_cnt_ids *ids; enum ofperr error = 0; ids = ofpact_put_DEC_TTL(out); - ids->ofpact.compat = compat; ids->n_controllers = 1; ofpbuf_put(out, &id, sizeof id); ids = out->frame; @@ -239,7 +1549,7 @@ dec_ttl_cnt_ids_from_openflow(const struct nx_action_cnt_ids *nac_ids, int i; ids = ofpact_put_DEC_TTL(out); - ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL_CNT_IDS; + ids->ofpact.raw = NXAST_RAW_DEC_TTL_CNT_IDS; ids->n_controllers = ntohs(nac_ids->n_controllers); ids_size = ntohs(nac_ids->len) - sizeof *nac_ids; @@ -285,6 +1595,62 @@ sample_from_openflow(const struct nx_action_sample *nas, return 0; } +static void +stack_action_from_openflow__(const struct nx_action_stack *nasp, + struct ofpact_stack *stack_action) +{ + stack_action->subfield.field = mf_from_nxm_header(ntohl(nasp->field)); + stack_action->subfield.ofs = ntohs(nasp->offset); + stack_action->subfield.n_bits = ntohs(nasp->n_bits); +} + +static void +nxm_stack_to_nxast__(const struct ofpact_stack *stack_action, + struct nx_action_stack *nasp) +{ + nasp->offset = htons(stack_action->subfield.ofs); + nasp->n_bits = htons(stack_action->subfield.n_bits); + nasp->field = htonl(stack_action->subfield.field->nxm_header); +} + +static enum ofperr +nxm_stack_push_from_openflow(const struct nx_action_stack *nasp, + struct ofpbuf *ofpacts) +{ + struct ofpact_stack *push; + + push = ofpact_put_STACK_PUSH(ofpacts); + stack_action_from_openflow__(nasp, push); + + return nxm_stack_push_check(push, NULL); +} + +static enum ofperr +nxm_stack_pop_from_openflow(const struct nx_action_stack *nasp, + struct ofpbuf *ofpacts) +{ + struct ofpact_stack *pop; + + pop = ofpact_put_STACK_POP(ofpacts); + stack_action_from_openflow__(nasp, pop); + + return nxm_stack_pop_check(pop, NULL); +} + +static void +nxm_stack_push_to_nxast(const struct ofpact_stack *stack, + struct ofpbuf *openflow) +{ + nxm_stack_to_nxast__(stack, put_NXAST_STACK_PUSH(openflow)); +} + +static void +nxm_stack_pop_to_nxast(const struct ofpact_stack *stack, + struct ofpbuf *openflow) +{ + nxm_stack_to_nxast__(stack, put_NXAST_STACK_POP(openflow)); +} + static enum ofperr push_mpls_from_openflow(ovs_be16 ethertype, struct ofpbuf *out) { @@ -300,330 +1666,670 @@ push_mpls_from_openflow(ovs_be16 ethertype, struct ofpbuf *out) } static enum ofperr -decode_nxast_action(const union ofp_action *a, enum ofputil_action_code *code) +set_field_from_openflow(const struct ofp12_action_set_field *oasf, + struct ofpbuf *ofpacts) { - const struct nx_action_header *nah = &a->nxa_header; - uint16_t len = ntohs(a->header.len); + uint16_t oasf_len = ntohs(oasf->len); + uint32_t oxm_header = ntohl(oasf->dst); + uint8_t oxm_length = NXM_LENGTH(oxm_header); + struct ofpact_set_field *sf; + const struct mf_field *mf; - if (len < sizeof(struct nx_action_header)) { - return OFPERR_OFPBAC_BAD_LEN; - } else if (a->vendor.vendor != CONSTANT_HTONL(NX_VENDOR_ID)) { - return OFPERR_OFPBAC_BAD_VENDOR; + /* ofp12_action_set_field is padded to 64 bits by zero */ + if (oasf_len != ROUND_UP(sizeof *oasf + oxm_length, 8)) { + return OFPERR_OFPBAC_BAD_SET_LEN; + } + if (!is_all_zeros((const uint8_t *)oasf + sizeof *oasf + oxm_length, + oasf_len - oxm_length - sizeof *oasf)) { + return OFPERR_OFPBAC_BAD_SET_ARGUMENT; } - switch (nah->subtype) { -#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \ - case CONSTANT_HTONS(ENUM): \ - if (EXTENSIBLE \ - ? len >= sizeof(struct STRUCT) \ - : len == sizeof(struct STRUCT)) { \ - *code = OFPUTIL_##ENUM; \ - return 0; \ - } else { \ - return OFPERR_OFPBAC_BAD_LEN; \ - } \ - OVS_NOT_REACHED(); -#include "ofp-util.def" + if (NXM_HASMASK(oxm_header)) { + return OFPERR_OFPBAC_BAD_SET_TYPE; + } + mf = mf_from_nxm_header(oxm_header); + if (!mf) { + return OFPERR_OFPBAC_BAD_SET_TYPE; + } + ovs_assert(mf->n_bytes == oxm_length); + /* oxm_length is now validated to be compatible with mf_value. */ + if (!mf->writable) { + VLOG_WARN_RL(&rl, "destination field %s is not writable", mf->name); + return OFPERR_OFPBAC_BAD_SET_ARGUMENT; + } + sf = ofpact_put_SET_FIELD(ofpacts); + sf->field = mf; + memcpy(&sf->value, oasf + 1, mf->n_bytes); - case CONSTANT_HTONS(NXAST_SNAT__OBSOLETE): - case CONSTANT_HTONS(NXAST_DROP_SPOOFED_ARP__OBSOLETE): - default: - return OFPERR_OFPBAC_BAD_TYPE; + /* The value must be valid for match and must have the OFPVID_PRESENT bit + * on for OXM_OF_VLAN_VID. */ + if (!mf_is_value_valid(mf, &sf->value) + || (mf->id == MFF_VLAN_VID + && !(sf->value.be16 & htons(OFPVID12_PRESENT)))) { + struct ds ds = DS_EMPTY_INITIALIZER; + mf_format(mf, &sf->value, NULL, &ds); + VLOG_WARN_RL(&rl, "Invalid value for set field %s: %s", + mf->name, ds_cstr(&ds)); + ds_destroy(&ds); + + return OFPERR_OFPBAC_BAD_SET_ARGUMENT; } + return 0; } -/* Parses 'a' to determine its type. On success stores the correct type into - * '*code' and returns 0. On failure returns an OFPERR_* error code and - * '*code' is indeterminate. - * - * The caller must have already verified that 'a''s length is potentially - * correct (that is, a->header.len is nonzero and a multiple of - * OFP_ACTION_ALIGN and no longer than the amount of space allocated to 'a'). - * - * This function verifies that 'a''s length is correct for the type of action - * that it represents. */ static enum ofperr -decode_openflow10_action(const union ofp_action *a, - enum ofputil_action_code *code) +output_from_openflow11(const struct ofp11_action_output *oao, + struct ofpbuf *out) { - switch (a->type) { - case CONSTANT_HTONS(OFPAT10_VENDOR): - return decode_nxast_action(a, code); - -#define OFPAT10_ACTION(ENUM, STRUCT, NAME) \ - case CONSTANT_HTONS(ENUM): \ - if (a->header.len == htons(sizeof(struct STRUCT))) { \ - *code = OFPUTIL_##ENUM; \ - return 0; \ - } else { \ - return OFPERR_OFPBAC_BAD_LEN; \ - } \ - break; -#include "ofp-util.def" + struct ofpact_output *output; + enum ofperr error; - default: - return OFPERR_OFPBAC_BAD_TYPE; + output = ofpact_put_OUTPUT(out); + output->max_len = ntohs(oao->max_len); + + error = ofputil_port_from_ofp11(oao->port, &output->port); + if (error) { + return error; } + + return ofpact_check_output_port(output->port, OFPP_MAX); +} + +/* Converts 'mp' into an OpenFlow NXAST_MULTIPATH action, which it appends to + * 'openflow'. */ +static void +multipath_to_nxast(const struct ofpact_multipath *mp, struct ofpbuf *openflow) +{ + struct nx_action_multipath *nam = put_NXAST_MULTIPATH(openflow); + + nam->fields = htons(mp->fields); + nam->basis = htons(mp->basis); + nam->algorithm = htons(mp->algorithm); + nam->max_link = htons(mp->max_link); + nam->arg = htonl(mp->arg); + nam->ofs_nbits = nxm_encode_ofs_nbits(mp->dst.ofs, mp->dst.n_bits); + nam->dst = htonl(mp->dst.field->nxm_header); } +/* Converts 'nam' into 'mp'. Returns 0 if successful, otherwise an + * OFPERR_*. */ static enum ofperr -ofpact_from_nxast(const union ofp_action *a, enum ofputil_action_code code, - struct ofpbuf *out) +multipath_from_openflow(const struct nx_action_multipath *nam, + struct ofpact_multipath *mp) { - struct ofpact_tunnel *tunnel; - enum ofperr error = 0; + uint32_t n_links = ntohs(nam->max_link) + 1; + size_t min_n_bits = log_2_ceil(n_links); + + ofpact_init_MULTIPATH(mp); + mp->fields = ntohs(nam->fields); + mp->basis = ntohs(nam->basis); + mp->algorithm = ntohs(nam->algorithm); + mp->max_link = ntohs(nam->max_link); + mp->arg = ntohl(nam->arg); + mp->dst.field = mf_from_nxm_header(ntohl(nam->dst)); + mp->dst.ofs = nxm_decode_ofs(nam->ofs_nbits); + mp->dst.n_bits = nxm_decode_n_bits(nam->ofs_nbits); + + if (!flow_hash_fields_valid(mp->fields)) { + VLOG_WARN_RL(&rl, "unsupported fields %d", (int) mp->fields); + return OFPERR_OFPBAC_BAD_ARGUMENT; + } else if (mp->algorithm != NX_MP_ALG_MODULO_N + && mp->algorithm != NX_MP_ALG_HASH_THRESHOLD + && mp->algorithm != NX_MP_ALG_HRW + && mp->algorithm != NX_MP_ALG_ITER_HASH) { + VLOG_WARN_RL(&rl, "unsupported algorithm %d", (int) mp->algorithm); + return OFPERR_OFPBAC_BAD_ARGUMENT; + } else if (mp->dst.n_bits < min_n_bits) { + VLOG_WARN_RL(&rl, "multipath action requires at least %"PRIuSIZE" bits for " + "%"PRIu32" links", min_n_bits, n_links); + return OFPERR_OFPBAC_BAD_ARGUMENT; + } - switch (code) { - case OFPUTIL_ACTION_INVALID: -#define OFPAT10_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM: -#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM: -#define OFPAT13_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM: -#include "ofp-util.def" - OVS_NOT_REACHED(); + return multipath_check(mp, NULL); +} - case OFPUTIL_NXAST_RESUBMIT: - resubmit_from_openflow(&a->resubmit, out); - break; +/* Checks that 'nab' specifies a bundle action which is supported by this + * bundle module. Uses the 'max_ports' parameter to validate each port using + * ofputil_check_output_port(). Returns 0 if 'nab' is supported, otherwise an + * OFPERR_* error code. */ +static enum ofperr +bundle_from_openflow(enum ofp_raw_action_type raw, + const struct nx_action_bundle *nab, + struct ofpbuf *ofpacts) +{ + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + struct ofpact_bundle *bundle; + uint32_t slave_type; + size_t slaves_size, i; + enum ofperr error; - case OFPUTIL_NXAST_SET_TUNNEL: - tunnel = ofpact_put_SET_TUNNEL(out); - tunnel->ofpact.compat = code; - tunnel->tun_id = ntohl(a->set_tunnel.tun_id); - break; + bundle = ofpact_put_BUNDLE(ofpacts); + + bundle->n_slaves = ntohs(nab->n_slaves); + bundle->basis = ntohs(nab->basis); + bundle->fields = ntohs(nab->fields); + bundle->algorithm = ntohs(nab->algorithm); + slave_type = ntohl(nab->slave_type); + slaves_size = ntohs(nab->len) - sizeof *nab; + + error = OFPERR_OFPBAC_BAD_ARGUMENT; + if (!flow_hash_fields_valid(bundle->fields)) { + VLOG_WARN_RL(&rl, "unsupported fields %d", (int) bundle->fields); + } else if (bundle->n_slaves > BUNDLE_MAX_SLAVES) { + VLOG_WARN_RL(&rl, "too many slaves"); + } else if (bundle->algorithm != NX_BD_ALG_HRW + && bundle->algorithm != NX_BD_ALG_ACTIVE_BACKUP) { + VLOG_WARN_RL(&rl, "unsupported algorithm %d", (int) bundle->algorithm); + } else if (slave_type != NXM_OF_IN_PORT) { + VLOG_WARN_RL(&rl, "unsupported slave type %"PRIu16, slave_type); + } else { + error = 0; + } - case OFPUTIL_NXAST_WRITE_METADATA: - error = metadata_from_nxast(&a->write_metadata, out); - break; + if (!is_all_zeros(nab->zero, sizeof nab->zero)) { + VLOG_WARN_RL(&rl, "reserved field is nonzero"); + error = OFPERR_OFPBAC_BAD_ARGUMENT; + } - case OFPUTIL_NXAST_SET_QUEUE: - ofpact_put_SET_QUEUE(out)->queue_id = ntohl(a->set_queue.queue_id); - break; + if (raw == NXAST_RAW_BUNDLE && (nab->ofs_nbits || nab->dst)) { + VLOG_WARN_RL(&rl, "bundle action has nonzero reserved fields"); + error = OFPERR_OFPBAC_BAD_ARGUMENT; + } - case OFPUTIL_NXAST_POP_QUEUE: - ofpact_put_POP_QUEUE(out); - break; + if (raw == NXAST_RAW_BUNDLE_LOAD) { + bundle->dst.field = mf_from_nxm_header(ntohl(nab->dst)); + bundle->dst.ofs = nxm_decode_ofs(nab->ofs_nbits); + bundle->dst.n_bits = nxm_decode_n_bits(nab->ofs_nbits); - case OFPUTIL_NXAST_REG_MOVE: - error = nxm_reg_move_from_openflow(&a->reg_move, out); - break; + if (bundle->dst.n_bits < 16) { + VLOG_WARN_RL(&rl, "bundle_load action requires at least 16 bit " + "destination."); + error = OFPERR_OFPBAC_BAD_ARGUMENT; + } + } - case OFPUTIL_NXAST_REG_LOAD: - error = nxm_reg_load_from_openflow(&a->reg_load, out); - break; + if (slaves_size < bundle->n_slaves * sizeof(ovs_be16)) { + VLOG_WARN_RL(&rl, "Nicira action %s only has %"PRIuSIZE" bytes " + "allocated for slaves. %"PRIuSIZE" bytes are required for " + "%"PRIu16" slaves.", + raw == NXAST_RAW_BUNDLE ? "bundle" : "bundle_load", + slaves_size, + bundle->n_slaves * sizeof(ovs_be16), bundle->n_slaves); + error = OFPERR_OFPBAC_BAD_LEN; + } - case OFPUTIL_NXAST_STACK_PUSH: - error = nxm_stack_push_from_openflow(&a->stack, out); - break; + for (i = 0; i < bundle->n_slaves; i++) { + uint16_t ofp_port = ntohs(((ovs_be16 *)(nab + 1))[i]); + ofpbuf_put(ofpacts, &ofp_port, sizeof ofp_port); + } - case OFPUTIL_NXAST_STACK_POP: - error = nxm_stack_pop_from_openflow(&a->stack, out); - break; + bundle = ofpacts->frame; + ofpact_update_len(ofpacts, &bundle->ofpact); - case OFPUTIL_NXAST_NOTE: - note_from_openflow(&a->note, out); - break; + if (!error) { + error = bundle_check(bundle, OFPP_MAX, NULL); + } + return error; +} - case OFPUTIL_NXAST_SET_TUNNEL64: - tunnel = ofpact_put_SET_TUNNEL(out); - tunnel->ofpact.compat = code; - tunnel->tun_id = ntohll(a->set_tunnel64.tun_id); - break; +static void +bundle_to_nxast(const struct ofpact_bundle *bundle, struct ofpbuf *openflow) +{ + int slaves_len = ROUND_UP(2 * bundle->n_slaves, OFP_ACTION_ALIGN); + struct nx_action_bundle *nab; + ovs_be16 *slaves; + size_t i; - case OFPUTIL_NXAST_MULTIPATH: - error = multipath_from_openflow(&a->multipath, - ofpact_put_MULTIPATH(out)); - break; + nab = (bundle->dst.field + ? put_NXAST_BUNDLE_LOAD(openflow) + : put_NXAST_BUNDLE(openflow)); + nab->len = htons(ntohs(nab->len) + slaves_len); + nab->algorithm = htons(bundle->algorithm); + nab->fields = htons(bundle->fields); + nab->basis = htons(bundle->basis); + nab->slave_type = htonl(NXM_OF_IN_PORT); + nab->n_slaves = htons(bundle->n_slaves); + if (bundle->dst.field) { + nab->ofs_nbits = nxm_encode_ofs_nbits(bundle->dst.ofs, + bundle->dst.n_bits); + nab->dst = htonl(bundle->dst.field->nxm_header); + } - case OFPUTIL_NXAST_BUNDLE: - case OFPUTIL_NXAST_BUNDLE_LOAD: - error = bundle_from_openflow(&a->bundle, out); - break; + slaves = ofpbuf_put_zeros(openflow, slaves_len); + for (i = 0; i < bundle->n_slaves; i++) { + slaves[i] = htons(ofp_to_u16(bundle->slaves[i])); + } +} - case OFPUTIL_NXAST_OUTPUT_REG: - error = output_reg_from_openflow(&a->output_reg, out); - break; +static ovs_be16 +get_be16(const void **pp) +{ + const ovs_be16 *p = *pp; + ovs_be16 value = *p; + *pp = p + 1; + return value; +} - case OFPUTIL_NXAST_RESUBMIT_TABLE: - error = resubmit_table_from_openflow(&a->resubmit, out); - break; +static ovs_be32 +get_be32(const void **pp) +{ + const ovs_be32 *p = *pp; + ovs_be32 value = get_unaligned_be32(p); + *pp = p + 1; + return value; +} - case OFPUTIL_NXAST_LEARN: - error = learn_from_openflow(&a->learn, out); - break; +static void +get_subfield(int n_bits, const void **p, struct mf_subfield *sf) +{ + sf->field = mf_from_nxm_header(ntohl(get_be32(p))); + sf->ofs = ntohs(get_be16(p)); + sf->n_bits = n_bits; +} - case OFPUTIL_NXAST_EXIT: - ofpact_put_EXIT(out); - break; +static unsigned int +learn_min_len(uint16_t header) +{ + int n_bits = header & NX_LEARN_N_BITS_MASK; + int src_type = header & NX_LEARN_SRC_MASK; + int dst_type = header & NX_LEARN_DST_MASK; + unsigned int min_len; + + min_len = 0; + if (src_type == NX_LEARN_SRC_FIELD) { + min_len += sizeof(ovs_be32); /* src_field */ + min_len += sizeof(ovs_be16); /* src_ofs */ + } else { + min_len += DIV_ROUND_UP(n_bits, 16); + } + if (dst_type == NX_LEARN_DST_MATCH || + dst_type == NX_LEARN_DST_LOAD) { + min_len += sizeof(ovs_be32); /* dst_field */ + min_len += sizeof(ovs_be16); /* dst_ofs */ + } + return min_len; +} - case OFPUTIL_NXAST_DEC_TTL: - error = dec_ttl_from_openflow(out, code); - break; +/* Converts 'nal' into a "struct ofpact_learn" and appends that struct to + * 'ofpacts'. Returns 0 if successful, otherwise an OFPERR_*. */ +static enum ofperr +learn_from_openflow(const struct nx_action_learn *nal, struct ofpbuf *ofpacts) +{ + struct ofpact_learn *learn; + const void *p, *end; - case OFPUTIL_NXAST_DEC_TTL_CNT_IDS: - error = dec_ttl_cnt_ids_from_openflow(&a->cnt_ids, out); - break; + if (nal->pad) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } - case OFPUTIL_NXAST_FIN_TIMEOUT: - fin_timeout_from_openflow(&a->fin_timeout, out); - break; + learn = ofpact_put_LEARN(ofpacts); - case OFPUTIL_NXAST_CONTROLLER: - controller_from_openflow(&a->controller, out); - break; + learn->idle_timeout = ntohs(nal->idle_timeout); + learn->hard_timeout = ntohs(nal->hard_timeout); + learn->priority = ntohs(nal->priority); + learn->cookie = nal->cookie; + learn->table_id = nal->table_id; + learn->fin_idle_timeout = ntohs(nal->fin_idle_timeout); + learn->fin_hard_timeout = ntohs(nal->fin_hard_timeout); - case OFPUTIL_NXAST_PUSH_MPLS: - error = push_mpls_from_openflow(a->push_mpls.ethertype, out); - break; + learn->flags = ntohs(nal->flags); + if (learn->flags & ~(NX_LEARN_F_SEND_FLOW_REM | + NX_LEARN_F_DELETE_LEARNED)) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } - case OFPUTIL_NXAST_SET_MPLS_LABEL: - ofpact_put_SET_MPLS_LABEL(out)->label = a->mpls_label.label; - break; + if (learn->table_id == 0xff) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } - case OFPUTIL_NXAST_SET_MPLS_TC: - ofpact_put_SET_MPLS_TC(out)->tc = a->mpls_tc.tc; - break; + end = (char *) nal + ntohs(nal->len); + for (p = nal + 1; p != end; ) { + struct ofpact_learn_spec *spec; + uint16_t header = ntohs(get_be16(&p)); - case OFPUTIL_NXAST_SET_MPLS_TTL: - ofpact_put_SET_MPLS_TTL(out)->ttl = a->mpls_ttl.ttl; - break; + if (!header) { + break; + } - case OFPUTIL_NXAST_DEC_MPLS_TTL: - ofpact_put_DEC_MPLS_TTL(out); - break; + spec = ofpbuf_put_zeros(ofpacts, sizeof *spec); + learn = ofpacts->frame; + learn->n_specs++; - case OFPUTIL_NXAST_POP_MPLS: - ofpact_put_POP_MPLS(out)->ethertype = a->pop_mpls.ethertype; - break; + spec->src_type = header & NX_LEARN_SRC_MASK; + spec->dst_type = header & NX_LEARN_DST_MASK; + spec->n_bits = header & NX_LEARN_N_BITS_MASK; - case OFPUTIL_NXAST_SAMPLE: - error = sample_from_openflow(&a->sample, out); - break; + /* Check for valid src and dst type combination. */ + if (spec->dst_type == NX_LEARN_DST_MATCH || + spec->dst_type == NX_LEARN_DST_LOAD || + (spec->dst_type == NX_LEARN_DST_OUTPUT && + spec->src_type == NX_LEARN_SRC_FIELD)) { + /* OK. */ + } else { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + + /* Check that the arguments don't overrun the end of the action. */ + if ((char *) end - (char *) p < learn_min_len(header)) { + return OFPERR_OFPBAC_BAD_LEN; + } + + /* Get the source. */ + if (spec->src_type == NX_LEARN_SRC_FIELD) { + get_subfield(spec->n_bits, &p, &spec->src); + } else { + int p_bytes = 2 * DIV_ROUND_UP(spec->n_bits, 16); + + bitwise_copy(p, p_bytes, 0, + &spec->src_imm, sizeof spec->src_imm, 0, + spec->n_bits); + p = (const uint8_t *) p + p_bytes; + } + + /* Get the destination. */ + if (spec->dst_type == NX_LEARN_DST_MATCH || + spec->dst_type == NX_LEARN_DST_LOAD) { + get_subfield(spec->n_bits, &p, &spec->dst); + } } + ofpact_update_len(ofpacts, &learn->ofpact); - return error; + if (!is_all_zeros(p, (char *) end - (char *) p)) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + + return 0; } -static enum ofperr -ofpact_from_openflow10(const union ofp_action *a, - enum ofp_version version OVS_UNUSED, - struct ofpbuf *out) +static void +put_be16(struct ofpbuf *b, ovs_be16 x) { - enum ofputil_action_code code; - enum ofperr error; - struct ofpact_vlan_vid *vlan_vid; - struct ofpact_vlan_pcp *vlan_pcp; + ofpbuf_put(b, &x, sizeof x); +} - error = decode_openflow10_action(a, &code); - if (error) { - return error; +static void +put_be32(struct ofpbuf *b, ovs_be32 x) +{ + ofpbuf_put(b, &x, sizeof x); +} + +static void +put_u16(struct ofpbuf *b, uint16_t x) +{ + put_be16(b, htons(x)); +} + +static void +put_u32(struct ofpbuf *b, uint32_t x) +{ + put_be32(b, htonl(x)); +} + +/* Converts 'learn' into a "struct nx_action_learn" and appends that action to + * 'ofpacts'. */ +static void +learn_to_nxast(const struct ofpact_learn *learn, struct ofpbuf *openflow) +{ + const struct ofpact_learn_spec *spec; + struct nx_action_learn *nal; + size_t start_ofs; + + start_ofs = ofpbuf_size(openflow); + nal = put_NXAST_LEARN(openflow); + nal->idle_timeout = htons(learn->idle_timeout); + nal->hard_timeout = htons(learn->hard_timeout); + nal->fin_idle_timeout = htons(learn->fin_idle_timeout); + nal->fin_hard_timeout = htons(learn->fin_hard_timeout); + nal->priority = htons(learn->priority); + nal->cookie = learn->cookie; + nal->flags = htons(learn->flags); + nal->table_id = learn->table_id; + + for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) { + put_u16(openflow, spec->n_bits | spec->dst_type | spec->src_type); + + if (spec->src_type == NX_LEARN_SRC_FIELD) { + put_u32(openflow, spec->src.field->nxm_header); + put_u16(openflow, spec->src.ofs); + } else { + size_t n_dst_bytes = 2 * DIV_ROUND_UP(spec->n_bits, 16); + uint8_t *bits = ofpbuf_put_zeros(openflow, n_dst_bytes); + bitwise_copy(&spec->src_imm, sizeof spec->src_imm, 0, + bits, n_dst_bytes, 0, + spec->n_bits); + } + + if (spec->dst_type == NX_LEARN_DST_MATCH || + spec->dst_type == NX_LEARN_DST_LOAD) { + put_u32(openflow, spec->dst.field->nxm_header); + put_u16(openflow, spec->dst.ofs); + } } - switch (code) { - case OFPUTIL_ACTION_INVALID: -#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM: -#define OFPAT13_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM: -#include "ofp-util.def" - OVS_NOT_REACHED(); + if ((ofpbuf_size(openflow) - start_ofs) % 8) { + ofpbuf_put_zeros(openflow, 8 - (ofpbuf_size(openflow) - start_ofs) % 8); + } - case OFPUTIL_OFPAT10_OUTPUT: + nal = ofpbuf_at_assert(openflow, start_ofs, sizeof *nal); + nal->len = htons(ofpbuf_size(openflow) - start_ofs); +} + +static enum ofperr +ofpact_decode(const union ofp_action *a, enum ofp_raw_action_type raw, + uint64_t arg, struct ofpbuf *out) +{ + switch (raw) { + case OFPAT_RAW10_OUTPUT: return output_from_openflow10(&a->output10, out); + case OFPAT_RAW11_OUTPUT: + return output_from_openflow11(&a->ofp11_output, out); - case OFPUTIL_OFPAT10_SET_VLAN_VID: - if (a->vlan_vid.vlan_vid & ~htons(0xfff)) { + case OFPAT_RAW10_SET_VLAN_VID: + case OFPAT_RAW11_SET_VLAN_VID: + if (arg & ~0xfff) { return OFPERR_OFPBAC_BAD_ARGUMENT; + } else { + struct ofpact_vlan_vid *vlan_vid = ofpact_put_SET_VLAN_VID(out); + vlan_vid->vlan_vid = arg; + vlan_vid->push_vlan_if_needed = raw == OFPAT_RAW10_SET_VLAN_VID; + } + break; + + case OFPAT_RAW10_SET_VLAN_PCP: + case OFPAT_RAW11_SET_VLAN_PCP: + if (arg & ~7) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } else { + struct ofpact_vlan_pcp *vlan_pcp = ofpact_put_SET_VLAN_PCP(out); + vlan_pcp->vlan_pcp = arg; + vlan_pcp->push_vlan_if_needed = raw == OFPAT_RAW10_SET_VLAN_PCP; } - vlan_vid = ofpact_put_SET_VLAN_VID(out); - vlan_vid->vlan_vid = ntohs(a->vlan_vid.vlan_vid); - vlan_vid->push_vlan_if_needed = true; - vlan_vid->ofpact.compat = code; break; - case OFPUTIL_OFPAT10_SET_VLAN_PCP: - if (a->vlan_pcp.vlan_pcp & ~7) { + case OFPAT_RAW11_PUSH_VLAN: + if (arg != ETH_TYPE_VLAN_8021Q) { + /* XXX 802.1AD(QinQ) isn't supported at the moment */ return OFPERR_OFPBAC_BAD_ARGUMENT; } - vlan_pcp = ofpact_put_SET_VLAN_PCP(out); - vlan_pcp->vlan_pcp = a->vlan_pcp.vlan_pcp; - vlan_pcp->push_vlan_if_needed = true; - vlan_pcp->ofpact.compat = code; + ofpact_put_PUSH_VLAN(out); break; - case OFPUTIL_OFPAT10_STRIP_VLAN: - ofpact_put_STRIP_VLAN(out)->ofpact.compat = code; + case OFPAT_RAW10_STRIP_VLAN: + case OFPAT_RAW11_POP_VLAN: + ofpact_put_STRIP_VLAN(out)->ofpact.raw = raw; break; - case OFPUTIL_OFPAT10_SET_DL_SRC: + case OFPAT_RAW_SET_DL_SRC: memcpy(ofpact_put_SET_ETH_SRC(out)->mac, a->dl_addr.dl_addr, ETH_ADDR_LEN); break; - case OFPUTIL_OFPAT10_SET_DL_DST: + case OFPAT_RAW_SET_DL_DST: memcpy(ofpact_put_SET_ETH_DST(out)->mac, a->dl_addr.dl_addr, ETH_ADDR_LEN); break; - case OFPUTIL_OFPAT10_SET_NW_SRC: - ofpact_put_SET_IPV4_SRC(out)->ipv4 = a->nw_addr.nw_addr; + case OFPAT_RAW_SET_NW_SRC: + ofpact_put_SET_IPV4_SRC(out)->ipv4 = htonl(arg); + break; + + case OFPAT_RAW_SET_NW_DST: + ofpact_put_SET_IPV4_DST(out)->ipv4 = htonl(arg); break; - case OFPUTIL_OFPAT10_SET_NW_DST: - ofpact_put_SET_IPV4_DST(out)->ipv4 = a->nw_addr.nw_addr; + case OFPAT_RAW_SET_NW_TOS: + if (arg & ~IP_DSCP_MASK) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + ofpact_put_SET_IP_DSCP(out)->dscp = arg; break; - case OFPUTIL_OFPAT10_SET_NW_TOS: - if (a->nw_tos.nw_tos & ~IP_DSCP_MASK) { + case OFPAT_RAW11_SET_NW_ECN: + if (arg & ~IP_ECN_MASK) { return OFPERR_OFPBAC_BAD_ARGUMENT; } - ofpact_put_SET_IP_DSCP(out)->dscp = a->nw_tos.nw_tos; + ofpact_put_SET_IP_ECN(out)->ecn = arg; break; - case OFPUTIL_OFPAT10_SET_TP_SRC: - ofpact_put_SET_L4_SRC_PORT(out)->port = ntohs(a->tp_port.tp_port); + case OFPAT_RAW_SET_TP_SRC: + ofpact_put_SET_L4_SRC_PORT(out)->port = arg; break; - case OFPUTIL_OFPAT10_SET_TP_DST: - ofpact_put_SET_L4_DST_PORT(out)->port = ntohs(a->tp_port.tp_port); + case OFPAT_RAW_SET_TP_DST: + ofpact_put_SET_L4_DST_PORT(out)->port = arg; + break; + + case OFPAT_RAW10_ENQUEUE: + return enqueue_from_openflow10(&a->enqueue, out); + + case OFPAT_RAW11_COPY_TTL_OUT: + case OFPAT_RAW11_COPY_TTL_IN: + VLOG_WARN_RL(&rl, "ttl copy actions not yet supported"); + return OFPERR_OFPBAC_BAD_TYPE; + + case OFPAT_RAW_SET_QUEUE: + ofpact_put_SET_QUEUE(out)->queue_id = arg; + break; + case OFPAT_RAW_DEC_NW_TTL: + dec_ttl_from_openflow(out); break; - case OFPUTIL_OFPAT10_ENQUEUE: - error = enqueue_from_openflow10(&a->enqueue, out); + case OFPAT_RAW11_SET_NW_TTL: + ofpact_put_SET_IP_TTL(out)->ttl = arg; + break; + + case OFPAT_RAW12_SET_FIELD: + return set_field_from_openflow(&a->set_field, out); + + case OFPAT_RAW_SET_MPLS_LABEL: + ofpact_put_SET_MPLS_LABEL(out)->label = htonl(arg); + break; + + case OFPAT_RAW_SET_MPLS_TC: + ofpact_put_SET_MPLS_TC(out)->tc = arg; + break; + + case OFPAT_RAW_SET_MPLS_TTL: + ofpact_put_SET_MPLS_TTL(out)->ttl = arg; + break; + + case OFPAT_RAW_DEC_MPLS_TTL: + ofpact_put_DEC_MPLS_TTL(out); break; -#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM: -#include "ofp-util.def" - return ofpact_from_nxast(a, code, out); + case OFPAT_RAW_PUSH_MPLS: + return push_mpls_from_openflow(htons(arg), out); + + case OFPAT_RAW_POP_MPLS: + ofpact_put_POP_MPLS(out)->ethertype = htons(arg); + break; + + case OFPAT_RAW11_GROUP: + ofpact_put_GROUP(out)->group_id = arg; + break; + + case NXAST_RAW_RESUBMIT: + resubmit_from_openflow(arg, out); + break; + + case NXAST_RAW_SET_TUNNEL: { + struct ofpact_tunnel *tunnel = ofpact_put_SET_TUNNEL(out); + tunnel->ofpact.raw = raw; + tunnel->tun_id = arg; + break; } - return error; -} + case NXAST_RAW_WRITE_METADATA: + return metadata_from_nxast(&a->write_metadata, out); + + case NXAST_RAW_POP_QUEUE: + ofpact_put_POP_QUEUE(out); + break; -static enum ofperr ofpact_from_openflow11(const union ofp_action *, - enum ofp_version, - struct ofpbuf *out); + case OFPAT_RAW15_COPY_FIELD: + return copy_field_from_openflow(&a->copy_field, out); + case NXAST_RAW_REG_MOVE: + return nxm_reg_move_from_openflow(&a->reg_move, out); -static inline union ofp_action * -action_next(const union ofp_action *a) -{ - return ((union ofp_action *) (void *) - ((uint8_t *) a + ntohs(a->header.len))); -} + case NXAST_RAW_REG_LOAD: + return nxm_reg_load_from_openflow(&a->reg_load, out); -static inline bool -action_is_valid(const union ofp_action *a, size_t max_actions) -{ - uint16_t len = ntohs(a->header.len); - return (!(len % OFP_ACTION_ALIGN) - && len >= OFP_ACTION_ALIGN - && len / OFP_ACTION_ALIGN <= max_actions); -} + case NXAST_RAW_STACK_PUSH: + return nxm_stack_push_from_openflow(&a->stack, out); + + case NXAST_RAW_STACK_POP: + return nxm_stack_pop_from_openflow(&a->stack, out); + + case NXAST_RAW_NOTE: + note_from_openflow(&a->note, out); + break; -/* This macro is careful to check for actions with bad lengths. */ -#define ACTION_FOR_EACH(ITER, LEFT, ACTIONS, MAX_ACTIONS) \ - for ((ITER) = (ACTIONS), (LEFT) = (MAX_ACTIONS); \ - (LEFT) > 0 && action_is_valid(ITER, LEFT); \ - ((LEFT) -= ntohs((ITER)->header.len) / OFP_ACTION_ALIGN, \ - (ITER) = action_next(ITER))) + case NXAST_RAW_SET_TUNNEL64: { + struct ofpact_tunnel *tunnel = ofpact_put_SET_TUNNEL(out); + tunnel->ofpact.raw = raw; + tunnel->tun_id = arg; + break; + } + + case NXAST_RAW_MULTIPATH: + return multipath_from_openflow(&a->multipath, + ofpact_put_MULTIPATH(out)); + + case NXAST_RAW_BUNDLE: + case NXAST_RAW_BUNDLE_LOAD: + return bundle_from_openflow(raw, &a->bundle, out); + + case NXAST_RAW_OUTPUT_REG: + return output_reg_from_openflow(&a->output_reg, out); + + case NXAST_RAW_RESUBMIT_TABLE: + return resubmit_table_from_openflow(&a->resubmit, out); + + case NXAST_RAW_LEARN: + return learn_from_openflow(&a->learn, out); + + case NXAST_RAW_EXIT: + ofpact_put_EXIT(out); + break; + + case NXAST_RAW_DEC_TTL_CNT_IDS: + return dec_ttl_cnt_ids_from_openflow(&a->cnt_ids, out); + + case NXAST_RAW_FIN_TIMEOUT: + fin_timeout_from_openflow(&a->fin_timeout, out); + break; + + case NXAST_RAW_CONTROLLER: + controller_from_openflow(&a->controller, out); + break; + + case NXAST_RAW_SAMPLE: + return sample_from_openflow(&a->sample, out); + } + + return 0; +} static void log_bad_action(const union ofp_action *actions, size_t max_actions, @@ -642,32 +2348,30 @@ log_bad_action(const union ofp_action *actions, size_t max_actions, } static enum ofperr -ofpacts_from_openflow(const union ofp_action *in, size_t n_in, - enum ofp_version version, struct ofpbuf *out) +ofpacts_decode(const void *actions, size_t actions_len, + enum ofp_version ofp_version, struct ofpbuf *ofpacts) { - const union ofp_action *a; - size_t left; + struct ofpbuf openflow; - enum ofperr (*ofpact_from_openflow)(const union ofp_action *a, - enum ofp_version, - struct ofpbuf *out) = - (version == OFP10_VERSION) ? - ofpact_from_openflow10 : ofpact_from_openflow11; + ofpbuf_use_const(&openflow, actions, actions_len); + while (ofpbuf_size(&openflow)) { + const union ofp_action *action = ofpbuf_data(&openflow); + enum ofp_raw_action_type raw; + enum ofperr error; + uint64_t arg; + + error = ofpact_pull_raw(&openflow, ofp_version, &raw, &arg); + if (!error) { + error = ofpact_decode(action, raw, arg, ofpacts); + } - ACTION_FOR_EACH (a, left, in, n_in) { - enum ofperr error = ofpact_from_openflow(a, version, out); if (error) { - log_bad_action(in, n_in, a, error); + log_bad_action(actions, actions_len * 8, action, error); return error; } } - if (left) { - enum ofperr error = OFPERR_OFPBAC_BAD_LEN; - log_bad_action(in, n_in, a, error); - return error; - } - ofpact_pad(out); + ofpact_pad(ofpacts); return 0; } @@ -712,8 +2416,7 @@ ofpacts_pull_openflow_actions(struct ofpbuf *openflow, return OFPERR_OFPBRC_BAD_LEN; } - error = ofpacts_from_openflow(actions, actions_len / OFP_ACTION_ALIGN, - version, ofpacts); + error = ofpacts_decode(actions, actions_len, version, ofpacts); if (error) { ofpbuf_clear(ofpacts); return error; @@ -729,96 +2432,6 @@ ofpacts_pull_openflow_actions(struct ofpbuf *openflow, /* OpenFlow 1.1 actions. */ -/* Parses 'a' to determine its type. On success stores the correct type into - * '*code' and returns 0. On failure returns an OFPERR_* error code and - * '*code' is indeterminate. - * - * The caller must have already verified that 'a''s length is potentially - * correct (that is, a->header.len is nonzero and a multiple of - * OFP_ACTION_ALIGN and no longer than the amount of space allocated to 'a'). - * - * This function verifies that 'a''s length is correct for the type of action - * that it represents. */ -static enum ofperr -decode_openflow11_action(const union ofp_action *a, - enum ofputil_action_code *code) -{ - uint16_t len; - - switch (a->type) { - case CONSTANT_HTONS(OFPAT11_EXPERIMENTER): - return decode_nxast_action(a, code); - -#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \ - case CONSTANT_HTONS(ENUM): \ - len = ntohs(a->header.len); \ - if (EXTENSIBLE \ - ? len >= sizeof(struct STRUCT) \ - : len == sizeof(struct STRUCT)) { \ - *code = OFPUTIL_##ENUM; \ - return 0; \ - } else { \ - return OFPERR_OFPBAC_BAD_LEN; \ - } \ - OVS_NOT_REACHED(); -#include "ofp-util.def" - - default: - return OFPERR_OFPBAC_BAD_TYPE; - } -} - -static enum ofperr -set_field_from_openflow(const struct ofp12_action_set_field *oasf, - struct ofpbuf *ofpacts) -{ - uint16_t oasf_len = ntohs(oasf->len); - uint32_t oxm_header = ntohl(oasf->dst); - uint8_t oxm_length = NXM_LENGTH(oxm_header); - struct ofpact_set_field *sf; - const struct mf_field *mf; - - /* ofp12_action_set_field is padded to 64 bits by zero */ - if (oasf_len != ROUND_UP(sizeof *oasf + oxm_length, 8)) { - return OFPERR_OFPBAC_BAD_SET_LEN; - } - if (!is_all_zeros((const uint8_t *)oasf + sizeof *oasf + oxm_length, - oasf_len - oxm_length - sizeof *oasf)) { - return OFPERR_OFPBAC_BAD_SET_ARGUMENT; - } - - if (NXM_HASMASK(oxm_header)) { - return OFPERR_OFPBAC_BAD_SET_TYPE; - } - mf = mf_from_nxm_header(oxm_header); - if (!mf) { - return OFPERR_OFPBAC_BAD_SET_TYPE; - } - ovs_assert(mf->n_bytes == oxm_length); - /* oxm_length is now validated to be compatible with mf_value. */ - if (!mf->writable) { - VLOG_WARN_RL(&rl, "destination field %s is not writable", mf->name); - return OFPERR_OFPBAC_BAD_SET_ARGUMENT; - } - sf = ofpact_put_SET_FIELD(ofpacts); - sf->field = mf; - memcpy(&sf->value, oasf + 1, mf->n_bytes); - - /* The value must be valid for match and must have the OFPVID_PRESENT bit - * on for OXM_OF_VLAN_VID. */ - if (!mf_is_value_valid(mf, &sf->value) - || (mf->id == MFF_VLAN_VID - && !(sf->value.be16 & htons(OFPVID12_PRESENT)))) { - struct ds ds = DS_EMPTY_INITIALIZER; - mf_format(mf, &sf->value, NULL, &ds); - VLOG_WARN_RL(&rl, "Invalid value for set field %s: %s", - mf->name, ds_cstr(&ds)); - ds_destroy(&ds); - - return OFPERR_OFPBAC_BAD_SET_ARGUMENT; - } - return 0; -} static void set_field_to_openflow12(const struct ofpact_set_field *sf, @@ -829,7 +2442,7 @@ set_field_to_openflow12(const struct ofpact_set_field *sf, struct ofp12_action_set_field *oasf; char *value; - oasf = ofputil_put_OFPAT12_SET_FIELD(openflow); + oasf = ofpact_put_raw(openflow, version, OFPAT_RAW12_SET_FIELD, 0); oasf->dst = htonl(mf_oxm_header(sf->field->id, version)); oasf->len = htons(sizeof *oasf + padded_value_len); @@ -848,17 +2461,17 @@ set_field_to_nxast(const struct ofpact_set_field *sf, struct ofpbuf *openflow) ovs_assert(mf->n_bytes == 16); /* IPv6 addr. */ /* Split into 64bit chunks */ /* Lower bits first. */ - narl = ofputil_put_NXAST_REG_LOAD(openflow); + narl = put_NXAST_REG_LOAD(openflow); narl->ofs_nbits = nxm_encode_ofs_nbits(0, 64); narl->dst = htonl(mf->nxm_header); memcpy(&narl->value, &sf->value.ipv6.s6_addr[8], sizeof narl->value); /* Higher bits next. */ - narl = ofputil_put_NXAST_REG_LOAD(openflow); + narl = put_NXAST_REG_LOAD(openflow); narl->ofs_nbits = nxm_encode_ofs_nbits(64, mf->n_bits - 64); narl->dst = htonl(mf->nxm_header); memcpy(&narl->value, &sf->value.ipv6.s6_addr[0], sizeof narl->value); } else { - narl = ofputil_put_NXAST_REG_LOAD(openflow); + narl = put_NXAST_REG_LOAD(openflow); narl->ofs_nbits = nxm_encode_ofs_nbits(0, mf->n_bits); narl->dst = htonl(mf->nxm_header); memset(&narl->value, 0, 8 - mf->n_bytes); @@ -895,18 +2508,19 @@ set_field_to_openflow11(const struct ofpact_set_field *sf, /* Push a VLAN tag, if one was not seen at action validation * time. */ if (!sf->flow_has_vlan) { - ofputil_put_OFPAT11_PUSH_VLAN(openflow)->ethertype - = htons(ETH_TYPE_VLAN_8021Q); + ofpact_put_raw(openflow, OFP11_VERSION, OFPAT_RAW11_PUSH_VLAN, + ETH_TYPE_VLAN_8021Q); } - ofputil_put_OFPAT11_SET_VLAN_VID(openflow)->vlan_vid - = sf->value.be16 & htons(VLAN_VID_MASK); - ofputil_put_OFPAT11_SET_VLAN_PCP(openflow)->vlan_pcp - = vlan_tci_to_pcp(sf->value.be16); + ofpact_put_raw(openflow, OFP11_VERSION, OFPAT_RAW11_SET_VLAN_VID, + ntohs(sf->value.be16) & VLAN_VID_MASK); + ofpact_put_raw(openflow, OFP11_VERSION, OFPAT_RAW11_SET_VLAN_PCP, + vlan_tci_to_pcp(sf->value.be16)); } else { /* If the flow did not match on vlan, we have no way of * knowing if the vlan tag exists, so we must POP just to be * sure. */ - ofputil_put_OFPAT11_POP_VLAN(openflow); + ofpact_put_raw(openflow, OFP11_VERSION, OFPAT_RAW11_POP_VLAN, + ETH_TYPE_VLAN_8021Q); } break; @@ -915,69 +2529,69 @@ set_field_to_openflow11(const struct ofpact_set_field *sf, * Set field on OXM_OF_VLAN_VID onlyapplies to an existing vlan * tag. Clear the OFPVID_PRESENT bit. */ - ofputil_put_OFPAT11_SET_VLAN_VID(openflow)->vlan_vid - = sf->value.be16 & htons(VLAN_VID_MASK); + ofpact_put_raw(openflow, OFP11_VERSION, OFPAT_RAW11_SET_VLAN_VID, + ntohs(sf->value.be16) & VLAN_VID_MASK); break; case MFF_VLAN_PCP: /* OXM VLAN_PCP to OpenFlow 1.1. * OXM_OF_VLAN_PCP only applies to existing vlan tag. */ - ofputil_put_OFPAT11_SET_VLAN_PCP(openflow)->vlan_pcp = sf->value.u8; + ofpact_put_raw(openflow, OFP11_VERSION, OFPAT_RAW11_SET_VLAN_PCP, + sf->value.u8); break; case MFF_ETH_SRC: - memcpy(ofputil_put_OFPAT11_SET_DL_SRC(openflow)->dl_addr, + memcpy(put_OFPAT_SET_DL_SRC(openflow, OFP11_VERSION)->dl_addr, sf->value.mac, ETH_ADDR_LEN); break; case MFF_ETH_DST: - memcpy(ofputil_put_OFPAT11_SET_DL_DST(openflow)->dl_addr, + memcpy(put_OFPAT_SET_DL_DST(openflow, OFP11_VERSION)->dl_addr, sf->value.mac, ETH_ADDR_LEN); break; case MFF_MPLS_LABEL: - ofputil_put_OFPAT11_SET_MPLS_LABEL(openflow)->mpls_label = - sf->value.be32; + put_OFPAT_SET_MPLS_LABEL(openflow, OFP11_VERSION, sf->value.be32); break; case MFF_MPLS_TC: - ofputil_put_OFPAT11_SET_MPLS_TC(openflow)->mpls_tc = sf->value.u8; + put_OFPAT_SET_MPLS_TC(openflow, OFP11_VERSION, sf->value.u8); break; case MFF_IPV4_SRC: - ofputil_put_OFPAT11_SET_NW_SRC(openflow)->nw_addr = sf->value.be32; + put_OFPAT_SET_NW_SRC(openflow, OFP11_VERSION, sf->value.be32); break; case MFF_IPV4_DST: - ofputil_put_OFPAT11_SET_NW_DST(openflow)->nw_addr = sf->value.be32; + put_OFPAT_SET_NW_DST(openflow, OFP11_VERSION, sf->value.be32); break; case MFF_IP_DSCP: - ofputil_put_OFPAT11_SET_NW_TOS(openflow)->nw_tos = sf->value.u8; + put_OFPAT_SET_NW_TOS(openflow, OFP11_VERSION, sf->value.u8); break; case MFF_IP_DSCP_SHIFTED: - ofputil_put_OFPAT11_SET_NW_TOS(openflow)->nw_tos = sf->value.u8 << 2; + put_OFPAT_SET_NW_TOS(openflow, OFP11_VERSION, sf->value.u8 << 2); break; case MFF_IP_ECN: - ofputil_put_OFPAT11_SET_NW_ECN(openflow)->nw_ecn = sf->value.u8; + put_OFPAT11_SET_NW_ECN(openflow, sf->value.u8); break; case MFF_IP_TTL: - ofputil_put_OFPAT11_SET_NW_TTL(openflow)->nw_ttl = sf->value.u8; + put_OFPAT11_SET_NW_TTL(openflow, sf->value.u8); break; case MFF_TCP_SRC: case MFF_UDP_SRC: case MFF_SCTP_SRC: - ofputil_put_OFPAT11_SET_TP_SRC(openflow)->tp_port = sf->value.be16; + put_OFPAT_SET_TP_SRC(openflow, sf->value.be16); break; case MFF_TCP_DST: case MFF_UDP_DST: case MFF_SCTP_DST: - ofputil_put_OFPAT11_SET_TP_DST(openflow)->tp_port = sf->value.be16; + put_OFPAT_SET_TP_DST(openflow, sf->value.be16); break; default: @@ -1005,12 +2619,12 @@ set_field_to_openflow10(const struct ofpact_set_field *sf, * If CFI=0, strip VLAN header, if any. */ if (sf->value.be16 & htons(VLAN_CFI)) { - ofputil_put_OFPAT10_SET_VLAN_VID(openflow)->vlan_vid - = sf->value.be16 & htons(VLAN_VID_MASK); - ofputil_put_OFPAT10_SET_VLAN_PCP(openflow)->vlan_pcp - = vlan_tci_to_pcp(sf->value.be16); + put_OFPAT10_SET_VLAN_VID(openflow, + ntohs(sf->value.be16) & VLAN_VID_MASK); + put_OFPAT10_SET_VLAN_PCP(openflow, + vlan_tci_to_pcp(sf->value.be16)); } else { - ofputil_put_OFPAT10_STRIP_VLAN(openflow); + put_OFPAT10_STRIP_VLAN(openflow); } break; @@ -1019,50 +2633,50 @@ set_field_to_openflow10(const struct ofpact_set_field *sf, * Set field on OXM_OF_VLAN_VID onlyapplies to an existing vlan * tag. Clear the OFPVID_PRESENT bit. */ - ofputil_put_OFPAT10_SET_VLAN_VID(openflow)->vlan_vid - = sf->value.be16 & htons(VLAN_VID_MASK); + put_OFPAT10_SET_VLAN_VID(openflow, + ntohs(sf->value.be16) & VLAN_VID_MASK); break; case MFF_VLAN_PCP: /* OXM VLAN_PCP to OpenFlow 1.0. * OXM_OF_VLAN_PCP only applies to existing vlan tag. */ - ofputil_put_OFPAT10_SET_VLAN_PCP(openflow)->vlan_pcp = sf->value.u8; + put_OFPAT10_SET_VLAN_PCP(openflow, sf->value.u8); break; case MFF_ETH_SRC: - memcpy(ofputil_put_OFPAT10_SET_DL_SRC(openflow)->dl_addr, + memcpy(put_OFPAT_SET_DL_SRC(openflow, OFP10_VERSION)->dl_addr, sf->value.mac, ETH_ADDR_LEN); break; case MFF_ETH_DST: - memcpy(ofputil_put_OFPAT10_SET_DL_DST(openflow)->dl_addr, + memcpy(put_OFPAT_SET_DL_DST(openflow, OFP10_VERSION)->dl_addr, sf->value.mac, ETH_ADDR_LEN); break; case MFF_IPV4_SRC: - ofputil_put_OFPAT10_SET_NW_SRC(openflow)->nw_addr = sf->value.be32; + put_OFPAT_SET_NW_SRC(openflow, OFP10_VERSION, sf->value.be32); break; case MFF_IPV4_DST: - ofputil_put_OFPAT10_SET_NW_DST(openflow)->nw_addr = sf->value.be32; + put_OFPAT_SET_NW_DST(openflow, OFP10_VERSION, sf->value.be32); break; case MFF_IP_DSCP: - ofputil_put_OFPAT10_SET_NW_TOS(openflow)->nw_tos = sf->value.u8; + put_OFPAT_SET_NW_TOS(openflow, OFP10_VERSION, sf->value.u8); break; case MFF_IP_DSCP_SHIFTED: - ofputil_put_OFPAT10_SET_NW_TOS(openflow)->nw_tos = sf->value.u8 << 2; + put_OFPAT_SET_NW_TOS(openflow, OFP10_VERSION, sf->value.u8 << 2); break; case MFF_TCP_SRC: case MFF_UDP_SRC: - ofputil_put_OFPAT10_SET_TP_SRC(openflow)->tp_port = sf->value.be16; + put_OFPAT_SET_TP_SRC(openflow, sf->value.be16); break; case MFF_TCP_DST: case MFF_UDP_DST: - ofputil_put_OFPAT10_SET_TP_DST(openflow)->tp_port = sf->value.be16; + put_OFPAT_SET_TP_DST(openflow, sf->value.be16); break; default: @@ -1088,190 +2702,6 @@ set_field_to_openflow(const struct ofpact_set_field *sf, } } -static enum ofperr -output_from_openflow11(const struct ofp11_action_output *oao, - struct ofpbuf *out) -{ - struct ofpact_output *output; - enum ofperr error; - - output = ofpact_put_OUTPUT(out); - output->max_len = ntohs(oao->max_len); - - error = ofputil_port_from_ofp11(oao->port, &output->port); - if (error) { - return error; - } - - return ofpact_check_output_port(output->port, OFPP_MAX); -} - -static enum ofperr -ofpact_from_openflow11(const union ofp_action *a, enum ofp_version version, - struct ofpbuf *out) -{ - enum ofputil_action_code code; - enum ofperr error; - struct ofpact_vlan_vid *vlan_vid; - struct ofpact_vlan_pcp *vlan_pcp; - - error = decode_openflow11_action(a, &code); - if (error) { - return error; - } - - if (version >= OFP12_VERSION) { - switch ((int)code) { - case OFPUTIL_OFPAT11_SET_VLAN_VID: - case OFPUTIL_OFPAT11_SET_VLAN_PCP: - case OFPUTIL_OFPAT11_SET_DL_SRC: - case OFPUTIL_OFPAT11_SET_DL_DST: - case OFPUTIL_OFPAT11_SET_NW_SRC: - case OFPUTIL_OFPAT11_SET_NW_DST: - case OFPUTIL_OFPAT11_SET_NW_TOS: - case OFPUTIL_OFPAT11_SET_NW_ECN: - case OFPUTIL_OFPAT11_SET_TP_SRC: - case OFPUTIL_OFPAT11_SET_TP_DST: - VLOG_WARN_RL(&rl, "Deprecated action %s received over %s", - ofputil_action_name_from_code(code), - ofputil_version_to_string(version)); - } - } - - switch (code) { - case OFPUTIL_ACTION_INVALID: -#define OFPAT10_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM: -#define OFPAT13_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM: -#include "ofp-util.def" - OVS_NOT_REACHED(); - - case OFPUTIL_OFPAT11_OUTPUT: - return output_from_openflow11(&a->ofp11_output, out); - - case OFPUTIL_OFPAT11_SET_VLAN_VID: - if (a->vlan_vid.vlan_vid & ~htons(0xfff)) { - return OFPERR_OFPBAC_BAD_ARGUMENT; - } - vlan_vid = ofpact_put_SET_VLAN_VID(out); - vlan_vid->vlan_vid = ntohs(a->vlan_vid.vlan_vid); - vlan_vid->push_vlan_if_needed = false; - vlan_vid->ofpact.compat = code; - break; - - case OFPUTIL_OFPAT11_SET_VLAN_PCP: - if (a->vlan_pcp.vlan_pcp & ~7) { - return OFPERR_OFPBAC_BAD_ARGUMENT; - } - vlan_pcp = ofpact_put_SET_VLAN_PCP(out); - vlan_pcp->vlan_pcp = a->vlan_pcp.vlan_pcp; - vlan_pcp->push_vlan_if_needed = false; - vlan_pcp->ofpact.compat = code; - break; - - case OFPUTIL_OFPAT11_PUSH_VLAN: - if (a->push.ethertype != htons(ETH_TYPE_VLAN_8021Q)) { - /* XXX 802.1AD(QinQ) isn't supported at the moment */ - return OFPERR_OFPBAC_BAD_ARGUMENT; - } - ofpact_put_PUSH_VLAN(out); - break; - - case OFPUTIL_OFPAT11_POP_VLAN: - ofpact_put_STRIP_VLAN(out)->ofpact.compat = code; - break; - - case OFPUTIL_OFPAT11_SET_QUEUE: - ofpact_put_SET_QUEUE(out)->queue_id = - ntohl(a->ofp11_set_queue.queue_id); - break; - - case OFPUTIL_OFPAT11_SET_DL_SRC: - memcpy(ofpact_put_SET_ETH_SRC(out)->mac, a->dl_addr.dl_addr, - ETH_ADDR_LEN); - break; - - case OFPUTIL_OFPAT11_SET_DL_DST: - memcpy(ofpact_put_SET_ETH_DST(out)->mac, a->dl_addr.dl_addr, - ETH_ADDR_LEN); - break; - - case OFPUTIL_OFPAT11_DEC_NW_TTL: - dec_ttl_from_openflow(out, code); - break; - - case OFPUTIL_OFPAT11_SET_NW_SRC: - ofpact_put_SET_IPV4_SRC(out)->ipv4 = a->nw_addr.nw_addr; - break; - - case OFPUTIL_OFPAT11_SET_NW_DST: - ofpact_put_SET_IPV4_DST(out)->ipv4 = a->nw_addr.nw_addr; - break; - - case OFPUTIL_OFPAT11_SET_NW_TOS: - if (a->nw_tos.nw_tos & ~IP_DSCP_MASK) { - return OFPERR_OFPBAC_BAD_ARGUMENT; - } - ofpact_put_SET_IP_DSCP(out)->dscp = a->nw_tos.nw_tos; - break; - - case OFPUTIL_OFPAT11_SET_NW_ECN: - if (a->nw_ecn.nw_ecn & ~IP_ECN_MASK) { - return OFPERR_OFPBAC_BAD_ARGUMENT; - } - ofpact_put_SET_IP_ECN(out)->ecn = a->nw_ecn.nw_ecn; - break; - - case OFPUTIL_OFPAT11_SET_NW_TTL: - ofpact_put_SET_IP_TTL(out)->ttl = a->nw_ttl.nw_ttl; - break; - - case OFPUTIL_OFPAT11_SET_TP_SRC: - ofpact_put_SET_L4_SRC_PORT(out)->port = ntohs(a->tp_port.tp_port); - break; - - case OFPUTIL_OFPAT11_SET_TP_DST: - ofpact_put_SET_L4_DST_PORT(out)->port = ntohs(a->tp_port.tp_port); - break; - - case OFPUTIL_OFPAT12_SET_FIELD: - return set_field_from_openflow(&a->set_field, out); - - case OFPUTIL_OFPAT11_SET_MPLS_LABEL: - ofpact_put_SET_MPLS_LABEL(out)->label = a->ofp11_mpls_label.mpls_label; - break; - - case OFPUTIL_OFPAT11_SET_MPLS_TC: - ofpact_put_SET_MPLS_TC(out)->tc = a->ofp11_mpls_tc.mpls_tc; - break; - - case OFPUTIL_OFPAT11_SET_MPLS_TTL: - ofpact_put_SET_MPLS_TTL(out)->ttl = a->ofp11_mpls_ttl.mpls_ttl; - break; - - case OFPUTIL_OFPAT11_DEC_MPLS_TTL: - ofpact_put_DEC_MPLS_TTL(out); - break; - - case OFPUTIL_OFPAT11_PUSH_MPLS: - error = push_mpls_from_openflow(a->push.ethertype, out); - break; - - case OFPUTIL_OFPAT11_POP_MPLS: - ofpact_put_POP_MPLS(out)->ethertype = a->ofp11_pop_mpls.ethertype; - break; - - case OFPUTIL_OFPAT11_GROUP: - ofpact_put_GROUP(out)->group_id = ntohl(a->group.group_id); - break; - -#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM: -#include "ofp-util.def" - return ofpact_from_nxast(a, code, out); - } - - return error; -} - /* True if an action sets the value of a field * in a way that is compatibile with the action set. * False otherwise. */ @@ -1486,15 +2916,15 @@ ofpacts_execute_action_set(struct ofpbuf *action_list, static enum ofperr -ofpacts_from_openflow11_for_action_set(const union ofp_action *in, - size_t n_in, enum ofp_version version, - struct ofpbuf *out) +ofpacts_decode_for_action_set(const union ofp_action *in, + size_t n_in, enum ofp_version version, + struct ofpbuf *out) { enum ofperr error; struct ofpact *a; size_t start = ofpbuf_size(out); - error = ofpacts_from_openflow(in, n_in, version, out); + error = ofpacts_decode(in, n_in, version, out); if (error) { return error; @@ -1794,10 +3224,10 @@ decode_openflow11_instructions(const struct ofp11_instruction insts[], static void get_actions_from_instruction(const struct ofp11_instruction *inst, const union ofp_action **actions, - size_t *max_actions) + size_t *actions_len) { *actions = ALIGNED_CAST(const union ofp_action *, inst + 1); - *max_actions = (ntohs(inst->len) - sizeof *inst) / OFP11_INSTRUCTION_ALIGN; + *actions_len = ntohs(inst->len) - sizeof *inst; } enum ofperr @@ -1848,11 +3278,11 @@ ofpacts_pull_openflow_instructions(struct ofpbuf *openflow, } if (insts[OVSINST_OFPIT11_APPLY_ACTIONS]) { const union ofp_action *actions; - size_t max_actions; + size_t actions_len; get_actions_from_instruction(insts[OVSINST_OFPIT11_APPLY_ACTIONS], - &actions, &max_actions); - error = ofpacts_from_openflow(actions, max_actions, version, ofpacts); + &actions, &actions_len); + error = ofpacts_decode(actions, actions_len, version, ofpacts); if (error) { goto exit; } @@ -1865,17 +3295,17 @@ ofpacts_pull_openflow_instructions(struct ofpbuf *openflow, if (insts[OVSINST_OFPIT11_WRITE_ACTIONS]) { struct ofpact_nest *on; const union ofp_action *actions; - size_t max_actions; + size_t actions_len; size_t start; ofpact_pad(ofpacts); start = ofpbuf_size(ofpacts); - ofpact_put(ofpacts, OFPACT_WRITE_ACTIONS, - offsetof(struct ofpact_nest, actions)); + on = ofpact_put(ofpacts, OFPACT_WRITE_ACTIONS, + offsetof(struct ofpact_nest, actions)); get_actions_from_instruction(insts[OVSINST_OFPIT11_WRITE_ACTIONS], - &actions, &max_actions); - error = ofpacts_from_openflow11_for_action_set(actions, max_actions, - version, ofpacts); + &actions, &actions_len); + error = ofpacts_decode_for_action_set(actions, actions_len, + version, ofpacts); if (error) { goto exit; } @@ -2289,9 +3719,9 @@ ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len) static void ofpact_output_reg_to_nxast(const struct ofpact_output_reg *output_reg, - struct ofpbuf *out) + struct ofpbuf *out) { - struct nx_action_output_reg *naor = ofputil_put_NXAST_OUTPUT_REG(out); + struct nx_action_output_reg *naor = put_NXAST_OUTPUT_REG(out); naor->ofs_nbits = nxm_encode_ofs_nbits(output_reg->src.ofs, output_reg->src.n_bits); @@ -2303,16 +3733,16 @@ static void ofpact_resubmit_to_nxast(const struct ofpact_resubmit *resubmit, struct ofpbuf *out) { - struct nx_action_resubmit *nar; + uint16_t in_port = ofp_to_u16(resubmit->in_port); if (resubmit->table_id == 0xff - && resubmit->ofpact.compat != OFPUTIL_NXAST_RESUBMIT_TABLE) { - nar = ofputil_put_NXAST_RESUBMIT(out); + && resubmit->ofpact.raw != NXAST_RAW_RESUBMIT_TABLE) { + put_NXAST_RESUBMIT(out, in_port); } else { - nar = ofputil_put_NXAST_RESUBMIT_TABLE(out); + struct nx_action_resubmit *nar = put_NXAST_RESUBMIT_TABLE(out); nar->table = resubmit->table_id; + nar->in_port = htons(in_port); } - nar->in_port = htons(ofp_to_u16(resubmit->in_port)); } static void @@ -2322,10 +3752,10 @@ ofpact_set_tunnel_to_nxast(const struct ofpact_tunnel *tunnel, uint64_t tun_id = tunnel->tun_id; if (tun_id <= UINT32_MAX - && tunnel->ofpact.compat != OFPUTIL_NXAST_SET_TUNNEL64) { - ofputil_put_NXAST_SET_TUNNEL(out)->tun_id = htonl(tun_id); + && tunnel->ofpact.raw != NXAST_RAW_SET_TUNNEL64) { + put_NXAST_SET_TUNNEL(out, tun_id); } else { - ofputil_put_NXAST_SET_TUNNEL64(out)->tun_id = htonll(tun_id); + put_NXAST_SET_TUNNEL64(out, tun_id); } } @@ -2335,7 +3765,7 @@ ofpact_write_metadata_to_nxast(const struct ofpact_metadata *om, { struct nx_action_write_metadata *nawm; - nawm = ofputil_put_NXAST_WRITE_METADATA(out); + nawm = put_NXAST_WRITE_METADATA(out); nawm->metadata = om->metadata; nawm->mask = om->mask; } @@ -2348,7 +3778,7 @@ ofpact_note_to_nxast(const struct ofpact_note *note, struct ofpbuf *out) unsigned int remainder; unsigned int len; - ofputil_put_NXAST_NOTE(out); + put_NXAST_NOTE(out); ofpbuf_set_size(out, ofpbuf_size(out) - sizeof nan->note); ofpbuf_put(out, note->data, note->length); @@ -2368,7 +3798,7 @@ ofpact_controller_to_nxast(const struct ofpact_controller *oc, { struct nx_action_controller *nac; - nac = ofputil_put_NXAST_CONTROLLER(out); + nac = put_NXAST_CONTROLLER(out); nac->max_len = htons(oc->max_len); nac->controller_id = htons(oc->controller_id); nac->reason = oc->reason; @@ -2376,13 +3806,10 @@ ofpact_controller_to_nxast(const struct ofpact_controller *oc, static void ofpact_dec_ttl_to_nxast(const struct ofpact_cnt_ids *oc_ids, - struct ofpbuf *out) + struct ofpbuf *out, enum ofp_version version) { - if (oc_ids->ofpact.compat == OFPUTIL_NXAST_DEC_TTL) { - ofputil_put_NXAST_DEC_TTL(out); - } else { - struct nx_action_cnt_ids *nac_ids = - ofputil_put_NXAST_DEC_TTL_CNT_IDS(out); + if (oc_ids->ofpact.raw == NXAST_RAW_DEC_TTL_CNT_IDS) { + struct nx_action_cnt_ids *nac_ids = put_NXAST_DEC_TTL_CNT_IDS(out); int ids_len = ROUND_UP(2 * oc_ids->n_controllers, OFP_ACTION_ALIGN); ovs_be16 *ids; size_t i; @@ -2394,6 +3821,8 @@ ofpact_dec_ttl_to_nxast(const struct ofpact_cnt_ids *oc_ids, for (i = 0; i < oc_ids->n_controllers; i++) { ids[i] = htons(oc_ids->cnt_ids[i]); } + } else { + put_OFPAT_DEC_NW_TTL(out, version); } } @@ -2401,7 +3830,7 @@ static void ofpact_fin_timeout_to_nxast(const struct ofpact_fin_timeout *fin_timeout, struct ofpbuf *out) { - struct nx_action_fin_timeout *naft = ofputil_put_NXAST_FIN_TIMEOUT(out); + struct nx_action_fin_timeout *naft = put_NXAST_FIN_TIMEOUT(out); naft->fin_idle_timeout = htons(fin_timeout->fin_idle_timeout); naft->fin_hard_timeout = htons(fin_timeout->fin_hard_timeout); } @@ -2412,7 +3841,7 @@ ofpact_sample_to_nxast(const struct ofpact_sample *os, { struct nx_action_sample *nas; - nas = ofputil_put_NXAST_SAMPLE(out); + nas = put_NXAST_SAMPLE(out); nas->probability = htons(os->probability); nas->collector_set_id = htonl(os->collector_set_id); nas->obs_domain_id = htonl(os->obs_domain_id); @@ -2420,7 +3849,35 @@ ofpact_sample_to_nxast(const struct ofpact_sample *os, } static void -ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out) +reg_move_to_openflow(const struct ofpact_reg_move *move, + enum ofp_version ofp_version, struct ofpbuf *openflow) +{ + if (ofp_version < OFP15_VERSION + || move->ofpact.raw == NXAST_RAW_REG_MOVE) { + struct nx_action_reg_move *narm; + + narm = put_NXAST_REG_MOVE(openflow); + narm->n_bits = htons(move->dst.n_bits); + narm->src_ofs = htons(move->src.ofs); + narm->dst_ofs = htons(move->dst.ofs); + narm->src = htonl(move->src.field->nxm_header); + narm->dst = htonl(move->dst.field->nxm_header); + } else { + struct ofp15_action_copy_field *copy; + + copy = put_OFPAT15_COPY_FIELD(openflow); + copy->n_bits = htons(move->dst.n_bits); + copy->src_offset = htons(move->src.ofs); + copy->dst_offset = htons(move->dst.ofs); + copy->oxm_id_len = htons(8); + copy->src = htonl(mf_oxm_header(move->src.field->id, ofp_version)); + copy->dst = htonl(mf_oxm_header(move->dst.field->id, ofp_version)); + } +} + +static void +ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out, + enum ofp_version version) { switch (a->type) { case OFPACT_CONTROLLER: @@ -2436,7 +3893,7 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out) break; case OFPACT_REG_MOVE: - nxm_reg_move_to_nxast(ofpact_get_REG_MOVE(a), out); + reg_move_to_openflow(ofpact_get_REG_MOVE(a), OFP10_VERSION, out); break; case OFPACT_REG_LOAD: @@ -2452,26 +3909,26 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out) break; case OFPACT_DEC_TTL: - ofpact_dec_ttl_to_nxast(ofpact_get_DEC_TTL(a), out); + ofpact_dec_ttl_to_nxast(ofpact_get_DEC_TTL(a), out, version); break; case OFPACT_SET_MPLS_LABEL: - ofputil_put_NXAST_SET_MPLS_LABEL(out)->label - = ofpact_get_SET_MPLS_LABEL(a)->label; + put_OFPAT_SET_MPLS_LABEL(out, OFP10_VERSION, + ofpact_get_SET_MPLS_LABEL(a)->label); break; case OFPACT_SET_MPLS_TC: - ofputil_put_NXAST_SET_MPLS_TC(out)->tc - = ofpact_get_SET_MPLS_TC(a)->tc; + put_OFPAT_SET_MPLS_TC(out, OFP10_VERSION, + ofpact_get_SET_MPLS_TC(a)->tc); break; case OFPACT_SET_MPLS_TTL: - ofputil_put_NXAST_SET_MPLS_TTL(out)->ttl - = ofpact_get_SET_MPLS_TTL(a)->ttl; + put_OFPAT_SET_MPLS_TTL(out, OFP10_VERSION, + ofpact_get_SET_MPLS_TTL(a)->ttl); break; case OFPACT_DEC_MPLS_TTL: - ofputil_put_NXAST_DEC_MPLS_TTL(out); + put_OFPAT_DEC_MPLS_TTL(out, OFP10_VERSION); break; case OFPACT_SET_TUNNEL: @@ -2483,12 +3940,12 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out) break; case OFPACT_SET_QUEUE: - ofputil_put_NXAST_SET_QUEUE(out)->queue_id - = htonl(ofpact_get_SET_QUEUE(a)->queue_id); + put_OFPAT_SET_QUEUE(out, OFP10_VERSION, + ofpact_get_SET_QUEUE(a)->queue_id); break; case OFPACT_POP_QUEUE: - ofputil_put_NXAST_POP_QUEUE(out); + put_NXAST_POP_QUEUE(out); break; case OFPACT_FIN_TIMEOUT: @@ -2512,17 +3969,17 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out) break; case OFPACT_EXIT: - ofputil_put_NXAST_EXIT(out); + put_NXAST_EXIT(out); break; case OFPACT_PUSH_MPLS: - ofputil_put_NXAST_PUSH_MPLS(out)->ethertype = - ofpact_get_PUSH_MPLS(a)->ethertype; + put_OFPAT_PUSH_MPLS(out, OFP10_VERSION, + ofpact_get_PUSH_MPLS(a)->ethertype); break; case OFPACT_POP_MPLS: - ofputil_put_NXAST_POP_MPLS(out)->ethertype = - ofpact_get_POP_MPLS(a)->ethertype; + put_OFPAT_POP_MPLS(out, OFP10_VERSION, + ofpact_get_POP_MPLS(a)->ethertype); break; case OFPACT_SAMPLE: @@ -2562,7 +4019,7 @@ ofpact_output_to_openflow10(const struct ofpact_output *output, { struct ofp10_action_output *oao; - oao = ofputil_put_OFPAT10_OUTPUT(out); + oao = put_OFPAT10_OUTPUT(out); oao->port = htons(ofp_to_u16(output->port)); oao->max_len = htons(output->max_len); } @@ -2573,7 +4030,7 @@ ofpact_enqueue_to_openflow10(const struct ofpact_enqueue *enqueue, { struct ofp10_action_enqueue *oae; - oae = ofputil_put_OFPAT10_ENQUEUE(out); + oae = put_OFPAT10_ENQUEUE(out); oae->port = htons(ofp_to_u16(enqueue->port)); oae->queue_id = htonl(enqueue->queue); } @@ -2591,52 +4048,48 @@ ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out) break; case OFPACT_SET_VLAN_VID: - ofputil_put_OFPAT10_SET_VLAN_VID(out)->vlan_vid - = htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid); + put_OFPAT10_SET_VLAN_VID(out, ofpact_get_SET_VLAN_VID(a)->vlan_vid); break; case OFPACT_SET_VLAN_PCP: - ofputil_put_OFPAT10_SET_VLAN_PCP(out)->vlan_pcp - = ofpact_get_SET_VLAN_PCP(a)->vlan_pcp; + put_OFPAT10_SET_VLAN_PCP(out, ofpact_get_SET_VLAN_PCP(a)->vlan_pcp); break; case OFPACT_STRIP_VLAN: - ofputil_put_OFPAT10_STRIP_VLAN(out); + put_OFPAT10_STRIP_VLAN(out); break; case OFPACT_SET_ETH_SRC: - memcpy(ofputil_put_OFPAT10_SET_DL_SRC(out)->dl_addr, + memcpy(put_OFPAT_SET_DL_SRC(out, OFP10_VERSION)->dl_addr, ofpact_get_SET_ETH_SRC(a)->mac, ETH_ADDR_LEN); break; case OFPACT_SET_ETH_DST: - memcpy(ofputil_put_OFPAT10_SET_DL_DST(out)->dl_addr, + memcpy(put_OFPAT_SET_DL_DST(out, OFP10_VERSION)->dl_addr, ofpact_get_SET_ETH_DST(a)->mac, ETH_ADDR_LEN); break; case OFPACT_SET_IPV4_SRC: - ofputil_put_OFPAT10_SET_NW_SRC(out)->nw_addr - = ofpact_get_SET_IPV4_SRC(a)->ipv4; + put_OFPAT_SET_NW_SRC(out, OFP10_VERSION, + ofpact_get_SET_IPV4_SRC(a)->ipv4); break; case OFPACT_SET_IPV4_DST: - ofputil_put_OFPAT10_SET_NW_DST(out)->nw_addr - = ofpact_get_SET_IPV4_DST(a)->ipv4; + put_OFPAT_SET_NW_DST(out, OFP10_VERSION, + ofpact_get_SET_IPV4_DST(a)->ipv4); break; case OFPACT_SET_IP_DSCP: - ofputil_put_OFPAT10_SET_NW_TOS(out)->nw_tos - = ofpact_get_SET_IP_DSCP(a)->dscp; + put_OFPAT_SET_NW_TOS(out, OFP10_VERSION, + ofpact_get_SET_IP_DSCP(a)->dscp); break; case OFPACT_SET_L4_SRC_PORT: - ofputil_put_OFPAT10_SET_TP_SRC(out)->tp_port - = htons(ofpact_get_SET_L4_SRC_PORT(a)->port); + put_OFPAT_SET_TP_SRC(out, htons(ofpact_get_SET_L4_SRC_PORT(a)->port)); break; case OFPACT_SET_L4_DST_PORT: - ofputil_put_OFPAT10_SET_TP_DST(out)->tp_port - = htons(ofpact_get_SET_L4_DST_PORT(a)->port); + put_OFPAT_SET_TP_DST(out, htons(ofpact_get_SET_L4_DST_PORT(a)->port)); break; case OFPACT_PUSH_VLAN: @@ -2685,7 +4138,7 @@ ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out) case OFPACT_PUSH_MPLS: case OFPACT_POP_MPLS: case OFPACT_SAMPLE: - ofpact_to_nxast(a, out); + ofpact_to_nxast(a, out, OFP10_VERSION); break; } } @@ -2698,7 +4151,7 @@ ofpact_output_to_openflow11(const struct ofpact_output *output, { struct ofp11_action_output *oao; - oao = ofputil_put_OFPAT11_OUTPUT(out); + oao = put_OFPAT11_OUTPUT(out); oao->port = ofputil_port_to_ofp11(output->port); oao->max_len = htons(output->max_len); } @@ -2708,16 +4161,37 @@ ofpact_dec_ttl_to_openflow11(const struct ofpact_cnt_ids *dec_ttl, struct ofpbuf *out) { if (dec_ttl->n_controllers == 1 && dec_ttl->cnt_ids[0] == 0 - && (!dec_ttl->ofpact.compat || - dec_ttl->ofpact.compat == OFPUTIL_OFPAT11_DEC_NW_TTL)) { - ofputil_put_OFPAT11_DEC_NW_TTL(out); + && (!dec_ttl->ofpact.raw || + dec_ttl->ofpact.raw == OFPAT_RAW_DEC_NW_TTL)) { + put_OFPAT_DEC_NW_TTL(out, OFP11_VERSION); } else { - ofpact_dec_ttl_to_nxast(dec_ttl, out); + ofpact_dec_ttl_to_nxast(dec_ttl, out, OFP11_VERSION); + } +} + +static void +ofpact_put_set_field(struct ofpbuf *openflow, enum ofp_version ofp_version, + enum mf_field_id field, uint64_t value) +{ + const struct mf_field *mf = mf_from_id(field); + struct ofp12_action_set_field *oasf; + ovs_be64 n_value; + + oasf = put_OFPAT12_SET_FIELD(openflow); + oasf->dst = htonl(mf_oxm_header(mf->id, ofp_version)); + oasf->len = htons(sizeof *oasf + 8); + + ovs_assert(mf->n_bytes <= 8); + if (mf->n_bytes < 8) { + value <<= 8 * (8 - mf->n_bytes); } + n_value = htonll(value); + ofpbuf_put(openflow, &n_value, 8); } static void -ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out) +ofpact_to_openflow11(const struct ofpact *a, enum ofp_version ofp_version, + struct ofpbuf *out) { switch (a->type) { case OFPACT_OUTPUT: @@ -2727,109 +4201,190 @@ ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out) /* XXX */ break; - case OFPACT_SET_VLAN_VID: + case OFPACT_SET_VLAN_VID: { + const struct ofpact_vlan_vid *vlan_vid = ofpact_get_SET_VLAN_VID(a); + uint16_t vid = vlan_vid->vlan_vid; + /* Push a VLAN tag, if one was not seen at action validation time. */ - if (!ofpact_get_SET_VLAN_VID(a)->flow_has_vlan - && ofpact_get_SET_VLAN_VID(a)->push_vlan_if_needed) { - ofputil_put_OFPAT11_PUSH_VLAN(out)->ethertype - = htons(ETH_TYPE_VLAN_8021Q); + if (!vlan_vid->flow_has_vlan && vlan_vid->push_vlan_if_needed) { + put_OFPAT11_PUSH_VLAN(out, htons(ETH_TYPE_VLAN_8021Q)); + } + if (ofp_version >= OFP12_VERSION) { + ofpact_put_set_field(out, ofp_version, + MFF_VLAN_VID, vid | OFPVID12_PRESENT); + } else { + put_OFPAT11_SET_VLAN_VID(out, vid); } - ofputil_put_OFPAT11_SET_VLAN_VID(out)->vlan_vid - = htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid); break; + } + + case OFPACT_SET_VLAN_PCP: { + const struct ofpact_vlan_pcp *vlan_pcp = ofpact_get_SET_VLAN_PCP(a); + uint8_t pcp = vlan_pcp->vlan_pcp; - case OFPACT_SET_VLAN_PCP: /* Push a VLAN tag, if one was not seen at action validation time. */ - if (!ofpact_get_SET_VLAN_PCP(a)->flow_has_vlan - && ofpact_get_SET_VLAN_PCP(a)->push_vlan_if_needed) { - ofputil_put_OFPAT11_PUSH_VLAN(out)->ethertype - = htons(ETH_TYPE_VLAN_8021Q); + if (!vlan_pcp->flow_has_vlan && vlan_pcp->push_vlan_if_needed) { + put_OFPAT11_PUSH_VLAN(out, htons(ETH_TYPE_VLAN_8021Q)); + } + + if (ofp_version >= OFP12_VERSION) { + ofpact_put_set_field(out, ofp_version, MFF_VLAN_PCP, pcp); + } else { + put_OFPAT11_SET_VLAN_PCP(out, + ofpact_get_SET_VLAN_PCP(a)->vlan_pcp); } - ofputil_put_OFPAT11_SET_VLAN_PCP(out)->vlan_pcp - = ofpact_get_SET_VLAN_PCP(a)->vlan_pcp; break; + } case OFPACT_STRIP_VLAN: - ofputil_put_OFPAT11_POP_VLAN(out); + put_OFPAT11_POP_VLAN(out); break; case OFPACT_PUSH_VLAN: /* XXX ETH_TYPE_VLAN_8021AD case */ - ofputil_put_OFPAT11_PUSH_VLAN(out)->ethertype = - htons(ETH_TYPE_VLAN_8021Q); + put_OFPAT11_PUSH_VLAN(out, htons(ETH_TYPE_VLAN_8021Q)); break; case OFPACT_SET_QUEUE: - ofputil_put_OFPAT11_SET_QUEUE(out)->queue_id - = htonl(ofpact_get_SET_QUEUE(a)->queue_id); + put_OFPAT_SET_QUEUE(out, OFP11_VERSION, + ofpact_get_SET_QUEUE(a)->queue_id); break; - case OFPACT_SET_ETH_SRC: - memcpy(ofputil_put_OFPAT11_SET_DL_SRC(out)->dl_addr, - ofpact_get_SET_ETH_SRC(a)->mac, ETH_ADDR_LEN); + case OFPACT_SET_ETH_SRC: { + const uint8_t *mac = ofpact_get_SET_ETH_SRC(a)->mac; + if (ofp_version >= OFP12_VERSION) { + ofpact_put_set_field(out, ofp_version, + MFF_ETH_SRC, eth_addr_to_uint64(mac)); + } else { + memcpy(put_OFPAT_SET_DL_SRC(out, OFP11_VERSION)->dl_addr, + mac, ETH_ADDR_LEN); + } break; + } - case OFPACT_SET_ETH_DST: - memcpy(ofputil_put_OFPAT11_SET_DL_DST(out)->dl_addr, - ofpact_get_SET_ETH_DST(a)->mac, ETH_ADDR_LEN); + case OFPACT_SET_ETH_DST: { + const uint8_t *mac = ofpact_get_SET_ETH_DST(a)->mac; + if (ofp_version >= OFP12_VERSION) { + ofpact_put_set_field(out, ofp_version, + MFF_ETH_DST, eth_addr_to_uint64(mac)); + } else { + memcpy(put_OFPAT_SET_DL_DST(out, OFP11_VERSION)->dl_addr, + mac, ETH_ADDR_LEN); + } break; + } - case OFPACT_SET_IPV4_SRC: - ofputil_put_OFPAT11_SET_NW_SRC(out)->nw_addr - = ofpact_get_SET_IPV4_SRC(a)->ipv4; + case OFPACT_SET_IPV4_SRC: { + ovs_be32 ipv4 = ofpact_get_SET_IPV4_SRC(a)->ipv4; + if (ofp_version >= OFP12_VERSION) { + ofpact_put_set_field(out, ofp_version, MFF_IPV4_SRC, ntohl(ipv4)); + } else { + put_OFPAT_SET_NW_SRC(out, OFP11_VERSION, ipv4); + } break; + } - case OFPACT_SET_IPV4_DST: - ofputil_put_OFPAT11_SET_NW_DST(out)->nw_addr - = ofpact_get_SET_IPV4_DST(a)->ipv4; + case OFPACT_SET_IPV4_DST: { + ovs_be32 ipv4 = ofpact_get_SET_IPV4_DST(a)->ipv4; + if (ofp_version >= OFP12_VERSION) { + ofpact_put_set_field(out, ofp_version, MFF_IPV4_DST, ntohl(ipv4)); + } else { + put_OFPAT_SET_NW_DST(out, OFP11_VERSION, ipv4); + } break; + } - case OFPACT_SET_IP_DSCP: - ofputil_put_OFPAT11_SET_NW_TOS(out)->nw_tos - = ofpact_get_SET_IP_DSCP(a)->dscp; + case OFPACT_SET_IP_DSCP: { + uint8_t dscp = ofpact_get_SET_IP_DSCP(a)->dscp; + if (ofp_version >= OFP12_VERSION) { + ofpact_put_set_field(out, ofp_version, + MFF_IP_DSCP_SHIFTED, dscp >> 2); + } else { + put_OFPAT_SET_NW_TOS(out, OFP11_VERSION, dscp); + } break; + } - case OFPACT_SET_IP_ECN: - ofputil_put_OFPAT11_SET_NW_ECN(out)->nw_ecn - = ofpact_get_SET_IP_ECN(a)->ecn; + case OFPACT_SET_IP_ECN: { + uint8_t ecn = ofpact_get_SET_IP_ECN(a)->ecn; + if (ofp_version >= OFP12_VERSION) { + ofpact_put_set_field(out, ofp_version, MFF_IP_ECN, ecn); + } else { + put_OFPAT11_SET_NW_ECN(out, ecn); + } break; + } case OFPACT_SET_IP_TTL: - ofputil_put_OFPAT11_SET_NW_TTL(out)->nw_ttl - = ofpact_get_SET_IP_TTL(a)->ttl; + put_OFPAT11_SET_NW_TTL(out, ofpact_get_SET_IP_TTL(a)->ttl); break; - case OFPACT_SET_L4_SRC_PORT: - ofputil_put_OFPAT11_SET_TP_SRC(out)->tp_port - = htons(ofpact_get_SET_L4_SRC_PORT(a)->port); + case OFPACT_SET_L4_SRC_PORT: { + const struct ofpact_l4_port *l4_port = ofpact_get_SET_L4_SRC_PORT(a); + uint16_t port = l4_port->port; + uint8_t proto = l4_port->flow_ip_proto; + enum mf_field_id field = (proto == IPPROTO_TCP ? MFF_TCP_SRC + : proto == IPPROTO_UDP ? MFF_UDP_SRC + : proto == IPPROTO_SCTP ? MFF_SCTP_SRC + : MFF_N_IDS); + + if (ofp_version >= OFP12_VERSION && field != MFF_N_IDS) { + ofpact_put_set_field(out, ofp_version, field, port); + } else { + put_OFPAT_SET_TP_SRC(out, htons(port)); + } break; + } - case OFPACT_SET_L4_DST_PORT: - ofputil_put_OFPAT11_SET_TP_DST(out)->tp_port - = htons(ofpact_get_SET_L4_DST_PORT(a)->port); + case OFPACT_SET_L4_DST_PORT: { + const struct ofpact_l4_port *l4_port = ofpact_get_SET_L4_DST_PORT(a); + uint16_t port = l4_port->port; + uint8_t proto = l4_port->flow_ip_proto; + enum mf_field_id field = (proto == IPPROTO_TCP ? MFF_TCP_DST + : proto == IPPROTO_UDP ? MFF_UDP_DST + : proto == IPPROTO_SCTP ? MFF_SCTP_DST + : MFF_N_IDS); + + if (ofp_version >= OFP12_VERSION && field != MFF_N_IDS) { + ofpact_put_set_field(out, ofp_version, field, port); + } else { + put_OFPAT_SET_TP_DST(out, htons(port)); + } break; + } case OFPACT_DEC_TTL: ofpact_dec_ttl_to_openflow11(ofpact_get_DEC_TTL(a), out); break; - case OFPACT_SET_MPLS_LABEL: - ofputil_put_OFPAT11_SET_MPLS_LABEL(out)->mpls_label - = ofpact_get_SET_MPLS_LABEL(a)->label; + case OFPACT_SET_MPLS_LABEL: { + ovs_be32 label = ofpact_get_SET_MPLS_LABEL(a)->label; + if (ofp_version >= OFP12_VERSION) { + ofpact_put_set_field(out, ofp_version, MFF_MPLS_LABEL, + ntohl(label)); + } else { + put_OFPAT_SET_MPLS_LABEL(out, ofp_version, label); + } break; + } - case OFPACT_SET_MPLS_TC: - ofputil_put_OFPAT11_SET_MPLS_TC(out)->mpls_tc - = ofpact_get_SET_MPLS_TC(a)->tc; + case OFPACT_SET_MPLS_TC: { + uint8_t tc = ofpact_get_SET_MPLS_TC(a)->tc; + if (ofp_version >= OFP12_VERSION) { + ofpact_put_set_field(out, ofp_version, MFF_MPLS_TC, tc); + } else { + put_OFPAT_SET_MPLS_TC(out, ofp_version, tc); + } break; + } case OFPACT_SET_MPLS_TTL: - ofputil_put_OFPAT11_SET_MPLS_TTL(out)->mpls_ttl - = ofpact_get_SET_MPLS_TTL(a)->ttl; + put_OFPAT_SET_MPLS_TTL(out, ofp_version, + ofpact_get_SET_MPLS_TTL(a)->ttl); break; case OFPACT_DEC_MPLS_TTL: - ofputil_put_OFPAT11_DEC_MPLS_TTL(out); + put_OFPAT_DEC_MPLS_TTL(out, ofp_version); break; case OFPACT_WRITE_METADATA: @@ -2837,13 +4392,13 @@ ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out) break; case OFPACT_PUSH_MPLS: - ofputil_put_OFPAT11_PUSH_MPLS(out)->ethertype = - ofpact_get_PUSH_MPLS(a)->ethertype; + put_OFPAT_PUSH_MPLS(out, ofp_version, + ofpact_get_PUSH_MPLS(a)->ethertype); break; case OFPACT_POP_MPLS: - ofputil_put_OFPAT11_POP_MPLS(out)->ethertype = - ofpact_get_POP_MPLS(a)->ethertype; + put_OFPAT_POP_MPLS(out, ofp_version, + ofpact_get_POP_MPLS(a)->ethertype); break; @@ -2854,22 +4409,32 @@ ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out) OVS_NOT_REACHED(); case OFPACT_GROUP: - ofputil_put_OFPAT11_GROUP(out)->group_id = - htonl(ofpact_get_GROUP(a)->group_id); + put_OFPAT11_GROUP(out, ofpact_get_GROUP(a)->group_id); break; case OFPACT_SET_FIELD: set_field_to_openflow(ofpact_get_SET_FIELD(a), out); break; + case OFPACT_SET_TUNNEL: + if (ofp_version >= OFP12_VERSION) { + ofpact_put_set_field(out, ofp_version, MFF_TUN_ID, + ofpact_get_SET_TUNNEL(a)->tun_id); + } else { + ofpact_to_nxast(a, out, OFP11_VERSION); + } + break; + + case OFPACT_REG_MOVE: + reg_move_to_openflow(ofpact_get_REG_MOVE(a), ofp_version, out); + break; + case OFPACT_CONTROLLER: case OFPACT_OUTPUT_REG: case OFPACT_BUNDLE: - case OFPACT_REG_MOVE: case OFPACT_REG_LOAD: case OFPACT_STACK_PUSH: case OFPACT_STACK_POP: - case OFPACT_SET_TUNNEL: case OFPACT_POP_QUEUE: case OFPACT_FIN_TIMEOUT: case OFPACT_RESUBMIT: @@ -2878,155 +4443,11 @@ ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out) case OFPACT_NOTE: case OFPACT_EXIT: case OFPACT_SAMPLE: - ofpact_to_nxast(a, out); + ofpact_to_nxast(a, out, OFP11_VERSION); break; } } -/* Output deprecated set actions as set_field actions. */ -static void -ofpact_to_openflow12(const struct ofpact *a, struct ofpbuf *out) -{ - enum mf_field_id field; - union mf_value value; - struct ofpact_l4_port *l4port; - uint8_t proto; - - /* - * Convert actions deprecated in OpenFlow 1.2 to Set Field actions, - * if possible. - */ - switch ((int)a->type) { - case OFPACT_SET_VLAN_VID: - case OFPACT_SET_VLAN_PCP: - case OFPACT_SET_ETH_SRC: - case OFPACT_SET_ETH_DST: - case OFPACT_SET_IPV4_SRC: - case OFPACT_SET_IPV4_DST: - case OFPACT_SET_IP_DSCP: - case OFPACT_SET_IP_ECN: - case OFPACT_SET_L4_SRC_PORT: - case OFPACT_SET_L4_DST_PORT: - case OFPACT_SET_MPLS_LABEL: - case OFPACT_SET_MPLS_TC: - case OFPACT_SET_TUNNEL: /* Convert to a set_field, too. */ - - switch ((int)a->type) { - - case OFPACT_SET_VLAN_VID: - if (!ofpact_get_SET_VLAN_VID(a)->flow_has_vlan && - ofpact_get_SET_VLAN_VID(a)->push_vlan_if_needed) { - ofputil_put_OFPAT11_PUSH_VLAN(out)->ethertype - = htons(ETH_TYPE_VLAN_8021Q); - } - field = MFF_VLAN_VID; - /* Set-Field on OXM_OF_VLAN_VID must have OFPVID_PRESENT set. */ - value.be16 = htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid - | OFPVID12_PRESENT); - break; - - case OFPACT_SET_VLAN_PCP: - if (!ofpact_get_SET_VLAN_PCP(a)->flow_has_vlan && - ofpact_get_SET_VLAN_PCP(a)->push_vlan_if_needed) { - ofputil_put_OFPAT11_PUSH_VLAN(out)->ethertype - = htons(ETH_TYPE_VLAN_8021Q); - } - field = MFF_VLAN_PCP; - value.u8 = ofpact_get_SET_VLAN_PCP(a)->vlan_pcp; - break; - - case OFPACT_SET_ETH_SRC: - field = MFF_ETH_SRC; - memcpy(value.mac, ofpact_get_SET_ETH_SRC(a)->mac, ETH_ADDR_LEN); - break; - - case OFPACT_SET_ETH_DST: - field = MFF_ETH_DST; - memcpy(value.mac, ofpact_get_SET_ETH_DST(a)->mac, ETH_ADDR_LEN); - break; - - case OFPACT_SET_IPV4_SRC: - field = MFF_IPV4_SRC; - value.be32 = ofpact_get_SET_IPV4_SRC(a)->ipv4; - break; - - case OFPACT_SET_IPV4_DST: - field = MFF_IPV4_DST; - value.be32 = ofpact_get_SET_IPV4_DST(a)->ipv4; - break; - - case OFPACT_SET_IP_DSCP: - field = MFF_IP_DSCP_SHIFTED; /* OXM_OF_IP_DSCP */ - value.u8 = ofpact_get_SET_IP_DSCP(a)->dscp >> 2; - break; - - case OFPACT_SET_IP_ECN: - field = MFF_IP_ECN; - value.u8 = ofpact_get_SET_IP_ECN(a)->ecn; - break; - - case OFPACT_SET_L4_SRC_PORT: - /* We keep track of IP protocol while translating actions to be - * able to translate to the proper OXM type. - * If the IP protocol type is unknown, the translation cannot - * be performed and we will send the action using the original - * action type. */ - l4port = ofpact_get_SET_L4_SRC_PORT(a); - proto = l4port->flow_ip_proto; - field = proto == IPPROTO_TCP ? MFF_TCP_SRC - : proto == IPPROTO_UDP ? MFF_UDP_SRC - : proto == IPPROTO_SCTP ? MFF_SCTP_SRC - : MFF_N_IDS; /* RFC: Unknown IP proto, do not translate. */ - value.be16 = htons(l4port->port); - break; - - case OFPACT_SET_L4_DST_PORT: - l4port = ofpact_get_SET_L4_DST_PORT(a); - proto = l4port->flow_ip_proto; - field = proto == IPPROTO_TCP ? MFF_TCP_DST - : proto == IPPROTO_UDP ? MFF_UDP_DST - : proto == IPPROTO_SCTP ? MFF_SCTP_DST - : MFF_N_IDS; /* RFC: Unknown IP proto, do not translate. */ - value.be16 = htons(l4port->port); - break; - - case OFPACT_SET_MPLS_LABEL: - field = MFF_MPLS_LABEL; - value.be32 = ofpact_get_SET_MPLS_LABEL(a)->label; - break; - - case OFPACT_SET_MPLS_TC: - field = MFF_MPLS_TC; - value.u8 = ofpact_get_SET_MPLS_TC(a)->tc; - break; - - case OFPACT_SET_TUNNEL: - field = MFF_TUN_ID; - value.be64 = htonll(ofpact_get_SET_TUNNEL(a)->tun_id); - break; - - default: - field = MFF_N_IDS; - } - - /* Put the action out as a set field action, if possible. */ - if (field < MFF_N_IDS) { - uint64_t ofpacts_stub[128 / 8]; - struct ofpbuf sf_act; - struct ofpact_set_field *sf; - - ofpbuf_use_stub(&sf_act, ofpacts_stub, sizeof ofpacts_stub); - sf = ofpact_put_SET_FIELD(&sf_act); - sf->field = mf_from_id(field); - memcpy(&sf->value, &value, sf->field->n_bytes); - set_field_to_openflow(sf, out); - return; - } - } - - ofpact_to_openflow11(a, out); -} - /* Converts the 'ofpacts_len' bytes of ofpacts in 'ofpacts' into OpenFlow * actions in 'openflow', appending the actions to any existing data in * 'openflow'. */ @@ -3038,13 +4459,12 @@ ofpacts_put_openflow_actions(const struct ofpact ofpacts[], size_t ofpacts_len, const struct ofpact *a; size_t start_size = ofpbuf_size(openflow); - void (*translate)(const struct ofpact *a, struct ofpbuf *out) = - (ofp_version == OFP10_VERSION) ? ofpact_to_openflow10 : - (ofp_version == OFP11_VERSION) ? ofpact_to_openflow11 : - ofpact_to_openflow12; - OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { - translate(a, openflow); + if (ofp_version == OFP10_VERSION) { + ofpact_to_openflow10(a, openflow); + } else { + ofpact_to_openflow11(a, ofp_version, openflow); + } } return ofpbuf_size(openflow) - start_size; } @@ -3122,11 +4542,7 @@ ofpacts_put_openflow_instructions(const struct ofpact ofpacts[], != OVSINST_OFPIT11_APPLY_ACTIONS) { break; } - if (ofp_version == OFP11_VERSION) { - ofpact_to_openflow11(action, openflow); - } else { - ofpact_to_openflow12(action, openflow); - } + ofpact_to_openflow11(action, ofp_version, openflow); processed = action; } ofpacts_update_instruction_actions(openflow, ofs); @@ -3437,7 +4853,7 @@ print_dec_ttl(const struct ofpact_cnt_ids *ids, size_t i; ds_put_cstr(s, "dec_ttl"); - if (ids->ofpact.compat == OFPUTIL_NXAST_DEC_TTL_CNT_IDS) { + if (ids->ofpact.raw == NXAST_RAW_DEC_TTL_CNT_IDS) { ds_put_cstr(s, "("); for (i = 0; i < ids->n_controllers; i++) { if (i) { @@ -3538,22 +4954,22 @@ ofpact_format(const struct ofpact *a, struct ds *s) case OFPACT_SET_VLAN_VID: ds_put_format(s, "%s:%"PRIu16, - (a->compat == OFPUTIL_OFPAT11_SET_VLAN_VID - ? "set_vlan_vid" - : "mod_vlan_vid"), + (ofpact_get_SET_VLAN_VID(a)->push_vlan_if_needed + ? "mod_vlan_vid" + : "set_vlan_vid"), ofpact_get_SET_VLAN_VID(a)->vlan_vid); break; case OFPACT_SET_VLAN_PCP: ds_put_format(s, "%s:%"PRIu8, - (a->compat == OFPUTIL_OFPAT11_SET_VLAN_PCP - ? "set_vlan_pcp" - : "mod_vlan_pcp"), + (ofpact_get_SET_VLAN_PCP(a)->push_vlan_if_needed + ? "mod_vlan_pcp" + : "set_vlan_pcp"), ofpact_get_SET_VLAN_PCP(a)->vlan_pcp); break; case OFPACT_STRIP_VLAN: - ds_put_cstr(s, a->compat == OFPUTIL_OFPAT11_POP_VLAN + ds_put_cstr(s, a->raw == OFPAT_RAW11_POP_VLAN ? "pop_vlan" : "strip_vlan"); break; @@ -3653,7 +5069,7 @@ ofpact_format(const struct ofpact *a, struct ds *s) tunnel = ofpact_get_SET_TUNNEL(a); ds_put_format(s, "set_tunnel%s:%#"PRIx64, (tunnel->tun_id > UINT32_MAX - || a->compat == OFPUTIL_NXAST_SET_TUNNEL64 ? "64" : ""), + || a->raw == NXAST_RAW_SET_TUNNEL64 ? "64" : ""), tunnel->tun_id); break; @@ -3810,7 +5226,7 @@ ofpact_init(struct ofpact *ofpact, enum ofpact_type type, size_t len) { memset(ofpact, 0, len); ofpact->type = type; - ofpact->compat = OFPUTIL_ACTION_INVALID; + ofpact->raw = -1; ofpact->len = len; } @@ -3845,6 +5261,1202 @@ ofpact_pad(struct ofpbuf *ofpacts) ofpbuf_put_zeros(ofpacts, pad); } } + +static char * WARN_UNUSED_RESULT +parse_ENQUEUE(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + char *sp = NULL; + char *port = strtok_r(arg, ":q,", &sp); + char *queue = strtok_r(NULL, "", &sp); + struct ofpact_enqueue *enqueue; + + if (port == NULL || queue == NULL) { + return xstrdup("\"enqueue\" syntax is \"enqueue:PORT:QUEUE\" or " + "\"enqueue(PORT,QUEUE)\""); + } + + enqueue = ofpact_put_ENQUEUE(ofpacts); + if (!ofputil_port_from_string(port, &enqueue->port)) { + return xasprintf("%s: enqueue to unknown port", port); + } + return str_to_u32(queue, &enqueue->queue); +} + +static char * WARN_UNUSED_RESULT +parse_OUTPUT(const char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + if (strchr(arg, '[')) { + struct ofpact_output_reg *output_reg; + + output_reg = ofpact_put_OUTPUT_REG(ofpacts); + output_reg->max_len = UINT16_MAX; + return mf_parse_subfield(&output_reg->src, arg); + } else { + struct ofpact_output *output; + + output = ofpact_put_OUTPUT(ofpacts); + if (!ofputil_port_from_string(arg, &output->port)) { + return xasprintf("%s: output to unknown port", arg); + } + output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0; + return NULL; + } +} + +static char * WARN_UNUSED_RESULT +parse_OUTPUT_REG(const char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + return parse_OUTPUT(arg, ofpacts, usable_protocols); +} + +static char * WARN_UNUSED_RESULT +parse_RESUBMIT(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + struct ofpact_resubmit *resubmit; + char *in_port_s, *table_s; + + resubmit = ofpact_put_RESUBMIT(ofpacts); + + in_port_s = strsep(&arg, ","); + if (in_port_s && in_port_s[0]) { + if (!ofputil_port_from_string(in_port_s, &resubmit->in_port)) { + return xasprintf("%s: resubmit to unknown port", in_port_s); + } + } else { + resubmit->in_port = OFPP_IN_PORT; + } + + table_s = strsep(&arg, ","); + if (table_s && table_s[0]) { + uint32_t table_id = 0; + char *error; + + error = str_to_u32(table_s, &table_id); + if (error) { + return error; + } + resubmit->table_id = table_id; + } else { + resubmit->table_id = 255; + } + + if (resubmit->in_port == OFPP_IN_PORT && resubmit->table_id == 255) { + return xstrdup("at least one \"in_port\" or \"table\" must be " + "specified on resubmit"); + } + return NULL; +} + +/* Parses a "set_field" action with argument 'arg', appending the parsed + * action to 'ofpacts'. + * + * Returns NULL if successful, otherwise a malloc()'d string describing the + * error. The caller is responsible for freeing the returned string. */ +static char * WARN_UNUSED_RESULT +set_field_parse__(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols) +{ + struct ofpact_set_field *sf = ofpact_put_SET_FIELD(ofpacts); + char *value; + char *delim; + char *key; + const struct mf_field *mf; + char *error; + + value = arg; + delim = strstr(arg, "->"); + if (!delim) { + return xasprintf("%s: missing `->'", arg); + } + if (strlen(delim) <= strlen("->")) { + return xasprintf("%s: missing field name following `->'", arg); + } + + key = delim + strlen("->"); + mf = mf_from_name(key); + if (!mf) { + return xasprintf("%s is not a valid OXM field name", key); + } + if (!mf->writable) { + return xasprintf("%s is read-only", key); + } + sf->field = mf; + delim[0] = '\0'; + error = mf_parse_value(mf, value, &sf->value); + if (error) { + return error; + } + + if (!mf_is_value_valid(mf, &sf->value)) { + return xasprintf("%s is not a valid value for field %s", value, key); + } + + *usable_protocols &= mf->usable_protocols; + return NULL; +} + +/* Parses 'arg' as the argument to a "set_field" action, and appends such an + * action to 'ofpacts'. + * + * Returns NULL if successful, otherwise a malloc()'d string describing the + * error. The caller is responsible for freeing the returned string. */ +static char * WARN_UNUSED_RESULT +parse_SET_FIELD(const char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols) +{ + char *copy = xstrdup(arg); + char *error = set_field_parse__(copy, ofpacts, usable_protocols); + free(copy); + return error; +} + +/* Parses 'arg' as the argument to a "sample" action, and appends such an + * action to 'ofpacts'. + * + * Returns NULL if successful, otherwise a malloc()'d string describing the + * error. The caller is responsible for freeing the returned string. */ +static char * WARN_UNUSED_RESULT +parse_SAMPLE(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + struct ofpact_sample *os = ofpact_put_SAMPLE(ofpacts); + char *key, *value; + + while (ofputil_parse_key_value(&arg, &key, &value)) { + char *error = NULL; + + if (!strcmp(key, "probability")) { + error = str_to_u16(value, "probability", &os->probability); + if (!error && os->probability == 0) { + error = xasprintf("invalid probability value \"%s\"", value); + } + } else if (!strcmp(key, "collector_set_id")) { + error = str_to_u32(value, &os->collector_set_id); + } else if (!strcmp(key, "obs_domain_id")) { + error = str_to_u32(value, &os->obs_domain_id); + } else if (!strcmp(key, "obs_point_id")) { + error = str_to_u32(value, &os->obs_point_id); + } else { + error = xasprintf("invalid key \"%s\" in \"sample\" argument", + key); + } + if (error) { + return error; + } + } + if (os->probability == 0) { + return xstrdup("non-zero \"probability\" must be specified on sample"); + } + return NULL; +} + +static char * WARN_UNUSED_RESULT +parse_set_vlan_vid(char *arg, struct ofpbuf *ofpacts, bool push_vlan_if_needed) +{ + struct ofpact_vlan_vid *vlan_vid; + uint16_t vid; + char *error; + + error = str_to_u16(arg, "VLAN VID", &vid); + if (error) { + return error; + } + + if (vid & ~VLAN_VID_MASK) { + return xasprintf("%s: not a valid VLAN VID", arg); + } + vlan_vid = ofpact_put_SET_VLAN_VID(ofpacts); + vlan_vid->vlan_vid = vid; + vlan_vid->push_vlan_if_needed = push_vlan_if_needed; + return NULL; +} + +static char * WARN_UNUSED_RESULT +parse_SET_VLAN_VID(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + return parse_set_vlan_vid(arg, ofpacts, false); +} + +static char * WARN_UNUSED_RESULT +parse_set_vlan_pcp(char *arg, struct ofpbuf *ofpacts, bool push_vlan_if_needed) +{ + struct ofpact_vlan_pcp *vlan_pcp; + uint8_t pcp; + char *error; + + error = str_to_u8(arg, "VLAN PCP", &pcp); + if (error) { + return error; + } + + if (pcp & ~7) { + return xasprintf("%s: not a valid VLAN PCP", arg); + } + vlan_pcp = ofpact_put_SET_VLAN_PCP(ofpacts); + vlan_pcp->vlan_pcp = pcp; + vlan_pcp->push_vlan_if_needed = push_vlan_if_needed; + return NULL; +} + +static char * WARN_UNUSED_RESULT +parse_SET_VLAN_PCP(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + return parse_set_vlan_pcp(arg, ofpacts, false); +} + +static char * WARN_UNUSED_RESULT +parse_STRIP_VLAN(char *arg OVS_UNUSED, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + ofpact_put_STRIP_VLAN(ofpacts)->ofpact.raw = OFPAT_RAW10_STRIP_VLAN; + return NULL; +} + +static char * WARN_UNUSED_RESULT +parse_pop_vlan(struct ofpbuf *ofpacts) +{ + ofpact_put_STRIP_VLAN(ofpacts)->ofpact.raw = OFPAT_RAW11_POP_VLAN; + return NULL; +} + +static char * WARN_UNUSED_RESULT +parse_PUSH_VLAN(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + uint16_t ethertype; + char *error; + + *usable_protocols &= OFPUTIL_P_OF11_UP; + error = str_to_u16(arg, "ethertype", ðertype); + if (error) { + return error; + } + + if (ethertype != ETH_TYPE_VLAN_8021Q) { + /* XXX ETH_TYPE_VLAN_8021AD case isn't supported */ + return xasprintf("%s: not a valid VLAN ethertype", arg); + } + + ofpact_put_PUSH_VLAN(ofpacts); + return NULL; +} + +static char * WARN_UNUSED_RESULT +parse_SET_QUEUE(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + return str_to_u32(arg, &ofpact_put_SET_QUEUE(ofpacts)->queue_id); +} + +static char * WARN_UNUSED_RESULT +parse_SET_ETH_SRC(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + return str_to_mac(arg, ofpact_put_SET_ETH_SRC(ofpacts)->mac); +} + +static char * WARN_UNUSED_RESULT +parse_SET_ETH_DST(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + return str_to_mac(arg, ofpact_put_SET_ETH_DST(ofpacts)->mac); +} + +static char * WARN_UNUSED_RESULT +parse_SET_IPV4_SRC(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + return str_to_ip(arg, &ofpact_put_SET_IPV4_SRC(ofpacts)->ipv4); +} + +static char * WARN_UNUSED_RESULT +parse_SET_IPV4_DST(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + return str_to_ip(arg, &ofpact_put_SET_IPV4_DST(ofpacts)->ipv4); +} + +static char * WARN_UNUSED_RESULT +parse_SET_IP_DSCP(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + uint8_t tos; + char *error; + + error = str_to_u8(arg, "TOS", &tos); + if (error) { + return error; + } + + if (tos & ~IP_DSCP_MASK) { + return xasprintf("%s: not a valid TOS", arg); + } + ofpact_put_SET_IP_DSCP(ofpacts)->dscp = tos; + return NULL; +} + +static char * WARN_UNUSED_RESULT +parse_SET_IP_ECN(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + uint8_t ecn; + char *error; + + error = str_to_u8(arg, "ECN", &ecn); + if (error) { + return error; + } + + if (ecn & ~IP_ECN_MASK) { + return xasprintf("%s: not a valid ECN", arg); + } + ofpact_put_SET_IP_ECN(ofpacts)->ecn = ecn; + return NULL; +} + +static char * WARN_UNUSED_RESULT +parse_SET_IP_TTL(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + uint8_t ttl; + char *error; + + error = str_to_u8(arg, "TTL", &ttl); + if (error) { + return error; + } + + ofpact_put_SET_IP_TTL(ofpacts)->ttl = ttl; + return NULL; +} + +static char * WARN_UNUSED_RESULT +parse_SET_L4_SRC_PORT(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + return str_to_u16(arg, "source port", + &ofpact_put_SET_L4_SRC_PORT(ofpacts)->port); +} + +static char * WARN_UNUSED_RESULT +parse_SET_L4_DST_PORT(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + return str_to_u16(arg, "destination port", + &ofpact_put_SET_L4_DST_PORT(ofpacts)->port); +} + +static char * WARN_UNUSED_RESULT +parse_set_tunnel(char *arg, struct ofpbuf *ofpacts, + enum ofp_raw_action_type raw) +{ + struct ofpact_tunnel *tunnel; + + tunnel = ofpact_put_SET_TUNNEL(ofpacts); + tunnel->ofpact.raw = raw; + return str_to_u64(arg, &tunnel->tun_id); +} + +static char * WARN_UNUSED_RESULT +parse_SET_TUNNEL(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + return parse_set_tunnel(arg, ofpacts, NXAST_RAW_SET_TUNNEL); +} + +static char * WARN_UNUSED_RESULT +parse_POP_QUEUE(const char *arg OVS_UNUSED, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + ofpact_put_POP_QUEUE(ofpacts); + return NULL; +} + +static char * WARN_UNUSED_RESULT +parse_REG_MOVE(const char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + struct ofpact_reg_move *move = ofpact_put_REG_MOVE(ofpacts); + const char *full_arg = arg; + char *error; + + error = mf_parse_subfield__(&move->src, &arg); + if (error) { + return error; + } + if (strncmp(arg, "->", 2)) { + return xasprintf("%s: missing `->' following source", full_arg); + } + arg += 2; + error = mf_parse_subfield(&move->dst, arg); + if (error) { + return error; + } + + if (move->src.n_bits != move->dst.n_bits) { + return xasprintf("%s: source field is %d bits wide but destination is " + "%d bits wide", full_arg, + move->src.n_bits, move->dst.n_bits); + } + return NULL; +} + +static char * WARN_UNUSED_RESULT +parse_REG_LOAD(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + struct ofpact_reg_load *load = ofpact_put_REG_LOAD(ofpacts); + const char *full_arg = arg; + uint64_t value = strtoull(arg, (char **) &arg, 0); + char *error; + + if (strncmp(arg, "->", 2)) { + return xasprintf("%s: missing `->' following value", full_arg); + } + arg += 2; + error = mf_parse_subfield(&load->dst, arg); + if (error) { + return error; + } + + if (load->dst.n_bits < 64 && (value >> load->dst.n_bits) != 0) { + return xasprintf("%s: value %"PRIu64" does not fit into %d bits", + full_arg, value, load->dst.n_bits); + } + + load->subvalue.be64[0] = htonll(0); + load->subvalue.be64[1] = htonll(value); + return NULL; +} + +static char * WARN_UNUSED_RESULT +parse_NOTE(const char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + struct ofpact_note *note; + + note = ofpact_put_NOTE(ofpacts); + while (*arg != '\0') { + uint8_t byte; + bool ok; + + if (*arg == '.') { + arg++; + } + if (*arg == '\0') { + break; + } + + byte = hexits_value(arg, 2, &ok); + if (!ok) { + return xstrdup("bad hex digit in `note' argument"); + } + ofpbuf_put(ofpacts, &byte, 1); + + note = ofpacts->frame; + note->length++; + + arg += 2; + } + ofpact_update_len(ofpacts, ¬e->ofpact); + return NULL; +} + +static char * WARN_UNUSED_RESULT +parse_multipath__(struct ofpact_multipath *mp, const char *s_, char *s) +{ + char *save_ptr = NULL; + char *fields, *basis, *algorithm, *n_links_str, *arg, *dst; + char *error; + int n_links; + + fields = strtok_r(s, ", ", &save_ptr); + basis = strtok_r(NULL, ", ", &save_ptr); + algorithm = strtok_r(NULL, ", ", &save_ptr); + n_links_str = strtok_r(NULL, ", ", &save_ptr); + arg = strtok_r(NULL, ", ", &save_ptr); + dst = strtok_r(NULL, ", ", &save_ptr); + if (!dst) { + return xasprintf("%s: not enough arguments to multipath action", s_); + } + + ofpact_init_MULTIPATH(mp); + if (!strcasecmp(fields, "eth_src")) { + mp->fields = NX_HASH_FIELDS_ETH_SRC; + } else if (!strcasecmp(fields, "symmetric_l4")) { + mp->fields = NX_HASH_FIELDS_SYMMETRIC_L4; + } else { + return xasprintf("%s: unknown fields `%s'", s_, fields); + } + mp->basis = atoi(basis); + if (!strcasecmp(algorithm, "modulo_n")) { + mp->algorithm = NX_MP_ALG_MODULO_N; + } else if (!strcasecmp(algorithm, "hash_threshold")) { + mp->algorithm = NX_MP_ALG_HASH_THRESHOLD; + } else if (!strcasecmp(algorithm, "hrw")) { + mp->algorithm = NX_MP_ALG_HRW; + } else if (!strcasecmp(algorithm, "iter_hash")) { + mp->algorithm = NX_MP_ALG_ITER_HASH; + } else { + return xasprintf("%s: unknown algorithm `%s'", s_, algorithm); + } + n_links = atoi(n_links_str); + if (n_links < 1 || n_links > 65536) { + return xasprintf("%s: n_links %d is not in valid range 1 to 65536", + s_, n_links); + } + mp->max_link = n_links - 1; + mp->arg = atoi(arg); + + error = mf_parse_subfield(&mp->dst, dst); + if (error) { + return error; + } + if (mp->dst.n_bits < 16 && n_links > (1u << mp->dst.n_bits)) { + return xasprintf("%s: %d-bit destination field has %u possible " + "values, less than specified n_links %d", + s_, mp->dst.n_bits, 1u << mp->dst.n_bits, n_links); + } + + return NULL; +} + +static char * WARN_UNUSED_RESULT +parse_MULTIPATH(const char *arg_, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + struct ofpact_multipath *mp = ofpact_put_MULTIPATH(ofpacts); + char *arg = xstrdup(arg_); + char *error = parse_multipath__(mp, arg_, arg); + free(arg); + return error; +} + +static char * WARN_UNUSED_RESULT +parse_bundle__(const char *s, char **save_ptr, + const char *fields, const char *basis, const char *algorithm, + const char *slave_type, const char *dst, + const char *slave_delim, struct ofpbuf *ofpacts) +{ + struct ofpact_bundle *bundle; + + if (!slave_delim) { + return xasprintf("%s: not enough arguments to bundle action", s); + } + + if (strcasecmp(slave_delim, "slaves")) { + return xasprintf("%s: missing slave delimiter, expected `slaves' " + "got `%s'", s, slave_delim); + } + + bundle = ofpact_put_BUNDLE(ofpacts); + + for (;;) { + ofp_port_t slave_port; + char *slave; + + slave = strtok_r(NULL, ", []", save_ptr); + if (!slave || bundle->n_slaves >= BUNDLE_MAX_SLAVES) { + break; + } + + if (!ofputil_port_from_string(slave, &slave_port)) { + return xasprintf("%s: bad port number", slave); + } + ofpbuf_put(ofpacts, &slave_port, sizeof slave_port); + + bundle = ofpacts->frame; + bundle->n_slaves++; + } + ofpact_update_len(ofpacts, &bundle->ofpact); + + bundle->basis = atoi(basis); + + if (!strcasecmp(fields, "eth_src")) { + bundle->fields = NX_HASH_FIELDS_ETH_SRC; + } else if (!strcasecmp(fields, "symmetric_l4")) { + bundle->fields = NX_HASH_FIELDS_SYMMETRIC_L4; + } else { + return xasprintf("%s: unknown fields `%s'", s, fields); + } + + if (!strcasecmp(algorithm, "active_backup")) { + bundle->algorithm = NX_BD_ALG_ACTIVE_BACKUP; + } else if (!strcasecmp(algorithm, "hrw")) { + bundle->algorithm = NX_BD_ALG_HRW; + } else { + return xasprintf("%s: unknown algorithm `%s'", s, algorithm); + } + + if (strcasecmp(slave_type, "ofport")) { + return xasprintf("%s: unknown slave_type `%s'", s, slave_type); + } + + if (dst) { + char *error = mf_parse_subfield(&bundle->dst, dst); + if (error) { + return error; + } + } + + return NULL; +} + +static char * WARN_UNUSED_RESULT +parse_BUNDLE(const char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + char *fields, *basis, *algorithm, *slave_type, *slave_delim; + char *tokstr, *save_ptr; + char *error; + + save_ptr = NULL; + tokstr = xstrdup(arg); + fields = strtok_r(tokstr, ", ", &save_ptr); + basis = strtok_r(NULL, ", ", &save_ptr); + algorithm = strtok_r(NULL, ", ", &save_ptr); + slave_type = strtok_r(NULL, ", ", &save_ptr); + slave_delim = strtok_r(NULL, ": ", &save_ptr); + + error = parse_bundle__(arg, &save_ptr, fields, basis, algorithm, + slave_type, NULL, slave_delim, ofpacts); + free(tokstr); + + return error; +} + +static char * WARN_UNUSED_RESULT +parse_bundle_load(const char *arg, struct ofpbuf *ofpacts) +{ + char *fields, *basis, *algorithm, *slave_type, *dst, *slave_delim; + char *tokstr, *save_ptr; + char *error; + + save_ptr = NULL; + tokstr = xstrdup(arg); + fields = strtok_r(tokstr, ", ", &save_ptr); + basis = strtok_r(NULL, ", ", &save_ptr); + algorithm = strtok_r(NULL, ", ", &save_ptr); + slave_type = strtok_r(NULL, ", ", &save_ptr); + dst = strtok_r(NULL, ", ", &save_ptr); + slave_delim = strtok_r(NULL, ": ", &save_ptr); + + error = parse_bundle__(arg, &save_ptr, fields, basis, algorithm, + slave_type, dst, slave_delim, ofpacts); + + free(tokstr); + + return error; +} + +static char * WARN_UNUSED_RESULT +parse_LEARN(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + return learn_parse(arg, ofpacts); +} + +static char * WARN_UNUSED_RESULT +parse_EXIT(char *arg OVS_UNUSED, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + ofpact_put_EXIT(ofpacts); + return NULL; +} + +static void +parse_noargs_dec_ttl(struct ofpbuf *ofpacts) +{ + struct ofpact_cnt_ids *ids; + uint16_t id = 0; + + ofpact_put_DEC_TTL(ofpacts); + ofpbuf_put(ofpacts, &id, sizeof id); + ids = ofpacts->frame; + ids->n_controllers++; + ofpact_update_len(ofpacts, &ids->ofpact); +} + +static char * WARN_UNUSED_RESULT +parse_DEC_TTL(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + if (*arg == '\0') { + parse_noargs_dec_ttl(ofpacts); + } else { + struct ofpact_cnt_ids *ids; + char *cntr; + + ids = ofpact_put_DEC_TTL(ofpacts); + ids->ofpact.raw = NXAST_RAW_DEC_TTL_CNT_IDS; + for (cntr = strtok_r(arg, ", ", &arg); cntr != NULL; + cntr = strtok_r(NULL, ", ", &arg)) { + uint16_t id = atoi(cntr); + + ofpbuf_put(ofpacts, &id, sizeof id); + ids = ofpacts->frame; + ids->n_controllers++; + } + if (!ids->n_controllers) { + return xstrdup("dec_ttl_cnt_ids: expected at least one controller " + "id."); + } + ofpact_update_len(ofpacts, &ids->ofpact); + } + return NULL; +} + +static char * WARN_UNUSED_RESULT +parse_SET_MPLS_LABEL(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + struct ofpact_mpls_label *mpls_label = ofpact_put_SET_MPLS_LABEL(ofpacts); + if (*arg == '\0') { + return xstrdup("set_mpls_label: expected label."); + } + + mpls_label->label = htonl(atoi(arg)); + return NULL; +} + +static char * WARN_UNUSED_RESULT +parse_SET_MPLS_TC(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + struct ofpact_mpls_tc *mpls_tc = ofpact_put_SET_MPLS_TC(ofpacts); + + if (*arg == '\0') { + return xstrdup("set_mpls_tc: expected tc."); + } + + mpls_tc->tc = atoi(arg); + return NULL; +} + +/* Parses 'arg' as the argument to a "set_mpls_ttl" action, and appends such an + * action to 'ofpacts'. + * + * Returns NULL if successful, otherwise a malloc()'d string describing the + * error. The caller is responsible for freeing the returned string. */ +static char * WARN_UNUSED_RESULT +parse_SET_MPLS_TTL(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + struct ofpact_mpls_ttl *mpls_ttl = ofpact_put_SET_MPLS_TTL(ofpacts); + + if (*arg == '\0') { + return xstrdup("set_mpls_ttl: expected ttl."); + } + + mpls_ttl->ttl = atoi(arg); + return NULL; +} + +static char * WARN_UNUSED_RESULT +parse_DEC_MPLS_TTL(char *arg OVS_UNUSED, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + ofpact_put_DEC_MPLS_TTL(ofpacts); + return NULL; +} + +static char * WARN_UNUSED_RESULT +parse_FIN_TIMEOUT(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + struct ofpact_fin_timeout *oft = ofpact_put_FIN_TIMEOUT(ofpacts); + char *key, *value; + + while (ofputil_parse_key_value(&arg, &key, &value)) { + char *error; + + if (!strcmp(key, "idle_timeout")) { + error = str_to_u16(value, key, &oft->fin_idle_timeout); + } else if (!strcmp(key, "hard_timeout")) { + error = str_to_u16(value, key, &oft->fin_hard_timeout); + } else { + error = xasprintf("invalid key '%s' in 'fin_timeout' argument", + key); + } + + if (error) { + return error; + } + } + return NULL; +} + +static char * WARN_UNUSED_RESULT +parse_CONTROLLER(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + enum ofp_packet_in_reason reason = OFPR_ACTION; + uint16_t controller_id = 0; + uint16_t max_len = UINT16_MAX; + + if (!arg[0]) { + /* Use defaults. */ + } else if (strspn(arg, "0123456789") == strlen(arg)) { + char *error = str_to_u16(arg, "max_len", &max_len); + if (error) { + return error; + } + } else { + char *name, *value; + + while (ofputil_parse_key_value(&arg, &name, &value)) { + if (!strcmp(name, "reason")) { + if (!ofputil_packet_in_reason_from_string(value, &reason)) { + return xasprintf("unknown reason \"%s\"", value); + } + } else if (!strcmp(name, "max_len")) { + char *error = str_to_u16(value, "max_len", &max_len); + if (error) { + return error; + } + } else if (!strcmp(name, "id")) { + char *error = str_to_u16(value, "id", &controller_id); + if (error) { + return error; + } + } else { + return xasprintf("unknown key \"%s\" parsing controller " + "action", name); + } + } + } + + if (reason == OFPR_ACTION && controller_id == 0) { + struct ofpact_output *output; + + output = ofpact_put_OUTPUT(ofpacts); + output->port = OFPP_CONTROLLER; + output->max_len = max_len; + } else { + struct ofpact_controller *controller; + + controller = ofpact_put_CONTROLLER(ofpacts); + controller->max_len = max_len; + controller->reason = reason; + controller->controller_id = controller_id; + } + + return NULL; +} + +static char * WARN_UNUSED_RESULT +parse_PUSH_MPLS(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + uint16_t ethertype; + char *error; + + error = str_to_u16(arg, "push_mpls", ðertype); + if (!error) { + ofpact_put_PUSH_MPLS(ofpacts)->ethertype = htons(ethertype); + } + return error; +} + +static char * WARN_UNUSED_RESULT +parse_POP_MPLS(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + uint16_t ethertype; + char *error; + + error = str_to_u16(arg, "pop_mpls", ðertype); + if (!error) { + ofpact_put_POP_MPLS(ofpacts)->ethertype = htons(ethertype); + } + return error; +} + +static char * WARN_UNUSED_RESULT +parse_GROUP(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + return str_to_u32(arg, &ofpact_put_GROUP(ofpacts)->group_id); +} + +static char * WARN_UNUSED_RESULT +parse_STACK_PUSH(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + return nxm_parse_stack_action(ofpact_put_STACK_PUSH(ofpacts), arg); +} + +static char * WARN_UNUSED_RESULT +parse_STACK_POP(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + return nxm_parse_stack_action(ofpact_put_STACK_POP(ofpacts), arg); +} + +static char * WARN_UNUSED_RESULT +parse_WRITE_ACTIONS(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols) +{ + struct ofpact_nest *on; + char *error; + size_t ofs; + + /* Pull off existing actions or instructions. */ + ofpact_pad(ofpacts); + ofs = ofpbuf_size(ofpacts); + ofpbuf_pull(ofpacts, ofs); + + /* Add a Write-Actions instruction and then pull it off. */ + ofpact_put(ofpacts, OFPACT_WRITE_ACTIONS, sizeof *on); + ofpbuf_pull(ofpacts, sizeof *on); + + /* Parse nested actions. + * + * We pulled off "write-actions" and the previous actions because the + * OFPACT_WRITE_ACTIONS is only partially constructed: its length is such + * that it doesn't actually include the nested actions. That means that + * ofpacts_parse() would reject them as being part of an Apply-Actions that + * follows a Write-Actions, which is an invalid order. */ + error = ofpacts_parse(arg, ofpacts, usable_protocols, false); + + /* Put the Write-Actions back on and update its length. */ + on = ofpbuf_push_uninit(ofpacts, sizeof *on); + on->ofpact.len = ofpbuf_size(ofpacts); + + /* Put any previous actions or instructions back on. */ + ofpbuf_push_uninit(ofpacts, ofs); + + return error; +} + +static char * WARN_UNUSED_RESULT +parse_CLEAR_ACTIONS(char *arg OVS_UNUSED, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + ofpact_put_CLEAR_ACTIONS(ofpacts); + return NULL; +} + +static char * WARN_UNUSED_RESULT +parse_METER(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols) +{ + *usable_protocols &= OFPUTIL_P_OF13_UP; + return str_to_u32(arg, &ofpact_put_METER(ofpacts)->meter_id); +} + +static char * WARN_UNUSED_RESULT +parse_WRITE_METADATA(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols) +{ + struct ofpact_metadata *om; + char *mask = strchr(arg, '/'); + + *usable_protocols &= OFPUTIL_P_NXM_OF11_UP; + + om = ofpact_put_WRITE_METADATA(ofpacts); + if (mask) { + char *error; + + *mask = '\0'; + error = str_to_be64(mask + 1, &om->mask); + if (error) { + return error; + } + } else { + om->mask = OVS_BE64_MAX; + } + + return str_to_be64(arg, &om->metadata); +} + +static char * WARN_UNUSED_RESULT +parse_GOTO_TABLE(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + struct ofpact_goto_table *ogt = ofpact_put_GOTO_TABLE(ofpacts); + char *table_s = strsep(&arg, ","); + if (!table_s || !table_s[0]) { + return xstrdup("instruction goto-table needs table id"); + } + return str_to_u8(table_s, "table", &ogt->table_id); +} + +static char * WARN_UNUSED_RESULT +ofpact_parse(enum ofpact_type type, char *value, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols) +{ + switch (type) { +#define OFPACT(ENUM, STRUCT, MEMBER, NAME) \ + case OFPACT_##ENUM: \ + return parse_##ENUM(value, ofpacts, usable_protocols); + OFPACTS +#undef OFPACT + default: + OVS_NOT_REACHED(); + } +} + +static bool +ofpact_type_from_name(const char *name, enum ofpact_type *type) +{ +#define OFPACT(ENUM, STRUCT, MEMBER, NAME) \ + if (!strcasecmp(name, NAME)) { \ + *type = OFPACT_##ENUM; \ + return true; \ + } + OFPACTS +#undef OFPACT + + return false; +} + +/* Parses 'str' as a series of instructions, and appends them to 'ofpacts'. + * + * Returns NULL if successful, otherwise a malloc()'d string describing the + * error. The caller is responsible for freeing the returned string. */ +static char * WARN_UNUSED_RESULT +ofpacts_parse__(char *str, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols, + bool allow_instructions) +{ + int prev_inst = -1; + enum ofperr retval; + char *key, *value; + bool drop = false; + char *pos; + + pos = str; + while (ofputil_parse_key_value(&pos, &key, &value)) { + enum ovs_instruction_type inst = OVSINST_OFPIT11_APPLY_ACTIONS; + enum ofpact_type type; + char *error = NULL; + ofp_port_t port; + + if (ofpact_type_from_name(key, &type)) { + error = ofpact_parse(type, value, ofpacts, usable_protocols); + inst = ovs_instruction_type_from_ofpact_type(type); + } else if (!strcasecmp(key, "mod_vlan_vid")) { + error = parse_set_vlan_vid(value, ofpacts, true); + } else if (!strcasecmp(key, "mod_vlan_pcp")) { + error = parse_set_vlan_pcp(value, ofpacts, true); + } else if (!strcasecmp(key, "set_nw_ttl")) { + error = parse_SET_IP_TTL(value, ofpacts, usable_protocols); + } else if (!strcasecmp(key, "pop_vlan")) { + error = parse_pop_vlan(ofpacts); + } else if (!strcasecmp(key, "set_tunnel64")) { + error = parse_set_tunnel(value, ofpacts, + NXAST_RAW_SET_TUNNEL64); + } else if (!strcasecmp(key, "bundle_load")) { + error = parse_bundle_load(value, ofpacts); + } else if (!strcasecmp(key, "drop")) { + drop = true; + } else if (!strcasecmp(key, "apply_actions")) { + return xstrdup("apply_actions is the default instruction"); + } else if (ofputil_port_from_string(key, &port)) { + ofpact_put_OUTPUT(ofpacts)->port = port; + } else { + return xasprintf("unknown action %s", key); + } + if (error) { + return error; + } + + if (inst != OVSINST_OFPIT11_APPLY_ACTIONS) { + if (!allow_instructions) { + return xasprintf("only actions are allowed here (not " + "instruction %s)", + ovs_instruction_name_from_type(inst)); + } + if (inst == prev_inst) { + return xasprintf("instruction %s may be specified only once", + ovs_instruction_name_from_type(inst)); + } + } + if (prev_inst != -1 && inst < prev_inst) { + return xasprintf("instruction %s must be specified before %s", + ovs_instruction_name_from_type(inst), + ovs_instruction_name_from_type(prev_inst)); + } + prev_inst = inst; + } + ofpact_pad(ofpacts); + + if (drop && ofpbuf_size(ofpacts)) { + return xstrdup("\"drop\" must not be accompanied by any other action " + "or instruction"); + } + + /* XXX + * + * If write_metadata is specified as an action AND an instruction, ofpacts + * could be invalid. */ + retval = ofpacts_verify(ofpbuf_data(ofpacts), ofpbuf_size(ofpacts)); + if (retval) { + return xstrdup("Incorrect instruction ordering"); + } + + return NULL; +} + +static char * WARN_UNUSED_RESULT +ofpacts_parse(char *str, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols, bool allow_instructions) +{ + uint32_t orig_size = ofpbuf_size(ofpacts); + char *error = ofpacts_parse__(str, ofpacts, usable_protocols, + allow_instructions); + if (error) { + ofpbuf_set_size(ofpacts, orig_size); + } + return error; +} + +static char * WARN_UNUSED_RESULT +ofpacts_parse_copy(const char *s_, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols, + bool allow_instructions) +{ + char *error, *s; + + *usable_protocols = OFPUTIL_P_ANY; + + s = xstrdup(s_); + error = ofpacts_parse(s, ofpacts, usable_protocols, allow_instructions); + free(s); + + return error; +} + +/* Parses 's' as a set of OpenFlow actions and appends the actions to + * 'ofpacts'. + * + * Returns NULL if successful, otherwise a malloc()'d string describing the + * error. The caller is responsible for freeing the returned string. */ +char * WARN_UNUSED_RESULT +ofpacts_parse_actions(const char *s, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols) +{ + return ofpacts_parse_copy(s, ofpacts, usable_protocols, false); +} + +/* Parses 's' as a set of OpenFlow instructions and appends the instructions to + * 'ofpacts'. + * + * Returns NULL if successful, otherwise a malloc()'d string describing the + * error. The caller is responsible for freeing the returned string. */ +char * WARN_UNUSED_RESULT +ofpacts_parse_instructions(const char *s, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols) +{ + return ofpacts_parse_copy(s, ofpacts, usable_protocols, true); +} const char * ofpact_name(enum ofpact_type type) diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h index c215ffcb4..3329122bc 100644 --- a/lib/ofp-actions.h +++ b/lib/ofp-actions.h @@ -130,14 +130,14 @@ enum { * ofpact", usually followed by other data that describes the action. Actions * are padded out to a multiple of OFPACT_ALIGNTO bytes in length. * - * The 'compat' member is special: + * The 'raw' member is special: * * - Most "struct ofpact"s correspond to one particular kind of OpenFlow * action, at least in a given OpenFlow version. For example, * OFPACT_SET_VLAN_VID corresponds to OFPAT10_SET_VLAN_VID in OpenFlow * 1.0. * - * For such actions, the 'compat' member is not meaningful and generally + * For such actions, the 'raw' member is not meaningful and generally * should be zero. * * - A few "struct ofpact"s correspond to multiple OpenFlow actions. For @@ -148,14 +148,14 @@ enum { * (Otherwise, we'd violate the promise made in DESIGN, in the "Action * Reproduction" section.) * - * For such actions, the 'compat' member should be the original action - * type. (If the action didn't originate from OpenFlow, then setting - * 'compat' to zero should be fine: code to translate the ofpact to - * OpenFlow must tolerate this case.) + * For such actions, the 'raw' member should be the "enum ofp_raw_action" + * originally extracted from the OpenFlow action. (If the action didn't + * originate from OpenFlow, then setting 'raw' to zero should be fine: + * code to translate the ofpact to OpenFlow must tolerate this case.) */ struct ofpact { enum ofpact_type type; /* OFPACT_*. */ - enum ofputil_action_code compat; /* Original type when added, if any. */ + uint8_t raw; /* Original type when added, if any. */ uint16_t len; /* Length of the action, in bytes, including * struct ofpact, excluding padding. */ }; @@ -237,6 +237,29 @@ struct ofpact_output_reg { struct mf_subfield src; }; +/* Bundle slave choice algorithm to apply. + * + * In the descriptions below, 'slaves' is the list of possible slaves in the + * order they appear in the OpenFlow action. */ +enum nx_bd_algorithm { + /* Chooses the first live slave listed in the bundle. + * + * O(n_slaves) performance. */ + NX_BD_ALG_ACTIVE_BACKUP = 0, + + /* Highest Random Weight. + * + * for i in [0,n_slaves): + * weights[i] = hash(flow, i) + * slave = { slaves[i] such that weights[i] >= weights[j] for all j != i } + * + * Redistributes 1/n_slaves of traffic when a slave's liveness changes. + * O(n_slaves) performance. + * + * Uses the 'fields' and 'basis' parameters. */ + NX_BD_ALG_HRW = 1 +}; + /* OFPACT_BUNDLE. * * Used for NXAST_BUNDLE. */ @@ -439,6 +462,8 @@ struct ofpact_nest { struct ofpact actions[]; }; BUILD_ASSERT_DECL(offsetof(struct ofpact_nest, actions) % OFPACT_ALIGNTO == 0); +BUILD_ASSERT_DECL(offsetof(struct ofpact_nest, actions) + == sizeof(struct ofpact_nest)); static inline size_t ofpact_nest_get_action_len(const struct ofpact_nest *on) @@ -470,6 +495,51 @@ struct ofpact_learn_spec { struct mf_subfield dst; /* NX_LEARN_DST_MATCH, NX_LEARN_DST_LOAD only. */ }; + +/* Bits for 'flags' in struct nx_action_learn. + * + * If NX_LEARN_F_SEND_FLOW_REM is set, then the learned flows will have their + * OFPFF_SEND_FLOW_REM flag set. + * + * If NX_LEARN_F_DELETE_LEARNED is set, then removing this action will delete + * all the flows from the learn action's 'table_id' that have the learn + * action's 'cookie'. Important points: + * + * - The deleted flows include those created by this action, those created + * by other learn actions with the same 'table_id' and 'cookie', those + * created by flow_mod requests by a controller in the specified table + * with the specified cookie, and those created through any other + * means. + * + * - If multiple flows specify "learn" actions with + * NX_LEARN_F_DELETE_LEARNED with the same 'table_id' and 'cookie', then + * no deletion occurs until all of those "learn" actions are deleted. + * + * - Deleting a flow that contains a learn action is the most obvious way + * to delete a learn action. Modifying a flow's actions, or replacing it + * by a new flow, can also delete a learn action. Finally, replacing a + * learn action with NX_LEARN_F_DELETE_LEARNED with a learn action + * without that flag also effectively deletes the learn action and can + * trigger flow deletion. + * + * NX_LEARN_F_DELETE_LEARNED was added in Open vSwitch 2.4. */ +enum nx_learn_flags { + NX_LEARN_F_SEND_FLOW_REM = 1 << 0, + NX_LEARN_F_DELETE_LEARNED = 1 << 1, +}; + +#define NX_LEARN_N_BITS_MASK 0x3ff + +#define NX_LEARN_SRC_FIELD (0 << 13) /* Copy from field. */ +#define NX_LEARN_SRC_IMMEDIATE (1 << 13) /* Copy from immediate value. */ +#define NX_LEARN_SRC_MASK (1 << 13) + +#define NX_LEARN_DST_MATCH (0 << 11) /* Add match criterion. */ +#define NX_LEARN_DST_LOAD (1 << 11) /* Add NXAST_REG_LOAD action. */ +#define NX_LEARN_DST_OUTPUT (2 << 11) /* Add OFPAT_OUTPUT action. */ +#define NX_LEARN_DST_RESERVED (3 << 11) /* Not yet defined. */ +#define NX_LEARN_DST_MASK (3 << 11) + /* OFPACT_LEARN. * * Used for NXAST_LEARN. */ @@ -489,6 +559,60 @@ struct ofpact_learn { struct ofpact_learn_spec specs[]; }; +/* Multipath link choice algorithm to apply. + * + * In the descriptions below, 'n_links' is max_link + 1. */ +enum nx_mp_algorithm { + /* link = hash(flow) % n_links. + * + * Redistributes all traffic when n_links changes. O(1) performance. See + * RFC 2992. + * + * Use UINT16_MAX for max_link to get a raw hash value. */ + NX_MP_ALG_MODULO_N = 0, + + /* link = hash(flow) / (MAX_HASH / n_links). + * + * Redistributes between one-quarter and one-half of traffic when n_links + * changes. O(1) performance. See RFC 2992. + */ + NX_MP_ALG_HASH_THRESHOLD = 1, + + /* Highest Random Weight. + * + * for i in [0,n_links): + * weights[i] = hash(flow, i) + * link = { i such that weights[i] >= weights[j] for all j != i } + * + * Redistributes 1/n_links of traffic when n_links changes. O(n_links) + * performance. If n_links is greater than a threshold (currently 64, but + * subject to change), Open vSwitch will substitute another algorithm + * automatically. See RFC 2992. */ + NX_MP_ALG_HRW = 2, + + /* Iterative Hash. + * + * i = 0 + * repeat: + * i = i + 1 + * link = hash(flow, i) % arg + * while link > max_link + * + * Redistributes 1/n_links of traffic when n_links changes. O(1) + * performance when arg/max_link is bounded by a constant. + * + * Redistributes all traffic when arg changes. + * + * arg must be greater than max_link and for best performance should be no + * more than approximately max_link * 2. If arg is outside the acceptable + * range, Open vSwitch will automatically substitute the least power of 2 + * greater than max_link. + * + * This algorithm is specific to Open vSwitch. + */ + NX_MP_ALG_ITER_HASH = 3, +}; + /* OFPACT_MULTIPATH. * * Used for NXAST_MULTIPATH. */ @@ -624,10 +748,14 @@ bool ofpacts_equal(const struct ofpact a[], size_t a_len, const struct ofpact b[], size_t b_len); uint32_t ofpacts_get_meter(const struct ofpact[], size_t ofpacts_len); -/* Formatting ofpacts. - * - * (For parsing ofpacts, see ofp-parse.h.) */ +/* Formatting and parsing ofpacts. */ void ofpacts_format(const struct ofpact[], size_t ofpacts_len, struct ds *); +char *ofpacts_parse_actions(const char *, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols) + WARN_UNUSED_RESULT; +char *ofpacts_parse_instructions(const char *, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols) + WARN_UNUSED_RESULT; const char *ofpact_name(enum ofpact_type); /* Internal use by the helpers below. */ diff --git a/lib/ofp-msgs.h b/lib/ofp-msgs.h index 53423ea13..ab64a11da 100644 --- a/lib/ofp-msgs.h +++ b/lib/ofp-msgs.h @@ -172,7 +172,7 @@ enum ofpraw { /* OFPT 1.1+ (13): struct ofp11_packet_out, uint8_t[]. */ OFPRAW_OFPT11_PACKET_OUT, - /* OFPT 1.0 (14): struct ofp10_flow_mod, struct ofp_action_header[]. */ + /* OFPT 1.0 (14): struct ofp10_flow_mod, uint8_t[]. */ OFPRAW_OFPT10_FLOW_MOD, /* OFPT 1.1+ (14): struct ofp11_flow_mod, struct ofp11_instruction[]. */ OFPRAW_OFPT11_FLOW_MOD, diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c index 96eaadd52..aa141cccd 100644 --- a/lib/ofp-parse.c +++ b/lib/ofp-parse.c @@ -169,1003 +169,6 @@ str_to_ip(const char *str, ovs_be32 *ip) return NULL; } -/* Parses 'arg' as the argument to an "enqueue" action, and appends such an - * action to 'ofpacts'. - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string. */ -static char * WARN_UNUSED_RESULT -parse_enqueue(char *arg, struct ofpbuf *ofpacts) -{ - char *sp = NULL; - char *port = strtok_r(arg, ":q,", &sp); - char *queue = strtok_r(NULL, "", &sp); - struct ofpact_enqueue *enqueue; - - if (port == NULL || queue == NULL) { - return xstrdup("\"enqueue\" syntax is \"enqueue:PORT:QUEUE\" or " - "\"enqueue(PORT,QUEUE)\""); - } - - enqueue = ofpact_put_ENQUEUE(ofpacts); - if (!ofputil_port_from_string(port, &enqueue->port)) { - return xasprintf("%s: enqueue to unknown port", port); - } - return str_to_u32(queue, &enqueue->queue); -} - -/* Parses 'arg' as the argument to an "output" action, and appends such an - * action to 'ofpacts'. - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string. */ -static char * WARN_UNUSED_RESULT -parse_output(const char *arg, struct ofpbuf *ofpacts) -{ - if (strchr(arg, '[')) { - struct ofpact_output_reg *output_reg; - - output_reg = ofpact_put_OUTPUT_REG(ofpacts); - output_reg->max_len = UINT16_MAX; - return mf_parse_subfield(&output_reg->src, arg); - } else { - struct ofpact_output *output; - - output = ofpact_put_OUTPUT(ofpacts); - if (!ofputil_port_from_string(arg, &output->port)) { - return xasprintf("%s: output to unknown port", arg); - } - output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0; - return NULL; - } -} - -/* Parses 'arg' as the argument to an "resubmit" action, and appends such an - * action to 'ofpacts'. - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string. */ -static char * WARN_UNUSED_RESULT -parse_resubmit(char *arg, struct ofpbuf *ofpacts) -{ - struct ofpact_resubmit *resubmit; - char *in_port_s, *table_s; - - resubmit = ofpact_put_RESUBMIT(ofpacts); - - in_port_s = strsep(&arg, ","); - if (in_port_s && in_port_s[0]) { - if (!ofputil_port_from_string(in_port_s, &resubmit->in_port)) { - return xasprintf("%s: resubmit to unknown port", in_port_s); - } - } else { - resubmit->in_port = OFPP_IN_PORT; - } - - table_s = strsep(&arg, ","); - if (table_s && table_s[0]) { - uint32_t table_id = 0; - char *error; - - error = str_to_u32(table_s, &table_id); - if (error) { - return error; - } - resubmit->table_id = table_id; - } else { - resubmit->table_id = 255; - } - - if (resubmit->in_port == OFPP_IN_PORT && resubmit->table_id == 255) { - return xstrdup("at least one \"in_port\" or \"table\" must be " - "specified on resubmit"); - } - return NULL; -} - -/* Parses 'arg' as the argument to a "note" action, and appends such an action - * to 'ofpacts'. - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string. */ -static char * WARN_UNUSED_RESULT -parse_note(const char *arg, struct ofpbuf *ofpacts) -{ - struct ofpact_note *note; - - note = ofpact_put_NOTE(ofpacts); - while (*arg != '\0') { - uint8_t byte; - bool ok; - - if (*arg == '.') { - arg++; - } - if (*arg == '\0') { - break; - } - - byte = hexits_value(arg, 2, &ok); - if (!ok) { - return xstrdup("bad hex digit in `note' argument"); - } - ofpbuf_put(ofpacts, &byte, 1); - - note = ofpacts->frame; - note->length++; - - arg += 2; - } - ofpact_update_len(ofpacts, ¬e->ofpact); - return NULL; -} - -/* Parses 'arg' as the argument to a "fin_timeout" action, and appends such an - * action to 'ofpacts'. - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string. */ -static char * WARN_UNUSED_RESULT -parse_fin_timeout(struct ofpbuf *b, char *arg) -{ - struct ofpact_fin_timeout *oft = ofpact_put_FIN_TIMEOUT(b); - char *key, *value; - - while (ofputil_parse_key_value(&arg, &key, &value)) { - char *error; - - if (!strcmp(key, "idle_timeout")) { - error = str_to_u16(value, key, &oft->fin_idle_timeout); - } else if (!strcmp(key, "hard_timeout")) { - error = str_to_u16(value, key, &oft->fin_hard_timeout); - } else { - error = xasprintf("invalid key '%s' in 'fin_timeout' argument", - key); - } - - if (error) { - return error; - } - } - return NULL; -} - -/* Parses 'arg' as the argument to a "controller" action, and appends such an - * action to 'ofpacts'. - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string. */ -static char * WARN_UNUSED_RESULT -parse_controller(struct ofpbuf *b, char *arg) -{ - enum ofp_packet_in_reason reason = OFPR_ACTION; - uint16_t controller_id = 0; - uint16_t max_len = UINT16_MAX; - - if (!arg[0]) { - /* Use defaults. */ - } else if (strspn(arg, "0123456789") == strlen(arg)) { - char *error = str_to_u16(arg, "max_len", &max_len); - if (error) { - return error; - } - } else { - char *name, *value; - - while (ofputil_parse_key_value(&arg, &name, &value)) { - if (!strcmp(name, "reason")) { - if (!ofputil_packet_in_reason_from_string(value, &reason)) { - return xasprintf("unknown reason \"%s\"", value); - } - } else if (!strcmp(name, "max_len")) { - char *error = str_to_u16(value, "max_len", &max_len); - if (error) { - return error; - } - } else if (!strcmp(name, "id")) { - char *error = str_to_u16(value, "id", &controller_id); - if (error) { - return error; - } - } else { - return xasprintf("unknown key \"%s\" parsing controller " - "action", name); - } - } - } - - if (reason == OFPR_ACTION && controller_id == 0) { - struct ofpact_output *output; - - output = ofpact_put_OUTPUT(b); - output->port = OFPP_CONTROLLER; - output->max_len = max_len; - } else { - struct ofpact_controller *controller; - - controller = ofpact_put_CONTROLLER(b); - controller->max_len = max_len; - controller->reason = reason; - controller->controller_id = controller_id; - } - - return NULL; -} - -static void -parse_noargs_dec_ttl(struct ofpbuf *b) -{ - struct ofpact_cnt_ids *ids; - uint16_t id = 0; - - ofpact_put_DEC_TTL(b); - ofpbuf_put(b, &id, sizeof id); - ids = b->frame; - ids->n_controllers++; - ofpact_update_len(b, &ids->ofpact); -} - -/* Parses 'arg' as the argument to a "dec_ttl" action, and appends such an - * action to 'ofpacts'. - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string. */ -static char * WARN_UNUSED_RESULT -parse_dec_ttl(struct ofpbuf *b, char *arg) -{ - if (*arg == '\0') { - parse_noargs_dec_ttl(b); - } else { - struct ofpact_cnt_ids *ids; - char *cntr; - - ids = ofpact_put_DEC_TTL(b); - ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL_CNT_IDS; - for (cntr = strtok_r(arg, ", ", &arg); cntr != NULL; - cntr = strtok_r(NULL, ", ", &arg)) { - uint16_t id = atoi(cntr); - - ofpbuf_put(b, &id, sizeof id); - ids = b->frame; - ids->n_controllers++; - } - if (!ids->n_controllers) { - return xstrdup("dec_ttl_cnt_ids: expected at least one controller " - "id."); - } - ofpact_update_len(b, &ids->ofpact); - } - return NULL; -} - -/* Parses 'arg' as the argument to a "set_mpls_label" action, and appends such - * an action to 'b'. - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string. */ -static char * WARN_UNUSED_RESULT -parse_set_mpls_label(struct ofpbuf *b, const char *arg) -{ - struct ofpact_mpls_label *mpls_label = ofpact_put_SET_MPLS_LABEL(b); - - if (*arg == '\0') { - return xstrdup("parse_set_mpls_label: expected label."); - } - - mpls_label->label = htonl(atoi(arg)); - return NULL; -} - -/* Parses 'arg' as the argument to a "set_mpls_tc" action, and appends such an - * action to 'b'. - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string. */ -static char * WARN_UNUSED_RESULT -parse_set_mpls_tc(struct ofpbuf *b, const char *arg) -{ - struct ofpact_mpls_tc *mpls_tc = ofpact_put_SET_MPLS_TC(b); - - if (*arg == '\0') { - return xstrdup("parse_set_mpls_tc: expected tc."); - } - - mpls_tc->tc = atoi(arg); - return NULL; -} - -/* Parses 'arg' as the argument to a "set_mpls_ttl" action, and appends such an - * action to 'ofpacts'. - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string. */ -static char * WARN_UNUSED_RESULT -parse_set_mpls_ttl(struct ofpbuf *b, const char *arg) -{ - struct ofpact_mpls_ttl *mpls_ttl = ofpact_put_SET_MPLS_TTL(b); - - if (*arg == '\0') { - return xstrdup("parse_set_mpls_ttl: expected ttl."); - } - - mpls_ttl->ttl = atoi(arg); - return NULL; -} - -/* Parses a "set_field" action with argument 'arg', appending the parsed - * action to 'ofpacts'. - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string. */ -static char * WARN_UNUSED_RESULT -set_field_parse__(char *arg, struct ofpbuf *ofpacts, - enum ofputil_protocol *usable_protocols) -{ - struct ofpact_set_field *sf = ofpact_put_SET_FIELD(ofpacts); - char *value; - char *delim; - char *key; - const struct mf_field *mf; - char *error; - - value = arg; - delim = strstr(arg, "->"); - if (!delim) { - return xasprintf("%s: missing `->'", arg); - } - if (strlen(delim) <= strlen("->")) { - return xasprintf("%s: missing field name following `->'", arg); - } - - key = delim + strlen("->"); - mf = mf_from_name(key); - if (!mf) { - return xasprintf("%s is not a valid OXM field name", key); - } - if (!mf->writable) { - return xasprintf("%s is read-only", key); - } - sf->field = mf; - delim[0] = '\0'; - error = mf_parse_value(mf, value, &sf->value); - if (error) { - return error; - } - - if (!mf_is_value_valid(mf, &sf->value)) { - return xasprintf("%s is not a valid value for field %s", value, key); - } - - *usable_protocols &= mf->usable_protocols; - return NULL; -} - -/* Parses 'arg' as the argument to a "set_field" action, and appends such an - * action to 'ofpacts'. - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string. */ -static char * WARN_UNUSED_RESULT -set_field_parse(const char *arg, struct ofpbuf *ofpacts, - enum ofputil_protocol *usable_protocols) -{ - char *copy = xstrdup(arg); - char *error = set_field_parse__(copy, ofpacts, usable_protocols); - free(copy); - return error; -} - -/* Parses 'arg' as the argument to a "write_metadata" instruction, and appends - * such an action to 'ofpacts'. - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string. */ -static char * WARN_UNUSED_RESULT -parse_metadata(struct ofpbuf *b, char *arg) -{ - struct ofpact_metadata *om; - char *mask = strchr(arg, '/'); - - om = ofpact_put_WRITE_METADATA(b); - - if (mask) { - char *error; - - *mask = '\0'; - error = str_to_be64(mask + 1, &om->mask); - if (error) { - return error; - } - } else { - om->mask = OVS_BE64_MAX; - } - - return str_to_be64(arg, &om->metadata); -} - -/* Parses 'arg' as the argument to a "sample" action, and appends such an - * action to 'ofpacts'. - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string. */ -static char * WARN_UNUSED_RESULT -parse_sample(struct ofpbuf *b, char *arg) -{ - struct ofpact_sample *os = ofpact_put_SAMPLE(b); - char *key, *value; - - while (ofputil_parse_key_value(&arg, &key, &value)) { - char *error = NULL; - - if (!strcmp(key, "probability")) { - error = str_to_u16(value, "probability", &os->probability); - if (!error && os->probability == 0) { - error = xasprintf("invalid probability value \"%s\"", value); - } - } else if (!strcmp(key, "collector_set_id")) { - error = str_to_u32(value, &os->collector_set_id); - } else if (!strcmp(key, "obs_domain_id")) { - error = str_to_u32(value, &os->obs_domain_id); - } else if (!strcmp(key, "obs_point_id")) { - error = str_to_u32(value, &os->obs_point_id); - } else { - error = xasprintf("invalid key \"%s\" in \"sample\" argument", - key); - } - if (error) { - return error; - } - } - if (os->probability == 0) { - return xstrdup("non-zero \"probability\" must be specified on sample"); - } - return NULL; -} - -/* Parses 'arg' as the argument to action 'code', and appends such an action to - * 'ofpacts'. - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string. */ -static char * WARN_UNUSED_RESULT -parse_named_action(enum ofputil_action_code code, - char *arg, struct ofpbuf *ofpacts, - enum ofputil_protocol *usable_protocols) -{ - size_t orig_size = ofpbuf_size(ofpacts); - struct ofpact_tunnel *tunnel; - struct ofpact_vlan_vid *vlan_vid; - struct ofpact_vlan_pcp *vlan_pcp; - char *error = NULL; - uint16_t ethertype = 0; - uint16_t vid = 0; - uint8_t tos = 0; - uint8_t ecn = 0; - uint8_t ttl = 0; - uint8_t pcp = 0; - - switch (code) { - case OFPUTIL_ACTION_INVALID: - OVS_NOT_REACHED(); - - case OFPUTIL_OFPAT10_OUTPUT: - case OFPUTIL_OFPAT11_OUTPUT: - case OFPUTIL_OFPAT13_OUTPUT: - error = parse_output(arg, ofpacts); - break; - - case OFPUTIL_OFPAT10_SET_VLAN_VID: - case OFPUTIL_OFPAT11_SET_VLAN_VID: - error = str_to_u16(arg, "VLAN VID", &vid); - if (error) { - return error; - } - - if (vid & ~VLAN_VID_MASK) { - return xasprintf("%s: not a valid VLAN VID", arg); - } - vlan_vid = ofpact_put_SET_VLAN_VID(ofpacts); - vlan_vid->vlan_vid = vid; - vlan_vid->ofpact.compat = code; - vlan_vid->push_vlan_if_needed = code == OFPUTIL_OFPAT10_SET_VLAN_VID; - break; - - case OFPUTIL_OFPAT10_SET_VLAN_PCP: - case OFPUTIL_OFPAT11_SET_VLAN_PCP: - error = str_to_u8(arg, "VLAN PCP", &pcp); - if (error) { - return error; - } - - if (pcp & ~7) { - return xasprintf("%s: not a valid VLAN PCP", arg); - } - vlan_pcp = ofpact_put_SET_VLAN_PCP(ofpacts); - vlan_pcp->vlan_pcp = pcp; - vlan_pcp->ofpact.compat = code; - vlan_pcp->push_vlan_if_needed = code == OFPUTIL_OFPAT10_SET_VLAN_PCP; - break; - - case OFPUTIL_OFPAT12_SET_FIELD: - case OFPUTIL_OFPAT13_SET_FIELD: - return set_field_parse(arg, ofpacts, usable_protocols); - - case OFPUTIL_OFPAT10_STRIP_VLAN: - case OFPUTIL_OFPAT11_POP_VLAN: - case OFPUTIL_OFPAT13_POP_VLAN: - ofpact_put_STRIP_VLAN(ofpacts)->ofpact.compat = code; - break; - - case OFPUTIL_OFPAT11_PUSH_VLAN: - case OFPUTIL_OFPAT13_PUSH_VLAN: - *usable_protocols &= OFPUTIL_P_OF11_UP; - error = str_to_u16(arg, "ethertype", ðertype); - if (error) { - return error; - } - - if (ethertype != ETH_TYPE_VLAN_8021Q) { - /* XXX ETH_TYPE_VLAN_8021AD case isn't supported */ - return xasprintf("%s: not a valid VLAN ethertype", arg); - } - - ofpact_put_PUSH_VLAN(ofpacts); - break; - - case OFPUTIL_OFPAT11_SET_QUEUE: - case OFPUTIL_OFPAT13_SET_QUEUE: - error = str_to_u32(arg, &ofpact_put_SET_QUEUE(ofpacts)->queue_id); - break; - - case OFPUTIL_OFPAT10_SET_DL_SRC: - case OFPUTIL_OFPAT11_SET_DL_SRC: - error = str_to_mac(arg, ofpact_put_SET_ETH_SRC(ofpacts)->mac); - break; - - case OFPUTIL_OFPAT10_SET_DL_DST: - case OFPUTIL_OFPAT11_SET_DL_DST: - error = str_to_mac(arg, ofpact_put_SET_ETH_DST(ofpacts)->mac); - break; - - case OFPUTIL_OFPAT10_SET_NW_SRC: - case OFPUTIL_OFPAT11_SET_NW_SRC: - error = str_to_ip(arg, &ofpact_put_SET_IPV4_SRC(ofpacts)->ipv4); - break; - - case OFPUTIL_OFPAT10_SET_NW_DST: - case OFPUTIL_OFPAT11_SET_NW_DST: - error = str_to_ip(arg, &ofpact_put_SET_IPV4_DST(ofpacts)->ipv4); - break; - - case OFPUTIL_OFPAT10_SET_NW_TOS: - case OFPUTIL_OFPAT11_SET_NW_TOS: - error = str_to_u8(arg, "TOS", &tos); - if (error) { - return error; - } - - if (tos & ~IP_DSCP_MASK) { - return xasprintf("%s: not a valid TOS", arg); - } - ofpact_put_SET_IP_DSCP(ofpacts)->dscp = tos; - break; - - case OFPUTIL_OFPAT11_SET_NW_ECN: - error = str_to_u8(arg, "ECN", &ecn); - if (error) { - return error; - } - - if (ecn & ~IP_ECN_MASK) { - return xasprintf("%s: not a valid ECN", arg); - } - ofpact_put_SET_IP_ECN(ofpacts)->ecn = ecn; - break; - - case OFPUTIL_OFPAT11_SET_NW_TTL: - case OFPUTIL_OFPAT13_SET_NW_TTL: - error = str_to_u8(arg, "TTL", &ttl); - if (error) { - return error; - } - - ofpact_put_SET_IP_TTL(ofpacts)->ttl = ttl; - break; - - case OFPUTIL_OFPAT11_DEC_NW_TTL: - case OFPUTIL_OFPAT13_DEC_NW_TTL: - OVS_NOT_REACHED(); - - case OFPUTIL_OFPAT10_SET_TP_SRC: - case OFPUTIL_OFPAT11_SET_TP_SRC: - error = str_to_u16(arg, "source port", - &ofpact_put_SET_L4_SRC_PORT(ofpacts)->port); - break; - - case OFPUTIL_OFPAT10_SET_TP_DST: - case OFPUTIL_OFPAT11_SET_TP_DST: - error = str_to_u16(arg, "destination port", - &ofpact_put_SET_L4_DST_PORT(ofpacts)->port); - break; - - case OFPUTIL_OFPAT10_ENQUEUE: - error = parse_enqueue(arg, ofpacts); - break; - - case OFPUTIL_NXAST_RESUBMIT: - error = parse_resubmit(arg, ofpacts); - break; - - case OFPUTIL_NXAST_SET_TUNNEL: - case OFPUTIL_NXAST_SET_TUNNEL64: - tunnel = ofpact_put_SET_TUNNEL(ofpacts); - tunnel->ofpact.compat = code; - error = str_to_u64(arg, &tunnel->tun_id); - break; - - case OFPUTIL_NXAST_WRITE_METADATA: - error = parse_metadata(ofpacts, arg); - break; - - case OFPUTIL_NXAST_SET_QUEUE: - error = str_to_u32(arg, &ofpact_put_SET_QUEUE(ofpacts)->queue_id); - break; - - case OFPUTIL_NXAST_POP_QUEUE: - ofpact_put_POP_QUEUE(ofpacts); - break; - - case OFPUTIL_NXAST_REG_MOVE: - error = nxm_parse_reg_move(ofpact_put_REG_MOVE(ofpacts), arg); - break; - - case OFPUTIL_NXAST_REG_LOAD: - error = nxm_parse_reg_load(ofpact_put_REG_LOAD(ofpacts), arg); - break; - - case OFPUTIL_NXAST_NOTE: - error = parse_note(arg, ofpacts); - break; - - case OFPUTIL_NXAST_MULTIPATH: - error = multipath_parse(ofpact_put_MULTIPATH(ofpacts), arg); - break; - - case OFPUTIL_NXAST_BUNDLE: - error = bundle_parse(arg, ofpacts); - break; - - case OFPUTIL_NXAST_BUNDLE_LOAD: - error = bundle_parse_load(arg, ofpacts); - break; - - case OFPUTIL_NXAST_RESUBMIT_TABLE: - case OFPUTIL_NXAST_OUTPUT_REG: - case OFPUTIL_NXAST_DEC_TTL_CNT_IDS: - OVS_NOT_REACHED(); - - case OFPUTIL_NXAST_LEARN: - error = learn_parse(arg, ofpacts); - break; - - case OFPUTIL_NXAST_EXIT: - ofpact_put_EXIT(ofpacts); - break; - - case OFPUTIL_NXAST_DEC_TTL: - error = parse_dec_ttl(ofpacts, arg); - break; - - case OFPUTIL_NXAST_SET_MPLS_LABEL: - case OFPUTIL_OFPAT11_SET_MPLS_LABEL: - error = parse_set_mpls_label(ofpacts, arg); - break; - - case OFPUTIL_NXAST_SET_MPLS_TC: - case OFPUTIL_OFPAT11_SET_MPLS_TC: - error = parse_set_mpls_tc(ofpacts, arg); - break; - - case OFPUTIL_NXAST_SET_MPLS_TTL: - case OFPUTIL_OFPAT11_SET_MPLS_TTL: - case OFPUTIL_OFPAT13_SET_MPLS_TTL: - error = parse_set_mpls_ttl(ofpacts, arg); - break; - - case OFPUTIL_OFPAT11_DEC_MPLS_TTL: - case OFPUTIL_OFPAT13_DEC_MPLS_TTL: - case OFPUTIL_NXAST_DEC_MPLS_TTL: - ofpact_put_DEC_MPLS_TTL(ofpacts); - break; - - case OFPUTIL_NXAST_FIN_TIMEOUT: - error = parse_fin_timeout(ofpacts, arg); - break; - - case OFPUTIL_NXAST_CONTROLLER: - error = parse_controller(ofpacts, arg); - break; - - case OFPUTIL_OFPAT11_PUSH_MPLS: - case OFPUTIL_OFPAT13_PUSH_MPLS: - case OFPUTIL_NXAST_PUSH_MPLS: - error = str_to_u16(arg, "push_mpls", ðertype); - if (!error) { - ofpact_put_PUSH_MPLS(ofpacts)->ethertype = htons(ethertype); - } - break; - - case OFPUTIL_OFPAT11_POP_MPLS: - case OFPUTIL_OFPAT13_POP_MPLS: - case OFPUTIL_NXAST_POP_MPLS: - error = str_to_u16(arg, "pop_mpls", ðertype); - if (!error) { - ofpact_put_POP_MPLS(ofpacts)->ethertype = htons(ethertype); - } - break; - - case OFPUTIL_OFPAT11_GROUP: - case OFPUTIL_OFPAT13_GROUP: - error = str_to_u32(arg, &ofpact_put_GROUP(ofpacts)->group_id); - break; - - /* FIXME when implement OFPAT13_* */ - case OFPUTIL_OFPAT13_COPY_TTL_OUT: - case OFPUTIL_OFPAT13_COPY_TTL_IN: - case OFPUTIL_OFPAT13_PUSH_PBB: - case OFPUTIL_OFPAT13_POP_PBB: - OVS_NOT_REACHED(); - - case OFPUTIL_NXAST_STACK_PUSH: - error = nxm_parse_stack_action(ofpact_put_STACK_PUSH(ofpacts), arg); - break; - case OFPUTIL_NXAST_STACK_POP: - error = nxm_parse_stack_action(ofpact_put_STACK_POP(ofpacts), arg); - break; - - case OFPUTIL_NXAST_SAMPLE: - error = parse_sample(ofpacts, arg); - break; - } - - if (error) { - ofpbuf_set_size(ofpacts, orig_size); - } - return error; -} - -/* Parses action 'act', with argument 'arg', and appends a parsed version to - * 'ofpacts'. - * - * 'n_actions' specifies the number of actions already parsed (for proper - * handling of "drop" actions). - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string. */ -static char * WARN_UNUSED_RESULT -str_to_ofpact__(char *pos, char *act, char *arg, - struct ofpbuf *ofpacts, int n_actions, - enum ofputil_protocol *usable_protocols) -{ - int code = ofputil_action_code_from_name(act); - if (code >= 0) { - return parse_named_action(code, arg, ofpacts, usable_protocols); - } else if (!strcasecmp(act, "drop")) { - if (n_actions) { - return xstrdup("Drop actions must not be preceded by other " - "actions"); - } else if (ofputil_parse_key_value(&pos, &act, &arg)) { - return xstrdup("Drop actions must not be followed by other " - "actions"); - } - } else { - ofp_port_t port; - if (ofputil_port_from_string(act, &port)) { - ofpact_put_OUTPUT(ofpacts)->port = port; - } else { - return xasprintf("Unknown action: %s", act); - } - } - - return NULL; -} - -/* Parses 'str' as a series of actions, and appends them to 'ofpacts'. - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string. */ -static char * WARN_UNUSED_RESULT -str_to_ofpacts__(char *str, struct ofpbuf *ofpacts, - enum ofputil_protocol *usable_protocols) -{ - size_t orig_size = ofpbuf_size(ofpacts); - char *pos, *act, *arg; - int n_actions; - - pos = str; - n_actions = 0; - while (ofputil_parse_key_value(&pos, &act, &arg)) { - char *error = str_to_ofpact__(pos, act, arg, ofpacts, n_actions, - usable_protocols); - if (error) { - ofpbuf_set_size(ofpacts, orig_size); - return error; - } - n_actions++; - } - - ofpact_pad(ofpacts); - return NULL; -} - - -/* Parses 'str' as a series of actions, and appends them to 'ofpacts'. - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string. */ -static char * WARN_UNUSED_RESULT -str_to_ofpacts(char *str, struct ofpbuf *ofpacts, - enum ofputil_protocol *usable_protocols) -{ - size_t orig_size = ofpbuf_size(ofpacts); - char *error_s; - enum ofperr error; - - error_s = str_to_ofpacts__(str, ofpacts, usable_protocols); - if (error_s) { - return error_s; - } - - error = ofpacts_verify(ofpbuf_data(ofpacts), ofpbuf_size(ofpacts)); - if (error) { - ofpbuf_set_size(ofpacts, orig_size); - return xstrdup("Incorrect action ordering"); - } - - return NULL; -} - -/* Parses 'arg' as the argument to instruction 'type', and appends such an - * instruction to 'ofpacts'. - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string. */ -static char * WARN_UNUSED_RESULT -parse_named_instruction(enum ovs_instruction_type type, - char *arg, struct ofpbuf *ofpacts, - enum ofputil_protocol *usable_protocols) -{ - char *error_s = NULL; - enum ofperr error; - - *usable_protocols &= OFPUTIL_P_OF11_UP; - - switch (type) { - case OVSINST_OFPIT11_APPLY_ACTIONS: - OVS_NOT_REACHED(); /* This case is handled by str_to_inst_ofpacts() */ - break; - - case OVSINST_OFPIT11_WRITE_ACTIONS: { - struct ofpact_nest *on; - size_t ofs; - - ofpact_pad(ofpacts); - ofs = ofpbuf_size(ofpacts); - ofpact_put(ofpacts, OFPACT_WRITE_ACTIONS, - offsetof(struct ofpact_nest, actions)); - error_s = str_to_ofpacts__(arg, ofpacts, usable_protocols); - - on = ofpbuf_at_assert(ofpacts, ofs, sizeof *on); - on->ofpact.len = ofpbuf_size(ofpacts) - ofs; - - if (error_s) { - ofpbuf_set_size(ofpacts, ofs); - } - break; - } - - case OVSINST_OFPIT11_CLEAR_ACTIONS: - ofpact_put_CLEAR_ACTIONS(ofpacts); - break; - - case OVSINST_OFPIT13_METER: - *usable_protocols &= OFPUTIL_P_OF13_UP; - error_s = str_to_u32(arg, &ofpact_put_METER(ofpacts)->meter_id); - break; - - case OVSINST_OFPIT11_WRITE_METADATA: - *usable_protocols &= OFPUTIL_P_NXM_OF11_UP; - error_s = parse_metadata(ofpacts, arg); - break; - - case OVSINST_OFPIT11_GOTO_TABLE: { - struct ofpact_goto_table *ogt = ofpact_put_GOTO_TABLE(ofpacts); - char *table_s = strsep(&arg, ","); - if (!table_s || !table_s[0]) { - return xstrdup("instruction goto-table needs table id"); - } - error_s = str_to_u8(table_s, "table", &ogt->table_id); - break; - } - } - - if (error_s) { - return error_s; - } - - /* If write_metadata is specified as an action AND an instruction, ofpacts - could be invalid. */ - error = ofpacts_verify(ofpbuf_data(ofpacts), ofpbuf_size(ofpacts)); - if (error) { - return xstrdup("Incorrect instruction ordering"); - } - return NULL; -} - -/* Parses 'str' as a series of instructions, and appends them to 'ofpacts'. - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string. */ -static char * WARN_UNUSED_RESULT -str_to_inst_ofpacts(char *str, struct ofpbuf *ofpacts, - enum ofputil_protocol *usable_protocols) -{ - size_t orig_size = ofpbuf_size(ofpacts); - char *pos, *inst, *arg; - int type; - const char *prev_inst = NULL; - int prev_type = -1; - int n_actions = 0; - - pos = str; - while (ofputil_parse_key_value(&pos, &inst, &arg)) { - type = ovs_instruction_type_from_name(inst); - if (type < 0) { - char *error = str_to_ofpact__(pos, inst, arg, ofpacts, n_actions, - usable_protocols); - if (error) { - ofpbuf_set_size(ofpacts, orig_size); - return error; - } - - type = OVSINST_OFPIT11_APPLY_ACTIONS; - if (prev_type == type) { - n_actions++; - continue; - } - } else if (type == OVSINST_OFPIT11_APPLY_ACTIONS) { - ofpbuf_set_size(ofpacts, orig_size); - return xasprintf("%s isn't supported. Just write actions then " - "it is interpreted as apply_actions", inst); - } else { - char *error = parse_named_instruction(type, arg, ofpacts, - usable_protocols); - if (error) { - ofpbuf_set_size(ofpacts, orig_size); - return error; - } - } - - if (type <= prev_type) { - ofpbuf_set_size(ofpacts, orig_size); - if (type == prev_type) { - return xasprintf("instruction %s may be specified only once", - inst); - } else { - return xasprintf("instruction %s must be specified before %s", - inst, prev_inst); - } - } - - prev_inst = inst; - prev_type = type; - n_actions++; - } - ofpact_pad(ofpacts); - - return NULL; -} - struct protocol { const char *name; uint16_t dl_type; @@ -1224,6 +227,20 @@ parse_field(const struct mf_field *mf, const char *s, struct match *match, return error; } +static char * +extract_actions(char *s) +{ + s = strstr(s, "action"); + if (s) { + *s = '\0'; + s = strchr(s + 1, '='); + return s ? s + 1 : NULL; + } else { + return NULL; + } +} + + static char * WARN_UNUSED_RESULT parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string, enum ofputil_protocol *usable_protocols) @@ -1291,18 +308,10 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string, fm->out_group = OFPG11_ANY; fm->delete_reason = OFPRR_DELETE; if (fields & F_ACTIONS) { - act_str = strstr(string, "action"); + act_str = extract_actions(string); if (!act_str) { return xstrdup("must specify an action"); } - *act_str = '\0'; - - act_str = strchr(act_str + 1, '='); - if (!act_str) { - return xstrdup("must specify an action"); - } - - act_str++; } for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { @@ -1429,11 +438,14 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string, fm->new_cookie = htonll(0); } if (fields & F_ACTIONS) { + enum ofputil_protocol action_usable_protocols; struct ofpbuf ofpacts; char *error; ofpbuf_init(&ofpacts, 32); - error = str_to_inst_ofpacts(act_str, &ofpacts, usable_protocols); + error = ofpacts_parse_instructions(act_str, &ofpacts, + &action_usable_protocols); + *usable_protocols &= action_usable_protocols; if (!error) { enum ofperr err; @@ -1799,26 +811,6 @@ parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr, return error; } -/* Parses 's' as a set of OpenFlow actions and appends the actions to - * 'actions'. - * - * Returns NULL if successful, otherwise a malloc()'d string describing the - * error. The caller is responsible for freeing the returned string. */ -char * WARN_UNUSED_RESULT -parse_ofpacts(const char *s_, struct ofpbuf *ofpacts, - enum ofputil_protocol *usable_protocols) -{ - char *s = xstrdup(s_); - char *error; - - *usable_protocols = OFPUTIL_P_ANY; - - error = str_to_ofpacts(s, ofpacts, usable_protocols); - free(s); - - return error; -} - /* Parses 'string' as an OFPT_FLOW_MOD or NXT_FLOW_MOD with command 'command' * (one of OFPFC_*) into 'fm'. * @@ -2093,53 +1085,59 @@ static char * WARN_UNUSED_RESULT parse_bucket_str(struct ofputil_bucket *bucket, char *str_, enum ofputil_protocol *usable_protocols) { + char *pos, *key, *value; struct ofpbuf ofpacts; - char *pos, *act, *arg; - int n_actions; + struct ds actions; + char *error; bucket->weight = 1; bucket->watch_port = OFPP_ANY; bucket->watch_group = OFPG11_ANY; - pos = str_; - n_actions = 0; - ofpbuf_init(&ofpacts, 64); - while (ofputil_parse_key_value(&pos, &act, &arg)) { - char *error = NULL; + ds_init(&actions); - if (!strcasecmp(act, "weight")) { - error = str_to_u16(arg, "weight", &bucket->weight); - } else if (!strcasecmp(act, "watch_port")) { - if (!ofputil_port_from_string(arg, &bucket->watch_port) + pos = str_; + error = NULL; + while (ofputil_parse_key_value(&pos, &key, &value)) { + if (!strcasecmp(key, "weight")) { + error = str_to_u16(value, "weight", &bucket->weight); + } else if (!strcasecmp(key, "watch_port")) { + if (!ofputil_port_from_string(value, &bucket->watch_port) || (ofp_to_u16(bucket->watch_port) >= ofp_to_u16(OFPP_MAX) && bucket->watch_port != OFPP_ANY)) { - error = xasprintf("%s: invalid watch_port", arg); + error = xasprintf("%s: invalid watch_port", value); } - } else if (!strcasecmp(act, "watch_group")) { - error = str_to_u32(arg, &bucket->watch_group); + } else if (!strcasecmp(key, "watch_group")) { + error = str_to_u32(value, &bucket->watch_group); if (!error && bucket->watch_group > OFPG_MAX) { error = xasprintf("invalid watch_group id %"PRIu32, bucket->watch_group); } - } else if (!strcasecmp(act, "actions")) { - if (ofputil_parse_key_value(&arg, &act, &arg)) { - error = str_to_ofpact__(pos, act, arg, &ofpacts, n_actions, - usable_protocols); - n_actions++; - } + } else if (!strcasecmp(key, "action") || !strcasecmp(key, "actions")) { + ds_put_format(&actions, "%s,", value); } else { - error = str_to_ofpact__(pos, act, arg, &ofpacts, n_actions, - usable_protocols); - n_actions++; + ds_put_format(&actions, "%s(%s),", key, value); } if (error) { - ofpbuf_uninit(&ofpacts); + ds_destroy(&actions); return error; } } - ofpact_pad(&ofpacts); + if (!actions.length) { + return xstrdup("bucket must specify actions"); + } + ds_chomp(&actions, ','); + + ofpbuf_init(&ofpacts, 0); + error = ofpacts_parse_actions(ds_cstr(&actions), &ofpacts, + usable_protocols); + ds_destroy(&actions); + if (error) { + ofpbuf_uninit(&ofpacts); + return error; + } bucket->ofpacts = ofpbuf_data(&ofpacts); bucket->ofpacts_len = ofpbuf_size(&ofpacts); diff --git a/lib/ofp-parse.h b/lib/ofp-parse.h index 16398a10c..4636dffd3 100644 --- a/lib/ofp-parse.h +++ b/lib/ofp-parse.h @@ -60,10 +60,6 @@ char *parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *, enum ofputil_protocol *usable_protocols) WARN_UNUSED_RESULT; -char *parse_ofpacts(const char *, struct ofpbuf *ofpacts, - enum ofputil_protocol *usable_protocols) - WARN_UNUSED_RESULT; - char *parse_ofp_exact_flow(struct flow *flow, struct flow *mask, const char *s, const struct simap *portno_names); diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 84be1df72..3a2b1c2ce 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -6258,121 +6258,6 @@ ofputil_pull_phy_port(enum ofp_version ofp_version, struct ofpbuf *b, } } -/* ofp-util.def lists the mapping from names to action. */ -static const char *const names[OFPUTIL_N_ACTIONS] = { - NULL, -#define OFPAT10_ACTION(ENUM, STRUCT, NAME) NAME, -#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) NAME, -#define OFPAT13_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) NAME, -#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) NAME, -#include "ofp-util.def" -}; - -/* Returns the 'enum ofputil_action_code' corresponding to 'name' (e.g. if - * 'name' is "output" then the return value is OFPUTIL_OFPAT10_OUTPUT), or -1 - * if 'name' is not the name of any action. */ -int -ofputil_action_code_from_name(const char *name) -{ - const char *const *p; - - for (p = names; p < &names[ARRAY_SIZE(names)]; p++) { - if (*p && !strcasecmp(name, *p)) { - return p - names; - } - } - return -1; -} - -/* Returns name corresponding to the 'enum ofputil_action_code', - * or "Unkonwn action", if the name is not available. */ -const char * -ofputil_action_name_from_code(enum ofputil_action_code code) -{ - return code < (int)OFPUTIL_N_ACTIONS && names[code] ? names[code] - : "Unknown action"; -} - -enum ofputil_action_code -ofputil_action_code_from_ofp13_action(enum ofp13_action_type type) -{ - switch (type) { - -#define OFPAT13_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \ - case ENUM: \ - return OFPUTIL_##ENUM; -#include "ofp-util.def" - - default: - return OFPUTIL_ACTION_INVALID; - } -} - -/* Appends an action of the type specified by 'code' to 'buf' and returns the - * action. Initializes the parts of 'action' that identify it as having type - * <ENUM> and length 'sizeof *action' and zeros the rest. For actions that - * have variable length, the length used and cleared is that of struct - * <STRUCT>. */ -void * -ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf) -{ - switch (code) { - case OFPUTIL_ACTION_INVALID: -#define OFPAT13_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM: -#include "ofp-util.def" - OVS_NOT_REACHED(); - -#define OFPAT10_ACTION(ENUM, STRUCT, NAME) \ - case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf); -#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \ - case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf); -#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \ - case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf); -#include "ofp-util.def" - } - OVS_NOT_REACHED(); -} - -#define OFPAT10_ACTION(ENUM, STRUCT, NAME) \ - void \ - ofputil_init_##ENUM(struct STRUCT *s) \ - { \ - memset(s, 0, sizeof *s); \ - s->type = htons(ENUM); \ - s->len = htons(sizeof *s); \ - } \ - \ - struct STRUCT * \ - ofputil_put_##ENUM(struct ofpbuf *buf) \ - { \ - struct STRUCT *s = ofpbuf_put_uninit(buf, sizeof *s); \ - ofputil_init_##ENUM(s); \ - return s; \ - } -#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \ - OFPAT10_ACTION(ENUM, STRUCT, NAME) -#define OFPAT13_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \ - OFPAT10_ACTION(ENUM, STRUCT, NAME) -#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \ - void \ - ofputil_init_##ENUM(struct STRUCT *s) \ - { \ - memset(s, 0, sizeof *s); \ - s->type = htons(OFPAT10_VENDOR); \ - s->len = htons(sizeof *s); \ - s->vendor = htonl(NX_VENDOR_ID); \ - s->subtype = htons(ENUM); \ - } \ - \ - struct STRUCT * \ - ofputil_put_##ENUM(struct ofpbuf *buf) \ - { \ - struct STRUCT *s = ofpbuf_put_uninit(buf, sizeof *s); \ - ofputil_init_##ENUM(s); \ - return s; \ - } -#include "ofp-util.def" - static void ofputil_normalize_match__(struct match *match, bool may_log) { diff --git a/lib/ofp-util.def b/lib/ofp-util.def deleted file mode 100644 index 44decaeff..000000000 --- a/lib/ofp-util.def +++ /dev/null @@ -1,104 +0,0 @@ -/* -*- c -*- */ - -#ifndef OFPAT10_ACTION -#define OFPAT10_ACTION(ENUM, STRUCT, NAME) -#endif -OFPAT10_ACTION(OFPAT10_OUTPUT, ofp10_action_output, "output") -OFPAT10_ACTION(OFPAT10_SET_VLAN_VID, ofp_action_vlan_vid, "mod_vlan_vid") -OFPAT10_ACTION(OFPAT10_SET_VLAN_PCP, ofp_action_vlan_pcp, "mod_vlan_pcp") -OFPAT10_ACTION(OFPAT10_STRIP_VLAN, ofp_action_header, "strip_vlan") -OFPAT10_ACTION(OFPAT10_SET_DL_SRC, ofp_action_dl_addr, "mod_dl_src") -OFPAT10_ACTION(OFPAT10_SET_DL_DST, ofp_action_dl_addr, "mod_dl_dst") -OFPAT10_ACTION(OFPAT10_SET_NW_SRC, ofp_action_nw_addr, "mod_nw_src") -OFPAT10_ACTION(OFPAT10_SET_NW_DST, ofp_action_nw_addr, "mod_nw_dst") -OFPAT10_ACTION(OFPAT10_SET_NW_TOS, ofp_action_nw_tos, "mod_nw_tos") -OFPAT10_ACTION(OFPAT10_SET_TP_SRC, ofp_action_tp_port, "mod_tp_src") -OFPAT10_ACTION(OFPAT10_SET_TP_DST, ofp_action_tp_port, "mod_tp_dst") -OFPAT10_ACTION(OFPAT10_ENQUEUE, ofp10_action_enqueue, "enqueue") - -#ifndef OFPAT11_ACTION -#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) -#endif -OFPAT11_ACTION(OFPAT11_OUTPUT, ofp11_action_output, 0, "output") -OFPAT11_ACTION(OFPAT11_SET_VLAN_VID, ofp_action_vlan_vid, 0, "set_vlan_vid") -OFPAT11_ACTION(OFPAT11_SET_VLAN_PCP, ofp_action_vlan_pcp, 0, "set_vlan_pcp") -OFPAT11_ACTION(OFPAT11_SET_DL_SRC, ofp_action_dl_addr, 0, "mod_dl_src") -OFPAT11_ACTION(OFPAT11_SET_DL_DST, ofp_action_dl_addr, 0, "mod_dl_dst") -OFPAT11_ACTION(OFPAT11_SET_NW_SRC, ofp_action_nw_addr, 0, "mod_nw_src") -OFPAT11_ACTION(OFPAT11_SET_NW_DST, ofp_action_nw_addr, 0, "mod_nw_dst") -OFPAT11_ACTION(OFPAT11_SET_NW_TOS, ofp_action_nw_tos, 0, "mod_nw_tos") -OFPAT11_ACTION(OFPAT11_SET_NW_ECN, ofp11_action_nw_ecn, 0, "mod_nw_ecn") -OFPAT11_ACTION(OFPAT11_SET_TP_SRC, ofp_action_tp_port, 0, "mod_tp_src") -OFPAT11_ACTION(OFPAT11_SET_TP_DST, ofp_action_tp_port, 0, "mod_tp_dst") -OFPAT11_ACTION(OFPAT11_SET_MPLS_LABEL, ofp11_action_mpls_label, 0, "set_mpls_label") -OFPAT11_ACTION(OFPAT11_SET_MPLS_TC, ofp11_action_mpls_tc, 0, "set_mpls_tc") -OFPAT11_ACTION(OFPAT11_SET_MPLS_TTL, ofp11_action_mpls_ttl, 0, "set_mpls_ttl") -OFPAT11_ACTION(OFPAT11_DEC_MPLS_TTL, ofp_action_header, 0, "dec_mpls_ttl") -OFPAT11_ACTION(OFPAT11_PUSH_VLAN, ofp11_action_push, 0, "push_vlan") -OFPAT11_ACTION(OFPAT11_POP_VLAN, ofp_action_header, 0, "pop_vlan") -OFPAT11_ACTION(OFPAT11_PUSH_MPLS, ofp11_action_push, 0, "push_mpls") -OFPAT11_ACTION(OFPAT11_POP_MPLS, ofp11_action_pop_mpls, 0, "pop_mpls") -OFPAT11_ACTION(OFPAT11_SET_QUEUE, ofp11_action_set_queue, 0, "set_queue") -OFPAT11_ACTION(OFPAT11_SET_NW_TTL, ofp11_action_nw_ttl, 0, "mod_nw_ttl") -OFPAT11_ACTION(OFPAT11_DEC_NW_TTL, ofp_action_header, 0, NULL) -OFPAT11_ACTION(OFPAT12_SET_FIELD, ofp12_action_set_field, 1, "set_field") -OFPAT11_ACTION(OFPAT11_GROUP, ofp11_action_group, 0, "group") - -#ifndef OFPAT13_ACTION -#define OFPAT13_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) -#endif -OFPAT13_ACTION(OFPAT13_OUTPUT, ofp11_action_output, 0, "output") -OFPAT13_ACTION(OFPAT13_COPY_TTL_OUT, ofp_action_header, 0, "copy_ttl_out") -OFPAT13_ACTION(OFPAT13_COPY_TTL_IN, ofp_action_header, 0, "copy_ttl_in") -OFPAT13_ACTION(OFPAT13_SET_MPLS_TTL, ofp11_action_mpls_ttl, 0, "set_mpls_ttl") -OFPAT13_ACTION(OFPAT13_DEC_MPLS_TTL, ofp_action_header, 0, "dec_mpls_ttl") -OFPAT13_ACTION(OFPAT13_PUSH_VLAN, ofp11_action_push, 0, "push_vlan") -OFPAT13_ACTION(OFPAT13_POP_VLAN, ofp_action_header, 0, "pop_vlan") -OFPAT13_ACTION(OFPAT13_PUSH_MPLS, ofp11_action_push, 0, "push_mpls") -OFPAT13_ACTION(OFPAT13_POP_MPLS, ofp11_action_pop_mpls, 0, "pop_mpls") -OFPAT13_ACTION(OFPAT13_SET_QUEUE, ofp11_action_set_queue, 0, "set_queue") -OFPAT13_ACTION(OFPAT13_GROUP, ofp11_action_group, 0, "group") -OFPAT13_ACTION(OFPAT13_SET_NW_TTL, ofp11_action_nw_ttl, 0, "set_nw_ttl") -OFPAT13_ACTION(OFPAT13_DEC_NW_TTL, ofp_action_header, 0, "dec_nw_ttl") -OFPAT13_ACTION(OFPAT13_SET_FIELD, ofp12_action_set_field, 1, "set_field") -OFPAT13_ACTION(OFPAT13_PUSH_PBB, ofp11_action_push, 0, "push_pbb") -OFPAT13_ACTION(OFPAT13_POP_PBB, ofp_action_header, 0, "pop_pbb") - -#ifndef NXAST_ACTION -#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) -#endif -NXAST_ACTION(NXAST_RESUBMIT, nx_action_resubmit, 0, "resubmit") -NXAST_ACTION(NXAST_SET_TUNNEL, nx_action_set_tunnel, 0, "set_tunnel") -NXAST_ACTION(NXAST_SET_QUEUE, nx_action_set_queue, 0, "set_queue") -NXAST_ACTION(NXAST_POP_QUEUE, nx_action_pop_queue, 0, "pop_queue") -NXAST_ACTION(NXAST_REG_MOVE, nx_action_reg_move, 0, "move") -NXAST_ACTION(NXAST_REG_LOAD, nx_action_reg_load, 0, "load") -NXAST_ACTION(NXAST_STACK_PUSH, nx_action_stack, 0, "push") -NXAST_ACTION(NXAST_STACK_POP, nx_action_stack, 0, "pop") -NXAST_ACTION(NXAST_NOTE, nx_action_note, 1, "note") -NXAST_ACTION(NXAST_SET_TUNNEL64, nx_action_set_tunnel64, 0, "set_tunnel64") -NXAST_ACTION(NXAST_MULTIPATH, nx_action_multipath, 0, "multipath") -NXAST_ACTION(NXAST_BUNDLE, nx_action_bundle, 1, "bundle") -NXAST_ACTION(NXAST_BUNDLE_LOAD, nx_action_bundle, 1, "bundle_load") -NXAST_ACTION(NXAST_RESUBMIT_TABLE, nx_action_resubmit, 0, NULL) -NXAST_ACTION(NXAST_OUTPUT_REG, nx_action_output_reg, 0, NULL) -NXAST_ACTION(NXAST_LEARN, nx_action_learn, 1, "learn") -NXAST_ACTION(NXAST_EXIT, nx_action_header, 0, "exit") -NXAST_ACTION(NXAST_DEC_TTL, nx_action_header, 0, "dec_ttl") -NXAST_ACTION(NXAST_FIN_TIMEOUT, nx_action_fin_timeout, 0, "fin_timeout") -NXAST_ACTION(NXAST_CONTROLLER, nx_action_controller, 0, "controller") -NXAST_ACTION(NXAST_DEC_TTL_CNT_IDS, nx_action_cnt_ids, 1, NULL) -NXAST_ACTION(NXAST_WRITE_METADATA, nx_action_write_metadata, 0, - "write_metadata") -NXAST_ACTION(NXAST_SET_MPLS_LABEL, nx_action_mpls_label, 0, "set_mpls_label") -NXAST_ACTION(NXAST_SET_MPLS_TC, nx_action_mpls_tc, 0, "set_mpls_tc") -NXAST_ACTION(NXAST_SET_MPLS_TTL, nx_action_mpls_ttl, 0, "set_mpls_ttl") -NXAST_ACTION(NXAST_DEC_MPLS_TTL, nx_action_header, 0, "dec_mpls_ttl") -NXAST_ACTION(NXAST_PUSH_MPLS, nx_action_push_mpls, 0, "push_mpls") -NXAST_ACTION(NXAST_POP_MPLS, nx_action_pop_mpls, 0, "pop_mpls") -NXAST_ACTION(NXAST_SAMPLE, nx_action_sample, 0, "sample") - -#undef OFPAT10_ACTION -#undef OFPAT11_ACTION -#undef OFPAT13_ACTION -#undef NXAST_ACTION diff --git a/lib/ofp-util.h b/lib/ofp-util.h index 1c43cdfe9..63acb18b0 100644 --- a/lib/ofp-util.h +++ b/lib/ofp-util.h @@ -882,99 +882,6 @@ bool ofputil_frag_handling_from_string(const char *, enum ofp_config_flags *); /* Actions. */ -/* The type of an action. - * - * For each implemented OFPAT10_* and NXAST_* action type, there is a - * corresponding constant prefixed with OFPUTIL_, e.g.: - * - * OFPUTIL_OFPAT10_OUTPUT - * OFPUTIL_OFPAT10_SET_VLAN_VID - * OFPUTIL_OFPAT10_SET_VLAN_PCP - * OFPUTIL_OFPAT10_STRIP_VLAN - * OFPUTIL_OFPAT10_SET_DL_SRC - * OFPUTIL_OFPAT10_SET_DL_DST - * OFPUTIL_OFPAT10_SET_NW_SRC - * OFPUTIL_OFPAT10_SET_NW_DST - * OFPUTIL_OFPAT10_SET_NW_TOS - * OFPUTIL_OFPAT10_SET_TP_SRC - * OFPUTIL_OFPAT10_SET_TP_DST - * OFPUTIL_OFPAT10_ENQUEUE - * OFPUTIL_NXAST_RESUBMIT - * OFPUTIL_NXAST_SET_TUNNEL - * OFPUTIL_NXAST_SET_METADATA - * OFPUTIL_NXAST_SET_QUEUE - * OFPUTIL_NXAST_POP_QUEUE - * OFPUTIL_NXAST_REG_MOVE - * OFPUTIL_NXAST_REG_LOAD - * OFPUTIL_NXAST_NOTE - * OFPUTIL_NXAST_SET_TUNNEL64 - * OFPUTIL_NXAST_MULTIPATH - * OFPUTIL_NXAST_BUNDLE - * OFPUTIL_NXAST_BUNDLE_LOAD - * OFPUTIL_NXAST_RESUBMIT_TABLE - * OFPUTIL_NXAST_OUTPUT_REG - * OFPUTIL_NXAST_LEARN - * OFPUTIL_NXAST_DEC_TTL - * OFPUTIL_NXAST_FIN_TIMEOUT - * - * (The above list helps developers who want to "grep" for these definitions.) - */ -enum OVS_PACKED_ENUM ofputil_action_code { - OFPUTIL_ACTION_INVALID, -#define OFPAT10_ACTION(ENUM, STRUCT, NAME) OFPUTIL_##ENUM, -#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) OFPUTIL_##ENUM, -#define OFPAT13_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) OFPUTIL_##ENUM, -#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) OFPUTIL_##ENUM, -#include "ofp-util.def" -}; - -/* The number of values of "enum ofputil_action_code". */ -enum { -#define OFPAT10_ACTION(ENUM, STRUCT, NAME) + 1 -#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) + 1 -#define OFPAT13_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) + 1 -#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) + 1 - OFPUTIL_N_ACTIONS = 1 -#include "ofp-util.def" -}; - -int ofputil_action_code_from_name(const char *); -const char * ofputil_action_name_from_code(enum ofputil_action_code code); -enum ofputil_action_code ofputil_action_code_from_ofp13_action( - enum ofp13_action_type type); - -void *ofputil_put_action(enum ofputil_action_code, struct ofpbuf *buf); - -/* For each OpenFlow action <ENUM> that has a corresponding action structure - * struct <STRUCT>, this defines two functions: - * - * void ofputil_init_<ENUM>(struct <STRUCT> *action); - * - * Initializes the parts of 'action' that identify it as having type <ENUM> - * and length 'sizeof *action' and zeros the rest. For actions that have - * variable length, the length used and cleared is that of struct <STRUCT>. - * - * struct <STRUCT> *ofputil_put_<ENUM>(struct ofpbuf *buf); - * - * Appends a new 'action', of length 'sizeof(struct <STRUCT>)', to 'buf', - * initializes it with ofputil_init_<ENUM>(), and returns it. - */ -#define OFPAT10_ACTION(ENUM, STRUCT, NAME) \ - void ofputil_init_##ENUM(struct STRUCT *); \ - struct STRUCT *ofputil_put_##ENUM(struct ofpbuf *); -#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \ - void ofputil_init_##ENUM(struct STRUCT *); \ - struct STRUCT *ofputil_put_##ENUM(struct ofpbuf *); -#define OFPAT13_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \ - void ofputil_init_##ENUM(struct STRUCT *); \ - struct STRUCT *ofputil_put_##ENUM(struct ofpbuf *); -#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \ - void ofputil_init_##ENUM(struct STRUCT *); \ - struct STRUCT *ofputil_put_##ENUM(struct ofpbuf *); -#include "ofp-util.def" - -#define OFP_ACTION_ALIGN 8 /* Alignment of ofp_actions. */ - bool action_outputs_to_port(const union ofp_action *, ovs_be16 port); enum ofperr ofputil_pull_actions(struct ofpbuf *, unsigned int actions_len, diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index a8a417d6c..78ea18425 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -1250,7 +1250,6 @@ add_internal_flows(struct ofproto_dpif *ofproto) * (priority=2), recirc=0, actions=resubmit(, 0) */ resubmit = ofpact_put_RESUBMIT(&ofpacts); - resubmit->ofpact.compat = 0; resubmit->in_port = OFPP_IN_PORT; resubmit->table_id = 0; @@ -4291,7 +4290,7 @@ ofproto_unixctl_trace_actions(struct unixctl_conn *conn, int argc, ofpbuf_init(&ofpacts, 0); /* Parse actions. */ - error = parse_ofpacts(argv[--argc], &ofpacts, &usable_protocols); + error = ofpacts_parse_actions(argv[--argc], &ofpacts, &usable_protocols); if (error) { unixctl_command_reply_error(conn, error); free(error); diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index cc88bdac6..ec7337766 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -3195,7 +3195,7 @@ handle_table_features_request(struct ofconn *ofconn, ofpraw_pull_assert(&msg); if (ofpbuf_size(&msg) || ofpmp_more(request)) { return OFPERR_OFPTFFC_EPERM; - } + } query_tables(ofproto, &features, NULL); diff --git a/tests/ofp-actions.at b/tests/ofp-actions.at index 4090ca3f9..7dacd7a05 100644 --- a/tests/ofp-actions.at +++ b/tests/ofp-actions.at @@ -275,10 +275,6 @@ ffff 0048 00002320 0010 000a 0014 0050 123456789abcdef0 0000 02 00 0002 0004 dnl # actions=exit ffff 0010 00002320 0011 000000000000 -dnl NXAST_DEC_TTL -# actions=dec_ttl -ffff 0010 00002320 0012 000000000000 - dnl OpenFlow 1.1 OFPAT_DEC_TTL # actions=dec_ttl 0018 0008 00000000 diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index e63f5a79c..d2c75dcc1 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -2316,7 +2316,7 @@ AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0xb, n_packets=3, n_bytes=180, mpls,dl_src=50:55:55:55:55:55 actions=load:0x3e8->OXM_OF_MPLS_LABEL[[]],CONTROLLER:65535 cookie=0xc, n_packets=3, n_bytes=180, dl_src=70:77:77:77:77:77 actions=push_mpls:0x8848,load:0x3e8->OXM_OF_MPLS_LABEL[[]],load:0x7->OXM_OF_MPLS_TC[[]],CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=180, dl_src=60:66:66:66:00:08 actions=pop_mpls:0x0806,resubmit(1,1) - cookie=0xd, n_packets=3, n_bytes=186, dl_src=60:66:66:66:00:01 actions=pop_mpls:0x0800,dec_ttl(0),CONTROLLER:65535 + cookie=0xd, n_packets=3, n_bytes=186, dl_src=60:66:66:66:00:01 actions=pop_mpls:0x0800,dec_ttl,CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=186, dl_src=60:66:66:66:00:02 actions=pop_mpls:0x0800,load:0xa000001->NXM_OF_IP_DST[[]],CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=186, dl_src=60:66:66:66:00:03 actions=pop_mpls:0x0800,move:NXM_OF_IP_DST[[]]->NXM_OF_IP_SRC[[]],CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=186, dl_src=60:66:66:66:00:04 actions=pop_mpls:0x0800,push:NXM_OF_IP_DST[[]],pop:NXM_OF_IP_SRC[[]],CONTROLLER:65535 @@ -2328,7 +2328,7 @@ AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0xd, n_packets=3, n_bytes=186, dl_src=60:66:66:66:00:0b actions=pop_mpls:0x0800,mod_nw_src:10.0.0.1,CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=186, dl_src=60:66:66:66:04:00 actions=pop_mpls:0x0800,push_mpls:0x8847,CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=186, dl_src=60:66:66:66:04:01 actions=pop_mpls:0x0800,push_mpls:0x8848,dec_mpls_ttl,CONTROLLER:65535 - cookie=0xd, n_packets=3, n_bytes=186, dl_src=60:66:66:66:04:10 actions=pop_mpls:0x0800,dec_ttl(0),push_mpls:0x8848,dec_mpls_ttl,CONTROLLER:65535 + cookie=0xd, n_packets=3, n_bytes=186, dl_src=60:66:66:66:04:10 actions=pop_mpls:0x0800,dec_ttl,push_mpls:0x8848,dec_mpls_ttl,CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=186, dl_src=60:66:66:66:05:00 actions=push_mpls:0x8848,pop_mpls:0x8847,CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=186, dl_src=60:66:66:66:05:01 actions=push_mpls:0x8847,pop_mpls:0x8848,dec_mpls_ttl,CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=186, dl_src=60:66:66:66:05:10 actions=push_mpls:0x8848,dec_mpls_ttl,pop_mpls:0x8847,dec_mpls_ttl,CONTROLLER:65535 @@ -2337,8 +2337,8 @@ AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl cookie=0xd, n_packets=3, n_bytes=198, dl_src=60:66:66:66:01:01 actions=pop_mpls:0x8847,dec_mpls_ttl,CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=198, dl_src=60:66:66:66:01:02 actions=pop_mpls:0x8848,load:0x3->OXM_OF_MPLS_TC[[]],CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=198, dl_src=60:66:66:66:02:00 actions=pop_mpls:0x8847,pop_mpls:0x0800,CONTROLLER:65535 - cookie=0xd, n_packets=3, n_bytes=198, dl_src=60:66:66:66:02:01 actions=pop_mpls:0x8848,pop_mpls:0x0800,dec_ttl(0),CONTROLLER:65535 - cookie=0xd, n_packets=3, n_bytes=198, dl_src=60:66:66:66:02:10 actions=pop_mpls:0x8847,dec_mpls_ttl,pop_mpls:0x0800,dec_ttl(0),CONTROLLER:65535 + cookie=0xd, n_packets=3, n_bytes=198, dl_src=60:66:66:66:02:01 actions=pop_mpls:0x8848,pop_mpls:0x0800,dec_ttl,CONTROLLER:65535 + cookie=0xd, n_packets=3, n_bytes=198, dl_src=60:66:66:66:02:10 actions=pop_mpls:0x8847,dec_mpls_ttl,pop_mpls:0x0800,dec_ttl,CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=210, dl_src=60:66:66:66:03:00 actions=pop_mpls:0x8848,pop_mpls:0x8848,CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=210, dl_src=60:66:66:66:03:01 actions=pop_mpls:0x8847,pop_mpls:0x8847,dec_mpls_ttl,CONTROLLER:65535 cookie=0xd, n_packets=3, n_bytes=210, dl_src=60:66:66:66:03:10 actions=pop_mpls:0x8848,dec_mpls_ttl,pop_mpls:0x8848,dec_mpls_ttl,CONTROLLER:65535 diff --git a/tests/test-bundle.c b/tests/test-bundle.c index 9a6583967..42e428394 100644 --- a/tests/test-bundle.c +++ b/tests/test-bundle.c @@ -66,15 +66,19 @@ slave_enabled_cb(ofp_port_t slave_id, void *aux) } static struct ofpact_bundle * -parse_bundle_actions(char *actions) +parse_bundle_actions(const char *args) { + enum ofputil_protocol usable_protocols; struct ofpact_bundle *bundle; struct ofpbuf ofpacts; struct ofpact *action; + char *action_string; char *error; ofpbuf_init(&ofpacts, 0); - error = bundle_parse_load(actions, &ofpacts); + action_string = xasprintf("bundle_load(%s)", args); + error = ofpacts_parse_actions(action_string, &ofpacts, &usable_protocols); + free(action_string); if (error) { ovs_fatal(0, "%s", error); } diff --git a/tests/test-multipath.c b/tests/test-multipath.c index 50747d9b6..45b33c162 100644 --- a/tests/test-multipath.c +++ b/tests/test-multipath.c @@ -26,6 +26,7 @@ #include "flow.h" #include "ofp-actions.h" +#include "ofpbuf.h" #include "util.h" #include "ovstest.h" @@ -33,7 +34,10 @@ static void test_multipath_main(int argc, char *argv[]) { enum { MP_MAX_LINKS = 63 }; + enum ofputil_protocol usable_protocols; struct ofpact_multipath mp; + struct ofpbuf ofpacts; + char *action_string; bool ok = true; char *error; int n; @@ -44,11 +48,15 @@ test_multipath_main(int argc, char *argv[]) ovs_fatal(0, "usage: %s multipath_action", program_name); } - error = multipath_parse(&mp, argv[1]); + ofpbuf_init(&ofpacts, 0); + action_string = xasprintf("multipath(%s)", argv[1]); + error = ofpacts_parse_actions(action_string, &ofpacts, &usable_protocols); + free(action_string); if (error) { ovs_fatal(0, "%s", error); } + mp = *ofpact_get_MULTIPATH(ofpbuf_data(&ofpacts)); for (n = 1; n <= MP_MAX_LINKS; n++) { enum { N_FLOWS = 65536 }; double disruption, perfect, distribution; diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c index 7b4a006bb..e03b97397 100644 --- a/utilities/ovs-ofctl.c +++ b/utilities/ovs-ofctl.c @@ -1675,7 +1675,7 @@ ofctl_packet_out(int argc, char *argv[]) enum ofputil_protocol usable_protocols; /* XXX: Use in proto selection */ ofpbuf_init(&ofpacts, 64); - error = parse_ofpacts(argv[3], &ofpacts, &usable_protocols); + error = ofpacts_parse_actions(argv[3], &ofpacts, &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } |