[ovs-dev,v2,1/2] OVN: add icmp4{} action support
diff mbox series

Message ID 9f8a8b12902562ff69ba096bd596a053d9323942.1519147772.git.lorenzo.bianconi@redhat.com
State Accepted
Headers show
Series
  • add acl reject rule support introducing icmp4 action
Related show

Commit Message

Lorenzo Bianconi Feb. 20, 2018, 5:39 p.m. UTC
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 <lorenzo.bianconi@redhat.com>
---
 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(-)

Comments

Ben Pfaff March 9, 2018, 7:39 p.m. UTC | #1
On Tue, Feb 20, 2018 at 06:39:43PM +0100, Lorenzo Bianconi wrote:
> 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 <lorenzo.bianconi@redhat.com>

Thank you!

I folded in the two changes below, which looked to me like minor
inconsistencies, and applied this to master.

--8<--------------------------cut here-------------------------->8--

diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml
index 67da8a7f47ff..6a8b818a347e 100644
--- a/ovn/ovn-sb.xml
+++ b/ovn/ovn-sb.xml
@@ -1725,7 +1725,7 @@
           <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>ip.ttl = 255</code></li>
             <li><code>icmp4.type = 3</code> (destination unreachable)</li>
             <li><code>icmp4.code = 1</code> (host unreachable)</li>
           </ul>
diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c
index 93094eea6c99..00e885a1430d 100644
--- a/ovn/utilities/ovn-trace.c
+++ b/ovn/utilities/ovn-trace.c
@@ -1549,6 +1549,7 @@ execute_icmp4(const struct ovnact_nest *on,
     icmp4_flow.nw_dst = uflow->nw_dst;
     icmp4_flow.nw_src = uflow->nw_src;
     icmp4_flow.nw_proto = IPPROTO_ICMP;
+    icmp4_flow.nw_ttl = 255;
     icmp4_flow.tp_src = htons(ICMP4_DST_UNREACH); /* icmp type */
     icmp4_flow.tp_dst = htons(1); /* icmp code */
Lorenzo Bianconi March 9, 2018, 8:03 p.m. UTC | #2
> On Tue, Feb 20, 2018 at 06:39:43PM +0100, Lorenzo Bianconi wrote:
>> 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 <lorenzo.bianconi@redhat.com>
>
> Thank you!
>
> I folded in the two changes below, which looked to me like minor
> inconsistencies, and applied this to master.

Sounds good, thx :)
Regards,

Lorenzo

>
> --8<--------------------------cut here-------------------------->8--
>
> diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml
> index 67da8a7f47ff..6a8b818a347e 100644
> --- a/ovn/ovn-sb.xml
> +++ b/ovn/ovn-sb.xml
> @@ -1725,7 +1725,7 @@
>            <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>ip.ttl = 255</code></li>
>              <li><code>icmp4.type = 3</code> (destination unreachable)</li>
>              <li><code>icmp4.code = 1</code> (host unreachable)</li>
>            </ul>
> diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c
> index 93094eea6c99..00e885a1430d 100644
> --- a/ovn/utilities/ovn-trace.c
> +++ b/ovn/utilities/ovn-trace.c
> @@ -1549,6 +1549,7 @@ execute_icmp4(const struct ovnact_nest *on,
>      icmp4_flow.nw_dst = uflow->nw_dst;
>      icmp4_flow.nw_src = uflow->nw_src;
>      icmp4_flow.nw_proto = IPPROTO_ICMP;
> +    icmp4_flow.nw_ttl = 255;
>      icmp4_flow.tp_src = htons(ICMP4_DST_UNREACH); /* icmp type */
>      icmp4_flow.tp_dst = htons(1); /* icmp code */
>

Patch
diff mbox series

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[]