Hi Ryan,
Thank you for your advice. The controller will drop the icmp4 packet whose 
eth.src or eth.dst is
broadcast/multicast address and the type of icmp4 is “destination unreachable”. 
That may reduce 
the broadcast storm attack(e.g. icmp flood, dhcp flood). I write the rate 
limiting of icmp4 into 
my to-do list. Thanks.

ovn: the implementation of icmp4 reject actions.

It support icmp4 reject (e.g. icmp-net-unreachable, icmp-host-prohibited, 
tcp-reset,
icmp-admin-prohibited, icmp-port-unreachable, icmp-net-prohibited, 
icmp-host-unreachable,
and icmp-proto-unreachable). The icmp-net-unreachable is default. The "TCP RST" 
function
will be completed soon. Reject action support only "from-lport" direction. In 
general,
considering performance requirements, it might make sense to support only 
"from-lport" direction.

Signed-off-by: nickcooper-zhangtonghao <nickcooper-zhangtong...@opencloud.tech>
---
 lib/dp-packet.h           |  16 +++++++
 lib/packets.c             |  21 +++++++++
 lib/packets.h             |   1 +
 ovn/controller/pinctrl.c  | 113 ++++++++++++++++++++++++++++++++++++++++++++++
 ovn/lib/actions.c         |   4 +-
 ovn/lib/actions.h         |   6 +++
 ovn/northd/ovn-northd.c   |  37 +++++++++++++--
 ovn/ovn-nb.ovsschema      |   6 ++-
 ovn/ovn-nb.xml            |  44 ++++++++++++++++--
 ovn/ovn-sb.xml            |   4 --
 ovn/utilities/ovn-nbctl.c |  41 +++++++++++++++--
 11 files changed, 277 insertions(+), 16 deletions(-)

diff --git a/lib/dp-packet.h b/lib/dp-packet.h
index 7c1e637..a6df36f 100644
--- a/lib/dp-packet.h
+++ b/lib/dp-packet.h
@@ -318,6 +318,15 @@ dp_packet_set_l3(struct dp_packet *b, void *l3)
     b->l3_ofs = l3 ? (char *) l3 - (char *) dp_packet_data(b) : UINT16_MAX;
 }
 
+static inline size_t
+dp_packet_l3_size(const struct dp_packet *b)
+{
+    return b->l3_ofs != UINT16_MAX
+        ? (const char *)dp_packet_tail(b) - (const char *)dp_packet_l3(b)
+        - dp_packet_l2_pad_size(b)
+        : 0;
+}
+
 static inline void *
 dp_packet_l4(const struct dp_packet *b)
 {
@@ -342,6 +351,13 @@ dp_packet_l4_size(const struct dp_packet *b)
 }
 
 static inline const void *
+dp_packet_get_ip4_payload(const struct dp_packet *b)
+{
+    return OVS_LIKELY(dp_packet_l3_size(b) >= IP_HEADER_LEN)
+        ? (const char *)dp_packet_l3(b) + IP_HEADER_LEN : NULL;
+}
+
+static inline const void *
 dp_packet_get_tcp_payload(const struct dp_packet *b)
 {
     size_t l4_size = dp_packet_l4_size(b);
diff --git a/lib/packets.c b/lib/packets.c
index a27264c..de90c9b 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -1251,6 +1251,9 @@ packet_format_tcp_flags(struct ds *s, uint16_t tcp_flags)
 #define ARP_PACKET_SIZE  (2 + ETH_HEADER_LEN + VLAN_HEADER_LEN + \
                           ARP_ETH_HEADER_LEN)
 
+#define ICMP4_PACKET_SIZE  (2 + ETH_HEADER_LEN + VLAN_HEADER_LEN + \
+                            IP_HEADER_LEN + ICMP_HEADER_LEN)
+
 /* Clears 'b' and replaces its contents by an ARP frame with the specified
  * 'arp_op', 'arp_sha', 'arp_tha', 'arp_spa', and 'arp_tpa'.  The outer
  * Ethernet frame is initialized with Ethernet source 'arp_sha' and destination
@@ -1299,6 +1302,24 @@ compose_arp__(struct dp_packet *b)
     dp_packet_set_l3(b, arp);
 }
 
+void
+compose_icmp4__(struct dp_packet *b)
+{
+    dp_packet_clear(b);
+    dp_packet_prealloc_tailroom(b, ICMP4_PACKET_SIZE);
+    dp_packet_reserve(b, 2 + VLAN_HEADER_LEN);
+
+    struct eth_header *eth = dp_packet_put_zeros(b, sizeof *eth);
+    eth->eth_type = htons(ETH_TYPE_IP);
+
+    struct ip_header *ip4 = dp_packet_put_zeros(b, sizeof *ip4);
+    struct icmp_header *icmp4 = dp_packet_put_zeros(b, sizeof *icmp4);
+
+    dp_packet_reset_offsets(b);
+    dp_packet_set_l4(b, icmp4);
+    dp_packet_set_l3(b, ip4);
+}
+
 /* This function expect packet with ethernet header with correct
  * l3 pointer set. */
 static void *
diff --git a/lib/packets.h b/lib/packets.h
index 077ccfa..adfb02b 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -1067,6 +1067,7 @@ void compose_arp(struct dp_packet *, uint16_t arp_op,
                  const struct eth_addr arp_sha,
                  const struct eth_addr arp_tha, bool broadcast,
                  ovs_be32 arp_spa, ovs_be32 arp_tpa);
+void compose_icmp4__(struct dp_packet *);
 void compose_nd(struct dp_packet *, const struct eth_addr eth_src,
                 struct in6_addr *, struct in6_addr *);
 void compose_na(struct dp_packet *,
diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
index 1370301..7893872 100644
--- a/ovn/controller/pinctrl.c
+++ b/ovn/controller/pinctrl.c
@@ -72,6 +72,10 @@ static void send_garp_run(const struct ovsrec_bridge *,
 static void pinctrl_handle_na(const struct flow *ip_flow,
                               const struct match *md,
                               struct ofpbuf *userdata);
+static void pinctrl_handle_icmp4(struct dp_packet *pkt_in,
+                                const struct flow *ip_flow,
+                                const struct match *md,
+                                struct ofpbuf *userdata);
 static void reload_metadata(struct ofpbuf *ofpacts,
                             const struct match *md);
 
@@ -412,6 +416,10 @@ process_packet_in(const struct ofp_header *msg)
     case ACTION_OPCODE_NA:
         pinctrl_handle_na(&headers, &pin.flow_metadata, &userdata);
         break;
+    
+    case ACTION_OPCODE_ICMP4:
+        pinctrl_handle_icmp4(&packet, &headers, &pin.flow_metadata, &userdata);
+        break;
 
     default:
         VLOG_WARN_RL(&rl, "unrecognized packet-in opcode %"PRIu32,
@@ -1003,3 +1011,108 @@ exit:
     dp_packet_uninit(&packet);
     ofpbuf_uninit(&ofpacts);
 }
+
+static void
+pinctrl_handle_icmp4(struct dp_packet *pkt_in,
+                    const struct flow *ip_flow,
+                    const struct match *md,
+                    struct ofpbuf *userdata)
+{
+    /* This action only works for ICMP packets, and the switch should only send
+     * us ICMP packets this way, but check here just to be sure. */
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+    if (ip_flow->dl_type != htons(ETH_TYPE_IP) ||
+            ip_flow->nw_proto != IPPROTO_ICMP) {
+        VLOG_WARN_RL(&rl, "ICMP4 action on non-IP packet (Ethertype 
%"PRIx16")" 
+                    " and (IP Protocol %"PRIx16")",
+                     ntohs(ip_flow->dl_type),
+                     ntohs(ip_flow->nw_proto));
+        return;
+    }
+    
+    if (eth_addr_is_broadcast(ip_flow->dl_dst) ||
+            eth_addr_is_multicast(ip_flow->dl_dst)) {
+        VLOG_WARN_RL(&rl, "The ICMP4 packet's destination mac address is"
+                    " broadcast or multicast address.");
+        return;
+    }
+
+    const struct icmp_header *icmp4_in = dp_packet_get_ip4_payload(pkt_in);
+    if (icmp4_in->icmp_type == 3) {
+        VLOG_WARN_RL(&rl, "The ICMP4 packets received indecate that the" 
+                    " destination is unreachable.");
+        return;
+    }
+    
+    /* Compose an ICMP4 packet. */
+    uint64_t packet_stub[128 / 8];
+    struct dp_packet pkt_out;
+
+    dp_packet_use_stub(&pkt_out, packet_stub, sizeof packet_stub);
+    compose_icmp4__(&pkt_out);
+
+    struct eth_header *eth = dp_packet_l2(&pkt_out);
+    eth->eth_dst = ip_flow->dl_dst;
+    eth->eth_src = ip_flow->dl_src;
+
+    struct ip_header *ip4 = dp_packet_l3(&pkt_out);
+    ip4->ip_ihl_ver = IP_IHL_VER(5, 4);
+    ip4->ip_tos = ip_flow->nw_tos;
+    ip4->ip_tot_len = htons(sizeof *ip4 + sizeof *icmp4_in + sizeof *ip4 + 8);
+    ip4->ip_frag_off = htons(IP_DONT_FRAGMENT);
+    ip4->ip_ttl = MAXTTL;
+    ip4->ip_proto = IPPROTO_ICMP;
+    put_16aligned_be32(&ip4->ip_src, ip_flow->nw_src);
+    put_16aligned_be32(&ip4->ip_dst, ip_flow->nw_dst);
+    ip4->ip_csum = csum(ip4, sizeof *ip4);
+
+    /* Format of the ICMP destination unreachable message packet is
+     * ------------------------------------------------------------
+     * | IP HEADER | ICMP HEADER | ICMP DATA: IP Header + 8 Bytes |
+     * ------------------------------------------------------------
+     */
+    struct icmp_header *icmp4 = dp_packet_l4(&pkt_out);
+
+    /* IP header and first 8 bytes of original datagram's data'*/
+    dp_packet_put(&pkt_out, dp_packet_l3(pkt_in), IP_HEADER_LEN +8);
+    icmp4->icmp_csum = csum(icmp4, sizeof *icmp4);
+
+    if (ip_flow->vlan_tci & htons(VLAN_CFI)) {
+        eth_push_vlan(&pkt_out, htons(ETH_TYPE_VLAN_8021Q), ip_flow->vlan_tci);
+    }
+    
+    /* Compose actions.
+     *
+     * First, copy metadata from 'md' into the packet-out via "set_field"
+     * actions, then add actions from 'userdata'.
+     */
+    uint64_t ofpacts_stub[4096 / 8];
+    struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
+    enum ofp_version version = rconn_get_version(swconn);
+
+    reload_metadata(&ofpacts, md);
+    enum ofperr error = ofpacts_pull_openflow_actions(userdata, 
userdata->size, version, &ofpacts);
+    if (error) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+        VLOG_WARN_RL(&rl, "failed to parse icmp actions (%s)",
+                     ofperr_to_string(error));
+        goto exit;
+    }
+
+    struct ofputil_packet_out po = {
+        .packet = dp_packet_data(&pkt_out),
+        .packet_len = dp_packet_size(&pkt_out),
+        .buffer_id = UINT32_MAX,
+        .in_port = OFPP_CONTROLLER,
+        .ofpacts = ofpacts.data,
+        .ofpacts_len = ofpacts.size,
+    };
+
+    enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
+    queue_msg(ofputil_encode_packet_out(&po, proto));
+
+exit:
+    dp_packet_uninit(&pkt_out);
+    ofpbuf_uninit(&ofpacts);
+}
diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c
index 3d10d61..a9b7c5a 100644
--- a/ovn/lib/actions.c
+++ b/ovn/lib/actions.c
@@ -249,7 +249,7 @@ put_controller_op(struct ofpbuf *ofpacts, enum 
action_opcode opcode)
     finish_controller_op(ofpacts, ofs);
 }
 
-/* Implements the "arp" and "na" actions, which execute nested actions on a
+/* Implements the "arp", "na" and "icmp4" actions, which execute nested 
actions on a
  * packet derived from the one being processed. */
 static void
 parse_nested_action(struct action_context *ctx, enum action_opcode opcode,
@@ -1068,6 +1068,8 @@ parse_action(struct action_context *ctx)
         parse_get_arp_action(ctx);
     } else if (lexer_match_id(ctx->lexer, "put_arp")) {
         parse_put_arp_action(ctx);
+    } else if (lexer_match_id(ctx->lexer, "icmp4")) {
+        parse_nested_action(ctx, ACTION_OPCODE_ICMP4, "ip4");
     } else {
         action_syntax_error(ctx, "expecting action");
     }
diff --git a/ovn/lib/actions.h b/ovn/lib/actions.h
index 48f0140..ed4ef5c 100644
--- a/ovn/lib/actions.h
+++ b/ovn/lib/actions.h
@@ -78,6 +78,12 @@ enum action_opcode {
      * The actions, in OpenFlow 1.3 format, follow the action_header.
      */
     ACTION_OPCODE_NA,
+
+    /* "icmp4 { ...actions... }".
+     *
+     * The actions, in OpenFlow 1.3 format, follow the action_header.
+     */
+    ACTION_OPCODE_ICMP4,
 };
 
 /* Header. */
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index 6712d21..78679b1 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -1625,11 +1625,42 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows)
                           acl->priority + OVN_ACL_PRI_OFFSET,
                           acl->match, "drop;");
         } else if (!strcmp(acl->action, "reject")) {
-            /* xxx Need to support "reject". */
-            VLOG_INFO("reject is not a supported action");
+
+            /* ACLs support the "icmp-reject" actions. 
+             * icmp-net-unreachable
+             * icmp-host-unreachable
+             * icmp-proto-unreachable
+             * icmp-port-unreachable
+             * icmp-net-prohibited
+             * icmp-host-prohibited
+             * icmp-admin-prohibited */
+
+            char reject_action[200]; 
+            uint8_t icmp_type = 3, icmp_code = 0;
+
+            if (!strcmp(acl->reject_action, "icmp-net-unreachable")) {
+                icmp_code = 0;
+            } else if (!strcmp(acl->reject_action, "icmp-host-unreachable")) {
+                icmp_code = 1;
+            } else if (!strcmp(acl->reject_action, "icmp-proto-unreachable")) {
+                icmp_code = 2;
+            } else if (!strcmp(acl->reject_action, "icmp-port-unreachable")) {
+                icmp_code = 3;
+            } else if (!strcmp(acl->reject_action, "icmp-net-prohibited")) {
+                icmp_code = 9;
+            } else if (!strcmp(acl->reject_action, "icmp-host-prohibited")) {
+                icmp_code = 10;
+            } else if (!strcmp(acl->reject_action, "icmp-admin-prohibited")) {
+                icmp_code = 13;
+            }
+
+            sprintf(reject_action, "icmp4 { eth.src<->eth.dst; 
ip4.src<->ip4.dst;"
+                    "icmp4.type = %d; icmp4.code = %d; outport = inport; 
inport = \"\";output;};",
+                    icmp_type, icmp_code);
+
             ovn_lflow_add(lflows, od, stage,
                           acl->priority + OVN_ACL_PRI_OFFSET,
-                          acl->match, "drop;");
+                          acl->match, reject_action);
         }
     }
 }
diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema
index 460d5bd..9842664 100644
--- a/ovn/ovn-nb.ovsschema
+++ b/ovn/ovn-nb.ovsschema
@@ -1,7 +1,7 @@
 {
     "name": "OVN_Northbound",
     "version": "5.0.0",
-    "cksum": "849073644 7576",
+    "cksum": "2026671360 7919",
     "tables": {
         "Logical_Switch": {
             "columns": {
@@ -87,6 +87,10 @@
                 "match": {"type": "string"},
                 "action": {"type": {"key": {"type": "string",
                                             "enum": ["set", ["allow", 
"allow-related", "drop", "reject"]]}}},
+                "reject_action": {"type": {"key": {"type": "string",
+                                            "enum": ["set", 
["icmp-net-unreachable", "icmp-host-unreachable", 
+                                           "icmp-proto-unreachable", 
"icmp-port-unreachable", "icmp-net-prohibited",
+                                           "icmp-host-prohibited", 
"icmp-admin-prohibited", "tcp-reset", ""]]}}},
                 "log": {"type": "boolean"},
                 "external_ids": {
                     "type": {"key": "string", "value": "string",
diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml
index e571eeb..70d20da 100644
--- a/ovn/ovn-nb.xml
+++ b/ovn/ovn-nb.xml
@@ -681,13 +681,51 @@
         </li>
 
         <li>
-          <code>reject</code>: Drop the packet, replying with a RST for TCP or
-          ICMP unreachable message for other IP-based protocols.
-          <code>Not implemented--currently treated as drop</code>
+          <code>reject</code>: Reject the packet, replying with a RST for TCP 
or
+          ICMP unreachable message for other IP-based protocols. Reject action 
support only <code>from-lport</code> direction.
         </li>
       </ul>
     </column>
 
+    <column name="reject_action">
+      <p>The action to take when the reject ACL rule matches:</p>
+      <ul>
+        <li>
+          <code>icmp-net-unreachable</code>: ICMP network unreachable 
(default).
+        </li>
+
+        <li>
+          <code>icmp-host-unreachable</code>: ICMP host unreachable.
+        </li>
+
+        <li>
+          <code>icmp-proto-unreachable</code>: ICMP protocol unreachable.
+        </li>
+
+        <li>
+          <code>icmp-port-unreachable</code>: ICMP port unreachable.
+        </li>
+
+        <li>
+          <code>icmp-net-prohibited</code>: ICMP network prohibited.
+        </li>
+        
+        <li>
+          <code>icmp-host-prohibited</code>: ICMP host prohibited.
+        </li>
+        
+        <li>
+          <code>icmp-admin-prohibited</code>: ICMP administratively prohibited.
+        </li>
+        
+        <li>
+          <code>tcp-reset</code>: TCP RST packet.
+          <code>Not implemented--currently treated as 
icmp-net-unreachable</code>
+        </li>
+
+      </ul>
+    </column>
+
     <column name="log">
       <p>
         If set to <code>true</code>, packets that match the ACL will trigger a
diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml
index 7b45bbb..73e9799 100644
--- a/ovn/ovn-sb.xml
+++ b/ovn/ovn-sb.xml
@@ -1251,10 +1251,6 @@
             <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-nbctl.c b/ovn/utilities/ovn-nbctl.c
index 25916da..55cc1da 100644
--- a/ovn/utilities/ovn-nbctl.c
+++ b/ovn/utilities/ovn-nbctl.c
@@ -302,7 +302,7 @@ Logical switch commands:\n\
   ls-list                   print the names of all logical switches\n\
 \n\
 ACL commands:\n\
-  acl-add SWITCH DIRECTION PRIORITY MATCH ACTION [log]\n\
+  acl-add SWITCH DIRECTION PRIORITY MATCH ACTION [REJECT-ACTION] [log]\n\
                             add an ACL to SWITCH\n\
   acl-del SWITCH [DIRECTION [PRIORITY MATCH]]\n\
                             remove ACLs from SWITCH\n\
@@ -1082,9 +1082,11 @@ nbctl_acl_list(struct ctl_context *ctx)
 
     for (i = 0; i < ls->n_acls; i++) {
         const struct nbrec_acl *acl = acls[i];
-        ds_put_format(&ctx->output, "%10s %5"PRId64" (%s) %s%s\n",
+        ds_put_format(&ctx->output, "%10s %5"PRId64" (%s) %s %s %s\n",
                       acl->direction, acl->priority,
-                      acl->match, acl->action, acl->log ? " log" : "");
+                      acl->match, acl->action, 
+                      strcmp(acl->action, "reject") ? "" : acl->reject_action, 
+                      acl->log ? "log" : "");
     }
 
     free(acls);
@@ -1120,6 +1122,7 @@ nbctl_acl_add(struct ctl_context *ctx)
 {
     const struct nbrec_logical_switch *ls;
     const char *action = ctx->argv[5];
+    const char *reject_action = ctx->argv[6];
 
     ls = ls_by_name_or_uuid(ctx, ctx->argv[1], true);
 
@@ -1133,6 +1136,35 @@ nbctl_acl_add(struct ctl_context *ctx)
                   "\"drop\", and \"reject\"", action);
         return;
     }
+   
+    /* Validate reject action. */
+    if (strcmp(action, "reject")) {
+        reject_action = "";
+    } else {
+        if (strcmp(direction, "from-lport"))
+            ctl_fatal("%s: reject action support only \"from-lport\" 
direction.",
+                    direction);
+            
+        if (ctx->argc == 6) {
+            reject_action = "icmp-net-unreachable";
+        } else if (ctx->argc == 7) {
+            if (strcmp(reject_action, "icmp-net-unreachable")
+                && strcmp(reject_action, "icmp-host-unreachable")
+                && strcmp(reject_action, "icmp-proto-unreachable")
+                && strcmp(reject_action, "icmp-port-unreachable")
+                && strcmp(reject_action, "icmp-net-prohibited")
+                && strcmp(reject_action, "icmp-host-prohibited")
+                && strcmp(reject_action, "icmp-admin-prohibited")
+                && strcmp(reject_action, "tcp-reset")) {
+                ctl_fatal("%s: reject action must be one of 
\"icmp-net-unreachable\", " 
+                        "\"icmp-host-unreachable\", 
\"icmp-proto-unreachable\", "
+                        "\"icmp-port-unreachable\", \"icmp-net-prohibited\", "
+                        "\"icmp-host-prohibited\", \"icmp-admin-prohibited\", "
+                          "and \"tcp-reset\"", reject_action);
+                return;
+            }
+        }
+    }
 
     /* Create the acl. */
     struct nbrec_acl *acl = nbrec_acl_insert(ctx->txn);
@@ -1140,6 +1172,7 @@ nbctl_acl_add(struct ctl_context *ctx)
     nbrec_acl_set_direction(acl, direction);
     nbrec_acl_set_match(acl, ctx->argv[4]);
     nbrec_acl_set_action(acl, action);
+    nbrec_acl_set_reject_action(acl, reject_action);
     if (shash_find(&ctx->options, "--log") != NULL) {
         nbrec_acl_set_log(acl, true);
     }
@@ -2139,7 +2172,7 @@ static const struct ctl_command_syntax nbctl_commands[] = 
{
     { "ls-list", 0, 0, "", NULL, nbctl_ls_list, NULL, "", RO },
 
     /* acl commands. */
-    { "acl-add", 5, 5, "SWITCH DIRECTION PRIORITY MATCH ACTION", NULL,
+    { "acl-add", 5, 6, "SWITCH DIRECTION PRIORITY MATCH ACTION 
[REJECT-ACTIONS]", NULL,
       nbctl_acl_add, NULL, "--log", RW },
     { "acl-del", 1, 4, "SWITCH [DIRECTION [PRIORITY MATCH]]", NULL,
       nbctl_acl_del, NULL, "", RW },
-- 
1.8.3.1



_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to