+ofp_print_nx_action(struct ds *string, const struct nx_action_header *nah)
+{
+
+ if (nah->subtype == htonl(NXAST_SNAT)) {
+ const struct nx_action_snat *nas = (struct nx_action_snat *)nah;
+ uint16_t port = ntohs(nas->port);
+
+ if (port < OFPP_MAX) {
+ ds_put_format(string, "nat:%"PRIu16, port);
+ } else {
+ ds_put_format(string, "nat:%"PRIu16" (invalid port)", port);
+ }
+ } else {
+ ds_put_format(string, "***unknown Nicira action:%d***\n",
+ ntohl(nah->subtype));
+ }
+}
+
+static int
+ofp_print_action(struct ds *string, const struct ofp_action_header *ah,
+ size_t actions_len)
+{
+ uint16_t type;
+ size_t len;
+
+ struct openflow_action {
+ size_t min_size;
+ size_t max_size;
+ };
+
+ const struct openflow_action of_actions[] = {
+ [OFPAT_OUTPUT] = {
+ sizeof(struct ofp_action_output),
+ sizeof(struct ofp_action_output),
+ },
+ [OFPAT_SET_VLAN_VID] = {
+ sizeof(struct ofp_action_vlan_vid),
+ sizeof(struct ofp_action_vlan_vid),
+ },
+ [OFPAT_SET_VLAN_PCP] = {
+ sizeof(struct ofp_action_vlan_pcp),
+ sizeof(struct ofp_action_vlan_pcp),
+ },
+ [OFPAT_STRIP_VLAN] = {
+ sizeof(struct ofp_action_header),
+ sizeof(struct ofp_action_header),
+ },
+ [OFPAT_SET_DL_SRC] = {
+ sizeof(struct ofp_action_dl_addr),
+ sizeof(struct ofp_action_dl_addr),
+ },
+ [OFPAT_SET_DL_DST] = {
+ sizeof(struct ofp_action_dl_addr),
+ sizeof(struct ofp_action_dl_addr),
+ },
+ [OFPAT_SET_NW_SRC] = {
+ sizeof(struct ofp_action_nw_addr),
+ sizeof(struct ofp_action_nw_addr),
+ },
+ [OFPAT_SET_NW_DST] = {
+ sizeof(struct ofp_action_nw_addr),
+ sizeof(struct ofp_action_nw_addr),
+ },
+ [OFPAT_SET_TP_SRC] = {
+ sizeof(struct ofp_action_tp_port),
+ sizeof(struct ofp_action_tp_port),
+ },
+ [OFPAT_SET_TP_DST] = {
+ sizeof(struct ofp_action_tp_port),
+ sizeof(struct ofp_action_tp_port),
+ }
+ /* OFPAT_VENDOR is not here, since it would blow up the array size. */
+ };
+
+ if (actions_len < sizeof *ah) {
+ ds_put_format(string, "***action array too short for next action***\n");
+ return -1;
+ }
+
+ type = ntohs(ah->type);
+ len = ntohs(ah->len);
+ if (actions_len < len) {
+ ds_put_format(string, "***truncated action %"PRIu16"***\n", type);
+ return -1;
+ }
+
+ if ((len % 8) != 0) {
+ ds_put_format(string,
+ "***action %"PRIu16" length not a multiple of 8***\n",
+ type);
+ return -1;
+ }
+
+ if (type < ARRAY_SIZE(of_actions)) {
+ const struct openflow_action *act = &of_actions[type];
+ if ((len < act->min_size) || (len > act->max_size)) {
+ ds_put_format(string,
+ "***action %"PRIu16" wrong length: %zu***\n", type, len);
+ return -1;
+ }
+ }
+
+ switch (type) {
+ case OFPAT_OUTPUT: {
+ struct ofp_action_output *oa = (struct ofp_action_output *)ah;
+ uint16_t port = ntohs(oa->port);
+ if (port < OFPP_MAX) {
+ ds_put_format(string, "output:%"PRIu16, port);
+ } else {
+ ofp_print_port_name(string, port);
+ if (port == OFPP_CONTROLLER) {
+ if (oa->max_len) {
+ ds_put_format(string, ":%"PRIu16, ntohs(oa->max_len));
+ } else {
+ ds_put_cstr(string, ":all");
+ }
+ }
+ }
+ break;
+ }
+
+ case OFPAT_SET_VLAN_VID: {
+ struct ofp_action_vlan_vid *va = (struct ofp_action_vlan_vid *)ah;
+ ds_put_format(string, "mod_vlan_vid:%"PRIu16, ntohs(va->vlan_vid));
+ break;
+ }
+
+ case OFPAT_SET_VLAN_PCP: {
+ struct ofp_action_vlan_pcp *va = (struct ofp_action_vlan_pcp *)ah;
+ ds_put_format(string, "mod_vlan_pcp:%"PRIu8, va->vlan_pcp);
+ break;
+ }
+
+ case OFPAT_STRIP_VLAN:
+ ds_put_cstr(string, "strip_vlan");
+ break;
+
+ case OFPAT_SET_DL_SRC: {
+ struct ofp_action_dl_addr *da = (struct ofp_action_dl_addr *)ah;
+ ds_put_format(string, "mod_dl_src:"ETH_ADDR_FMT,
+ ETH_ADDR_ARGS(da->dl_addr));
+ break;
+ }
+
+ case OFPAT_SET_DL_DST: {
+ struct ofp_action_dl_addr *da = (struct ofp_action_dl_addr *)ah;
+ ds_put_format(string, "mod_dl_dst:"ETH_ADDR_FMT,
+ ETH_ADDR_ARGS(da->dl_addr));
+ break;
+ }
+
+ case OFPAT_SET_NW_SRC: {
+ struct ofp_action_nw_addr *na = (struct ofp_action_nw_addr *)ah;
+ ds_put_format(string, "mod_nw_src:"IP_FMT, IP_ARGS(&na->nw_addr));
+ break;
+ }
+
+ case OFPAT_SET_NW_DST: {
+ struct ofp_action_nw_addr *na = (struct ofp_action_nw_addr *)ah;
+ ds_put_format(string, "mod_nw_dst:"IP_FMT, IP_ARGS(&na->nw_addr));
+ break;
+ }
+
+ case OFPAT_SET_TP_SRC: {
+ struct ofp_action_tp_port *ta = (struct ofp_action_tp_port *)ah;
+ ds_put_format(string, "mod_tp_src:%d", ntohs(ta->tp_port));
+ break;
+ }
+
+ case OFPAT_SET_TP_DST: {
+ struct ofp_action_tp_port *ta = (struct ofp_action_tp_port *)ah;
+ ds_put_format(string, "mod_tp_dst:%d", ntohs(ta->tp_port));
+ break;
+ }
+
+ case OFPAT_VENDOR: {
+ struct ofp_action_vendor_header *avh
+ = (struct ofp_action_vendor_header *)ah;
+ if (len < sizeof *avh) {
+ ds_put_format(string, "***ofpat_vendor truncated***\n");
+ return -1;
+ }
+ if (avh->vendor == htonl(NX_VENDOR_ID)) {
+ ofp_print_nx_action(string, (struct nx_action_header *)avh);
+ } else {
+ ds_put_format(string, "vendor action:0x%x", ntohl(avh->vendor));