From 320437cd400e00973a65daa480612d43e6c26d79 Mon Sep 17 00:00:00 2001 From: Justin Pettit Date: Wed, 26 Nov 2008 20:57:54 -0800 Subject: [PATCH] Add support for understanding ICMP type and code in flow entries. --- datapath/flow.c | 24 +++++++++++++++++++++++- datapath/flow.h | 5 +++++ include/openflow/openflow.h | 10 ++++++++++ lib/flow.c | 17 +++++++++++++++++ lib/ofp-print.c | 15 +++++++++++---- lib/packets.h | 8 ++++++++ switch/switch-flow.c | 3 ++- utilities/dpctl.8.in | 7 +++++++ utilities/dpctl.c | 2 ++ 9 files changed, 85 insertions(+), 6 deletions(-) diff --git a/datapath/flow.c b/datapath/flow.c index 10060b05..effd0c83 100644 --- a/datapath/flow.c +++ b/datapath/flow.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -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; diff --git a/datapath/flow.h b/datapath/flow.h index 4db6204f..f0f8c66e 100644 --- a/datapath/flow.h +++ b/datapath/flow.h @@ -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, diff --git a/include/openflow/openflow.h b/include/openflow/openflow.h index 15c73ad5..e7da0d1c 100644 --- a/include/openflow/openflow.h +++ b/include/openflow/openflow.h @@ -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 diff --git a/lib/flow.c b/lib/flow.c index 8a02d7f0..70a9c4b2 100644 --- a/lib/flow.c +++ b/lib/flow.c @@ -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; diff --git a/lib/ofp-print.c b/lib/ofp-print.c index c145b3d1..41aed2fd 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -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' diff --git a/lib/packets.h b/lib/packets.h index 09ee05dc..adda6bdd 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -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; diff --git a/switch/switch-flow.c b/switch/switch-flow.c index 8e3b7f7a..4ea258b9 100644 --- a/switch/switch-flow.c +++ b/switch/switch-flow.c @@ -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 { diff --git a/utilities/dpctl.8.in b/utilities/dpctl.8.in index 65148614..d1f03bb6 100644 --- a/utilities/dpctl.8.in +++ b/utilities/dpctl.8.in @@ -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: diff --git a/utilities/dpctl.c b/utilities/dpctl.c index 44e27e8f..33847009 100644 --- a/utilities/dpctl.c +++ b/utilities/dpctl.c @@ -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; -- 2.30.2