#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>
* 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 {
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. */
* 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;
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,
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.
};
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
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)
{
* 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;
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'
};
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;
* 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 {
.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:
{ "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;