Implement arbitrary bitwise masks for tun_id field.
[sliver-openvswitch.git] / lib / ofp-parse.c
index a72d1aa..7fed553 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 Nicira Networks.
+ * Copyright (c) 2010, 2011 Nicira Networks.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@
 #include "byte-order.h"
 #include "dynamic-string.h"
 #include "netdev.h"
+#include "multipath.h"
 #include "nx-match.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
@@ -127,6 +128,35 @@ str_to_ip(const char *str_, ovs_be32 *ip, ovs_be32 *maskp)
     free(str);
 }
 
+static void
+str_to_tun_id(const char *str, ovs_be64 *tun_idp, ovs_be64 *maskp)
+{
+    uint64_t tun_id, mask;
+    char *tail;
+
+    errno = 0;
+    tun_id = strtoull(str, &tail, 0);
+    if (errno || (*tail != '\0' && *tail != '/')) {
+        goto error;
+    }
+
+    if (*tail == '/') {
+        mask = strtoull(tail + 1, &tail, 0);
+        if (errno || *tail != '\0') {
+            goto error;
+        }
+    } else {
+        mask = UINT64_MAX;
+    }
+
+    *tun_idp = htonll(tun_id);
+    *maskp = htonll(mask);
+    return;
+
+error:
+    ovs_fatal(0, "%s: bad syntax for tunnel id", str);
+}
+
 static void *
 put_action(struct ofpbuf *b, size_t size, uint16_t type)
 {
@@ -194,26 +224,66 @@ parse_port_name(const char *name, uint16_t *port)
 static void
 str_to_action(char *str, struct ofpbuf *b)
 {
-    char *act, *arg;
-    char *saveptr = NULL;
     bool drop = false;
     int n_actions;
+    char *pos;
 
-    for (act = strtok_r(str, ", \t\r\n", &saveptr), n_actions = 0; act;
-         act = strtok_r(NULL, ", \t\r\n", &saveptr), n_actions++)
-    {
+    pos = str;
+    n_actions = 0;
+    for (;;) {
+        char *act, *arg;
+        size_t actlen;
         uint16_t port;
 
+        pos += strspn(pos, ", \t\r\n");
+        if (*pos == '\0') {
+            break;
+        }
+
         if (drop) {
             ovs_fatal(0, "Drop actions must not be followed by other actions");
         }
 
-        /* Arguments are separated by colons */
-        arg = strchr(act, ':');
-        if (arg) {
-            *arg = '\0';
-            arg++;
+        act = pos;
+        actlen = strcspn(pos, ":(, \t\r\n");
+        if (act[actlen] == ':') {
+            /* The argument can be separated by a colon. */
+            size_t arglen;
+
+            arg = act + actlen + 1;
+            arglen = strcspn(arg, ", \t\r\n");
+            pos = arg + arglen + (arg[arglen] != '\0');
+            arg[arglen] = '\0';
+        } else if (act[actlen] == '(') {
+            /* The argument can be surrounded by balanced parentheses.  The
+             * outermost set of parentheses is removed. */
+            int level = 1;
+            size_t arglen;
+
+            arg = act + actlen + 1;
+            for (arglen = 0; level > 0; arglen++) {
+                switch (arg[arglen]) {
+                case '\0':
+                    ovs_fatal(0, "unbalanced parentheses in argument to %s "
+                              "action", act);
+
+                case '(':
+                    level++;
+                    break;
+
+                case ')':
+                    level--;
+                    break;
+                }
+            }
+            arg[arglen - 1] = '\0';
+            pos = arg + arglen;
+        } else {
+            /* There might be no argument at all. */
+            arg = NULL;
+            pos = act + actlen + (act[actlen] != '\0');
         }
+        act[actlen] = '\0';
 
         if (!strcasecmp(act, "mod_vlan_vid")) {
             struct ofp_action_vlan_vid *va;
@@ -334,6 +404,10 @@ str_to_action(char *str, struct ofpbuf *b)
             struct nx_action_reg_load *load;
             load = ofpbuf_put_uninit(b, sizeof *load);
             nxm_parse_reg_load(load, arg);
+        } else if (!strcasecmp(act, "multipath")) {
+            struct nx_action_multipath *nam;
+            nam = ofpbuf_put_uninit(b, sizeof *nam);
+            multipath_parse(nam, arg);
         } else if (!strcasecmp(act, "output")) {
             put_output_action(b, str_to_u32(arg));
         } else if (!strcasecmp(act, "enqueue")) {
@@ -370,6 +444,7 @@ str_to_action(char *str, struct ofpbuf *b)
         } else {
             ovs_fatal(0, "Unknown action: %s", act);
         }
+        n_actions++;
     }
 }
 
@@ -402,7 +477,7 @@ parse_protocol(const char *name, const struct protocol **p_out)
 }
 
 #define FIELDS                                              \
-    FIELD(F_TUN_ID,      "tun_id",      FWW_TUN_ID)         \
+    FIELD(F_TUN_ID,      "tun_id",      0)                  \
     FIELD(F_IN_PORT,     "in_port",     FWW_IN_PORT)        \
     FIELD(F_DL_VLAN,     "dl_vlan",     0)                  \
     FIELD(F_DL_VLAN_PCP, "dl_vlan_pcp", 0)                  \
@@ -456,12 +531,14 @@ parse_field_value(struct cls_rule *rule, enum field_index index,
                   const char *value)
 {
     uint8_t mac[ETH_ADDR_LEN];
+    ovs_be64 tun_id, tun_mask;
     ovs_be32 ip, mask;
     uint16_t port_no;
 
     switch (index) {
     case F_TUN_ID:
-        cls_rule_set_tun_id(rule, htonll(str_to_u64(value)));
+        str_to_tun_id(value, &tun_id, &tun_mask);
+        cls_rule_set_tun_id_masked(rule, tun_id, tun_mask);
         break;
 
     case F_IN_PORT: