Пример #1
0
/* Reconfigures ovsdb-server based on information in the database. */
static void
reconfigure_from_db(struct ovsdb_jsonrpc_server *jsonrpc,
                    const struct ovsdb *db, struct shash *remotes)
{
    struct shash resolved_remotes;
    struct shash_node *node;

    /* Configure remotes. */
    shash_init(&resolved_remotes);
    SHASH_FOR_EACH (node, remotes) {
        const char *name = node->name;

        if (!strncmp(name, "db:", 3)) {
            query_db_remotes(name, db, &resolved_remotes);
        } else {
            shash_add_once(&resolved_remotes, name, NULL);
        }
    }
    ovsdb_jsonrpc_server_set_remotes(jsonrpc, &resolved_remotes);
    shash_destroy(&resolved_remotes);

#if HAVE_OPENSSL
    /* Configure SSL. */
    stream_ssl_set_key_and_cert(query_db_string(db, private_key_file),
                                query_db_string(db, certificate_file));
    stream_ssl_set_ca_cert_file(query_db_string(db, ca_cert_file),
                                bootstrap_ca_cert);
#endif
}
Пример #2
0
static void
create_port_groups(struct shash *port_groups)
{
    shash_init(port_groups);

    static const char *const pg1[] = {
        "lsp1", "lsp2", "lsp3",
    };
    static const char *const pg2[] = { NULL };

    expr_const_sets_add(port_groups, "pg1", pg1, 3, false);
    expr_const_sets_add(port_groups, "pg_empty", pg2, 0, false);
}
Пример #3
0
static void
create_addr_sets(struct shash *addr_sets)
{
    shash_init(addr_sets);

    static const char *const addrs1[] = {
        "10.0.0.1", "10.0.0.2", "10.0.0.3",
    };
    static const char *const addrs2[] = {
        "::1", "::2", "::3",
    };
    static const char *const addrs3[] = {
        "00:00:00:00:00:01", "00:00:00:00:00:02", "00:00:00:00:00:03",
    };
    static const char *const addrs4[] = { NULL };

    expr_const_sets_add(addr_sets, "set1", addrs1, 3, true);
    expr_const_sets_add(addr_sets, "set2", addrs2, 3, true);
    expr_const_sets_add(addr_sets, "set3", addrs3, 3, true);
    expr_const_sets_add(addr_sets, "set4", addrs4, 0, true);
}
Пример #4
0
void
ovn_init_symtab(struct shash *symtab)
{
    shash_init(symtab);

    /* Reserve a pair of registers for the logical inport and outport.  A full
     * 32-bit register each is bigger than we need, but the expression code
     * doesn't yet support string fields that occupy less than a full OXM. */
    expr_symtab_add_string(symtab, "inport", MFF_LOG_INPORT, NULL);
    expr_symtab_add_string(symtab, "outport", MFF_LOG_OUTPORT, NULL);

    /* Logical registers:
     *     128-bit xxregs
     *     64-bit xregs
     *     32-bit regs
     *
     * The expression language doesn't handle overlapping fields properly
     * unless they're formally defined as subfields.  It's a little awkward. */
    for (int xxi = 0; xxi < MFF_N_LOG_REGS / 4; xxi++) {
        char *xxname = xasprintf("xxreg%d", xxi);
        expr_symtab_add_field(symtab, xxname, MFF_XXREG0 + xxi, NULL, false);
        free(xxname);
    }
    for (int xi = 0; xi < MFF_N_LOG_REGS / 2; xi++) {
        char *xname = xasprintf("xreg%d", xi);
        int xxi = xi / 2;
        if (xxi < MFF_N_LOG_REGS / 4) {
            add_subregister(xname, "xxreg", xxi, 64, 1 - xi % 2, symtab);
        } else {
            expr_symtab_add_field(symtab, xname, MFF_XREG0 + xi, NULL, false);
        }
        free(xname);
    }
    for (int i = 0; i < MFF_N_LOG_REGS; i++) {
        char *name = xasprintf("reg%d", i);
        int xxi = i / 4;
        int xi = i / 2;
        if (xxi < MFF_N_LOG_REGS / 4) {
            add_subregister(name, "xxreg", xxi, 32, 3 - i % 4, symtab);
        } else if (xi < MFF_N_LOG_REGS / 2) {
            add_subregister(name, "xreg", xi, 32, 1 - i % 2, symtab);
        } else {
            expr_symtab_add_field(symtab, name, MFF_REG0 + i, NULL, false);
        }
        free(name);
    }

    /* Flags used in logical to physical transformation. */
    expr_symtab_add_field(symtab, "flags", MFF_LOG_FLAGS, NULL, false);
    char flags_str[16];
    snprintf(flags_str, sizeof flags_str, "flags[%d]", MLF_ALLOW_LOOPBACK_BIT);
    expr_symtab_add_subfield(symtab, "flags.loopback", NULL, flags_str);
    snprintf(flags_str, sizeof flags_str, "flags[%d]",
             MLF_FORCE_SNAT_FOR_DNAT_BIT);
    expr_symtab_add_subfield(symtab, "flags.force_snat_for_dnat", NULL,
                             flags_str);
    snprintf(flags_str, sizeof flags_str, "flags[%d]",
             MLF_FORCE_SNAT_FOR_LB_BIT);
    expr_symtab_add_subfield(symtab, "flags.force_snat_for_lb", NULL,
                             flags_str);

    /* Connection tracking state. */
    expr_symtab_add_field(symtab, "ct_mark", MFF_CT_MARK, NULL, false);

    expr_symtab_add_field(symtab, "ct_label", MFF_CT_LABEL, NULL, false);
    expr_symtab_add_subfield(symtab, "ct_label.blocked", NULL, "ct_label[0]");

    expr_symtab_add_field(symtab, "ct_state", MFF_CT_STATE, NULL, false);

    struct ct_bit {
        const char *name;
        int bit;
    };
    static const struct ct_bit bits[] = {
        {"trk", CS_TRACKED_BIT},
        {"new", CS_NEW_BIT},
        {"est", CS_ESTABLISHED_BIT},
        {"rel", CS_RELATED_BIT},
        {"rpl", CS_REPLY_DIR_BIT},
        {"inv", CS_INVALID_BIT},
        {"dnat", CS_DST_NAT_BIT},
        {"snat", CS_SRC_NAT_BIT},
    };
    for (const struct ct_bit *b = bits; b < &bits[ARRAY_SIZE(bits)]; b++) {
        char *name = xasprintf("ct.%s", b->name);
        char *expansion = xasprintf("ct_state[%d]", b->bit);
        const char *prereqs = b->bit == CS_TRACKED_BIT ? NULL : "ct.trk";
        expr_symtab_add_subfield(symtab, name, prereqs, expansion);
        free(expansion);
        free(name);
    }

    /* Data fields. */
    expr_symtab_add_field(symtab, "eth.src", MFF_ETH_SRC, NULL, false);
    expr_symtab_add_field(symtab, "eth.dst", MFF_ETH_DST, NULL, false);
    expr_symtab_add_field(symtab, "eth.type", MFF_ETH_TYPE, NULL, true);
    expr_symtab_add_predicate(symtab, "eth.bcast",
                              "eth.dst == ff:ff:ff:ff:ff:ff");
    expr_symtab_add_subfield(symtab, "eth.mcast", NULL, "eth.dst[40]");

    expr_symtab_add_field(symtab, "vlan.tci", MFF_VLAN_TCI, NULL, false);
    expr_symtab_add_predicate(symtab, "vlan.present", "vlan.tci[12]");
    expr_symtab_add_subfield(symtab, "vlan.pcp", "vlan.present",
                             "vlan.tci[13..15]");
    expr_symtab_add_subfield(symtab, "vlan.vid", "vlan.present",
                             "vlan.tci[0..11]");

    expr_symtab_add_predicate(symtab, "ip4", "eth.type == 0x800");
    expr_symtab_add_predicate(symtab, "ip6", "eth.type == 0x86dd");
    expr_symtab_add_predicate(symtab, "ip", "ip4 || ip6");
    expr_symtab_add_field(symtab, "ip.proto", MFF_IP_PROTO, "ip", true);
    expr_symtab_add_field(symtab, "ip.dscp", MFF_IP_DSCP_SHIFTED, "ip", false);
    expr_symtab_add_field(symtab, "ip.ecn", MFF_IP_ECN, "ip", false);
    expr_symtab_add_field(symtab, "ip.ttl", MFF_IP_TTL, "ip", false);

    expr_symtab_add_field(symtab, "ip4.src", MFF_IPV4_SRC, "ip4", false);
    expr_symtab_add_field(symtab, "ip4.dst", MFF_IPV4_DST, "ip4", false);
    expr_symtab_add_predicate(symtab, "ip4.mcast", "ip4.dst[28..31] == 0xe");

    expr_symtab_add_predicate(symtab, "icmp4", "ip4 && ip.proto == 1");
    expr_symtab_add_field(symtab, "icmp4.type", MFF_ICMPV4_TYPE, "icmp4",
              false);
    expr_symtab_add_field(symtab, "icmp4.code", MFF_ICMPV4_CODE, "icmp4",
              false);

    expr_symtab_add_field(symtab, "ip6.src", MFF_IPV6_SRC, "ip6", false);
    expr_symtab_add_field(symtab, "ip6.dst", MFF_IPV6_DST, "ip6", false);
    expr_symtab_add_field(symtab, "ip6.label", MFF_IPV6_LABEL, "ip6", false);

    expr_symtab_add_predicate(symtab, "icmp6", "ip6 && ip.proto == 58");
    expr_symtab_add_field(symtab, "icmp6.type", MFF_ICMPV6_TYPE, "icmp6",
                          true);
    expr_symtab_add_field(symtab, "icmp6.code", MFF_ICMPV6_CODE, "icmp6",
                          true);

    expr_symtab_add_predicate(symtab, "icmp", "icmp4 || icmp6");

    expr_symtab_add_field(symtab, "ip.frag", MFF_IP_FRAG, "ip", false);
    expr_symtab_add_predicate(symtab, "ip.is_frag", "ip.frag[0]");
    expr_symtab_add_predicate(symtab, "ip.later_frag", "ip.frag[1]");
    expr_symtab_add_predicate(symtab, "ip.first_frag",
                              "ip.is_frag && !ip.later_frag");

    expr_symtab_add_predicate(symtab, "arp", "eth.type == 0x806");
    expr_symtab_add_field(symtab, "arp.op", MFF_ARP_OP, "arp", false);
    expr_symtab_add_field(symtab, "arp.spa", MFF_ARP_SPA, "arp", false);
    expr_symtab_add_field(symtab, "arp.sha", MFF_ARP_SHA, "arp", false);
    expr_symtab_add_field(symtab, "arp.tpa", MFF_ARP_TPA, "arp", false);
    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");
    expr_symtab_add_predicate(symtab, "nd_ns",
              "icmp6.type == 135 && icmp6.code == 0 && ip.ttl == 255");
    expr_symtab_add_predicate(symtab, "nd_na",
              "icmp6.type == 136 && icmp6.code == 0 && ip.ttl == 255");
    expr_symtab_add_field(symtab, "nd.target", MFF_ND_TARGET, "nd", false);
    expr_symtab_add_field(symtab, "nd.sll", MFF_ND_SLL, "nd_ns", false);
    expr_symtab_add_field(symtab, "nd.tll", MFF_ND_TLL, "nd_na", false);

    expr_symtab_add_predicate(symtab, "tcp", "ip.proto == 6");
    expr_symtab_add_field(symtab, "tcp.src", MFF_TCP_SRC, "tcp", false);
    expr_symtab_add_field(symtab, "tcp.dst", MFF_TCP_DST, "tcp", false);
    expr_symtab_add_field(symtab, "tcp.flags", MFF_TCP_FLAGS, "tcp", false);

    expr_symtab_add_predicate(symtab, "udp", "ip.proto == 17");
    expr_symtab_add_field(symtab, "udp.src", MFF_UDP_SRC, "udp", false);
    expr_symtab_add_field(symtab, "udp.dst", MFF_UDP_DST, "udp", false);

    expr_symtab_add_predicate(symtab, "sctp", "ip.proto == 132");
    expr_symtab_add_field(symtab, "sctp.src", MFF_SCTP_SRC, "sctp", false);
    expr_symtab_add_field(symtab, "sctp.dst", MFF_SCTP_DST, "sctp", false);
}
Пример #5
0
// create a new locl_subsystem object
static struct locl_subsystem *
add_subsystem(const struct ovsrec_subsystem *ovsrec_subsys)
{
    struct locl_subsystem *result;
    int rc;
    int idx;
    struct ovsdb_idl_txn *txn;
    struct ovsrec_temp_sensor **sensor_array;
    int sensor_idx;
    int sensor_count;
    const char *dir;
    const YamlThermalInfo *info;

    // create and initialize basic subsystem information
    VLOG_DBG("Adding new subsystem %s", ovsrec_subsys->name);
    result = (struct locl_subsystem *)malloc(sizeof(struct locl_subsystem));
    memset(result, 0, sizeof(struct locl_subsystem));
    (void)shash_add(&subsystem_data, ovsrec_subsys->name, (void *)result);
    result->name = strdup(ovsrec_subsys->name);
    result->marked = false;
    result->marked = true;
    result->parent_subsystem = NULL;  // OPS_TODO: find parent subsystem
    shash_init(&result->subsystem_sensors);

    // use a default if the hw_desc_dir has not been populated
    dir = ovsrec_subsys->hw_desc_dir;

    if (dir == NULL || strlen(dir) == 0) {
        VLOG_ERR("No h/w description directory for subsystem %s",
                                        ovsrec_subsys->name);
        return(NULL);
    }

    // since this is a new subsystem, load all of the hardware description
    // information about devices and sensors (just for this subsystem).
    // parse sensors and device data for subsystem
    rc = yaml_add_subsystem(yaml_handle, ovsrec_subsys->name, dir);

    if (rc != 0) {
        VLOG_ERR("Error reading h/w description files for subsystem %s",
                                        ovsrec_subsys->name);
        return(NULL);
    }

    // need devices data
    rc = yaml_parse_devices(yaml_handle, ovsrec_subsys->name);

    if (rc != 0) {
        VLOG_ERR("Unable to parse subsystem %s devices file (in %s)",
                                        ovsrec_subsys->name, dir);
        return(NULL);
    }

    // need thermal (sensor) data
    rc = yaml_parse_thermal(yaml_handle, ovsrec_subsys->name);

    if (rc != 0) {
        VLOG_ERR("Unable to parse subsystem %s thermal file (in %s)",
                                        ovsrec_subsys->name, dir);
        return(NULL);
    }

    // get the thermal info, need it for shutdown flag
    info = yaml_get_thermal_info(yaml_handle, ovsrec_subsys->name);
    result->emergency_shutdown = info->auto_shutdown;

    // OPS_TODO: the thermal info has a polling period, but when we
    // OPS_TODO: have multiple subsystems, that could be tricky to
    // OPS_TODO: implement if there are different polling periods.
    // OPS_TODO: For now, hardware the polling period to 5 seconds.

    // prepare to add sensors to db
    sensor_idx = 0;
    sensor_count = yaml_get_sensor_count(yaml_handle, ovsrec_subsys->name);

    if (sensor_count <= 0) {
        return(NULL);
    }

    result->valid = true;

    // subsystem db object has reference array for sensors
    sensor_array = (struct ovsrec_temp_sensor **)malloc(sensor_count * sizeof(struct ovsrec_temp_sensor *));
    memset(sensor_array, 0, sensor_count * sizeof(struct ovsrec_temp_sensor *));

    txn = ovsdb_idl_txn_create(idl);

    VLOG_DBG("There are %d sensors in subsystem %s", sensor_count, ovsrec_subsys->name);

    for (idx = 0; idx < sensor_count; idx++) {
        const YamlSensor *sensor = yaml_get_sensor(yaml_handle, ovsrec_subsys->name, idx);

        struct ovsrec_temp_sensor *ovs_sensor;
        char *sensor_name = NULL;
        struct locl_sensor *new_sensor;
        VLOG_DBG("Adding sensor %d (%s) in subsystem %s",
            sensor->number,
            sensor->location,
            ovsrec_subsys->name);

        // create a name for the sensor from the subsystem name and the
        // sensor number
        asprintf(&sensor_name, "%s-%d", ovsrec_subsys->name, sensor->number);
        // allocate and initialize basic sensor information
        new_sensor = (struct locl_sensor *)malloc(sizeof(struct locl_sensor));
        new_sensor->name = sensor_name;
        new_sensor->subsystem = result;
        new_sensor->yaml_sensor = sensor;
        new_sensor->min = 1000000;
        new_sensor->max = -1000000;
        new_sensor->temp = 0;
        new_sensor->status = SENSOR_STATUS_NORMAL;
        new_sensor->fan_speed = SENSOR_FAN_NORMAL;
        new_sensor->test_temp = -1;     // no test temperature override set

        // try to populate sensor information with real data
        tempd_read_sensor(new_sensor);

        // add sensor to subsystem sensor dictionary
        shash_add(&result->subsystem_sensors, sensor_name, (void *)new_sensor);
        // add sensor to global sensor dictionary
        shash_add(&sensor_data, sensor_name, (void *)new_sensor);

        // look for existing Temp_sensor rows
        ovs_sensor = lookup_sensor(sensor_name);

        if (ovs_sensor == NULL) {
            // existing sensor doesn't exist in db, create it
            ovs_sensor = ovsrec_temp_sensor_insert(txn);
        }

        // set initial data
        ovsrec_temp_sensor_set_name(ovs_sensor, sensor_name);
        ovsrec_temp_sensor_set_status(ovs_sensor,
            sensor_status_to_string(new_sensor->status));
        ovsrec_temp_sensor_set_temperature(ovs_sensor, new_sensor->temp);
        ovsrec_temp_sensor_set_min(ovs_sensor, new_sensor->min);
        ovsrec_temp_sensor_set_max(ovs_sensor, new_sensor->max);
        ovsrec_temp_sensor_set_fan_state(ovs_sensor,
            sensor_speed_to_string(new_sensor->fan_speed));
        ovsrec_temp_sensor_set_location(ovs_sensor, sensor->location);

        // add sensor to subsystem reference list
        sensor_array[sensor_idx++] = ovs_sensor;
    }

    ovsrec_subsystem_set_temp_sensors(ovsrec_subsys, sensor_array, sensor_count);
    // execute transaction
    ovsdb_idl_txn_commit_block(txn);
    ovsdb_idl_txn_destroy(txn);
    free(sensor_array);

    return(result);
}
Пример #6
0
// initialize the subsystem and global sensor dictionaries
static void
init_subsystems(void)
{
    shash_init(&subsystem_data);
    shash_init(&sensor_data);
}
Пример #7
0
int
main(int argc, char *argv[])
{
    struct ovsdb_idl *idl;
    struct ctl_command *commands;
    struct shash local_options;
    unsigned int seqno;
    size_t n_commands;
    char *args;

    set_program_name(argv[0]);
    fatal_ignore_sigpipe();
    vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN);
    vlog_set_levels_from_string_assert("reconnect:warn");
    sbrec_init();

    sbctl_cmd_init();

    /* Log our arguments.  This is often valuable for debugging systems. */
    args = process_escape_args(argv);
    VLOG(ctl_might_write_to_db(argv) ? VLL_INFO : VLL_DBG, "Called as %s", args);

    /* Parse command line. */
    shash_init(&local_options);
    parse_options(argc, argv, &local_options);
    commands = ctl_parse_commands(argc - optind, argv + optind, &local_options,
                                  &n_commands);

    if (timeout) {
        time_alarm(timeout);
    }

    /* Initialize IDL. */
    idl = the_idl = ovsdb_idl_create(db, &sbrec_idl_class, false, false);
    run_prerequisites(commands, n_commands, idl);

    /* Execute the commands.
     *
     * 'seqno' is the database sequence number for which we last tried to
     * execute our transaction.  There's no point in trying to commit more than
     * once for any given sequence number, because if the transaction fails
     * it's because the database changed and we need to obtain an up-to-date
     * view of the database before we try the transaction again. */
    seqno = ovsdb_idl_get_seqno(idl);
    for (;;) {
        ovsdb_idl_run(idl);
        if (!ovsdb_idl_is_alive(idl)) {
            int retval = ovsdb_idl_get_last_error(idl);
            ctl_fatal("%s: database connection failed (%s)",
                        db, ovs_retval_to_string(retval));
        }

        if (seqno != ovsdb_idl_get_seqno(idl)) {
            seqno = ovsdb_idl_get_seqno(idl);
            if (do_sbctl(args, commands, n_commands, idl)) {
                free(args);
                exit(EXIT_SUCCESS);
            }
        }

        if (seqno == ovsdb_idl_get_seqno(idl)) {
            ovsdb_idl_wait(idl);
            poll_block();
        }
    }
}
static void
symtab_init(void)
{
    shash_init(&symtab);

    /* Reserve a pair of registers for the logical inport and outport.  A full
     * 32-bit register each is bigger than we need, but the expression code
     * doesn't yet support string fields that occupy less than a full OXM. */
    expr_symtab_add_string(&symtab, "inport", MFF_LOG_INPORT, NULL);
    expr_symtab_add_string(&symtab, "outport", MFF_LOG_OUTPORT, NULL);

    /* Logical registers. */
#define MFF_LOG_REG(ID) add_logical_register(&symtab, ID);
    MFF_LOG_REGS;
#undef MFF_LOG_REG

    /* Connection tracking state. */
    expr_symtab_add_field(&symtab, "ct_state", MFF_CT_STATE, NULL, false);
    char ct_state_str[16];
    snprintf(ct_state_str, sizeof ct_state_str, "ct_state[%d]", CS_TRACKED_BIT);
    expr_symtab_add_predicate(&symtab, "ct.trk", ct_state_str);
    snprintf(ct_state_str, sizeof ct_state_str, "ct_state[%d]", CS_NEW_BIT);
    expr_symtab_add_subfield(&symtab, "ct.new", "ct.trk", ct_state_str);
    snprintf(ct_state_str, sizeof ct_state_str, "ct_state[%d]", CS_ESTABLISHED_BIT);
    expr_symtab_add_subfield(&symtab, "ct.est", "ct.trk", ct_state_str);
    snprintf(ct_state_str, sizeof ct_state_str, "ct_state[%d]", CS_RELATED_BIT);
    expr_symtab_add_subfield(&symtab, "ct.rel", "ct.trk", ct_state_str);
    snprintf(ct_state_str, sizeof ct_state_str, "ct_state[%d]", CS_REPLY_DIR_BIT);
    expr_symtab_add_subfield(&symtab, "ct.rpl", "ct.trk", ct_state_str);
    snprintf(ct_state_str, sizeof ct_state_str, "ct_state[%d]", CS_INVALID_BIT);
    expr_symtab_add_subfield(&symtab, "ct.inv", "ct.trk", ct_state_str);

    /* Data fields. */
    expr_symtab_add_field(&symtab, "eth.src", MFF_ETH_SRC, NULL, false);
    expr_symtab_add_field(&symtab, "eth.dst", MFF_ETH_DST, NULL, false);
    expr_symtab_add_field(&symtab, "eth.type", MFF_ETH_TYPE, NULL, true);
    expr_symtab_add_predicate(&symtab, "eth.bcast",
                              "eth.dst == ff:ff:ff:ff:ff:ff");
    expr_symtab_add_subfield(&symtab, "eth.mcast", NULL, "eth.dst[40]");

    expr_symtab_add_field(&symtab, "vlan.tci", MFF_VLAN_TCI, NULL, false);
    expr_symtab_add_predicate(&symtab, "vlan.present", "vlan.tci[12]");
    expr_symtab_add_subfield(&symtab, "vlan.pcp", "vlan.present",
                             "vlan.tci[13..15]");
    expr_symtab_add_subfield(&symtab, "vlan.vid", "vlan.present",
                             "vlan.tci[0..11]");

    expr_symtab_add_predicate(&symtab, "ip4", "eth.type == 0x800");
    expr_symtab_add_predicate(&symtab, "ip6", "eth.type == 0x86dd");
    expr_symtab_add_predicate(&symtab, "ip", "ip4 || ip6");
    expr_symtab_add_field(&symtab, "ip.proto", MFF_IP_PROTO, "ip", true);
    expr_symtab_add_field(&symtab, "ip.dscp", MFF_IP_DSCP, "ip", false);
    expr_symtab_add_field(&symtab, "ip.ecn", MFF_IP_ECN, "ip", false);
    expr_symtab_add_field(&symtab, "ip.ttl", MFF_IP_TTL, "ip", false);

    expr_symtab_add_field(&symtab, "ip4.src", MFF_IPV4_SRC, "ip4", false);
    expr_symtab_add_field(&symtab, "ip4.dst", MFF_IPV4_DST, "ip4", false);
    expr_symtab_add_predicate(&symtab, "ip4.mcast", "ip4.dst[28..31] == 0xe");

    expr_symtab_add_predicate(&symtab, "icmp4", "ip4 && ip.proto == 1");
    expr_symtab_add_field(&symtab, "icmp4.type", MFF_ICMPV4_TYPE, "icmp4",
              false);
    expr_symtab_add_field(&symtab, "icmp4.code", MFF_ICMPV4_CODE, "icmp4",
              false);

    expr_symtab_add_field(&symtab, "ip6.src", MFF_IPV6_SRC, "ip6", false);
    expr_symtab_add_field(&symtab, "ip6.dst", MFF_IPV6_DST, "ip6", false);
    expr_symtab_add_field(&symtab, "ip6.label", MFF_IPV6_LABEL, "ip6", false);

    expr_symtab_add_predicate(&symtab, "icmp6", "ip6 && ip.proto == 58");
    expr_symtab_add_field(&symtab, "icmp6.type", MFF_ICMPV6_TYPE, "icmp6",
                          true);
    expr_symtab_add_field(&symtab, "icmp6.code", MFF_ICMPV6_CODE, "icmp6",
                          true);

    expr_symtab_add_predicate(&symtab, "icmp", "icmp4 || icmp6");

    expr_symtab_add_field(&symtab, "ip.frag", MFF_IP_FRAG, "ip", false);
    expr_symtab_add_predicate(&symtab, "ip.is_frag", "ip.frag[0]");
    expr_symtab_add_predicate(&symtab, "ip.later_frag", "ip.frag[1]");
    expr_symtab_add_predicate(&symtab, "ip.first_frag",
                              "ip.is_frag && !ip.later_frag");

    expr_symtab_add_predicate(&symtab, "arp", "eth.type == 0x806");
    expr_symtab_add_field(&symtab, "arp.op", MFF_ARP_OP, "arp", false);
    expr_symtab_add_field(&symtab, "arp.spa", MFF_ARP_SPA, "arp", false);
    expr_symtab_add_field(&symtab, "arp.sha", MFF_ARP_SHA, "arp", false);
    expr_symtab_add_field(&symtab, "arp.tpa", MFF_ARP_TPA, "arp", false);
    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");
    expr_symtab_add_field(&symtab, "nd.target", MFF_ND_TARGET, "nd", false);
    expr_symtab_add_field(&symtab, "nd.sll", MFF_ND_SLL,
              "nd && icmp6.type == 135", false);
    expr_symtab_add_field(&symtab, "nd.tll", MFF_ND_TLL,
              "nd && icmp6.type == 136", false);

    expr_symtab_add_predicate(&symtab, "tcp", "ip.proto == 6");
    expr_symtab_add_field(&symtab, "tcp.src", MFF_TCP_SRC, "tcp", false);
    expr_symtab_add_field(&symtab, "tcp.dst", MFF_TCP_DST, "tcp", false);
    expr_symtab_add_field(&symtab, "tcp.flags", MFF_TCP_FLAGS, "tcp", false);

    expr_symtab_add_predicate(&symtab, "udp", "ip.proto == 17");
    expr_symtab_add_field(&symtab, "udp.src", MFF_UDP_SRC, "udp", false);
    expr_symtab_add_field(&symtab, "udp.dst", MFF_UDP_DST, "udp", false);

    expr_symtab_add_predicate(&symtab, "sctp", "ip.proto == 132");
    expr_symtab_add_field(&symtab, "sctp.src", MFF_SCTP_SRC, "sctp", false);
    expr_symtab_add_field(&symtab, "sctp.dst", MFF_SCTP_DST, "sctp", false);
}