X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=datapath%2Fflow.c;h=9dcec90afe01bf72f4a4b4b50b889b9e74c33866;hb=1489ca26576404613c36f5684cba5fe6952ccd05;hp=d4c4802138f76602d15ada6bb2c69fa3b61b5e75;hpb=b0a11b7a5a911a238eb90c85613e62353086bbb7;p=sliver-openvswitch.git diff --git a/datapath/flow.c b/datapath/flow.c index d4c480213..9dcec90af 100644 --- a/datapath/flow.c +++ b/datapath/flow.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -28,46 +29,66 @@ struct kmem_cache *flow_cache; /* Internal function used to compare fields in flow. */ static inline int flow_fields_match(const struct sw_flow_key *a, const struct sw_flow_key *b, - uint16_t w) + uint32_t w, uint32_t src_mask, uint32_t dst_mask) { return ((w & OFPFW_IN_PORT || a->in_port == b->in_port) && (w & OFPFW_DL_VLAN || a->dl_vlan == b->dl_vlan) && (w & OFPFW_DL_SRC || !memcmp(a->dl_src, b->dl_src, ETH_ALEN)) && (w & OFPFW_DL_DST || !memcmp(a->dl_dst, b->dl_dst, ETH_ALEN)) && (w & OFPFW_DL_TYPE || a->dl_type == b->dl_type) - && (w & OFPFW_NW_SRC || a->nw_src == b->nw_src) - && (w & OFPFW_NW_DST || a->nw_dst == b->nw_dst) + && !((a->nw_src ^ b->nw_src) & src_mask) + && !((a->nw_dst ^ b->nw_dst) & dst_mask) && (w & OFPFW_NW_PROTO || a->nw_proto == b->nw_proto) && (w & OFPFW_TP_SRC || a->tp_src == b->tp_src) && (w & OFPFW_TP_DST || a->tp_dst == b->tp_dst)); } /* Returns nonzero if 'a' and 'b' match, that is, if their fields are equal - * modulo wildcards, zero otherwise. */ -int flow_matches(const struct sw_flow_key *a, const struct sw_flow_key *b) + * modulo wildcards in 'b', zero otherwise. */ +int flow_matches_1wild(const struct sw_flow_key *a, + const struct sw_flow_key *b) { - return flow_fields_match(a, b, (a->wildcards | b->wildcards)); + return flow_fields_match(a, b, b->wildcards, + b->nw_src_mask, b->nw_dst_mask); } -EXPORT_SYMBOL(flow_matches); +EXPORT_SYMBOL(flow_matches_1wild); -/* Returns nonzero if 't' (the table entry's key) and 'd' (the key - * describing the deletion) match, that is, if their fields are +/* Returns nonzero if 'a' and 'b' match, that is, if their fields are equal + * modulo wildcards in 'a' or 'b', zero otherwise. */ +int flow_matches_2wild(const struct sw_flow_key *a, + const struct sw_flow_key *b) +{ + return flow_fields_match(a, b, + a->wildcards | b->wildcards, + a->nw_src_mask & b->nw_src_mask, + a->nw_dst_mask & b->nw_dst_mask); +} +EXPORT_SYMBOL(flow_matches_2wild); + +/* Returns nonzero if 't' (the table entry's key) and 'd' (the key + * describing the match) match, that is, if their fields are * equal modulo wildcards, zero otherwise. If 'strict' is nonzero, the * wildcards must match in both 't_key' and 'd_key'. Note that the * table's wildcards are ignored unless 'strict' is set. */ -int flow_del_matches(const struct sw_flow_key *t, const struct sw_flow_key *d, int strict) +int flow_matches_desc(const struct sw_flow_key *t, const struct sw_flow_key *d, + int strict) { - if (strict && (t->wildcards != d->wildcards)) + if (strict && d->wildcards != t->wildcards) return 0; + return flow_matches_1wild(t, d); +} +EXPORT_SYMBOL(flow_matches_desc); - return flow_fields_match(t, d, d->wildcards); +static uint32_t make_nw_mask(int n_wild_bits) +{ + n_wild_bits &= (1u << OFPFW_NW_SRC_BITS) - 1; + return n_wild_bits < 32 ? htonl(~((1u << n_wild_bits) - 1)) : 0; } -EXPORT_SYMBOL(flow_del_matches); void flow_extract_match(struct sw_flow_key* to, const struct ofp_match* from) { - to->wildcards = ntohs(from->wildcards) & OFPFW_ALL; - memset(to->pad, '\0', sizeof(to->pad)); + to->wildcards = ntohl(from->wildcards) & OFPFW_ALL; + to->pad = 0; to->in_port = from->in_port; to->dl_vlan = from->dl_vlan; memcpy(to->dl_src, from->dl_src, ETH_ALEN); @@ -78,7 +99,7 @@ void flow_extract_match(struct sw_flow_key* to, const struct ofp_match* from) 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) +#define OFPFW_NW (OFPFW_NW_SRC_MASK | OFPFW_NW_DST_MASK | 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. */ @@ -108,39 +129,61 @@ void flow_extract_match(struct sw_flow_key* to, const struct ofp_match* from) * instead of falling into table-linear. */ to->wildcards &= ~(OFPFW_NW | OFPFW_TP); } + + /* We set these late because code above adjusts to->wildcards. */ + to->nw_src_mask = make_nw_mask(to->wildcards >> OFPFW_NW_SRC_SHIFT); + to->nw_dst_mask = make_nw_mask(to->wildcards >> OFPFW_NW_DST_SHIFT); } void flow_fill_match(struct ofp_match* to, const struct sw_flow_key* from) { - to->wildcards = htons(from->wildcards); + to->wildcards = htonl(from->wildcards); to->in_port = from->in_port; to->dl_vlan = from->dl_vlan; memcpy(to->dl_src, from->dl_src, ETH_ALEN); memcpy(to->dl_dst, from->dl_dst, ETH_ALEN); to->dl_type = from->dl_type; - to->nw_src = from->nw_src; - to->nw_dst = from->nw_dst; + to->nw_src = from->nw_src; + to->nw_dst = from->nw_dst; to->nw_proto = from->nw_proto; - to->tp_src = from->tp_src; - to->tp_dst = from->tp_dst; - memset(to->pad, '\0', sizeof(to->pad)); + to->tp_src = from->tp_src; + to->tp_dst = from->tp_dst; + to->pad = 0; +} + +int flow_timeout(struct sw_flow *flow) +{ + 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_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. */ -struct sw_flow *flow_alloc(int n_actions, gfp_t flags) +/* 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. */ +struct sw_flow *flow_alloc(size_t actions_len, gfp_t flags) { + struct sw_flow_actions *sfa; + size_t size = sizeof *sfa + actions_len; struct sw_flow *flow = kmem_cache_alloc(flow_cache, flags); if (unlikely(!flow)) return NULL; - flow->n_actions = n_actions; - flow->actions = kmalloc(n_actions * sizeof *flow->actions, - flags); - if (unlikely(!flow->actions) && n_actions > 0) { + sfa = kmalloc(size, flags); + if (unlikely(!sfa)) { kmem_cache_free(flow_cache, flow); return NULL; } + sfa->actions_len = actions_len; + flow->sf_acts = sfa; + return flow; } @@ -149,13 +192,13 @@ void flow_free(struct sw_flow *flow) { if (unlikely(!flow)) return; - kfree(flow->actions); + kfree(flow->sf_acts); kmem_cache_free(flow_cache, flow); } EXPORT_SYMBOL(flow_free); /* RCU callback used by flow_deferred_free. */ -static void rcu_callback(struct rcu_head *rcu) +static void rcu_free_flow_callback(struct rcu_head *rcu) { struct sw_flow *flow = container_of(rcu, struct sw_flow, rcu); flow_free(flow); @@ -165,14 +208,53 @@ static void rcu_callback(struct rcu_head *rcu) * The caller must hold rcu_read_lock for this to be sensible. */ void flow_deferred_free(struct sw_flow *flow) { - call_rcu(&flow->rcu, rcu_callback); + call_rcu(&flow->rcu, rcu_free_flow_callback); } EXPORT_SYMBOL(flow_deferred_free); +/* RCU callback used by flow_deferred_free_acts. */ +static void rcu_free_acts_callback(struct rcu_head *rcu) +{ + struct sw_flow_actions *sf_acts = container_of(rcu, + struct sw_flow_actions, rcu); + kfree(sf_acts); +} + +/* Schedules 'sf_acts' to be freed after the next RCU grace period. + * The caller must hold rcu_read_lock for this to be sensible. */ +void flow_deferred_free_acts(struct sw_flow_actions *sf_acts) +{ + call_rcu(&sf_acts->rcu, rcu_free_acts_callback); +} +EXPORT_SYMBOL(flow_deferred_free_acts); + +/* Copies 'actions' into a newly allocated structure for use by 'flow' + * and safely frees the structure that defined the previous actions. */ +void flow_replace_acts(struct sw_flow *flow, + const struct ofp_action_header *actions, size_t actions_len) +{ + struct sw_flow_actions *sfa; + struct sw_flow_actions *orig_sfa = flow->sf_acts; + size_t size = sizeof *sfa + actions_len; + + sfa = kmalloc(size, GFP_ATOMIC); + if (unlikely(!sfa)) + return; + + sfa->actions_len = actions_len; + memcpy(sfa->actions, actions, actions_len); + + rcu_assign_pointer(flow->sf_acts, sfa); + flow_deferred_free_acts(orig_sfa); + + return; +} +EXPORT_SYMBOL(flow_replace_acts); + /* Prints a representation of 'key' to the kernel log. */ void print_flow(const struct sw_flow_key *key) { - printk("wild%04x port%04x:vlan%04x mac%02x:%02x:%02x:%02x:%02x:%02x" + printk("wild%08x port%04x:vlan%04x mac%02x:%02x:%02x:%02x:%02x:%02x" "->%02x:%02x:%02x:%02x:%02x:%02x " "proto%04x ip%u.%u.%u.%u->%u.%u.%u.%u port%d->%d\n", key->wildcards, ntohs(key->in_port), ntohs(key->dl_vlan), @@ -193,18 +275,38 @@ void print_flow(const struct sw_flow_key *key) } 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->pad = 0; key->wildcards = 0; - memset(key->pad, '\0', sizeof(key->pad)); + key->nw_src_mask = 0; + key->nw_dst_mask = 0; /* This code doesn't check that skb->len is long enough to contain the * MAC or network header. With a 46-byte minimum length frame this @@ -223,7 +325,7 @@ void flow_extract(struct sk_buff *skb, uint16_t in_port, if (snap_get_ethertype(skb, &key->dl_type) != -EINVAL) { nh_ofs += sizeof(struct snap_hdr); } else { - key->dl_type = OFP_DL_TYPE_NOT_ETH_TYPE; + key->dl_type = htons(OFP_DL_TYPE_NOT_ETH_TYPE); nh_ofs += sizeof(struct llc_pdu_un); } } @@ -251,25 +353,50 @@ void flow_extract(struct sk_buff *skb, uint16_t in_port, 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) - || nh->frag_off & htons(IP_MF | IP_OFFSET)) { + 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 { + goto no_th; + } + } 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.