#include <linux/if_vlan.h>
#include <net/llc_pdu.h>
#include <linux/ip.h>
+#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/in.h>
#include <linux/rcupdate.h>
+#include <net/ip.h>
#include "openflow.h"
#include "compat.h"
memcpy(to->dl_dst, from->dl_dst, ETH_ALEN);
to->dl_type = from->dl_type;
- if (likely(from->dl_type == htons(ETH_P_IP))) {
+ to->nw_src = to->nw_dst = to->nw_proto = 0;
+ to->tp_src = to->tp_dst = 0;
+
+#define OFPFW_TP (OFPFW_TP_SRC | OFPFW_TP_DST)
+#define OFPFW_NW (OFPFW_NW_SRC | OFPFW_NW_DST | OFPFW_NW_PROTO)
+ if (to->wildcards & OFPFW_DL_TYPE) {
+ /* Can't sensibly match on network or transport headers if the
+ * data link type is unknown. */
+ to->wildcards |= OFPFW_NW | OFPFW_TP;
+ } else if (from->dl_type == htons(ETH_P_IP)) {
to->nw_src = from->nw_src;
to->nw_dst = from->nw_dst;
to->nw_proto = from->nw_proto;
- if ((from->nw_proto != IPPROTO_TCP && from->nw_proto != IPPROTO_UDP)) {
- goto no_th;
+ if (to->wildcards & OFPFW_NW_PROTO) {
+ /* Can't sensibly match on transport headers if the
+ * network protocol is unknown. */
+ to->wildcards |= OFPFW_TP;
+ } else if (from->nw_proto == IPPROTO_TCP
+ || from->nw_proto == IPPROTO_UDP) {
+ to->tp_src = from->tp_src;
+ to->tp_dst = from->tp_dst;
+ } else {
+ /* Transport layer fields are undefined. Mark them as
+ * exact-match to allow such flows to reside in
+ * table-hash, instead of falling into table-linear. */
+ to->wildcards &= ~OFPFW_TP;
}
- to->tp_src = from->tp_src;
- to->tp_dst = from->tp_dst;
- return;
+ } else {
+ /* Network and transport layer fields are undefined. Mark them
+ * as exact-match to allow such flows to reside in table-hash,
+ * instead of falling into table-linear. */
+ to->wildcards &= ~(OFPFW_NW | OFPFW_TP);
}
-
- to->nw_src = 0;
- to->nw_dst = 0;
- to->nw_proto = 0;
-
-no_th:
- to->tp_src = 0;
- to->tp_dst = 0;
}
void flow_fill_match(struct ofp_match* to, const struct sw_flow_key* from)
memset(to->pad, '\0', sizeof(to->pad));
}
-/* Returns true if 'flow' can be deleted and set up for a deferred free, false
- * if deletion has already been scheduled (by another thread).
- *
- * Caller must hold rcu_read_lock. */
-int flow_del(struct sw_flow *flow)
+int flow_timeout(struct sw_flow *flow)
{
- return !atomic_cmpxchg(&flow->deleted, 0, 1);
+ if (flow->idle_timeout != OFP_FLOW_PERMANENT
+ && time_after(jiffies, flow->used + flow->idle_timeout * HZ))
+ return OFPER_IDLE_TIMEOUT;
+ else if (flow->hard_timeout != OFP_FLOW_PERMANENT
+ && time_after(jiffies,
+ flow->init_time + flow->hard_timeout * HZ))
+ return OFPER_HARD_TIMEOUT;
+ else
+ return -1;
}
-EXPORT_SYMBOL(flow_del);
+EXPORT_SYMBOL(flow_timeout);
/* Allocates and returns a new flow with 'n_actions' action, using allocation
* flags 'flags'. Returns the new flow or a null pointer on failure. */
}
EXPORT_SYMBOL(print_flow);
+static int tcphdr_ok(struct sk_buff *skb)
+{
+ int th_ofs = skb_transport_offset(skb);
+ if (skb->len >= th_ofs + sizeof(struct tcphdr)) {
+ int tcp_len = tcp_hdrlen(skb);
+ return (tcp_len >= sizeof(struct tcphdr)
+ && skb->len >= th_ofs + tcp_len);
+ }
+ return 0;
+}
+
+static int udphdr_ok(struct sk_buff *skb)
+{
+ int th_ofs = skb_transport_offset(skb);
+ return skb->len >= th_ofs + sizeof(struct udphdr);
+}
+
/* Parses the Ethernet frame in 'skb', which was received on 'in_port',
- * and initializes 'key' to match. */
-void flow_extract(struct sk_buff *skb, uint16_t in_port,
- struct sw_flow_key *key)
+ * and initializes 'key' to match. Returns 1 if 'skb' contains an IP
+ * fragment, 0 otherwise. */
+int flow_extract(struct sk_buff *skb, uint16_t in_port,
+ struct sw_flow_key *key)
{
struct ethhdr *mac;
- struct udphdr *th;
int nh_ofs, th_ofs;
+ int retval = 0;
key->in_port = htons(in_port);
key->wildcards = 0;
skb_set_transport_header(skb, th_ofs);
/* Transport layer. */
- if ((key->nw_proto != IPPROTO_TCP && key->nw_proto != IPPROTO_UDP)
- || skb->len < th_ofs + sizeof(struct udphdr)) {
+ if (!(nh->frag_off & htons(IP_MF | IP_OFFSET))) {
+ if (key->nw_proto == IPPROTO_TCP) {
+ if (tcphdr_ok(skb)) {
+ struct tcphdr *tcp = tcp_hdr(skb);
+ key->tp_src = tcp->source;
+ key->tp_dst = tcp->dest;
+ } else {
+ /* Avoid tricking other code into
+ * thinking that this packet has an L4
+ * header. */
+ goto no_proto;
+ }
+ } else if (key->nw_proto == IPPROTO_UDP) {
+ if (udphdr_ok(skb)) {
+ struct udphdr *udp = udp_hdr(skb);
+ key->tp_src = udp->source;
+ key->tp_dst = udp->dest;
+ } else {
+ /* Avoid tricking other code into
+ * thinking that this packet has an L4
+ * header. */
+ goto no_proto;
+ }
+ }
+ } else {
+ retval = 1;
goto no_th;
}
- th = udp_hdr(skb);
- key->tp_src = th->source;
- key->tp_dst = th->dest;
- return;
+ return 0;
}
key->nw_src = 0;
key->nw_dst = 0;
+
+no_proto:
key->nw_proto = 0;
no_th:
key->tp_src = 0;
key->tp_dst = 0;
+ return retval;
}
/* Initializes the flow module.