/*-
 * 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,
};
