diff --git a/include/ovn/actions.h b/include/ovn/actions.h
index a395ce9..9eaa8db 100644
--- a/include/ovn/actions.h
+++ b/include/ovn/actions.h
@@ -88,6 +88,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 d790c49..150f2e7 100644
--- a/include/ovn/expr.h
+++ b/include/ovn/expr.h
@@ -450,6 +450,17 @@ struct expr_constant_set {
     bool in_curlies;              /* Whether the constants were in {}. */
 };
 
+/* Context maintained during expr_parse(). */
+struct expr_context {
+    struct lexer *lexer;        /* Lexer for pulling more tokens. */
+    const struct shash *symtab; /* Symbol table. */
+    const struct shash *macros; /* Table of macros. */
+    char *error;                /* Error, if any, otherwise NULL. */
+    bool not;                   /* True inside odd number of NOT operators. */
+};
+
+bool parse_constant(struct expr_context *ctx, struct expr_constant_set *cs,
+                    size_t *allocated_values);
 char *expr_parse_constant_set(struct lexer *, const struct shash *symtab,
                               struct expr_constant_set *cs)
     OVS_WARN_UNUSED_RESULT;
diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c
index fda10eb..18afefd 100644
--- a/ovn/controller/lflow.c
+++ b/ovn/controller/lflow.c
@@ -164,7 +164,11 @@ lflow_init(void)
     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/controller/pinctrl.c b/ovn/controller/pinctrl.c
index bd685fe..fa1e7d7 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,
+                                 struct ofputil_packet_in *pin,
+                                 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, &userdata);
+        break;
+
     default:
         VLOG_WARN_RL(&rl, "unrecognized packet-in opcode %"PRIu32,
                      ntohl(ah->opcode));
@@ -1019,3 +1026,164 @@ 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 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);
+
+    /* 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 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);
+
+    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);
+    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);
+    }
+    packet_put_ra_mtu_opt(packet, *mtu);
+    packet_put_ra_sll_opt(packet, *sll);
+    ret_val = true;
+
+exit:
+    if (prefixes) {
+        free(prefixes);
+    }
+    return ret_val;
+}
+
+static void
+pinctrl_handle_nd_ra(const struct flow *ip_flow, struct ofputil_packet_in *pin,
+                     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, &pin->flow_metadata);
+
+    /* 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 b9d1205..7e913ce 100644
--- a/ovn/lib/actions.c
+++ b/ovn/lib/actions.c
@@ -1080,6 +1080,71 @@ 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)
+{
+    if (!lexer_match(ctx->lexer, LEX_T_LPAREN)) {
+        action_syntax_error(ctx, "expecting '('");
+        return;
+    }
+
+    struct expr_constant_set cs = {
+        .type = EXPR_C_INTEGER,
+        .values = NULL,
+        .n_values = 0,
+        .in_curlies = false,
+    };
+    struct expr_context expr_ctx = {
+       .lexer = ctx->lexer,
+       .symtab = NULL,
+    };
+    size_t allocated_values = 0;
+
+    do {
+        if (!parse_constant(&expr_ctx, &cs, &allocated_values)) {
+            break;
+        }
+        lexer_match(expr_ctx.lexer, LEX_T_COMMA);
+    } while(!lexer_match(expr_ctx.lexer, LEX_T_RPAREN));
+    /* Multiple prefixes, as least 1 MTU, at least 1 SLL. */
+    if (cs.n_values < 3) {
+        action_syntax_error(ctx, "not enough constants found");
+        goto exit;
+    }
+    /* Check prefixes. */
+    for (size_t i = 0; i < cs.n_values - 2; i++) {
+        if (cs.values[i].format != LEX_F_IPV6) {
+            action_syntax_error(ctx, "expecting IPv6 address format");
+            goto exit;
+        }
+    }
+
+    /* Set controller data. */
+    size_t oc_offset = start_controller_op(ctx->ofpacts, ACTION_OPCODE_SLAAC,
+                                           false);
+    cs.n_values = cs.n_values - 2;
+    /* Put prefixes number. */
+    ofpbuf_put(ctx->ofpacts, &cs.n_values, 1);
+    /* Put prefixes. */
+    int plen = 0;
+    for (size_t i = 0; 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);
+    }
+    /* Put MTU. */
+    ofpbuf_put(ctx->ofpacts, &cs.values[cs.n_values].value.be32[31],
+                             sizeof(ovs_be32));
+    /* Put SLL. */
+    ofpbuf_put(ctx->ofpacts, &cs.values[cs.n_values + 1].value.mac,
+                             sizeof(struct eth_addr));
+    finish_controller_op(ctx->ofpacts, oc_offset);
+
+exit:
+    expr_constant_set_destroy(&cs);
+}
+
 static bool
 parse_action(struct action_context *ctx)
 {
@@ -1125,6 +1190,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 1649c05..730e749 100644
--- a/ovn/lib/expr.c
+++ b/ovn/lib/expr.c
@@ -406,15 +406,6 @@ expr_print(const struct expr *e)
 
 /* Parsing. */
 
-/* Context maintained during expr_parse(). */
-struct expr_context {
-    struct lexer *lexer;        /* Lexer for pulling more tokens. */
-    const struct shash *symtab; /* Symbol table. */
-    const struct shash *macros; /* Table of macros. */
-    char *error;                /* Error, if any, otherwise NULL. */
-    bool not;                   /* True inside odd number of NOT operators. */
-};
-
 struct expr *expr_parse__(struct expr_context *);
 static void expr_not(struct expr *);
 static bool parse_field(struct expr_context *, struct expr_field *);
@@ -757,7 +748,7 @@ parse_macros(struct expr_context *ctx, struct expr_constant_set *cs,
     return true;
 }
 
-static bool
+bool
 parse_constant(struct expr_context *ctx, struct expr_constant_set *cs,
                size_t *allocated_values)
 {
diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml
index 13c9526..66a9009 100644
--- a/ovn/ovn-sb.xml
+++ b/ovn/ovn-sb.xml
@@ -1151,8 +1151,38 @@
           </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>: One or more IPv6 prefixes, 16-bit MUT, 48-bit
+            mac address.
+            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_arp(<var>P</var>, <var>A</var>);</code></dt>
+
+        <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 54fa8c5..454cb24 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -701,7 +701,11 @@ 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)
+
+# ra for slaac
+nd_ra{slaac(fdad:a0f9:a012::/64,fdad:a0f9:c593::/64,1450,fa:16:3e:32:3c:e0); outport = inport; flags.loopback = 1; output;}; => actions=controller(userdata=00.00.00.05.00.00.00.00.ff.ff.00.50.00.00.23.20.00.25.00.00.00.00.00.00.00.03.00.39.00.00.00.06.00.00.00.00.02.fd.ad.a0.f9.a0.12.00.00.00.00.00.00.00.00.00.00.40.fd.ad.a0.f9.c5.93.00.00.00.00.00.00.00.00.00.00.40.00.00.05.aa.fa.16.3e.32.3c.e0.00.00.00.00.00.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.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
+nd_ra{slaac(fd80:a123:b345::/64,1450,fa:16:3e:62:f1:e6); 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.01.fd.80.a1.23.b3.45.00.00.00.00.00.00.00.00.00.00.40.00.00.05.aa.fa.16.3e.62.f1.e6.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
 
 # 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
diff --git a/tests/test-ovn.c b/tests/test-ovn.c
index acb6a99..3a7c406 100644
--- a/tests/test-ovn.c
+++ b/tests/test-ovn.c
@@ -213,7 +213,11 @@ create_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",
