Add support for listing and deleting entries based on an output port.
[sliver-openvswitch.git] / datapath / flow.c
index 10060b0..a8c3368 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 {
@@ -165,6 +167,40 @@ int flow_timeout(struct sw_flow *flow)
 }
 EXPORT_SYMBOL(flow_timeout);
 
+/* Returns nonzero if 'flow' contains an output action to 'out_port' or
+ * has the value OFPP_NONE. 'out_port' is in network-byte order. */
+int flow_has_out_port(struct sw_flow *flow, uint16_t out_port)
+{
+       struct sw_flow_actions *sf_acts;
+       size_t actions_len;
+       uint8_t *p;
+
+       if (out_port == htons(OFPP_NONE))
+               return 1;
+
+       sf_acts = rcu_dereference(flow->sf_acts);
+
+       actions_len = sf_acts->actions_len;
+       p = (uint8_t *)sf_acts->actions;
+
+       while (actions_len > 0) {
+               struct ofp_action_header *ah = (struct ofp_action_header *)p;
+               size_t len = ntohs(ah->len);
+
+               if (ah->type == htons(OFPAT_OUTPUT)) {
+                       struct ofp_action_output *oa = (struct ofp_action_output *)p;
+                       if (oa->port == out_port)
+                               return 1;
+               }
+
+               p += len;
+               actions_len -= len;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(flow_has_out_port);
+
 /* Allocates and returns a new flow with room for 'actions_len' actions, 
  * using allocation flags 'flags'.  Returns the new flow or a null pointer 
  * on failure. */
@@ -322,6 +358,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 +446,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;