Implement subnet mask matching in OpenFlow.
authorBen Pfaff <blp@nicira.com>
Thu, 7 Aug 2008 05:00:29 +0000 (22:00 -0700)
committerBen Pfaff <blp@nicira.com>
Mon, 18 Aug 2008 21:26:50 +0000 (14:26 -0700)
18 files changed:
datapath/datapath.c
datapath/flow.c
datapath/flow.h
datapath/hwtable_dummy/hwtable_dummy.c
datapath/table-hash.c
datapath/table-linear.c
datapath/table_t.c
include/openflow.h
include/packets.h
lib/ofp-print.c
lib/vconn.c
switch/datapath.c
switch/switch-flow.c
switch/switch-flow.h
switch/table-hash.c
switch/table-linear.c
utilities/dpctl.8
utilities/dpctl.c

index 6d9a8fe..5c3a84c 100644 (file)
@@ -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);
index afd91bf..a2c96ab 100644 (file)
@@ -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
index 3438d96..00445f4 100644 (file)
@@ -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);
index d6699b9..6f21e1d 100644 (file)
@@ -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;
index 1c0c6e7..46acf80 100644 (file)
@@ -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;
index d2d7e3c..e7c6e67 100644 (file)
@@ -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;
index 3cc6a58..5f7cd1c 100644 (file)
@@ -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;
index cd21673..677bf26 100644 (file)
@@ -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. */
 };
index ea73cb1..83b7184 100644 (file)
@@ -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
 
index f462449..8329e30 100644 (file)
@@ -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,
index 6dfba22..7df6d27 100644 (file)
@@ -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);
index f9f4a8d..77b3aae 100644 (file)
@@ -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);
index eea201c..9c9538d 100644 (file)
 #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),
index 8da8534..0f6c721 100644 (file)
@@ -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);
index 9b63290..90fd87a 100644 (file)
@@ -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;
index fa87763..ea3777f 100644 (file)
@@ -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);
index 7084ad7..a7aaf1f 100644 (file)
@@ -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
index f969412..37ee659 100644 (file)
@@ -32,6 +32,7 @@
  */
 
 #include <config.h>
+#include <arpa/inet.h>
 #include <errno.h>
 #include <getopt.h>
 #include <inttypes.h>
@@ -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[])