diff options
Diffstat (limited to 'usr.sbin/bsnmpd/modules/snmp_wlan/wlan_snmp.c')
-rw-r--r-- | usr.sbin/bsnmpd/modules/snmp_wlan/wlan_snmp.c | 4513 |
1 files changed, 4513 insertions, 0 deletions
diff --git a/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_snmp.c b/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_snmp.c new file mode 100644 index 0000000..ec94ac6 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_wlan/wlan_snmp.c @@ -0,0 +1,4513 @@ +/*- + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * 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. + * + * $FreeBSD$ + */ + +#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> + +#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, +}; |