ovn: the implementation of icmp4 reject actions.

The ovn-contoller will process the packet-in data packet and compose an ICMP4 
packet,
whose icmp4 data contains the IP header and first 8 bytes of original 
datagram's data.

Signed-off-by: nickcooper-zhangtonghao <nickcooper-zhangtong...@opencloud.tech>


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. */


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..3556409 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,95 @@ 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. */
+    if (ip_flow->dl_type != htons(ETH_TYPE_IP) ||
+            ip_flow->nw_proto != IPPROTO_ICMP) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+        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;
+    }
+
+    /* Compose an ICMP4 packet. */
+    struct icmp_header *icmp4;
+    struct ip_header *ip4;
+    struct eth_header *eth;
+    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);
+
+    eth = dp_packet_l2(&pkt_out);
+    eth->eth_dst = ip_flow->dl_dst;
+    eth->eth_src = ip_flow->dl_src;
+
+    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 + 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, 1 Byte Type, 1 Byte Code, 2 Bytes CSUM | IP 
Header + 8 Bytes|
+     * 
----------------------------------------------------------------------------------------
+     */
+    icmp4 = dp_packet_l4(&pkt_out);
+    icmp4->icmp_csum = csum(icmp4, sizeof *icmp4);
+
+    /* 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);
+
+    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,
 };





thanks,
nick








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

Reply via email to