ofs->length = htons(length);
ofs->table_id = s->table_idx;
ofs->pad = 0;
- ofs->match.wildcards = htons(flow->key.wildcards);
+ ofs->match.wildcards = htonl(flow->key.wildcards);
ofs->match.in_port = flow->key.in_port;
memcpy(ofs->match.dl_src, flow->key.dl_src, ETH_ALEN);
memcpy(ofs->match.dl_dst, flow->key.dl_dst, ETH_ALEN);
ofs->match.nw_src = flow->key.nw_src;
ofs->match.nw_dst = flow->key.nw_dst;
ofs->match.nw_proto = flow->key.nw_proto;
- memset(ofs->match.pad, 0, sizeof ofs->match.pad);
+ ofs->match.pad = 0;
ofs->match.tp_src = flow->key.tp_src;
ofs->match.tp_dst = flow->key.tp_dst;
ofs->duration = htonl((jiffies - flow->init_time) / HZ);
/* 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 deletion) 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)
{
- if (strict && (t->wildcards != d->wildcards))
+ if (strict && d->wildcards != t->wildcards)
return 0;
-
- return flow_fields_match(t, d, d->wildcards);
+ return flow_matches_1wild(t, d);
}
EXPORT_SYMBOL(flow_del_matches);
+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;
+}
+
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);
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. */
* 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);
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->pad = 0;
}
int flow_timeout(struct sw_flow *flow)
/* 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),
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
inter-flow variability, so that failing bytewise comparisons with memcmp
terminate as quickly as possible on average. */
struct sw_flow_key {
+ uint32_t wildcards; /* Wildcard fields (host byte order). */
uint32_t nw_src; /* IP source address. */
+ uint32_t nw_src_mask; /* 1-bit in each significant nw_src bit. */
uint32_t nw_dst; /* IP destination address. */
+ uint32_t nw_dst_mask; /* 1-bit in each significant nw_dst bit. */
uint16_t in_port; /* Input switch port */
uint16_t dl_vlan; /* Input VLAN. */
uint16_t dl_type; /* Ethernet frame type. */
uint16_t tp_src; /* TCP/UDP source port. */
uint16_t tp_dst; /* TCP/UDP destination port. */
- uint16_t wildcards; /* Wildcard fields (host byte order). */
uint8_t dl_src[ETH_ALEN]; /* Ethernet source address. */
uint8_t dl_dst[ETH_ALEN]; /* Ethernet destination address. */
uint8_t nw_proto; /* IP protocol. */
- uint8_t pad[3]; /* NB: Pad to make 32-bit aligned */
+ uint8_t pad; /* NB: Pad to make 32-bit aligned */
};
/* We need to manually make sure that the structure is 32-bit aligned,
*/
static inline void check_key_align(void)
{
- BUILD_BUG_ON(sizeof(struct sw_flow_key) != 36);
+ BUILD_BUG_ON(sizeof(struct sw_flow_key) != 44);
}
/* Locking:
struct rcu_head rcu;
};
-int flow_matches(const struct sw_flow_key *, const struct sw_flow_key *);
+int flow_matches_1wild(const struct sw_flow_key *, const struct sw_flow_key *);
+int flow_matches_2wild(const struct sw_flow_key *, const struct sw_flow_key *);
int flow_del_matches(const struct sw_flow_key *, const struct sw_flow_key *,
int);
struct sw_flow *flow_alloc(int n_actions, gfp_t flags);
struct sw_table_dummy *td = (struct sw_table_dummy *) swt;
struct sw_flow *flow;
list_for_each_entry (flow, &td->flows, node) {
- if (flow_matches(&flow->key, key)) {
+ if (flow_matches_1wild(key, &flow->key)) {
return flow;
}
}
start = ~position->private[0];
list_for_each_entry (flow, &tl->iter_flows, iter_node) {
- if (flow->serial <= start && flow_matches(key, &flow->key)) {
+ if (flow->serial <= start && flow_matches_2wild(key,
+ &flow->key)) {
int error = callback(flow, private);
if (error) {
position->private[0] = ~flow->serial;
for (i = position->private[0]; i <= th->bucket_mask; i++) {
struct sw_flow *flow = th->buckets[i];
- if (flow && flow_matches(key, &flow->key)) {
+ if (flow && flow_matches_1wild(&flow->key, key)) {
int error = callback(flow, private);
if (error) {
position->private[0] = i;
struct sw_table_linear *tl = (struct sw_table_linear *) swt;
struct sw_flow *flow;
list_for_each_entry_rcu (flow, &tl->flows, node) {
- if (flow_matches(&flow->key, key))
+ if (flow_matches_1wild(key, &flow->key))
return flow;
}
return NULL;
list_for_each_entry (f, &tl->flows, node) {
if (f->priority == flow->priority
&& f->key.wildcards == flow->key.wildcards
- && flow_matches(&f->key, &flow->key)) {
+ && flow_matches_2wild(&f->key, &flow->key)) {
flow->serial = f->serial;
list_replace_rcu(&f->node, &flow->node);
list_replace_rcu(&f->iter_node, &flow->iter_node);
start = position->private[0];
list_for_each_entry (flow, &tl->iter_flows, iter_node) {
- if (flow->serial >= start && flow_matches(key, &flow->key)) {
+ if (flow->serial >= start
+ && flow_matches_2wild(key, &flow->key)) {
int error = callback(flow, private);
if (error) {
position->private[0] = flow->serial;
}
static void
-simple_insert_delete(struct sw_table *swt, uint16_t wildcards)
+simple_insert_delete(struct sw_table *swt, uint32_t wildcards)
{
struct sw_flow *a_flow = flow_zalloc(0, GFP_KERNEL);
struct sw_flow *b_flow = flow_zalloc(0, GFP_KERNEL);
}
static void
-multiple_insert_destroy(struct sw_table *swt, int inserts, uint16_t wildcards,
+multiple_insert_destroy(struct sw_table *swt, int inserts, uint32_t wildcards,
int min_collisions, int max_collisions)
{
int i;
}
static void
-set_random_key(struct sw_flow_key *key, uint16_t wildcards)
+set_random_key(struct sw_flow_key *key, uint32_t wildcards)
{
key->nw_src = random32();
key->nw_dst = random32();
*/
static struct flow_key_entry *
-allocate_random_keys(int n_keys, uint16_t wildcards)
+allocate_random_keys(int n_keys, uint32_t wildcards)
{
struct flow_key_entry *entries, *pos;
struct list_head *keys;
*/
static int
-iterator_test(struct sw_table *swt, int n_flows, uint16_t wildcards)
+iterator_test(struct sw_table *swt, int n_flows, uint32_t wildcards)
{
struct flow_key_entry *allocated, h1, h2;
struct list_head *added, *deleted, *tmp;
*/
static int
-add_test(struct sw_table *swt, uint16_t wildcards)
+add_test(struct sw_table *swt, uint32_t wildcards)
{
struct flow_key_entry *allocated, h1, h2;
struct list_head *added, *deleted, *tmp, *tmp2;
*/
static int
-delete_test(struct sw_table *swt, uint16_t wildcards)
+delete_test(struct sw_table *swt, uint32_t wildcards)
{
struct flow_key_entry *allocated, h1, h2;
struct list_head *added, *deleted, *tmp, *tmp2;
*/
static int
-complex_add_delete_test(struct sw_table *swt, int n_flows, int i, uint16_t wildcards)
+complex_add_delete_test(struct sw_table *swt, int n_flows, int i, uint32_t wildcards)
{
struct flow_key_entry *allocated, h1, h2;
struct list_head *added, *deleted, *tmp;
/* The most significant bit being set in the version field indicates an
* experimental OpenFlow version.
*/
-#define OFP_VERSION 0x88
+#define OFP_VERSION 0x89
#define OFP_MAX_TABLE_NAME_LEN 32
#define OFP_MAX_PORT_NAME_LEN 16
OFPFW_DL_SRC = 1 << 2, /* Ethernet source address. */
OFPFW_DL_DST = 1 << 3, /* Ethernet destination address. */
OFPFW_DL_TYPE = 1 << 4, /* Ethernet frame type. */
- OFPFW_NW_SRC = 1 << 5, /* IP source address. */
- OFPFW_NW_DST = 1 << 6, /* IP destination address. */
- OFPFW_NW_PROTO = 1 << 7, /* IP protocol. */
- OFPFW_TP_SRC = 1 << 8, /* TCP/UDP source port. */
- OFPFW_TP_DST = 1 << 9, /* TCP/UDP destination port. */
- OFPFW_ALL = (1 << 10) - 1
+ OFPFW_NW_PROTO = 1 << 5, /* IP protocol. */
+ OFPFW_TP_SRC = 1 << 6, /* TCP/UDP source port. */
+ OFPFW_TP_DST = 1 << 7, /* TCP/UDP destination port. */
+
+ /* IP source address wildcard bit count. 0 is exact match, 1 ignores the
+ * LSB, 2 ignores the 2 least-significant bits, ..., 32 and higher wildcard
+ * the entire field. This is the *opposite* of the usual convention where
+ * e.g. /24 indicates that 8 bits (not 24 bits) are wildcarded. */
+ OFPFW_NW_SRC_SHIFT = 8,
+ OFPFW_NW_SRC_BITS = 6,
+ OFPFW_NW_SRC_MASK = ((1 << OFPFW_NW_SRC_BITS) - 1) << OFPFW_NW_SRC_SHIFT,
+ OFPFW_NW_SRC_ALL = 32 << OFPFW_NW_SRC_SHIFT,
+
+ /* IP destination address wildcard bit count. Same format as source. */
+ OFPFW_NW_DST_SHIFT = 14,
+ OFPFW_NW_DST_BITS = 6,
+ OFPFW_NW_DST_MASK = ((1 << OFPFW_NW_DST_BITS) - 1) << OFPFW_NW_DST_SHIFT,
+ OFPFW_NW_DST_ALL = 32 << OFPFW_NW_DST_SHIFT,
+
+ /* Wildcard all fields. */
+ OFPFW_ALL = ((1 << 20) - 1)
};
/* Values below this cutoff are 802.3 packets and the two bytes
/* Fields to match against flows */
struct ofp_match {
- uint16_t wildcards; /* Wildcard fields. */
+ uint32_t wildcards; /* Wildcard fields. */
uint16_t in_port; /* Input switch port. */
uint8_t dl_src[OFP_ETH_ALEN]; /* Ethernet source address. */
uint8_t dl_dst[OFP_ETH_ALEN]; /* Ethernet destination address. */
uint16_t dl_vlan; /* Input VLAN. */
uint16_t dl_type; /* Ethernet frame type. */
+ uint8_t nw_proto; /* IP protocol. */
+ uint8_t pad; /* Align to 32-bits. */
uint32_t nw_src; /* IP source address. */
uint32_t nw_dst; /* IP destination address. */
- uint8_t nw_proto; /* IP protocol. */
- uint8_t pad[3]; /* Align to 32-bits. */
uint16_t tp_src; /* TCP/UDP source port. */
uint16_t tp_dst; /* TCP/UDP destination port. */
};
#define IP_IHL(ip_ihl_ver) ((ip_ihl_ver) & 15)
#define IP_IHL_VER(ihl, ver) (((ver) << 4) | (ihl))
+#define IP_TYPE_ICMP 1
#define IP_TYPE_TCP 6
#define IP_TYPE_UDP 17
ds_put_char(string, ',');
}
+static void
+print_ip_netmask(struct ds *string, const char *leader, uint32_t ip,
+ uint32_t wild_bits, int verbosity)
+{
+ if (wild_bits >= 32 && verbosity < 2) {
+ return;
+ }
+ ds_put_cstr(string, leader);
+ if (wild_bits < 32) {
+ ds_put_format(string, IP_FMT, IP_ARGS(&ip));
+ if (wild_bits) {
+ ds_put_format(string, "/%d", 32 - wild_bits);
+ }
+ } else {
+ ds_put_char(string, '*');
+ }
+ ds_put_char(string, ',');
+}
+
/* Pretty-print the ofp_match structure */
static void ofp_print_match(struct ds *f, const struct ofp_match *om,
int verbosity)
{
- uint16_t w = ntohs(om->wildcards);
-
+ uint32_t w = ntohl(om->wildcards);
+ bool skip_type = false;
+ bool skip_proto = false;
+
+ if (!(w & OFPFW_DL_TYPE) &&om->dl_type == htons(ETH_TYPE_IP)) {
+ skip_type = true;
+ if (!(w & OFPFW_NW_PROTO)) {
+ skip_proto = true;
+ if (om->nw_proto == IP_TYPE_ICMP) {
+ ds_put_cstr(f, "icmp,");
+ } else if (om->nw_proto == IP_TYPE_TCP) {
+ ds_put_cstr(f, "tcp,");
+ } else if (om->nw_proto == IP_TYPE_UDP) {
+ ds_put_cstr(f, "udp,");
+ } else {
+ ds_put_cstr(f, "ip,");
+ skip_proto = false;
+ }
+ } else {
+ ds_put_cstr(f, "ip,");
+ }
+ }
print_wild(f, "in_port=", w & OFPFW_IN_PORT, verbosity,
"%d", ntohs(om->in_port));
print_wild(f, "dl_vlan=", w & OFPFW_DL_VLAN, verbosity,
ETH_ADDR_FMT, ETH_ADDR_ARGS(om->dl_src));
print_wild(f, "dl_dst=", w & OFPFW_DL_DST, verbosity,
ETH_ADDR_FMT, ETH_ADDR_ARGS(om->dl_dst));
- print_wild(f, "dl_type=", w & OFPFW_DL_TYPE, verbosity,
- "0x%04x", ntohs(om->dl_type));
- print_wild(f, "nw_src=", w & OFPFW_NW_SRC, verbosity,
- IP_FMT, IP_ARGS(&om->nw_src));
- print_wild(f, "nw_dst=", w & OFPFW_NW_DST, verbosity,
- IP_FMT, IP_ARGS(&om->nw_dst));
- print_wild(f, "nw_proto=", w & OFPFW_NW_PROTO, verbosity,
- "%u", om->nw_proto);
+ if (!skip_type) {
+ print_wild(f, "dl_type=", w & OFPFW_DL_TYPE, verbosity,
+ "0x%04x", ntohs(om->dl_type));
+ }
+ print_ip_netmask(f, "nw_src=", om->nw_src,
+ (w & OFPFW_NW_SRC_MASK) >> OFPFW_NW_SRC_SHIFT, verbosity);
+ print_ip_netmask(f, "nw_dst=", om->nw_dst,
+ (w & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT, verbosity);
+ if (!skip_proto) {
+ 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,
ofm->header.version = OFP_VERSION;
ofm->header.type = OFPT_FLOW_MOD;
ofm->header.length = htons(size);
- ofm->match.wildcards = htons(0);
+ ofm->match.wildcards = htonl(0);
ofm->match.in_port = flow->in_port;
memcpy(ofm->match.dl_src, flow->dl_src, sizeof ofm->match.dl_src);
memcpy(ofm->match.dl_dst, flow->dl_dst, sizeof ofm->match.dl_dst);
ofs->length = htons(length);
ofs->table_id = table_idx;
ofs->pad = 0;
- ofs->match.wildcards = htons(flow->key.wildcards);
+ ofs->match.wildcards = htonl(flow->key.wildcards);
ofs->match.in_port = flow->key.flow.in_port;
memcpy(ofs->match.dl_src, flow->key.flow.dl_src, ETH_ADDR_LEN);
memcpy(ofs->match.dl_dst, flow->key.flow.dl_dst, ETH_ADDR_LEN);
ofs->match.nw_src = flow->key.flow.nw_src;
ofs->match.nw_dst = flow->key.flow.nw_dst;
ofs->match.nw_proto = flow->key.flow.nw_proto;
- memset(ofs->match.pad, 0, sizeof ofs->match.pad);
+ ofs->match.pad = 0;
ofs->match.tp_src = flow->key.flow.tp_src;
ofs->match.tp_dst = flow->key.flow.tp_dst;
ofs->duration = htonl(now - flow->created);
#include "timeval.h"
/* Internal function used to compare fields in flow. */
-static inline
-int flow_fields_match(const struct flow *a, const struct flow *b, uint16_t w)
+static inline int
+flow_fields_match(const struct flow *a, const struct flow *b, uint16_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_ADDR_LEN))
- && (w & OFPFW_DL_DST || !memcmp(a->dl_dst, b->dl_dst, ETH_ADDR_LEN))
+ && (w & OFPFW_DL_SRC || eth_addr_equals(a->dl_src, b->dl_src))
+ && (w & OFPFW_DL_DST || eth_addr_equals(a->dl_dst, b->dl_dst))
&& (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));
}
+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;
+}
+
+/* Returns nonzero if 'a' and 'b' match, that is, if their fields are equal
+ * modulo wildcards in 'b', zero otherwise. */
+inline int
+flow_matches_1wild(const struct sw_flow_key *a, const struct sw_flow_key *b)
+{
+ return flow_fields_match(&a->flow, &b->flow, b->wildcards,
+ b->nw_src_mask, b->nw_dst_mask);
+}
+
/* Returns nonzero if 'a' and 'b' match, that is, if their fields are equal
- * modulo wildcards, zero otherwise. */
-inline
-int flow_matches(const struct sw_flow_key *a, const struct sw_flow_key *b)
+ * modulo wildcards in 'a' or 'b', zero otherwise. */
+inline int
+flow_matches_2wild(const struct sw_flow_key *a, const struct sw_flow_key *b)
{
- return flow_fields_match(&a->flow, &b->flow, a->wildcards | b->wildcards);
+ return flow_fields_match(&a->flow, &b->flow, a->wildcards | b->wildcards,
+ a->nw_src_mask & b->nw_src_mask,
+ a->nw_dst_mask & b->nw_dst_mask);
}
/* Returns nonzero if 't' (the table entry's key) and 'd' (the key
* 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. */
-inline
-int flow_del_matches(const struct sw_flow_key *t, const struct sw_flow_key *d, int strict)
+int
+flow_del_matches(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_fields_match(&t->flow, &d->flow, d->wildcards);
+ }
+ return flow_matches_1wild(t, d);
}
-void flow_extract_match(struct sw_flow_key* to, const struct ofp_match* from)
+void
+flow_extract_match(struct sw_flow_key* to, const struct ofp_match* from)
{
- to->wildcards = ntohs(from->wildcards) & OFPFW_ALL;
+ to->wildcards = ntohl(from->wildcards) & OFPFW_ALL;
to->flow.reserved = 0;
to->flow.in_port = from->in_port;
to->flow.dl_vlan = from->dl_vlan;
to->flow.tp_src = to->flow.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. */
* 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)
+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->flow.in_port;
to->dl_vlan = from->flow.dl_vlan;
memcpy(to->dl_src, from->flow.dl_src, ETH_ADDR_LEN);
to->nw_proto = from->flow.nw_proto;
to->tp_src = from->flow.tp_src;
to->tp_dst = from->flow.tp_dst;
- memset(to->pad, '\0', sizeof(to->pad));
+ to->pad = 0;
}
/* 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)
+struct sw_flow *
+flow_alloc(int n_actions)
{
struct sw_flow *flow = malloc(sizeof *flow);
if (!flow)
}
/* Frees 'flow' immediately. */
-void flow_free(struct sw_flow *flow)
+void
+flow_free(struct sw_flow *flow)
{
if (!flow) {
return;
}
/* Prints a representation of 'key' to the kernel log. */
-void print_flow(const struct sw_flow_key *key)
+void
+print_flow(const struct sw_flow_key *key)
{
const struct flow *f = &key->flow;
- printf("wild%04x port%04x:vlan%04x mac%02x:%02x:%02x:%02x:%02x:%02x"
+ printf("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(f->in_port), ntohs(f->dl_vlan),
struct sw_flow_key {
struct flow flow; /* Flow data (in network byte order). */
uint32_t wildcards; /* Wildcard fields (in host byte order). */
+ uint32_t nw_src_mask; /* 1-bit in each significant nw_src bit. */
+ uint32_t nw_dst_mask; /* 1-bit in each significant nw_dst bit. */
};
struct sw_flow {
struct ofp_action *actions;
};
-int flow_matches(const struct sw_flow_key *, const struct sw_flow_key *);
+int flow_matches_1wild(const struct sw_flow_key *, const struct sw_flow_key *);
+int flow_matches_2wild(const struct sw_flow_key *, const struct sw_flow_key *);
int flow_del_matches(const struct sw_flow_key *, const struct sw_flow_key *,
int);
struct sw_flow *flow_alloc(int n_actions);
for (i = position->private[0]; i <= th->bucket_mask; i++) {
struct sw_flow *flow = th->buckets[i];
- if (flow && flow_matches(key, &flow->key)) {
+ if (flow && flow_matches_1wild(&flow->key, key)) {
int error = callback(flow, private);
if (error) {
position->private[0] = i + 1;
struct sw_table_linear *tl = (struct sw_table_linear *) swt;
struct sw_flow *flow;
LIST_FOR_EACH (flow, struct sw_flow, node, &tl->flows) {
- if (flow_matches(&flow->key, key))
+ if (flow_matches_1wild(key, &flow->key))
return flow;
}
return NULL;
LIST_FOR_EACH (f, struct sw_flow, node, &tl->flows) {
if (f->priority == flow->priority
&& f->key.wildcards == flow->key.wildcards
- && flow_matches(&f->key, &flow->key)) {
+ && flow_matches_2wild(&f->key, &flow->key)) {
flow->serial = f->serial;
list_replace(&flow->node, &f->node);
list_replace(&flow->iter_node, &f->iter_node);
start = ~position->private[0];
LIST_FOR_EACH (flow, struct sw_flow, iter_node, &tl->iter_flows) {
- if (flow->serial <= start && flow_matches(key, &flow->key)) {
+ if (flow->serial <= start && flow_matches_2wild(key, &flow->key)) {
int error = callback(flow, private);
if (error) {
position->private[0] = ~(flow->serial - 1);
decimal or as a hexadecimal number prefixed by \fB0x\fR,
e.g. \fB0x0806\fR to match ARP packets.
-.IP \fBnw_src=\fIip\fR
+.IP \fBnw_src=\fIip\fR[\fB/\fInetmask\fR]
Matches IPv4 source address \fIip\fR, which should be specified as an
IP address or host name, e.g. \fB192.168.1.1\fR or
-\fBwww.example.com\fR.
+\fBwww.example.com\fR. The optional \fInetmask\fR allows matching
+only on an IPv4 address prefix. It may be specified as a dotted quad
+(e.g. \fB192.168.1.0/255.255.255.0\fR) or as a count of bits
+(e.g. \fB192.168.1.0/24\fR).
-.IP \fBnw_dst=\fInw_dst\fR
+.IP \fBnw_dst=\fIip\fR[\fB/\fInetmask\fR]
Matches IPv4 destination address \fIip\fR.
.IP \fBnw_proto=\fIproto\fR
*/
#include <config.h>
+#include <arpa/inet.h>
#include <errno.h>
#include <getopt.h>
#include <inttypes.h>
static uint32_t
str_to_int(const char *str)
{
+ char *tail;
uint32_t value;
- if (sscanf(str, "%"SCNu32, &value) != 1) {
+
+ errno = 0;
+ value = strtoul(str, &tail, 0);
+ if (errno == EINVAL || errno == ERANGE || *tail) {
fatal(0, "invalid numeric format %s", str);
}
return value;
}
}
-static void
-str_to_ip(const char *str, uint32_t *ip)
+static uint32_t
+str_to_ip(const char *str_, uint32_t *ip)
{
+ char *str = xstrdup(str_);
+ char *save_ptr = NULL;
+ const char *name, *netmask;
struct in_addr in_addr;
- int retval;
+ int n_wild, retval;
- retval = lookup_ip(str, &in_addr);
+ name = strtok_r(str, "//", &save_ptr);
+ retval = name ? lookup_ip(name, &in_addr) : EINVAL;
if (retval) {
fatal(0, "%s: could not convert to IP address", str);
}
*ip = in_addr.s_addr;
+
+ netmask = strtok_r(NULL, "//", &save_ptr);
+ if (netmask) {
+ uint8_t o[4];
+ if (sscanf(netmask, "%"SCNu8".%"SCNu8".%"SCNu8".%"SCNu8,
+ &o[0], &o[1], &o[2], &o[3]) == 4) {
+ uint32_t nm = (o[0] << 24) | (o[1] << 16) | (o[2] << 8) | o[3];
+ int i;
+
+ /* Find first 1-bit. */
+ for (i = 0; i < 32; i++) {
+ if (nm & (1u << i)) {
+ break;
+ }
+ }
+ n_wild = i;
+
+ /* Verify that the rest of the bits are 1-bits. */
+ for (; i < 32; i++) {
+ if (!(nm & (1u << i))) {
+ fatal(0, "%s: %s is not a valid netmask", str, netmask);
+ }
+ }
+ } else {
+ int prefix = atoi(netmask);
+ if (prefix <= 0 || prefix > 32) {
+ fatal(0, "%s: network prefix bits not between 1 and 32", str);
+ }
+ n_wild = 32 - prefix;
+ }
+ } else {
+ n_wild = 0;
+ }
+
+ free(str);
+ return n_wild;
}
static void
const char *name;
uint32_t wildcard;
enum { F_U8, F_U16, F_MAC, F_IP } type;
- size_t offset;
+ size_t offset, shift;
};
#define F_OFS(MEMBER) offsetof(struct ofp_match, MEMBER)
{ "dl_src", OFPFW_DL_SRC, F_MAC, F_OFS(dl_src) },
{ "dl_dst", OFPFW_DL_DST, F_MAC, F_OFS(dl_dst) },
{ "dl_type", OFPFW_DL_TYPE, F_U16, F_OFS(dl_type) },
- { "nw_src", OFPFW_NW_SRC, F_IP, F_OFS(nw_src) },
- { "nw_dst", OFPFW_NW_DST, F_IP, F_OFS(nw_dst) },
+ { "nw_src", OFPFW_NW_SRC_MASK, F_IP,
+ F_OFS(nw_src), OFPFW_NW_SRC_SHIFT },
+ { "nw_dst", OFPFW_NW_DST_MASK, F_IP,
+ F_OFS(nw_dst), OFPFW_NW_DST_SHIFT },
{ "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) },
} else if (f->type == F_MAC) {
str_to_mac(value, data);
} else if (f->type == F_IP) {
- str_to_ip(value, data);
+ wildcards |= str_to_ip(value, data) << f->shift;
} else {
NOT_REACHED();
}
if (name && !value) {
fatal(0, "field %s missing value", name);
}
- match->wildcards = htons(wildcards);
+ match->wildcards = htonl(wildcards);
}
static void do_dump_flows(int argc, char *argv[])