+static void
+parse_field_value(struct cls_rule *rule, enum field_index index,
+ const char *value)
+{
+ uint8_t mac[ETH_ADDR_LEN], mac_mask[ETH_ADDR_LEN];
+ ovs_be64 tun_id, tun_mask;
+ ovs_be32 ip, mask;
+ ovs_be16 tci, tci_mask;
+ struct in6_addr ipv6, ipv6_mask;
+ uint16_t port_no;
+
+ switch (index) {
+ case F_TUN_ID:
+ str_to_tun_id(value, &tun_id, &tun_mask);
+ cls_rule_set_tun_id_masked(rule, tun_id, tun_mask);
+ break;
+
+ case F_IN_PORT:
+ if (!parse_port_name(value, &port_no)) {
+ port_no = atoi(value);
+ }
+ cls_rule_set_in_port(rule, port_no);
+ break;
+
+ case F_DL_VLAN:
+ cls_rule_set_dl_vlan(rule, htons(str_to_u32(value)));
+ break;
+
+ case F_DL_VLAN_PCP:
+ cls_rule_set_dl_vlan_pcp(rule, str_to_u32(value));
+ break;
+
+ case F_VLAN_TCI:
+ str_to_vlan_tci(value, &tci, &tci_mask);
+ cls_rule_set_dl_tci_masked(rule, tci, tci_mask);
+ break;
+
+ case F_DL_SRC:
+ str_to_mac(value, mac);
+ cls_rule_set_dl_src(rule, mac);
+ break;
+
+ case F_DL_DST:
+ str_to_eth_dst(value, mac, mac_mask);
+ cls_rule_set_dl_dst_masked(rule, mac, mac_mask);
+ break;
+
+ case F_DL_TYPE:
+ cls_rule_set_dl_type(rule, htons(str_to_u32(value)));
+ break;
+
+ case F_NW_SRC:
+ str_to_ip(value, &ip, &mask);
+ cls_rule_set_nw_src_masked(rule, ip, mask);
+ break;
+
+ case F_NW_DST:
+ str_to_ip(value, &ip, &mask);
+ cls_rule_set_nw_dst_masked(rule, ip, mask);
+ break;
+
+ case F_NW_PROTO:
+ cls_rule_set_nw_proto(rule, str_to_u32(value));
+ break;
+
+ case F_NW_TOS:
+ cls_rule_set_nw_tos(rule, str_to_u32(value));
+ break;
+
+ case F_TP_SRC:
+ cls_rule_set_tp_src(rule, htons(str_to_u32(value)));
+ break;
+
+ case F_TP_DST:
+ cls_rule_set_tp_dst(rule, htons(str_to_u32(value)));
+ break;
+
+ case F_ICMP_TYPE:
+ cls_rule_set_icmp_type(rule, str_to_u32(value));
+ break;
+
+ case F_ICMP_CODE:
+ cls_rule_set_icmp_code(rule, str_to_u32(value));
+ break;
+
+ case F_ARP_SHA:
+ str_to_mac(value, mac);
+ cls_rule_set_arp_sha(rule, mac);
+ break;
+
+ case F_ARP_THA:
+ str_to_mac(value, mac);
+ cls_rule_set_arp_tha(rule, mac);
+ break;
+
+ case F_IPV6_SRC:
+ str_to_ipv6(value, &ipv6, &ipv6_mask);
+ cls_rule_set_ipv6_src_masked(rule, &ipv6, &ipv6_mask);
+ break;
+
+ case F_IPV6_DST:
+ str_to_ipv6(value, &ipv6, &ipv6_mask);
+ cls_rule_set_ipv6_dst_masked(rule, &ipv6, &ipv6_mask);
+ break;
+
+ case F_ND_TARGET:
+ str_to_ipv6(value, &ipv6, NULL);
+ cls_rule_set_nd_target(rule, ipv6);
+ break;
+
+ case F_ND_SLL:
+ str_to_mac(value, mac);
+ cls_rule_set_arp_sha(rule, mac);
+ break;
+
+ case F_ND_TLL:
+ str_to_mac(value, mac);
+ cls_rule_set_arp_tha(rule, mac);
+ break;
+
+ case N_FIELDS:
+ NOT_REACHED();
+ }
+}
+
+static void
+parse_reg_value(struct cls_rule *rule, int reg_idx, const char *value)
+{
+ /* This uses an oversized destination field (64 bits when 32 bits would do)
+ * because some sscanf() implementations truncate the range of %i
+ * directives, so that e.g. "%"SCNi16 interprets input of "0xfedc" as a
+ * value of 0x7fff. The other alternatives are to allow only a single
+ * radix (e.g. decimal or hexadecimal) or to write more sophisticated
+ * parsers. */
+ unsigned long long int reg_value, reg_mask;
+
+ if (!strcmp(value, "ANY") || !strcmp(value, "*")) {
+ cls_rule_set_reg_masked(rule, reg_idx, 0, 0);
+ } else if (sscanf(value, "%lli/%lli",
+ ®_value, ®_mask) == 2) {
+ cls_rule_set_reg_masked(rule, reg_idx, reg_value, reg_mask);
+ } else if (sscanf(value, "%lli", ®_value)) {
+ cls_rule_set_reg(rule, reg_idx, reg_value);
+ } else {
+ ovs_fatal(0, "register fields must take the form <value> "
+ "or <value>/<mask>");
+ }
+}
+
+/* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man
+ * page) into 'fm' for sending the specified flow_mod 'command' to a switch.
+ * If 'actions' is specified, an action must be in 'string' and may be expanded
+ * or reallocated.
+ *
+ * To parse syntax for an OFPT_FLOW_MOD (or NXT_FLOW_MOD), use an OFPFC_*
+ * constant for 'command'. To parse syntax for an OFPST_FLOW or
+ * OFPST_AGGREGATE (or NXST_FLOW or NXST_AGGREGATE), use -1 for 'command'. */