icmp4 action is used to replace the IPv4 packet been processed with
an ICMPv4 packet initialized based on incoming IPv4 one.
Ethernet and IPv4 fields not listed are not changed:
- ip.proto = 1 (ICMPv4)
- ip.frag = 0 (not a fragment)
- ip.ttl = 255
- icmp4.type = 3 (destination unreachable)
- icmp4.code = 1 (host unreachable)
Prerequisite: ip4

Signed-off-by: Lorenzo Bianconi <[email protected]>
---
 include/ovn/actions.h     |  7 +++++++
 ovn/controller/pinctrl.c  | 53 +++++++++++++++++++++++++++++++++++++++++++++++
 ovn/lib/actions.c         | 22 ++++++++++++++++++++
 ovn/ovn-sb.xml            |  5 +----
 ovn/utilities/ovn-trace.c | 29 ++++++++++++++++++++++++++
 tests/ovn.at              | 10 +++++++++
 6 files changed, 122 insertions(+), 4 deletions(-)

diff --git a/include/ovn/actions.h b/include/ovn/actions.h
index 9554a395d..b1988d6aa 100644
--- a/include/ovn/actions.h
+++ b/include/ovn/actions.h
@@ -64,6 +64,7 @@ struct ovn_extend_table;
     OVNACT(CT_CLEAR,          ovnact_null)            \
     OVNACT(CLONE,             ovnact_nest)            \
     OVNACT(ARP,               ovnact_nest)            \
+    OVNACT(ICMP4,             ovnact_nest)            \
     OVNACT(ND_NA,             ovnact_nest)            \
     OVNACT(GET_ARP,           ovnact_get_mac_bind)    \
     OVNACT(PUT_ARP,           ovnact_put_mac_bind)    \
@@ -429,6 +430,12 @@ enum action_opcode {
      * The actions, in OpenFlow 1.3 format, follow the action_header.
      */
     ACTION_OPCODE_ND_NS,
+
+    /* "icmp4 { ...actions... }".
+     *
+     * The actions, in OpenFlow 1.3 format, follow the action_header.
+     */
+    ACTION_OPCODE_ICMP4,
 };
 
 /* Header. */
diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
index e34916f5c..f08fbab59 100644
--- a/ovn/controller/pinctrl.c
+++ b/ovn/controller/pinctrl.c
@@ -219,6 +219,55 @@ pinctrl_handle_arp(const struct flow *ip_flow, const 
struct match *md,
     dp_packet_uninit(&packet);
 }
 
+static void
+pinctrl_handle_icmp4(const struct flow *ip_flow, const struct match *md,
+                     struct ofpbuf *userdata)
+{
+    /* This action only works for IP packets, and the switch should only send
+     * us IP packets this way, but check here just to be sure. */
+    if (ip_flow->dl_type != htons(ETH_TYPE_IP)) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+        VLOG_WARN_RL(&rl,
+                     "ICMP4 action on non-IP packet (eth_type 0x%"PRIx16")",
+                     ntohs(ip_flow->dl_type));
+        return;
+    }
+
+    uint64_t packet_stub[128 / 8];
+    struct dp_packet packet;
+
+    dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
+    dp_packet_clear(&packet);
+    packet.packet_type = htonl(PT_ETH);
+
+    struct eth_header *eh = dp_packet_put_zeros(&packet, sizeof *eh);
+    eh->eth_dst = ip_flow->dl_dst;
+    eh->eth_src = ip_flow->dl_src;
+    eh->eth_type = htons(ETH_TYPE_IP);
+
+    struct ip_header *nh = dp_packet_put_zeros(&packet, sizeof *nh);
+    dp_packet_set_l3(&packet, nh);
+    nh->ip_ihl_ver = IP_IHL_VER(5, 4);
+    nh->ip_tot_len = htons(sizeof(struct ip_header) +
+                           sizeof(struct icmp_header));
+    nh->ip_proto = IPPROTO_ICMP;
+    nh->ip_frag_off = htons(IP_DF);
+    packet_set_ipv4(&packet, ip_flow->nw_src, ip_flow->nw_dst,
+                    ip_flow->nw_tos, 255);
+
+    struct icmp_header *ih = dp_packet_put_zeros(&packet, sizeof *ih);
+    dp_packet_set_l4(&packet, ih);
+    packet_set_icmp(&packet, ICMP4_DST_UNREACH, 1);
+
+    if (ip_flow->vlans[0].tci & htons(VLAN_CFI)) {
+        eth_push_vlan(&packet, htons(ETH_TYPE_VLAN_8021Q),
+                      ip_flow->vlans[0].tci);
+    }
+
+    set_actions_and_enqueue_msg(&packet, md, userdata);
+    dp_packet_uninit(&packet);
+}
+
 static void
 pinctrl_handle_put_dhcp_opts(
     struct dp_packet *pkt_in, struct ofputil_packet_in *pin,
@@ -1022,6 +1071,10 @@ process_packet_in(const struct ofp_header *msg, struct 
controller_ctx *ctx)
         pinctrl_handle_nd_ns(&headers, &pin.flow_metadata, &userdata);
         break;
 
+    case ACTION_OPCODE_ICMP4:
+        pinctrl_handle_icmp4(&headers, &pin.flow_metadata, &userdata);
+        break;
+
     default:
         VLOG_WARN_RL(&rl, "unrecognized packet-in opcode %"PRIu32,
                      ntohl(ah->opcode));
diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c
index fde3bff00..fc5ace1f6 100644
--- a/ovn/lib/actions.c
+++ b/ovn/lib/actions.c
@@ -1141,6 +1141,12 @@ parse_ARP(struct action_context *ctx)
     parse_nested_action(ctx, OVNACT_ARP, "ip4");
 }
 
+static void
+parse_ICMP4(struct action_context *ctx)
+{
+    parse_nested_action(ctx, OVNACT_ICMP4, "ip4");
+}
+
 static void
 parse_ND_NA(struct action_context *ctx)
 {
@@ -1174,6 +1180,12 @@ format_ARP(const struct ovnact_nest *nest, struct ds *s)
     format_nested_action(nest, "arp", s);
 }
 
+static void
+format_ICMP4(const struct ovnact_nest *nest, struct ds *s)
+{
+    format_nested_action(nest, "icmp4", s);
+}
+
 static void
 format_ND_NA(const struct ovnact_nest *nest, struct ds *s)
 {
@@ -1224,6 +1236,14 @@ encode_ARP(const struct ovnact_nest *on,
     encode_nested_actions(on, ep, ACTION_OPCODE_ARP, ofpacts);
 }
 
+static void
+encode_ICMP4(const struct ovnact_nest *on,
+             const struct ovnact_encode_params *ep,
+             struct ofpbuf *ofpacts)
+{
+    encode_nested_actions(on, ep, ACTION_OPCODE_ICMP4, ofpacts);
+}
+
 static void
 encode_ND_NA(const struct ovnact_nest *on,
              const struct ovnact_encode_params *ep,
@@ -2247,6 +2267,8 @@ parse_action(struct action_context *ctx)
         parse_CLONE(ctx);
     } else if (lexer_match_id(ctx->lexer, "arp")) {
         parse_ARP(ctx);
+    } else if (lexer_match_id(ctx->lexer, "icmp4")) {
+        parse_ICMP4(ctx);
     } else if (lexer_match_id(ctx->lexer, "nd_na")) {
         parse_ND_NA(ctx);
     } else if (lexer_match_id(ctx->lexer, "nd_ns")) {
diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml
index f000b166c..67da8a7f4 100644
--- a/ovn/ovn-sb.xml
+++ b/ovn/ovn-sb.xml
@@ -1725,14 +1725,11 @@
           <ul>
             <li><code>ip.proto = 1</code> (ICMPv4)</li>
             <li><code>ip.frag = 0</code> (not a fragment)</li>
+            <li><code>ip.ttl = 255</code> (not a fragment)</li>
             <li><code>icmp4.type = 3</code> (destination unreachable)</li>
             <li><code>icmp4.code = 1</code> (host unreachable)</li>
           </ul>
 
-          <p>
-            Details TBD.
-          </p>
-
           <p><b>Prerequisite:</b> <code>ip4</code></p>
         </dd>
 
diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c
index db39c4961..93094eea6 100644
--- a/ovn/utilities/ovn-trace.c
+++ b/ovn/utilities/ovn-trace.c
@@ -1535,6 +1535,30 @@ execute_nd_ns(const struct ovnact_nest *on, const struct 
ovntrace_datapath *dp,
                   table_id, pipeline, &node->subs);
 }
 
+static void
+execute_icmp4(const struct ovnact_nest *on,
+              const struct ovntrace_datapath *dp,
+              const struct flow *uflow, uint8_t table_id,
+              enum ovnact_pipeline pipeline, struct ovs_list *super)
+{
+    struct flow icmp4_flow = *uflow;
+
+    /* Update fields for ICMP. */
+    icmp4_flow.dl_dst = uflow->dl_dst;
+    icmp4_flow.dl_src = uflow->dl_src;
+    icmp4_flow.nw_dst = uflow->nw_dst;
+    icmp4_flow.nw_src = uflow->nw_src;
+    icmp4_flow.nw_proto = IPPROTO_ICMP;
+    icmp4_flow.tp_src = htons(ICMP4_DST_UNREACH); /* icmp type */
+    icmp4_flow.tp_dst = htons(1); /* icmp code */
+
+    struct ovntrace_node *node = ovntrace_node_append(
+        super, OVNTRACE_NODE_TRANSFORMATION, "icmp4");
+
+    trace_actions(on->nested, on->nested_len, dp, &icmp4_flow,
+                  table_id, pipeline, &node->subs);
+}
+
 static void
 execute_get_mac_bind(const struct ovnact_get_mac_bind *bind,
                      const struct ovntrace_datapath *dp,
@@ -1893,6 +1917,11 @@ trace_actions(const struct ovnact *ovnacts, size_t 
ovnacts_len,
         case OVNACT_SET_METER:
             /* Nothing to do. */
             break;
+
+        case OVNACT_ICMP4:
+            execute_icmp4(ovnact_get_ICMP4(a), dp, uflow, table_id, pipeline,
+                          super);
+            break;
         }
 
     }
diff --git a/tests/ovn.at b/tests/ovn.at
index 8ee3bf0b5..37e63810d 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -1125,6 +1125,16 @@ reg1[0] = put_nd_ra_opts(addr_mode = "slaac", mtu = 
"1500", slla = ae:01:02:03:0
 reg1[0] = put_nd_ra_opts(addr_mode = "slaac", mtu = 10.0.0.4, slla = 
ae:01:02:03:04:10);
     Invalid value for "mtu" option
 
+# icmp4
+icmp4 { eth.dst = ff:ff:ff:ff:ff:ff; output; }; output;
+    encodes as 
controller(userdata=00.00.00.0a.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64)
+    has prereqs ip4
+
+icmp4 { };
+    formats as icmp4 { drop; };
+    encodes as controller(userdata=00.00.00.0a.00.00.00.00)
+    has prereqs ip4
+
 # Contradictionary prerequisites (allowed but not useful):
 ip4.src = ip6.src[0..31];
     encodes as move:NXM_NX_IPV6_SRC[0..31]->NXM_OF_IP_SRC[]
-- 
2.14.3

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to