Support matching and modifying IP TTL.
[sliver-openvswitch.git] / lib / meta-flow.c
index 6d2bfbd..2df1cb4 100644 (file)
@@ -80,6 +80,9 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     REGISTER(3),
 #endif
 #if FLOW_N_REGS > 4
+    REGISTER(4),
+#endif
+#if FLOW_N_REGS > 5
 #error
 #endif
 
@@ -168,6 +171,14 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         MFP_IPV6,
         NXM_NX_IPV6_DST,
     },
+    {
+        MFF_IPV6_LABEL, "ipv6_label", NULL,
+        4, 20,
+        MFM_NONE, FWW_IPV6_LABEL,
+        MFS_HEXADECIMAL,
+        MFP_IPV6,
+        NXM_NX_IPV6_LABEL,
+    },
 
     {
         MFF_IP_PROTO, "nw_proto", NULL,
@@ -177,12 +188,33 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         MFP_IP_ANY,
         NXM_OF_IP_PROTO,
     }, {
-        MFF_IP_TOS, "nw_tos", NULL,
+        MFF_IP_DSCP, "nw_tos", NULL,
         MF_FIELD_SIZES(u8),
-        MFM_NONE, FWW_NW_TOS,
+        MFM_NONE, 0,
         MFS_DECIMAL,
         MFP_IP_ANY,
         NXM_OF_IP_TOS,
+    }, {
+        MFF_IP_ECN, "nw_ecn", NULL,
+        1, 2,
+        MFM_NONE, 0,
+        MFS_DECIMAL,
+        MFP_IP_ANY,
+        NXM_NX_IP_ECN,
+    }, {
+        MFF_IP_TTL, "nw_ttl", NULL,
+        MF_FIELD_SIZES(u8),
+        MFM_NONE, FWW_NW_TTL,
+        MFS_DECIMAL,
+        MFP_IP_ANY,
+        NXM_NX_IP_TTL,
+    }, {
+        MFF_IP_FRAG, "ip_frag", NULL,
+        1, 2,
+        MFM_FULLY, 0,
+        MFS_FRAG,
+        MFP_IP_ANY,
+        NXM_NX_IP_FRAG,
     },
 
     {
@@ -344,7 +376,8 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
     case MFF_ETH_SRC:
     case MFF_ETH_TYPE:
     case MFF_IP_PROTO:
-    case MFF_IP_TOS:
+    case MFF_IP_TTL:
+    case MFF_IPV6_LABEL:
     case MFF_ARP_OP:
     case MFF_ARP_SHA:
     case MFF_ARP_THA:
@@ -376,6 +409,9 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
     case MFF_REG3:
 #endif
 #if FLOW_N_REGS > 4
+    case MFF_REG4:
+#endif
+#if FLOW_N_REGS > 5
 #error
 #endif
         return !wc->reg_masks[mf->id - MFF_REG0];
@@ -401,6 +437,13 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
     case MFF_IPV6_DST:
         return ipv6_mask_is_any(&wc->ipv6_dst_mask);
 
+    case MFF_IP_DSCP:
+        return !(wc->tos_mask & IP_DSCP_MASK);
+    case MFF_IP_ECN:
+        return !(wc->tos_mask & IP_ECN_MASK);
+    case MFF_IP_FRAG:
+        return !(wc->frag_mask & FLOW_FRAG_MASK);
+
     case MFF_ARP_SPA:
         return !wc->nw_src_mask;
     case MFF_ARP_TPA:
@@ -427,7 +470,8 @@ mf_get_mask(const struct mf_field *mf, const struct flow_wildcards *wc,
     case MFF_ETH_SRC:
     case MFF_ETH_TYPE:
     case MFF_IP_PROTO:
-    case MFF_IP_TOS:
+    case MFF_IP_TTL:
+    case MFF_IPV6_LABEL:
     case MFF_ARP_OP:
     case MFF_ARP_SHA:
     case MFF_ARP_THA:
@@ -461,6 +505,9 @@ mf_get_mask(const struct mf_field *mf, const struct flow_wildcards *wc,
     case MFF_REG3:
 #endif
 #if FLOW_N_REGS > 4
+    case MFF_REG4:
+#endif
+#if FLOW_N_REGS > 5
 #error
 #endif
         mask->be32 = htonl(wc->reg_masks[mf->id - MFF_REG0]);
@@ -495,6 +542,16 @@ mf_get_mask(const struct mf_field *mf, const struct flow_wildcards *wc,
         mask->ipv6 = wc->ipv6_dst_mask;
         break;
 
+    case MFF_IP_DSCP:
+        mask->u8 = wc->tos_mask & IP_DSCP_MASK;
+        break;
+    case MFF_IP_ECN:
+        mask->u8 = wc->tos_mask & IP_ECN_MASK;
+        break;
+    case MFF_IP_FRAG:
+        mask->u8 = wc->frag_mask & FLOW_FRAG_MASK;
+        break;
+
     case MFF_ARP_SPA:
         mask->be32 = wc->nw_src_mask;
         break;
@@ -582,17 +639,17 @@ mf_are_prereqs_ok(const struct mf_field *mf, const struct flow *flow)
 
     case MFP_ND:
         return (is_icmpv6(flow)
-                && flow->icmp_code == htons(0)
-                && (flow->icmp_type == htons(ND_NEIGHBOR_SOLICIT) ||
-                    flow->icmp_type == htons(ND_NEIGHBOR_ADVERT)));
+                && flow->tp_dst == htons(0)
+                && (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT) ||
+                    flow->tp_src == htons(ND_NEIGHBOR_ADVERT)));
     case MFP_ND_SOLICIT:
         return (is_icmpv6(flow)
-                && flow->icmp_code == htons(0)
-                && (flow->icmp_type == htons(ND_NEIGHBOR_SOLICIT)));
+                && flow->tp_dst == htons(0)
+                && (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT)));
     case MFP_ND_ADVERT:
         return (is_icmpv6(flow)
-                && flow->icmp_code == htons(0)
-                && (flow->icmp_type == htons(ND_NEIGHBOR_ADVERT)));
+                && flow->tp_dst == htons(0)
+                && (flow->tp_src == htons(ND_NEIGHBOR_ADVERT)));
     }
 
     NOT_REACHED();
@@ -607,7 +664,7 @@ mf_are_prereqs_ok(const struct mf_field *mf, const struct flow *flow)
  * without the VLAN_CFI bit being set, but we can't reject those values because
  * it is still legitimate to test just for those bits (see the documentation
  * for NXM_OF_VLAN_TCI in nicira-ext.h).  On the other hand, there is never a
- * reason to set the low bit of MFF_IP_TOS to 1, so we reject that. */
+ * reason to set the low bit of MFF_IP_DSCP to 1, so we reject that. */
 bool
 mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
 {
@@ -627,6 +684,9 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
     case MFF_REG3:
 #endif
 #if FLOW_N_REGS > 4
+    case MFF_REG4:
+#endif
+#if FLOW_N_REGS > 5
 #error
 #endif
     case MFF_ETH_SRC:
@@ -638,6 +698,7 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
     case MFF_IPV6_SRC:
     case MFF_IPV6_DST:
     case MFF_IP_PROTO:
+    case MFF_IP_TTL:
     case MFF_ARP_SPA:
     case MFF_ARP_TPA:
     case MFF_ARP_SHA:
@@ -653,8 +714,12 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
     case MFF_ND_TLL:
         return true;
 
-    case MFF_IP_TOS:
-        return !(value->u8 & 0x03);
+    case MFF_IP_DSCP:
+        return !(value->u8 & ~IP_DSCP_MASK);
+    case MFF_IP_ECN:
+        return !(value->u8 & ~IP_ECN_MASK);
+    case MFF_IP_FRAG:
+        return !(value->u8 & ~FLOW_FRAG_MASK);
 
     case MFF_ARP_OP:
         return !(value->be16 & htons(0xff00));
@@ -665,6 +730,9 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
     case MFF_VLAN_PCP:
         return !(value->u8 & ~7);
 
+    case MFF_IPV6_LABEL:
+        return !(value->be32 & ~htonl(IPV6_LABEL_MASK));
+
     case MFF_N_IDS:
     default:
         NOT_REACHED();
@@ -699,9 +767,12 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
     case MFF_REG3:
 #endif
 #if FLOW_N_REGS > 4
+    case MFF_REG4:
+#endif
+#if FLOW_N_REGS > 5
 #error
 #endif
-        value->be32 = htonl(flow->regs[0]);
+        value->be32 = htonl(flow->regs[mf->id - MFF_REG0]);
         break;
 
     case MFF_ETH_SRC:
@@ -744,12 +815,28 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
         value->ipv6 = flow->ipv6_dst;
         break;
 
+    case MFF_IPV6_LABEL:
+        value->be32 = flow->ipv6_label;
+        break;
+
     case MFF_IP_PROTO:
         value->u8 = flow->nw_proto;
         break;
 
-    case MFF_IP_TOS:
-        value->u8 = flow->nw_tos;
+    case MFF_IP_DSCP:
+        value->u8 = flow->tos & IP_DSCP_MASK;
+        break;
+
+    case MFF_IP_ECN:
+        value->u8 = flow->tos & IP_ECN_MASK;
+        break;
+
+    case MFF_IP_TTL:
+        value->u8 = flow->nw_ttl;
+        break;
+
+    case MFF_IP_FRAG:
+        value->u8 = flow->frag;
         break;
 
     case MFF_ARP_OP:
@@ -837,6 +924,9 @@ mf_set_value(const struct mf_field *mf,
     case MFF_REG3:
 #endif
 #if FLOW_N_REGS > 4
+    case MFF_REG4:
+#endif
+#if FLOW_N_REGS > 5
 #error
 #endif
 #if FLOW_N_REGS > 0
@@ -884,12 +974,28 @@ mf_set_value(const struct mf_field *mf,
         cls_rule_set_ipv6_dst(rule, &value->ipv6);
         break;
 
+    case MFF_IPV6_LABEL:
+        cls_rule_set_ipv6_label(rule, value->be32);
+        break;
+
     case MFF_IP_PROTO:
         cls_rule_set_nw_proto(rule, value->u8);
         break;
 
-    case MFF_IP_TOS:
-        cls_rule_set_nw_tos(rule, value->u8);
+    case MFF_IP_DSCP:
+        cls_rule_set_nw_dscp(rule, value->u8);
+        break;
+
+    case MFF_IP_ECN:
+        cls_rule_set_nw_ecn(rule, value->u8);
+        break;
+
+    case MFF_IP_TTL:
+        cls_rule_set_nw_ttl(rule, value->u8);
+        break;
+
+    case MFF_IP_FRAG:
+        cls_rule_set_frag(rule, value->u8);
         break;
 
     case MFF_ARP_OP:
@@ -986,6 +1092,11 @@ mf_set_wild(const struct mf_field *mf, struct cls_rule *rule)
         break;
 #endif
 #if FLOW_N_REGS > 4
+    case MFF_REG4:
+        cls_rule_set_reg_masked(rule, 4, 0, 0);
+        break;
+#endif
+#if FLOW_N_REGS > 5
 #error
 #endif
 
@@ -1036,14 +1147,34 @@ mf_set_wild(const struct mf_field *mf, struct cls_rule *rule)
         memset(&rule->flow.ipv6_dst, 0, sizeof rule->flow.ipv6_dst);
         break;
 
+    case MFF_IPV6_LABEL:
+        rule->wc.wildcards |= FWW_IPV6_LABEL;
+        rule->flow.ipv6_label = 0;
+        break;
+
     case MFF_IP_PROTO:
         rule->wc.wildcards |= FWW_NW_PROTO;
         rule->flow.nw_proto = 0;
         break;
 
-    case MFF_IP_TOS:
-        rule->wc.wildcards |= FWW_NW_TOS;
-        rule->flow.nw_tos = 0;
+    case MFF_IP_DSCP:
+        rule->wc.tos_mask |= IP_DSCP_MASK;
+        rule->flow.tos &= ~IP_DSCP_MASK;
+        break;
+
+    case MFF_IP_ECN:
+        rule->wc.tos_mask |= IP_ECN_MASK;
+        rule->flow.tos &= ~IP_ECN_MASK;
+        break;
+
+    case MFF_IP_TTL:
+        rule->wc.wildcards |= FWW_NW_TTL;
+        rule->flow.nw_ttl = 0;
+        break;
+
+    case MFF_IP_FRAG:
+        rule->wc.frag_mask |= FLOW_FRAG_MASK;
+        rule->flow.frag &= ~FLOW_FRAG_MASK;
         break;
 
     case MFF_ARP_OP:
@@ -1118,8 +1249,11 @@ mf_set(const struct mf_field *mf,
     case MFF_ETH_TYPE:
     case MFF_VLAN_VID:
     case MFF_VLAN_PCP:
+    case MFF_IPV6_LABEL:
     case MFF_IP_PROTO:
-    case MFF_IP_TOS:
+    case MFF_IP_TTL:
+    case MFF_IP_DSCP:
+    case MFF_IP_ECN:
     case MFF_ARP_OP:
     case MFF_ARP_SHA:
     case MFF_ARP_THA:
@@ -1151,6 +1285,9 @@ mf_set(const struct mf_field *mf,
     case MFF_REG3:
 #endif
 #if FLOW_N_REGS > 4
+    case MFF_REG4:
+#endif
+#if FLOW_N_REGS > 5
 #error
 #endif
         cls_rule_set_reg_masked(rule, mf->id - MFF_REG0,
@@ -1183,6 +1320,10 @@ mf_set(const struct mf_field *mf,
         cls_rule_set_ipv6_dst_masked(rule, &value->ipv6, &mask->ipv6);
         break;
 
+    case MFF_IP_FRAG:
+        cls_rule_set_frag_masked(rule, value->u8, mask->u8);
+        break;
+
     case MFF_ARP_SPA:
         cls_rule_set_nw_src_masked(rule, value->be32, mask->be32);
         break;
@@ -1302,6 +1443,9 @@ mf_random_value(const struct mf_field *mf, union mf_value *value)
     case MFF_REG3:
 #endif
 #if FLOW_N_REGS > 4
+    case MFF_REG4:
+#endif
+#if FLOW_N_REGS > 5
 #error
 #endif
     case MFF_ETH_SRC:
@@ -1313,6 +1457,7 @@ mf_random_value(const struct mf_field *mf, union mf_value *value)
     case MFF_IPV6_SRC:
     case MFF_IPV6_DST:
     case MFF_IP_PROTO:
+    case MFF_IP_TTL:
     case MFF_ARP_SPA:
     case MFF_ARP_TPA:
     case MFF_ARP_SHA:
@@ -1328,8 +1473,20 @@ mf_random_value(const struct mf_field *mf, union mf_value *value)
     case MFF_ND_TLL:
         break;
 
-    case MFF_IP_TOS:
-        value->u8 &= ~0x03;
+    case MFF_IPV6_LABEL:
+        value->be32 &= ~htonl(IPV6_LABEL_MASK);
+        break;
+
+    case MFF_IP_DSCP:
+        value->u8 &= IP_DSCP_MASK;
+        break;
+
+    case MFF_IP_ECN:
+        value->u8 &= IP_ECN_MASK;
+        break;
+
+    case MFF_IP_FRAG:
+        value->u8 &= FLOW_FRAG_MASK;
         break;
 
     case MFF_ARP_OP:
@@ -1495,6 +1652,49 @@ mf_from_ofp_port_string(const struct mf_field *mf, const char *s,
     }
 }
 
+struct frag_handling {
+    const char *name;
+    uint8_t mask;
+    uint8_t value;
+};
+
+static const struct frag_handling all_frags[] = {
+#define A FLOW_FRAG_ANY
+#define L FLOW_FRAG_LATER
+    /* name               mask  value */
+
+    { "no",               A|L,  0     },
+    { "first",            A|L,  A     },
+    { "later",            A|L,  A|L   },
+
+    { "no",               A,    0     },
+    { "yes",              A,    A     },
+
+    { "not_later",        L,    0     },
+    { "later",            L,    L     },
+#undef A
+#undef L
+};
+
+static char *
+mf_from_frag_string(const char *s, uint8_t *valuep, uint8_t *maskp)
+{
+    const struct frag_handling *h;
+
+    for (h = all_frags; h < &all_frags[ARRAY_SIZE(all_frags)]; h++) {
+        if (!strcasecmp(s, h->name)) {
+            /* We force the upper bits of the mask on to make mf_parse_value()
+             * happy (otherwise it will never think it's an exact match.) */
+            *maskp = h->mask | ~FLOW_FRAG_MASK;
+            *valuep = h->value;
+            return NULL;
+        }
+    }
+
+    return xasprintf("%s: unknown fragment type (valid types are \"no\", "
+                     "\"yes\", \"first\", \"later\", \"not_first\"", s);
+}
+
 /* Parses 's', a string value for field 'mf', into 'value' and 'mask'.  Returns
  * NULL if successful, otherwise a malloc()'d string describing the error. */
 char *
@@ -1524,6 +1724,9 @@ mf_parse(const struct mf_field *mf, const char *s,
 
     case MFS_OFP_PORT:
         return mf_from_ofp_port_string(mf, s, &value->be16, &mask->be16);
+
+    case MFS_FRAG:
+        return mf_from_frag_string(s, &value->u8, &mask->u8);
     }
     NOT_REACHED();
 }
@@ -1581,6 +1784,26 @@ mf_format_integer_string(const struct mf_field *mf, const uint8_t *valuep,
     }
 }
 
+static void
+mf_format_frag_string(const uint8_t *valuep, const uint8_t *maskp,
+                      struct ds *s)
+{
+    const struct frag_handling *h;
+    uint8_t value = *valuep;
+    uint8_t mask = *maskp;
+
+    value &= mask;
+    mask &= FLOW_FRAG_MASK;
+
+    for (h = all_frags; h < &all_frags[ARRAY_SIZE(all_frags)]; h++) {
+        if (value == h->value && mask == h->mask) {
+            ds_put_cstr(s, h->name);
+            return;
+        }
+    }
+    ds_put_cstr(s, "<error>");
+}
+
 /* Appends to 's' a string representation of field 'mf' whose value is in
  * 'value' and 'mask'.  'mask' may be NULL to indicate an exact match. */
 void
@@ -1625,6 +1848,10 @@ mf_format(const struct mf_field *mf,
         print_ipv6_masked(s, &value->ipv6, mask ? &mask->ipv6 : NULL);
         break;
 
+    case MFS_FRAG:
+        mf_format_frag_string(&value->u8, &mask->u8, s);
+        break;
+
     default:
         NOT_REACHED();
     }