root/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_snmp.c
/*-
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 2010 The FreeBSD Foundation
 *
 * This software was developed by Shteryana Sotirova Shopova under
 * sponsorship from the FreeBSD Foundation.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/types.h>

#include <net/if.h>
#include <net/if_media.h>
#include <net/if_mib.h>
#include <net/if_types.h>
#include <net80211/ieee80211.h>
#include <net80211/ieee80211_ioctl.h>

#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <syslog.h>

#include <bsnmp/snmpmod.h>
#include <bsnmp/snmp_mibII.h>

#define SNMPTREE_TYPES
#include "wlan_tree.h"
#include "wlan_snmp.h"
#include "wlan_oid.h"

static struct lmodule *wlan_module;

/* For the registration. */
static const struct asn_oid oid_wlan = OIDX_begemotWlan;
/* The registration. */
static uint reg_wlan;

/* Periodic timer for polling the module's data. */
static void *wlan_data_timer;

/*
 * Poll data from kernel every 15 minutes unless explicitly requested by an
 * SNMP client.
 * XXX: make that configurable.
 */
static int wlan_poll_ticks = (15 * 60) * 100;

/* The age of each table. */
#define WLAN_LIST_MAXAGE        5

static time_t wlan_iflist_age;
static time_t wlan_peerlist_age;
static time_t wlan_chanlist_age;
static time_t wlan_roamlist_age;
static time_t wlan_tx_paramlist_age;
static time_t wlan_scanlist_age;
static time_t wlan_maclist_age;
static time_t wlan_mrlist_age;

/*
 * The list of all virtual wireless interfaces - sorted by name.
 */
SLIST_HEAD(wlan_ifaces, wlan_iface);
static struct wlan_ifaces wlan_ifaces = SLIST_HEAD_INITIALIZER(wlan_ifaces);

static struct wlan_config wlan_config;

/* Forward declarations */
static int      bits_get(struct snmp_value *, const u_char *, ssize_t);

static int      wlan_add_wif(struct wlan_iface *);
static void     wlan_delete_wif(struct wlan_iface *);
static int      wlan_attach_newif(struct mibif *);
static int      wlan_iface_create(struct wlan_iface *);
static int      wlan_iface_destroy(struct wlan_iface *);
static struct wlan_iface *      wlan_new_wif(char *);

static void     wlan_free_interface(struct wlan_iface *);
static void     wlan_free_iflist(void);
static void     wlan_free_peerlist(struct wlan_iface *);
static void     wlan_scan_free_results(struct wlan_iface *);
static void     wlan_mac_free_maclist(struct wlan_iface *);
static void     wlan_mesh_free_routes(struct wlan_iface *);

static int      wlan_update_interface(struct wlan_iface *);
static void     wlan_update_interface_list(void);
static void     wlan_update_peers(void);
static void     wlan_update_channels(void);
static void     wlan_update_roam_params(void);
static void     wlan_update_tx_params(void);
static void     wlan_scan_update_results(void);
static void     wlan_mac_update_aclmacs(void);
static void     wlan_mesh_update_routes(void);

static struct wlan_iface *      wlan_find_interface(const char *);
static struct wlan_peer *       wlan_find_peer(struct wlan_iface *, uint8_t *);
static struct ieee80211_channel*        wlan_find_channel(struct wlan_iface *,
    uint32_t);
static struct wlan_scan_result *        wlan_scan_find_result(struct wlan_iface *,
    uint8_t *, uint8_t *);
static struct wlan_mac_mac *            wlan_mac_find_mac(struct wlan_iface *,
    uint8_t *);
static struct wlan_mesh_route *         wlan_mesh_find_route(struct wlan_iface *,
    uint8_t *);

static struct wlan_iface *      wlan_first_interface(void);
static struct wlan_iface *      wlan_next_interface(struct wlan_iface *);
static struct wlan_iface *      wlan_mesh_first_interface(void);
static struct wlan_iface *      wlan_mesh_next_interface(struct wlan_iface *);

static struct wlan_iface *      wlan_get_interface(const struct asn_oid *, uint);
static struct wlan_iface *      wlan_get_snmp_interface(const struct asn_oid *,
    uint);
static struct wlan_peer *       wlan_get_peer(const struct asn_oid *, uint,
    struct wlan_iface **);
static struct ieee80211_channel *wlan_get_channel(const struct asn_oid *, uint,
    struct wlan_iface **);
static struct ieee80211_roamparam *wlan_get_roam_param(const struct asn_oid *,
    uint, struct wlan_iface **);
static struct ieee80211_txparam *wlan_get_tx_param(const struct asn_oid *,
    uint, struct wlan_iface **, uint32_t *);
static struct wlan_scan_result *wlan_get_scanr(const struct asn_oid *, uint,
    struct wlan_iface **);
static struct wlan_mac_mac *    wlan_get_acl_mac(const struct asn_oid *,
    uint, struct wlan_iface **);
static struct wlan_iface *      wlan_mesh_get_iface(const struct asn_oid *, uint);
static struct wlan_peer *       wlan_mesh_get_peer(const struct asn_oid *, uint,
    struct wlan_iface **);
static struct wlan_mesh_route * wlan_mesh_get_route(const struct asn_oid *,
    uint, struct wlan_iface **);

static struct wlan_iface *      wlan_get_next_interface(const struct asn_oid *,
    uint);
static struct wlan_iface *      wlan_get_next_snmp_interface(const struct
    asn_oid *, uint);
static struct wlan_peer *       wlan_get_next_peer(const struct asn_oid *, uint,
    struct wlan_iface **);
static struct ieee80211_channel *wlan_get_next_channel(const struct asn_oid *,
    uint, struct wlan_iface **);
static struct ieee80211_roamparam *wlan_get_next_roam_param(const struct
    asn_oid *, uint sub, struct wlan_iface **, uint32_t *);
static struct ieee80211_txparam *wlan_get_next_tx_param(const struct asn_oid *,
    uint, struct wlan_iface **, uint32_t *);
static struct wlan_scan_result *wlan_get_next_scanr(const struct asn_oid *,
    uint , struct wlan_iface **);
static struct wlan_mac_mac *    wlan_get_next_acl_mac(const struct asn_oid *,
    uint, struct wlan_iface **);
static struct wlan_iface *      wlan_mesh_get_next_iface(const struct asn_oid *,
    uint);
static struct wlan_peer *       wlan_mesh_get_next_peer(const struct asn_oid *,
    uint, struct wlan_iface **);
static struct wlan_mesh_route * wlan_mesh_get_next_route(const struct asn_oid *,
    uint sub, struct wlan_iface **);

static uint8_t *wlan_get_ifname(const struct asn_oid *, uint, uint8_t *);
static int      wlan_mac_index_decode(const struct asn_oid *, uint, char *,
    uint8_t *);
static int      wlan_channel_index_decode(const struct asn_oid *, uint,
    char *, uint32_t *);
static int      wlan_phy_index_decode(const struct asn_oid *, uint, char *,
    uint32_t *);
static int wlan_scanr_index_decode(const struct asn_oid *oid, uint sub,
    char *wname, uint8_t *ssid, uint8_t *bssid);

static void     wlan_append_ifindex(struct asn_oid *, uint,
    const struct wlan_iface *);
static void     wlan_append_mac_index(struct asn_oid *, uint, char *, uint8_t *);
static void     wlan_append_channel_index(struct asn_oid *, uint,
    const struct wlan_iface *, const struct ieee80211_channel *);
static void     wlan_append_phy_index(struct asn_oid *, uint, char *, uint32_t);
static void     wlan_append_scanr_index(struct asn_oid *, uint, char *,
    uint8_t *, uint8_t *);

static int      wlan_acl_mac_set_status(struct snmp_context *,
    struct snmp_value *, uint);
static int      wlan_mesh_route_set_status(struct snmp_context *,
    struct snmp_value *, uint);

static int32_t  wlan_get_channel_type(struct ieee80211_channel *);
static int      wlan_scan_compare_result(struct wlan_scan_result *,
    struct wlan_scan_result *);
static int      wlan_mac_delete_mac(struct wlan_iface *, struct wlan_mac_mac *);
static int      wlan_mesh_delete_route(struct wlan_iface *,
    struct wlan_mesh_route *);

/*
 * The module's GET/SET data hooks per each table or group of objects as
 * required by bsnmpd(1).
 */
int
op_wlan_iface(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub,
    uint32_t iidx __unused, enum snmp_op op)
{
        int rc;
        char wname[IFNAMSIZ];
        struct wlan_iface *wif;

        wlan_update_interface_list();

        switch (op) {
        case SNMP_OP_GET:
                if ((wif = wlan_get_snmp_interface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                break;

        case SNMP_OP_GETNEXT:
                if ((wif = wlan_get_next_snmp_interface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                wlan_append_ifindex(&val->var, sub, wif);
                break;

        case SNMP_OP_SET:
                if ((wif = wlan_get_snmp_interface(&val->var, sub)) == NULL) {
                        if (val->var.subs[sub - 1] != LEAF_wlanIfaceName)
                                return (SNMP_ERR_NOSUCHNAME);
                        if (wlan_get_ifname(&val->var, sub, wname) == NULL)
                                return (SNMP_ERR_INCONS_VALUE);
                        if ((wif = wlan_new_wif(wname)) == NULL)
                                return (SNMP_ERR_GENERR);
                        wif->internal = 1;
                }
                if (wif->status == RowStatus_active &&
                    val->var.subs[sub - 1] != LEAF_wlanIfaceStatus &&
                    val->var.subs[sub - 1] != LEAF_wlanIfaceState)
                        return (SNMP_ERR_INCONS_VALUE);

                switch (val->var.subs[sub - 1]) {
                case LEAF_wlanIfaceIndex:
                        return (SNMP_ERR_NOT_WRITEABLE);

                case LEAF_wlanIfaceName:
                        if (val->v.octetstring.len >= IFNAMSIZ)
                                return (SNMP_ERR_INCONS_VALUE);
                        if ((ctx->scratch->ptr1 = malloc(IFNAMSIZ)) == NULL)
                                return (SNMP_ERR_GENERR);
                        strlcpy(ctx->scratch->ptr1, wif->wname, IFNAMSIZ);
                        memcpy(wif->wname, val->v.octetstring.octets,
                            val->v.octetstring.len);
                        wif->wname[val->v.octetstring.len] = '\0';
                        return (SNMP_ERR_NOERROR);

                case LEAF_wlanParentIfName:
                        if (val->v.octetstring.len >= IFNAMSIZ)
                                return (SNMP_ERR_INCONS_VALUE);
                        if ((ctx->scratch->ptr1 = malloc(IFNAMSIZ)) == NULL)
                                return (SNMP_ERR_GENERR);
                        strlcpy(ctx->scratch->ptr1, wif->pname, IFNAMSIZ);
                        memcpy(wif->pname, val->v.octetstring.octets,
                            val->v.octetstring.len);
                        wif->pname[val->v.octetstring.len] = '\0';
                        return (SNMP_ERR_NOERROR);

                case LEAF_wlanIfaceOperatingMode:
                        ctx->scratch->int1 = wif->mode;
                        wif->mode = val->v.integer;
                        return (SNMP_ERR_NOERROR);

                case LEAF_wlanIfaceFlags:
                        if (val->v.octetstring.len > sizeof(wif->flags))
                                return (SNMP_ERR_INCONS_VALUE);
                        ctx->scratch->ptr1 = malloc(sizeof(wif->flags));
                        if (ctx->scratch->ptr1 == NULL)
                                return (SNMP_ERR_GENERR);
                        memcpy(ctx->scratch->ptr1, (uint8_t *)&wif->flags,
                            sizeof(wif->flags));
                        memcpy((uint8_t *)&wif->flags, val->v.octetstring.octets,
                            sizeof(wif->flags));
                        return (SNMP_ERR_NOERROR);

                case LEAF_wlanIfaceBssid:
                        if (val->v.octetstring.len != IEEE80211_ADDR_LEN)
                                return (SNMP_ERR_INCONS_VALUE);
                        ctx->scratch->ptr1 = malloc(IEEE80211_ADDR_LEN);
                        if (ctx->scratch->ptr1 == NULL)
                                return (SNMP_ERR_GENERR);
                        memcpy(ctx->scratch->ptr1, wif->dbssid,
                            IEEE80211_ADDR_LEN);
                        memcpy(wif->dbssid, val->v.octetstring.octets,
                            IEEE80211_ADDR_LEN);
                        return (SNMP_ERR_NOERROR);

                case LEAF_wlanIfaceLocalAddress:
                        if (val->v.octetstring.len != IEEE80211_ADDR_LEN)
                                return (SNMP_ERR_INCONS_VALUE);
                        ctx->scratch->ptr1 = malloc(IEEE80211_ADDR_LEN);
                        if (ctx->scratch->ptr1 == NULL)
                                return (SNMP_ERR_GENERR);
                        memcpy(ctx->scratch->ptr1, wif->dlmac,
                            IEEE80211_ADDR_LEN);
                        memcpy(wif->dlmac, val->v.octetstring.octets,
                            IEEE80211_ADDR_LEN);
                        return (SNMP_ERR_NOERROR);

                case LEAF_wlanIfaceStatus:
                        ctx->scratch->int1 = wif->status;
                        wif->status = val->v.integer;
                        if (wif->status == RowStatus_active) {
                                rc = wlan_iface_create(wif); /* XXX */
                                if (rc != SNMP_ERR_NOERROR) {
                                        wif->status = ctx->scratch->int1;
                                        return (rc);
                                }
                        } else if (wif->status == RowStatus_destroy)
                                return (wlan_iface_destroy(wif));
                        else
                                wif->status = RowStatus_notReady;
                        return (SNMP_ERR_NOERROR);

                case LEAF_wlanIfaceState:
                        ctx->scratch->int1 = wif->state;
                        wif->state = val->v.integer;
                        if (wif->status == RowStatus_active)
                                if (wlan_config_state(wif, 1) < 0)
                                        return (SNMP_ERR_GENERR);
                        return (SNMP_ERR_NOERROR);
                }
                abort();

        case SNMP_OP_ROLLBACK:
                if ((wif = wlan_get_snmp_interface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                switch (val->var.subs[sub - 1]) {
                case LEAF_wlanIfaceName:
                        strlcpy(wif->wname, ctx->scratch->ptr1, IFNAMSIZ);
                        free(ctx->scratch->ptr1);
                        break;

                case LEAF_wlanParentIfName:
                        strlcpy(wif->pname, ctx->scratch->ptr1, IFNAMSIZ);
                        free(ctx->scratch->ptr1);
                        break;

                case LEAF_wlanIfaceOperatingMode:
                        wif->mode = ctx->scratch->int1;
                        break;

                case LEAF_wlanIfaceFlags:
                        memcpy((uint8_t *)&wif->flags, ctx->scratch->ptr1,
                            sizeof(wif->flags));
                        free(ctx->scratch->ptr1);
                        break;

                case LEAF_wlanIfaceBssid:
                        memcpy(wif->dbssid, ctx->scratch->ptr1,
                            IEEE80211_ADDR_LEN);
                        free(ctx->scratch->ptr1);
                        break;

                case LEAF_wlanIfaceLocalAddress:
                        memcpy(wif->dlmac, ctx->scratch->ptr1,
                            IEEE80211_ADDR_LEN);
                        free(ctx->scratch->ptr1);
                        break;

                case LEAF_wlanIfaceStatus:
                        wif->status = ctx->scratch->int1;
                        if (ctx->scratch->int1 == RowStatus_active)
                                return (SNMP_ERR_GENERR); /* XXX: FIXME */
                        else if (wif->internal != 0)
                                return (wlan_iface_destroy(wif));
                        break;

                case LEAF_wlanIfaceState:
                        wif->state = ctx->scratch->int1;
                        if (wif->status == RowStatus_active)
                                if (wlan_config_state(wif, 1) < 0)
                                        return (SNMP_ERR_GENERR);
                        break;
                }
                return (SNMP_ERR_NOERROR);

        case SNMP_OP_COMMIT:
                switch (val->var.subs[sub - 1]) {
                case LEAF_wlanIfaceName:
                case LEAF_wlanParentIfName:
                case LEAF_wlanIfaceFlags:
                case LEAF_wlanIfaceBssid:
                case LEAF_wlanIfaceLocalAddress:
                        free(ctx->scratch->ptr1);
                        /* FALLTHROUGH */
                default:
                        return (SNMP_ERR_NOERROR);
                }
        default:
                abort();
        }

        switch (val->var.subs[sub - 1]) {
        case LEAF_wlanIfaceIndex:
                val->v.integer = wif->index;
                return (SNMP_ERR_NOERROR);
        case LEAF_wlanIfaceName:
                return (string_get(val, wif->wname, -1));
        case LEAF_wlanParentIfName:
                return (string_get(val, wif->pname, -1));
        case LEAF_wlanIfaceOperatingMode:
                val->v.integer = wif->mode;
                return (SNMP_ERR_NOERROR);
        case LEAF_wlanIfaceFlags:
                return (bits_get(val, (uint8_t *)&wif->flags,
                    sizeof(wif->flags)));
        case LEAF_wlanIfaceBssid:
                return (string_get(val, wif->dbssid, IEEE80211_ADDR_LEN));
        case LEAF_wlanIfaceLocalAddress:
                return (string_get(val, wif->dlmac, IEEE80211_ADDR_LEN));
        case LEAF_wlanIfaceStatus:
                val->v.integer = wif->status;
                return (SNMP_ERR_NOERROR);
        case LEAF_wlanIfaceState:
                val->v.integer = wif->state;
                return (SNMP_ERR_NOERROR);
        }

        abort();
}

int
op_wlan_if_parent(struct snmp_context *ctx __unused, struct snmp_value *val,
    uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
{
        struct wlan_iface *wif;

        wlan_update_interface_list();

        switch (op) {
        case SNMP_OP_GET:
                if ((wif = wlan_get_interface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                break;
        case SNMP_OP_GETNEXT:
                if ((wif = wlan_get_next_interface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                wlan_append_ifindex(&val->var, sub, wif);
                break;
        case SNMP_OP_SET:
                return (SNMP_ERR_NOT_WRITEABLE);
        case SNMP_OP_COMMIT:
                /* FALLTHROUGH */
        case SNMP_OP_ROLLBACK:
                /* FALLTHROUGH */
        default:
                abort();
        }

        switch (val->var.subs[sub - 1]) {
        case LEAF_wlanIfParentDriverCapabilities:
                return (bits_get(val, (uint8_t *)&wif->drivercaps,
                    sizeof(wif->drivercaps)));
        case LEAF_wlanIfParentCryptoCapabilities:
                return (bits_get(val, (uint8_t *)&wif->cryptocaps,
                    sizeof(wif->cryptocaps)));
        case LEAF_wlanIfParentHTCapabilities:
                return (bits_get(val, (uint8_t *)&wif->htcaps,
                    sizeof(wif->htcaps)));
        }

        abort();
}

int
op_wlan_iface_config(struct snmp_context *ctx, struct snmp_value *val,
    uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
{
        int intval, vlen, rc;
        char *strval;
        struct wlan_iface *wif;

        wlan_update_interface_list();

        switch (op) {
        case SNMP_OP_GET:
                if ((wif = wlan_get_interface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                goto get_config;

        case SNMP_OP_GETNEXT:
                if ((wif = wlan_get_next_interface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                wlan_append_ifindex(&val->var, sub, wif);
                goto get_config;

        case SNMP_OP_SET:
                if ((wif = wlan_get_interface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);

                intval = val->v.integer;
                strval = NULL;
                vlen = 0;

                /* Simple sanity checks & save old data. */
                switch (val->var.subs[sub - 1]) {
                case LEAF_wlanIfaceCountryCode:
                        if (val->v.octetstring.len != WLAN_COUNTRY_CODE_SIZE)
                                return (SNMP_ERR_INCONS_VALUE);
                        break;
                case LEAF_wlanIfaceDesiredSsid:
                        if (val->v.octetstring.len > IEEE80211_NWID_LEN)
                                return (SNMP_ERR_INCONS_VALUE);
                        break;
                case LEAF_wlanIfaceDesiredBssid:
                        if (val->v.octetstring.len != IEEE80211_ADDR_LEN)
                                return (SNMP_ERR_INCONS_VALUE);
                        break;
                case LEAF_wlanIfacePacketBurst:
                        ctx->scratch->int1 = wif->packet_burst;
                        break;
                case LEAF_wlanIfaceRegDomain:
                        ctx->scratch->int1 = wif->reg_domain;
                        break;
                case LEAF_wlanIfaceDesiredChannel:
                        ctx->scratch->int1 = wif->desired_channel;
                        break;
                case LEAF_wlanIfaceDynamicFreqSelection:
                        ctx->scratch->int1 = wif->dyn_frequency;
                        break;
                case LEAF_wlanIfaceFastFrames:
                        ctx->scratch->int1 = wif->fast_frames;
                        break;
                case LEAF_wlanIfaceDturbo:
                        ctx->scratch->int1 = wif->dturbo;
                        break;
                case LEAF_wlanIfaceTxPower:
                        ctx->scratch->int1 = wif->tx_power;
                        break;
                case LEAF_wlanIfaceFragmentThreshold:
                        ctx->scratch->int1 = wif->frag_threshold;
                        break;
                case LEAF_wlanIfaceRTSThreshold:
                        ctx->scratch->int1 = wif->rts_threshold;
                        break;
                case LEAF_wlanIfaceWlanPrivacySubscribe:
                        ctx->scratch->int1 = wif->priv_subscribe;
                        break;
                case LEAF_wlanIfaceBgScan:
                        ctx->scratch->int1 = wif->bg_scan;
                        break;
                case LEAF_wlanIfaceBgScanIdle:
                        ctx->scratch->int1 = wif->bg_scan_idle;
                        break;
                case LEAF_wlanIfaceBgScanInterval:
                        ctx->scratch->int1 = wif->bg_scan_interval;
                        break;
                case LEAF_wlanIfaceBeaconMissedThreshold:
                        ctx->scratch->int1 = wif->beacons_missed;
                        break;
                case LEAF_wlanIfaceRoamingMode:
                        ctx->scratch->int1 = wif->roam_mode;
                        break;
                case LEAF_wlanIfaceDot11d:
                        ctx->scratch->int1 = wif->dot11d;
                        break;
                case LEAF_wlanIfaceDot11h:
                        ctx->scratch->int1 = wif->dot11h;
                        break;
                case LEAF_wlanIfaceDynamicWds:
                        ctx->scratch->int1 = wif->dynamic_wds;
                        break;
                case LEAF_wlanIfacePowerSave:
                        ctx->scratch->int1 = wif->power_save;
                        break;
                case LEAF_wlanIfaceApBridge:
                        ctx->scratch->int1 = wif->ap_bridge;
                        break;
                case LEAF_wlanIfaceBeaconInterval:
                        ctx->scratch->int1 = wif->beacon_interval;
                        break;
                case LEAF_wlanIfaceDtimPeriod:
                        ctx->scratch->int1 = wif->dtim_period;
                        break;
                case LEAF_wlanIfaceHideSsid:
                        ctx->scratch->int1 = wif->hide_ssid;
                        break;
                case LEAF_wlanIfaceInactivityProccess:
                        ctx->scratch->int1 = wif->inact_process;
                        break;
                case LEAF_wlanIfaceDot11gProtMode:
                        ctx->scratch->int1 = wif->do11g_protect;
                        break;
                case LEAF_wlanIfaceDot11gPureMode:
                        ctx->scratch->int1 = wif->dot11g_pure;
                        break;
                case LEAF_wlanIfaceDot11nPureMode:
                        ctx->scratch->int1 = wif->dot11n_pure;
                        break;
                case LEAF_wlanIfaceDot11nAmpdu:
                        ctx->scratch->int1 = wif->ampdu;
                        break;
                case LEAF_wlanIfaceDot11nAmpduDensity:
                        ctx->scratch->int1 = wif->ampdu_density;
                        break;
                case LEAF_wlanIfaceDot11nAmpduLimit:
                        ctx->scratch->int1 = wif->ampdu_limit;
                        break;
                case LEAF_wlanIfaceDot11nAmsdu:
                        ctx->scratch->int1 = wif->amsdu;
                        break;
                case LEAF_wlanIfaceDot11nAmsduLimit:
                        ctx->scratch->int1 = wif->amsdu_limit;
                        break;
                case LEAF_wlanIfaceDot11nHighThroughput:
                        ctx->scratch->int1 = wif->ht_enabled;
                        break;
                case LEAF_wlanIfaceDot11nHTCompatible:
                        ctx->scratch->int1 = wif->ht_compatible;
                        break;
                case LEAF_wlanIfaceDot11nHTProtMode:
                        ctx->scratch->int1 = wif->ht_prot_mode;
                        break;
                case LEAF_wlanIfaceDot11nRIFS:
                        ctx->scratch->int1 = wif->rifs;
                        break;
                case LEAF_wlanIfaceDot11nShortGI:
                        ctx->scratch->int1 = wif->short_gi;
                        break;
                case LEAF_wlanIfaceDot11nSMPSMode:
                        ctx->scratch->int1 = wif->smps_mode;
                        break;
                case LEAF_wlanIfaceTdmaSlot:
                        ctx->scratch->int1 = wif->tdma_slot;
                        break;
                case LEAF_wlanIfaceTdmaSlotCount:
                        ctx->scratch->int1 = wif->tdma_slot_count;
                        break;
                case LEAF_wlanIfaceTdmaSlotLength:
                        ctx->scratch->int1 = wif->tdma_slot_length;
                        break;
                case LEAF_wlanIfaceTdmaBeaconInterval:
                        ctx->scratch->int1 = wif->tdma_binterval;
                        break;
                default:
                        abort();
                }

                if (val->syntax != SNMP_SYNTAX_OCTETSTRING)
                        goto set_config;

                ctx->scratch->int1 = val->v.octetstring.len;
                ctx->scratch->ptr1 = malloc(val->v.octetstring.len + 1);
                if (ctx->scratch->ptr1 == NULL)
                        return (SNMP_ERR_GENERR); /* XXX */
                if (val->var.subs[sub - 1] == LEAF_wlanIfaceDesiredSsid)
                        strlcpy(ctx->scratch->ptr1, val->v.octetstring.octets,
                            val->v.octetstring.len + 1);
                else
                        memcpy(ctx->scratch->ptr1, val->v.octetstring.octets,
                            val->v.octetstring.len);
                strval = val->v.octetstring.octets;
                vlen = val->v.octetstring.len;
                goto set_config;

        case SNMP_OP_ROLLBACK:
                intval = ctx->scratch->int1;
                strval = NULL;
                vlen = 0;

                if ((wif = wlan_get_interface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                switch (val->var.subs[sub - 1]) {
                case LEAF_wlanIfaceCountryCode:
                case LEAF_wlanIfaceDesiredSsid:
                case LEAF_wlanIfaceDesiredBssid:
                        strval = ctx->scratch->ptr1;
                        vlen = ctx->scratch->int1;
                        break;
                default:
                        break;
                }
                goto set_config;

        case SNMP_OP_COMMIT:
                switch (val->var.subs[sub - 1]) {
                case LEAF_wlanIfaceCountryCode:
                case LEAF_wlanIfaceDesiredSsid:
                case LEAF_wlanIfaceDesiredBssid:
                        free(ctx->scratch->ptr1);
                        /* FALLTHROUGH */
                default:
                        return (SNMP_ERR_NOERROR);
                }
        }
        abort();

get_config:

        if (wlan_config_get_ioctl(wif, val->var.subs[sub - 1]) < 0)
                return (SNMP_ERR_GENERR);

        switch (val->var.subs[sub - 1]) {
        case LEAF_wlanIfacePacketBurst:
                val->v.integer = wif->packet_burst;
                break;
        case LEAF_wlanIfaceCountryCode:
                return (string_get(val, wif->country_code,
                    WLAN_COUNTRY_CODE_SIZE));
        case LEAF_wlanIfaceRegDomain:
                val->v.integer = wif->reg_domain;
                break;
        case LEAF_wlanIfaceDesiredSsid:
                return (string_get(val, wif->desired_ssid, -1));
        case LEAF_wlanIfaceDesiredChannel:
                val->v.integer = wif->desired_channel;
                break;
        case LEAF_wlanIfaceDynamicFreqSelection:
                val->v.integer = wif->dyn_frequency;
                break;
        case LEAF_wlanIfaceFastFrames:
                val->v.integer = wif->fast_frames;
                break;
        case LEAF_wlanIfaceDturbo:
                val->v.integer = wif->dturbo;
                break;
        case LEAF_wlanIfaceTxPower:
                val->v.integer = wif->tx_power;
                break;
        case LEAF_wlanIfaceFragmentThreshold:
                val->v.integer = wif->frag_threshold;
                break;
        case LEAF_wlanIfaceRTSThreshold:
                val->v.integer = wif->rts_threshold;
                break;
        case LEAF_wlanIfaceWlanPrivacySubscribe:
                val->v.integer = wif->priv_subscribe;
                break;
        case LEAF_wlanIfaceBgScan:
                val->v.integer = wif->bg_scan;
                break;
        case LEAF_wlanIfaceBgScanIdle:
                val->v.integer = wif->bg_scan_idle;
                break;
        case LEAF_wlanIfaceBgScanInterval:
                val->v.integer = wif->bg_scan_interval;
                break;
        case LEAF_wlanIfaceBeaconMissedThreshold:
                val->v.integer = wif->beacons_missed;
                break;
        case LEAF_wlanIfaceDesiredBssid:
                return (string_get(val, wif->desired_bssid,
                    IEEE80211_ADDR_LEN));
        case LEAF_wlanIfaceRoamingMode:
                val->v.integer = wif->roam_mode;
                break;
        case LEAF_wlanIfaceDot11d:
                val->v.integer = wif->dot11d;
                break;
        case LEAF_wlanIfaceDot11h:
                val->v.integer = wif->dot11h;
                break;
        case LEAF_wlanIfaceDynamicWds:
                val->v.integer = wif->dynamic_wds;
                break;
        case LEAF_wlanIfacePowerSave:
                val->v.integer = wif->power_save;
                break;
        case LEAF_wlanIfaceApBridge:
                val->v.integer = wif->ap_bridge;
                break;
        case LEAF_wlanIfaceBeaconInterval:
                val->v.integer = wif->beacon_interval;
                break;
        case LEAF_wlanIfaceDtimPeriod:
                val->v.integer = wif->dtim_period;
                break;
        case LEAF_wlanIfaceHideSsid:
                val->v.integer = wif->hide_ssid;
                break;
        case LEAF_wlanIfaceInactivityProccess:
                val->v.integer = wif->inact_process;
                break;
        case LEAF_wlanIfaceDot11gProtMode:
                val->v.integer = wif->do11g_protect;
                break;
        case LEAF_wlanIfaceDot11gPureMode:
                val->v.integer = wif->dot11g_pure;
                break;
        case LEAF_wlanIfaceDot11nPureMode:
                val->v.integer = wif->dot11n_pure;
                break;
        case LEAF_wlanIfaceDot11nAmpdu:
                val->v.integer = wif->ampdu;
                break;
        case LEAF_wlanIfaceDot11nAmpduDensity:
                val->v.integer = wif->ampdu_density;
                break;
        case LEAF_wlanIfaceDot11nAmpduLimit:
                val->v.integer = wif->ampdu_limit;
                break;
        case LEAF_wlanIfaceDot11nAmsdu:
                val->v.integer = wif->amsdu;
                break;
        case LEAF_wlanIfaceDot11nAmsduLimit:
                val->v.integer = wif->amsdu_limit;
                break;
        case LEAF_wlanIfaceDot11nHighThroughput:
                val->v.integer = wif->ht_enabled;
                break;
        case LEAF_wlanIfaceDot11nHTCompatible:
                val->v.integer = wif->ht_compatible;
                break;
        case LEAF_wlanIfaceDot11nHTProtMode:
                val->v.integer = wif->ht_prot_mode;
                break;
        case LEAF_wlanIfaceDot11nRIFS:
                val->v.integer = wif->rifs;
                break;
        case LEAF_wlanIfaceDot11nShortGI:
                val->v.integer = wif->short_gi;
                break;
        case LEAF_wlanIfaceDot11nSMPSMode:
                val->v.integer = wif->smps_mode;
                break;
        case LEAF_wlanIfaceTdmaSlot:
                val->v.integer = wif->tdma_slot;
                break;
        case LEAF_wlanIfaceTdmaSlotCount:
                val->v.integer = wif->tdma_slot_count;
                break;
        case LEAF_wlanIfaceTdmaSlotLength:
                val->v.integer = wif->tdma_slot_length;
                break;
        case LEAF_wlanIfaceTdmaBeaconInterval:
                val->v.integer = wif->tdma_binterval;
                break;
        }

        return (SNMP_ERR_NOERROR);

set_config:
        rc = wlan_config_set_ioctl(wif, val->var.subs[sub - 1], intval,
            strval, vlen);

        if (op == SNMP_OP_ROLLBACK) {
                switch (val->var.subs[sub - 1]) {
                case LEAF_wlanIfaceCountryCode:
                case LEAF_wlanIfaceDesiredSsid:
                case LEAF_wlanIfaceDesiredBssid:
                        free(ctx->scratch->ptr1);
                        /* FALLTHROUGH */
                default:
                        break;
                }
        }

        if (rc < 0)
                return (SNMP_ERR_GENERR);

        return (SNMP_ERR_NOERROR);
}

int
op_wlan_if_peer(struct snmp_context *ctx, struct snmp_value *val, uint32_t sub,
    uint32_t iidx __unused, enum snmp_op op)
{
        struct wlan_peer *wip;
        struct wlan_iface *wif;

        wlan_update_interface_list();
        wlan_update_peers();

        switch (op) {
        case SNMP_OP_GET:
                if ((wip = wlan_get_peer(&val->var, sub, &wif)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                break;
        case SNMP_OP_GETNEXT:
                if ((wip = wlan_get_next_peer(&val->var, sub, &wif)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                wlan_append_mac_index(&val->var, sub, wif->wname, wip->pmac);
                break;
        case SNMP_OP_SET:
                if ((wip = wlan_get_peer(&val->var, sub, &wif)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                if (val->var.subs[sub - 1] != LEAF_wlanIfacePeerVlanTag)
                        return (SNMP_ERR_GENERR);
                ctx->scratch->int1 = wip->vlan;
                if (wlan_peer_set_vlan(wif, wip, val->v.integer) < 0)
                        return (SNMP_ERR_GENERR);
                return (SNMP_ERR_NOERROR);
        case SNMP_OP_COMMIT:
                return (SNMP_ERR_NOERROR);
        case SNMP_OP_ROLLBACK:
                if ((wip = wlan_get_peer(&val->var, sub, &wif)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                if (val->var.subs[sub - 1] != LEAF_wlanIfacePeerVlanTag)
                        return (SNMP_ERR_GENERR);
                if (wlan_peer_set_vlan(wif, wip, ctx->scratch->int1) < 0)
                        return (SNMP_ERR_GENERR);
                return (SNMP_ERR_NOERROR);
        default:
                abort();
        }

        switch (val->var.subs[sub - 1]) {
        case LEAF_wlanIfacePeerAddress:
                return (string_get(val, wip->pmac, IEEE80211_ADDR_LEN));
        case LEAF_wlanIfacePeerAssociationId:
                val->v.integer = wip->associd;
                break;
        case LEAF_wlanIfacePeerVlanTag:
                val->v.integer = wip->vlan;
                break;
        case LEAF_wlanIfacePeerFrequency:
                val->v.integer = wip->frequency;
                break;
        case LEAF_wlanIfacePeerCurrentTXRate:
                val->v.integer = wip->txrate;
                break;
        case LEAF_wlanIfacePeerRxSignalStrength:
                val->v.integer = wip->rssi;
                break;
        case LEAF_wlanIfacePeerIdleTimer:
                val->v.integer = wip->idle;
                break;
        case LEAF_wlanIfacePeerTxSequenceNo:
                val->v.integer = wip->txseqs;
                break;
        case LEAF_wlanIfacePeerRxSequenceNo:
                val->v.integer = wip->rxseqs;
                break;
        case LEAF_wlanIfacePeerTxPower:
                val->v.integer = wip->txpower;
                break;
        case LEAF_wlanIfacePeerCapabilities:
                return (bits_get(val, (uint8_t *)&wip->capinfo,
                    sizeof(wip->capinfo)));
        case LEAF_wlanIfacePeerFlags:
                return (bits_get(val, (uint8_t *)&wip->state,
                    sizeof(wip->state)));
        default:
                abort();
        }

        return (SNMP_ERR_NOERROR);
}

int
op_wlan_channels(struct snmp_context *ctx __unused, struct snmp_value *val,
    uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
{
        int32_t bits;
        struct ieee80211_channel *channel;
        struct wlan_iface *wif;

        wlan_update_interface_list();
        wlan_update_channels();

        switch (op) {
        case SNMP_OP_GET:
                if ((channel = wlan_get_channel(&val->var, sub, &wif)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                break;
        case SNMP_OP_GETNEXT:
                channel = wlan_get_next_channel(&val->var, sub, &wif);
                if (channel == NULL || wif == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                wlan_append_channel_index(&val->var, sub, wif, channel);
                break;
        case SNMP_OP_SET:
                return (SNMP_ERR_NOT_WRITEABLE);
        case SNMP_OP_COMMIT:
                /* FALLTHROUGH */
        case SNMP_OP_ROLLBACK:
                /* FALLTHROUGH */
        default:
                abort();
        }

        switch (val->var.subs[sub - 1]) {
        case LEAF_wlanIfaceChannelIeeeId:
                val->v.integer = channel->ic_ieee;
                break;
        case LEAF_wlanIfaceChannelType:
                val->v.integer = wlan_get_channel_type(channel);
                break;
        case LEAF_wlanIfaceChannelFlags:
                bits = wlan_channel_flags_to_snmp(channel->ic_flags);
                return (bits_get(val, (uint8_t *)&bits, sizeof(bits)));
        case LEAF_wlanIfaceChannelFrequency:
                val->v.integer = channel->ic_freq;
                break;
        case LEAF_wlanIfaceChannelMaxRegPower:
                val->v.integer = channel->ic_maxregpower;
                break;
        case LEAF_wlanIfaceChannelMaxTxPower:
                val->v.integer = channel->ic_maxpower;
                break;
        case LEAF_wlanIfaceChannelMinTxPower:
                val->v.integer = channel->ic_minpower;
                break;
        case LEAF_wlanIfaceChannelState:
                bits = wlan_channel_state_to_snmp(channel->ic_state);
                return (bits_get(val, (uint8_t *)&bits, sizeof(bits)));
        case LEAF_wlanIfaceChannelHTExtension:
                val->v.integer = channel->ic_extieee;
                break;
        case LEAF_wlanIfaceChannelMaxAntennaGain:
                val->v.integer = channel->ic_maxantgain;
                break;
        }

        return (SNMP_ERR_NOERROR);
}

int
op_wlan_roam_params(struct snmp_context *ctx __unused, struct snmp_value *val,
    uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
{
        uint32_t phy;
        struct ieee80211_roamparam *rparam;
        struct wlan_iface *wif;

        wlan_update_interface_list();
        wlan_update_roam_params();

        switch (op) {
        case SNMP_OP_GET:
                rparam = wlan_get_roam_param(&val->var, sub, &wif);
                if (rparam == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                break;
        case SNMP_OP_GETNEXT:
                rparam = wlan_get_next_roam_param(&val->var, sub, &wif, &phy);
                if (rparam == NULL || wif == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                wlan_append_phy_index(&val->var, sub, wif->wname, phy);
                break;
        case SNMP_OP_SET:
                return (SNMP_ERR_NOT_WRITEABLE);
        case SNMP_OP_COMMIT:
                /* FALLTHROUGH */
        case SNMP_OP_ROLLBACK:
                /* FALLTHROUGH */
        default:
                abort();
        }

        switch (val->var.subs[sub - 1]) {
        case LEAF_wlanIfRoamRxSignalStrength:
                val->v.integer = rparam->rssi/2;
                break;
        case LEAF_wlanIfRoamTxRateThreshold:
                val->v.integer = rparam->rate/2;
                break;
        default:
                abort();
        }

        return (SNMP_ERR_NOERROR);
}

int
op_wlan_tx_params(struct snmp_context *ctx, struct snmp_value *val,
    uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
{
        uint32_t phy;
        struct ieee80211_txparam *txparam;
        struct wlan_iface *wif;

        wlan_update_interface_list();
        wlan_update_tx_params();

        switch (op) {
        case SNMP_OP_GET:
                txparam = wlan_get_tx_param(&val->var, sub, &wif, &phy);
                if (txparam == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                goto get_txparams;

        case SNMP_OP_GETNEXT:
                txparam = wlan_get_next_tx_param(&val->var, sub, &wif, &phy);
                if (txparam == NULL || wif == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                wlan_append_phy_index(&val->var, sub, wif->wname, phy);
                goto get_txparams;

        case SNMP_OP_SET:
                txparam = wlan_get_tx_param(&val->var, sub, &wif, &phy);
                if (txparam == NULL || wif == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                switch (val->var.subs[sub - 1]) {
                case LEAF_wlanIfTxUnicastRate:
                        ctx->scratch->int1 = txparam->ucastrate;
                        txparam->ucastrate = val->v.integer * 2;
                        break;
                case LEAF_wlanIfTxMcastRate:
                        ctx->scratch->int1 = txparam->mcastrate;
                        txparam->mcastrate = val->v.integer * 2;
                        break;
                case LEAF_wlanIfTxMgmtRate:
                        ctx->scratch->int1 = txparam->mgmtrate;
                        txparam->mgmtrate = val->v.integer * 2;
                        break;
                case LEAF_wlanIfTxMaxRetryCount:
                        ctx->scratch->int1 = txparam->maxretry;
                        txparam->maxretry = val->v.integer;
                        break;
                default:
                        abort();
                }
                if (wlan_set_tx_params(wif, phy) < 0)
                        return (SNMP_ERR_GENERR);
                return (SNMP_ERR_NOERROR);

        case SNMP_OP_COMMIT:
                return (SNMP_ERR_NOERROR);

        case SNMP_OP_ROLLBACK:
                txparam = wlan_get_tx_param(&val->var, sub, &wif, &phy);
                if (txparam == NULL || wif == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                switch (val->var.subs[sub - 1]) {
                case LEAF_wlanIfTxUnicastRate:
                        txparam->ucastrate = ctx->scratch->int1;
                        break;
                case LEAF_wlanIfTxMcastRate:
                        txparam->mcastrate = ctx->scratch->int1;
                        break;
                case LEAF_wlanIfTxMgmtRate:
                        txparam->mgmtrate = ctx->scratch->int1;
                        break;
                case LEAF_wlanIfTxMaxRetryCount:
                        txparam->maxretry = ctx->scratch->int1;
                        break;
                default:
                        abort();
                }
                if (wlan_set_tx_params(wif, phy) < 0)
                        return (SNMP_ERR_GENERR);
                return (SNMP_ERR_NOERROR);
        default:
                abort();
        }

get_txparams:
        switch (val->var.subs[sub - 1]) {
        case LEAF_wlanIfTxUnicastRate:
                val->v.integer = txparam->ucastrate / 2;
                break;
        case LEAF_wlanIfTxMcastRate:
                val->v.integer = txparam->mcastrate / 2;
                break;
        case LEAF_wlanIfTxMgmtRate:
                val->v.integer = txparam->mgmtrate / 2;
                break;
        case LEAF_wlanIfTxMaxRetryCount:
                val->v.integer = txparam->maxretry;
                break;
        default:
                abort();
        }

        return (SNMP_ERR_NOERROR);
}

int
op_wlan_scan_config(struct snmp_context *ctx, struct snmp_value *val,
    uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
{
        struct wlan_iface *wif;

        wlan_update_interface_list();

        switch (op) {
        case SNMP_OP_GET:
                if ((wif = wlan_get_interface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                break;

        case SNMP_OP_GETNEXT:
                if ((wif = wlan_get_next_interface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                wlan_append_ifindex(&val->var, sub, wif);
                break;

        case SNMP_OP_SET:
                if ((wif = wlan_get_interface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                if (wif->scan_status ==  wlanScanConfigStatus_running
                    && val->var.subs[sub - 1] != LEAF_wlanScanConfigStatus)
                        return (SNMP_ERR_INCONS_VALUE);
                switch (val->var.subs[sub - 1]) {
                case LEAF_wlanScanFlags:
                        ctx->scratch->int1 = wif->scan_flags;
                        wif->scan_flags = val->v.integer;
                        break;
                case LEAF_wlanScanDuration:
                        ctx->scratch->int1 = wif->scan_duration;
                        wif->scan_duration = val->v.integer;
                        break;
                case LEAF_wlanScanMinChannelDwellTime:
                        ctx->scratch->int1 = wif->scan_mindwell;
                        wif->scan_mindwell = val->v.integer;
                        break;
                case LEAF_wlanScanMaxChannelDwellTime:
                        ctx->scratch->int1 = wif->scan_maxdwell;
                        wif->scan_maxdwell = val->v.integer;
                        break;
                case LEAF_wlanScanConfigStatus:
                        if (val->v.integer == wlanScanConfigStatus_running ||
                            val->v.integer == wlanScanConfigStatus_cancel) {
                                ctx->scratch->int1 = wif->scan_status;
                                wif->scan_status = val->v.integer;
                                break;
                        }
                        return (SNMP_ERR_INCONS_VALUE);
                }
                return (SNMP_ERR_NOERROR);

        case SNMP_OP_COMMIT:
                if ((wif = wlan_get_interface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                if (val->var.subs[sub - 1] == LEAF_wlanScanConfigStatus)
                        if (wif->scan_status == wlanScanConfigStatus_running)
                                (void)wlan_set_scan_config(wif); /* XXX */
                return (SNMP_ERR_NOERROR);

        case SNMP_OP_ROLLBACK:
                if ((wif = wlan_get_interface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                switch (val->var.subs[sub - 1]) {
                case LEAF_wlanScanFlags:
                        wif->scan_flags = ctx->scratch->int1;
                        break;
                case LEAF_wlanScanDuration:
                        wif->scan_duration = ctx->scratch->int1;
                        break;
                case LEAF_wlanScanMinChannelDwellTime:
                        wif->scan_mindwell = ctx->scratch->int1;
                        break;
                case LEAF_wlanScanMaxChannelDwellTime:
                        wif->scan_maxdwell = ctx->scratch->int1;
                        break;
                case LEAF_wlanScanConfigStatus:
                        wif->scan_status = ctx->scratch->int1;
                        break;
                }
                return (SNMP_ERR_NOERROR);
        default:
                abort();
        }

        switch (val->var.subs[sub - 1]) {
        case LEAF_wlanScanFlags:
                val->v.integer = wif->scan_flags;
                break;
        case LEAF_wlanScanDuration:
                val->v.integer = wif->scan_duration;
                break;
        case LEAF_wlanScanMinChannelDwellTime:
                val->v.integer = wif->scan_mindwell;
                break;
        case LEAF_wlanScanMaxChannelDwellTime:
                val->v.integer = wif->scan_maxdwell;
                break;
        case LEAF_wlanScanConfigStatus:
                val->v.integer = wif->scan_status;
                break;
        }

        return (SNMP_ERR_NOERROR);
}

int
op_wlan_scan_results(struct snmp_context *ctx __unused, struct snmp_value *val,
    uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
{
        struct wlan_scan_result *sr;
        struct wlan_iface *wif;

        wlan_update_interface_list();
        wlan_scan_update_results();

        switch (op) {
        case SNMP_OP_GET:
                if ((sr = wlan_get_scanr(&val->var, sub, &wif)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                break;

        case SNMP_OP_GETNEXT:
                if ((sr = wlan_get_next_scanr(&val->var, sub, &wif)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                wlan_append_scanr_index(&val->var, sub, wif->wname, sr->ssid,
                    sr->bssid);
                break;

        case SNMP_OP_SET:
                return (SNMP_ERR_NOT_WRITEABLE);
        case SNMP_OP_COMMIT:
                /* FALLTHROUGH */
        case SNMP_OP_ROLLBACK:
                /* FALLTHROUGH */
        default:
                abort();
        }

        switch (val->var.subs[sub - 1]) {
        case LEAF_wlanScanResultID:
                return (string_get(val, sr->ssid, -1));
        case LEAF_wlanScanResultBssid:
                return (string_get(val, sr->bssid, IEEE80211_ADDR_LEN));
        case LEAF_wlanScanResultChannel:
                val->v.integer = sr->opchannel; /* XXX */
                break;
        case LEAF_wlanScanResultRate:
                val->v.integer = sr->rssi;
                break;
        case LEAF_wlanScanResultNoise:
                val->v.integer = sr->noise;
                break;
        case LEAF_wlanScanResultBeaconInterval:
                val->v.integer = sr->bintval;
                break;
        case LEAF_wlanScanResultCapabilities:
                return (bits_get(val, &sr->capinfo, sizeof(sr->capinfo)));
        default:
                abort();
        }

        return (SNMP_ERR_NOERROR);
}

int
op_wlan_iface_stats(struct snmp_context *ctx __unused, struct snmp_value *val,
    uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
{
        struct wlan_iface *wif;

        wlan_update_interface_list();

        switch (op) {
        case SNMP_OP_GET:
                if ((wif = wlan_get_interface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                break;
        case SNMP_OP_GETNEXT:
                if ((wif = wlan_get_next_interface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                wlan_append_ifindex(&val->var, sub, wif);
                break;
        case SNMP_OP_SET:
                /* XXX: LEAF_wlanStatsReset */
                return (SNMP_ERR_NOT_WRITEABLE);
        case SNMP_OP_COMMIT:
                /* FALLTHROUGH */
        case SNMP_OP_ROLLBACK:
                /* FALLTHROUGH */
        default:
                abort();
        }

        if (wlan_get_stats(wif) < 0)
                return (SNMP_ERR_GENERR);

        switch (val->var.subs[sub - 1]) {
        case LEAF_wlanStatsRxBadVersion:
                val->v.uint32 = wif->stats.is_rx_badversion;
                break;
        case LEAF_wlanStatsRxTooShort:
                val->v.uint32 = wif->stats.is_rx_tooshort;
                break;
        case LEAF_wlanStatsRxWrongBssid:
                val->v.uint32 = wif->stats.is_rx_wrongbss;
                break;
        case LEAF_wlanStatsRxDiscardedDups:
                val->v.uint32 = wif->stats.is_rx_dup;
                break;
        case LEAF_wlanStatsRxWrongDir:
                val->v.uint32 = wif->stats.is_rx_wrongdir;
                break;
        case LEAF_wlanStatsRxDiscardMcastEcho:
                val->v.uint32 = wif->stats.is_rx_mcastecho;
                break;
        case LEAF_wlanStatsRxDiscardNoAssoc:
                val->v.uint32 = wif->stats.is_rx_notassoc;
                break;
        case LEAF_wlanStatsRxWepNoPrivacy:
                val->v.uint32 = wif->stats.is_rx_noprivacy;
                break;
        case LEAF_wlanStatsRxWepUnencrypted:
                val->v.uint32 = wif->stats.is_rx_unencrypted;
                break;
        case LEAF_wlanStatsRxWepFailed:
                val->v.uint32 = wif->stats.is_rx_wepfail;
                break;
        case LEAF_wlanStatsRxDecapsulationFailed:
                val->v.uint32 = wif->stats.is_rx_decap;
                break;
        case LEAF_wlanStatsRxDiscardMgmt:
                val->v.uint32 = wif->stats.is_rx_mgtdiscard;
                break;
        case LEAF_wlanStatsRxControl:
                val->v.uint32 = wif->stats.is_rx_ctl;
                break;
        case LEAF_wlanStatsRxBeacon:
                val->v.uint32 = wif->stats.is_rx_beacon;
                break;
        case LEAF_wlanStatsRxRateSetTooBig:
                val->v.uint32 = wif->stats.is_rx_rstoobig;
                break;
        case LEAF_wlanStatsRxElemMissing:
                val->v.uint32 = wif->stats.is_rx_elem_missing;
                break;
        case LEAF_wlanStatsRxElemTooBig:
                val->v.uint32 = wif->stats.is_rx_elem_toobig;
                break;
        case LEAF_wlanStatsRxElemTooSmall:
                val->v.uint32 = wif->stats.is_rx_elem_toosmall;
                break;
        case LEAF_wlanStatsRxElemUnknown:
                val->v.uint32 = wif->stats.is_rx_elem_unknown;
                break;
        case LEAF_wlanStatsRxChannelMismatch:
                val->v.uint32 = wif->stats.is_rx_chanmismatch;
                break;
        case LEAF_wlanStatsRxDropped:
                val->v.uint32 = wif->stats.is_rx_nodealloc;
                break;
        case LEAF_wlanStatsRxSsidMismatch:
                val->v.uint32 = wif->stats.is_rx_ssidmismatch;
                break;
        case LEAF_wlanStatsRxAuthNotSupported:
                val->v.uint32 = wif->stats.is_rx_auth_unsupported;
                break;
        case LEAF_wlanStatsRxAuthFailed:
                val->v.uint32 = wif->stats.is_rx_auth_fail;
                break;
        case LEAF_wlanStatsRxAuthCM:
                val->v.uint32 = wif->stats.is_rx_auth_countermeasures;
                break;
        case LEAF_wlanStatsRxAssocWrongBssid:
                val->v.uint32 = wif->stats.is_rx_assoc_bss;
                break;
        case LEAF_wlanStatsRxAssocNoAuth:
                val->v.uint32 = wif->stats.is_rx_assoc_notauth;
                break;
        case LEAF_wlanStatsRxAssocCapMismatch:
                val->v.uint32 = wif->stats.is_rx_assoc_capmismatch;
                break;
        case LEAF_wlanStatsRxAssocNoRateMatch:
                val->v.uint32 = wif->stats.is_rx_assoc_norate;
                break;
        case LEAF_wlanStatsRxBadWpaIE:
                val->v.uint32 = wif->stats.is_rx_assoc_badwpaie;
                break;
        case LEAF_wlanStatsRxDeauthenticate:
                val->v.uint32 = wif->stats.is_rx_deauth;
                break;
        case LEAF_wlanStatsRxDisassociate:
                val->v.uint32 = wif->stats.is_rx_disassoc;
                break;
        case LEAF_wlanStatsRxUnknownSubtype:
                val->v.uint32 = wif->stats.is_rx_badsubtype;
                break;
        case LEAF_wlanStatsRxFailedNoBuf:
                val->v.uint32 = wif->stats.is_rx_nobuf;
                break;
        case LEAF_wlanStatsRxBadAuthRequest:
                val->v.uint32 = wif->stats.is_rx_bad_auth;
                break;
        case LEAF_wlanStatsRxUnAuthorized:
                val->v.uint32 = wif->stats.is_rx_unauth;
                break;
        case LEAF_wlanStatsRxBadKeyId:
                val->v.uint32 = wif->stats.is_rx_badkeyid;
                break;
        case LEAF_wlanStatsRxCCMPSeqViolation:
                val->v.uint32 = wif->stats.is_rx_ccmpreplay;
                break;
        case LEAF_wlanStatsRxCCMPBadFormat:
                val->v.uint32 = wif->stats.is_rx_ccmpformat;
                break;
        case LEAF_wlanStatsRxCCMPFailedMIC:
                val->v.uint32 = wif->stats.is_rx_ccmpmic;
                break;
        case LEAF_wlanStatsRxTKIPSeqViolation:
                val->v.uint32 = wif->stats.is_rx_tkipreplay;
                break;
        case LEAF_wlanStatsRxTKIPBadFormat:
                val->v.uint32 = wif->stats.is_rx_tkipformat;
                break;
        case LEAF_wlanStatsRxTKIPFailedMIC:
                val->v.uint32 = wif->stats.is_rx_tkipmic;
                break;
        case LEAF_wlanStatsRxTKIPFailedICV:
                val->v.uint32 = wif->stats.is_rx_tkipicv;
                break;
        case LEAF_wlanStatsRxDiscardACL:
                val->v.uint32 = wif->stats.is_rx_acl;
                break;
        case LEAF_wlanStatsTxFailedNoBuf:
                val->v.uint32 = wif->stats.is_tx_nobuf;
                break;
        case LEAF_wlanStatsTxFailedNoNode:
                val->v.uint32 = wif->stats.is_tx_nonode;
                break;
        case LEAF_wlanStatsTxUnknownMgmt:
                val->v.uint32 = wif->stats.is_tx_unknownmgt;
                break;
        case LEAF_wlanStatsTxBadCipher:
                val->v.uint32 = wif->stats.is_tx_badcipher;
                break;
        case LEAF_wlanStatsTxNoDefKey:
                val->v.uint32 = wif->stats.is_tx_nodefkey;
                break;
        case LEAF_wlanStatsTxFragmented:
                val->v.uint32 = wif->stats.is_tx_fragframes;
                break;
        case LEAF_wlanStatsTxFragmentsCreated:
                val->v.uint32 = wif->stats.is_tx_frags;
                break;
        case LEAF_wlanStatsActiveScans:
                val->v.uint32 = wif->stats.is_scan_active;
                break;
        case LEAF_wlanStatsPassiveScans:
                val->v.uint32 = wif->stats.is_scan_passive;
                break;
        case LEAF_wlanStatsTimeoutInactivity:
                val->v.uint32 = wif->stats.is_node_timeout;
                break;
        case LEAF_wlanStatsCryptoNoMem:
                val->v.uint32 = wif->stats.is_crypto_nomem;
                break;
        case LEAF_wlanStatsSwCryptoTKIP:
                val->v.uint32 = wif->stats.is_crypto_tkip;
                break;
        case LEAF_wlanStatsSwCryptoTKIPEnMIC:
                val->v.uint32 = wif->stats.is_crypto_tkipenmic;
                break;
        case LEAF_wlanStatsSwCryptoTKIPDeMIC:
                val->v.uint32 = wif->stats.is_crypto_tkipdemic;
                break;
        case LEAF_wlanStatsCryptoTKIPCM:
                val->v.uint32 = wif->stats.is_crypto_tkipcm;
                break;
        case LEAF_wlanStatsSwCryptoCCMP:
                val->v.uint32 = wif->stats.is_crypto_ccmp;
                break;
        case LEAF_wlanStatsSwCryptoWEP:
                val->v.uint32 = wif->stats.is_crypto_wep;
                break;
        case LEAF_wlanStatsCryptoCipherKeyRejected:
                val->v.uint32 = wif->stats.is_crypto_setkey_cipher;
                break;
        case LEAF_wlanStatsCryptoNoKey:
                val->v.uint32 = wif->stats.is_crypto_setkey_nokey;
                break;
        case LEAF_wlanStatsCryptoDeleteKeyFailed:
                val->v.uint32 = wif->stats.is_crypto_delkey;
                break;
        case LEAF_wlanStatsCryptoUnknownCipher:
                val->v.uint32 = wif->stats.is_crypto_badcipher;
                break;
        case LEAF_wlanStatsCryptoAttachFailed:
                val->v.uint32 = wif->stats.is_crypto_attachfail;
                break;
        case LEAF_wlanStatsCryptoKeyFailed:
                val->v.uint32 = wif->stats.is_crypto_keyfail;
                break;
        case LEAF_wlanStatsCryptoEnMICFailed:
                val->v.uint32 = wif->stats.is_crypto_enmicfail;
                break;
        case LEAF_wlanStatsIBSSCapMismatch:
                val->v.uint32 = wif->stats.is_ibss_capmismatch;
                break;
        case LEAF_wlanStatsUnassocStaPSPoll:
                val->v.uint32 = wif->stats.is_ps_unassoc;
                break;
        case LEAF_wlanStatsBadAidPSPoll:
                val->v.uint32 = wif->stats.is_ps_badaid;
                break;
        case LEAF_wlanStatsEmptyPSPoll:
                val->v.uint32 = wif->stats.is_ps_qempty;
                break;
        case LEAF_wlanStatsRxFFBadHdr:
                val->v.uint32 = wif->stats.is_ff_badhdr;
                break;
        case LEAF_wlanStatsRxFFTooShort:
                val->v.uint32 = wif->stats.is_ff_tooshort;
                break;
        case LEAF_wlanStatsRxFFSplitError:
                val->v.uint32 = wif->stats.is_ff_split;
                break;
        case LEAF_wlanStatsRxFFDecap:
                val->v.uint32 = wif->stats.is_ff_decap;
                break;
        case LEAF_wlanStatsTxFFEncap:
                val->v.uint32 = wif->stats.is_ff_encap;
                break;
        case LEAF_wlanStatsRxBadBintval:
                val->v.uint32 = wif->stats.is_rx_badbintval;
                break;
        case LEAF_wlanStatsRxDemicFailed:
                val->v.uint32 = wif->stats.is_rx_demicfail;
                break;
        case LEAF_wlanStatsRxDefragFailed:
                val->v.uint32 = wif->stats.is_rx_defrag;
                break;
        case LEAF_wlanStatsRxMgmt:
                val->v.uint32 = wif->stats.is_rx_mgmt;
                break;
        case LEAF_wlanStatsRxActionMgmt:
                val->v.uint32 = wif->stats.is_rx_action;
                break;
        case LEAF_wlanStatsRxAMSDUTooShort:
                val->v.uint32 = wif->stats.is_amsdu_tooshort;
                break;
        case LEAF_wlanStatsRxAMSDUSplitError:
                val->v.uint32 = wif->stats.is_amsdu_split;
                break;
        case LEAF_wlanStatsRxAMSDUDecap:
                val->v.uint32 = wif->stats.is_amsdu_decap;
                break;
        case LEAF_wlanStatsTxAMSDUEncap:
                val->v.uint32 = wif->stats.is_amsdu_encap;
                break;
        case LEAF_wlanStatsAMPDUBadBAR:
                val->v.uint32 = wif->stats.is_ampdu_bar_bad;
                break;
        case LEAF_wlanStatsAMPDUOowBar:
                val->v.uint32 = wif->stats.is_ampdu_bar_oow;
                break;
        case LEAF_wlanStatsAMPDUMovedBAR:
                val->v.uint32 = wif->stats.is_ampdu_bar_move;
                break;
        case LEAF_wlanStatsAMPDURxBAR:
                val->v.uint32 = wif->stats.is_ampdu_bar_rx;
                break;
        case LEAF_wlanStatsAMPDURxOor:
                val->v.uint32 = wif->stats.is_ampdu_rx_oor;
                break;
        case LEAF_wlanStatsAMPDURxCopied:
                val->v.uint32 = wif->stats.is_ampdu_rx_copy;
                break;
        case LEAF_wlanStatsAMPDURxDropped:
                val->v.uint32 = wif->stats.is_ampdu_rx_drop;
                break;
        case LEAF_wlanStatsTxDiscardBadState:
                val->v.uint32 = wif->stats.is_tx_badstate;
                break;
        case LEAF_wlanStatsTxFailedNoAssoc:
                val->v.uint32 = wif->stats.is_tx_notassoc;
                break;
        case LEAF_wlanStatsTxClassifyFailed:
                val->v.uint32 = wif->stats.is_tx_classify;
                break;
        case LEAF_wlanStatsDwdsMcastDiscard:
                val->v.uint32 = wif->stats.is_dwds_mcast;
                break;
        case LEAF_wlanStatsHTAssocRejectNoHT:
                val->v.uint32 = wif->stats.is_ht_assoc_nohtcap;
                break;
        case LEAF_wlanStatsHTAssocDowngrade:
                val->v.uint32 = wif->stats.is_ht_assoc_downgrade;
                break;
        case LEAF_wlanStatsHTAssocRateMismatch:
                val->v.uint32 = wif->stats.is_ht_assoc_norate;
                break;
        case LEAF_wlanStatsAMPDURxAge:
                val->v.uint32 = wif->stats.is_ampdu_rx_age;
                break;
        case LEAF_wlanStatsAMPDUMoved:
                val->v.uint32 = wif->stats.is_ampdu_rx_move;
                break;
        case LEAF_wlanStatsADDBADisabledReject:
                val->v.uint32 = wif->stats.is_addba_reject;
                break;
        case LEAF_wlanStatsADDBANoRequest:
                val->v.uint32 = wif->stats.is_addba_norequest;
                break;
        case LEAF_wlanStatsADDBABadToken:
                val->v.uint32 = wif->stats.is_addba_badtoken;
                break;
        case LEAF_wlanStatsADDBABadPolicy:
                val->v.uint32 = wif->stats.is_addba_badpolicy;
                break;
        case LEAF_wlanStatsAMPDUStopped:
                val->v.uint32 = wif->stats.is_ampdu_stop;
                break;
        case LEAF_wlanStatsAMPDUStopFailed:
                val->v.uint32 = wif->stats.is_ampdu_stop_failed;
                break;
        case LEAF_wlanStatsAMPDURxReorder:
                val->v.uint32 = wif->stats.is_ampdu_rx_reorder;
                break;
        case LEAF_wlanStatsScansBackground:
                val->v.uint32 = wif->stats.is_scan_bg;
                break;
        case LEAF_wlanLastDeauthReason:
                val->v.uint32 = wif->stats.is_rx_deauth_code;
                break;
        case LEAF_wlanLastDissasocReason:
                val->v.uint32 = wif->stats.is_rx_disassoc_code;
                break;
        case LEAF_wlanLastAuthFailReason:
                val->v.uint32 = wif->stats.is_rx_authfail_code;
                break;
        case LEAF_wlanStatsBeaconMissedEvents:
                val->v.uint32 = wif->stats.is_beacon_miss;
                break;
        case LEAF_wlanStatsRxDiscardBadStates:
                val->v.uint32 = wif->stats.is_rx_badstate;
                break;
        case LEAF_wlanStatsFFFlushed:
                val->v.uint32 = wif->stats.is_ff_flush;
                break;
        case LEAF_wlanStatsTxControlFrames:
                val->v.uint32 = wif->stats.is_tx_ctl;
                break;
        case LEAF_wlanStatsAMPDURexmt:
                val->v.uint32 = wif->stats.is_ampdu_rexmt;
                break;
        case LEAF_wlanStatsAMPDURexmtFailed:
                val->v.uint32 = wif->stats.is_ampdu_rexmt_fail;
                break;
        case LEAF_wlanStatsReset:
                val->v.uint32 = wlanStatsReset_no_op;
                break;
        default:
                abort();
        }

        return (SNMP_ERR_NOERROR);
}

int
op_wlan_wep_iface(struct snmp_context *ctx, struct snmp_value *val,
    uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
{
        struct wlan_iface *wif;

        wlan_update_interface_list();

        switch (op) {
        case SNMP_OP_GET:
                if ((wif = wlan_get_interface(&val->var, sub)) == NULL ||
                    !wif->wepsupported)
                        return (SNMP_ERR_NOSUCHNAME);
                break;

        case SNMP_OP_GETNEXT:
                /* XXX: filter wif->wepsupported */
                if ((wif = wlan_get_next_interface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                wlan_append_ifindex(&val->var, sub, wif);
                break;

        case SNMP_OP_SET:
                if ((wif = wlan_get_interface(&val->var, sub)) == NULL ||
                    !wif->wepsupported)
                        return (SNMP_ERR_NOSUCHNAME);
                switch (val->var.subs[sub - 1]) {
                case LEAF_wlanWepMode:
                        if (val->v.integer < wlanWepMode_off ||
                            val->v.integer > wlanWepMode_mixed)
                                return (SNMP_ERR_INCONS_VALUE);
                        ctx->scratch->int1 = wif->wepmode;
                        wif->wepmode = val->v.integer;
                        if (wlan_set_wepmode(wif) < 0) {
                                wif->wepmode = ctx->scratch->int1;
                                return (SNMP_ERR_GENERR);
                        }
                        break;
                case LEAF_wlanWepDefTxKey:
                        if (val->v.integer < 0 ||
                            val->v.integer > IEEE80211_WEP_NKID)
                                return (SNMP_ERR_INCONS_VALUE);
                        ctx->scratch->int1 = wif->weptxkey;
                        wif->weptxkey = val->v.integer;
                        if (wlan_set_weptxkey(wif) < 0) {
                                wif->weptxkey = ctx->scratch->int1;
                                return (SNMP_ERR_GENERR);
                        }
                        break;
                default:
                        abort();
                }
                return (SNMP_ERR_NOERROR);

        case SNMP_OP_COMMIT:
                return (SNMP_ERR_NOERROR);

        case SNMP_OP_ROLLBACK:
                if ((wif = wlan_get_interface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                switch (val->var.subs[sub - 1]) {
                case LEAF_wlanWepMode:
                        wif->wepmode = ctx->scratch->int1;
                        if (wlan_set_wepmode(wif) < 0)
                                return (SNMP_ERR_GENERR);
                        break;
                case LEAF_wlanWepDefTxKey:
                        wif->weptxkey = ctx->scratch->int1;
                        if (wlan_set_weptxkey(wif) < 0)
                                return (SNMP_ERR_GENERR);
                        break;
                default:
                        abort();
                }
                return (SNMP_ERR_NOERROR);

        default:
                abort();
        }

        switch (val->var.subs[sub - 1]) {
        case LEAF_wlanWepMode:
                if (wlan_get_wepmode(wif) < 0)
                        return (SNMP_ERR_GENERR);
                val->v.integer = wif->wepmode;
                break;
        case LEAF_wlanWepDefTxKey:
                if (wlan_get_weptxkey(wif) < 0)
                        return (SNMP_ERR_GENERR);
                val->v.integer = wif->weptxkey;
                break;
        default:
                abort();
        }

        return (SNMP_ERR_NOERROR);
}

int
op_wlan_wep_key(struct snmp_context *ctx __unused,
    struct snmp_value *val __unused, uint32_t sub __unused,
    uint32_t iidx __unused, enum snmp_op op __unused)
{
        return (SNMP_ERR_NOSUCHNAME);
}

int
op_wlan_mac_access_control(struct snmp_context *ctx, struct snmp_value *val,
    uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
{
        struct wlan_iface *wif;

        wlan_update_interface_list();

        switch (op) {
        case SNMP_OP_GET:
                if ((wif = wlan_get_interface(&val->var, sub)) == NULL ||
                    !wif->macsupported)
                        return (SNMP_ERR_NOSUCHNAME);
                break;

        case SNMP_OP_GETNEXT:
                /* XXX: filter wif->macsupported */
                if ((wif = wlan_get_next_interface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                wlan_append_ifindex(&val->var, sub, wif);
                break;

        case SNMP_OP_SET:
                if ((wif = wlan_get_interface(&val->var, sub)) == NULL ||
                    !wif->macsupported)
                        return (SNMP_ERR_NOSUCHNAME);
                switch (val->var.subs[sub - 1]) {
                case LEAF_wlanMACAccessControlPolicy:
                        ctx->scratch->int1 = wif->mac_policy;
                        wif->mac_policy = val->v.integer;
                        break;
                case LEAF_wlanMACAccessControlNacl:
                        return (SNMP_ERR_NOT_WRITEABLE);
                case LEAF_wlanMACAccessControlFlush:
                        break;
                default:
                        abort();
                }
                return (SNMP_ERR_NOERROR);

        case SNMP_OP_COMMIT:
                if ((wif = wlan_get_interface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                switch (val->var.subs[sub - 1]) {
                case LEAF_wlanMACAccessControlPolicy:
                        if (wlan_set_mac_policy(wif) < 0) {
                                wif->mac_policy = ctx->scratch->int1;
                                return (SNMP_ERR_GENERR);
                        }
                        break;
                case LEAF_wlanMACAccessControlFlush:
                        if (wlan_flush_mac_mac(wif) < 0)
                                return (SNMP_ERR_GENERR);
                        break;
                default:
                        abort();
                }
                return (SNMP_ERR_NOERROR);

        case SNMP_OP_ROLLBACK:
                if ((wif = wlan_get_interface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                if (val->var.subs[sub - 1] == LEAF_wlanMACAccessControlPolicy)
                        wif->mac_policy = ctx->scratch->int1;
                return (SNMP_ERR_NOERROR);

        default:
                abort();
        }

        if (wlan_get_mac_policy(wif) < 0)
                return (SNMP_ERR_GENERR);

        switch (val->var.subs[sub - 1]) {
        case LEAF_wlanMACAccessControlPolicy:
                val->v.integer = wif->mac_policy;
                break;
        case LEAF_wlanMACAccessControlNacl:
                val->v.integer = wif->mac_nacls;
                break;
        case LEAF_wlanMACAccessControlFlush:
                val->v.integer = wlanMACAccessControlFlush_no_op;
                break;
        default:
                abort();
        }

        return (SNMP_ERR_NOERROR);
}

int
op_wlan_mac_acl_mac(struct snmp_context *ctx, struct snmp_value *val,
    uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
{
        struct wlan_iface *wif;
        struct wlan_mac_mac *macl;

        wlan_update_interface_list();
        wlan_mac_update_aclmacs();

        switch (op) {
        case SNMP_OP_GET:
                if ((macl = wlan_get_acl_mac(&val->var, sub, &wif)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                break;

        case SNMP_OP_GETNEXT:
                if ((macl = wlan_get_next_acl_mac(&val->var, sub, &wif))
                    == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                wlan_append_mac_index(&val->var, sub, wif->wname, macl->mac);
                break;

        case SNMP_OP_SET:
                switch (val->var.subs[sub - 1]) {
                case LEAF_wlanMACAccessControlMAC:
                        return (SNMP_ERR_INCONS_NAME);
                case LEAF_wlanMACAccessControlMACStatus:
                        return(wlan_acl_mac_set_status(ctx, val, sub));
                default:
                        abort();
                }

        case SNMP_OP_COMMIT:
                if ((macl = wlan_get_acl_mac(&val->var, sub, &wif)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                if (val->v.integer == RowStatus_destroy &&
                    wlan_mac_delete_mac(wif, macl) < 0)
                        return (SNMP_ERR_GENERR);
                return (SNMP_ERR_NOERROR);

        case SNMP_OP_ROLLBACK:
                if ((macl = wlan_get_acl_mac(&val->var, sub, &wif)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                if (ctx->scratch->int1 == RowStatus_destroy &&
                    wlan_mac_delete_mac(wif, macl) < 0)
                        return (SNMP_ERR_GENERR);
                return (SNMP_ERR_NOERROR);

        default:
                abort();
        }

        switch (val->var.subs[sub - 1]) {
        case LEAF_wlanMACAccessControlMAC:
                return (string_get(val, macl->mac, IEEE80211_ADDR_LEN));
        case LEAF_wlanMACAccessControlMACStatus:
                val->v.integer = macl->mac_status;
                break;
        default:
                abort();
        }

        return (SNMP_ERR_NOERROR);
}

int
op_wlan_mesh_config(struct snmp_context *ctx, struct snmp_value *val,
    uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
{
        int which;

        switch (val->var.subs[sub - 1]) {
        case LEAF_wlanMeshMaxRetries:
                which = WLAN_MESH_MAX_RETRIES;
                break;
        case LEAF_wlanMeshHoldingTimeout:
                which = WLAN_MESH_HOLDING_TO;
                break;
        case LEAF_wlanMeshConfirmTimeout:
                which = WLAN_MESH_CONFIRM_TO;
                break;
        case LEAF_wlanMeshRetryTimeout:
                which = WLAN_MESH_RETRY_TO;
                break;
        default:
                abort();
        }

        switch (op) {
        case SNMP_OP_GET:
                if (wlan_do_sysctl(&wlan_config, which, 0) < 0)
                        return (SNMP_ERR_GENERR);
                break;

        case SNMP_OP_GETNEXT:
                abort();

        case SNMP_OP_SET:
                switch (val->var.subs[sub - 1]) {
                case LEAF_wlanMeshRetryTimeout :
                        ctx->scratch->int1 = wlan_config.mesh_retryto;
                        wlan_config.mesh_retryto = val->v.integer;
                        break;
                case LEAF_wlanMeshHoldingTimeout:
                        ctx->scratch->int1 = wlan_config.mesh_holdingto;
                        wlan_config.mesh_holdingto = val->v.integer;
                        break;
                case LEAF_wlanMeshConfirmTimeout:
                        ctx->scratch->int1 = wlan_config.mesh_confirmto;
                        wlan_config.mesh_confirmto = val->v.integer;
                        break;
                case LEAF_wlanMeshMaxRetries:
                        ctx->scratch->int1 = wlan_config.mesh_maxretries;
                        wlan_config.mesh_maxretries = val->v.integer;
                        break;
                }
                if (wlan_do_sysctl(&wlan_config, which, 1) < 0)
                        return (SNMP_ERR_GENERR);
                return (SNMP_ERR_NOERROR);

        case SNMP_OP_COMMIT:
                return (SNMP_ERR_NOERROR);

        case SNMP_OP_ROLLBACK:
                switch (val->var.subs[sub - 1]) {
                case LEAF_wlanMeshRetryTimeout:
                        wlan_config.mesh_retryto = ctx->scratch->int1;
                        break;
                case LEAF_wlanMeshConfirmTimeout:
                        wlan_config.mesh_confirmto = ctx->scratch->int1;
                        break;
                case LEAF_wlanMeshHoldingTimeout:
                        wlan_config.mesh_holdingto= ctx->scratch->int1;
                        break;
                case LEAF_wlanMeshMaxRetries:
                        wlan_config.mesh_maxretries = ctx->scratch->int1;
                        break;
                }
                if (wlan_do_sysctl(&wlan_config, which, 1) < 0)
                        return (SNMP_ERR_GENERR);
                return (SNMP_ERR_NOERROR);

        default:
                abort();
        }

        switch (val->var.subs[sub - 1]) {
        case LEAF_wlanMeshRetryTimeout:
                val->v.integer = wlan_config.mesh_retryto;
                break;
        case LEAF_wlanMeshHoldingTimeout:
                val->v.integer = wlan_config.mesh_holdingto;
                break;
        case LEAF_wlanMeshConfirmTimeout:
                val->v.integer = wlan_config.mesh_confirmto;
                break;
        case LEAF_wlanMeshMaxRetries:
                val->v.integer = wlan_config.mesh_maxretries;
                break;
        }

        return (SNMP_ERR_NOERROR);
}

int
op_wlan_mesh_iface(struct snmp_context *ctx, struct snmp_value *val,
    uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
{
        int rc;
        struct wlan_iface *wif;

        wlan_update_interface_list();

        switch (op) {
        case SNMP_OP_GET:
                if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                break;

        case SNMP_OP_GETNEXT:
                if ((wif = wlan_mesh_get_next_iface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                wlan_append_ifindex(&val->var, sub, wif);
                break;

        case SNMP_OP_SET:
                if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                switch (val->var.subs[sub - 1]) {
                case LEAF_wlanMeshId:
                        if (val->v.octetstring.len > IEEE80211_NWID_LEN)
                                return (SNMP_ERR_INCONS_VALUE);
                        ctx->scratch->ptr1 = malloc(val->v.octetstring.len + 1);
                        if (ctx->scratch->ptr1 == NULL)
                                return (SNMP_ERR_GENERR);
                        strlcpy(ctx->scratch->ptr1, wif->desired_ssid,
                            val->v.octetstring.len + 1);
                        ctx->scratch->int1 = strlen(wif->desired_ssid);
                        memcpy(wif->desired_ssid, val->v.octetstring.octets,
                            val->v.octetstring.len);
                        wif->desired_ssid[val->v.octetstring.len] = '\0';
                        break;
                case LEAF_wlanMeshTTL:
                        ctx->scratch->int1 = wif->mesh_ttl;
                        wif->mesh_ttl = val->v.integer;
                        break;
                case LEAF_wlanMeshPeeringEnabled:
                        ctx->scratch->int1 = wif->mesh_peering;
                        wif->mesh_peering = val->v.integer;
                        break;
                case LEAF_wlanMeshForwardingEnabled:
                        ctx->scratch->int1 = wif->mesh_forwarding;
                        wif->mesh_forwarding = val->v.integer;
                        break;
                case LEAF_wlanMeshMetric:
                        ctx->scratch->int1 = wif->mesh_metric;
                        wif->mesh_metric = val->v.integer;
                        break;
                case LEAF_wlanMeshPath:
                        ctx->scratch->int1 = wif->mesh_path;
                        wif->mesh_path = val->v.integer;
                        break;
                case LEAF_wlanMeshRoutesFlush:
                        if (val->v.integer != wlanMeshRoutesFlush_flush)
                                return (SNMP_ERR_INCONS_VALUE);
                        return (SNMP_ERR_NOERROR);
                default:
                        abort();
                }
                if (val->var.subs[sub - 1] == LEAF_wlanMeshId)
                        rc = wlan_config_set_dssid(wif,
                            val->v.octetstring.octets, val->v.octetstring.len);
                else
                        rc = wlan_mesh_config_set(wif, val->var.subs[sub - 1]);
                if (rc < 0)
                        return (SNMP_ERR_GENERR);
                return (SNMP_ERR_NOERROR);

        case SNMP_OP_COMMIT:
                if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                if (val->var.subs[sub - 1] == LEAF_wlanMeshRoutesFlush &&
                    wlan_mesh_flush_routes(wif) < 0)
                        return (SNMP_ERR_GENERR);
                if (val->var.subs[sub - 1] == LEAF_wlanMeshId)
                        free(ctx->scratch->ptr1);
                return (SNMP_ERR_NOERROR);

        case SNMP_OP_ROLLBACK:
                if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                switch (val->var.subs[sub - 1]) {
                case LEAF_wlanMeshId:
                        strlcpy(wif->desired_ssid, ctx->scratch->ptr1,
                            IEEE80211_NWID_LEN);
                        free(ctx->scratch->ptr1);
                        break;
                case LEAF_wlanMeshTTL:
                        wif->mesh_ttl = ctx->scratch->int1;
                        break;
                case LEAF_wlanMeshPeeringEnabled:
                        wif->mesh_peering = ctx->scratch->int1;
                        break;
                case LEAF_wlanMeshForwardingEnabled:
                        wif->mesh_forwarding = ctx->scratch->int1;
                        break;
                case LEAF_wlanMeshMetric:
                        wif->mesh_metric = ctx->scratch->int1;
                        break;
                case LEAF_wlanMeshPath:
                        wif->mesh_path = ctx->scratch->int1;
                        break;
                case LEAF_wlanMeshRoutesFlush:
                        return (SNMP_ERR_NOERROR);
                default:
                        abort();
                }
                if (val->var.subs[sub - 1] == LEAF_wlanMeshId)
                        rc = wlan_config_set_dssid(wif, wif->desired_ssid,
                            strlen(wif->desired_ssid));
                else
                        rc = wlan_mesh_config_set(wif, val->var.subs[sub - 1]);
                if (rc < 0)
                        return (SNMP_ERR_GENERR);
                return (SNMP_ERR_NOERROR);

        default:
                abort();
        }

        if (val->var.subs[sub - 1] == LEAF_wlanMeshId)
                rc = wlan_config_get_dssid(wif);
        else
                rc = wlan_mesh_config_get(wif, val->var.subs[sub - 1]);
        if (rc < 0)
                return (SNMP_ERR_GENERR);

        switch (val->var.subs[sub - 1]) {
        case LEAF_wlanMeshId:
                return (string_get(val, wif->desired_ssid, -1));
        case LEAF_wlanMeshTTL:
                val->v.integer = wif->mesh_ttl;
                break;
        case LEAF_wlanMeshPeeringEnabled:
                val->v.integer = wif->mesh_peering;
                break;
        case LEAF_wlanMeshForwardingEnabled:
                val->v.integer = wif->mesh_forwarding;
                break;
        case LEAF_wlanMeshMetric:
                val->v.integer = wif->mesh_metric;
                break;
        case LEAF_wlanMeshPath:
                val->v.integer = wif->mesh_path;
                break;
        case LEAF_wlanMeshRoutesFlush:
                val->v.integer = wlanMeshRoutesFlush_no_op;
                break;
        default:
                abort();
        }

        return (SNMP_ERR_NOERROR);
}

int
op_wlan_mesh_neighbor(struct snmp_context *ctx __unused, struct snmp_value *val,
    uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
{
        struct wlan_peer *wip;
        struct wlan_iface *wif;

        wlan_update_interface_list();
        wlan_update_peers();

        switch (op) {
        case SNMP_OP_GET:
                if ((wip = wlan_mesh_get_peer(&val->var, sub, &wif)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                break;
        case SNMP_OP_GETNEXT:
                wip = wlan_mesh_get_next_peer(&val->var, sub, &wif);
                if (wip == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                wlan_append_mac_index(&val->var, sub, wif->wname,
                    wip->pmac);
                break;
        case SNMP_OP_SET:
                return (SNMP_ERR_NOT_WRITEABLE);
        case SNMP_OP_COMMIT:
                /* FALLTHROUGH */
        case SNMP_OP_ROLLBACK:
                /* FALLTHROUGH */
        default:
                abort();
        }

        switch (val->var.subs[sub - 1]) {
        case LEAF_wlanMeshNeighborAddress:
                return (string_get(val, wip->pmac, IEEE80211_ADDR_LEN));
        case LEAF_wlanMeshNeighborFrequency:
                val->v.integer = wip->frequency;
                break;
        case LEAF_wlanMeshNeighborLocalId:
                val->v.integer = wip->local_id;
                break;
        case LEAF_wlanMeshNeighborPeerId:
                val->v.integer = wip->peer_id;
                break;
        case LEAF_wlanMeshNeighborPeerState:
                return (bits_get(val, (uint8_t *)&wip->state,
                    sizeof(wip->state)));
        case LEAF_wlanMeshNeighborCurrentTXRate:
                val->v.integer = wip->txrate;
                break;
        case LEAF_wlanMeshNeighborRxSignalStrength:
                val->v.integer = wip->rssi;
                break;
        case LEAF_wlanMeshNeighborIdleTimer:
                val->v.integer = wip->idle;
                break;
        case LEAF_wlanMeshNeighborTxSequenceNo:
                val->v.integer = wip->txseqs;
                break;
        case LEAF_wlanMeshNeighborRxSequenceNo:
                val->v.integer = wip->rxseqs;
                break;
        default:
                abort();
        }

        return (SNMP_ERR_NOERROR);
}

int
op_wlan_mesh_route(struct snmp_context *ctx, struct snmp_value *val,
    uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
{
        struct wlan_mesh_route *wmr;
        struct wlan_iface *wif;

        wlan_update_interface_list();
        wlan_mesh_update_routes();

        switch (op) {
        case SNMP_OP_GET:
                if ((wmr = wlan_mesh_get_route(&val->var, sub, &wif)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                break;

        case SNMP_OP_GETNEXT:
                wmr = wlan_mesh_get_next_route(&val->var, sub, &wif);
                if (wmr == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                wlan_append_mac_index(&val->var, sub, wif->wname,
                    wmr->imroute.imr_dest);
                break;

        case SNMP_OP_SET:
                switch (val->var.subs[sub - 1]) {
                case LEAF_wlanMeshRouteDestination:
                        return (SNMP_ERR_INCONS_NAME);
                case LEAF_wlanMeshRouteStatus:
                        return(wlan_mesh_route_set_status(ctx, val, sub));
                default:
                        return (SNMP_ERR_NOT_WRITEABLE);
                }
                abort();

        case SNMP_OP_COMMIT:
                if ((wmr = wlan_mesh_get_route(&val->var, sub, &wif)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                if (val->v.integer == RowStatus_destroy &&
                    wlan_mesh_delete_route(wif, wmr) < 0)
                        return (SNMP_ERR_GENERR);
                return (SNMP_ERR_NOERROR);

        case SNMP_OP_ROLLBACK:
                if ((wmr = wlan_mesh_get_route(&val->var, sub, &wif)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                if (ctx->scratch->int1 == RowStatus_destroy &&
                    wlan_mesh_delete_route(wif, wmr) < 0)
                        return (SNMP_ERR_GENERR);
                return (SNMP_ERR_NOERROR);

        default:
                abort();
        }

        switch (val->var.subs[sub - 1]) {
        case LEAF_wlanMeshRouteDestination:
                return (string_get(val, wmr->imroute.imr_dest,
                    IEEE80211_ADDR_LEN));
        case LEAF_wlanMeshRouteNextHop:
                return (string_get(val, wmr->imroute.imr_nexthop,
                    IEEE80211_ADDR_LEN));
        case LEAF_wlanMeshRouteHops:
                val->v.integer = wmr->imroute.imr_nhops;
                break;
        case LEAF_wlanMeshRouteMetric:
                val->v.integer = wmr->imroute.imr_metric;
                break;
        case LEAF_wlanMeshRouteLifeTime:
                val->v.integer = wmr->imroute.imr_lifetime;
                break;
        case LEAF_wlanMeshRouteLastMseq:
                val->v.integer = wmr->imroute.imr_lastmseq;
                break;
        case LEAF_wlanMeshRouteFlags:
                val->v.integer = 0;
                if ((wmr->imroute.imr_flags &
                    IEEE80211_MESHRT_FLAGS_VALID) != 0)
                        val->v.integer |= (0x1 << wlanMeshRouteFlags_valid);
                if ((wmr->imroute.imr_flags &
                    IEEE80211_MESHRT_FLAGS_PROXY) != 0)
                        val->v.integer |= (0x1 << wlanMeshRouteFlags_proxy);
                return (bits_get(val, (uint8_t *)&val->v.integer,
                    sizeof(val->v.integer)));
        case LEAF_wlanMeshRouteStatus:
                val->v.integer = wmr->mroute_status;
                break;
        }

        return (SNMP_ERR_NOERROR);
}

int
op_wlan_mesh_stats(struct snmp_context *ctx __unused, struct snmp_value *val,
    uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
{
        struct wlan_iface *wif;

        wlan_update_interface_list();

        switch (op) {
        case SNMP_OP_GET:
                if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                break;
        case SNMP_OP_GETNEXT:
                if ((wif = wlan_mesh_get_next_iface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                wlan_append_ifindex(&val->var, sub, wif);
                break;
        case SNMP_OP_SET:
                return (SNMP_ERR_NOT_WRITEABLE);
        case SNMP_OP_COMMIT:
                /* FALLTHROUGH */
        case SNMP_OP_ROLLBACK:
                /* FALLTHROUGH */
        default:
                abort();
        }

        if (wlan_get_stats(wif) < 0)
                return (SNMP_ERR_GENERR);

        switch (val->var.subs[sub - 1]) {
        case LEAF_wlanMeshDroppedBadSta:
                val->v.uint32 = wif->stats.is_mesh_wrongmesh;
                break;
        case LEAF_wlanMeshDroppedNoLink:
                val->v.uint32 = wif->stats.is_mesh_nolink;
                break;
        case LEAF_wlanMeshNoFwdTtl:
                val->v.uint32 = wif->stats.is_mesh_fwd_ttl;
                break;
        case LEAF_wlanMeshNoFwdBuf:
                val->v.uint32 = wif->stats.is_mesh_fwd_nobuf;
                break;
        case LEAF_wlanMeshNoFwdTooShort:
                val->v.uint32 = wif->stats.is_mesh_fwd_tooshort;
                break;
        case LEAF_wlanMeshNoFwdDisabled:
                val->v.uint32 = wif->stats.is_mesh_fwd_disabled;
                break;
        case LEAF_wlanMeshNoFwdPathUnknown:
                val->v.uint32 = wif->stats.is_mesh_fwd_nopath;
                break;
        case LEAF_wlanMeshDroppedBadAE:
                val->v.uint32 = wif->stats.is_mesh_badae;
                break;
        case LEAF_wlanMeshRouteAddFailed:
                val->v.uint32 = wif->stats.is_mesh_rtaddfailed;
                break;
        case LEAF_wlanMeshDroppedNoProxy:
                val->v.uint32 = wif->stats.is_mesh_notproxy;
                break;
        case LEAF_wlanMeshDroppedMisaligned:
                val->v.uint32 = wif->stats.is_rx_badalign;
                break;
        default:
                abort();
        }

        return (SNMP_ERR_NOERROR);
}

int
op_wlan_hwmp_config(struct snmp_context *ctx, struct snmp_value *val,
    uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
{
        int which;

        switch (val->var.subs[sub - 1]) {
        case LEAF_wlanHWMPRouteInactiveTimeout:
                which = WLAN_HWMP_INACTIVITY_TO;
                break;
        case LEAF_wlanHWMPRootAnnounceInterval:
                which = WLAN_HWMP_RANN_INT;
                break;
        case LEAF_wlanHWMPRootInterval:
                which = WLAN_HWMP_ROOT_INT;
                break;
        case LEAF_wlanHWMPRootTimeout:
                which = WLAN_HWMP_ROOT_TO;
                break;
        case LEAF_wlanHWMPPathLifetime:
                which = WLAN_HWMP_PATH_LIFETIME;
                break;
        case LEAF_wlanHWMPReplyForwardBit:
                which = WLAN_HWMP_REPLY_FORWARD;
                break;
        case LEAF_wlanHWMPTargetOnlyBit:
                which = WLAN_HWMP_TARGET_ONLY;
                break;
        default:
                abort();
        }

        switch (op) {
        case SNMP_OP_GET:
                if (wlan_do_sysctl(&wlan_config, which, 0) < 0)
                        return (SNMP_ERR_GENERR);
                break;

        case SNMP_OP_GETNEXT:
                abort();

        case SNMP_OP_SET:
                switch (val->var.subs[sub - 1]) {
                case LEAF_wlanHWMPRouteInactiveTimeout:
                        ctx->scratch->int1 = wlan_config.hwmp_inact;
                        wlan_config.hwmp_inact = val->v.integer;
                        break;
                case LEAF_wlanHWMPRootAnnounceInterval:
                        ctx->scratch->int1 = wlan_config.hwmp_rannint;
                        wlan_config.hwmp_rannint = val->v.integer;
                        break;
                case LEAF_wlanHWMPRootInterval:
                        ctx->scratch->int1 = wlan_config.hwmp_rootint;
                        wlan_config.hwmp_rootint = val->v.integer;
                        break;
                case LEAF_wlanHWMPRootTimeout:
                        ctx->scratch->int1 = wlan_config.hwmp_roottimeout;
                        wlan_config.hwmp_roottimeout = val->v.integer;
                        break;
                case LEAF_wlanHWMPPathLifetime:
                        ctx->scratch->int1 = wlan_config.hwmp_pathlifetime;
                        wlan_config.hwmp_pathlifetime = val->v.integer;
                        break;
                case LEAF_wlanHWMPReplyForwardBit:
                        ctx->scratch->int1 = wlan_config.hwmp_replyforward;
                        wlan_config.hwmp_replyforward = val->v.integer;
                        break;
                case LEAF_wlanHWMPTargetOnlyBit:
                        ctx->scratch->int1 = wlan_config.hwmp_targetonly;
                        wlan_config.hwmp_targetonly = val->v.integer;
                        break;
                }
                if (wlan_do_sysctl(&wlan_config, which, 1) < 0)
                        return (SNMP_ERR_GENERR);
                return (SNMP_ERR_NOERROR);

        case SNMP_OP_COMMIT:
                return (SNMP_ERR_NOERROR);

        case SNMP_OP_ROLLBACK:
                switch (val->var.subs[sub - 1]) {
                case LEAF_wlanHWMPRouteInactiveTimeout:
                        wlan_config.hwmp_inact = ctx->scratch->int1;
                        break;
                case LEAF_wlanHWMPRootAnnounceInterval:
                        wlan_config.hwmp_rannint = ctx->scratch->int1;
                        break;
                case LEAF_wlanHWMPRootInterval:
                        wlan_config.hwmp_rootint = ctx->scratch->int1;
                        break;
                case LEAF_wlanHWMPRootTimeout:
                        wlan_config.hwmp_roottimeout = ctx->scratch->int1;
                        break;
                case LEAF_wlanHWMPPathLifetime:
                        wlan_config.hwmp_pathlifetime = ctx->scratch->int1;
                        break;
                case LEAF_wlanHWMPReplyForwardBit:
                        wlan_config.hwmp_replyforward = ctx->scratch->int1;
                        break;
                case LEAF_wlanHWMPTargetOnlyBit:
                        wlan_config.hwmp_targetonly = ctx->scratch->int1;
                        break;
                }
                if (wlan_do_sysctl(&wlan_config, which, 1) < 0)
                        return (SNMP_ERR_GENERR);
                return (SNMP_ERR_NOERROR);

        default:
                abort();
        }

        switch (val->var.subs[sub - 1]) {
        case LEAF_wlanHWMPRouteInactiveTimeout:
                val->v.integer = wlan_config.hwmp_inact;
                break;
        case LEAF_wlanHWMPRootAnnounceInterval:
                val->v.integer = wlan_config.hwmp_rannint;
                break;
        case LEAF_wlanHWMPRootInterval:
                val->v.integer = wlan_config.hwmp_rootint;
                break;
        case LEAF_wlanHWMPRootTimeout:
                val->v.integer = wlan_config.hwmp_roottimeout;
                break;
        case LEAF_wlanHWMPPathLifetime:
                val->v.integer = wlan_config.hwmp_pathlifetime;
                break;
        case LEAF_wlanHWMPReplyForwardBit:
                val->v.integer = wlan_config.hwmp_replyforward;
                break;
        case LEAF_wlanHWMPTargetOnlyBit:
                val->v.integer = wlan_config.hwmp_targetonly;
                break;
        }

        return (SNMP_ERR_NOERROR);
}

int
op_wlan_hwmp_iface(struct snmp_context *ctx, struct snmp_value *val,
    uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
{
        struct wlan_iface *wif;

        wlan_update_interface_list();

        switch (op) {
        case SNMP_OP_GET:
                if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                break;

        case SNMP_OP_GETNEXT:
                if ((wif = wlan_mesh_get_next_iface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                wlan_append_ifindex(&val->var, sub, wif);
                break;

        case SNMP_OP_SET:
                if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                switch (val->var.subs[sub - 1]) {
                case LEAF_wlanHWMPRootMode:
                        ctx->scratch->int1 = wif->hwmp_root_mode;
                        wif->hwmp_root_mode = val->v.integer;
                        break;
                case LEAF_wlanHWMPMaxHops:
                        ctx->scratch->int1 = wif->hwmp_max_hops;
                        wif->hwmp_max_hops = val->v.integer;
                        break;
                default:
                        abort();
                }
                if (wlan_hwmp_config_set(wif, val->var.subs[sub - 1]) < 0)
                        return (SNMP_ERR_GENERR);
                return (SNMP_ERR_NOERROR);

        case SNMP_OP_COMMIT:
                return (SNMP_ERR_NOERROR);

        case SNMP_OP_ROLLBACK:
                if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                switch (val->var.subs[sub - 1]) {
                case LEAF_wlanHWMPRootMode:
                        wif->hwmp_root_mode = ctx->scratch->int1;
                        break;
                case LEAF_wlanHWMPMaxHops:
                        wif->hwmp_max_hops = ctx->scratch->int1;
                        break;
                default:
                        abort();
                }
                if (wlan_hwmp_config_set(wif, val->var.subs[sub - 1]) < 0)
                        return (SNMP_ERR_GENERR);
                return (SNMP_ERR_NOERROR);

        default:
                abort();
        }

        if (wlan_hwmp_config_get(wif, val->var.subs[sub - 1]) < 0)
                return (SNMP_ERR_GENERR);

        switch (val->var.subs[sub - 1]) {
        case LEAF_wlanHWMPRootMode:
                val->v.integer = wif->hwmp_root_mode;
                break;
        case LEAF_wlanHWMPMaxHops:
                val->v.integer = wif->hwmp_max_hops;
                break;
        default:
                abort();
        }

        return (SNMP_ERR_NOERROR);
}

int
op_wlan_hwmp_stats(struct snmp_context *ctx __unused, struct snmp_value *val,
    uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
{
        struct wlan_iface *wif;

        wlan_update_interface_list();

        switch (op) {
        case SNMP_OP_GET:
                if ((wif = wlan_mesh_get_iface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                break;
        case SNMP_OP_GETNEXT:
                if ((wif = wlan_mesh_get_next_iface(&val->var, sub)) == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                wlan_append_ifindex(&val->var, sub, wif);
                break;
        case SNMP_OP_SET:
                return (SNMP_ERR_NOT_WRITEABLE);
        case SNMP_OP_COMMIT:
                /* FALLTHROUGH */
        case SNMP_OP_ROLLBACK:
                /* FALLTHROUGH */
        default:
                abort();
        }

        if (wlan_get_stats(wif) < 0)
                return (SNMP_ERR_GENERR);

        switch (val->var.subs[sub - 1]) {
        case LEAF_wlanMeshHWMPWrongSeqNo:
                val->v.uint32 = wif->stats.is_hwmp_wrongseq;
                break;
        case LEAF_wlanMeshHWMPTxRootPREQ:
                val->v.uint32 = wif->stats.is_hwmp_rootreqs;
                break;
        case LEAF_wlanMeshHWMPTxRootRANN:
                val->v.uint32 = wif->stats.is_hwmp_rootrann;
                break;
        case LEAF_wlanMeshHWMPProxy:
                val->v.uint32 = wif->stats.is_hwmp_proxy;
                break;
        default:
                abort();
        }

        return (SNMP_ERR_NOERROR);
}

/*
 * Encode BITS type for a response packet - XXX: this belongs to the snmp lib.
 */
static int
bits_get(struct snmp_value *value, const u_char *ptr, ssize_t len)
{
        int size;

        if (ptr == NULL) {
                value->v.octetstring.len = 0;
                value->v.octetstring.octets = NULL;
                return (SNMP_ERR_NOERROR);
        }

        /* Determine length - up to 8 octets supported so far. */
        for (size = len; size > 0; size--)
                if (ptr[size - 1] != 0)
                        break;
        if (size == 0)
                size = 1;

        value->v.octetstring.len = (u_long)size;
        if ((value->v.octetstring.octets = malloc((size_t)size)) == NULL)
                return (SNMP_ERR_RES_UNAVAIL);
        memcpy(value->v.octetstring.octets, ptr, (size_t)size);
        return (SNMP_ERR_NOERROR);
}

/*
 * Calls for adding/updating/freeing/etc of wireless interfaces.
 */
static void
wlan_free_interface(struct wlan_iface *wif)
{
        wlan_free_peerlist(wif);
        free(wif->chanlist);
        wlan_scan_free_results(wif);
        wlan_mac_free_maclist(wif);
        wlan_mesh_free_routes(wif);
        free(wif);
}

static void
wlan_free_iflist(void)
{
        struct wlan_iface *w;

        while ((w = SLIST_FIRST(&wlan_ifaces)) != NULL) {
                SLIST_REMOVE_HEAD(&wlan_ifaces, w_if);
                wlan_free_interface(w);
        }
}

static struct wlan_iface *
wlan_find_interface(const char *wname)
{
        struct wlan_iface *wif;

        SLIST_FOREACH(wif, &wlan_ifaces, w_if)
                if (strcmp(wif->wname, wname) == 0) {
                        if (wif->status != RowStatus_active)
                                return (NULL);
                        break;
                }

        return (wif);
}

static struct wlan_iface *
wlan_first_interface(void)
{
        return (SLIST_FIRST(&wlan_ifaces));
}

static struct wlan_iface *
wlan_next_interface(struct wlan_iface *wif)
{
        if (wif == NULL)
                return (NULL);

        return (SLIST_NEXT(wif, w_if));
}

/*
 * Add a new interface to the list - sorted by name.
 */
static int
wlan_add_wif(struct wlan_iface *wif)
{
        int cmp;
        struct wlan_iface *temp, *prev;

        if ((prev = SLIST_FIRST(&wlan_ifaces)) == NULL ||
            strcmp(wif->wname, prev->wname) < 0) {
                SLIST_INSERT_HEAD(&wlan_ifaces, wif, w_if);
                return (0);
        }

        SLIST_FOREACH(temp, &wlan_ifaces, w_if) {
                if ((cmp = strcmp(wif->wname, temp->wname)) <= 0)
                        break;
                prev = temp;
        }

        if (temp == NULL)
                SLIST_INSERT_AFTER(prev, wif, w_if);
        else if (cmp > 0)
                SLIST_INSERT_AFTER(temp, wif, w_if);
        else {
                syslog(LOG_ERR, "Wlan iface %s already in list", wif->wname);
                return (-1);
        }

        return (0);
}

static struct wlan_iface *
wlan_new_wif(char *wname)
{
        struct wlan_iface *wif;

        /* Make sure it's not in the list. */
        for (wif = wlan_first_interface(); wif != NULL;
            wif = wlan_next_interface(wif))
                if (strcmp(wname, wif->wname) == 0) {
                        wif->internal = 0;
                        return (wif);
                }

        if ((wif = (struct wlan_iface *)malloc(sizeof(*wif))) == NULL)
                return (NULL);

        memset(wif, 0, sizeof(struct wlan_iface));
        strlcpy(wif->wname, wname, IFNAMSIZ);
        wif->status = RowStatus_notReady;
        wif->state = wlanIfaceState_down;
        wif->mode = WlanIfaceOperatingModeType_station;

        if (wlan_add_wif(wif) < 0) {
                free(wif);
                return (NULL);
        }

        return (wif);
}

static void
wlan_delete_wif(struct wlan_iface *wif)
{
        SLIST_REMOVE(&wlan_ifaces, wif, wlan_iface, w_if);
        wlan_free_interface(wif);
}

static int
wlan_attach_newif(struct mibif *mif)
{
        struct wlan_iface *wif;

        if (mif->mib.ifmd_data.ifi_type != IFT_ETHER ||
            wlan_check_media(mif->name) != IFM_IEEE80211)
                return (0);

        if ((wif = wlan_new_wif(mif->name)) == NULL)
                return (-1);

        (void)wlan_get_opmode(wif);
        wif->index = mif->index;
        wif->status = RowStatus_active;
        (void)wlan_update_interface(wif);

        return (0);
}

static int
wlan_iface_create(struct wlan_iface *wif)
{
        int rc;

        if ((rc = wlan_clone_create(wif)) == SNMP_ERR_NOERROR) {
                /*
                 * The rest of the info will be updated once the
                 * snmp_mibII module notifies us of the interface.
                 */
                wif->status = RowStatus_active;
                if (wif->state == wlanIfaceState_up)
                        (void)wlan_config_state(wif, 1);
        }

        return (rc);
}

static int
wlan_iface_destroy(struct wlan_iface *wif)
{
        int rc = SNMP_ERR_NOERROR;

        if (wif->internal == 0)
                rc = wlan_clone_destroy(wif);

        if (rc == SNMP_ERR_NOERROR)
                wlan_delete_wif(wif);

        return (rc);
}

static int
wlan_update_interface(struct wlan_iface *wif)
{
        int i;

        (void)wlan_config_state(wif, 0);
        (void)wlan_get_driver_caps(wif);
        for (i = LEAF_wlanIfacePacketBurst;
            i <= LEAF_wlanIfaceTdmaBeaconInterval; i++)
                (void)wlan_config_get_ioctl(wif, i);
        (void)wlan_get_stats(wif);
        /*
         * XXX: wlan_get_channel_list() not needed -
         * fetched with wlan_get_driver_caps()
         */
        (void)wlan_get_channel_list(wif);
        (void)wlan_get_roam_params(wif);
        (void)wlan_get_tx_params(wif);
        (void)wlan_get_scan_results(wif);
        (void)wlan_get_wepmode(wif);
        (void)wlan_get_weptxkey(wif);
        (void)wlan_get_mac_policy(wif);
        (void)wlan_get_mac_acl_macs(wif);
        (void)wlan_get_peerinfo(wif);

        if (wif->mode == WlanIfaceOperatingModeType_meshPoint) {
                for (i = LEAF_wlanMeshTTL; i <= LEAF_wlanMeshPath; i++)
                        (void)wlan_mesh_config_get(wif, i);
                (void)wlan_mesh_get_routelist(wif);
                for (i = LEAF_wlanHWMPRootMode; i <= LEAF_wlanHWMPMaxHops; i++)
                        (void)wlan_hwmp_config_get(wif, i);
        }

        return (0);
}

static void
wlan_update_interface_list(void)
{
        struct wlan_iface *wif, *twif;

        if ((time(NULL) - wlan_iflist_age) <= WLAN_LIST_MAXAGE)
                return;

        /*
         * The snmp_mibII module would have notified us for new interfaces,
         * so only check if any have been deleted.
         */
        SLIST_FOREACH_SAFE(wif, &wlan_ifaces, w_if, twif)
                if (wif->status == RowStatus_active && wlan_get_opmode(wif) < 0)
                        wlan_delete_wif(wif);

        wlan_iflist_age = time(NULL);
}

static void
wlan_append_ifindex(struct asn_oid *oid, uint sub, const struct wlan_iface *w)
{
        uint32_t i;

        oid->len = sub + strlen(w->wname) + 1;
        oid->subs[sub] = strlen(w->wname);
        for (i = 1; i <= strlen(w->wname); i++)
                oid->subs[sub + i] = w->wname[i - 1];
}

static uint8_t *
wlan_get_ifname(const struct asn_oid *oid, uint sub, uint8_t *wname)
{
        uint32_t i;

        memset(wname, 0, IFNAMSIZ);

        if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ)
                return (NULL);

        for (i = 0; i < oid->subs[sub]; i++)
                wname[i] = oid->subs[sub + i + 1];
        wname[i] = '\0';

        return (wname);
}

static struct wlan_iface *
wlan_get_interface(const struct asn_oid *oid, uint sub)
{
        uint8_t wname[IFNAMSIZ];

        if (wlan_get_ifname(oid, sub, wname) == NULL)
                return (NULL);

        return (wlan_find_interface(wname));
}

static struct wlan_iface *
wlan_get_next_interface(const struct asn_oid *oid, uint sub)
{
        uint32_t i;
        uint8_t wname[IFNAMSIZ];
        struct wlan_iface *wif;

        if (oid->len - sub == 0) {
                for (wif = wlan_first_interface(); wif != NULL;
                    wif = wlan_next_interface(wif))
                        if (wif->status == RowStatus_active)
                                break;
                return (wif);
        }

        if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ)
                return (NULL);

        memset(wname, 0, IFNAMSIZ);
        for (i = 0; i < oid->subs[sub]; i++)
                wname[i] = oid->subs[sub + i + 1];
        wname[i] = '\0';
        if ((wif = wlan_find_interface(wname)) == NULL)
                return (NULL);

        while ((wif = wlan_next_interface(wif)) != NULL)
                if (wif->status == RowStatus_active)
                        break;

        return (wif);
}

static struct wlan_iface *
wlan_get_snmp_interface(const struct asn_oid *oid, uint sub)
{
        uint8_t wname[IFNAMSIZ];
        struct wlan_iface *wif;

        if (wlan_get_ifname(oid, sub, wname) == NULL)
                return (NULL);

        for (wif = wlan_first_interface(); wif != NULL;
            wif = wlan_next_interface(wif))
                if (strcmp(wif->wname, wname) == 0)
                        break;

        return (wif);
}

static struct wlan_iface *
wlan_get_next_snmp_interface(const struct asn_oid *oid, uint sub)
{
        uint32_t i;
        uint8_t wname[IFNAMSIZ];
        struct wlan_iface *wif;

        if (oid->len - sub == 0)
                return (wlan_first_interface());

        if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ)
                return (NULL);

        memset(wname, 0, IFNAMSIZ);
        for (i = 0; i < oid->subs[sub]; i++)
                wname[i] = oid->subs[sub + i + 1];
        wname[i] = '\0';

        for (wif = wlan_first_interface(); wif != NULL;
            wif = wlan_next_interface(wif))
                if (strcmp(wif->wname, wname) == 0)
                        break;

        return (wlan_next_interface(wif));
}

/*
 * Decode/Append an index for tables indexed by the wireless interface
 * name and a MAC address - ACL MACs and Mesh Routes.
 */
static int
wlan_mac_index_decode(const struct asn_oid *oid, uint sub,
    char *wname, uint8_t *mac)
{
        uint32_t i;
        int mac_off;

        if (oid->len - sub != oid->subs[sub] + 2 + IEEE80211_ADDR_LEN
            || oid->subs[sub] >= IFNAMSIZ)
                return (-1);

        for (i = 0; i < oid->subs[sub]; i++)
                wname[i] = oid->subs[sub + i + 1];
        wname[i] = '\0';

        mac_off = sub + oid->subs[sub] + 1;
        if (oid->subs[mac_off] != IEEE80211_ADDR_LEN)
                return (-1);
        for (i = 0; i < IEEE80211_ADDR_LEN; i++)
                mac[i] = oid->subs[mac_off + i + 1];

        return (0);
}

static void
wlan_append_mac_index(struct asn_oid *oid, uint sub, char *wname, uint8_t *mac)
{
        uint32_t i;

        oid->len = sub + strlen(wname) + IEEE80211_ADDR_LEN + 2;
        oid->subs[sub] = strlen(wname);
        for (i = 1; i <= strlen(wname); i++)
                oid->subs[sub + i] = wname[i - 1];

        sub += strlen(wname) + 1;
        oid->subs[sub] = IEEE80211_ADDR_LEN;
        for (i = 1; i <= IEEE80211_ADDR_LEN; i++)
                oid->subs[sub + i] = mac[i - 1];
}

/*
 * Decode/Append an index for tables indexed by the wireless interface
 * name and the PHY mode - Roam and TX params.
 */
static int
wlan_phy_index_decode(const struct asn_oid *oid, uint sub, char *wname,
    uint32_t *phy)
{
        uint32_t i;

        if (oid->len - sub != oid->subs[sub] + 2 || oid->subs[sub] >= IFNAMSIZ)
                return (-1);

        for (i = 0; i < oid->subs[sub]; i++)
                wname[i] = oid->subs[sub + i + 1];
        wname[i] = '\0';

        *phy = oid->subs[sub + oid->subs[sub] + 1];
        return (0);
}

static void
wlan_append_phy_index(struct asn_oid *oid, uint sub, char *wname, uint32_t phy)
{
        uint32_t i;

        oid->len = sub + strlen(wname) + 2;
        oid->subs[sub] = strlen(wname);
        for (i = 1; i <= strlen(wname); i++)
                oid->subs[sub + i] = wname[i - 1];
        oid->subs[sub + strlen(wname) + 1] = phy;
}

/*
 * Calls for manipulating the peerlist of a wireless interface.
 */
static void
wlan_free_peerlist(struct wlan_iface *wif)
{
        struct wlan_peer *wip;

        while ((wip = SLIST_FIRST(&wif->peerlist)) != NULL) {
                SLIST_REMOVE_HEAD(&wif->peerlist, wp);
                free(wip);
        }

        SLIST_INIT(&wif->peerlist);
}

static struct wlan_peer *
wlan_find_peer(struct wlan_iface *wif, uint8_t *peermac)
{
        struct wlan_peer *wip;

        SLIST_FOREACH(wip, &wif->peerlist, wp)
                if (memcmp(wip->pmac, peermac, IEEE80211_ADDR_LEN) == 0)
                        break;

        return (wip);
}

struct wlan_peer *
wlan_new_peer(const uint8_t *pmac)
{
        struct wlan_peer *wip;

        if ((wip = (struct wlan_peer *)malloc(sizeof(*wip))) == NULL)
                return (NULL);

        memset(wip, 0, sizeof(struct wlan_peer));
        memcpy(wip->pmac, pmac, IEEE80211_ADDR_LEN);

        return (wip);
}

void
wlan_free_peer(struct wlan_peer *wip)
{
        free(wip);
}

int
wlan_add_peer(struct wlan_iface *wif, struct wlan_peer *wip)
{
        struct wlan_peer *temp, *prev;

        SLIST_FOREACH(temp, &wif->peerlist, wp)
                if (memcmp(temp->pmac, wip->pmac, IEEE80211_ADDR_LEN) == 0)
                        return (-1);

        if ((prev = SLIST_FIRST(&wif->peerlist)) == NULL ||
            memcmp(wip->pmac, prev->pmac, IEEE80211_ADDR_LEN) < 0) {
                SLIST_INSERT_HEAD(&wif->peerlist, wip, wp);
                return (0);
        }

        SLIST_FOREACH(temp, &wif->peerlist, wp) {
                if (memcmp(wip->pmac, temp->pmac, IEEE80211_ADDR_LEN) < 0)
                        break;
                prev = temp;
        }

        SLIST_INSERT_AFTER(prev, wip, wp);
        return (0);
}

static void
wlan_update_peers(void)
{
        struct wlan_iface *wif;

        if ((time(NULL) - wlan_peerlist_age) <= WLAN_LIST_MAXAGE)
                return;

        for (wif = wlan_first_interface(); wif != NULL;
            wif = wlan_next_interface(wif)) {
                if (wif->status != RowStatus_active)
                        continue;
                wlan_free_peerlist(wif);
                (void)wlan_get_peerinfo(wif);
        }
        wlan_peerlist_age = time(NULL);
}

static struct wlan_peer *
wlan_get_peer(const struct asn_oid *oid, uint sub, struct wlan_iface **wif)
{
        char wname[IFNAMSIZ];
        uint8_t pmac[IEEE80211_ADDR_LEN];

        if (wlan_mac_index_decode(oid, sub, wname, pmac) < 0)
                return (NULL);

        if ((*wif = wlan_find_interface(wname)) == NULL)
                return (NULL);

        return (wlan_find_peer(*wif, pmac));
}

static struct wlan_peer *
wlan_get_next_peer(const struct asn_oid *oid, uint sub, struct wlan_iface **wif)
{
        char wname[IFNAMSIZ];
        char pmac[IEEE80211_ADDR_LEN];
        struct wlan_peer *wip;

        if (oid->len - sub == 0) {
                for (*wif = wlan_first_interface(); *wif != NULL;
                    *wif = wlan_next_interface(*wif)) {
                        if ((*wif)->mode ==
                            WlanIfaceOperatingModeType_meshPoint)
                                continue;
                        wip = SLIST_FIRST(&(*wif)->peerlist);
                        if (wip != NULL)
                                return (wip);
                }
                return (NULL);
        }

        if (wlan_mac_index_decode(oid, sub, wname, pmac) < 0 ||
            (*wif = wlan_find_interface(wname)) == NULL ||
            (wip = wlan_find_peer(*wif, pmac)) == NULL)
                return (NULL);

        if ((wip = SLIST_NEXT(wip, wp)) != NULL)
                return (wip);

        while ((*wif = wlan_next_interface(*wif)) != NULL) {
                if ((*wif)->mode == WlanIfaceOperatingModeType_meshPoint)
                        continue;
                if ((wip = SLIST_FIRST(&(*wif)->peerlist)) != NULL)
                        break;
        }

        return (wip);
}

/*
 * Calls for manipulating the active channel list of a wireless interface.
 */
static void
wlan_update_channels(void)
{
        struct wlan_iface *wif;

        if ((time(NULL) - wlan_chanlist_age) <= WLAN_LIST_MAXAGE)
                return;

        for (wif = wlan_first_interface(); wif != NULL;
            wif = wlan_next_interface(wif)) {
                if (wif->status != RowStatus_active)
                        continue;
                (void)wlan_get_channel_list(wif);
        }
        wlan_chanlist_age = time(NULL);
}

static int
wlan_channel_index_decode(const struct asn_oid *oid, uint sub, char *wname,
    uint32_t *cindex)
{
        uint32_t i;
        if (oid->len - sub != oid->subs[sub] + 2 || oid->subs[sub] >= IFNAMSIZ)
                return (-1);

        for (i = 0; i < oid->subs[sub]; i++)
                wname[i] = oid->subs[sub + i + 1];
        wname[i] = '\0';

        *cindex = oid->subs[sub + oid->subs[sub] + 1];

        return (0);
}

static void
wlan_append_channel_index(struct asn_oid *oid, uint sub,
    const struct wlan_iface *wif, const struct ieee80211_channel *channel)
{
        uint32_t i;

        oid->len = sub + strlen(wif->wname) + 2;
        oid->subs[sub] = strlen(wif->wname);
        for (i = 1; i <= strlen(wif->wname); i++)
                oid->subs[sub + i] = wif->wname[i - 1];
        oid->subs[sub + strlen(wif->wname) + 1] = (channel - wif->chanlist) + 1;
}

static int32_t
wlan_get_channel_type(struct ieee80211_channel *c)
{
        if (IEEE80211_IS_CHAN_FHSS(c))
                return (WlanChannelType_fhss);
        if (IEEE80211_IS_CHAN_A(c))
                return (WlanChannelType_dot11a);
        if (IEEE80211_IS_CHAN_B(c))
                return (WlanChannelType_dot11b);
        if (IEEE80211_IS_CHAN_ANYG(c))
                return (WlanChannelType_dot11g);
        if (IEEE80211_IS_CHAN_HALF(c))
                return (WlanChannelType_tenMHz);
        if (IEEE80211_IS_CHAN_QUARTER(c))
                return (WlanChannelType_fiveMHz);
        if (IEEE80211_IS_CHAN_TURBO(c))
                return (WlanChannelType_turbo);
        if (IEEE80211_IS_CHAN_HT(c))
                return (WlanChannelType_ht);

        return (-1);
}

static struct ieee80211_channel *
wlan_find_channel(struct wlan_iface *wif, uint32_t cindex)
{
        if (wif->chanlist == NULL || cindex > wif->nchannels)
                return (NULL);

        return (wif->chanlist + cindex - 1);
}

static struct ieee80211_channel *
wlan_get_channel(const struct asn_oid *oid, uint sub, struct wlan_iface **wif)
{
        uint32_t cindex;
        char wname[IFNAMSIZ];

        if (wlan_channel_index_decode(oid, sub, wname, &cindex) < 0)
                return (NULL);

        if ((*wif = wlan_find_interface(wname)) == NULL)
                return (NULL);

        return (wlan_find_channel(*wif, cindex));
}

static struct ieee80211_channel *
wlan_get_next_channel(const struct asn_oid *oid, uint sub,
    struct wlan_iface **wif)
{
        uint32_t cindex;
        char wname[IFNAMSIZ];

        if (oid->len - sub == 0) {
                for (*wif = wlan_first_interface(); *wif != NULL;
                    *wif = wlan_next_interface(*wif)) {
                        if ((*wif)->status != RowStatus_active)
                                continue;
                        if ((*wif)->nchannels != 0 && (*wif)->chanlist != NULL)
                                return ((*wif)->chanlist);
                }
                return (NULL);
        }

        if (wlan_channel_index_decode(oid, sub, wname, &cindex) < 0)
                return (NULL);

        if ((*wif = wlan_find_interface(wname)) == NULL)
                return (NULL);

        if (cindex < (*wif)->nchannels)
                return ((*wif)->chanlist + cindex);

        while ((*wif = wlan_next_interface(*wif)) != NULL)
                if ((*wif)->status == RowStatus_active)
                        if ((*wif)->nchannels != 0 && (*wif)->chanlist != NULL)
                                return ((*wif)->chanlist);

        return (NULL);
}

/*
 * Calls for manipulating the roam params of a wireless interface.
 */
static void
wlan_update_roam_params(void)
{
        struct wlan_iface *wif;

        if ((time(NULL) - wlan_roamlist_age) <= WLAN_LIST_MAXAGE)
                return;

        for (wif = wlan_first_interface(); wif != NULL;
            wif = wlan_next_interface(wif)) {
                if (wif->status != RowStatus_active)
                        continue;
                (void)wlan_get_roam_params(wif);
        }
        wlan_roamlist_age = time(NULL);
}

static struct ieee80211_roamparam *
wlan_get_roam_param(const struct asn_oid *oid, uint sub, struct wlan_iface **wif)
{
        uint32_t phy;
        char wname[IFNAMSIZ];

        if (wlan_phy_index_decode(oid, sub, wname, &phy) < 0)
                return (NULL);

        if ((*wif = wlan_find_interface(wname)) == NULL)
                return (NULL);

        if (phy == 0 || phy > IEEE80211_MODE_MAX)
                return (NULL);

        return ((*wif)->roamparams.params + phy - 1);
}

static struct ieee80211_roamparam *
wlan_get_next_roam_param(const struct asn_oid *oid, uint sub,
    struct wlan_iface **wif, uint32_t *phy)
{
        char wname[IFNAMSIZ];

        if (oid->len - sub == 0) {
                for (*wif = wlan_first_interface(); *wif != NULL;
                    *wif = wlan_next_interface(*wif)) {
                        if ((*wif)->status != RowStatus_active)
                                continue;
                        *phy = 1;
                        return ((*wif)->roamparams.params);
                }
                return (NULL);
        }

        if (wlan_phy_index_decode(oid, sub, wname, phy) < 0)
                return (NULL);

        if (*phy == 0  || (*wif = wlan_find_interface(wname)) == NULL)
                return (NULL);

        if (++(*phy) <= IEEE80211_MODE_MAX)
                return ((*wif)->roamparams.params + *phy - 1);

        *phy = 1;
        while ((*wif = wlan_next_interface(*wif)) != NULL)
                if ((*wif)->status == RowStatus_active)
                        return ((*wif)->roamparams.params);

        return (NULL);
}

/*
 * Calls for manipulating the tx params of a wireless interface.
 */
static void
wlan_update_tx_params(void)
{
        struct wlan_iface *wif;

        if ((time(NULL) - wlan_tx_paramlist_age) <= WLAN_LIST_MAXAGE)
                return;

        for (wif = wlan_first_interface(); wif != NULL;
            wif = wlan_next_interface(wif)) {
                if (wif->status != RowStatus_active)
                        continue;
                (void)wlan_get_tx_params(wif);
        }

        wlan_tx_paramlist_age = time(NULL);
}

static struct ieee80211_txparam *
wlan_get_tx_param(const struct asn_oid *oid, uint sub, struct wlan_iface **wif,
    uint32_t *phy)
{
        char wname[IFNAMSIZ];

        if (wlan_phy_index_decode(oid, sub, wname, phy) < 0)
                return (NULL);

        if ((*wif = wlan_find_interface(wname)) == NULL)
                return (NULL);

        if (*phy == 0 || *phy > IEEE80211_MODE_MAX)
                return (NULL);

        return ((*wif)->txparams.params + *phy - 1);
}

static struct ieee80211_txparam *
wlan_get_next_tx_param(const struct asn_oid *oid, uint sub,
    struct wlan_iface **wif, uint32_t *phy)
{
        char wname[IFNAMSIZ];

        if (oid->len - sub == 0) {
                for (*wif = wlan_first_interface(); *wif != NULL;
                    *wif = wlan_next_interface(*wif)) {
                        if ((*wif)->status != RowStatus_active)
                                continue;
                        *phy = 1;
                        return ((*wif)->txparams.params);
                }
                return (NULL);
        }

        if (wlan_phy_index_decode(oid, sub, wname, phy) < 0)
                return (NULL);

        if (*phy == 0 || (*wif = wlan_find_interface(wname)) == NULL)
                return (NULL);

        if (++(*phy) <= IEEE80211_MODE_MAX)
                return ((*wif)->txparams.params + *phy - 1);

        *phy = 1;
        while ((*wif = wlan_next_interface(*wif)) != NULL)
                if ((*wif)->status == RowStatus_active)
                        return ((*wif)->txparams.params);

        return (NULL);
}

/*
 * Calls for manipulating the scan results for a wireless interface.
 */
static void
wlan_scan_free_results(struct wlan_iface *wif)
{
        struct wlan_scan_result *sr;

        while ((sr = SLIST_FIRST(&wif->scanlist)) != NULL) {
                SLIST_REMOVE_HEAD(&wif->scanlist, wsr);
                free(sr);
        }

        SLIST_INIT(&wif->scanlist);
}

static struct wlan_scan_result *
wlan_scan_find_result(struct wlan_iface *wif, uint8_t *ssid, uint8_t *bssid)
{
        struct wlan_scan_result *sr;

        SLIST_FOREACH(sr, &wif->scanlist, wsr)
                if (strlen(ssid) == strlen(sr->ssid) &&
                    strcmp(sr->ssid, ssid) == 0 &&
                    memcmp(sr->bssid, bssid, IEEE80211_ADDR_LEN) == 0)
                        break;

        return (sr);
}

struct wlan_scan_result *
wlan_scan_new_result(const uint8_t *ssid, const uint8_t *bssid)
{
        struct wlan_scan_result *sr;

        sr = (struct wlan_scan_result *)malloc(sizeof(*sr));
        if (sr == NULL)
                return (NULL);

        memset(sr, 0, sizeof(*sr));
        if (ssid[0] != '\0')
                strlcpy(sr->ssid, ssid, IEEE80211_NWID_LEN + 1);
        memcpy(sr->bssid, bssid, IEEE80211_ADDR_LEN);

        return (sr);
}

void
wlan_scan_free_result(struct wlan_scan_result *sr)
{
        free(sr);
}

static int
wlan_scan_compare_result(struct wlan_scan_result *sr1,
    struct wlan_scan_result *sr2)
{
        uint32_t i;

        if (strlen(sr1->ssid) < strlen(sr2->ssid))
                return (-1);
        if (strlen(sr1->ssid) > strlen(sr2->ssid))
                return (1);

        for (i = 0; i < strlen(sr1->ssid) && i < strlen(sr2->ssid); i++) {
                if (sr1->ssid[i] < sr2->ssid[i])
                        return (-1);
                if (sr1->ssid[i] > sr2->ssid[i])
                        return (1);
        }

        for (i = 0; i < IEEE80211_ADDR_LEN; i++) {
                if (sr1->bssid[i] < sr2->bssid[i])
                        return (-1);
                if (sr1->bssid[i] > sr2->bssid[i])
                        return (1);
        }

        return (0);
}

int
wlan_scan_add_result(struct wlan_iface *wif, struct wlan_scan_result *sr)
{
        struct wlan_scan_result *prev, *temp;

        SLIST_FOREACH(temp, &wif->scanlist, wsr)
                if (strlen(temp->ssid) == strlen(sr->ssid) &&
                    strcmp(sr->ssid, temp->ssid) == 0 &&
                    memcmp(sr->bssid, temp->bssid, IEEE80211_ADDR_LEN) == 0)
                        return (-1);

        if ((prev = SLIST_FIRST(&wif->scanlist)) == NULL ||
            wlan_scan_compare_result(sr, prev) < 0) {
                SLIST_INSERT_HEAD(&wif->scanlist, sr, wsr);
                return (0);
        }

        SLIST_FOREACH(temp, &wif->scanlist, wsr) {
                if (wlan_scan_compare_result(sr, temp) < 0)
                        break;
                prev = temp;
        }

        SLIST_INSERT_AFTER(prev, sr, wsr);
        return (0);
}

static void
wlan_scan_update_results(void)
{
        struct wlan_iface *wif;

        if ((time(NULL) - wlan_scanlist_age) <= WLAN_LIST_MAXAGE)
                return;

        for (wif = wlan_first_interface(); wif != NULL;
            wif = wlan_next_interface(wif)) {
                if (wif->status != RowStatus_active)
                        continue;
                wlan_scan_free_results(wif);
                (void)wlan_get_scan_results(wif);
        }
        wlan_scanlist_age = time(NULL);
}

static int
wlan_scanr_index_decode(const struct asn_oid *oid, uint sub,
    char *wname, uint8_t *ssid, uint8_t *bssid)
{
        uint32_t i;
        int offset;

        if (oid->subs[sub] >= IFNAMSIZ)
                return (-1);
        for (i = 0; i < oid->subs[sub]; i++)
                wname[i] = oid->subs[sub + i + 1];
        wname[oid->subs[sub]] = '\0';

        offset = sub + oid->subs[sub] + 1;
        if (oid->subs[offset] > IEEE80211_NWID_LEN)
                return (-1);
        for (i = 0; i < oid->subs[offset]; i++)
                ssid[i] = oid->subs[offset + i + 1];
        ssid[i] = '\0';

        offset = sub + oid->subs[sub] + oid->subs[offset] + 2;
        if (oid->subs[offset] != IEEE80211_ADDR_LEN)
                return (-1);
        for (i = 0; i < IEEE80211_ADDR_LEN; i++)
                bssid[i] = oid->subs[offset + i + 1];

        return (0);
}

static void
wlan_append_scanr_index(struct asn_oid *oid, uint sub, char *wname,
    uint8_t *ssid, uint8_t *bssid)
{
        uint32_t i;

        oid->len = sub + strlen(wname) + strlen(ssid) + IEEE80211_ADDR_LEN + 3;
        oid->subs[sub] = strlen(wname);
        for (i = 1; i <= strlen(wname); i++)
                oid->subs[sub + i] = wname[i - 1];

        sub += strlen(wname) + 1;
        oid->subs[sub] = strlen(ssid);
        for (i = 1; i <= strlen(ssid); i++)
                oid->subs[sub + i] = ssid[i - 1];

        sub += strlen(ssid) + 1;
        oid->subs[sub] = IEEE80211_ADDR_LEN;
        for (i = 1; i <= IEEE80211_ADDR_LEN; i++)
                oid->subs[sub + i] = bssid[i - 1];
}

static struct wlan_scan_result *
wlan_get_scanr(const struct asn_oid *oid, uint sub, struct wlan_iface **wif)
{
        char wname[IFNAMSIZ];
        uint8_t ssid[IEEE80211_NWID_LEN + 1];
        uint8_t bssid[IEEE80211_ADDR_LEN];

        if (wlan_scanr_index_decode(oid, sub, wname, ssid, bssid) < 0)
                return (NULL);

        if ((*wif = wlan_find_interface(wname)) == NULL)
                return (NULL);

        return (wlan_scan_find_result(*wif, ssid, bssid));
}

static struct wlan_scan_result *
wlan_get_next_scanr(const struct asn_oid *oid, uint sub,
    struct wlan_iface **wif)
{
        char wname[IFNAMSIZ];
        uint8_t ssid[IEEE80211_NWID_LEN + 1];
        uint8_t bssid[IEEE80211_ADDR_LEN];
        struct wlan_scan_result *sr;

        if (oid->len - sub == 0) {
                for (*wif = wlan_first_interface(); *wif != NULL;
                    *wif = wlan_next_interface(*wif)) {
                        sr = SLIST_FIRST(&(*wif)->scanlist);
                        if (sr != NULL)
                                return (sr);
                }
                return (NULL);
        }

        if (wlan_scanr_index_decode(oid, sub, wname, ssid, bssid) < 0 ||
            (*wif = wlan_find_interface(wname)) == NULL ||
            (sr = wlan_scan_find_result(*wif, ssid, bssid)) == NULL)
                return (NULL);

        if ((sr = SLIST_NEXT(sr, wsr)) != NULL)
                return (sr);

        while ((*wif = wlan_next_interface(*wif)) != NULL)
                if ((sr = SLIST_FIRST(&(*wif)->scanlist)) != NULL)
                        break;

        return (sr);
}

/*
 * MAC Access Control.
 */
static void
wlan_mac_free_maclist(struct wlan_iface *wif)
{
        struct wlan_mac_mac *wmm;

        while ((wmm = SLIST_FIRST(&wif->mac_maclist)) != NULL) {
                SLIST_REMOVE_HEAD(&wif->mac_maclist, wm);
                free(wmm);
        }

        SLIST_INIT(&wif->mac_maclist);
}

static struct wlan_mac_mac *
wlan_mac_find_mac(struct wlan_iface *wif, uint8_t *mac)
{
        struct wlan_mac_mac *wmm;

        SLIST_FOREACH(wmm, &wif->mac_maclist, wm)
                if (memcmp(wmm->mac, mac, IEEE80211_ADDR_LEN) == 0)
                        break;

        return (wmm);
}

struct wlan_mac_mac *
wlan_mac_new_mac(const uint8_t *mac)
{
        struct wlan_mac_mac *wmm;

        if ((wmm = (struct wlan_mac_mac *)malloc(sizeof(*wmm))) == NULL)
                return (NULL);

        memset(wmm, 0, sizeof(*wmm));
        memcpy(wmm->mac, mac, IEEE80211_ADDR_LEN);
        wmm->mac_status = RowStatus_notReady;

        return (wmm);
}

void
wlan_mac_free_mac(struct wlan_mac_mac *wmm)
{
        free(wmm);
}

int
wlan_mac_add_mac(struct wlan_iface *wif, struct wlan_mac_mac *wmm)
{
        struct wlan_mac_mac *temp, *prev;

        SLIST_FOREACH(temp, &wif->mac_maclist, wm)
                if (memcmp(temp->mac, wmm->mac, IEEE80211_ADDR_LEN) == 0)
                        return (-1);

        if ((prev = SLIST_FIRST(&wif->mac_maclist)) == NULL ||
            memcmp(wmm->mac, prev->mac,IEEE80211_ADDR_LEN) < 0) {
                SLIST_INSERT_HEAD(&wif->mac_maclist, wmm, wm);
                return (0);
        }

        SLIST_FOREACH(temp, &wif->mac_maclist, wm) {
                if (memcmp(wmm->mac, temp->mac, IEEE80211_ADDR_LEN) < 0)
                        break;
                prev = temp;
        }

        SLIST_INSERT_AFTER(prev, wmm, wm);
        return (0);
}

static int
wlan_mac_delete_mac(struct wlan_iface *wif, struct wlan_mac_mac *wmm)
{
        if (wmm->mac_status == RowStatus_active &&
            wlan_del_mac_acl_mac(wif, wmm) < 0)
                return (-1);

        SLIST_REMOVE(&wif->mac_maclist, wmm, wlan_mac_mac, wm);
        free(wmm);

        return (0);
}

static void
wlan_mac_update_aclmacs(void)
{
        struct wlan_iface *wif;
        struct wlan_mac_mac *wmm, *twmm;

        if ((time(NULL) - wlan_maclist_age) <= WLAN_LIST_MAXAGE)
                return;

        for (wif = wlan_first_interface(); wif != NULL;
            wif = wlan_next_interface(wif)) {
                if (wif->status != RowStatus_active)
                        continue;
                /*
                 * Nuke old entries - XXX - they are likely not to
                 * change often - reconsider.
                 */
                SLIST_FOREACH_SAFE(wmm, &wif->mac_maclist, wm, twmm)
                        if (wmm->mac_status == RowStatus_active) {
                                SLIST_REMOVE(&wif->mac_maclist, wmm,
                                    wlan_mac_mac, wm);
                                wlan_mac_free_mac(wmm);
                        }
                (void)wlan_get_mac_acl_macs(wif);
        }
        wlan_maclist_age = time(NULL);
}

static struct wlan_mac_mac *
wlan_get_acl_mac(const struct asn_oid *oid, uint sub, struct wlan_iface **wif)
{
        char wname[IFNAMSIZ];
        char mac[IEEE80211_ADDR_LEN];

        if (wlan_mac_index_decode(oid, sub, wname, mac) < 0)
                return (NULL);

        if ((*wif = wlan_find_interface(wname)) == NULL)
                return (NULL);

        return (wlan_mac_find_mac(*wif, mac));
}

static struct wlan_mac_mac *
wlan_get_next_acl_mac(const struct asn_oid *oid, uint sub,
    struct wlan_iface **wif)
{
        char wname[IFNAMSIZ];
        char mac[IEEE80211_ADDR_LEN];
        struct wlan_mac_mac *wmm;

        if (oid->len - sub == 0) {
                for (*wif = wlan_first_interface(); *wif != NULL;
                    *wif = wlan_next_interface(*wif)) {
                        wmm = SLIST_FIRST(&(*wif)->mac_maclist);
                        if (wmm != NULL)
                                return (wmm);
                }
                return (NULL);
        }

        if (wlan_mac_index_decode(oid, sub, wname, mac) < 0 ||
            (*wif = wlan_find_interface(wname)) == NULL ||
            (wmm = wlan_mac_find_mac(*wif, mac)) == NULL)
                return (NULL);

        if ((wmm = SLIST_NEXT(wmm, wm)) != NULL)
                return (wmm);

        while ((*wif = wlan_next_interface(*wif)) != NULL)
                if ((wmm = SLIST_FIRST(&(*wif)->mac_maclist)) != NULL)
                        break;

        return (wmm);
}

static int
wlan_acl_mac_set_status(struct snmp_context *ctx, struct snmp_value *val,
    uint sub)
{
        char wname[IFNAMSIZ];
        uint8_t mac[IEEE80211_ADDR_LEN];
        struct wlan_iface *wif;
        struct wlan_mac_mac *macl;

        if (wlan_mac_index_decode(&val->var, sub, wname, mac) < 0)
                return (SNMP_ERR_GENERR);
        macl = wlan_get_acl_mac(&val->var, sub, &wif);

        switch (val->v.integer) {
        case RowStatus_createAndGo:
                if (macl != NULL)
                        return (SNMP_ERR_INCONS_NAME);
                break;
        case RowStatus_destroy:
                if (macl == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                ctx->scratch->int1 = RowStatus_active;
                return (SNMP_ERR_NOERROR);
        default:
                return (SNMP_ERR_INCONS_VALUE);
        }


        if (wif == NULL || !wif->macsupported)
                return (SNMP_ERR_INCONS_VALUE);

        if ((macl = wlan_mac_new_mac((const uint8_t *)mac)) == NULL)
                return (SNMP_ERR_GENERR);

        ctx->scratch->int1 = RowStatus_destroy;

        if (wlan_mac_add_mac(wif, macl) < 0) {
                wlan_mac_free_mac(macl);
                return (SNMP_ERR_GENERR);
        }

        ctx->scratch->int1 = RowStatus_destroy;
        if (wlan_add_mac_acl_mac(wif, macl) < 0) {
                (void)wlan_mac_delete_mac(wif, macl);
                return (SNMP_ERR_GENERR);
        }

        return (SNMP_ERR_NOERROR);
}

/*
 * Wireless interfaces operating as mesh points.
 */
static struct wlan_iface *
wlan_mesh_first_interface(void)
{
        struct wlan_iface *wif;

        SLIST_FOREACH(wif, &wlan_ifaces, w_if)
                if (wif->mode == WlanIfaceOperatingModeType_meshPoint &&
                    wif->status == RowStatus_active)
                        break;

        return (wif);
}

static struct wlan_iface *
wlan_mesh_next_interface(struct wlan_iface *wif)
{
        struct wlan_iface *nwif;

        while ((nwif = wlan_next_interface(wif)) != NULL) {
                if (nwif->mode == WlanIfaceOperatingModeType_meshPoint &&
                    nwif->status == RowStatus_active)
                        break;
                wif = nwif;
        }

        return (nwif);
}

static struct wlan_iface *
wlan_mesh_get_iface(const struct asn_oid *oid, uint sub)
{
        struct wlan_iface *wif;

        if ((wif = wlan_get_interface(oid, sub)) == NULL)
                return (NULL);

        if (wif->mode != WlanIfaceOperatingModeType_meshPoint)
                return (NULL);

        return (wif);
}

static struct wlan_iface *
wlan_mesh_get_next_iface(const struct asn_oid *oid, uint sub)
{
        uint32_t i;
        uint8_t wname[IFNAMSIZ];
        struct wlan_iface *wif;

        if (oid->len - sub == 0)
                return (wlan_mesh_first_interface());

        if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ)
                return (NULL);

        memset(wname, 0, IFNAMSIZ);
        for (i = 0; i < oid->subs[sub]; i++)
                wname[i] = oid->subs[sub + i + 1];
        wname[i] = '\0';

        if ((wif = wlan_find_interface(wname)) == NULL)
                return (NULL);

        return (wlan_mesh_next_interface(wif));
}

/*
 * The neighbors of wireless interfaces operating as mesh points.
 */
static struct wlan_peer *
wlan_mesh_get_peer(const struct asn_oid *oid, uint sub, struct wlan_iface **wif)
{
        char wname[IFNAMSIZ];
        uint8_t pmac[IEEE80211_ADDR_LEN];

        if (wlan_mac_index_decode(oid, sub, wname, pmac) < 0)
                return (NULL);

        if ((*wif = wlan_find_interface(wname)) == NULL ||
            (*wif)->mode != WlanIfaceOperatingModeType_meshPoint)
                return (NULL);

        return (wlan_find_peer(*wif, pmac));
}

static struct wlan_peer *
wlan_mesh_get_next_peer(const struct asn_oid *oid, uint sub, struct wlan_iface **wif)
{
        char wname[IFNAMSIZ];
        char pmac[IEEE80211_ADDR_LEN];
        struct wlan_peer *wip;

        if (oid->len - sub == 0) {
                for (*wif = wlan_mesh_first_interface(); *wif != NULL;
                    *wif = wlan_mesh_next_interface(*wif)) {
                        wip = SLIST_FIRST(&(*wif)->peerlist);
                        if (wip != NULL)
                                return (wip);
                }
                return (NULL);
        }

        if (wlan_mac_index_decode(oid, sub, wname, pmac) < 0 ||
            (*wif = wlan_find_interface(wname)) == NULL ||
            (*wif)->mode != WlanIfaceOperatingModeType_meshPoint ||
            (wip = wlan_find_peer(*wif, pmac)) == NULL)
                return (NULL);

        if ((wip = SLIST_NEXT(wip, wp)) != NULL)
                return (wip);

        while ((*wif = wlan_mesh_next_interface(*wif)) != NULL)
                if ((wip = SLIST_FIRST(&(*wif)->peerlist)) != NULL)
                        break;

        return (wip);
}

/*
 * Mesh routing table.
 */
static void
wlan_mesh_free_routes(struct wlan_iface *wif)
{
        struct wlan_mesh_route *wmr;

        while ((wmr = SLIST_FIRST(&wif->mesh_routelist)) != NULL) {
                SLIST_REMOVE_HEAD(&wif->mesh_routelist, wr);
                free(wmr);
        }

        SLIST_INIT(&wif->mesh_routelist);
}

static struct wlan_mesh_route *
wlan_mesh_find_route(struct wlan_iface *wif, uint8_t *dstmac)
{
        struct wlan_mesh_route *wmr;

        if (wif->mode != WlanIfaceOperatingModeType_meshPoint)
                return (NULL);

        SLIST_FOREACH(wmr, &wif->mesh_routelist, wr)
                if (memcmp(wmr->imroute.imr_dest, dstmac,
                    IEEE80211_ADDR_LEN) == 0)
                        break;

        return (wmr);
}

struct wlan_mesh_route *
wlan_mesh_new_route(const uint8_t *dstmac)
{
        struct wlan_mesh_route *wmr;

        if ((wmr = (struct wlan_mesh_route *)malloc(sizeof(*wmr))) == NULL)
                return (NULL);

        memset(wmr, 0, sizeof(*wmr));
        memcpy(wmr->imroute.imr_dest, dstmac, IEEE80211_ADDR_LEN);
        wmr->mroute_status = RowStatus_notReady;

        return (wmr);
}

void
wlan_mesh_free_route(struct wlan_mesh_route *wmr)
{
        free(wmr);
}

int
wlan_mesh_add_rtentry(struct wlan_iface *wif, struct wlan_mesh_route *wmr)
{
        struct wlan_mesh_route *temp, *prev;

        SLIST_FOREACH(temp, &wif->mesh_routelist, wr)
                if (memcmp(temp->imroute.imr_dest, wmr->imroute.imr_dest,
                    IEEE80211_ADDR_LEN) == 0)
                        return (-1);

        if ((prev = SLIST_FIRST(&wif->mesh_routelist)) == NULL ||
            memcmp(wmr->imroute.imr_dest, prev->imroute.imr_dest,
            IEEE80211_ADDR_LEN) < 0) {
                SLIST_INSERT_HEAD(&wif->mesh_routelist, wmr, wr);
                return (0);
        }

        SLIST_FOREACH(temp, &wif->mesh_routelist, wr) {
                if (memcmp(wmr->imroute.imr_dest, temp->imroute.imr_dest,
                    IEEE80211_ADDR_LEN) < 0)
                        break;
                prev = temp;
        }

        SLIST_INSERT_AFTER(prev, wmr, wr);
        return (0);
}

static int
wlan_mesh_delete_route(struct wlan_iface *wif, struct wlan_mesh_route *wmr)
{
        if (wmr->mroute_status == RowStatus_active &&
            wlan_mesh_del_route(wif, wmr) < 0)
                return (-1);

        SLIST_REMOVE(&wif->mesh_routelist, wmr, wlan_mesh_route, wr);
        free(wmr);

        return (0);
}

static void
wlan_mesh_update_routes(void)
{
        struct wlan_iface *wif;
        struct wlan_mesh_route *wmr, *twmr;

        if ((time(NULL) - wlan_mrlist_age) <= WLAN_LIST_MAXAGE)
                return;

        for (wif = wlan_mesh_first_interface(); wif != NULL;
            wif = wlan_mesh_next_interface(wif)) {
                /*
                 * Nuke old entries - XXX - they are likely not to
                 * change often - reconsider.
                 */
                SLIST_FOREACH_SAFE(wmr, &wif->mesh_routelist, wr, twmr)
                        if (wmr->mroute_status == RowStatus_active) {
                                SLIST_REMOVE(&wif->mesh_routelist, wmr,
                                    wlan_mesh_route, wr);
                                wlan_mesh_free_route(wmr);
                        }
                (void)wlan_mesh_get_routelist(wif);
        }
        wlan_mrlist_age = time(NULL);
}

static struct wlan_mesh_route *
wlan_mesh_get_route(const struct asn_oid *oid, uint sub, struct wlan_iface **wif)
{
        char wname[IFNAMSIZ];
        char dstmac[IEEE80211_ADDR_LEN];

        if (wlan_mac_index_decode(oid, sub, wname, dstmac) < 0)
                return (NULL);

        if ((*wif = wlan_find_interface(wname)) == NULL)
                return (NULL);

        return (wlan_mesh_find_route(*wif, dstmac));
}

static struct wlan_mesh_route *
wlan_mesh_get_next_route(const struct asn_oid *oid, uint sub,
    struct wlan_iface **wif)
{
        char wname[IFNAMSIZ];
        char dstmac[IEEE80211_ADDR_LEN];
        struct wlan_mesh_route *wmr;

        if (oid->len - sub == 0) {
                for (*wif = wlan_mesh_first_interface(); *wif != NULL;
                    *wif = wlan_mesh_next_interface(*wif)) {
                        wmr = SLIST_FIRST(&(*wif)->mesh_routelist);
                        if (wmr != NULL)
                                return (wmr);
                }
                return (NULL);
        }

        if (wlan_mac_index_decode(oid, sub, wname, dstmac) < 0 ||
            (*wif = wlan_find_interface(wname)) == NULL ||
            (wmr = wlan_mesh_find_route(*wif, dstmac)) == NULL)
                return (NULL);

        if ((wmr = SLIST_NEXT(wmr, wr)) != NULL)
                return (wmr);

        while ((*wif = wlan_mesh_next_interface(*wif)) != NULL)
                if ((wmr = SLIST_FIRST(&(*wif)->mesh_routelist)) != NULL)
                        break;

        return (wmr);
}

static int
wlan_mesh_route_set_status(struct snmp_context *ctx, struct snmp_value *val,
    uint sub)
{
        char wname[IFNAMSIZ];
        char mac[IEEE80211_ADDR_LEN];
        struct wlan_mesh_route *wmr;
        struct wlan_iface *wif;

        if (wlan_mac_index_decode(&val->var, sub, wname, mac) < 0)
                return (SNMP_ERR_GENERR);
        wmr = wlan_mesh_get_route(&val->var, sub, &wif);

        switch (val->v.integer) {
        case RowStatus_createAndGo:
                if (wmr != NULL)
                        return (SNMP_ERR_INCONS_NAME);
                break;
        case RowStatus_destroy:
                if (wmr == NULL)
                        return (SNMP_ERR_NOSUCHNAME);
                ctx->scratch->int1 = RowStatus_active;
                return (SNMP_ERR_NOERROR);
        default:
                return (SNMP_ERR_INCONS_VALUE);
        }

        if ((wif = wlan_find_interface(wname)) == NULL)
                return (SNMP_ERR_INCONS_NAME);

        if ((wmr = wlan_mesh_new_route(mac)) == NULL)
                return (SNMP_ERR_GENERR);

        if (wlan_mesh_add_rtentry(wif, wmr) < 0) {
                wlan_mesh_free_route(wmr);
                return (SNMP_ERR_GENERR);
        }

        ctx->scratch->int1 = RowStatus_destroy;
        if (wlan_mesh_add_route(wif, wmr) < 0) {
                (void)wlan_mesh_delete_route(wif, wmr);
                return (SNMP_ERR_GENERR);
        }

        return (SNMP_ERR_NOERROR);
}

/*
 * Wlan snmp module initialization hook.
 * Returns 0 on success, < 0 on error.
 */
static int
wlan_init(struct lmodule * mod __unused, int argc __unused,
     char *argv[] __unused)
{
        if (wlan_kmodules_load() < 0)
                return (-1);

        if (wlan_ioctl_init() < 0)
                return (-1);

        /* Register for new interface creation notifications. */
        if (mib_register_newif(wlan_attach_newif, wlan_module)) {
                syslog(LOG_ERR, "Cannot register newif function: %s",
                    strerror(errno));
                return (-1);
        }

        return (0);
}

/*
 * Wlan snmp module finalization hook.
 */
static int
wlan_fini(void)
{
        mib_unregister_newif(wlan_module);
        or_unregister(reg_wlan);

        /* XXX: Cleanup! */
        wlan_free_iflist();

        return (0);
}

/*
 * Refetch all available data from the kernel.
 */
static void
wlan_update_data(void *arg __unused)
{
}

/*
 * Wlan snmp module start operation.
 */
static void
wlan_start(void)
{
        struct mibif *ifp;

        reg_wlan = or_register(&oid_wlan,
            "The MIB module for managing wireless networking.", wlan_module);

         /* Add the existing wlan interfaces. */
         for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp))
                wlan_attach_newif(ifp);

        wlan_data_timer = timer_start_repeat(wlan_poll_ticks,
            wlan_poll_ticks, wlan_update_data, NULL, wlan_module);
}

/*
 * Dump the Wlan snmp module data on SIGUSR1.
 */
static void
wlan_dump(void)
{
        /* XXX: Print some debug info to syslog. */
        struct wlan_iface *wif;

        for (wif = wlan_first_interface(); wif != NULL;
            wif = wlan_next_interface(wif))
                syslog(LOG_ERR, "wlan iface %s", wif->wname);
}

const char wlan_comment[] = \
"This module implements the BEGEMOT MIB for wireless networking.";

const struct snmp_module config = {
        .comment =      wlan_comment,
        .init =         wlan_init,
        .fini =         wlan_fini,
        .start =        wlan_start,
        .tree =         wlan_ctree,
        .dump =         wlan_dump,
        .tree_size =    wlan_CTREE_SIZE,
};