diff --git a/build-aux/extract-ofp-actions b/build-aux/extract-ofp-actions new file mode 100755 index 000000000..28ecfeabc --- /dev/null +++ b/build-aux/extract-ofp-actions @@ -0,0 +1,376 @@ +#! /usr/bin/python + +import sys +import os.path +import re + +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, "align": 1, "ntoh": None, "hton": None} +types['ovs_be16'] = {"size": 2, "align": 2, "ntoh": "ntohs", "hton": "htons"} +types['ovs_be32'] = {"size": 4, "align": 4, "ntoh": "ntohl", "hton": "htonl"} +types['ovs_be64'] = {"size": 8, "align": 8, "ntoh": "ntohll", "hton": "htonll"} +types['uint16_t'] = {"size": 2, "align": 2, "ntoh": None, "hton": None} +types['uint32_t'] = {"size": 4, "align": 4, "ntoh": None, "hton": None} +types['uint64_t'] = {"size": 8, "align": 8, "ntoh": None, "hton": None} + +line = "" + +arg_structs = set() + +def round_up(x, y): + return (x + (y - 1)) / y * y + +def open_file(fn): + global file_name + global input_file + global line_number + file_name = fn + input_file = open(file_name) + line_number = 0 + +def get_line(): + global input_file + global line + global line_number + line = input_file.readline() + line_number += 1 + if line == "": + fatal("unexpected end of input") + return line + +n_errors = 0 +def error(msg): + global n_errors + sys.stderr.write("%s:%d: %s\n" % (file_name, line_number, msg)) + n_errors += 1 + +def fatal(msg): + error(msg) + sys.exit(1) + +def usage(): + argv0 = os.path.basename(sys.argv[0]) + print ('''\ +%(argv0)s, for extracting OpenFlow action data +usage: %(argv0)s OFP_ACTIONS.C [--prototypes | --definitions] + +This program reads ofp-actions.c to obtain information about OpenFlow +actions. With --prototypes, it outputs on stdout a set of prototypes to +#include early in ofp-actions.c. With --definitions, it outputs on stdout +a set of definitions to #include late in ofp-actions.c + +OFP_ACTIONS.C should point to lib/ofp-actions.c.\ +''' % {"argv0": argv0}) + sys.exit(0) + +def extract_ofp_actions(fn, definitions): + error_types = {} + + comments = [] + names = [] + domain = {} + for code, size in vendor_map.values(): + domain[code] = {} + enums = {} + + n_errors = 0 + + open_file(fn) + + while True: + get_line() + if re.match('enum ofp_raw_action_type {', line): + break + + while True: + get_line() + 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('*/'): + get_line() + 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) + + get_line() + 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)) + deprecation = 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_]: + v = domain[vendor][type_][version] + msg = "%#x,%d in OF%s means both %s and %s" % ( + vendor, type_, version_reverse_map[version], + v["enum"], enum) + error("%s: %s." % (dst, msg)) + sys.stderr.write("%s:%d: %s: Here is the location " + "of the previous definition.\n" + % (v["file_name"], v["line_number"], + 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]['align'] + arg_len = types[base_argtype]['size'] + arg_ofs = round_up(header_len, arg_align) + min_length = round_up(arg_ofs + arg_len, + OFP_ACTION_ALIGN) + elif base_argtype == 'void': + min_length = round_up(header_len, OFP_ACTION_ALIGN) + arg_len = 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_len = 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": enum, # 0 + "deprecation": deprecation, # 1 + "file_name": file_name, # 2 + "line_number": line_number, # 3 + "min_length": min_length, # 4 + "max_length": max_length, # 5 + "arg_ofs": arg_ofs, # 6 + "arg_len": arg_len, # 7 + "base_argtype": base_argtype, # 8 + "version": version, # 9 + "type": type_} # 10 + domain[vendor][type_][version] = info + + enums.setdefault(enum, []) + enums[enum].append(info) + + input_file.close() + + if n_errors: + sys.exit(1) + + print """\ +/* Generated automatically; do not modify! -*- buffer-read-only: t -*- */ +""" + + if definitions: + 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["enum"] + print " HMAP_NODE_NULL_INITIALIZER," + print " HMAP_NODE_NULL_INITIALIZER," + print " %s," % d["min_length"] + print " %s," % d["max_length"] + print " %s," % d["arg_ofs"] + print " %s," % d["arg_len"] + print " \"%s\"," % re.sub('_RAW[0-9]*', '', d["enum"], 1) + if d["deprecation"]: + print " \"%s\"," % re.sub(r'(["\\])', r'\\\1', d["deprecation"]) + else: + print " NULL," + print " }," + print "};"; + + for versions in enums.values(): + need_ofp_version = False + for v in versions: + assert v["arg_len"] == versions[0]["arg_len"] + assert v["base_argtype"] == versions[0]["base_argtype"] + if (v["min_length"] != versions[0]["min_length"] or + v["arg_ofs"] != versions[0]["arg_ofs"] or + v["type"] != versions[0]["type"]): + need_ofp_version = True + base_argtype = versions[0]["base_argtype"] + + decl = "static inline " + if base_argtype.startswith('struct'): + decl += "%s *" %base_argtype + else: + decl += "void" + decl += "\nput_%s(struct ofpbuf *openflow" % versions[0]["enum"].replace('_RAW', '', 1) + if need_ofp_version: + decl += ", enum ofp_version version" + if base_argtype != 'void' and not base_argtype.startswith('struct'): + decl += ", %s arg" % base_argtype + decl += ")" + if definitions: + decl += "{\n" + decl += " " + if base_argtype.startswith('struct'): + decl += "return " + decl += "ofpact_put_raw(openflow, " + if need_ofp_version: + decl += "version" + else: + decl += "%s" % versions[0]["version"] + decl += ", %s, " % versions[0]["enum"] + if base_argtype.startswith('struct') or base_argtype == 'void': + decl += "0" + else: + ntoh = types[base_argtype]['ntoh'] + if ntoh: + decl += "%s(arg)" % ntoh + else: + decl += "arg" + decl += ");\n" + decl += "}" + else: + decl += ";" + print decl + print + + if definitions: + print """\ +static enum ofperr +ofpact_decode(const struct ofp_action_header *a, enum ofp_raw_action_type raw, + uint64_t arg, struct ofpbuf *out) +{ + switch (raw) {\ +""" + for versions in enums.values(): + enum = versions[0]["enum"] + print " case %s:" % enum + base_argtype = versions[0]["base_argtype"] + if base_argtype == 'void': + print " return decode_%s(out);" % enum + else: + if base_argtype.startswith('struct'): + arg = "ALIGNED_CAST(const %s *, a)" % base_argtype + else: + hton = types[base_argtype]['hton'] + if hton: + arg = "%s(arg)" % hton + else: + arg = "arg" + print " return decode_%s(%s, out);" % (enum, arg) + print + print """\ + default: + OVS_NOT_REACHED(); + } +}\ +""" + else: + for versions in enums.values(): + enum = versions[0]["enum"] + prototype = "static enum ofperr decode_%s(" % enum + base_argtype = versions[0]["base_argtype"] + if base_argtype != 'void': + if base_argtype.startswith('struct'): + prototype += "const %s *, " % base_argtype + else: + prototype += "%s, " % base_argtype + prototype += "struct ofpbuf *);" + print prototype + + print """ +static enum ofperr ofpact_decode(const struct ofp_action_header *, + enum ofp_raw_action_type raw, + uint64_t arg, struct ofpbuf *out); +""" + +if __name__ == '__main__': + if '--help' in sys.argv: + usage() + elif len(sys.argv) != 3: + sys.stderr.write("exactly two arguments required; " + "use --help for help\n") + sys.exit(1) + elif sys.argv[2] == '--prototypes': + extract_ofp_actions(sys.argv[1], False) + elif sys.argv[2] == '--definitions': + extract_ofp_actions(sys.argv[1], True) + else: + sys.stderr.write("invalid arguments; use --help for help\n") + sys.exit(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 5e618c9ef..f87c5cfb3 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 { @@ -340,97 +308,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 c7ba6fda6..58be0b57b 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 39de5b3bd..caae674fd 100644 --- a/include/openflow/openflow-1.3.h +++ b/include/openflow/openflow-1.3.h @@ -99,27 +99,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 75cf41342..05d53680a 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 663011443..f4426f2f7 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -139,7 +139,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 \ @@ -434,6 +433,13 @@ lib/dirs.c: lib/dirs.c.in Makefile > lib/dirs.c.tmp mv lib/dirs.c.tmp lib/dirs.c +lib/ofp-actions.inc1: $(srcdir)/build-aux/extract-ofp-actions lib/ofp-actions.c + $(run_python) $^ --prototypes > $@.tmp && mv $@.tmp $@ +lib/ofp-actions.inc2: $(srcdir)/build-aux/extract-ofp-actions lib/ofp-actions.c + $(run_python) $^ --definitions > $@.tmp && mv $@.tmp $@ +lib/ofp-actions.lo: lib/ofp-actions.inc1 lib/ofp-actions.inc2 +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..a514ebb64 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,34 +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. * 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..8983c6a09 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(). */ 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..1d3205f6f 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -1133,45 +1133,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 +1151,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 +1258,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 +1272,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..559ff05e7 100644 --- a/lib/nx-match.h +++ b/lib/nx-match.h @@ -67,21 +67,11 @@ char *nxm_parse_reg_load(struct ofpact_reg_load *, const char *) 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 +85,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 8dd4dddb5..c6aba5150 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -20,72 +20,333 @@ #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); - -/* Converting OpenFlow 1.0 to ofpacts. */ -union ofp_action { - ovs_be16 type; - 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 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; - struct nx_action_note note; - struct nx_action_multipath multipath; - struct nx_action_bundle bundle; - struct nx_action_output_reg output_reg; - 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; +struct ofp_action_header; + +/* Raw identifiers for OpenFlow actions. + * + * Decoding and encoding OpenFlow actions across multiple versions is difficult + * to do in a clean, consistent way. This enumeration lays out all of the + * forms of actions that Open vSwitch supports. + * + * The comments here must follow a stylized form because the + * "extract-ofp-actions" program parses them at build time to generate data + * tables. + * + * - The first part of each comment specifies the vendor, OpenFlow versions, + * and type for each protocol that supports the action: + * + * # The vendor is OF for standard OpenFlow actions, NX for Nicira + * extension actions. (Support for other vendors can be added, but + * it can't be done just based on a vendor ID definition alone + * because OpenFlow doesn't define a standard way to specify a + * subtype for vendor actions, so other vendors might do it different + * from Nicira.) + * + * # The version can specify a specific OpenFlow version, a version + * range delimited by "-", or an open-ended range with "+". + * + * # The type, in parentheses, is the action type number (for standard + * OpenFlow actions) or subtype (for vendor extension actions). + * + * # Optionally one may add "is deprecated" followed by a + * human-readable reason in parentheses (which will be used in log + * messages), if a particular action should no longer be used. + * + * Multiple such specifications may be separated by commas. + * + * - The second part describes the action's wire format. It may be: + * + * # "struct ": The struct fully specifies the wire format. The + * action is exactly the size of the struct. (Thus, the struct must + * be an exact multiple of 8 bytes in size.) + * + * # "struct , ...": The struct specifies the beginning of the + * wire format. An instance of the action is either the struct's + * exact size, or a multiple of 8 bytes longer. + * + * # "uint_t" or "ovs_be": The action consists of a (standard or + * vendor extension) header, followed by 0 or more pad bytes to align + * to a multiple of bits, followed by an argument of the given + * type, followed by 0 or more pad bytes to bring the total action up + * to a multiple of 8 bytes. + * + * # "void": The action is just a (standard or vendor extension) + * header. + * + * - Optional additional text enclosed in square brackets is commentary for + * the human reader. + */ +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, + + /* 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, + +/* ## ------------------------- ## */ +/* ## 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+(6): struct nx_action_reg_move. */ + NXAST_RAW_REG_MOVE, + + /* 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, }; +/* OpenFlow actions are always a multiple of 8 bytes in length. */ +#define OFP_ACTION_ALIGN 8 + +/* Define a few functions for working with instructions. */ +#define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) \ + static inline const struct STRUCT * OVS_UNUSED \ + instruction_get_##ENUM(const struct ofp11_instruction *inst)\ + { \ + ovs_assert(inst->type == htons(ENUM)); \ + return ALIGNED_CAST(struct STRUCT *, inst); \ + } \ + \ + static inline void OVS_UNUSED \ + instruction_init_##ENUM(struct STRUCT *s) \ + { \ + memset(s, 0, sizeof *s); \ + s->type = htons(ENUM); \ + s->len = htons(sizeof *s); \ + } \ + \ + static inline struct STRUCT * OVS_UNUSED \ + instruction_put_##ENUM(struct ofpbuf *buf) \ + { \ + struct STRUCT *s = ofpbuf_put_uninit(buf, sizeof *s); \ + instruction_init_##ENUM(s); \ + return s; \ + } +OVS_INSTRUCTIONS +#undef DEFINE_INST + +static void ofpacts_update_instruction_actions(struct ofpbuf *openflow, + size_t ofs); + +static enum ofperr ofpacts_verify(const struct ofpact[], size_t ofpacts_len, + uint32_t allowed_ovsinsts); + +static void ofpact_put_set_field(struct ofpbuf *openflow, enum ofp_version, + enum mf_field_id, uint64_t value); + +static enum ofperr ofpact_pull_raw(struct ofpbuf *, enum ofp_version, + enum ofp_raw_action_type *, uint64_t *arg); +static void *ofpact_put_raw(struct ofpbuf *, enum ofp_version, + enum ofp_raw_action_type, 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.inc1" + +/* Output actions. */ + +/* 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); + static enum ofperr -output_from_openflow10(const struct ofp10_action_output *oao, - struct ofpbuf *out) +decode_OFPAT_RAW10_OUTPUT(const struct ofp10_action_output *oao, + struct ofpbuf *out) { struct ofpact_output *output; @@ -97,8 +358,260 @@ output_from_openflow10(const struct ofp10_action_output *oao, } static enum ofperr -enqueue_from_openflow10(const struct ofp10_action_enqueue *oae, - struct ofpbuf *out) +decode_OFPAT_RAW11_OUTPUT(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 void +encode_OUTPUT(const struct ofpact_output *output, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + if (ofp_version == OFP10_VERSION) { + struct ofp10_action_output *oao; + + oao = put_OFPAT10_OUTPUT(out); + oao->port = htons(ofp_to_u16(output->port)); + oao->max_len = htons(output->max_len); + } else { + struct ofp11_action_output *oao; + + oao = put_OFPAT11_OUTPUT(out); + oao->port = ofputil_port_to_ofp11(output->port); + oao->max_len = htons(output->max_len); + } +} + +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 void +format_OUTPUT(const struct ofpact_output *a, struct ds *s) +{ + if (ofp_to_u16(a->port) < ofp_to_u16(OFPP_MAX)) { + ds_put_format(s, "output:%"PRIu16, a->port); + } else { + ofputil_format_port(a->port, s); + if (a->port == OFPP_CONTROLLER) { + ds_put_format(s, ":%"PRIu16, a->max_len); + } + } +} + +/* Group actions. */ + +static enum ofperr +decode_OFPAT_RAW11_GROUP(uint32_t group_id, struct ofpbuf *out) +{ + ofpact_put_GROUP(out)->group_id = group_id; + return 0; +} + +static void +encode_GROUP(const struct ofpact_group *group, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + if (ofp_version == OFP10_VERSION) { + /* XXX */ + } else { + put_OFPAT11_GROUP(out, group->group_id); + } +} + +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 void +format_GROUP(const struct ofpact_group *a, struct ds *s) +{ + ds_put_format(s, "group:%"PRIu32, a->group_id); +} + +/* 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); + +static enum ofperr +decode_NXAST_RAW_CONTROLLER(const struct nx_action_controller *nac, + struct ofpbuf *out) +{ + struct ofpact_controller *oc; + + oc = ofpact_put_CONTROLLER(out); + oc->max_len = ntohs(nac->max_len); + oc->controller_id = ntohs(nac->controller_id); + oc->reason = nac->reason; + return 0; +} + +static void +encode_CONTROLLER(const struct ofpact_controller *controller, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) +{ + struct nx_action_controller *nac; + + nac = put_NXAST_CONTROLLER(out); + nac->max_len = htons(controller->max_len); + nac->controller_id = htons(controller->controller_id); + nac->reason = controller->reason; +} + +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 void +format_CONTROLLER(const struct ofpact_controller *a, struct ds *s) +{ + if (a->reason == OFPR_ACTION && a->controller_id == 0) { + ds_put_format(s, "CONTROLLER:%"PRIu16, a->max_len); + } else { + enum ofp_packet_in_reason reason = a->reason; + + ds_put_cstr(s, "controller("); + if (reason != OFPR_ACTION) { + char reasonbuf[OFPUTIL_PACKET_IN_REASON_BUFSIZE]; + + ds_put_format(s, "reason=%s,", + ofputil_packet_in_reason_to_string( + reason, reasonbuf, sizeof reasonbuf)); + } + if (a->max_len != UINT16_MAX) { + ds_put_format(s, "max_len=%"PRIu16",", a->max_len); + } + if (a->controller_id != 0) { + ds_put_format(s, "id=%"PRIu16",", a->controller_id); + } + ds_chomp(s, ','); + ds_put_char(s, ')'); + } +} + +/* Enqueue action. */ +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); + +static enum ofperr +decode_OFPAT_RAW10_ENQUEUE(const struct ofp10_action_enqueue *oae, + struct ofpbuf *out) { struct ofpact_enqueue *enqueue; @@ -114,37 +627,82 @@ enqueue_from_openflow10(const struct ofp10_action_enqueue *oae, } static void -resubmit_from_openflow(const struct nx_action_resubmit *nar, - struct ofpbuf *out) +encode_ENQUEUE(const struct ofpact_enqueue *enqueue, + enum ofp_version ofp_version, struct ofpbuf *out) { - struct ofpact_resubmit *resubmit; + if (ofp_version == OFP10_VERSION) { + struct ofp10_action_enqueue *oae; - resubmit = ofpact_put_RESUBMIT(out); - resubmit->ofpact.compat = OFPUTIL_NXAST_RESUBMIT; - resubmit->in_port = u16_to_ofp(ntohs(nar->in_port)); - resubmit->table_id = 0xff; + oae = put_OFPAT10_ENQUEUE(out); + oae->port = htons(ofp_to_u16(enqueue->port)); + oae->queue_id = htonl(enqueue->queue); + } else { + /* XXX */ + } } -static enum ofperr -resubmit_table_from_openflow(const struct nx_action_resubmit *nar, - struct ofpbuf *out) +static char * WARN_UNUSED_RESULT +parse_ENQUEUE(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) { - struct ofpact_resubmit *resubmit; + char *sp = NULL; + char *port = strtok_r(arg, ":q,", &sp); + char *queue = strtok_r(NULL, "", &sp); + struct ofpact_enqueue *enqueue; - if (nar->pad[0] || nar->pad[1] || nar->pad[2]) { - return OFPERR_OFPBAC_BAD_ARGUMENT; + if (port == NULL || queue == NULL) { + return xstrdup("\"enqueue\" syntax is \"enqueue:PORT:QUEUE\" or " + "\"enqueue(PORT,QUEUE)\""); } - resubmit = ofpact_put_RESUBMIT(out); - resubmit->ofpact.compat = OFPUTIL_NXAST_RESUBMIT_TABLE; - resubmit->in_port = u16_to_ofp(ntohs(nar->in_port)); - resubmit->table_id = nar->table; - return 0; + 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 void +format_ENQUEUE(const struct ofpact_enqueue *a, struct ds *s) +{ + ds_put_format(s, "enqueue:"); + ofputil_format_port(a->port, s); + ds_put_format(s, ":%"PRIu32, a->queue); +} + +/* 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); + static enum ofperr -output_reg_from_openflow(const struct nx_action_output_reg *naor, - struct ofpbuf *out) +decode_NXAST_RAW_OUTPUT_REG(const struct nx_action_output_reg *naor, + struct ofpbuf *out) { struct ofpact_output_reg *output_reg; @@ -162,628 +720,1179 @@ output_reg_from_openflow(const struct nx_action_output_reg *naor, } static void -fin_timeout_from_openflow(const struct nx_action_fin_timeout *naft, - struct ofpbuf *out) -{ - struct ofpact_fin_timeout *oft; - - oft = ofpact_put_FIN_TIMEOUT(out); - oft->fin_idle_timeout = ntohs(naft->fin_idle_timeout); - oft->fin_hard_timeout = ntohs(naft->fin_hard_timeout); -} - -static void -controller_from_openflow(const struct nx_action_controller *nac, - struct ofpbuf *out) -{ - struct ofpact_controller *oc; - - oc = ofpact_put_CONTROLLER(out); - oc->max_len = ntohs(nac->max_len); - oc->controller_id = ntohs(nac->controller_id); - oc->reason = nac->reason; -} - -static enum ofperr -metadata_from_nxast(const struct nx_action_write_metadata *nawm, - struct ofpbuf *out) -{ - struct ofpact_metadata *om; - - if (!is_all_zeros(nawm->zeros, sizeof nawm->zeros)) { - return OFPERR_NXBRC_MUST_BE_ZERO; - } - - om = ofpact_put_WRITE_METADATA(out); - om->metadata = nawm->metadata; - om->mask = nawm->mask; - - return 0; -} - -static void -note_from_openflow(const struct nx_action_note *nan, struct ofpbuf *out) -{ - struct ofpact_note *note; - unsigned int length; - - length = ntohs(nan->len) - offsetof(struct nx_action_note, note); - note = ofpact_put(out, OFPACT_NOTE, - offsetof(struct ofpact_note, data) + length); - note->length = length; - memcpy(note->data, nan->note, length); -} - -static enum ofperr -dec_ttl_from_openflow(struct ofpbuf *out, enum ofputil_action_code compat) -{ - 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; - ofpact_update_len(out, &ids->ofpact); - return error; -} - -static enum ofperr -dec_ttl_cnt_ids_from_openflow(const struct nx_action_cnt_ids *nac_ids, - struct ofpbuf *out) -{ - struct ofpact_cnt_ids *ids; - size_t ids_size; - int i; - - ids = ofpact_put_DEC_TTL(out); - ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL_CNT_IDS; - ids->n_controllers = ntohs(nac_ids->n_controllers); - ids_size = ntohs(nac_ids->len) - sizeof *nac_ids; - - if (!is_all_zeros(nac_ids->zeros, sizeof nac_ids->zeros)) { - return OFPERR_NXBRC_MUST_BE_ZERO; - } - - if (ids_size < ids->n_controllers * sizeof(ovs_be16)) { - VLOG_WARN_RL(&rl, "Nicira action dec_ttl_cnt_ids only has %"PRIuSIZE" bytes " - "allocated for controller ids. %"PRIuSIZE" bytes are required for " - "%"PRIu16" controllers.", ids_size, - ids->n_controllers * sizeof(ovs_be16), ids->n_controllers); - return OFPERR_OFPBAC_BAD_LEN; - } - - for (i = 0; i < ids->n_controllers; i++) { - uint16_t id = ntohs(((ovs_be16 *)(nac_ids + 1))[i]); - ofpbuf_put(out, &id, sizeof id); - ids = out->frame; - } - - ofpact_update_len(out, &ids->ofpact); - - return 0; -} - -static enum ofperr -sample_from_openflow(const struct nx_action_sample *nas, - struct ofpbuf *out) -{ - struct ofpact_sample *sample; - - sample = ofpact_put_SAMPLE(out); - sample->probability = ntohs(nas->probability); - sample->collector_set_id = ntohl(nas->collector_set_id); - sample->obs_domain_id = ntohl(nas->obs_domain_id); - sample->obs_point_id = ntohl(nas->obs_point_id); - - if (sample->probability == 0) { - return OFPERR_OFPBAC_BAD_ARGUMENT; - } - - return 0; -} - -static enum ofperr -push_mpls_from_openflow(ovs_be16 ethertype, struct ofpbuf *out) -{ - struct ofpact_push_mpls *oam; - - if (!eth_type_mpls(ethertype)) { - return OFPERR_OFPBAC_BAD_ARGUMENT; - } - oam = ofpact_put_PUSH_MPLS(out); - oam->ethertype = ethertype; - - return 0; -} - -static enum ofperr -decode_nxast_action(const union ofp_action *a, enum ofputil_action_code *code) -{ - const struct nx_action_header *nah = &a->nxa_header; - uint16_t len = ntohs(a->header.len); - - 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; - } - - 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" - - case CONSTANT_HTONS(NXAST_SNAT__OBSOLETE): - case CONSTANT_HTONS(NXAST_DROP_SPOOFED_ARP__OBSOLETE): - default: - return OFPERR_OFPBAC_BAD_TYPE; - } -} - -/* 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) -{ - 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" - - default: - return OFPERR_OFPBAC_BAD_TYPE; - } -} - -static enum ofperr -ofpact_from_nxast(const union ofp_action *a, enum ofputil_action_code code, +encode_OUTPUT_REG(const struct ofpact_output_reg *output_reg, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { - struct ofpact_tunnel *tunnel; - enum ofperr error = 0; + struct nx_action_output_reg *naor = put_NXAST_OUTPUT_REG(out); - 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(); + naor->ofs_nbits = nxm_encode_ofs_nbits(output_reg->src.ofs, + output_reg->src.n_bits); + naor->src = htonl(output_reg->src.field->nxm_header); + naor->max_len = htons(output_reg->max_len); +} - case OFPUTIL_NXAST_RESUBMIT: - resubmit_from_openflow(&a->resubmit, out); - break; +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); +} - 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; +static void +format_OUTPUT_REG(const struct ofpact_output_reg *a, struct ds *s) +{ + ds_put_cstr(s, "output:"); + mf_format_subfield(&a->src, s); +} + +/* 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. */ - case OFPUTIL_NXAST_WRITE_METADATA: - error = metadata_from_nxast(&a->write_metadata, out); - break; + /* Slave choice algorithm to apply to hash value. */ + ovs_be16 algorithm; /* One of NX_BD_ALG_*. */ - case OFPUTIL_NXAST_SET_QUEUE: - ofpact_put_SET_QUEUE(out)->queue_id = ntohl(a->set_queue.queue_id); - break; + /* What fields to hash and how. */ + ovs_be16 fields; /* One of NX_HASH_FIELDS_*. */ + ovs_be16 basis; /* Universal hash parameter. */ - case OFPUTIL_NXAST_POP_QUEUE: - ofpact_put_POP_QUEUE(out); - break; + ovs_be32 slave_type; /* NXM_OF_IN_PORT. */ + ovs_be16 n_slaves; /* Number of slaves. */ - case OFPUTIL_NXAST_REG_MOVE: - error = nxm_reg_move_from_openflow(&a->reg_move, out); - break; + ovs_be16 ofs_nbits; /* (ofs << 6) | (n_bits - 1). */ + ovs_be32 dst; /* Destination. */ - case OFPUTIL_NXAST_REG_LOAD: - error = nxm_reg_load_from_openflow(&a->reg_load, out); - break; + uint8_t zero[4]; /* Reserved. Must be zero. */ +}; +OFP_ASSERT(sizeof(struct nx_action_bundle) == 32); - case OFPUTIL_NXAST_STACK_PUSH: - error = nxm_stack_push_from_openflow(&a->stack, out); - break; +static enum ofperr +decode_bundle(bool load, 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_STACK_POP: - error = nxm_stack_pop_from_openflow(&a->stack, out); - break; + bundle = ofpact_put_BUNDLE(ofpacts); - case OFPUTIL_NXAST_NOTE: - note_from_openflow(&a->note, out); - break; + 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; - 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; - - case OFPUTIL_NXAST_MULTIPATH: - error = multipath_from_openflow(&a->multipath, - ofpact_put_MULTIPATH(out)); - break; - - case OFPUTIL_NXAST_BUNDLE: - case OFPUTIL_NXAST_BUNDLE_LOAD: - error = bundle_from_openflow(&a->bundle, out); - break; - - case OFPUTIL_NXAST_OUTPUT_REG: - error = output_reg_from_openflow(&a->output_reg, out); - break; - - case OFPUTIL_NXAST_RESUBMIT_TABLE: - error = resubmit_table_from_openflow(&a->resubmit, out); - break; - - case OFPUTIL_NXAST_LEARN: - error = learn_from_openflow(&a->learn, out); - break; - - case OFPUTIL_NXAST_EXIT: - ofpact_put_EXIT(out); - break; - - case OFPUTIL_NXAST_DEC_TTL: - error = dec_ttl_from_openflow(out, code); - break; - - case OFPUTIL_NXAST_DEC_TTL_CNT_IDS: - error = dec_ttl_cnt_ids_from_openflow(&a->cnt_ids, out); - break; - - case OFPUTIL_NXAST_FIN_TIMEOUT: - fin_timeout_from_openflow(&a->fin_timeout, out); - break; - - case OFPUTIL_NXAST_CONTROLLER: - controller_from_openflow(&a->controller, out); - break; - - case OFPUTIL_NXAST_PUSH_MPLS: - error = push_mpls_from_openflow(a->push_mpls.ethertype, out); - break; - - case OFPUTIL_NXAST_SET_MPLS_LABEL: - ofpact_put_SET_MPLS_LABEL(out)->label = a->mpls_label.label; - break; - - case OFPUTIL_NXAST_SET_MPLS_TC: - ofpact_put_SET_MPLS_TC(out)->tc = a->mpls_tc.tc; - break; - - case OFPUTIL_NXAST_SET_MPLS_TTL: - ofpact_put_SET_MPLS_TTL(out)->ttl = a->mpls_ttl.ttl; - break; - - case OFPUTIL_NXAST_DEC_MPLS_TTL: - ofpact_put_DEC_MPLS_TTL(out); - break; - - case OFPUTIL_NXAST_POP_MPLS: - ofpact_put_POP_MPLS(out)->ethertype = a->pop_mpls.ethertype; - break; - - case OFPUTIL_NXAST_SAMPLE: - error = sample_from_openflow(&a->sample, out); - break; + 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; } + if (!is_all_zeros(nab->zero, sizeof nab->zero)) { + VLOG_WARN_RL(&rl, "reserved field is nonzero"); + error = OFPERR_OFPBAC_BAD_ARGUMENT; + } + + if (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; + } + } else { + if (nab->ofs_nbits || nab->dst) { + VLOG_WARN_RL(&rl, "bundle action has nonzero reserved fields"); + error = OFPERR_OFPBAC_BAD_ARGUMENT; + } + } + + 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.", + load ? "bundle_load" : "bundle", 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; } static enum ofperr -ofpact_from_openflow10(const union ofp_action *a, - enum ofp_version version OVS_UNUSED, - struct ofpbuf *out) +decode_NXAST_RAW_BUNDLE(const struct nx_action_bundle *nab, struct ofpbuf *out) { - enum ofputil_action_code code; - enum ofperr error; - struct ofpact_vlan_vid *vlan_vid; - struct ofpact_vlan_pcp *vlan_pcp; + return decode_bundle(false, nab, out); +} - error = decode_openflow10_action(a, &code); +static enum ofperr +decode_NXAST_RAW_BUNDLE_LOAD(const struct nx_action_bundle *nab, + struct ofpbuf *out) +{ + return decode_bundle(true, nab, out); +} + +static void +encode_BUNDLE(const struct ofpact_bundle *bundle, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) +{ + 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 + ? put_NXAST_BUNDLE_LOAD(out) + : put_NXAST_BUNDLE(out)); + 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(out, slaves_len); + for (i = 0; i < bundle->n_slaves; i++) { + slaves[i] = htons(ofp_to_u16(bundle->slaves[i])); + } +} + +static char * WARN_UNUSED_RESULT +parse_BUNDLE(const char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + return bundle_parse(arg, ofpacts); +} + +static char * WARN_UNUSED_RESULT +parse_bundle_load(const char *arg, struct ofpbuf *ofpacts) +{ + return bundle_parse_load(arg, ofpacts); +} + +static void +format_BUNDLE(const struct ofpact_bundle *a, struct ds *s) +{ + bundle_format(a, s); +} + +/* Set VLAN actions. */ + +static enum ofperr +decode_set_vlan_vid(uint16_t vid, bool push_vlan_if_needed, struct ofpbuf *out) +{ + if (vid & ~0xfff) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } else { + struct ofpact_vlan_vid *vlan_vid = ofpact_put_SET_VLAN_VID(out); + vlan_vid->vlan_vid = vid; + vlan_vid->push_vlan_if_needed = push_vlan_if_needed; + return 0; + } +} + +static enum ofperr +decode_OFPAT_RAW10_SET_VLAN_VID(uint16_t vid, struct ofpbuf *out) +{ + return decode_set_vlan_vid(vid, true, out); +} + +static enum ofperr +decode_OFPAT_RAW11_SET_VLAN_VID(uint16_t vid, struct ofpbuf *out) +{ + return decode_set_vlan_vid(vid, false, out); +} + +static void +encode_SET_VLAN_VID(const struct ofpact_vlan_vid *vlan_vid, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + uint16_t vid = vlan_vid->vlan_vid; + + /* Push a VLAN tag, if none is present and this form of the action calls + * for such a feature. */ + if (ofp_version > OFP10_VERSION + && vlan_vid->push_vlan_if_needed + && !vlan_vid->flow_has_vlan) { + put_OFPAT11_PUSH_VLAN(out, htons(ETH_TYPE_VLAN_8021Q)); + } + + if (ofp_version == OFP10_VERSION) { + put_OFPAT10_SET_VLAN_VID(out, vid); + } else if (ofp_version == OFP11_VERSION) { + put_OFPAT11_SET_VLAN_VID(out, vid); + } else { + ofpact_put_set_field(out, ofp_version, + MFF_VLAN_VID, vid | OFPVID12_PRESENT); + } +} + +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; } - 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(); - - case OFPUTIL_OFPAT10_OUTPUT: - return output_from_openflow10(&a->output10, out); - - case OFPUTIL_OFPAT10_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 = true; - vlan_vid->ofpact.compat = code; - break; - - case OFPUTIL_OFPAT10_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 = true; - vlan_pcp->ofpact.compat = code; - break; - - case OFPUTIL_OFPAT10_STRIP_VLAN: - ofpact_put_STRIP_VLAN(out)->ofpact.compat = code; - break; - - case OFPUTIL_OFPAT10_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: - 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; - break; - - case OFPUTIL_OFPAT10_SET_NW_DST: - ofpact_put_SET_IPV4_DST(out)->ipv4 = a->nw_addr.nw_addr; - break; - - case OFPUTIL_OFPAT10_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_OFPAT10_SET_TP_SRC: - ofpact_put_SET_L4_SRC_PORT(out)->port = ntohs(a->tp_port.tp_port); - break; - - case OFPUTIL_OFPAT10_SET_TP_DST: - ofpact_put_SET_L4_DST_PORT(out)->port = ntohs(a->tp_port.tp_port); - - break; - - case OFPUTIL_OFPAT10_ENQUEUE: - error = enqueue_from_openflow10(&a->enqueue, out); - break; - -#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM: -#include "ofp-util.def" - return ofpact_from_nxast(a, code, out); + if (vid & ~VLAN_VID_MASK) { + return xasprintf("%s: not a valid VLAN VID", arg); } - - return error; + 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 enum ofperr ofpact_from_openflow11(const union ofp_action *, - enum ofp_version, - struct ofpbuf *out); - -static inline union ofp_action * -action_next(const union ofp_action *a) +static char * WARN_UNUSED_RESULT +parse_SET_VLAN_VID(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) { - return ((union ofp_action *) (void *) - ((uint8_t *) a + ntohs(a->header.len))); + return parse_set_vlan_vid(arg, ofpacts, false); } -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); -} - -/* 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))) - static void -log_bad_action(const union ofp_action *actions, size_t max_actions, - const union ofp_action *bad_action, enum ofperr error) +format_SET_VLAN_VID(const struct ofpact_vlan_vid *a, struct ds *s) { - if (!VLOG_DROP_WARN(&rl)) { - struct ds s; + ds_put_format(s, "%s:%"PRIu16, + a->push_vlan_if_needed ? "mod_vlan_vid" : "set_vlan_vid", + a->vlan_vid); +} + +/* Set PCP actions. */ - ds_init(&s); - ds_put_hex_dump(&s, actions, max_actions * OFP_ACTION_ALIGN, 0, false); - VLOG_WARN("bad action at offset %#"PRIxPTR" (%s):\n%s", - (char *)bad_action - (char *)actions, - ofperr_get_name(error), ds_cstr(&s)); - ds_destroy(&s); +static enum ofperr +decode_set_vlan_pcp(uint8_t pcp, bool push_vlan_if_needed, struct ofpbuf *out) +{ + if (pcp & ~7) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } else { + struct ofpact_vlan_pcp *vlan_pcp = ofpact_put_SET_VLAN_PCP(out); + vlan_pcp->vlan_pcp = pcp; + vlan_pcp->push_vlan_if_needed = push_vlan_if_needed; + return 0; } } static enum ofperr -ofpacts_from_openflow(const union ofp_action *in, size_t n_in, - enum ofp_version version, struct ofpbuf *out) +decode_OFPAT_RAW10_SET_VLAN_PCP(uint8_t pcp, struct ofpbuf *out) { - const union ofp_action *a; - size_t left; + return decode_set_vlan_pcp(pcp, true, out); +} - 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; +static enum ofperr +decode_OFPAT_RAW11_SET_VLAN_PCP(uint8_t pcp, struct ofpbuf *out) +{ + return decode_set_vlan_pcp(pcp, false, out); +} - 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); - return error; - } +static void +encode_SET_VLAN_PCP(const struct ofpact_vlan_pcp *vlan_pcp, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + uint8_t pcp = vlan_pcp->vlan_pcp; + + /* Push a VLAN tag, if none is present and this form of the action calls + * for such a feature. */ + if (ofp_version > OFP10_VERSION + && vlan_pcp->push_vlan_if_needed + && !vlan_pcp->flow_has_vlan) { + put_OFPAT11_PUSH_VLAN(out, htons(ETH_TYPE_VLAN_8021Q)); } - if (left) { - enum ofperr error = OFPERR_OFPBAC_BAD_LEN; - log_bad_action(in, n_in, a, error); + + if (ofp_version == OFP10_VERSION) { + put_OFPAT10_SET_VLAN_PCP(out, pcp); + } else if (ofp_version == OFP11_VERSION) { + put_OFPAT11_SET_VLAN_PCP(out, pcp); + } else { + ofpact_put_set_field(out, ofp_version, MFF_VLAN_PCP, pcp); + } +} + +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; } - ofpact_pad(out); + 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 void +format_SET_VLAN_PCP(const struct ofpact_vlan_pcp *a, struct ds *s) +{ + ds_put_format(s, "%s:%"PRIu8, + a->push_vlan_if_needed ? "mod_vlan_pcp" : "set_vlan_pcp", + a->vlan_pcp); +} + +/* Strip VLAN actions. */ + +static enum ofperr +decode_OFPAT_RAW10_STRIP_VLAN(struct ofpbuf *out) +{ + ofpact_put_STRIP_VLAN(out)->ofpact.raw = OFPAT_RAW10_STRIP_VLAN; return 0; } static enum ofperr -ofpacts_pull_openflow_actions__(struct ofpbuf *openflow, - unsigned int actions_len, - enum ofp_version version, - uint32_t allowed_ovsinsts, - struct ofpbuf *ofpacts) +decode_OFPAT_RAW11_POP_VLAN(struct ofpbuf *out) { - const union ofp_action *actions; - enum ofperr error; + ofpact_put_STRIP_VLAN(out)->ofpact.raw = OFPAT_RAW11_POP_VLAN; + return 0; +} - ofpbuf_clear(ofpacts); - - if (actions_len % OFP_ACTION_ALIGN != 0) { - VLOG_WARN_RL(&rl, "OpenFlow message actions length %u is not a " - "multiple of %d", actions_len, OFP_ACTION_ALIGN); - return OFPERR_OFPBRC_BAD_LEN; +static void +encode_STRIP_VLAN(const struct ofpact_null *null OVS_UNUSED, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + if (ofp_version == OFP10_VERSION) { + put_OFPAT10_STRIP_VLAN(out); + } else { + put_OFPAT11_POP_VLAN(out); } +} - actions = ofpbuf_try_pull(openflow, actions_len); - if (actions == NULL) { - VLOG_WARN_RL(&rl, "OpenFlow message actions length %u exceeds " - "remaining message length (%"PRIu32")", - actions_len, ofpbuf_size(openflow)); - return OFPERR_OFPBRC_BAD_LEN; +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 void +format_STRIP_VLAN(const struct ofpact_null *a, struct ds *s) +{ + ds_put_cstr(s, (a->ofpact.raw == OFPAT_RAW11_POP_VLAN + ? "pop_vlan" + : "strip_vlan")); +} + +/* Push VLAN action. */ + +static enum ofperr +decode_OFPAT_RAW11_PUSH_VLAN(ovs_be16 eth_type, struct ofpbuf *out) +{ + if (eth_type != 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); + return 0; +} - error = ofpacts_from_openflow(actions, actions_len / OFP_ACTION_ALIGN, - version, ofpacts); +static void +encode_PUSH_VLAN(const struct ofpact_null *null OVS_UNUSED, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + if (ofp_version == OFP10_VERSION) { + /* PUSH is a side effect of a SET_VLAN_VID/PCP, which should + * follow this action. */ + } else { + /* XXX ETH_TYPE_VLAN_8021AD case */ + put_OFPAT11_PUSH_VLAN(out, htons(ETH_TYPE_VLAN_8021Q)); + } +} + +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) { - ofpbuf_clear(ofpacts); return error; } - error = ofpacts_verify(ofpbuf_data(ofpacts), ofpbuf_size(ofpacts), - allowed_ovsinsts); - if (error) { - ofpbuf_clear(ofpacts); + if (ethertype != ETH_TYPE_VLAN_8021Q) { + /* XXX ETH_TYPE_VLAN_8021AD case isn't supported */ + return xasprintf("%s: not a valid VLAN ethertype", arg); } - return error; + + ofpact_put_PUSH_VLAN(ofpacts); + return NULL; } -/* Attempts to convert 'actions_len' bytes of OpenFlow actions from the - * front of 'openflow' into ofpacts. On success, replaces any existing content - * in 'ofpacts' by the converted ofpacts; on failure, clears 'ofpacts'. - * Returns 0 if successful, otherwise an OpenFlow error. - * - * Actions are processed according to their OpenFlow version which - * is provided in the 'version' parameter. - * - * In most places in OpenFlow, actions appear encapsulated in instructions, so - * you should call ofpacts_pull_openflow_instructions() instead of this - * function. - * - * The parsed actions are valid generically, but they may not be valid in a - * specific context. For example, port numbers up to OFPP_MAX are valid - * generically, but specific datapaths may only support port numbers in a - * smaller range. Use ofpacts_check() to additional check whether actions are - * valid in a specific context. */ -enum ofperr -ofpacts_pull_openflow_actions(struct ofpbuf *openflow, - unsigned int actions_len, - enum ofp_version version, - struct ofpbuf *ofpacts) +static void +format_PUSH_VLAN(const struct ofpact_null *a OVS_UNUSED, struct ds *s) { - return ofpacts_pull_openflow_actions__(openflow, actions_len, version, - 1u << OVSINST_OFPIT11_APPLY_ACTIONS, - ofpacts); + /* XXX 802.1AD case*/ + ds_put_format(s, "push_vlan:%#"PRIx16, ETH_TYPE_VLAN_8021Q); } -/* OpenFlow 1.1 actions. */ +/* 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); -/* 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) +decode_OFPAT_RAW_SET_DL_SRC(const struct ofp_action_dl_addr *a, + struct ofpbuf *out) { - 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; - } + memcpy(ofpact_put_SET_ETH_SRC(out)->mac, a->dl_addr, ETH_ADDR_LEN); + return 0; } static enum ofperr -set_field_from_openflow(const struct ofp12_action_set_field *oasf, - struct ofpbuf *ofpacts) +decode_OFPAT_RAW_SET_DL_DST(const struct ofp_action_dl_addr *a, + struct ofpbuf *out) +{ + memcpy(ofpact_put_SET_ETH_DST(out)->mac, a->dl_addr, ETH_ADDR_LEN); + return 0; +} + +static void +encode_SET_ETH_addr(const struct ofpact_mac *mac, enum ofp_version ofp_version, + enum ofp_raw_action_type raw, enum mf_field_id field, + struct ofpbuf *out) +{ + const uint8_t *addr = mac->mac; + + if (ofp_version < OFP12_VERSION) { + struct ofp_action_dl_addr *oada; + + oada = ofpact_put_raw(out, ofp_version, raw, 0); + memcpy(oada->dl_addr, addr, ETH_ADDR_LEN); + } else { + ofpact_put_set_field(out, ofp_version, field, + eth_addr_to_uint64(addr)); + } +} + +static void +encode_SET_ETH_SRC(const struct ofpact_mac *mac, enum ofp_version ofp_version, + struct ofpbuf *out) +{ + encode_SET_ETH_addr(mac, ofp_version, OFPAT_RAW_SET_DL_SRC, MFF_ETH_SRC, + out); + +} + +static void +encode_SET_ETH_DST(const struct ofpact_mac *mac, + enum ofp_version ofp_version, + struct ofpbuf *out) +{ + encode_SET_ETH_addr(mac, ofp_version, OFPAT_RAW_SET_DL_DST, MFF_ETH_DST, + out); + +} + +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 void +format_SET_ETH_SRC(const struct ofpact_mac *a, struct ds *s) +{ + ds_put_format(s, "mod_dl_src:"ETH_ADDR_FMT, ETH_ADDR_ARGS(a->mac)); +} + +static void +format_SET_ETH_DST(const struct ofpact_mac *a, struct ds *s) +{ + ds_put_format(s, "mod_dl_dst:"ETH_ADDR_FMT, ETH_ADDR_ARGS(a->mac)); +} + +/* Set IPv4 address actions. */ + +static enum ofperr +decode_OFPAT_RAW_SET_NW_SRC(ovs_be32 ipv4, struct ofpbuf *out) +{ + ofpact_put_SET_IPV4_SRC(out)->ipv4 = ipv4; + return 0; +} + +static enum ofperr +decode_OFPAT_RAW_SET_NW_DST(ovs_be32 ipv4, struct ofpbuf *out) +{ + ofpact_put_SET_IPV4_DST(out)->ipv4 = ipv4; + return 0; +} + +static void +encode_SET_IPV4_addr(const struct ofpact_ipv4 *ipv4, + enum ofp_version ofp_version, + enum ofp_raw_action_type raw, enum mf_field_id field, + struct ofpbuf *out) +{ + ovs_be32 addr = ipv4->ipv4; + if (ofp_version < OFP12_VERSION) { + ofpact_put_raw(out, ofp_version, raw, ntohl(addr)); + } else { + ofpact_put_set_field(out, ofp_version, field, ntohl(addr)); + } +} + +static void +encode_SET_IPV4_SRC(const struct ofpact_ipv4 *ipv4, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + encode_SET_IPV4_addr(ipv4, ofp_version, OFPAT_RAW_SET_NW_SRC, MFF_IPV4_SRC, + out); +} + +static void +encode_SET_IPV4_DST(const struct ofpact_ipv4 *ipv4, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + encode_SET_IPV4_addr(ipv4, ofp_version, OFPAT_RAW_SET_NW_DST, MFF_IPV4_DST, + out); +} + +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 void +format_SET_IPV4_SRC(const struct ofpact_ipv4 *a, struct ds *s) +{ + ds_put_format(s, "mod_nw_src:"IP_FMT, IP_ARGS(a->ipv4)); +} + +static void +format_SET_IPV4_DST(const struct ofpact_ipv4 *a, struct ds *s) +{ + ds_put_format(s, "mod_nw_dst:"IP_FMT, IP_ARGS(a->ipv4)); +} + +/* Set IPv4/v6 TOS actions. */ + +static enum ofperr +decode_OFPAT_RAW_SET_NW_TOS(uint8_t dscp, struct ofpbuf *out) +{ + if (dscp & ~IP_DSCP_MASK) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } else { + ofpact_put_SET_IP_DSCP(out)->dscp = dscp; + return 0; + } +} + +static void +encode_SET_IP_DSCP(const struct ofpact_dscp *dscp, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + if (ofp_version < OFP12_VERSION) { + put_OFPAT_SET_NW_TOS(out, ofp_version, dscp->dscp); + } else { + ofpact_put_set_field(out, ofp_version, + MFF_IP_DSCP_SHIFTED, dscp->dscp >> 2); + } +} + +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 void +format_SET_IP_DSCP(const struct ofpact_dscp *a, struct ds *s) +{ + ds_put_format(s, "mod_nw_tos:%d", a->dscp); +} + +/* Set IPv4/v6 ECN actions. */ + +static enum ofperr +decode_OFPAT_RAW11_SET_NW_ECN(uint8_t ecn, struct ofpbuf *out) +{ + if (ecn & ~IP_ECN_MASK) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } else { + ofpact_put_SET_IP_ECN(out)->ecn = ecn; + return 0; + } +} + +static void +encode_SET_IP_ECN(const struct ofpact_ecn *ip_ecn, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + uint8_t ecn = ip_ecn->ecn; + if (ofp_version == OFP10_VERSION) { + /* XXX */ + } else if (ofp_version == OFP11_VERSION) { + put_OFPAT11_SET_NW_ECN(out, ecn); + } else { + ofpact_put_set_field(out, ofp_version, MFF_IP_ECN, ecn); + } +} + +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 void +format_SET_IP_ECN(const struct ofpact_ecn *a, struct ds *s) +{ + ds_put_format(s, "mod_nw_ecn:%d", a->ecn); +} + +/* Set IPv4/v6 TTL actions. */ + +static enum ofperr +decode_OFPAT_RAW11_SET_NW_TTL(uint8_t ttl, struct ofpbuf *out) +{ + ofpact_put_SET_IP_TTL(out)->ttl = ttl; + return 0; +} + +static void +encode_SET_IP_TTL(const struct ofpact_ip_ttl *ttl, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + if (ofp_version >= OFP11_VERSION) { + put_OFPAT11_SET_NW_TTL(out, ttl->ttl); + } else { + /* XXX */ + } +} + +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 void +format_SET_IP_TTL(const struct ofpact_ip_ttl *a, struct ds *s) +{ + ds_put_format(s, "mod_nw_ttl:%d", a->ttl); +} + +/* Set TCP/UDP/SCTP port actions. */ + +static enum ofperr +decode_OFPAT_RAW_SET_TP_SRC(ovs_be16 port, struct ofpbuf *out) +{ + ofpact_put_SET_L4_SRC_PORT(out)->port = ntohs(port); + return 0; +} + +static enum ofperr +decode_OFPAT_RAW_SET_TP_DST(ovs_be16 port, struct ofpbuf *out) +{ + ofpact_put_SET_L4_DST_PORT(out)->port = ntohs(port); + return 0; +} + +static void +encode_SET_L4_port(const struct ofpact_l4_port *l4_port, + enum ofp_version ofp_version, enum ofp_raw_action_type raw, + enum mf_field_id field, struct ofpbuf *out) +{ + uint16_t port = l4_port->port; + + if (ofp_version >= OFP12_VERSION && field != MFF_N_IDS) { + ofpact_put_set_field(out, ofp_version, field, port); + } else { + ofpact_put_raw(out, ofp_version, raw, port); + } +} + +static void +encode_SET_L4_SRC_PORT(const struct ofpact_l4_port *l4_port, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + 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); + + encode_SET_L4_port(l4_port, ofp_version, OFPAT_RAW_SET_TP_SRC, field, out); +} + +static void +encode_SET_L4_DST_PORT(const struct ofpact_l4_port *l4_port, + enum ofp_version ofp_version, + struct ofpbuf *out) +{ + 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); + + encode_SET_L4_port(l4_port, ofp_version, OFPAT_RAW_SET_TP_DST, field, out); +} + +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 void +format_SET_L4_SRC_PORT(const struct ofpact_l4_port *a, struct ds *s) +{ + ds_put_format(s, "mod_tp_src:%d", a->port); +} + +static void +format_SET_L4_DST_PORT(const struct ofpact_l4_port *a, struct ds *s) +{ + ds_put_format(s, "mod_tp_dst:%d", a->port); +} + +/* 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); + +static enum ofperr +decode_NXAST_RAW_REG_MOVE(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); +} + +static void +encode_REG_MOVE(const struct ofpact_reg_move *move, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out) +{ + struct nx_action_reg_move *narm; + + narm = put_NXAST_REG_MOVE(out); + 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); +} + +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 void +format_REG_MOVE(const struct ofpact_reg_move *a, struct ds *s) +{ + nxm_format_reg_move(a, s); +} + +/* 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); + +static enum ofperr +decode_NXAST_RAW_REG_LOAD(const struct nx_action_reg_load *narl, + struct ofpbuf *out) +{ + struct ofpact_reg_load *load; + + load = ofpact_put_REG_LOAD(out); + 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 +encode_REG_LOAD(const struct ofpact_reg_load *load, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) +{ + struct nx_action_reg_load *narl; + + narl = put_NXAST_REG_LOAD(out); + 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 char * WARN_UNUSED_RESULT +parse_REG_LOAD(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + return nxm_parse_reg_load(ofpact_put_REG_LOAD(ofpacts), arg); +} + +static void +format_REG_LOAD(const struct ofpact_reg_load *a, struct ds *s) +{ + nxm_format_reg_load(a, s); +} + +/* 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); + +static enum ofperr +decode_OFPAT_RAW12_SET_FIELD(const struct ofp12_action_set_field *oasf, + struct ofpbuf *ofpacts) { uint16_t oasf_len = ntohs(oasf->len); uint32_t oxm_header = ntohl(oasf->dst); @@ -834,20 +1943,23 @@ set_field_from_openflow(const struct ofp12_action_set_field *oasf, } static void -set_field_to_openflow12(const struct ofpact_set_field *sf, - struct ofpbuf *openflow, - enum ofp_version version) +ofpact_put_set_field(struct ofpbuf *openflow, enum ofp_version ofp_version, + enum mf_field_id field, uint64_t value) { - uint16_t padded_value_len = ROUND_UP(sf->field->n_bytes, 8); + const struct mf_field *mf = mf_from_id(field); struct ofp12_action_set_field *oasf; - char *value; + ovs_be64 n_value; - oasf = ofputil_put_OFPAT12_SET_FIELD(openflow); - oasf->dst = htonl(mf_oxm_header(sf->field->id, version)); - oasf->len = htons(sizeof *oasf + padded_value_len); + oasf = put_OFPAT12_SET_FIELD(openflow); + oasf->dst = htonl(mf_oxm_header(mf->id, ofp_version)); + oasf->len = htons(sizeof *oasf + 8); - value = ofpbuf_put_zeros(openflow, padded_value_len); - memcpy(value, &sf->value, sf->field->n_bytes); + 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); } /* Convert 'sf' to one or two REG_LOADs. */ @@ -861,17 +1973,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); @@ -880,7 +1992,7 @@ set_field_to_nxast(const struct ofpact_set_field *sf, struct ofpbuf *openflow) } } -/* Convert 'sf' to standard OpenFlow 1.1 actions, if we can, falling back +/* Convert 'sf' to standard OpenFlow 1.0/1.1 actions, if we can, falling back * to Nicira extensions if we must. * * We check only meta-flow types that can appear within set field actions and @@ -888,403 +2000,2225 @@ set_field_to_nxast(const struct ofpact_set_field *sf, struct ofpbuf *openflow) * definitions have a defined OXM or NXM header value and specify the field as * writable. */ static void -set_field_to_openflow11(const struct ofpact_set_field *sf, - struct ofpbuf *openflow) +set_field_to_legacy_openflow(const struct ofpact_set_field *sf, + enum ofp_version ofp_version, + struct ofpbuf *out) { switch ((int) sf->field->id) { - case MFF_VLAN_TCI: - /* NXM_OF_VLAN_TCI to OpenFlow 1.1 mapping: - * - * If CFI=1, Add or modify VLAN VID & PCP. - * OpenFlow 1.1 set actions only apply if the packet - * already has VLAN tags. To be sure that is the case - * we have to push a VLAN header. As we do not support - * multiple layers of VLANs, this is a no-op, if a VLAN - * header already exists. This may backfire, however, - * when we start supporting multiple layers of VLANs. - * If CFI=0, strip VLAN header, if any. - */ - if (sf->value.be16 & htons(VLAN_CFI)) { - /* 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); + case MFF_VLAN_TCI: { + ovs_be16 tci = sf->value.be16; + bool cfi = (tci & htons(VLAN_CFI)) != 0; + uint16_t vid = vlan_tci_to_vid(tci); + uint8_t pcp = vlan_tci_to_pcp(tci); + + if (ofp_version < OFP11_VERSION) { + /* NXM_OF_VLAN_TCI to OpenFlow 1.0 mapping: + * + * If CFI=1, Add or modify VLAN VID & PCP. + * If CFI=0, strip VLAN header, if any. + */ + if (cfi) { + put_OFPAT10_SET_VLAN_VID(out, vid); + put_OFPAT10_SET_VLAN_PCP(out, pcp); + } else { + put_OFPAT10_STRIP_VLAN(out); } - 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); } 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); + /* NXM_OF_VLAN_TCI to OpenFlow 1.1 mapping: + * + * If CFI=1, Add or modify VLAN VID & PCP. + * OpenFlow 1.1 set actions only apply if the packet + * already has VLAN tags. To be sure that is the case + * we have to push a VLAN header. As we do not support + * multiple layers of VLANs, this is a no-op, if a VLAN + * header already exists. This may backfire, however, + * when we start supporting multiple layers of VLANs. + * If CFI=0, strip VLAN header, if any. + */ + if (cfi) { + /* Push a VLAN tag, if one was not seen at action validation + * time. */ + if (!sf->flow_has_vlan) { + put_OFPAT11_PUSH_VLAN(out, htons(ETH_TYPE_VLAN_8021Q)); + } + put_OFPAT11_SET_VLAN_VID(out, vid); + put_OFPAT11_SET_VLAN_PCP(out, pcp); + } 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. */ + put_OFPAT11_POP_VLAN(out); + } } break; - - case MFF_VLAN_VID: - /* OXM VLAN_PCP to OpenFlow 1.1. - * 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); - 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; - break; - - case MFF_ETH_SRC: - memcpy(ofputil_put_OFPAT11_SET_DL_SRC(openflow)->dl_addr, - sf->value.mac, ETH_ADDR_LEN); - break; - - case MFF_ETH_DST: - memcpy(ofputil_put_OFPAT11_SET_DL_DST(openflow)->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; - break; - - case MFF_MPLS_TC: - ofputil_put_OFPAT11_SET_MPLS_TC(openflow)->mpls_tc = sf->value.u8; - break; - - case MFF_IPV4_SRC: - ofputil_put_OFPAT11_SET_NW_SRC(openflow)->nw_addr = sf->value.be32; - break; - - case MFF_IPV4_DST: - ofputil_put_OFPAT11_SET_NW_DST(openflow)->nw_addr = sf->value.be32; - break; - - case MFF_IP_DSCP: - ofputil_put_OFPAT11_SET_NW_TOS(openflow)->nw_tos = sf->value.u8; - break; - - case MFF_IP_DSCP_SHIFTED: - ofputil_put_OFPAT11_SET_NW_TOS(openflow)->nw_tos = sf->value.u8 << 2; - break; - - case MFF_IP_ECN: - ofputil_put_OFPAT11_SET_NW_ECN(openflow)->nw_ecn = sf->value.u8; - break; - - case MFF_IP_TTL: - ofputil_put_OFPAT11_SET_NW_TTL(openflow)->nw_ttl = 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; - 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; - break; - - default: - set_field_to_nxast(sf, openflow); - break; } -} -/* Convert 'sf' to standard OpenFlow 1.0 actions, if we can, falling back - * to Nicira extensions if we must. - * - * We check only meta-flow types that can appear within set field actions and - * that have a mapping to compatible action types. These struct mf_field - * definitions have a defined OXM or NXM header value and specify the field as - * writable. */ -static void -set_field_to_openflow10(const struct ofpact_set_field *sf, - struct ofpbuf *openflow) -{ - switch ((int) sf->field->id) { - case MFF_VLAN_TCI: - /* NXM_OF_VLAN_TCI to OpenFlow 1.0 mapping: - * - * If CFI=1, Add or modify VLAN VID & PCP. - * 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); + case MFF_VLAN_VID: { + uint16_t vid = ntohs(sf->value.be16) & VLAN_VID_MASK; + if (ofp_version == OFP10_VERSION) { + put_OFPAT10_SET_VLAN_VID(out, vid); } else { - ofputil_put_OFPAT10_STRIP_VLAN(openflow); + put_OFPAT11_SET_VLAN_VID(out, vid); + } + break; + } + + case MFF_VLAN_PCP: + if (ofp_version == OFP10_VERSION) { + put_OFPAT10_SET_VLAN_PCP(out, sf->value.u8); + } else { + put_OFPAT11_SET_VLAN_PCP(out, sf->value.u8); } break; - case MFF_VLAN_VID: - /* OXM VLAN_VID to OpenFlow 1.0. - * 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); - 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; - break; - case MFF_ETH_SRC: - memcpy(ofputil_put_OFPAT10_SET_DL_SRC(openflow)->dl_addr, + memcpy(put_OFPAT_SET_DL_SRC(out, ofp_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(out, ofp_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(out, ofp_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(out, ofp_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(out, ofp_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(out, ofp_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(out, 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(out, sf->value.be16); break; default: - set_field_to_nxast(sf, openflow); + set_field_to_nxast(sf, out); break; } } static void -set_field_to_openflow(const struct ofpact_set_field *sf, - struct ofpbuf *openflow) +encode_SET_FIELD(const struct ofpact_set_field *sf, + enum ofp_version ofp_version, struct ofpbuf *out) { - struct ofp_header *oh = (struct ofp_header *)openflow->frame; - - if (oh->version >= OFP12_VERSION) { - set_field_to_openflow12(sf, openflow, oh->version); - } else if (oh->version == OFP11_VERSION) { - set_field_to_openflow11(sf, openflow); - } else if (oh->version == OFP10_VERSION) { - set_field_to_openflow10(sf, openflow); + if (ofp_version < OFP12_VERSION) { + set_field_to_legacy_openflow(sf, ofp_version, out); } else { - OVS_NOT_REACHED(); + uint16_t padded_value_len = ROUND_UP(sf->field->n_bytes, 8); + struct ofp12_action_set_field *oasf; + char *value; + + oasf = ofpact_put_raw(out, ofp_version, OFPAT_RAW12_SET_FIELD, 0); + oasf->dst = htonl(mf_oxm_header(sf->field->id, ofp_version)); + oasf->len = htons(sizeof *oasf + padded_value_len); + + value = ofpbuf_put_zeros(out, padded_value_len); + memcpy(value, &sf->value, sf->field->n_bytes); } } -static enum ofperr -output_from_openflow11(const struct ofp11_action_output *oao, - struct ofpbuf *out) +/* 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_output *output; - enum ofperr error; + struct ofpact_set_field *sf = ofpact_put_SET_FIELD(ofpacts); + char *value; + char *delim; + char *key; + const struct mf_field *mf; + char *error; - output = ofpact_put_OUTPUT(out); - output->max_len = ntohs(oao->max_len); + value = arg; + delim = strstr(arg, "->"); + if (!delim) { + return xasprintf("%s: missing `->'", arg); + } + if (strlen(delim) <= strlen("->")) { + return xasprintf("%s: missing field name following `->'", arg); + } - error = ofputil_port_from_ofp11(oao->port, &output->port); + 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; } - return ofpact_check_output_port(output->port, OFPP_MAX); + 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; +} + +static void +format_SET_FIELD(const struct ofpact_set_field *a, struct ds *s) +{ + ds_put_format(s, "set_field:"); + mf_format(a->field, &a->value, NULL, s); + ds_put_format(s, "->%s", a->field->name); +} + +/* 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); + +static void +decode_stack_action(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 enum ofperr -ofpact_from_openflow11(const union ofp_action *a, enum ofp_version version, - struct ofpbuf *out) +decode_NXAST_RAW_STACK_PUSH(const struct nx_action_stack *nasp, + struct ofpbuf *ofpacts) { - enum ofputil_action_code code; - enum ofperr error; - struct ofpact_vlan_vid *vlan_vid; - struct ofpact_vlan_pcp *vlan_pcp; + struct ofpact_stack *push = ofpact_put_STACK_PUSH(ofpacts); + decode_stack_action(nasp, push); + return nxm_stack_push_check(push, NULL); +} - error = decode_openflow11_action(a, &code); - if (error) { - return error; +static enum ofperr +decode_NXAST_RAW_STACK_POP(const struct nx_action_stack *nasp, + struct ofpbuf *ofpacts) +{ + struct ofpact_stack *pop = ofpact_put_STACK_POP(ofpacts); + decode_stack_action(nasp, pop); + return nxm_stack_pop_check(pop, NULL); +} + +static void +encode_STACK_op(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 void +encode_STACK_PUSH(const struct ofpact_stack *stack, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) +{ + encode_STACK_op(stack, put_NXAST_STACK_PUSH(out)); +} + +static void +encode_STACK_POP(const struct ofpact_stack *stack, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) +{ + encode_STACK_op(stack, put_NXAST_STACK_POP(out)); +} + +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 void +format_STACK_PUSH(const struct ofpact_stack *a, struct ds *s) +{ + nxm_format_stack_push(a, s); +} + +static void +format_STACK_POP(const struct ofpact_stack *a, struct ds *s) +{ + nxm_format_stack_pop(a, s); +} + +/* 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); + +static enum ofperr +decode_OFPAT_RAW_DEC_NW_TTL(struct ofpbuf *out) +{ + uint16_t id = 0; + struct ofpact_cnt_ids *ids; + enum ofperr error = 0; + + ids = ofpact_put_DEC_TTL(out); + ids->n_controllers = 1; + ofpbuf_put(out, &id, sizeof id); + ids = out->frame; + ofpact_update_len(out, &ids->ofpact); + return error; +} + +static enum ofperr +decode_NXAST_RAW_DEC_TTL_CNT_IDS(const struct nx_action_cnt_ids *nac_ids, + struct ofpbuf *out) +{ + struct ofpact_cnt_ids *ids; + size_t ids_size; + int i; + + ids = ofpact_put_DEC_TTL(out); + 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; + + if (!is_all_zeros(nac_ids->zeros, sizeof nac_ids->zeros)) { + return OFPERR_NXBRC_MUST_BE_ZERO; } - 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)); + if (ids_size < ids->n_controllers * sizeof(ovs_be16)) { + VLOG_WARN_RL(&rl, "Nicira action dec_ttl_cnt_ids only has %"PRIuSIZE" " + "bytes allocated for controller ids. %"PRIuSIZE" bytes " + "are required for %"PRIu16" controllers.", + ids_size, ids->n_controllers * sizeof(ovs_be16), + ids->n_controllers); + return OFPERR_OFPBAC_BAD_LEN; + } + + for (i = 0; i < ids->n_controllers; i++) { + uint16_t id = ntohs(((ovs_be16 *)(nac_ids + 1))[i]); + ofpbuf_put(out, &id, sizeof id); + ids = out->frame; + } + + ofpact_update_len(out, &ids->ofpact); + + return 0; +} + +static void +encode_DEC_TTL(const struct ofpact_cnt_ids *dec_ttl, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + if (dec_ttl->ofpact.raw == NXAST_RAW_DEC_TTL_CNT_IDS + || dec_ttl->n_controllers != 1 + || dec_ttl->cnt_ids[0] != 0) { + struct nx_action_cnt_ids *nac_ids = put_NXAST_DEC_TTL_CNT_IDS(out); + int ids_len = ROUND_UP(2 * dec_ttl->n_controllers, OFP_ACTION_ALIGN); + ovs_be16 *ids; + size_t i; + + nac_ids->len = htons(ntohs(nac_ids->len) + ids_len); + nac_ids->n_controllers = htons(dec_ttl->n_controllers); + + ids = ofpbuf_put_zeros(out, ids_len); + for (i = 0; i < dec_ttl->n_controllers; i++) { + ids[i] = htons(dec_ttl->cnt_ids[i]); + } + } else { + put_OFPAT_DEC_NW_TTL(out, ofp_version); + } +} + +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 void +format_DEC_TTL(const struct ofpact_cnt_ids *a, struct ds *s) +{ + size_t i; + + ds_put_cstr(s, "dec_ttl"); + if (a->ofpact.raw == NXAST_RAW_DEC_TTL_CNT_IDS) { + ds_put_cstr(s, "("); + for (i = 0; i < a->n_controllers; i++) { + if (i) { + ds_put_cstr(s, ","); + } + ds_put_format(s, "%"PRIu16, a->cnt_ids[i]); + } + ds_put_cstr(s, ")"); + } +} + +/* Set MPLS label actions. */ + +static enum ofperr +decode_OFPAT_RAW_SET_MPLS_LABEL(ovs_be32 label, struct ofpbuf *out) +{ + ofpact_put_SET_MPLS_LABEL(out)->label = label; + return 0; +} + +static void +encode_SET_MPLS_LABEL(const struct ofpact_mpls_label *label, + enum ofp_version ofp_version, + struct ofpbuf *out) +{ + if (ofp_version < OFP12_VERSION) { + put_OFPAT_SET_MPLS_LABEL(out, ofp_version, label->label); + } else { + ofpact_put_set_field(out, ofp_version, MFF_MPLS_LABEL, + ntohl(label->label)); + } +} + +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 void +format_SET_MPLS_LABEL(const struct ofpact_mpls_label *a, struct ds *s) +{ + ds_put_format(s, "set_mpls_label(%"PRIu32")", ntohl(a->label)); +} + +/* Set MPLS TC actions. */ + +static enum ofperr +decode_OFPAT_RAW_SET_MPLS_TC(uint8_t tc, struct ofpbuf *out) +{ + ofpact_put_SET_MPLS_TC(out)->tc = tc; + return 0; +} + +static void +encode_SET_MPLS_TC(const struct ofpact_mpls_tc *tc, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + if (ofp_version < OFP12_VERSION) { + put_OFPAT_SET_MPLS_TC(out, ofp_version, tc->tc); + } else { + ofpact_put_set_field(out, ofp_version, MFF_MPLS_TC, tc->tc); + } +} + +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; +} + +static void +format_SET_MPLS_TC(const struct ofpact_mpls_tc *a, struct ds *s) +{ + ds_put_format(s, "set_mpls_ttl(%"PRIu8")", a->tc); +} + +/* Set MPLS TTL actions. */ + +static enum ofperr +decode_OFPAT_RAW_SET_MPLS_TTL(uint8_t ttl, struct ofpbuf *out) +{ + ofpact_put_SET_MPLS_TTL(out)->ttl = ttl; + return 0; +} + +static void +encode_SET_MPLS_TTL(const struct ofpact_mpls_ttl *ttl, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + put_OFPAT_SET_MPLS_TTL(out, ofp_version, ttl->ttl); +} + +/* 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 void +format_SET_MPLS_TTL(const struct ofpact_mpls_ttl *a, struct ds *s) +{ + ds_put_format(s, "set_mpls_ttl(%"PRIu8")", a->ttl); +} + +/* Decrement MPLS TTL actions. */ + +static enum ofperr +decode_OFPAT_RAW_DEC_MPLS_TTL(struct ofpbuf *out) +{ + ofpact_put_DEC_MPLS_TTL(out); + return 0; +} + +static void +encode_DEC_MPLS_TTL(const struct ofpact_null *null OVS_UNUSED, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + put_OFPAT_DEC_MPLS_TTL(out, ofp_version); +} + +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 void +format_DEC_MPLS_TTL(const struct ofpact_null *a OVS_UNUSED, struct ds *s) +{ + ds_put_cstr(s, "dec_mpls_ttl"); +} + +/* Push MPLS label action. */ + +static enum ofperr +decode_OFPAT_RAW_PUSH_MPLS(ovs_be16 ethertype, struct ofpbuf *out) +{ + struct ofpact_push_mpls *oam; + + if (!eth_type_mpls(ethertype)) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + oam = ofpact_put_PUSH_MPLS(out); + oam->ethertype = ethertype; + + return 0; +} + +static void +encode_PUSH_MPLS(const struct ofpact_push_mpls *push_mpls, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + put_OFPAT_PUSH_MPLS(out, ofp_version, push_mpls->ethertype); +} + +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 void +format_PUSH_MPLS(const struct ofpact_push_mpls *a, struct ds *s) +{ + ds_put_format(s, "push_mpls:0x%04"PRIx16, ntohs(a->ethertype)); +} + +/* Pop MPLS label action. */ + +static enum ofperr +decode_OFPAT_RAW_POP_MPLS(ovs_be16 ethertype, struct ofpbuf *out) +{ + ofpact_put_POP_MPLS(out)->ethertype = ethertype; + return 0; +} + +static void +encode_POP_MPLS(const struct ofpact_pop_mpls *pop_mpls, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + put_OFPAT_POP_MPLS(out, ofp_version, pop_mpls->ethertype); +} + +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 void +format_POP_MPLS(const struct ofpact_pop_mpls *a, struct ds *s) +{ + ds_put_format(s, "pop_mpls:0x%04"PRIx16, ntohs(a->ethertype)); +} + +/* Set tunnel ID actions. */ + +static enum ofperr +decode_NXAST_RAW_SET_TUNNEL(uint32_t tun_id, struct ofpbuf *out) +{ + struct ofpact_tunnel *tunnel = ofpact_put_SET_TUNNEL(out); + tunnel->ofpact.raw = NXAST_RAW_SET_TUNNEL; + tunnel->tun_id = tun_id; + return 0; +} + +static enum ofperr +decode_NXAST_RAW_SET_TUNNEL64(uint64_t tun_id, struct ofpbuf *out) +{ + struct ofpact_tunnel *tunnel = ofpact_put_SET_TUNNEL(out); + tunnel->ofpact.raw = NXAST_RAW_SET_TUNNEL64; + tunnel->tun_id = tun_id; + return 0; +} + +static void +encode_SET_TUNNEL(const struct ofpact_tunnel *tunnel, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + uint64_t tun_id = tunnel->tun_id; + + if (ofp_version < OFP12_VERSION) { + if (tun_id <= UINT32_MAX + && tunnel->ofpact.raw != NXAST_RAW_SET_TUNNEL64) { + put_NXAST_SET_TUNNEL(out, tun_id); + } else { + put_NXAST_SET_TUNNEL64(out, tun_id); + } + } else { + ofpact_put_set_field(out, ofp_version, MFF_TUN_ID, tun_id); + } +} + +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 void +format_SET_TUNNEL(const struct ofpact_tunnel *a, struct ds *s) +{ + ds_put_format(s, "set_tunnel%s:%#"PRIx64, + (a->tun_id > UINT32_MAX + || a->ofpact.raw == NXAST_RAW_SET_TUNNEL64 ? "64" : ""), + a->tun_id); +} + +/* Set queue action. */ + +static enum ofperr +decode_OFPAT_RAW_SET_QUEUE(uint32_t queue_id, struct ofpbuf *out) +{ + ofpact_put_SET_QUEUE(out)->queue_id = queue_id; + return 0; +} + +static void +encode_SET_QUEUE(const struct ofpact_queue *queue, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + put_OFPAT_SET_QUEUE(out, ofp_version, queue->queue_id); +} + +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 void +format_SET_QUEUE(const struct ofpact_queue *a, struct ds *s) +{ + ds_put_format(s, "set_queue:%"PRIu32, a->queue_id); +} + +/* Pop queue action. */ + +static enum ofperr +decode_NXAST_RAW_POP_QUEUE(struct ofpbuf *out) +{ + ofpact_put_POP_QUEUE(out); + return 0; +} + +static void +encode_POP_QUEUE(const struct ofpact_null *null OVS_UNUSED, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) +{ + put_NXAST_POP_QUEUE(out); +} + +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 void +format_POP_QUEUE(const struct ofpact_null *a OVS_UNUSED, struct ds *s) +{ + ds_put_cstr(s, "pop_queue"); +} + +/* 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); + +static enum ofperr +decode_NXAST_RAW_FIN_TIMEOUT(const struct nx_action_fin_timeout *naft, + struct ofpbuf *out) +{ + struct ofpact_fin_timeout *oft; + + oft = ofpact_put_FIN_TIMEOUT(out); + oft->fin_idle_timeout = ntohs(naft->fin_idle_timeout); + oft->fin_hard_timeout = ntohs(naft->fin_hard_timeout); + return 0; +} + +static void +encode_FIN_TIMEOUT(const struct ofpact_fin_timeout *fin_timeout, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *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); +} + +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 void +format_FIN_TIMEOUT(const struct ofpact_fin_timeout *a, struct ds *s) +{ + ds_put_cstr(s, "fin_timeout("); + if (a->fin_idle_timeout) { + ds_put_format(s, "idle_timeout=%"PRIu16",", a->fin_idle_timeout); + } + if (a->fin_hard_timeout) { + ds_put_format(s, "hard_timeout=%"PRIu16",", a->fin_hard_timeout); + } + ds_chomp(s, ','); + ds_put_char(s, ')'); +} + +/* 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); + +static enum ofperr +decode_NXAST_RAW_RESUBMIT(uint16_t port, struct ofpbuf *out) +{ + struct ofpact_resubmit *resubmit; + + resubmit = ofpact_put_RESUBMIT(out); + resubmit->ofpact.raw = NXAST_RAW_RESUBMIT; + resubmit->in_port = u16_to_ofp(port); + resubmit->table_id = 0xff; + return 0; +} + +static enum ofperr +decode_NXAST_RAW_RESUBMIT_TABLE(const struct nx_action_resubmit *nar, + struct ofpbuf *out) +{ + struct ofpact_resubmit *resubmit; + + if (nar->pad[0] || nar->pad[1] || nar->pad[2]) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + + resubmit = ofpact_put_RESUBMIT(out); + resubmit->ofpact.raw = NXAST_RAW_RESUBMIT_TABLE; + resubmit->in_port = u16_to_ofp(ntohs(nar->in_port)); + resubmit->table_id = nar->table; + return 0; +} + +static void +encode_RESUBMIT(const struct ofpact_resubmit *resubmit, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) +{ + uint16_t in_port = ofp_to_u16(resubmit->in_port); + + if (resubmit->table_id == 0xff + && resubmit->ofpact.raw != NXAST_RAW_RESUBMIT_TABLE) { + put_NXAST_RESUBMIT(out, in_port); + } else { + struct nx_action_resubmit *nar = put_NXAST_RESUBMIT_TABLE(out); + nar->table = resubmit->table_id; + nar->in_port = htons(in_port); + } +} + +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; +} + +static void +format_RESUBMIT(const struct ofpact_resubmit *a, struct ds *s) +{ + if (a->in_port != OFPP_IN_PORT && a->table_id == 255) { + ds_put_cstr(s, "resubmit:"); + ofputil_format_port(a->in_port, s); + } else { + ds_put_format(s, "resubmit("); + if (a->in_port != OFPP_IN_PORT) { + ofputil_format_port(a->in_port, s); + } + ds_put_char(s, ','); + if (a->table_id != 255) { + ds_put_format(s, "%"PRIu8, a->table_id); + } + ds_put_char(s, ')'); + } +} + +/* 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); + +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_*. */ +static enum ofperr +decode_NXAST_RAW_LEARN(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; +} + +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)); +} + +static void +encode_LEARN(const struct ofpact_learn *learn, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) +{ + const struct ofpact_learn_spec *spec; + struct nx_action_learn *nal; + size_t start_ofs; + + start_ofs = ofpbuf_size(out); + nal = put_NXAST_LEARN(out); + 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(out, spec->n_bits | spec->dst_type | spec->src_type); + + if (spec->src_type == NX_LEARN_SRC_FIELD) { + put_u32(out, spec->src.field->nxm_header); + put_u16(out, spec->src.ofs); + } else { + size_t n_dst_bytes = 2 * DIV_ROUND_UP(spec->n_bits, 16); + uint8_t *bits = ofpbuf_put_zeros(out, 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(out, spec->dst.field->nxm_header); + put_u16(out, spec->dst.ofs); } } - 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); + if ((ofpbuf_size(out) - start_ofs) % 8) { + ofpbuf_put_zeros(out, 8 - (ofpbuf_size(out) - start_ofs) % 8); } + nal = ofpbuf_at_assert(out, start_ofs, sizeof *nal); + nal->len = htons(ofpbuf_size(out) - start_ofs); +} + +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 void +format_LEARN(const struct ofpact_learn *a, struct ds *s) +{ + learn_format(a, s); +} + +/* 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); + +static enum ofperr +decode_NXAST_RAW_MULTIPATH(const struct nx_action_multipath *nam, + struct ofpbuf *out) +{ + uint32_t n_links = ntohs(nam->max_link) + 1; + size_t min_n_bits = log_2_ceil(n_links); + struct ofpact_multipath *mp; + + mp = ofpact_put_MULTIPATH(out); + 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); +} + +static void +encode_MULTIPATH(const struct ofpact_multipath *mp, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) +{ + struct nx_action_multipath *nam = put_NXAST_MULTIPATH(out); + + 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); +} + +static char * WARN_UNUSED_RESULT +parse_MULTIPATH(const char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + return multipath_parse(ofpact_put_MULTIPATH(ofpacts), arg); +} + +static void +format_MULTIPATH(const struct ofpact_multipath *a, struct ds *s) +{ + multipath_format(a, s); +} + +/* 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); + +static enum ofperr +decode_NXAST_RAW_NOTE(const struct nx_action_note *nan, struct ofpbuf *out) +{ + struct ofpact_note *note; + unsigned int length; + + length = ntohs(nan->len) - offsetof(struct nx_action_note, note); + note = ofpact_put(out, OFPACT_NOTE, + offsetof(struct ofpact_note, data) + length); + note->length = length; + memcpy(note->data, nan->note, length); + + return 0; +} + +static void +encode_NOTE(const struct ofpact_note *note, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) +{ + size_t start_ofs = ofpbuf_size(out); + struct nx_action_note *nan; + unsigned int remainder; + unsigned int len; + + put_NXAST_NOTE(out); + ofpbuf_set_size(out, ofpbuf_size(out) - sizeof nan->note); + + ofpbuf_put(out, note->data, note->length); + + len = ofpbuf_size(out) - start_ofs; + remainder = len % OFP_ACTION_ALIGN; + if (remainder) { + ofpbuf_put_zeros(out, OFP_ACTION_ALIGN - remainder); + } + nan = ofpbuf_at(out, start_ofs, sizeof *nan); + nan->len = htons(ofpbuf_size(out) - start_ofs); +} + +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 void +format_NOTE(const struct ofpact_note *a, struct ds *s) +{ + size_t i; + + ds_put_cstr(s, "note:"); + for (i = 0; i < a->length; i++) { + if (i) { + ds_put_char(s, '.'); + } + ds_put_format(s, "%02"PRIx8, a->data[i]); + } +} + +/* Exit action. */ + +static enum ofperr +decode_NXAST_RAW_EXIT(struct ofpbuf *out) +{ + ofpact_put_EXIT(out); + return 0; +} + +static void +encode_EXIT(const struct ofpact_null *null OVS_UNUSED, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) +{ + put_NXAST_EXIT(out); +} + +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 +format_EXIT(const struct ofpact_null *a OVS_UNUSED, struct ds *s) +{ + ds_put_cstr(s, "exit"); +} + +/* 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); + +static enum ofperr +decode_NXAST_RAW_SAMPLE(const struct nx_action_sample *nas, struct ofpbuf *out) +{ + struct ofpact_sample *sample; + + sample = ofpact_put_SAMPLE(out); + sample->probability = ntohs(nas->probability); + sample->collector_set_id = ntohl(nas->collector_set_id); + sample->obs_domain_id = ntohl(nas->obs_domain_id); + sample->obs_point_id = ntohl(nas->obs_point_id); + + if (sample->probability == 0) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + + return 0; +} + +static void +encode_SAMPLE(const struct ofpact_sample *sample, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) +{ + struct nx_action_sample *nas; + + nas = put_NXAST_SAMPLE(out); + nas->probability = htons(sample->probability); + nas->collector_set_id = htonl(sample->collector_set_id); + nas->obs_domain_id = htonl(sample->obs_domain_id); + nas->obs_point_id = htonl(sample->obs_point_id); +} + +/* 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 void +format_SAMPLE(const struct ofpact_sample *a, struct ds *s) +{ + ds_put_format(s, "sample(probability=%"PRIu16",collector_set_id=%"PRIu32 + ",obs_domain_id=%"PRIu32",obs_point_id=%"PRIu32")", + a->probability, a->collector_set_id, + a->obs_domain_id, a->obs_point_id); +} + +/* Meter instruction. */ + +static void +encode_METER(const struct ofpact_meter *meter, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + if (ofp_version >= OFP13_VERSION) { + instruction_put_OFPIT13_METER(out)->meter_id = htonl(meter->meter_id); + } +} + +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 void +format_METER(const struct ofpact_meter *a, struct ds *s) +{ + ds_put_format(s, "meter:%"PRIu32, a->meter_id); +} + +/* Clear-Actions instruction. */ + +static void +encode_CLEAR_ACTIONS(const struct ofpact_null *null OVS_UNUSED, + enum ofp_version ofp_version OVS_UNUSED, + struct ofpbuf *out OVS_UNUSED) +{ + if (ofp_version > OFP10_VERSION) { + instruction_put_OFPIT11_CLEAR_ACTIONS(out); + } +} + +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 void +format_CLEAR_ACTIONS(const struct ofpact_null *a OVS_UNUSED, struct ds *s) +{ + ds_put_cstr(s, "clear_actions"); +} + +/* Write-Actions instruction. */ + +static void +encode_WRITE_ACTIONS(const struct ofpact_nest *actions, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + if (ofp_version > OFP10_VERSION) { + const size_t ofs = ofpbuf_size(out); + + instruction_put_OFPIT11_WRITE_ACTIONS(out); + ofpacts_put_openflow_actions(actions->actions, + ofpact_nest_get_action_len(actions), + out, ofp_version); + ofpacts_update_instruction_actions(out, ofs); + } +} + +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 void +format_WRITE_ACTIONS(const struct ofpact_nest *a, struct ds *s) +{ + ds_put_cstr(s, "write_actions("); + ofpacts_format(a->actions, ofpact_nest_get_action_len(a), s); + ds_put_char(s, ')'); +} + +/* 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); + +static enum ofperr +decode_NXAST_RAW_WRITE_METADATA(const struct nx_action_write_metadata *nawm, + struct ofpbuf *out) +{ + struct ofpact_metadata *om; + + if (!is_all_zeros(nawm->zeros, sizeof nawm->zeros)) { + return OFPERR_NXBRC_MUST_BE_ZERO; + } + + om = ofpact_put_WRITE_METADATA(out); + om->metadata = nawm->metadata; + om->mask = nawm->mask; + + return 0; +} + +static void +encode_WRITE_METADATA(const struct ofpact_metadata *metadata, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + if (ofp_version == OFP10_VERSION) { + struct nx_action_write_metadata *nawm; + + nawm = put_NXAST_WRITE_METADATA(out); + nawm->metadata = metadata->metadata; + nawm->mask = metadata->mask; + } else { + struct ofp11_instruction_write_metadata *oiwm; + + oiwm = instruction_put_OFPIT11_WRITE_METADATA(out); + oiwm->metadata = metadata->metadata; + oiwm->metadata_mask = metadata->mask; + } +} + +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 void +format_WRITE_METADATA(const struct ofpact_metadata *a, struct ds *s) +{ + ds_put_format(s, "write_metadata:%#"PRIx64, ntohll(a->metadata)); + if (a->mask != OVS_BE64_MAX) { + ds_put_format(s, "/%#"PRIx64, ntohll(a->mask)); + } +} + +/* Goto-Table instruction. */ + +static void +encode_GOTO_TABLE(const struct ofpact_goto_table *goto_table, + enum ofp_version ofp_version, struct ofpbuf *out) +{ + if (ofp_version == OFP10_VERSION) { + struct nx_action_resubmit *nar; + + nar = put_NXAST_RESUBMIT_TABLE(out); + nar->table = goto_table->table_id; + nar->in_port = htons(ofp_to_u16(OFPP_IN_PORT)); + } else { + struct ofp11_instruction_goto_table *oigt; + + oigt = instruction_put_OFPIT11_GOTO_TABLE(out); + oigt->table_id = goto_table->table_id; + memset(oigt->pad, 0, sizeof oigt->pad); + } +} + +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 void +format_GOTO_TABLE(const struct ofpact_goto_table *a, struct ds *s) +{ + ds_put_format(s, "goto_table:%"PRIu8, a->table_id); +} + +static void +log_bad_action(const struct ofp_action_header *actions, size_t max_actions, + const struct ofp_action_header *bad_action, enum ofperr error) +{ + if (!VLOG_DROP_WARN(&rl)) { + struct ds s; + + ds_init(&s); + ds_put_hex_dump(&s, actions, max_actions * OFP_ACTION_ALIGN, 0, false); + VLOG_WARN("bad action at offset %#"PRIxPTR" (%s):\n%s", + (char *)bad_action - (char *)actions, + ofperr_get_name(error), ds_cstr(&s)); + ds_destroy(&s); + } +} + +static enum ofperr +ofpacts_decode(const void *actions, size_t actions_len, + enum ofp_version ofp_version, struct ofpbuf *ofpacts) +{ + struct ofpbuf openflow; + + ofpbuf_use_const(&openflow, actions, actions_len); + while (ofpbuf_size(&openflow)) { + const struct ofp_action_header *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); + } + + if (error) { + log_bad_action(actions, actions_len * 8, action, error); + return error; + } + } + + ofpact_pad(ofpacts); + return 0; +} + +static enum ofperr +ofpacts_pull_openflow_actions__(struct ofpbuf *openflow, + unsigned int actions_len, + enum ofp_version version, + uint32_t allowed_ovsinsts, + struct ofpbuf *ofpacts) +{ + const struct ofp_action_header *actions; + enum ofperr error; + + ofpbuf_clear(ofpacts); + + if (actions_len % OFP_ACTION_ALIGN != 0) { + VLOG_WARN_RL(&rl, "OpenFlow message actions length %u is not a " + "multiple of %d", actions_len, OFP_ACTION_ALIGN); + return OFPERR_OFPBRC_BAD_LEN; + } + + actions = ofpbuf_try_pull(openflow, actions_len); + if (actions == NULL) { + VLOG_WARN_RL(&rl, "OpenFlow message actions length %u exceeds " + "remaining message length (%"PRIu32")", + actions_len, ofpbuf_size(openflow)); + return OFPERR_OFPBRC_BAD_LEN; + } + + error = ofpacts_decode(actions, actions_len, version, ofpacts); + if (error) { + ofpbuf_clear(ofpacts); + return error; + } + + error = ofpacts_verify(ofpbuf_data(ofpacts), ofpbuf_size(ofpacts), + allowed_ovsinsts); + if (error) { + ofpbuf_clear(ofpacts); + } + return error; +} + +/* Attempts to convert 'actions_len' bytes of OpenFlow actions from the + * front of 'openflow' into ofpacts. On success, replaces any existing content + * in 'ofpacts' by the converted ofpacts; on failure, clears 'ofpacts'. + * Returns 0 if successful, otherwise an OpenFlow error. + * + * Actions are processed according to their OpenFlow version which + * is provided in the 'version' parameter. + * + * In most places in OpenFlow, actions appear encapsulated in instructions, so + * you should call ofpacts_pull_openflow_instructions() instead of this + * function. + * + * The parsed actions are valid generically, but they may not be valid in a + * specific context. For example, port numbers up to OFPP_MAX are valid + * generically, but specific datapaths may only support port numbers in a + * smaller range. Use ofpacts_check() to additional check whether actions are + * valid in a specific context. */ +enum ofperr +ofpacts_pull_openflow_actions(struct ofpbuf *openflow, + unsigned int actions_len, + enum ofp_version version, + struct ofpbuf *ofpacts) +{ + return ofpacts_pull_openflow_actions__(openflow, actions_len, version, + 1u << OVSINST_OFPIT11_APPLY_ACTIONS, + ofpacts); +} + +/* OpenFlow 1.1 actions. */ + + /* True if an action sets the value of a field * in a way that is compatibile with the action set. * False otherwise. */ @@ -1499,15 +4433,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 struct ofp_action_header *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; @@ -1522,36 +4456,9 @@ ofpacts_from_openflow11_for_action_set(const union ofp_action *in, return 0; } - /* OpenFlow 1.1 instructions. */ -#define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) \ - static inline const struct STRUCT * OVS_UNUSED \ - instruction_get_##ENUM(const struct ofp11_instruction *inst)\ - { \ - ovs_assert(inst->type == htons(ENUM)); \ - return ALIGNED_CAST(struct STRUCT *, inst); \ - } \ - \ - static inline void OVS_UNUSED \ - instruction_init_##ENUM(struct STRUCT *s) \ - { \ - memset(s, 0, sizeof *s); \ - s->type = htons(ENUM); \ - s->len = htons(sizeof *s); \ - } \ - \ - static inline struct STRUCT * OVS_UNUSED \ - instruction_put_##ENUM(struct ofpbuf *buf) \ - { \ - struct STRUCT *s = ofpbuf_put_uninit(buf, sizeof *s); \ - instruction_init_##ENUM(s); \ - return s; \ - } -OVS_INSTRUCTIONS -#undef DEFINE_INST - struct instruction_type_info { enum ovs_instruction_type type; const char *name; @@ -1714,14 +4621,13 @@ ovsinst_bitmap_to_openflow(uint32_t ovsinst_bitmap, enum ofp_version version) * with the given 'version' (OFP11_VERSION or later) into a bitmap whose bits * correspond to OVSINST_* values, and returns the result. */ uint32_t -ovsinst_bitmap_from_openflow(ovs_be32 ofpit_bitmap_, enum ofp_version version) +ovsinst_bitmap_from_openflow(ovs_be32 ofpit_bitmap, enum ofp_version version) { - uint32_t ofpit_bitmap = ntohl(ofpit_bitmap_); uint32_t ovsinst_bitmap = 0; const struct ovsinst_map *x; for (x = get_ovsinst_map(version); x->ofpit >= 0; x++) { - if (ofpit_bitmap & (1u << x->ofpit)) { + if (ofpit_bitmap & htonl(1u << x->ofpit)) { ovsinst_bitmap |= 1u << x->ovsinst; } } @@ -1815,11 +4721,11 @@ 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) + const struct ofp_action_header **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 = ALIGNED_CAST(const struct ofp_action_header *, inst + 1); + *actions_len = ntohs(inst->len) - sizeof *inst; } enum ofperr @@ -1876,12 +4782,12 @@ ofpacts_pull_openflow_instructions(struct ofpbuf *openflow, om->meter_id = ntohl(oim->meter_id); } if (insts[OVSINST_OFPIT11_APPLY_ACTIONS]) { - const union ofp_action *actions; - size_t max_actions; + const struct ofp_action_header *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; } @@ -1893,18 +4799,18 @@ 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; + const struct ofp_action_header *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; } @@ -1940,6 +4846,22 @@ exit: } return error; } + +/* Update the length of the instruction that begins at offset 'ofs' within + * 'openflow' and contains nested actions that extend to the end of 'openflow'. + * If the instruction contains no nested actions, deletes it entirely. */ +static void +ofpacts_update_instruction_actions(struct ofpbuf *openflow, size_t ofs) +{ + struct ofp11_instruction_actions *oia; + + oia = ofpbuf_at_assert(openflow, ofs, sizeof *oia); + if (ofpbuf_size(openflow) > ofs + sizeof *oia) { + oia->len = htons(ofpbuf_size(openflow) - ofs); + } else { + ofpbuf_set_size(openflow, ofs); + } +} /* Checks that 'port' is a valid output port for OFPACT_OUTPUT, given that the * switch will never have more than 'max_ports' ports. Returns 0 if 'port' is @@ -2280,7 +5202,7 @@ ofpacts_check_consistency(struct ofpact ofpacts[], size_t ofpacts_len, /* Verifies that the 'ofpacts_len' bytes of actions in 'ofpacts' are * in the appropriate order as defined by the OpenFlow spec. */ -enum ofperr +static enum ofperr ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len, uint32_t allowed_ovsinsts) { @@ -2322,747 +5244,22 @@ ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len, return 0; } -/* Converting ofpacts to Nicira OpenFlow extensions. */ +/* Converting ofpacts to OpenFlow. */ static void -ofpact_output_reg_to_nxast(const struct ofpact_output_reg *output_reg, - struct ofpbuf *out) -{ - struct nx_action_output_reg *naor = ofputil_put_NXAST_OUTPUT_REG(out); - - naor->ofs_nbits = nxm_encode_ofs_nbits(output_reg->src.ofs, - output_reg->src.n_bits); - naor->src = htonl(output_reg->src.field->nxm_header); - naor->max_len = htons(output_reg->max_len); -} - -static void -ofpact_resubmit_to_nxast(const struct ofpact_resubmit *resubmit, - struct ofpbuf *out) -{ - struct nx_action_resubmit *nar; - - if (resubmit->table_id == 0xff - && resubmit->ofpact.compat != OFPUTIL_NXAST_RESUBMIT_TABLE) { - nar = ofputil_put_NXAST_RESUBMIT(out); - } else { - nar = ofputil_put_NXAST_RESUBMIT_TABLE(out); - nar->table = resubmit->table_id; - } - nar->in_port = htons(ofp_to_u16(resubmit->in_port)); -} - -static void -ofpact_set_tunnel_to_nxast(const struct ofpact_tunnel *tunnel, - struct ofpbuf *out) -{ - 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); - } else { - ofputil_put_NXAST_SET_TUNNEL64(out)->tun_id = htonll(tun_id); - } -} - -static void -ofpact_write_metadata_to_nxast(const struct ofpact_metadata *om, - struct ofpbuf *out) -{ - struct nx_action_write_metadata *nawm; - - nawm = ofputil_put_NXAST_WRITE_METADATA(out); - nawm->metadata = om->metadata; - nawm->mask = om->mask; -} - -static void -ofpact_note_to_nxast(const struct ofpact_note *note, struct ofpbuf *out) -{ - size_t start_ofs = ofpbuf_size(out); - struct nx_action_note *nan; - unsigned int remainder; - unsigned int len; - - ofputil_put_NXAST_NOTE(out); - ofpbuf_set_size(out, ofpbuf_size(out) - sizeof nan->note); - - ofpbuf_put(out, note->data, note->length); - - len = ofpbuf_size(out) - start_ofs; - remainder = len % OFP_ACTION_ALIGN; - if (remainder) { - ofpbuf_put_zeros(out, OFP_ACTION_ALIGN - remainder); - } - nan = ofpbuf_at(out, start_ofs, sizeof *nan); - nan->len = htons(ofpbuf_size(out) - start_ofs); -} - -static void -ofpact_controller_to_nxast(const struct ofpact_controller *oc, - struct ofpbuf *out) -{ - struct nx_action_controller *nac; - - nac = ofputil_put_NXAST_CONTROLLER(out); - nac->max_len = htons(oc->max_len); - nac->controller_id = htons(oc->controller_id); - nac->reason = oc->reason; -} - -static void -ofpact_dec_ttl_to_nxast(const struct ofpact_cnt_ids *oc_ids, - struct ofpbuf *out) -{ - 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); - int ids_len = ROUND_UP(2 * oc_ids->n_controllers, OFP_ACTION_ALIGN); - ovs_be16 *ids; - size_t i; - - nac_ids->len = htons(ntohs(nac_ids->len) + ids_len); - nac_ids->n_controllers = htons(oc_ids->n_controllers); - - ids = ofpbuf_put_zeros(out, ids_len); - for (i = 0; i < oc_ids->n_controllers; i++) { - ids[i] = htons(oc_ids->cnt_ids[i]); - } - } -} - -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); - naft->fin_idle_timeout = htons(fin_timeout->fin_idle_timeout); - naft->fin_hard_timeout = htons(fin_timeout->fin_hard_timeout); -} - -static void -ofpact_sample_to_nxast(const struct ofpact_sample *os, - struct ofpbuf *out) -{ - struct nx_action_sample *nas; - - nas = ofputil_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); - nas->obs_point_id = htonl(os->obs_point_id); -} - -static void -ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out) +encode_ofpact(const struct ofpact *a, enum ofp_version ofp_version, + struct ofpbuf *out) { switch (a->type) { - case OFPACT_CONTROLLER: - ofpact_controller_to_nxast(ofpact_get_CONTROLLER(a), out); - break; - - case OFPACT_OUTPUT_REG: - ofpact_output_reg_to_nxast(ofpact_get_OUTPUT_REG(a), out); - break; - - case OFPACT_BUNDLE: - bundle_to_nxast(ofpact_get_BUNDLE(a), out); - break; - - case OFPACT_REG_MOVE: - nxm_reg_move_to_nxast(ofpact_get_REG_MOVE(a), out); - break; - - case OFPACT_REG_LOAD: - nxm_reg_load_to_nxast(ofpact_get_REG_LOAD(a), out); - break; - - case OFPACT_STACK_PUSH: - nxm_stack_push_to_nxast(ofpact_get_STACK_PUSH(a), out); - break; - - case OFPACT_STACK_POP: - nxm_stack_pop_to_nxast(ofpact_get_STACK_POP(a), out); - break; - - case OFPACT_DEC_TTL: - ofpact_dec_ttl_to_nxast(ofpact_get_DEC_TTL(a), out); - break; - - case OFPACT_SET_MPLS_LABEL: - ofputil_put_NXAST_SET_MPLS_LABEL(out)->label - = 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; - break; - - case OFPACT_SET_MPLS_TTL: - ofputil_put_NXAST_SET_MPLS_TTL(out)->ttl - = ofpact_get_SET_MPLS_TTL(a)->ttl; - break; - - case OFPACT_DEC_MPLS_TTL: - ofputil_put_NXAST_DEC_MPLS_TTL(out); - break; - - case OFPACT_SET_TUNNEL: - ofpact_set_tunnel_to_nxast(ofpact_get_SET_TUNNEL(a), out); - break; - - case OFPACT_WRITE_METADATA: - ofpact_write_metadata_to_nxast(ofpact_get_WRITE_METADATA(a), out); - break; - - case OFPACT_SET_QUEUE: - ofputil_put_NXAST_SET_QUEUE(out)->queue_id - = htonl(ofpact_get_SET_QUEUE(a)->queue_id); - break; - - case OFPACT_POP_QUEUE: - ofputil_put_NXAST_POP_QUEUE(out); - break; - - case OFPACT_FIN_TIMEOUT: - ofpact_fin_timeout_to_nxast(ofpact_get_FIN_TIMEOUT(a), out); - break; - - case OFPACT_RESUBMIT: - ofpact_resubmit_to_nxast(ofpact_get_RESUBMIT(a), out); - break; - - case OFPACT_LEARN: - learn_to_nxast(ofpact_get_LEARN(a), out); - break; - - case OFPACT_MULTIPATH: - multipath_to_nxast(ofpact_get_MULTIPATH(a), out); - break; - - case OFPACT_NOTE: - ofpact_note_to_nxast(ofpact_get_NOTE(a), out); - break; - - case OFPACT_EXIT: - ofputil_put_NXAST_EXIT(out); - break; - - case OFPACT_PUSH_MPLS: - ofputil_put_NXAST_PUSH_MPLS(out)->ethertype = - ofpact_get_PUSH_MPLS(a)->ethertype; - break; - - case OFPACT_POP_MPLS: - ofputil_put_NXAST_POP_MPLS(out)->ethertype = - ofpact_get_POP_MPLS(a)->ethertype; - break; - - case OFPACT_SAMPLE: - ofpact_sample_to_nxast(ofpact_get_SAMPLE(a), out); - break; - - case OFPACT_GROUP: - case OFPACT_OUTPUT: - case OFPACT_ENQUEUE: - case OFPACT_SET_VLAN_VID: - case OFPACT_SET_VLAN_PCP: - case OFPACT_STRIP_VLAN: - case OFPACT_PUSH_VLAN: - 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_IP_TTL: - case OFPACT_SET_L4_SRC_PORT: - case OFPACT_SET_L4_DST_PORT: - case OFPACT_WRITE_ACTIONS: - case OFPACT_CLEAR_ACTIONS: - case OFPACT_GOTO_TABLE: - case OFPACT_METER: - case OFPACT_SET_FIELD: +#define OFPACT(ENUM, STRUCT, MEMBER, NAME) \ + case OFPACT_##ENUM: \ + return encode_##ENUM(ofpact_get_##ENUM(a), ofp_version, out); + OFPACTS +#undef OFPACT + default: OVS_NOT_REACHED(); } } - -/* Converting ofpacts to OpenFlow 1.0. */ - -static void -ofpact_output_to_openflow10(const struct ofpact_output *output, - struct ofpbuf *out) -{ - struct ofp10_action_output *oao; - - oao = ofputil_put_OFPAT10_OUTPUT(out); - oao->port = htons(ofp_to_u16(output->port)); - oao->max_len = htons(output->max_len); -} - -static void -ofpact_enqueue_to_openflow10(const struct ofpact_enqueue *enqueue, - struct ofpbuf *out) -{ - struct ofp10_action_enqueue *oae; - - oae = ofputil_put_OFPAT10_ENQUEUE(out); - oae->port = htons(ofp_to_u16(enqueue->port)); - oae->queue_id = htonl(enqueue->queue); -} - -static void -ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out) -{ - switch (a->type) { - case OFPACT_OUTPUT: - ofpact_output_to_openflow10(ofpact_get_OUTPUT(a), out); - break; - - case OFPACT_ENQUEUE: - ofpact_enqueue_to_openflow10(ofpact_get_ENQUEUE(a), 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); - break; - - case OFPACT_SET_VLAN_PCP: - ofputil_put_OFPAT10_SET_VLAN_PCP(out)->vlan_pcp - = ofpact_get_SET_VLAN_PCP(a)->vlan_pcp; - break; - - case OFPACT_STRIP_VLAN: - ofputil_put_OFPAT10_STRIP_VLAN(out); - break; - - case OFPACT_SET_ETH_SRC: - memcpy(ofputil_put_OFPAT10_SET_DL_SRC(out)->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, - 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; - break; - - case OFPACT_SET_IPV4_DST: - ofputil_put_OFPAT10_SET_NW_DST(out)->nw_addr - = 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; - 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); - 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); - break; - - case OFPACT_PUSH_VLAN: - /* PUSH is a side effect of a SET_VLAN_VID/PCP, which should - * follow this action. */ - break; - - case OFPACT_SET_IP_ECN: - case OFPACT_SET_IP_TTL: - case OFPACT_CLEAR_ACTIONS: - case OFPACT_WRITE_ACTIONS: - case OFPACT_GOTO_TABLE: - case OFPACT_METER: - /* XXX */ - break; - - case OFPACT_GROUP: - break; - - case OFPACT_SET_FIELD: - set_field_to_openflow(ofpact_get_SET_FIELD(a), 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_DEC_TTL: - case OFPACT_SET_MPLS_LABEL: - case OFPACT_SET_MPLS_TC: - case OFPACT_SET_MPLS_TTL: - case OFPACT_DEC_MPLS_TTL: - case OFPACT_SET_TUNNEL: - case OFPACT_WRITE_METADATA: - case OFPACT_SET_QUEUE: - case OFPACT_POP_QUEUE: - case OFPACT_FIN_TIMEOUT: - case OFPACT_RESUBMIT: - case OFPACT_LEARN: - case OFPACT_MULTIPATH: - case OFPACT_NOTE: - case OFPACT_EXIT: - case OFPACT_PUSH_MPLS: - case OFPACT_POP_MPLS: - case OFPACT_SAMPLE: - ofpact_to_nxast(a, out); - break; - } -} - -/* Converting ofpacts to OpenFlow 1.1. */ - -static void -ofpact_output_to_openflow11(const struct ofpact_output *output, - struct ofpbuf *out) -{ - struct ofp11_action_output *oao; - - oao = ofputil_put_OFPAT11_OUTPUT(out); - oao->port = ofputil_port_to_ofp11(output->port); - oao->max_len = htons(output->max_len); -} - -static void -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); - } else { - ofpact_dec_ttl_to_nxast(dec_ttl, out); - } -} - -static void -ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out) -{ - switch (a->type) { - case OFPACT_OUTPUT: - return ofpact_output_to_openflow11(ofpact_get_OUTPUT(a), out); - - case OFPACT_ENQUEUE: - /* XXX */ - break; - - case OFPACT_SET_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); - } - ofputil_put_OFPAT11_SET_VLAN_VID(out)->vlan_vid - = htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid); - break; - - 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); - } - 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); - break; - - case OFPACT_PUSH_VLAN: - /* XXX ETH_TYPE_VLAN_8021AD case */ - ofputil_put_OFPAT11_PUSH_VLAN(out)->ethertype = - 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); - 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); - 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); - break; - - case OFPACT_SET_IPV4_SRC: - ofputil_put_OFPAT11_SET_NW_SRC(out)->nw_addr - = ofpact_get_SET_IPV4_SRC(a)->ipv4; - break; - - case OFPACT_SET_IPV4_DST: - ofputil_put_OFPAT11_SET_NW_DST(out)->nw_addr - = ofpact_get_SET_IPV4_DST(a)->ipv4; - break; - - case OFPACT_SET_IP_DSCP: - ofputil_put_OFPAT11_SET_NW_TOS(out)->nw_tos - = ofpact_get_SET_IP_DSCP(a)->dscp; - break; - - case OFPACT_SET_IP_ECN: - ofputil_put_OFPAT11_SET_NW_ECN(out)->nw_ecn - = ofpact_get_SET_IP_ECN(a)->ecn; - break; - - case OFPACT_SET_IP_TTL: - ofputil_put_OFPAT11_SET_NW_TTL(out)->nw_ttl - = 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); - 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); - 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; - break; - - case OFPACT_SET_MPLS_TC: - ofputil_put_OFPAT11_SET_MPLS_TC(out)->mpls_tc - = ofpact_get_SET_MPLS_TC(a)->tc; - break; - - case OFPACT_SET_MPLS_TTL: - ofputil_put_OFPAT11_SET_MPLS_TTL(out)->mpls_ttl - = ofpact_get_SET_MPLS_TTL(a)->ttl; - break; - - case OFPACT_DEC_MPLS_TTL: - ofputil_put_OFPAT11_DEC_MPLS_TTL(out); - break; - - case OFPACT_WRITE_METADATA: - /* OpenFlow 1.1 uses OFPIT_WRITE_METADATA to express this action. */ - break; - - case OFPACT_PUSH_MPLS: - ofputil_put_OFPAT11_PUSH_MPLS(out)->ethertype = - ofpact_get_PUSH_MPLS(a)->ethertype; - break; - - case OFPACT_POP_MPLS: - ofputil_put_OFPAT11_POP_MPLS(out)->ethertype = - ofpact_get_POP_MPLS(a)->ethertype; - - break; - - case OFPACT_CLEAR_ACTIONS: - case OFPACT_WRITE_ACTIONS: - case OFPACT_GOTO_TABLE: - case OFPACT_METER: - OVS_NOT_REACHED(); - - case OFPACT_GROUP: - ofputil_put_OFPAT11_GROUP(out)->group_id = - htonl(ofpact_get_GROUP(a)->group_id); - break; - - case OFPACT_SET_FIELD: - set_field_to_openflow(ofpact_get_SET_FIELD(a), 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: - case OFPACT_LEARN: - case OFPACT_MULTIPATH: - case OFPACT_NOTE: - case OFPACT_EXIT: - case OFPACT_SAMPLE: - ofpact_to_nxast(a, out); - 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 @@ -3075,29 +5272,17 @@ 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); + encode_ofpact(a, ofp_version, openflow); } return ofpbuf_size(openflow) - start_size; } -static void -ofpacts_update_instruction_actions(struct ofpbuf *openflow, size_t ofs) +static enum ovs_instruction_type +ofpact_is_apply_actions(const struct ofpact *a) { - struct ofp11_instruction_actions *oia; - - /* Update the instruction's length (or, if it's empty, delete it). */ - oia = ofpbuf_at_assert(openflow, ofs, sizeof *oia); - if (ofpbuf_size(openflow) > ofs + sizeof *oia) { - oia->len = htons(ofpbuf_size(openflow) - ofs); - } else { - ofpbuf_set_size(openflow, ofs); - } + return (ovs_instruction_type_from_ofpact_type(a->type) + == OVSINST_OFPIT11_APPLY_ACTIONS); } void @@ -3106,6 +5291,7 @@ ofpacts_put_openflow_instructions(const struct ofpact ofpacts[], struct ofpbuf *openflow, enum ofp_version ofp_version) { + const struct ofpact *end = ofpact_end(ofpacts, ofpacts_len); const struct ofpact *a; if (ofp_version == OFP10_VERSION) { @@ -3114,80 +5300,20 @@ ofpacts_put_openflow_instructions(const struct ofpact ofpacts[], return; } - OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { - switch (ovs_instruction_type_from_ofpact_type(a->type)) { - case OVSINST_OFPIT11_CLEAR_ACTIONS: - instruction_put_OFPIT11_CLEAR_ACTIONS(openflow); - break; - - case OVSINST_OFPIT11_GOTO_TABLE: { - struct ofp11_instruction_goto_table *oigt; - oigt = instruction_put_OFPIT11_GOTO_TABLE(openflow); - oigt->table_id = ofpact_get_GOTO_TABLE(a)->table_id; - memset(oigt->pad, 0, sizeof oigt->pad); - break; - } - - case OVSINST_OFPIT11_WRITE_METADATA: { - const struct ofpact_metadata *om; - struct ofp11_instruction_write_metadata *oiwm; - - om = ofpact_get_WRITE_METADATA(a); - oiwm = instruction_put_OFPIT11_WRITE_METADATA(openflow); - oiwm->metadata = om->metadata; - oiwm->metadata_mask = om->mask; - break; - } - - case OVSINST_OFPIT13_METER: - if (ofp_version >= OFP13_VERSION) { - const struct ofpact_meter *om; - struct ofp13_instruction_meter *oim; - - om = ofpact_get_METER(a); - oim = instruction_put_OFPIT13_METER(openflow); - oim->meter_id = htonl(om->meter_id); - } - break; - - case OVSINST_OFPIT11_APPLY_ACTIONS: { - const size_t ofs = ofpbuf_size(openflow); - const size_t ofpacts_len_left = - (uint8_t*)ofpact_end(ofpacts, ofpacts_len) - (uint8_t*)a; - const struct ofpact *action; - const struct ofpact *processed = a; + a = ofpacts; + while (a < end) { + if (ofpact_is_apply_actions(a)) { + size_t ofs = ofpbuf_size(openflow); instruction_put_OFPIT11_APPLY_ACTIONS(openflow); - OFPACT_FOR_EACH(action, a, ofpacts_len_left) { - if (ovs_instruction_type_from_ofpact_type(action->type) - != OVSINST_OFPIT11_APPLY_ACTIONS) { - break; - } - if (ofp_version == OFP11_VERSION) { - ofpact_to_openflow11(action, openflow); - } else { - ofpact_to_openflow12(action, openflow); - } - processed = action; - } + do { + encode_ofpact(a, ofp_version, openflow); + a = ofpact_next(a); + } while (a < end && ofpact_is_apply_actions(a)); ofpacts_update_instruction_actions(openflow, ofs); - a = processed; - break; - } - - case OVSINST_OFPIT11_WRITE_ACTIONS: { - const size_t ofs = ofpbuf_size(openflow); - const struct ofpact_nest *on; - - on = ofpact_get_WRITE_ACTIONS(a); - instruction_put_OFPIT11_WRITE_ACTIONS(openflow); - ofpacts_put_openflow_actions(on->actions, - ofpact_nest_get_action_len(on), - openflow, ofp_version); - ofpacts_update_instruction_actions(openflow, ofs); - - break; - } + } else { + encode_ofpact(a, ofp_version, openflow); + a = ofpact_next(a); } } } @@ -3469,357 +5595,18 @@ ofpacts_get_meter(const struct ofpact ofpacts[], size_t ofpacts_len) /* Formatting ofpacts. */ -static void -print_note(const struct ofpact_note *note, struct ds *string) -{ - size_t i; - - ds_put_cstr(string, "note:"); - for (i = 0; i < note->length; i++) { - if (i) { - ds_put_char(string, '.'); - } - ds_put_format(string, "%02"PRIx8, note->data[i]); - } -} - -static void -print_dec_ttl(const struct ofpact_cnt_ids *ids, - struct ds *s) -{ - size_t i; - - ds_put_cstr(s, "dec_ttl"); - if (ids->ofpact.compat == OFPUTIL_NXAST_DEC_TTL_CNT_IDS) { - ds_put_cstr(s, "("); - for (i = 0; i < ids->n_controllers; i++) { - if (i) { - ds_put_cstr(s, ","); - } - ds_put_format(s, "%"PRIu16, ids->cnt_ids[i]); - } - ds_put_cstr(s, ")"); - } -} - -static void -print_fin_timeout(const struct ofpact_fin_timeout *fin_timeout, - struct ds *s) -{ - ds_put_cstr(s, "fin_timeout("); - if (fin_timeout->fin_idle_timeout) { - ds_put_format(s, "idle_timeout=%"PRIu16",", - fin_timeout->fin_idle_timeout); - } - if (fin_timeout->fin_hard_timeout) { - ds_put_format(s, "hard_timeout=%"PRIu16",", - fin_timeout->fin_hard_timeout); - } - ds_chomp(s, ','); - ds_put_char(s, ')'); -} - static void ofpact_format(const struct ofpact *a, struct ds *s) { - const struct ofpact_enqueue *enqueue; - const struct ofpact_resubmit *resubmit; - const struct ofpact_controller *controller; - const struct ofpact_metadata *metadata; - const struct ofpact_tunnel *tunnel; - const struct ofpact_sample *sample; - const struct ofpact_set_field *set_field; - const struct mf_field *mf; - ofp_port_t port; - switch (a->type) { - case OFPACT_OUTPUT: - port = ofpact_get_OUTPUT(a)->port; - if (ofp_to_u16(port) < ofp_to_u16(OFPP_MAX)) { - ds_put_format(s, "output:%"PRIu16, port); - } else { - ofputil_format_port(port, s); - if (port == OFPP_CONTROLLER) { - ds_put_format(s, ":%"PRIu16, ofpact_get_OUTPUT(a)->max_len); - } - } - break; - - case OFPACT_CONTROLLER: - controller = ofpact_get_CONTROLLER(a); - if (controller->reason == OFPR_ACTION && - controller->controller_id == 0) { - ds_put_format(s, "CONTROLLER:%"PRIu16, - ofpact_get_CONTROLLER(a)->max_len); - } else { - enum ofp_packet_in_reason reason = controller->reason; - - ds_put_cstr(s, "controller("); - if (reason != OFPR_ACTION) { - char reasonbuf[OFPUTIL_PACKET_IN_REASON_BUFSIZE]; - - ds_put_format(s, "reason=%s,", - ofputil_packet_in_reason_to_string( - reason, reasonbuf, sizeof reasonbuf)); - } - if (controller->max_len != UINT16_MAX) { - ds_put_format(s, "max_len=%"PRIu16",", controller->max_len); - } - if (controller->controller_id != 0) { - ds_put_format(s, "id=%"PRIu16",", controller->controller_id); - } - ds_chomp(s, ','); - ds_put_char(s, ')'); - } - break; - - case OFPACT_ENQUEUE: - enqueue = ofpact_get_ENQUEUE(a); - ds_put_format(s, "enqueue:"); - ofputil_format_port(enqueue->port, s); - ds_put_format(s, ":%"PRIu32, enqueue->queue); - break; - - case OFPACT_OUTPUT_REG: - ds_put_cstr(s, "output:"); - mf_format_subfield(&ofpact_get_OUTPUT_REG(a)->src, s); - break; - - case OFPACT_BUNDLE: - bundle_format(ofpact_get_BUNDLE(a), s); - break; - - 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)->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)->vlan_pcp); - break; - - case OFPACT_STRIP_VLAN: - ds_put_cstr(s, a->compat == OFPUTIL_OFPAT11_POP_VLAN - ? "pop_vlan" : "strip_vlan"); - break; - - case OFPACT_PUSH_VLAN: - /* XXX 802.1AD case*/ - ds_put_format(s, "push_vlan:%#"PRIx16, ETH_TYPE_VLAN_8021Q); - break; - - case OFPACT_SET_ETH_SRC: - ds_put_format(s, "mod_dl_src:"ETH_ADDR_FMT, - ETH_ADDR_ARGS(ofpact_get_SET_ETH_SRC(a)->mac)); - break; - - case OFPACT_SET_ETH_DST: - ds_put_format(s, "mod_dl_dst:"ETH_ADDR_FMT, - ETH_ADDR_ARGS(ofpact_get_SET_ETH_DST(a)->mac)); - break; - - case OFPACT_SET_IPV4_SRC: - ds_put_format(s, "mod_nw_src:"IP_FMT, - IP_ARGS(ofpact_get_SET_IPV4_SRC(a)->ipv4)); - break; - - case OFPACT_SET_IPV4_DST: - ds_put_format(s, "mod_nw_dst:"IP_FMT, - IP_ARGS(ofpact_get_SET_IPV4_DST(a)->ipv4)); - break; - - case OFPACT_SET_IP_DSCP: - ds_put_format(s, "mod_nw_tos:%d", ofpact_get_SET_IP_DSCP(a)->dscp); - break; - - case OFPACT_SET_IP_ECN: - ds_put_format(s, "mod_nw_ecn:%d", ofpact_get_SET_IP_ECN(a)->ecn); - break; - - case OFPACT_SET_IP_TTL: - ds_put_format(s, "mod_nw_ttl:%d", ofpact_get_SET_IP_TTL(a)->ttl); - break; - - case OFPACT_SET_L4_SRC_PORT: - ds_put_format(s, "mod_tp_src:%d", ofpact_get_SET_L4_SRC_PORT(a)->port); - break; - - case OFPACT_SET_L4_DST_PORT: - ds_put_format(s, "mod_tp_dst:%d", ofpact_get_SET_L4_DST_PORT(a)->port); - break; - - case OFPACT_REG_MOVE: - nxm_format_reg_move(ofpact_get_REG_MOVE(a), s); - break; - - case OFPACT_REG_LOAD: - nxm_format_reg_load(ofpact_get_REG_LOAD(a), s); - break; - - case OFPACT_SET_FIELD: - set_field = ofpact_get_SET_FIELD(a); - mf = set_field->field; - ds_put_format(s, "set_field:"); - mf_format(mf, &set_field->value, NULL, s); - ds_put_format(s, "->%s", mf->name); - break; - - case OFPACT_STACK_PUSH: - nxm_format_stack_push(ofpact_get_STACK_PUSH(a), s); - break; - - case OFPACT_STACK_POP: - nxm_format_stack_pop(ofpact_get_STACK_POP(a), s); - break; - - case OFPACT_DEC_TTL: - print_dec_ttl(ofpact_get_DEC_TTL(a), s); - break; - - case OFPACT_SET_MPLS_LABEL: - ds_put_format(s, "set_mpls_label(%"PRIu32")", - ntohl(ofpact_get_SET_MPLS_LABEL(a)->label)); - break; - - case OFPACT_SET_MPLS_TC: - ds_put_format(s, "set_mpls_ttl(%"PRIu8")", - ofpact_get_SET_MPLS_TC(a)->tc); - break; - - case OFPACT_SET_MPLS_TTL: - ds_put_format(s, "set_mpls_ttl(%"PRIu8")", - ofpact_get_SET_MPLS_TTL(a)->ttl); - break; - - case OFPACT_DEC_MPLS_TTL: - ds_put_cstr(s, "dec_mpls_ttl"); - break; - - case OFPACT_SET_TUNNEL: - 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" : ""), - tunnel->tun_id); - break; - - case OFPACT_SET_QUEUE: - ds_put_format(s, "set_queue:%"PRIu32, - ofpact_get_SET_QUEUE(a)->queue_id); - break; - - case OFPACT_POP_QUEUE: - ds_put_cstr(s, "pop_queue"); - break; - - case OFPACT_FIN_TIMEOUT: - print_fin_timeout(ofpact_get_FIN_TIMEOUT(a), s); - break; - - case OFPACT_RESUBMIT: - resubmit = ofpact_get_RESUBMIT(a); - if (resubmit->in_port != OFPP_IN_PORT && resubmit->table_id == 255) { - ds_put_cstr(s, "resubmit:"); - ofputil_format_port(resubmit->in_port, s); - } else { - ds_put_format(s, "resubmit("); - if (resubmit->in_port != OFPP_IN_PORT) { - ofputil_format_port(resubmit->in_port, s); - } - ds_put_char(s, ','); - if (resubmit->table_id != 255) { - ds_put_format(s, "%"PRIu8, resubmit->table_id); - } - ds_put_char(s, ')'); - } - break; - - case OFPACT_LEARN: - learn_format(ofpact_get_LEARN(a), s); - break; - - case OFPACT_MULTIPATH: - multipath_format(ofpact_get_MULTIPATH(a), s); - break; - - case OFPACT_NOTE: - print_note(ofpact_get_NOTE(a), s); - break; - - case OFPACT_PUSH_MPLS: - ds_put_format(s, "push_mpls:0x%04"PRIx16, - ntohs(ofpact_get_PUSH_MPLS(a)->ethertype)); - break; - - case OFPACT_POP_MPLS: - ds_put_format(s, "pop_mpls:0x%04"PRIx16, - ntohs(ofpact_get_POP_MPLS(a)->ethertype)); - break; - - case OFPACT_EXIT: - ds_put_cstr(s, "exit"); - break; - - case OFPACT_SAMPLE: - sample = ofpact_get_SAMPLE(a); - ds_put_format( - s, "sample(probability=%"PRIu16",collector_set_id=%"PRIu32 - ",obs_domain_id=%"PRIu32",obs_point_id=%"PRIu32")", - sample->probability, sample->collector_set_id, - sample->obs_domain_id, sample->obs_point_id); - break; - - case OFPACT_WRITE_ACTIONS: { - struct ofpact_nest *on = ofpact_get_WRITE_ACTIONS(a); - ds_put_format(s, "%s(", - ovs_instruction_name_from_type( - OVSINST_OFPIT11_WRITE_ACTIONS)); - ofpacts_format(on->actions, ofpact_nest_get_action_len(on), s); - ds_put_char(s, ')'); - break; - } - - case OFPACT_CLEAR_ACTIONS: - ds_put_format(s, "%s", - ovs_instruction_name_from_type( - OVSINST_OFPIT11_CLEAR_ACTIONS)); - break; - - case OFPACT_WRITE_METADATA: - metadata = ofpact_get_WRITE_METADATA(a); - ds_put_format(s, "%s:%#"PRIx64, - ovs_instruction_name_from_type( - OVSINST_OFPIT11_WRITE_METADATA), - ntohll(metadata->metadata)); - if (metadata->mask != OVS_BE64_MAX) { - ds_put_format(s, "/%#"PRIx64, ntohll(metadata->mask)); - } - break; - - case OFPACT_GOTO_TABLE: - ds_put_format(s, "%s:%"PRIu8, - ovs_instruction_name_from_type( - OVSINST_OFPIT11_GOTO_TABLE), - ofpact_get_GOTO_TABLE(a)->table_id); - break; - - case OFPACT_METER: - ds_put_format(s, "%s:%"PRIu32, - ovs_instruction_name_from_type(OVSINST_OFPIT13_METER), - ofpact_get_METER(a)->meter_id); - break; - - case OFPACT_GROUP: - ds_put_format(s, "group:%"PRIu32, - ofpact_get_GROUP(a)->group_id); - break; +#define OFPACT(ENUM, STRUCT, MEMBER, NAME) \ + case OFPACT_##ENUM: \ + format_##ENUM(ALIGNED_CAST(const struct STRUCT *, a), s); \ + break; + OFPACTS +#undef OFPACT + default: + OVS_NOT_REACHED(); } } @@ -3863,7 +5650,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; } @@ -3898,6 +5685,178 @@ ofpact_pad(struct ofpbuf *ofpacts) ofpbuf_put_zeros(ofpacts, pad); } } + + + + +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"); + } + + retval = ofpacts_verify(ofpbuf_data(ofpacts), ofpbuf_size(ofpacts), + (allow_instructions + ? (1u << N_OVS_INSTRUCTIONS) - 1 + : 1u << OVSINST_OFPIT11_APPLY_ACTIONS)); + 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) @@ -3909,3 +5868,296 @@ ofpact_name(enum ofpact_type type) } return ""; } + +/* Low-level action decoding and encoding functions. */ + +/* Everything needed to identify a particular OpenFlow action. */ +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. */ +}; + +/* Information about a particular OpenFlow action. */ +struct ofpact_raw_instance { + /* The action's identity. */ + struct ofpact_hdrs hdrs; + enum ofp_raw_action_type raw; + + /* Looking up the action. */ + struct hmap_node decode_node; /* Based on 'hdrs'. */ + struct hmap_node encode_node; /* Based on 'raw' + 'hdrs.ofp_version'. */ + + /* The action's encoded size. + * + * If this action is fixed-length, 'min_length' == 'max_length'. + * If it is variable length, then 'max_length' is ROUND_DOWN(UINT16_MAX, + * OFP_ACTION_ALIGN) == 65528. */ + unsigned short int min_length; + unsigned short int max_length; + + /* For actions with a simple integer numeric argument, 'arg_ofs' is the + * offset of that argument from the beginning of the action and 'arg_len' + * its length, both in bytes. + * + * For actions that take other forms, these are both zero. */ + unsigned short int arg_ofs; + unsigned short int arg_len; + + /* The name of the action, e.g. "OFPAT_OUTPUT" or "NXAST_RESUBMIT". */ + const char *name; + + /* If this action is deprecated, a human-readable string with a brief + * explanation. */ + const char *deprecation; +}; + +/* Action header. */ +struct ofp_action_header { + /* The meaning of other values of 'type' generally depends on the OpenFlow + * version (see enum ofp_raw_action_type). + * + * Across all OpenFlow versions, OFPAT_VENDOR indicates that 'vendor' + * designates an OpenFlow vendor ID and that the remainder of the action + * structure has a vendor-defined meaning. + */ +#define OFPAT_VENDOR 0xffff + ovs_be16 type; + + /* Always a multiple of 8. */ + ovs_be16 len; + + /* For type == OFPAT_VENDOR only, this is a vendor ID, e.g. NX_VENDOR_ID or + * ONF_VENDOR_ID. Other 'type's use this space for some other purpose. */ + ovs_be32 vendor; +}; +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; /* At least 16. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* See enum ofp_raw_action_type. */ + uint8_t pad[6]; +}; +OFP_ASSERT(sizeof(struct nx_action_header) == 16); + +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); +} + +#include "ofp-actions.inc2" + +static struct hmap * +ofpact_decode_hmap(void) +{ + static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; + static struct hmap hmap; + + if (ovsthread_once_start(&once)) { + struct ofpact_raw_instance *inst; + + hmap_init(&hmap); + for (inst = all_raw_instances; + inst < &all_raw_instances[ARRAY_SIZE(all_raw_instances)]; + inst++) { + hmap_insert(&hmap, &inst->decode_node, + ofpact_hdrs_hash(&inst->hdrs)); + } + ovsthread_once_done(&once); + } + return &hmap; +} + +static struct hmap * +ofpact_encode_hmap(void) +{ + static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; + static struct hmap hmap; + + if (ovsthread_once_start(&once)) { + struct ofpact_raw_instance *inst; + + hmap_init(&hmap); + for (inst = all_raw_instances; + inst < &all_raw_instances[ARRAY_SIZE(all_raw_instances)]; + inst++) { + hmap_insert(&hmap, &inst->encode_node, + hash_2words(inst->raw, inst->hdrs.ofp_version)); + } + ovsthread_once_done(&once); + } + return &hmap; +} + +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. */ + hdrs.vendor = ntohl(oah->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 { + 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); + } + + 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; + } + + if (action->deprecation) { + VLOG_INFO_RL(&rl, "%s is deprecated in %s (%s)", + action->name, ofputil_version_to_string(ofp_version), + action->deprecation); + } + + 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; + + 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_header *oah; + const struct ofpact_hdrs *hdrs; + + inst = ofpact_raw_lookup(ofp_version, raw); + hdrs = &inst->hdrs; + + oah = ofpbuf_put_zeros(buf, inst->min_length); + oah->type = htons(hdrs->vendor ? OFPAT_VENDOR : hdrs->type); + oah->len = htons(inst->min_length); + oah->vendor = htonl(hdrs->vendor); + + switch (hdrs->vendor) { + case 0: + break; + + case NX_VENDOR_ID: { + struct nx_action_header *nah = (struct nx_action_header *) oah; + nah->subtype = htons(hdrs->type); + break; + } + + default: + OVS_NOT_REACHED(); + } + + if (inst->arg_len) { + uint8_t *p = (uint8_t *) oah + 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 oah; +} diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h index 52e307f9d..0f1ea3fae 100644 --- a/lib/ofp-actions.h +++ b/lib/ofp-actions.h @@ -133,14 +133,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 @@ -151,14 +151,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. */ }; @@ -240,6 +240,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. */ @@ -442,6 +465,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) @@ -473,6 +498,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. */ @@ -492,6 +562,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. */ @@ -602,8 +726,6 @@ enum ofperr ofpacts_check_consistency(struct ofpact[], size_t ofpacts_len, struct flow *, ofp_port_t max_ports, uint8_t table_id, uint8_t n_tables, enum ofputil_protocol usable_protocols); -enum ofperr ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len, - uint32_t allowed_ovsinsts); enum ofperr ofpact_check_output_port(ofp_port_t port, ofp_port_t max_ports); /* Converting ofpacts to OpenFlow. */ @@ -628,10 +750,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..a78747e80 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[8][]. */ 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 b4da7190b..9bafa8d41 100644 --- a/lib/ofp-parse.c +++ b/lib/ofp-parse.c @@ -169,1005 +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), - (1u << OVSINST_OFPIT11_APPLY_ACTIONS)); - 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), - (1u << N_OVS_INSTRUCTIONS) - 1); - 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; @@ -1226,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) @@ -1293,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)) { @@ -1431,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; @@ -1801,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'. * @@ -2096,53 +1086,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 b9ce3156a..c8d38e85d 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -6299,121 +6299,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 - * and length 'sizeof *action' and zeros the rest. For actions that - * have variable length, the length used and cleared is that of 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 86c8461ff..af1a2a3e0 100644 --- a/lib/ofp-util.h +++ b/lib/ofp-util.h @@ -910,99 +910,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 that has a corresponding action structure - * struct , this defines two functions: - * - * void ofputil_init_(struct *action); - * - * Initializes the parts of 'action' that identify it as having type - * and length 'sizeof *action' and zeros the rest. For actions that have - * variable length, the length used and cleared is that of struct . - * - * struct *ofputil_put_(struct ofpbuf *buf); - * - * Appends a new 'action', of length 'sizeof(struct )', to 'buf', - * initializes it with ofputil_init_(), 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 7bf53a4da..e17377fa7 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -1246,7 +1246,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; @@ -4287,7 +4286,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/tests/ofp-actions.at b/tests/ofp-actions.at index 7c22758ce..73b7dc651 100644 --- a/tests/ofp-actions.at +++ b/tests/ofp-actions.at @@ -265,10 +265,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 7d0b466d7..3b434f8aa 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -2337,7 +2337,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 @@ -2349,7 +2349,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 @@ -2358,8 +2358,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/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c index 6235462bb..c05197a5f 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); }