From: Ben Pfaff Date: Thu, 7 Aug 2008 05:00:29 +0000 (-0700) Subject: Implement subnet mask matching in OpenFlow. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9630e4100553c3531605aa44db65e9eaafe07dad;p=openvswitch Implement subnet mask matching in OpenFlow. --- diff --git a/datapath/datapath.c b/datapath/datapath.c index 6d9a8fe7..5c3a84c4 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -1153,7 +1153,7 @@ static int flow_stats_dump_callback(struct sw_flow *flow, void *private) 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); @@ -1162,7 +1162,7 @@ static int flow_stats_dump_callback(struct sw_flow *flow, void *private) 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); diff --git a/datapath/flow.c b/datapath/flow.c index afd91bf9..a2c96abd 100644 --- a/datapath/flow.c +++ b/datapath/flow.c @@ -29,46 +29,65 @@ 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 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); @@ -79,7 +98,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. */ @@ -109,11 +128,15 @@ 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); @@ -124,7 +147,7 @@ void flow_fill_match(struct ofp_match* to, const struct sw_flow_key* from) 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) @@ -187,7 +210,7 @@ EXPORT_SYMBOL(flow_deferred_free); /* 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), @@ -236,8 +259,10 @@ int flow_extract(struct sk_buff *skb, uint16_t in_port, 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 diff --git a/datapath/flow.h b/datapath/flow.h index 3438d96c..00445f4a 100644 --- a/datapath/flow.h +++ b/datapath/flow.h @@ -23,18 +23,20 @@ struct ofp_flow_mod; 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, @@ -43,7 +45,7 @@ struct sw_flow_key { */ 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: @@ -79,7 +81,8 @@ struct sw_flow { 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); diff --git a/datapath/hwtable_dummy/hwtable_dummy.c b/datapath/hwtable_dummy/hwtable_dummy.c index d6699b9d..6f21e1de 100644 --- a/datapath/hwtable_dummy/hwtable_dummy.c +++ b/datapath/hwtable_dummy/hwtable_dummy.c @@ -73,7 +73,7 @@ static struct sw_flow *table_dummy_lookup(struct sw_table *swt, 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; } } @@ -196,7 +196,8 @@ static int table_dummy_iterate(struct sw_table *swt, 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; diff --git a/datapath/table-hash.c b/datapath/table-hash.c index 1c0c6e73..46acf80e 100644 --- a/datapath/table-hash.c +++ b/datapath/table-hash.c @@ -169,7 +169,7 @@ static int table_hash_iterate(struct sw_table *swt, 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; diff --git a/datapath/table-linear.c b/datapath/table-linear.c index d2d7e3c7..e7c6e673 100644 --- a/datapath/table-linear.c +++ b/datapath/table-linear.c @@ -28,7 +28,7 @@ static struct sw_flow *table_linear_lookup(struct sw_table *swt, 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; @@ -47,7 +47,7 @@ static int table_linear_insert(struct sw_table *swt, struct sw_flow *flow) 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); @@ -140,7 +140,8 @@ static int table_linear_iterate(struct sw_table *swt, 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; diff --git a/datapath/table_t.c b/datapath/table_t.c index 3cc6a58d..5f7cd1cb 100644 --- a/datapath/table_t.c +++ b/datapath/table_t.c @@ -43,7 +43,7 @@ static struct sw_flow *flow_zalloc(int n_actions, gfp_t flags) } 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); @@ -81,7 +81,7 @@ simple_insert_delete(struct sw_table *swt, uint16_t wildcards) } 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; @@ -124,7 +124,7 @@ multiple_insert_destroy(struct sw_table *swt, int inserts, uint16_t wildcards, } 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(); @@ -156,7 +156,7 @@ struct flow_key_entry { */ 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; @@ -423,7 +423,7 @@ check_lookup_and_iter(struct sw_table *swt, struct list_head *deleted, */ 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; @@ -506,7 +506,7 @@ iterator_test_destr: */ 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; @@ -599,7 +599,7 @@ add_test_destr: */ 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; @@ -700,7 +700,7 @@ delete_test_destr: */ 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; diff --git a/include/openflow.h b/include/openflow.h index cd216736..677bf263 100644 --- a/include/openflow.h +++ b/include/openflow.h @@ -68,7 +68,7 @@ /* 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 @@ -330,12 +330,27 @@ enum ofp_flow_wildcards { 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 @@ -351,16 +366,16 @@ enum ofp_flow_wildcards { /* 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. */ }; diff --git a/include/packets.h b/include/packets.h index ea73cb14..83b71849 100644 --- a/include/packets.h +++ b/include/packets.h @@ -168,6 +168,7 @@ BUILD_ASSERT_DECL(VLAN_ETH_HEADER_LEN == sizeof(struct vlan_eth_header)); #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 diff --git a/lib/ofp-print.c b/lib/ofp-print.c index f462449d..8329e304 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -449,12 +449,51 @@ static void print_wild(struct ds *string, const char *leader, int is_wild, 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, @@ -463,14 +502,18 @@ static void ofp_print_match(struct ds *f, const struct ofp_match *om, 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, diff --git a/lib/vconn.c b/lib/vconn.c index 6dfba22c..7df6d27b 100644 --- a/lib/vconn.c +++ b/lib/vconn.c @@ -500,7 +500,7 @@ make_add_flow(const struct flow *flow, uint32_t buffer_id, 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); diff --git a/switch/datapath.c b/switch/datapath.c index f9f4a8db..77b3aaec 100644 --- a/switch/datapath.c +++ b/switch/datapath.c @@ -747,7 +747,7 @@ fill_flow_stats(struct buffer *buffer, struct sw_flow *flow, 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); @@ -756,7 +756,7 @@ fill_flow_stats(struct buffer *buffer, struct sw_flow *flow, 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); diff --git a/switch/switch-flow.c b/switch/switch-flow.c index eea201c0..9c9538d0 100644 --- a/switch/switch-flow.c +++ b/switch/switch-flow.c @@ -43,27 +43,45 @@ #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 @@ -71,18 +89,19 @@ int flow_matches(const struct sw_flow_key *a, const struct sw_flow_key *b) * 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; @@ -94,7 +113,7 @@ void flow_extract_match(struct sw_flow_key* to, const struct ofp_match* from) 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. */ @@ -124,11 +143,16 @@ 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) +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); @@ -139,12 +163,13 @@ void flow_fill_match(struct ofp_match* to, const struct sw_flow_key* from) 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) @@ -160,7 +185,8 @@ struct sw_flow *flow_alloc(int n_actions) } /* Frees 'flow' immediately. */ -void flow_free(struct sw_flow *flow) +void +flow_free(struct sw_flow *flow) { if (!flow) { return; @@ -170,10 +196,11 @@ void flow_free(struct sw_flow *flow) } /* 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), diff --git a/switch/switch-flow.h b/switch/switch-flow.h index 8da85344..0f6c7216 100644 --- a/switch/switch-flow.h +++ b/switch/switch-flow.h @@ -44,6 +44,8 @@ struct ofp_match; 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 { @@ -68,7 +70,8 @@ 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); diff --git a/switch/table-hash.c b/switch/table-hash.c index 9b632905..90fd87aa 100644 --- a/switch/table-hash.c +++ b/switch/table-hash.c @@ -180,7 +180,7 @@ static int table_hash_iterate(struct sw_table *swt, 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; diff --git a/switch/table-linear.c b/switch/table-linear.c index fa87763b..ea3777f2 100644 --- a/switch/table-linear.c +++ b/switch/table-linear.c @@ -55,7 +55,7 @@ static struct sw_flow *table_linear_lookup(struct sw_table *swt, 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; @@ -73,7 +73,7 @@ static int table_linear_insert(struct sw_table *swt, struct sw_flow *flow) 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); @@ -166,7 +166,7 @@ static int table_linear_iterate(struct sw_table *swt, 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); diff --git a/utilities/dpctl.8 b/utilities/dpctl.8 index 7084ad7e..a7aaf1f3 100644 --- a/utilities/dpctl.8 +++ b/utilities/dpctl.8 @@ -205,12 +205,15 @@ specified as a integer between 0 and 65535, inclusive, either in 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 diff --git a/utilities/dpctl.c b/utilities/dpctl.c index f9694123..37ee6597 100644 --- a/utilities/dpctl.c +++ b/utilities/dpctl.c @@ -32,6 +32,7 @@ */ #include +#include #include #include #include @@ -405,8 +406,12 @@ do_dump_tables(int argc, char *argv[]) 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; @@ -421,17 +426,57 @@ str_to_mac(const char *str, uint8_t mac[6]) } } -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 @@ -510,7 +555,7 @@ str_to_flow(char *string, struct ofp_match *match, 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) @@ -520,8 +565,10 @@ str_to_flow(char *string, struct ofp_match *match, { "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) }, @@ -617,7 +664,7 @@ str_to_flow(char *string, struct ofp_match *match, } 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(); } @@ -626,7 +673,7 @@ str_to_flow(char *string, struct ofp_match *match, 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[])