Add support for understanding ICMP type and code in flow entries.
authorJustin Pettit <jpettit@nicira.com>
Thu, 27 Nov 2008 04:57:54 +0000 (20:57 -0800)
committerJustin Pettit <jpettit@nicira.com>
Thu, 27 Nov 2008 04:58:33 +0000 (20:58 -0800)
datapath/flow.c
datapath/flow.h
include/openflow/openflow.h
lib/flow.c
lib/ofp-print.c
lib/packets.h
switch/switch-flow.c
utilities/dpctl.8.in
utilities/dpctl.c

index 10060b0..effd0c8 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/module.h>
 #include <linux/tcp.h>
 #include <linux/udp.h>
+#include <linux/icmp.h>
 #include <linux/in.h>
 #include <linux/rcupdate.h>
 #include <net/ip.h>
@@ -114,7 +115,8 @@ void flow_extract_match(struct sw_flow_key* to, const struct ofp_match* from)
                         * network protocol is unknown. */
                        to->wildcards |= OFPFW_TP;
                } else if (from->nw_proto == IPPROTO_TCP
-                          || from->nw_proto == IPPROTO_UDP) {
+                               || from->nw_proto == IPPROTO_UDP
+                               || from->nw_proto == IPPROTO_ICMP) {
                        to->tp_src = from->tp_src;
                        to->tp_dst = from->tp_dst;
                } else {
@@ -322,6 +324,12 @@ static int udphdr_ok(struct sk_buff *skb)
        return pskb_may_pull(skb, th_ofs + sizeof(struct udphdr));
 }
 
+static int icmphdr_ok(struct sk_buff *skb)
+{
+       int th_ofs = skb_transport_offset(skb);
+       return pskb_may_pull(skb, th_ofs + sizeof(struct icmphdr));
+}
+
 /* Parses the Ethernet frame in 'skb', which was received on 'in_port',
  * and initializes 'key' to match.  Returns 1 if 'skb' contains an IP
  * fragment, 0 otherwise. */
@@ -404,6 +412,20 @@ int flow_extract(struct sk_buff *skb, uint16_t in_port,
                                         * header. */
                                        key->nw_proto = 0;
                                }
+                       } else if (key->nw_proto == IPPROTO_ICMP) {
+                               if (icmphdr_ok(skb)) {
+                                       struct icmphdr *icmp = icmp_hdr(skb);
+                                       /* The ICMP type and code fields use the 16-bit
+                                        * transport port fields, so we need to store them
+                                        * in 16-bit network byte order. */
+                                       key->icmp_type = htons(icmp->type);
+                                       key->icmp_code = htons(icmp->code);
+                               } else {
+                                       /* Avoid tricking other code into
+                                        * thinking that this packet has an L4
+                                        * header. */
+                                       key->nw_proto = 0;
+                               }
                        }
                } else {
                        retval = 1;
index 4db6204..f0f8c66 100644 (file)
@@ -39,6 +39,11 @@ struct sw_flow_key {
        uint32_t nw_dst_mask;   /* 1-bit in each significant nw_dst bit. */
 };
 
+/* The match fields for ICMP type and code use the transport source and 
+ * destination port fields, respectively. */
+#define icmp_type tp_src
+#define icmp_code tp_dst
+
 /* Compare two sw_flow_keys and return true if they are the same flow, false
  * otherwise.  Wildcards and netmasks are not considered. */
 static inline int flow_keys_equal(const struct sw_flow_key *a,
index 15c73ad..e7da0d1 100644 (file)
@@ -469,6 +469,11 @@ enum ofp_flow_wildcards {
     OFPFW_ALL = ((1 << 20) - 1)
 };
 
+/* The wildcards for ICMP type and code fields use the transport source 
+ * and destination port fields, respectively. */
+#define OFPFW_ICMP_TYPE OFPFW_TP_SRC
+#define OFPFW_ICMP_CODE OFPFW_TP_DST
+
 /* Values below this cutoff are 802.3 packets and the two bytes
  * following MAC addresses are used as a frame length.  Otherwise, the
  * two bytes are used as the Ethernet type.
@@ -502,6 +507,11 @@ struct ofp_match {
 };
 OFP_ASSERT(sizeof(struct ofp_match) == 36);
 
+/* The match fields for ICMP type and code use the transport source and 
+ * destination port fields, respectively. */
+#define icmp_type tp_src
+#define icmp_code tp_dst
+
 /* Value used in "idle_timeout" and "hard_timeout" to indicate that the entry
  * is permanent. */
 #define OFP_FLOW_PERMANENT 0
index 8a02d7f..70a9c4b 100644 (file)
@@ -76,6 +76,12 @@ pull_udp(struct ofpbuf *packet)
     return ofpbuf_try_pull(packet, UDP_HEADER_LEN);
 }
 
+static struct icmp_header *
+pull_icmp(struct ofpbuf *packet) 
+{
+    return ofpbuf_try_pull(packet, ICMP_HEADER_LEN);
+}
+
 static struct eth_header *
 pull_eth(struct ofpbuf *packet) 
 {
@@ -181,6 +187,17 @@ flow_extract(struct ofpbuf *packet, uint16_t in_port, struct flow *flow)
                              * this packet has an L4 header. */
                             flow->nw_proto = 0;
                         }
+                    } else if (flow->nw_proto == IP_TYPE_ICMP) {
+                        const struct icmp_header *icmp = pull_icmp(&b);
+                        if (icmp) {
+                            flow->icmp_type = htons(icmp->icmp_type);
+                            flow->icmp_code = htons(icmp->icmp_code);
+                            packet->l7 = b.data;
+                        } else {
+                            /* Avoid tricking other code into thinking that
+                             * this packet has an L4 header. */
+                            flow->nw_proto = 0;
+                        }
                     }
                 } else {
                     retval = 1;
index c145b3d..41aed2f 100644 (file)
@@ -739,10 +739,17 @@ static void ofp_print_match(struct ds *f, const struct ofp_match *om,
         print_wild(f, "nw_proto=", w & OFPFW_NW_PROTO, verbosity,
                    "%u", om->nw_proto);
     }
-    print_wild(f, "tp_src=", w & OFPFW_TP_SRC, verbosity,
-               "%d", ntohs(om->tp_src));
-    print_wild(f, "tp_dst=", w & OFPFW_TP_DST, verbosity,
-               "%d", ntohs(om->tp_dst));
+    if (om->nw_proto == IP_TYPE_ICMP) {
+        print_wild(f, "icmp_type=", w & OFPFW_ICMP_TYPE, verbosity,
+                   "%d", ntohs(om->icmp_type));
+        print_wild(f, "icmp_code=", w & OFPFW_ICMP_CODE, verbosity,
+                   "%d", ntohs(om->icmp_code));
+    } else {
+        print_wild(f, "tp_src=", w & OFPFW_TP_SRC, verbosity,
+                   "%d", ntohs(om->tp_src));
+        print_wild(f, "tp_dst=", w & OFPFW_TP_DST, verbosity,
+                   "%d", ntohs(om->tp_dst));
+    }
 }
 
 /* Pretty-print the OFPT_FLOW_MOD packet of 'len' bytes at 'oh' to 'string'
index 09ee05d..adda6bd 100644 (file)
@@ -202,6 +202,14 @@ struct ip_header {
 };
 BUILD_ASSERT_DECL(IP_HEADER_LEN == sizeof(struct ip_header));
 
+#define ICMP_HEADER_LEN 4
+struct icmp_header {
+    uint8_t icmp_type;
+    uint8_t icmp_code;
+    uint16_t icmp_csum;
+};
+BUILD_ASSERT_DECL(ICMP_HEADER_LEN == sizeof(struct icmp_header));
+
 #define UDP_HEADER_LEN 8
 struct udp_header {
     uint16_t udp_src;
index 8e3b7f7..4ea258b 100644 (file)
@@ -129,7 +129,8 @@ flow_extract_match(struct sw_flow_key* to, const struct ofp_match* from)
              * protocol is unknown. */
             to->wildcards |= OFPFW_TP;
         } else if (from->nw_proto == IPPROTO_TCP 
-                || from->nw_proto == IPPROTO_UDP) {
+                || from->nw_proto == IPPROTO_UDP
+                || from->nw_proto == IPPROTO_ICMP) {
             to->flow.tp_src = from->tp_src;
             to->flow.tp_dst = from->tp_dst;
         } else {
index 6514861..d1f03bb 100644 (file)
@@ -296,6 +296,13 @@ packets originating from a HTTP server.
 .IP \fBtp_dst=\fIport\fR
 Matches UDP or TCP destination port \fIport\fR.
 
+.IP \fBicmp_type=\fItype\fR
+Matches ICMP message with \fItype\fR, which should be specified as a decimal 
+number between 0 and 255, inclusive.
+
+.IP \fBicmp_code=\fIcode\fR
+Matches ICMP messages with \fIcode\fR.
+
 .PP
 The following shorthand notations are also available:
 
index 44e27e8..3384700 100644 (file)
@@ -742,6 +742,8 @@ parse_field(const char *name, const struct field **f_out)
         { "nw_proto", OFPFW_NW_PROTO, F_U8, F_OFS(nw_proto) },
         { "tp_src", OFPFW_TP_SRC, F_U16, F_OFS(tp_src) },
         { "tp_dst", OFPFW_TP_DST, F_U16, F_OFS(tp_dst) },
+        { "icmp_type", OFPFW_ICMP_TYPE, F_U16, F_OFS(icmp_type) },
+        { "icmp_code", OFPFW_ICMP_CODE, F_U16, F_OFS(icmp_code) }
     };
     const struct field *f;