openflow: Support matching and modifying MPLS TTL field.
[cascardo/ovs.git] / lib / match.c
index 0177f82..e4b28fa 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -190,6 +190,36 @@ match_set_tun_dst_masked(struct match *match, ovs_be32 dst, ovs_be32 mask)
     match->flow.tunnel.ip_dst = dst & mask;
 }
 
+void
+match_set_tun_ipv6_src(struct match *match, const struct in6_addr *src)
+{
+    match->flow.tunnel.ipv6_src = *src;
+    match->wc.masks.tunnel.ipv6_src = in6addr_exact;
+}
+
+void
+match_set_tun_ipv6_src_masked(struct match *match, const struct in6_addr *src,
+                              const struct in6_addr *mask)
+{
+    match->flow.tunnel.ipv6_src = ipv6_addr_bitand(src, mask);
+    match->wc.masks.tunnel.ipv6_src = *mask;
+}
+
+void
+match_set_tun_ipv6_dst(struct match *match, const struct in6_addr *dst)
+{
+    match->flow.tunnel.ipv6_dst = *dst;
+    match->wc.masks.tunnel.ipv6_dst = in6addr_exact;
+}
+
+void
+match_set_tun_ipv6_dst_masked(struct match *match, const struct in6_addr *dst,
+                              const struct in6_addr *mask)
+{
+    match->flow.tunnel.ipv6_dst = ipv6_addr_bitand(dst, mask);
+    match->wc.masks.tunnel.ipv6_dst = *mask;
+}
+
 void
 match_set_tun_ttl(struct match *match, uint8_t ttl)
 {
@@ -284,6 +314,58 @@ match_set_pkt_mark_masked(struct match *match, uint32_t pkt_mark, uint32_t mask)
     match->wc.masks.pkt_mark = mask;
 }
 
+void
+match_set_ct_state(struct match *match, uint32_t ct_state)
+{
+    match_set_ct_state_masked(match, ct_state, UINT32_MAX);
+}
+
+void
+match_set_ct_state_masked(struct match *match, uint32_t ct_state, uint32_t mask)
+{
+    match->flow.ct_state = ct_state & mask & UINT16_MAX;
+    match->wc.masks.ct_state = mask & UINT16_MAX;
+}
+
+void
+match_set_ct_zone(struct match *match, uint16_t ct_zone)
+{
+    match->flow.ct_zone = ct_zone;
+    match->wc.masks.ct_zone = UINT16_MAX;
+}
+
+void
+match_set_ct_mark(struct match *match, uint32_t ct_mark)
+{
+    match_set_ct_mark_masked(match, ct_mark, UINT32_MAX);
+}
+
+void
+match_set_ct_mark_masked(struct match *match, uint32_t ct_mark,
+                           uint32_t mask)
+{
+    match->flow.ct_mark = ct_mark & mask;
+    match->wc.masks.ct_mark = mask;
+}
+
+void
+match_set_ct_label(struct match *match, ovs_u128 ct_label)
+{
+    ovs_u128 mask;
+
+    mask.u64.lo = UINT64_MAX;
+    mask.u64.hi = UINT64_MAX;
+    match_set_ct_label_masked(match, ct_label, mask);
+}
+
+void
+match_set_ct_label_masked(struct match *match, ovs_u128 value, ovs_u128 mask)
+{
+    match->flow.ct_label.u64.lo = value.u64.lo & mask.u64.lo;
+    match->flow.ct_label.u64.hi = value.u64.hi & mask.u64.hi;
+    match->wc.masks.ct_label = mask;
+}
+
 void
 match_set_dl_type(struct match *match, ovs_be16 dl_type)
 {
@@ -294,55 +376,54 @@ match_set_dl_type(struct match *match, ovs_be16 dl_type)
 /* Modifies 'value_src' so that the Ethernet address must match 'value_dst'
  * exactly.  'mask_dst' is set to all 1s. */
 static void
-set_eth(const uint8_t value_src[ETH_ADDR_LEN],
-        uint8_t value_dst[ETH_ADDR_LEN],
-        uint8_t mask_dst[ETH_ADDR_LEN])
+set_eth(const struct eth_addr value_src,
+        struct eth_addr *value_dst,
+        struct eth_addr *mask_dst)
 {
-    memcpy(value_dst, value_src, ETH_ADDR_LEN);
-    memset(mask_dst, 0xff, ETH_ADDR_LEN);
+    *value_dst = value_src;
+    *mask_dst = eth_addr_exact;
 }
 
 /* Modifies 'value_src' so that the Ethernet address must match 'value_src'
  * after each byte is ANDed with the appropriate byte in 'mask_src'.
  * 'mask_dst' is set to 'mask_src' */
 static void
-set_eth_masked(const uint8_t value_src[ETH_ADDR_LEN],
-               const uint8_t mask_src[ETH_ADDR_LEN],
-               uint8_t value_dst[ETH_ADDR_LEN],
-               uint8_t mask_dst[ETH_ADDR_LEN])
+set_eth_masked(const struct eth_addr value_src,
+               const struct eth_addr mask_src,
+               struct eth_addr *value_dst, struct eth_addr *mask_dst)
 {
     size_t i;
 
-    for (i = 0; i < ETH_ADDR_LEN; i++) {
-        value_dst[i] = value_src[i] & mask_src[i];
-        mask_dst[i] = mask_src[i];
+    for (i = 0; i < ARRAY_SIZE(value_dst->be16); i++) {
+        value_dst->be16[i] = value_src.be16[i] & mask_src.be16[i];
     }
+    *mask_dst = mask_src;
 }
 
 /* Modifies 'rule' so that the source Ethernet address must match 'dl_src'
  * exactly. */
 void
-match_set_dl_src(struct match *match, const uint8_t dl_src[ETH_ADDR_LEN])
+match_set_dl_src(struct match *match, const struct eth_addr dl_src)
 {
-    set_eth(dl_src, match->flow.dl_src, match->wc.masks.dl_src);
+    set_eth(dl_src, &match->flow.dl_src, &match->wc.masks.dl_src);
 }
 
 /* Modifies 'rule' so that the source Ethernet address must match 'dl_src'
  * after each byte is ANDed with the appropriate byte in 'mask'. */
 void
 match_set_dl_src_masked(struct match *match,
-                        const uint8_t dl_src[ETH_ADDR_LEN],
-                        const uint8_t mask[ETH_ADDR_LEN])
+                        const struct eth_addr dl_src,
+                        const struct eth_addr mask)
 {
-    set_eth_masked(dl_src, mask, match->flow.dl_src, match->wc.masks.dl_src);
+    set_eth_masked(dl_src, mask, &match->flow.dl_src, &match->wc.masks.dl_src);
 }
 
 /* Modifies 'match' so that the Ethernet address must match 'dl_dst'
  * exactly. */
 void
-match_set_dl_dst(struct match *match, const uint8_t dl_dst[ETH_ADDR_LEN])
+match_set_dl_dst(struct match *match, const struct eth_addr dl_dst)
 {
-    set_eth(dl_dst, match->flow.dl_dst, match->wc.masks.dl_dst);
+    set_eth(dl_dst, &match->flow.dl_dst, &match->wc.masks.dl_dst);
 }
 
 /* Modifies 'match' so that the Ethernet address must match 'dl_dst' after each
@@ -352,10 +433,10 @@ match_set_dl_dst(struct match *match, const uint8_t dl_dst[ETH_ADDR_LEN])
  * accepted by flow_wildcards_is_dl_dst_mask_valid() are allowed. */
 void
 match_set_dl_dst_masked(struct match *match,
-                        const uint8_t dl_dst[ETH_ADDR_LEN],
-                        const uint8_t mask[ETH_ADDR_LEN])
+                        const struct eth_addr dl_dst,
+                        const struct eth_addr mask)
 {
-    set_eth_masked(dl_dst, mask, match->flow.dl_dst, match->wc.masks.dl_dst);
+    set_eth_masked(dl_dst, mask, &match->flow.dl_dst, &match->wc.masks.dl_dst);
 }
 
 void
@@ -510,6 +591,23 @@ match_set_mpls_bos(struct match *match, int idx, uint8_t mpls_bos)
     flow_set_mpls_bos(&match->flow, idx, mpls_bos);
 }
 
+/* Modifies 'match' so that the TTL of MPLS label 'idx' is wildcarded. */
+void
+match_set_any_mpls_ttl(struct match *match, int idx)
+{
+    match->wc.masks.mpls_lse[idx] &= ~htonl(MPLS_TTL_MASK);
+    flow_set_mpls_ttl(&match->flow, idx, 0);
+}
+
+/* Modifies 'match' so that it matches only packets in which the TTL of MPLS
+ * label 'idx' equals 'mpls_ttl'. */
+void
+match_set_mpls_ttl(struct match *match, int idx, uint8_t mpls_ttl)
+{
+    match->wc.masks.mpls_lse[idx] |= htonl(MPLS_TTL_MASK);
+    flow_set_mpls_ttl(&match->flow, idx, mpls_ttl);
+}
+
 /* Modifies 'match' so that the MPLS LSE is wildcarded. */
 void
 match_set_any_mpls_lse(struct match *match, int idx)
@@ -644,35 +742,35 @@ match_set_icmp_code(struct match *match, uint8_t icmp_code)
 }
 
 void
-match_set_arp_sha(struct match *match, const uint8_t sha[ETH_ADDR_LEN])
+match_set_arp_sha(struct match *match, const struct eth_addr sha)
 {
-    memcpy(match->flow.arp_sha, sha, ETH_ADDR_LEN);
-    memset(match->wc.masks.arp_sha, UINT8_MAX, ETH_ADDR_LEN);
+    match->flow.arp_sha = sha;
+    match->wc.masks.arp_sha = eth_addr_exact;
 }
 
 void
 match_set_arp_sha_masked(struct match *match,
-                         const uint8_t arp_sha[ETH_ADDR_LEN],
-                         const uint8_t mask[ETH_ADDR_LEN])
+                         const struct eth_addr arp_sha,
+                         const struct eth_addr mask)
 {
     set_eth_masked(arp_sha, mask,
-                   match->flow.arp_sha, match->wc.masks.arp_sha);
+                   &match->flow.arp_sha, &match->wc.masks.arp_sha);
 }
 
 void
-match_set_arp_tha(struct match *match, const uint8_t tha[ETH_ADDR_LEN])
+match_set_arp_tha(struct match *match, const struct eth_addr tha)
 {
-    memcpy(match->flow.arp_tha, tha, ETH_ADDR_LEN);
-    memset(match->wc.masks.arp_tha, UINT8_MAX, ETH_ADDR_LEN);
+    match->flow.arp_tha = tha;
+    match->wc.masks.arp_tha = eth_addr_exact;
 }
 
 void
 match_set_arp_tha_masked(struct match *match,
-                         const uint8_t arp_tha[ETH_ADDR_LEN],
-                         const uint8_t mask[ETH_ADDR_LEN])
+                         const struct eth_addr arp_tha,
+                         const struct eth_addr mask)
 {
     set_eth_masked(arp_tha, mask,
-                   match->flow.arp_tha, match->wc.masks.arp_tha);
+                   &match->flow.arp_tha, &match->wc.masks.arp_tha);
 }
 
 void
@@ -784,12 +882,11 @@ match_init_hidden_fields(struct match *m)
 
 static void
 format_eth_masked(struct ds *s, const char *name,
-                  const uint8_t eth[ETH_ADDR_LEN],
-                  const uint8_t mask[ETH_ADDR_LEN])
+                  const struct eth_addr eth, const struct eth_addr mask)
 {
     if (!eth_addr_is_zero(mask)) {
         ds_put_format(s, "%s=", name);
-        eth_format_masked(eth, mask, s);
+        eth_format_masked(eth, &mask, s);
         ds_put_char(s, ',');
     }
 }
@@ -812,7 +909,22 @@ format_ipv6_netmask(struct ds *s, const char *name,
 {
     if (!ipv6_mask_is_any(netmask)) {
         ds_put_format(s, "%s=", name);
-        print_ipv6_masked(s, addr, netmask);
+        ipv6_format_masked(addr, netmask, s);
+        ds_put_char(s, ',');
+    }
+}
+
+static void
+format_uint16_masked(struct ds *s, const char *name,
+                   uint16_t value, uint16_t mask)
+{
+    if (mask != 0) {
+        ds_put_format(s, "%s=", name);
+        if (mask == UINT16_MAX) {
+            ds_put_format(s, "%"PRIu16, value);
+        } else {
+            ds_put_format(s, "0x%"PRIx16"/0x%"PRIx16, value, mask);
+        }
         ds_put_char(s, ',');
     }
 }
@@ -884,6 +996,10 @@ format_flow_tunnel(struct ds *s, const struct match *match)
     format_be64_masked(s, "tun_id", tnl->tun_id, wc->masks.tunnel.tun_id);
     format_ip_netmask(s, "tun_src", tnl->ip_src, wc->masks.tunnel.ip_src);
     format_ip_netmask(s, "tun_dst", tnl->ip_dst, wc->masks.tunnel.ip_dst);
+    format_ipv6_netmask(s, "tun_ipv6_src", &tnl->ipv6_src,
+                        &wc->masks.tunnel.ipv6_src);
+    format_ipv6_netmask(s, "tun_ipv6_dst", &tnl->ipv6_dst,
+                        &wc->masks.tunnel.ipv6_dst);
 
     if (wc->masks.tunnel.gbp_id) {
         format_be16_masked(s, "tun_gbp_id", tnl->gbp_id,
@@ -910,6 +1026,22 @@ format_flow_tunnel(struct ds *s, const struct match *match)
     tun_metadata_match_format(s, match);
 }
 
+static void
+format_ct_label_masked(struct ds *s, const ovs_u128 *key, const ovs_u128 *mask)
+{
+    if (!ovs_u128_is_zero(mask)) {
+        ovs_be128 value = hton128(*key);
+        ds_put_format(s, "ct_label=");
+        ds_put_hex(s, &value, sizeof value);
+        if (!is_all_ones(mask, sizeof(*mask))) {
+            value = hton128(*mask);
+            ds_put_char(s, '/');
+            ds_put_hex(s, &value, sizeof value);
+        }
+        ds_put_char(s, ',');
+    }
+}
+
 /* Appends a string representation of 'match' to 's'.  If 'priority' is
  * different from OFP_DEFAULT_PRIORITY, includes it in 's'. */
 void
@@ -923,7 +1055,7 @@ match_format(const struct match *match, struct ds *s, int priority)
 
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 33);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35);
 
     if (priority != OFP_DEFAULT_PRIORITY) {
         ds_put_format(s, "priority=%d,", priority);
@@ -955,6 +1087,33 @@ match_format(const struct match *match, struct ds *s, int priority)
         ds_put_char(s, ',');
     }
 
+    if (wc->masks.ct_state) {
+        if (wc->masks.ct_state == UINT16_MAX) {
+            ds_put_cstr(s, "ct_state=");
+            if (f->ct_state) {
+                format_flags(s, ct_state_to_string, f->ct_state, '|');
+            } else {
+                ds_put_cstr(s, "0"); /* No state. */
+            }
+        } else {
+            format_flags_masked(s, "ct_state", ct_state_to_string,
+                                f->ct_state, wc->masks.ct_state, UINT16_MAX);
+        }
+        ds_put_char(s, ',');
+    }
+
+    if (wc->masks.ct_zone) {
+        format_uint16_masked(s, "ct_zone", f->ct_zone, wc->masks.ct_zone);
+    }
+
+    if (wc->masks.ct_mark) {
+        format_uint32_masked(s, "ct_mark", f->ct_mark, wc->masks.ct_mark);
+    }
+
+    if (!ovs_u128_is_zero(&wc->masks.ct_label)) {
+        format_ct_label_masked(s, &f->ct_label, &wc->masks.ct_label);
+    }
+
     if (wc->masks.dl_type) {
         skip_type = true;
         if (f->dl_type == htons(ETH_TYPE_IP)) {