[ovs-dev] [PATCH RFC v3 2/3] ovn-controller: add RS responder for SLAAC

Zongkai LI zealokii at gmail.com
Fri Aug 12 08:02:01 UTC 2016


From: LI Zong Kai <lzklibj at cn.ibm.com>

This patch tries to implement Router Solicitation (RS) responder, who will
reply Router Advertisement (RA) message, for SLAAC on ovn-controller side.

It parses lflows which have:
 - match: inport == LRP_NAME && ip6.dst == ff02::2 && nd_rs
   (nd_rs: icmp6.type == 133 && icmp6.code == 0 && ttl == 255)
 - action: nd_ra{slaac(mac_address, MTU, prefix ,...);
           outport = inport; flags.loopback = 1; output;};
   (nd_ra is a new action which stands for RS responder;
    slaac is a new action which has the following parameters to tell
    ovn-controller to compose RA packet with SLAAC flags, with these
    parameters:
     - MAC address: router port mac address, such as fa:16:3e:12:34:56.
     - MTU: logical switch MTU, such as 1450.
     - IPv6 prefix(es): such as fd80:a0f9:a012::/64.
    Beside the parameters list above, nd_ra action will use eth.src and ip6.src
    from packet being processed as RA packet eth.src and ip6.src.
After a RA packet is composed, the left nested actions will make RA packet
transmitted back to the inport, where Router Solicitation (RS) packet comes.

For inner action 'slaac', as a prototype, it doesn't try to expose all RA
relevant flags and fields. As most flags are constant for in SLAAC scenario,
so it only exposed few fields user may care, such as prefixes, MTU and LLA.
It uses an ordered parameters list to hold all those values for now, but in
future, when we try to implement more feature on RA, the unexposed ones should
be exposed, and key-pair style parameter list should be used.

For outer action 'nd_ra', as a prototype, it's just a RA responder now, not a
real implement for RA, like it wont send periodic RA broadcast. This will be
harmful when routing relevant stuff changes, like user changes lrp mac,
disattach a switch from a router. However, a periodic version seems not so
necessary, but a broadcast should be sent when things change.

For lflow, this patch add prerequisites for Router Solicitation (RS) and RA
message. But it doesn't fix details of nd.target, nd.sll and nd.tll for RS
and RA, since these fields are not supported to be modified via ovs native
actions for now.
---
 include/ovn/actions.h    |  15 +++++
 include/ovn/expr.h       |   4 ++
 ovn/controller/pinctrl.c | 169 +++++++++++++++++++++++++++++++++++++++++++++++
 ovn/lib/actions.c        |  73 ++++++++++++++++++++
 ovn/lib/expr.c           |  27 ++++++++
 ovn/lib/logical-fields.c |   7 +-
 ovn/ovn-sb.xml           |  30 ++++++++-
 tests/ovn.at             |   5 +-
 8 files changed, 327 insertions(+), 3 deletions(-)

diff --git a/include/ovn/actions.h b/include/ovn/actions.h
index fb2d6a9..11eaf49 100644
--- a/include/ovn/actions.h
+++ b/include/ovn/actions.h
@@ -90,6 +90,21 @@ enum action_opcode {
      *     MFF_ETH_SRC = mac
      */
     ACTION_OPCODE_PUT_ND,
+
+    /* "nd_ra { ...actions... }".
+     *
+     * The actions, in OpenFlow 1.3 format, follow the action_header.
+     */
+    ACTION_OPCODE_ND_RA,
+
+    /* slaac (prefix0, [prefix1, ...,] mtu, mac)".
+     *
+     * Arguments follow the action_header, in this format:
+     *   - One or more IPv6 prefixes.
+     *   - A 16-bit MTU.
+     *   - A 48-bit Ether address.
+     */
+    ACTION_OPCODE_SLAAC,
 };
 
 /* Header. */
diff --git a/include/ovn/expr.h b/include/ovn/expr.h
index df22895..e1979ae 100644
--- a/include/ovn/expr.h
+++ b/include/ovn/expr.h
@@ -461,9 +461,13 @@ struct expr_constant_set {
 
 char *expr_parse_constant_set(struct lexer *, const struct shash *symtab,
                               struct expr_constant_set *cs)
+
     OVS_WARN_UNUSED_RESULT;
 void expr_constant_set_destroy(struct expr_constant_set *cs);
 
+bool expr_parse_list_integer_parameters(struct lexer *lexer,
+                                        struct expr_constant_set *cs);
+
 
 /* Address sets, aka "macros".
  *
diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
index bd685fe..4efaccf 100644
--- a/ovn/controller/pinctrl.c
+++ b/ovn/controller/pinctrl.c
@@ -73,6 +73,9 @@ static void send_garp_run(const struct ovsrec_bridge *,
 static void pinctrl_handle_nd_na(const struct flow *ip_flow,
                                  const struct match *md,
                                  struct ofpbuf *userdata);
+static void pinctrl_handle_nd_ra(const struct flow *ip_flow,
+                                 const struct match *md,
+                                 struct ofpbuf *userdata);
 static void reload_metadata(struct ofpbuf *ofpacts,
                             const struct match *md);
 
@@ -421,6 +424,10 @@ process_packet_in(const struct ofp_header *msg)
                                        false);
         break;
 
+    case ACTION_OPCODE_ND_RA:
+        pinctrl_handle_nd_ra(&headers, &pin.flow_metadata, &userdata);
+        break;
+
     default:
         VLOG_WARN_RL(&rl, "unrecognized packet-in opcode %"PRIu32,
                      ntohl(ah->opcode));
@@ -1019,3 +1026,165 @@ exit:
     dp_packet_uninit(&packet);
     ofpbuf_uninit(&ofpacts);
 }
+
+static bool pinctrl_handle_slaac(const struct flow *ip_flow,
+                                 struct ofpbuf *userdata,
+                                 struct dp_packet *packet,
+                                 int *left_bytes)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+    bool ret_val = false;
+    struct ipv6_netaddr *prefixes = NULL;
+
+    /* Get SLL. */
+    struct eth_addr *sll = ofpbuf_try_pull(userdata,
+                                           sizeof(struct eth_addr));
+    if (!sll) {
+        VLOG_WARN_RL(&rl, "Failed to parse link-layer address.");
+        goto exit;
+    }
+    (*left_bytes) -= sizeof(struct eth_addr);
+
+    /* Get MTU. */
+    ovs_be32 *mtu = ofpbuf_try_pull(userdata, sizeof(ovs_be32));
+    if (!mtu || *mtu == 0) {
+        VLOG_WARN_RL(&rl, "Failed to parse mtu.");
+        goto exit;
+    }
+    (*left_bytes) -= sizeof(ovs_be32);
+
+    /* Get number of prefixes. */
+    uint8_t *n_prefixes = ofpbuf_try_pull(userdata, 1);
+    if (!n_prefixes || *n_prefixes == 0) {
+        VLOG_WARN_RL(&rl, "Failed to parse prefixes number.");
+        goto exit;
+    }
+    (*left_bytes)--;
+
+    /* Get prefixes. */
+    size_t prefixes_size = sizeof(struct ipv6_netaddr) * (*n_prefixes);
+    prefixes = xmalloc(prefixes_size);
+    struct in6_addr *prefix;
+    uint8_t *prefix_len = NULL;
+    for (size_t i = 0; i < *n_prefixes; i++) {
+        prefix = ofpbuf_try_pull(userdata, sizeof(struct in6_addr));
+        if (!prefix) {
+            VLOG_WARN_RL(&rl, "Failed to parse ipv6 prefix.");
+            goto exit;
+        }
+        prefix_len = ofpbuf_try_pull(userdata, 1);
+        if (!prefix_len || *prefix_len == 0) {
+            VLOG_WARN_RL(&rl, "Failed to parse prefix len.");
+            goto exit;
+        }
+        memcpy(&prefixes[i].addr, prefix, sizeof(struct in6_addr));
+        prefixes[i].plen = *prefix_len;
+    }
+    (*left_bytes) -= *n_prefixes * (sizeof(struct in6_addr) + 1);
+
+    ovs_be32 ipv6_src[4], ipv6_dst[4], router_prefix[4];
+    struct in6_addr lla;
+    in6_generate_lla(*sll, &lla);
+    memcpy(ipv6_src, &lla, sizeof ipv6_src);
+    memcpy(ipv6_dst, &ip_flow->ipv6_src, sizeof ipv6_dst);
+
+    uint8_t cur_hop_limit = 64;
+    uint8_t mo_flags = 0;
+    ovs_be16 router_lifetime = htons(0x2a30);  // 3 hours.
+    uint8_t la_flags = ND_PREFIX_ON_LINK | ND_PREFIX_AUTONOMOUS_ADDRESS;
+    ovs_be32 valid_lifetime = htonl(0x2a30);
+    ovs_be32 preferred_lifetime = htonl(0x2a30);
+    ovs_be32 reachable_time = 0;
+    ovs_be32 retrans_timer = 0;
+
+    compose_nd_ra(packet, *sll, ip_flow->dl_src, ipv6_src, ipv6_dst,
+                  cur_hop_limit, mo_flags,
+                  router_lifetime, reachable_time, retrans_timer);
+    packet_put_ra_sll_opt(packet, *sll);
+    packet_put_ra_mtu_opt(packet, *mtu);
+    for (size_t i = 0; i < *n_prefixes; i++) {
+        memcpy(router_prefix, &prefixes[i].addr, sizeof router_prefix);
+        packet_put_ra_prefix_opt(packet, prefixes[i].plen, la_flags,
+                              valid_lifetime, preferred_lifetime,
+                              router_prefix);
+    }
+    ret_val = true;
+
+exit:
+    if (prefixes) {
+        free(prefixes);
+    }
+    return ret_val;
+}
+
+static void
+pinctrl_handle_nd_ra(const struct flow *ip_flow, const struct match *md,
+                     struct ofpbuf *userdata)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+    enum ofp_version version = rconn_get_version(swconn);
+    enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
+    uint64_t ofpacts_stub[4096 / 8];
+    struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
+    reload_metadata(&ofpacts, md);
+
+    /* Pull inner action. */
+    struct ofpact *act = ofpbuf_try_pull(userdata, sizeof(struct ofpact));
+    if (!act) {
+        VLOG_WARN_RL(&rl, "Failed to pull ofpact.");
+        return;
+    }
+    int left_bytes = ntohs(act->len) - sizeof(struct ofpact);
+    if (!ofpbuf_try_pull(userdata, sizeof(struct ofpact_controller))) {
+        VLOG_WARN_RL(&rl, "Failed to pull ofpact_controller.");
+        return;
+    }
+    left_bytes -= sizeof(struct ofpact_controller);
+    struct action_header *ah = ofpbuf_try_pull(userdata, sizeof *ah);
+    if (!ah) {
+        VLOG_WARN_RL(&rl, "Failed to pull action_header.");
+        return;
+    }
+    if (ntohl(ah->opcode) != ACTION_OPCODE_SLAAC) {
+        VLOG_WARN_RL(&rl, "Not supported action opcode %d.", ntohl(ah->opcode));
+        return;
+    }
+    left_bytes -= sizeof *ah;
+
+    uint64_t packet_stub[256 / 8];
+    struct dp_packet packet;
+    dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
+    if (!pinctrl_handle_slaac(ip_flow, userdata, &packet, &left_bytes)) {
+        VLOG_WARN_RL(&rl, "Failed to compose a RA packet for SLAAC.");
+        goto exit;
+    }
+    if (left_bytes < 0){
+        VLOG_WARN_RL(&rl, "Left bytes error in compose RA packet.");
+        goto exit;
+    } else if (left_bytes > 0) {
+        ofpbuf_try_pull(userdata, left_bytes);
+    }
+    /* Get left actions. */
+    enum ofperr error = ofpacts_pull_openflow_actions(userdata, userdata->size,
+                                                      version, &ofpacts);
+    if (error) {
+        VLOG_WARN_RL(&rl, "Failed to parse actions for 'ra' (%s)",
+                     ofperr_to_string(error));
+        goto exit;
+    }
+
+    struct ofputil_packet_out po = {
+        .packet = dp_packet_data(&packet),
+        .packet_len = dp_packet_size(&packet),
+        .buffer_id = UINT32_MAX,
+        .in_port = OFPP_CONTROLLER,
+        .ofpacts = ofpacts.data,
+        .ofpacts_len = ofpacts.size,
+    };
+
+    queue_msg(ofputil_encode_packet_out(&po, proto));
+
+exit:
+    dp_packet_uninit(&packet);
+    ofpbuf_uninit(&ofpacts);
+}
diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c
index ec82ec3..25f0a0c 100644
--- a/ovn/lib/actions.c
+++ b/ovn/lib/actions.c
@@ -1081,6 +1081,75 @@ parse_ct_nat(struct action_context *ctx, bool snat)
     ofpbuf_push_uninit(ctx->ofpacts, ct_offset);
 }
 
+static void
+parse_slaac_action(struct action_context *ctx)
+{
+    struct expr_constant_set cs = {
+        .type = EXPR_C_INTEGER,
+        .values = NULL,
+        .n_values = 0,
+        .in_curlies = false,
+    };
+
+    if (!expr_parse_list_integer_parameters(ctx->lexer, &cs)) {
+        action_syntax_error(ctx, "failed to parse list parameters"
+                                 " for 'slaac' action");
+        goto exit;
+    }
+
+    /* Parameters check:
+     * 1 ether address for SLL, 1 integer for MTU, and multiple IPv6
+     * addresses for Prefix Information options. */
+    if (cs.n_values < 3) {
+        action_syntax_error(ctx, "not enough parameters parsed in"
+                                 " 'slaac' action");
+        goto exit;
+    }
+    size_t i = 0;
+    if (cs.values[i++].format != LEX_F_ETHERNET) {
+        action_syntax_error(ctx, "the first parameter in 'slaac' action"
+                                 " should be ether address");
+        goto exit;
+    }
+    if (cs.values[i++].format != LEX_F_DECIMAL) {
+        action_syntax_error(ctx, "the second parameter in 'slaac' action"
+                                 " should be mtu, in decimal integer type");
+        goto exit;
+    }
+    for (; i != cs.n_values; i++) {
+        if (cs.values[i].format != LEX_F_IPV6) {
+            action_syntax_error(ctx, "expecting IPv6 address format in"
+                                     " 'slaac' action");
+            goto exit;
+        }
+    }
+
+    /* Set controller data. */
+    size_t oc_offset = start_controller_op(ctx->ofpacts, ACTION_OPCODE_SLAAC,
+                                           false);
+    /* Put ether address for SLL option. */
+    ofpbuf_put(ctx->ofpacts, &cs.values[0].value.mac,
+                             sizeof(struct eth_addr));
+    /* Put integer for MTU option. */
+    ofpbuf_put(ctx->ofpacts, &cs.values[1].value.be32[31],
+                             sizeof(ovs_be32));
+    /* Put number of Prefix Information options. */
+    int num = cs.n_values - 2;
+    ofpbuf_put(ctx->ofpacts, &num, 1);
+    /* Put prefixes and prefixes lengths for Prefix Information options. */
+    int plen = 0;
+    for (size_t i = 2; i != cs.n_values; i++) {
+        ofpbuf_put(ctx->ofpacts, &cs.values[i].value.ipv6,
+                   sizeof(struct in6_addr));
+        plen = ipv6_count_cidr_bits(&cs.values[i].mask.ipv6);
+        ofpbuf_put(ctx->ofpacts, &plen, 1);
+    }
+    finish_controller_op(ctx->ofpacts, oc_offset);
+
+exit:
+    expr_constant_set_destroy(&cs);
+}
+
 static bool
 parse_action(struct action_context *ctx)
 {
@@ -1126,6 +1195,10 @@ parse_action(struct action_context *ctx)
         parse_get_nd_action(ctx);
     } else if (lexer_match_id(ctx->lexer, "put_nd")) {
         parse_put_nd_action(ctx);
+    } else if (lexer_match_id(ctx->lexer, "nd_ra")) {
+        parse_nested_action(ctx, ACTION_OPCODE_ND_RA, "nd_rs");
+    } else if (lexer_match_id(ctx->lexer, "slaac")) {
+        parse_slaac_action(ctx);
     } else {
         action_syntax_error(ctx, "expecting action");
     }
diff --git a/ovn/lib/expr.c b/ovn/lib/expr.c
index db00d7c..c261e44 100644
--- a/ovn/lib/expr.c
+++ b/ovn/lib/expr.c
@@ -3026,3 +3026,30 @@ expr_parse_constant_set(struct lexer *lexer, const struct shash *symtab,
     parse_constant_set(&ctx, cs);
     return ctx.error;
 }
+
+/* Parses a ()-enclosed list integer parameters in an action into 'cs',
+ * which the caller need not have initialized.  Returns true on success, in
+ * otherwise return false. It's always the caller's responsibility to
+ * destroy 'cs'. */
+bool
+expr_parse_list_integer_parameters(struct lexer *lexer,
+                                   struct expr_constant_set *cs)
+{
+    if (!lexer_match(lexer, LEX_T_LPAREN)) {
+        return false;
+    }
+
+    struct expr_context ctx = { .lexer = lexer, .symtab = NULL };
+    size_t allocated_values = 0;
+    bool ok = true;
+
+    do {
+        if (!parse_constant(&ctx, cs, &allocated_values)) {
+            ok = false;
+            break;
+        }
+        lexer_match(ctx.lexer, LEX_T_COMMA);
+    } while(!lexer_match(ctx.lexer, LEX_T_RPAREN));
+
+    return ok;
+}
diff --git a/ovn/lib/logical-fields.c b/ovn/lib/logical-fields.c
index 6dbb4ae..b906cf8 100644
--- a/ovn/lib/logical-fields.c
+++ b/ovn/lib/logical-fields.c
@@ -174,7 +174,12 @@ ovn_init_symtab(struct shash *symtab)
     expr_symtab_add_field(symtab, "arp.tha", MFF_ARP_THA, "arp", false);
 
     expr_symtab_add_predicate(symtab, "nd",
-              "icmp6.type == {135, 136} && icmp6.code == 0 && ip.ttl == 255");
+              "icmp6.type == {133, 134, 135, 136}"
+              " && icmp6.code == 0 && ip.ttl == 255");
+    expr_symtab_add_predicate(symtab, "nd_rs",
+              "icmp6.type == 133 && icmp6.code == 0 && ip.ttl == 255");
+    expr_symtab_add_predicate(symtab, "nd_ra",
+              "icmp6.type == 134 && icmp6.code == 0 && ip.ttl == 255");
     expr_symtab_add_predicate(symtab, "nd_ns",
               "icmp6.type == 135 && icmp6.code == 0 && ip.ttl == 255");
     expr_symtab_add_predicate(symtab, "nd_na",
diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml
index 13c9526..dce9dc7 100644
--- a/ovn/ovn-sb.xml
+++ b/ovn/ovn-sb.xml
@@ -1151,8 +1151,36 @@
           </p>
         </dd>
 
-        <dt><code>get_nd(<var>P</var>, <var>A</var>);</code></dt>
+        <dt>
+          <code>nd_ra { <var>slaac(...)</var>; <var>action</var>; </code>...<code> };</code>
+        </dt>
+
+        <dd>
+          <p>
+            Generate an IPv6 Router Advertisement (RA) packet per inner action
+            'slaac' ordered parameters, and eth.src and ip6.src from Router
+            Solicitation (RS) packet being processed.
+            <b>Parameters</b>: 48-bit mac address for SLL option, 16-bit MTU,
+            one or more IPv6 prefixes for Prefix Information options.
+            Then executes each nested <var>action</var> on the RA packet.
+            Actions following the <var>ra</var> action, if any, apply to the
+            original, unmodified packet.
+            The composed RA packet will use RS packet eth.src as eth.dst, use
+            RS packet ip6.src as ip6.dst, and use 'slaac' action parameter mac
+            address and its lla as eth.src and ip6.src.
+          </p>
+
+          <p>
+            The RA packet has the same VLAN header, if any, as the IPv6 packet
+            it replaces.
+          </p>
+
+          <p>
+            <b>Prerequisite:</b> <code>nd_rs</code>
+          </p>
+        </dd>
 
+        <dt><code>get_nd(<var>P</var>, <var>A</var>);</code></dt>
         <dd>
           <p>
             <b>Parameters</b>: logical port string field <var>P</var>, 128-bit
diff --git a/tests/ovn.at b/tests/ovn.at
index a95e0b2..33ace16 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -732,6 +732,9 @@ reg1[0] = put_dhcp_opts(offerip=1.2.3.4, domain=1.2.3.4); => DHCP option domain
 # nd_na
 nd_na { eth.src = 12:34:56:78:9a:bc; nd.tll = 12:34:56:78:9a:bc; outport = inport; inport = ""; /* Allow sending out inport. */ output; }; => actions=controller(userdata=00.00.00.03.00.00.00.00.00.19.00.10.80.00.08.06.12.34.56.78.9a.bc.00.00.00.19.00.10.80.00.42.06.12.34.56.78.9a.bc.00.00.ff.ff.00.18.00.00.23.20.00.06.00.20.00.00.00.00.00.01.1c.04.00.01.1e.04.00.19.00.10.00.01.1c.04.00.00.00.00.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00), prereqs=nd_ns
 
+# nd_ra
+nd_ra{slaac(12:34:56:78:9a:bc,1450,fdad:a0f9:a012::/64);outport = inport;flags.loopback = 1;output;}; => actions=controller(userdata=00.00.00.05.00.00.00.00.ff.ff.00.38.00.00.23.20.00.25.00.00.00.00.00.00.00.03.00.28.00.00.00.06.00.00.00.00.12.34.56.78.9a.bc.00.00.05.aa.01.fd.ad.a0.f9.a0.12.00.00.00.00.00.00.00.00.00.00.40.ff.ff.00.18.00.00.23.20.00.06.00.20.00.00.00.00.00.01.1c.04.00.01.1e.04.ff.ff.00.18.00.00.23.20.00.07.00.00.00.01.14.04.00.00.00.00.00.00.00.01.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00), prereqs=nd_rs
+
 # get_nd
 get_nd(outport, ip6.dst); => actions=push:NXM_NX_XXREG0[],push:NXM_NX_IPV6_DST[],pop:NXM_NX_XXREG0[],set_field:00:00:00:00:00:00->eth_dst,resubmit(,65),pop:NXM_NX_XXREG0[], prereqs=eth.type == 0x86dd
 get_nd(inport, xxreg0); => actions=push:NXM_NX_REG15[],push:NXM_NX_REG14[],pop:NXM_NX_REG15[],set_field:00:00:00:00:00:00->eth_dst,resubmit(,65),pop:NXM_NX_REG15[], prereqs=1
@@ -745,7 +748,7 @@ get_nd(inport, outport); => Cannot use string field outport where numeric field
 get_nd(xxreg0, ip6.dst); => Cannot use numeric field xxreg0 where string field is required.
 
 # put_nd
-put_nd(inport, nd.target, nd.sll); => actions=push:NXM_NX_XXREG0[],push:NXM_OF_ETH_SRC[],push:NXM_NX_ND_SLL[],push:NXM_NX_ND_TARGET[],pop:NXM_NX_XXREG0[],pop:NXM_OF_ETH_SRC[],controller(userdata=00.00.00.04.00.00.00.00),pop:NXM_OF_ETH_SRC[],pop:NXM_NX_XXREG0[], prereqs=((icmp6.type == 0x87 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd)) || (icmp6.type == 0x88 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd))) && icmp6.code == 0 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && ip.ttl == 0xff && (eth.type == 0x800 || eth.type == 0x86dd) && icmp6.type == 0x87 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && icmp6.code == 0 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && ip.ttl == 0xff && (eth.type == 0x800 || eth.type == 0x86dd)
+put_nd(inport, nd.target, nd.sll); => actions=push:NXM_NX_XXREG0[],push:NXM_OF_ETH_SRC[],push:NXM_NX_ND_SLL[],push:NXM_NX_ND_TARGET[],pop:NXM_NX_XXREG0[],pop:NXM_OF_ETH_SRC[],controller(userdata=00.00.00.04.00.00.00.00),pop:NXM_OF_ETH_SRC[],pop:NXM_NX_XXREG0[], prereqs=((icmp6.type == 0x85 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd)) || (icmp6.type == 0x86 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd)) || (icmp6.type == 0x87 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd)) || (icmp6.type == 0x88 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd))) && icmp6.code == 0 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && ip.ttl == 0xff && (eth.type == 0x800 || eth.type == 0x86dd) && icmp6.type == 0x87 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd
 ) && icmp6.code == 0 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && ip.ttl == 0xff && (eth.type == 0x800 || eth.type == 0x86dd)
 
 # Contradictionary prerequisites (allowed but not useful):
 ip4.src = ip6.src[0..31]; => actions=move:NXM_NX_IPV6_SRC[0..31]->NXM_OF_IP_SRC[], prereqs=eth.type == 0x800 && eth.type == 0x86dd
-- 
1.9.1




More information about the dev mailing list