diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 18:02:34 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 18:02:34 +0000 |
commit | fadeddfbb2aa38a980dd959b5ec1ffba7afd43cb (patch) | |
tree | a7bde6111c84ea64619656a38fba50909fa0bf60 /src/daemon/agent.c | |
parent | Initial commit. (diff) | |
download | lldpd-fadeddfbb2aa38a980dd959b5ec1ffba7afd43cb.tar.xz lldpd-fadeddfbb2aa38a980dd959b5ec1ffba7afd43cb.zip |
Adding upstream version 1.0.18.upstream/1.0.18upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/daemon/agent.c')
-rw-r--r-- | src/daemon/agent.c | 1939 |
1 files changed, 1939 insertions, 0 deletions
diff --git a/src/daemon/agent.c b/src/daemon/agent.c new file mode 100644 index 0000000..cd631a8 --- /dev/null +++ b/src/daemon/agent.c @@ -0,0 +1,1939 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lldpd.h" + +#include <assert.h> + +#include "agent.h" + +#if HAVE_NET_SNMP_AGENT_UTIL_FUNCS_H +# include <net-snmp/agent/util_funcs.h> +#else +/* The above header may be buggy. We just need this function. */ +int header_generic(struct variable *, oid *, size_t *, int, size_t *, WriteMethod **); +#endif + +/* For net-snmp */ +extern int register_sysORTable(oid *, size_t, const char *); +extern int unregister_sysORTable(oid *, size_t); + +/* Global variable because no way to pass it as argument. Should not be used + * elsewhere. */ +#define scfg agent_scfg +struct lldpd *agent_scfg; + +static uint8_t +swap_bits(uint8_t n) +{ + n = ((n & 0xF0) >> 4) | ((n & 0x0F) << 4); + n = ((n & 0xCC) >> 2) | ((n & 0x33) << 2); + n = ((n & 0xAA) >> 1) | ((n & 0x55) << 1); + + return n; +}; + +extern struct timeval starttime; +static long int +lastchange(struct lldpd_port *port) +{ + if (port->p_lastchange > starttime.tv_sec) + return (port->p_lastchange - starttime.tv_sec) * 100; + return 0; +} + +/* ------------- + Helper functions to build header_*indexed_table() functions. + Those functions keep an internal state. They are not reentrant! +*/ +struct header_index { + struct variable *vp; + oid *name; /* Requested/returned OID */ + size_t *length; /* Length of above OID */ + int exact; + oid best[MAX_OID_LEN]; /* Best OID */ + size_t best_len; /* Best OID length */ + void *entity; /* Best entity */ +}; +static struct header_index header_idx; + +static int +header_index_init(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + /* If the requested OID name is less than OID prefix we + handle, adjust it to our prefix. */ + if ((snmp_oid_compare(name, *length, vp->name, vp->namelen)) < 0) { + memcpy(name, vp->name, sizeof(oid) * vp->namelen); + *length = vp->namelen; + } + /* Now, we can only handle OID matching our prefix. Those two + tests are not really necessary since NetSNMP won't give us + OID "above" our prefix. But this makes unit tests + easier. */ + if (*length < vp->namelen) return 0; + if (memcmp(name, vp->name, vp->namelen * sizeof(oid))) return 0; + + if (write_method != NULL) *write_method = 0; + *var_len = sizeof(long); + + /* Initialize our header index structure */ + header_idx.vp = vp; + header_idx.name = name; + header_idx.length = length; + header_idx.exact = exact; + header_idx.best_len = 0; + header_idx.entity = NULL; + return 1; +} + +static int +header_index_add(oid *index, size_t len, void *entity) +{ + int result; + oid *target; + size_t target_len; + + target = header_idx.name + header_idx.vp->namelen; + target_len = *header_idx.length - header_idx.vp->namelen; + if ((result = snmp_oid_compare(index, len, target, target_len)) < 0) + return 0; /* Too small. */ + if (result == 0) return header_idx.exact; + if (header_idx.best_len == 0 || + (snmp_oid_compare(index, len, header_idx.best, header_idx.best_len) < 0)) { + memcpy(header_idx.best, index, sizeof(oid) * len); + header_idx.best_len = len; + header_idx.entity = entity; + } + return 0; /* No best match yet. */ +} + +static void * +header_index_best() +{ + if (header_idx.entity == NULL) return NULL; + if (header_idx.exact) return NULL; + memcpy(header_idx.name + header_idx.vp->namelen, header_idx.best, + sizeof(oid) * header_idx.best_len); + *header_idx.length = header_idx.vp->namelen + header_idx.best_len; + return header_idx.entity; +} +/* ----------------------------- */ + +static struct lldpd_hardware * +header_portindexed_table(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_hardware *hardware; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + oid index[1] = { hardware->h_ifindex }; + if (header_index_add(index, 1, hardware)) return hardware; + } + return header_index_best(); +} + +#ifdef ENABLE_LLDPMED +static struct lldpd_med_policy * +header_pmedindexed_policy_table(struct variable *vp, oid *name, size_t *length, + int exact, size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_hardware *hardware; + int i; + oid index[2]; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + for (i = 0; i < LLDP_MED_APPTYPE_LAST; i++) { + if (hardware->h_lport.p_med_policy[i].type != i + 1) continue; + index[0] = hardware->h_ifindex; + index[1] = i + 1; + if (header_index_add(index, 2, + &hardware->h_lport.p_med_policy[i])) + return &hardware->h_lport.p_med_policy[i]; + } + } + return header_index_best(); +} + +static struct lldpd_med_loc * +header_pmedindexed_location_table(struct variable *vp, oid *name, size_t *length, + int exact, size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_hardware *hardware; + int i; + oid index[2]; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + for (i = 0; i < LLDP_MED_LOCFORMAT_LAST; i++) { + if (hardware->h_lport.p_med_location[i].format != i + 1) + continue; + index[0] = hardware->h_ifindex; + index[1] = i + 2; + if (header_index_add(index, 2, + &hardware->h_lport.p_med_location[i])) + return &hardware->h_lport.p_med_location[i]; + } + } + return header_index_best(); +} +#endif + +static struct lldpd_port * +header_tprindexed_table(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method, int withmed) +{ + struct lldpd_hardware *hardware; + struct lldpd_port *port; + oid index[3]; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + TAILQ_FOREACH (port, &hardware->h_rports, p_entries) { + if (SMART_HIDDEN(port)) continue; +#ifdef ENABLE_LLDPMED + if (withmed && !port->p_chassis->c_med_cap_available) continue; +#endif + index[0] = lastchange(port); + index[1] = hardware->h_ifindex; + index[2] = port->p_chassis->c_index; + if (header_index_add(index, 3, port)) return port; + } + } + return header_index_best(); +} + +static struct lldpd_mgmt * +header_ipindexed_table(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_chassis *chassis = LOCAL_CHASSIS(scfg); + struct lldpd_mgmt *mgmt; + oid index[2 + 16]; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (mgmt, &chassis->c_mgmt, m_entries) { + int i; + switch (mgmt->m_family) { + case LLDPD_AF_IPV4: + index[0] = 1; + break; + case LLDPD_AF_IPV6: + index[0] = 2; + break; + default: + assert(0); + } + index[1] = mgmt->m_addrsize; + if (index[1] > sizeof(index) - 2) continue; /* Odd... */ + for (i = 0; i < index[1]; i++) + index[i + 2] = mgmt->m_addr.octets[i]; + if (header_index_add(index, 2 + index[1], mgmt)) return mgmt; + } + + return header_index_best(); +} + +static struct lldpd_mgmt * +header_tpripindexed_table(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_hardware *hardware; + struct lldpd_port *port; + struct lldpd_mgmt *mgmt; + oid index[5 + 16]; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + TAILQ_FOREACH (port, &hardware->h_rports, p_entries) { + if (SMART_HIDDEN(port)) continue; + TAILQ_FOREACH (mgmt, &port->p_chassis->c_mgmt, m_entries) { + int i; + index[0] = lastchange(port); + index[1] = hardware->h_ifindex; + index[2] = port->p_chassis->c_index; + switch (mgmt->m_family) { + case LLDPD_AF_IPV4: + index[3] = 1; + break; + case LLDPD_AF_IPV6: + index[3] = 2; + break; + default: + assert(0); + } + index[4] = mgmt->m_addrsize; + if (index[4] > sizeof(index) - 5) continue; /* Odd... */ + for (i = 0; i < index[4]; i++) + index[i + 5] = mgmt->m_addr.octets[i]; + if (header_index_add(index, 5 + index[4], mgmt)) + return mgmt; + } + } + } + return header_index_best(); +} + +#ifdef ENABLE_CUSTOM +static struct lldpd_custom * +header_tprcustomindexed_table(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_hardware *hardware; + struct lldpd_port *port; + struct lldpd_custom *custom; + oid index[8]; + oid idx; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + TAILQ_FOREACH (port, &hardware->h_rports, p_entries) { + if (SMART_HIDDEN(port)) continue; + idx = 1; + TAILQ_FOREACH (custom, &port->p_custom_list, next) { + index[0] = lastchange(port); + index[1] = hardware->h_ifindex; + index[2] = port->p_chassis->c_index; + index[3] = custom->oui[0]; + index[4] = custom->oui[1]; + index[5] = custom->oui[2]; + index[6] = custom->subtype; + index[7] = idx++; + if (header_index_add(index, 8, custom)) return custom; + } + } + } + return header_index_best(); +} +#endif + +#ifdef ENABLE_LLDPMED +# define TPR_VARIANT_MED_POLICY 2 +# define TPR_VARIANT_MED_LOCATION 3 +static void * +header_tprmedindexed_table(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method, int variant) +{ + struct lldpd_hardware *hardware; + struct lldpd_port *port; + int j; + oid index[4]; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + TAILQ_FOREACH (port, &hardware->h_rports, p_entries) { + if (SMART_HIDDEN(port)) continue; + if (!port->p_chassis->c_med_cap_available) continue; + switch (variant) { + case TPR_VARIANT_MED_POLICY: + for (j = 0; j < LLDP_MED_APPTYPE_LAST; j++) { + if (port->p_med_policy[j].type != j + 1) + continue; + index[0] = lastchange(port); + index[1] = hardware->h_ifindex; + index[2] = port->p_chassis->c_index; + index[3] = j + 1; + if (header_index_add(index, 4, + &port->p_med_policy[j])) + return &port->p_med_policy[j]; + } + break; + case TPR_VARIANT_MED_LOCATION: + for (j = 0; j < LLDP_MED_LOCFORMAT_LAST; j++) { + if (port->p_med_location[j].format != j + 1) + continue; + index[0] = lastchange(port); + index[1] = hardware->h_ifindex; + index[2] = port->p_chassis->c_index; + index[3] = j + 2; + if (header_index_add(index, 4, + &port->p_med_location[j])) + return &port->p_med_location[j]; + } + break; + } + } + } + return header_index_best(); +} +#endif + +#ifdef ENABLE_DOT1 +static struct lldpd_vlan * +header_pvindexed_table(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_hardware *hardware; + struct lldpd_vlan *vlan; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + TAILQ_FOREACH (vlan, &hardware->h_lport.p_vlans, v_entries) { + oid index[2] = { hardware->h_ifindex, vlan->v_vid }; + if (header_index_add(index, 2, vlan)) return vlan; + } + } + return header_index_best(); +} + +static struct lldpd_vlan * +header_tprvindexed_table(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_hardware *hardware; + struct lldpd_port *port; + struct lldpd_vlan *vlan; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + TAILQ_FOREACH (port, &hardware->h_rports, p_entries) { + if (SMART_HIDDEN(port)) continue; + TAILQ_FOREACH (vlan, &port->p_vlans, v_entries) { + oid index[4] = { lastchange(port), hardware->h_ifindex, + port->p_chassis->c_index, vlan->v_vid }; + if (header_index_add(index, 4, vlan)) return vlan; + } + } + } + return header_index_best(); +} + +static struct lldpd_ppvid * +header_pppvidindexed_table(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_hardware *hardware; + struct lldpd_ppvid *ppvid; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + TAILQ_FOREACH (ppvid, &hardware->h_lport.p_ppvids, p_entries) { + oid index[2] = { hardware->h_ifindex, ppvid->p_ppvid }; + if (header_index_add(index, 2, ppvid)) return ppvid; + } + } + return header_index_best(); +} + +static struct lldpd_ppvid * +header_tprppvidindexed_table(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_hardware *hardware; + struct lldpd_port *port; + struct lldpd_ppvid *ppvid; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + TAILQ_FOREACH (port, &hardware->h_rports, p_entries) { + if (SMART_HIDDEN(port)) continue; + TAILQ_FOREACH (ppvid, &port->p_ppvids, p_entries) { + oid index[4] = { lastchange(port), hardware->h_ifindex, + port->p_chassis->c_index, ppvid->p_ppvid }; + if (header_index_add(index, 4, ppvid)) return ppvid; + } + } + } + return header_index_best(); +} + +static struct lldpd_pi * +header_ppiindexed_table(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_hardware *hardware; + struct lldpd_pi *pi; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + TAILQ_FOREACH (pi, &hardware->h_lport.p_pids, p_entries) { + oid index[2] = { hardware->h_ifindex, + frame_checksum((const u_char *)pi->p_pi, pi->p_pi_len, + 0) }; + if (header_index_add(index, 2, pi)) return pi; + } + } + return header_index_best(); +} + +static struct lldpd_pi * +header_tprpiindexed_table(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_hardware *hardware; + struct lldpd_port *port; + struct lldpd_pi *pi; + + if (!header_index_init(vp, name, length, exact, var_len, write_method)) + return NULL; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + TAILQ_FOREACH (port, &hardware->h_rports, p_entries) { + if (SMART_HIDDEN(port)) continue; + TAILQ_FOREACH (pi, &port->p_pids, p_entries) { + oid index[4] = { lastchange(port), hardware->h_ifindex, + port->p_chassis->c_index, + frame_checksum((const u_char *)pi->p_pi, + pi->p_pi_len, 0) }; + if (header_index_add(index, 4, pi)) return pi; + } + } + } + return header_index_best(); +} +#endif + +/* Scalars */ +#define LLDP_SNMP_TXINTERVAL 1 +#define LLDP_SNMP_TXMULTIPLIER 2 +#define LLDP_SNMP_REINITDELAY 3 +#define LLDP_SNMP_TXDELAY 4 +#define LLDP_SNMP_NOTIFICATION 5 +#define LLDP_SNMP_LASTUPDATE 6 +#define LLDP_SNMP_STATS_INSERTS 7 +#define LLDP_SNMP_STATS_DELETES 8 +#define LLDP_SNMP_STATS_DROPS 9 +#define LLDP_SNMP_STATS_AGEOUTS 10 +/* Chassis */ +#define LLDP_SNMP_CIDSUBTYPE 1 +#define LLDP_SNMP_CID 2 +#define LLDP_SNMP_SYSNAME 3 +#define LLDP_SNMP_SYSDESCR 4 +#define LLDP_SNMP_SYSCAP_SUP 5 +#define LLDP_SNMP_SYSCAP_ENA 6 +/* Stats */ +#define LLDP_SNMP_STATS_TX 2 +#define LLDP_SNMP_STATS_RX_DISCARDED 4 +#define LLDP_SNMP_STATS_RX_ERRORS 5 +#define LLDP_SNMP_STATS_RX 6 +#define LLDP_SNMP_STATS_RX_TLVDISCARDED 7 +#define LLDP_SNMP_STATS_RX_TLVUNRECOGNIZED 8 +#define LLDP_SNMP_STATS_RX_AGEOUTS 9 +/* Ports */ +#define LLDP_SNMP_PIDSUBTYPE 2 +#define LLDP_SNMP_PID 3 +#define LLDP_SNMP_PORTDESC 4 +#define LLDP_SNMP_DOT3_AUTONEG_SUPPORT 5 +#define LLDP_SNMP_DOT3_AUTONEG_ENABLED 6 +#define LLDP_SNMP_DOT3_AUTONEG_ADVERTISED 7 +#define LLDP_SNMP_DOT3_AUTONEG_MAU 8 +#define LLDP_SNMP_DOT3_AGG_STATUS 9 +#define LLDP_SNMP_DOT3_AGG_ID 10 +#define LLDP_SNMP_DOT3_MFS 11 +#define LLDP_SNMP_DOT3_POWER_DEVICETYPE 12 +#define LLDP_SNMP_DOT3_POWER_SUPPORT 13 +#define LLDP_SNMP_DOT3_POWER_ENABLED 14 +#define LLDP_SNMP_DOT3_POWER_PAIRCONTROL 15 +#define LLDP_SNMP_DOT3_POWER_PAIRS 16 +#define LLDP_SNMP_DOT3_POWER_CLASS 17 +#define LLDP_SNMP_DOT3_POWER_TYPE 18 +#define LLDP_SNMP_DOT3_POWER_SOURCE 19 +#define LLDP_SNMP_DOT3_POWER_PRIORITY 20 +#define LLDP_SNMP_DOT3_POWER_REQUESTED 21 +#define LLDP_SNMP_DOT3_POWER_ALLOCATED 22 +#define LLDP_SNMP_DOT1_PVID 23 +/* Vlans */ +#define LLDP_SNMP_DOT1_VLANNAME 1 +/* Protocol VLAN IDs */ +#define LLDP_SNMP_DOT1_PPVLAN_SUPPORTED 2 +#define LLDP_SNMP_DOT1_PPVLAN_ENABLED 3 +/* Protocol Identity */ +#define LLDP_SNMP_DOT1_PI 1 +/* Management address */ +#define LLDP_SNMP_ADDR_LEN 1 +#define LLDP_SNMP_ADDR_IFSUBTYPE 2 +#define LLDP_SNMP_ADDR_IFID 3 +#define LLDP_SNMP_ADDR_OID 4 +/* Custom TLVs */ +#define LLDP_SNMP_ORG_DEF_INFO 1 +/* LLDP-MED */ +#define LLDP_SNMP_MED_CAP_AVAILABLE 1 +#define LLDP_SNMP_MED_CAP_ENABLED 2 +#define LLDP_SNMP_MED_CLASS 3 +#define LLDP_SNMP_MED_HW 4 +#define LLDP_SNMP_MED_FW 5 +#define LLDP_SNMP_MED_SW 6 +#define LLDP_SNMP_MED_SN 7 +#define LLDP_SNMP_MED_MANUF 8 +#define LLDP_SNMP_MED_MODEL 9 +#define LLDP_SNMP_MED_ASSET 10 +#define LLDP_SNMP_MED_POLICY_VID 11 +#define LLDP_SNMP_MED_POLICY_PRIO 12 +#define LLDP_SNMP_MED_POLICY_DSCP 13 +#define LLDP_SNMP_MED_POLICY_UNKNOWN 14 +#define LLDP_SNMP_MED_POLICY_TAGGED 15 +#define LLDP_SNMP_MED_LOCATION 16 +#define LLDP_SNMP_MED_POE_DEVICETYPE 17 +#define LLDP_SNMP_MED_POE_PSE_POWERVAL 19 +#define LLDP_SNMP_MED_POE_PSE_POWERSOURCE 20 +#define LLDP_SNMP_MED_POE_PSE_POWERPRIORITY 21 +#define LLDP_SNMP_MED_POE_PD_POWERVAL 22 +#define LLDP_SNMP_MED_POE_PD_POWERSOURCE 23 +#define LLDP_SNMP_MED_POE_PD_POWERPRIORITY 24 + +/* The following macro should be used anytime where the selected OID + is finally not returned (for example, when the associated data is + not available). In this case, we retry the function with the next + OID. */ +#define TRYNEXT(X) \ + do { \ + if (!exact && (name[*length - 1] < MAX_SUBID)) \ + return X(vp, name, length, exact, var_len, write_method); \ + return NULL; \ + } while (0) + +static u_char * +agent_h_scalars(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + static unsigned long long_ret; + struct lldpd_hardware *hardware; + struct lldpd_port *port; + + if (header_generic(vp, name, length, exact, var_len, write_method)) return NULL; + + switch (vp->magic) { + case LLDP_SNMP_TXINTERVAL: + long_ret = (scfg->g_config.c_tx_interval + 999) / 1000; + return (u_char *)&long_ret; + case LLDP_SNMP_TXMULTIPLIER: + long_ret = scfg->g_config.c_tx_hold; + return (u_char *)&long_ret; + case LLDP_SNMP_REINITDELAY: + long_ret = 1; + return (u_char *)&long_ret; + case LLDP_SNMP_TXDELAY: + long_ret = LLDPD_TX_MSGDELAY; + return (u_char *)&long_ret; + case LLDP_SNMP_NOTIFICATION: + long_ret = 5; + return (u_char *)&long_ret; + case LLDP_SNMP_LASTUPDATE: + long_ret = 0; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + /* Check if the last removal of a remote port on this local port + * was the last change. */ + if (hardware->h_lport.p_lastremove > long_ret) + long_ret = hardware->h_lport.p_lastremove; + /* Check if any change on the existing remote ports was the last + * change. */ + TAILQ_FOREACH (port, &hardware->h_rports, p_entries) { + if (SMART_HIDDEN(port)) continue; + if (port->p_lastchange > long_ret) + long_ret = port->p_lastchange; + } + } + if (long_ret) long_ret = (long_ret - starttime.tv_sec) * 100; + return (u_char *)&long_ret; + case LLDP_SNMP_STATS_INSERTS: + /* We assume this is equal to valid frames received on all ports */ + long_ret = 0; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) + long_ret += hardware->h_insert_cnt; + return (u_char *)&long_ret; + case LLDP_SNMP_STATS_AGEOUTS: + long_ret = 0; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) + long_ret += hardware->h_ageout_cnt; + return (u_char *)&long_ret; + case LLDP_SNMP_STATS_DELETES: + long_ret = 0; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) + long_ret += hardware->h_delete_cnt; + return (u_char *)&long_ret; + case LLDP_SNMP_STATS_DROPS: + long_ret = 0; + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) + long_ret += hardware->h_drop_cnt; + return (u_char *)&long_ret; + default: + break; + } + return NULL; +} + +#ifdef ENABLE_LLDPMED +static u_char * +agent_v_med_power(struct variable *vp, size_t *var_len, struct lldpd_med_power *power) +{ + static unsigned long long_ret; + + switch (vp->magic) { + case LLDP_SNMP_MED_POE_DEVICETYPE: + switch (power->devicetype) { + case LLDP_MED_POW_TYPE_PSE: + long_ret = 2; + break; + case LLDP_MED_POW_TYPE_PD: + long_ret = 3; + break; + case 0: + long_ret = 4; + break; + default: + long_ret = 1; + } + return (u_char *)&long_ret; + case LLDP_SNMP_MED_POE_PSE_POWERVAL: + case LLDP_SNMP_MED_POE_PD_POWERVAL: + if (((vp->magic == LLDP_SNMP_MED_POE_PSE_POWERVAL) && + (power->devicetype == LLDP_MED_POW_TYPE_PSE)) || + ((vp->magic == LLDP_SNMP_MED_POE_PD_POWERVAL) && + (power->devicetype == LLDP_MED_POW_TYPE_PD))) { + long_ret = power->val; + return (u_char *)&long_ret; + } + break; + case LLDP_SNMP_MED_POE_PSE_POWERSOURCE: + if (power->devicetype == LLDP_MED_POW_TYPE_PSE) { + switch (power->source) { + case LLDP_MED_POW_SOURCE_PRIMARY: + long_ret = 2; + break; + case LLDP_MED_POW_SOURCE_BACKUP: + long_ret = 3; + break; + default: + long_ret = 1; + } + return (u_char *)&long_ret; + } + break; + case LLDP_SNMP_MED_POE_PD_POWERSOURCE: + if (power->devicetype == LLDP_MED_POW_TYPE_PD) { + switch (power->source) { + case LLDP_MED_POW_SOURCE_PSE: + long_ret = 2; + break; + case LLDP_MED_POW_SOURCE_LOCAL: + long_ret = 3; + break; + case LLDP_MED_POW_SOURCE_BOTH: + long_ret = 4; + break; + default: + long_ret = 1; + } + return (u_char *)&long_ret; + } + break; + case LLDP_SNMP_MED_POE_PSE_POWERPRIORITY: + case LLDP_SNMP_MED_POE_PD_POWERPRIORITY: + if (((vp->magic == LLDP_SNMP_MED_POE_PSE_POWERPRIORITY) && + (power->devicetype == LLDP_MED_POW_TYPE_PSE)) || + ((vp->magic == LLDP_SNMP_MED_POE_PD_POWERPRIORITY) && + (power->devicetype == LLDP_MED_POW_TYPE_PD))) { + switch (power->priority) { + case LLDP_MED_POW_PRIO_CRITICAL: + long_ret = 2; + break; + case LLDP_MED_POW_PRIO_HIGH: + long_ret = 3; + break; + case LLDP_MED_POW_PRIO_LOW: + long_ret = 4; + break; + default: + long_ret = 1; + } + return (u_char *)&long_ret; + } + break; + } + + return NULL; +} +static u_char * +agent_h_local_med_power(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_med_power *power = NULL; + struct lldpd_hardware *hardware; + int pse = 0; + + if (!LOCAL_CHASSIS(scfg)->c_med_cap_available) return NULL; + if (header_generic(vp, name, length, exact, var_len, write_method)) return NULL; + + /* LLDP-MED requires only one device type for all + ports. Moreover, a PSE can only have one power source. At + least, all PD values are global and not per-port. We try to + do our best. For device type, we decide on the number of + PD/PSE ports. */ + TAILQ_FOREACH (hardware, &scfg->g_hardware, h_entries) { + if (hardware->h_lport.p_med_power.devicetype == LLDP_MED_POW_TYPE_PSE) { + pse++; + if (pse == 1) /* Take this port as a reference */ + power = &hardware->h_lport.p_med_power; + } else if (hardware->h_lport.p_med_power.devicetype == + LLDP_MED_POW_TYPE_PD) { + pse--; + if (pse == -1) /* Take this one instead */ + power = &hardware->h_lport.p_med_power; + } + } + if (power) { + u_char *a; + if ((a = agent_v_med_power(vp, var_len, power)) != NULL) return a; + } + TRYNEXT(agent_h_local_med_power); +} +static u_char * +agent_h_remote_med_power(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_port *port; + u_char *a; + + if ((port = header_tprindexed_table(vp, name, length, exact, var_len, + write_method, 1)) == NULL) + return NULL; + + if ((a = agent_v_med_power(vp, var_len, &port->p_med_power)) != NULL) return a; + TRYNEXT(agent_h_remote_med_power); +} + +static u_char * +agent_v_med(struct variable *vp, size_t *var_len, struct lldpd_chassis *chassis, + struct lldpd_port *port) +{ + static unsigned long long_ret; + static uint8_t bit; + + switch (vp->magic) { + case LLDP_SNMP_MED_CLASS: + long_ret = chassis->c_med_type; + return (u_char *)&long_ret; + case LLDP_SNMP_MED_CAP_AVAILABLE: + *var_len = 1; + bit = swap_bits(chassis->c_med_cap_available); + return (u_char *)&bit; + case LLDP_SNMP_MED_CAP_ENABLED: + if (!port) break; + *var_len = 1; + bit = swap_bits(port->p_med_cap_enabled); + return (u_char *)&bit; + +# define LLDP_H_MED(magic, variable) \ + case magic: \ + if (chassis->variable) { \ + *var_len = strlen(chassis->variable); \ + return (u_char *)chassis->variable; \ + } \ + break + + LLDP_H_MED(LLDP_SNMP_MED_HW, c_med_hw); + LLDP_H_MED(LLDP_SNMP_MED_SW, c_med_sw); + LLDP_H_MED(LLDP_SNMP_MED_FW, c_med_fw); + LLDP_H_MED(LLDP_SNMP_MED_SN, c_med_sn); + LLDP_H_MED(LLDP_SNMP_MED_MANUF, c_med_manuf); + LLDP_H_MED(LLDP_SNMP_MED_MODEL, c_med_model); + LLDP_H_MED(LLDP_SNMP_MED_ASSET, c_med_asset); + } + return NULL; +} +static u_char * +agent_h_local_med(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + u_char *a; + + if (!LOCAL_CHASSIS(scfg)->c_med_cap_available) return NULL; + if (header_generic(vp, name, length, exact, var_len, write_method)) return NULL; + + if ((a = agent_v_med(vp, var_len, LOCAL_CHASSIS(scfg), NULL)) != NULL) return a; + TRYNEXT(agent_h_local_med); +} + +static u_char * +agent_h_remote_med(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_port *port; + u_char *a; + + if ((port = header_tprindexed_table(vp, name, length, exact, var_len, + write_method, 1)) == NULL) + return NULL; + + if ((a = agent_v_med(vp, var_len, port->p_chassis, port)) != NULL) return a; + TRYNEXT(agent_h_remote_med); +} + +static u_char * +agent_v_med_policy(struct variable *vp, size_t *var_len, + struct lldpd_med_policy *policy) +{ + static unsigned long long_ret; + + switch (vp->magic) { + case LLDP_SNMP_MED_POLICY_VID: + long_ret = policy->vid; + return (u_char *)&long_ret; + case LLDP_SNMP_MED_POLICY_PRIO: + long_ret = policy->priority; + return (u_char *)&long_ret; + case LLDP_SNMP_MED_POLICY_DSCP: + long_ret = policy->dscp; + return (u_char *)&long_ret; + case LLDP_SNMP_MED_POLICY_UNKNOWN: + long_ret = policy->unknown ? 1 : 2; + return (u_char *)&long_ret; + case LLDP_SNMP_MED_POLICY_TAGGED: + long_ret = policy->tagged ? 1 : 2; + return (u_char *)&long_ret; + default: + return NULL; + } +} +static u_char * +agent_h_remote_med_policy(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_med_policy *policy; + + if ((policy = (struct lldpd_med_policy *)header_tprmedindexed_table(vp, name, + length, exact, var_len, write_method, TPR_VARIANT_MED_POLICY)) == NULL) + return NULL; + + return agent_v_med_policy(vp, var_len, policy); +} +static u_char * +agent_h_local_med_policy(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_med_policy *policy; + + if ((policy = (struct lldpd_med_policy *)header_pmedindexed_policy_table(vp, + name, length, exact, var_len, write_method)) == NULL) + return NULL; + + return agent_v_med_policy(vp, var_len, policy); +} + +static u_char * +agent_v_med_location(struct variable *vp, size_t *var_len, + struct lldpd_med_loc *location) +{ + switch (vp->magic) { + case LLDP_SNMP_MED_LOCATION: + *var_len = location->data_len; + return (u_char *)location->data; + default: + return NULL; + } +} +static u_char * +agent_h_remote_med_location(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_med_loc *location; + + if ((location = (struct lldpd_med_loc *)header_tprmedindexed_table(vp, name, + length, exact, var_len, write_method, TPR_VARIANT_MED_LOCATION)) == + NULL) + return NULL; + + return agent_v_med_location(vp, var_len, location); +} +static u_char * +agent_h_local_med_location(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_med_loc *location; + + if ((location = (struct lldpd_med_loc *)header_pmedindexed_location_table(vp, + name, length, exact, var_len, write_method)) == NULL) + return NULL; + + return agent_v_med_location(vp, var_len, location); +} +#endif + +static u_char * +agent_v_chassis(struct variable *vp, size_t *var_len, struct lldpd_chassis *chassis) +{ + static uint8_t bit; + static unsigned long long_ret; + + switch (vp->magic) { + case LLDP_SNMP_CIDSUBTYPE: + long_ret = chassis->c_id_subtype; + return (u_char *)&long_ret; + case LLDP_SNMP_CID: + *var_len = chassis->c_id_len; + return (u_char *)chassis->c_id; + case LLDP_SNMP_SYSNAME: + if (!chassis->c_name || *chassis->c_name == '\0') break; + *var_len = strlen(chassis->c_name); + return (u_char *)chassis->c_name; + case LLDP_SNMP_SYSDESCR: + if (!chassis->c_descr || *chassis->c_descr == '\0') break; + *var_len = strlen(chassis->c_descr); + return (u_char *)chassis->c_descr; + case LLDP_SNMP_SYSCAP_SUP: + *var_len = 1; + bit = swap_bits(chassis->c_cap_available); + return (u_char *)&bit; + case LLDP_SNMP_SYSCAP_ENA: + *var_len = 1; + bit = swap_bits(chassis->c_cap_enabled); + return (u_char *)&bit; + default: + break; + } + return NULL; +} +static u_char * +agent_h_local_chassis(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + u_char *a; + + if (header_generic(vp, name, length, exact, var_len, write_method)) return NULL; + + if ((a = agent_v_chassis(vp, var_len, LOCAL_CHASSIS(scfg))) != NULL) return a; + TRYNEXT(agent_h_local_chassis); +} +static u_char * +agent_h_remote_chassis(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_port *port; + u_char *a; + + if ((port = header_tprindexed_table(vp, name, length, exact, var_len, + write_method, 0)) == NULL) + return NULL; + + if ((a = agent_v_chassis(vp, var_len, port->p_chassis)) != NULL) return a; + TRYNEXT(agent_h_remote_chassis); +} + +static u_char * +agent_h_stats(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + static unsigned long long_ret; + struct lldpd_hardware *hardware; + + if ((hardware = header_portindexed_table(vp, name, length, exact, var_len, + write_method)) == NULL) + return NULL; + + switch (vp->magic) { + case LLDP_SNMP_STATS_TX: + long_ret = hardware->h_tx_cnt; + return (u_char *)&long_ret; + case LLDP_SNMP_STATS_RX: + long_ret = hardware->h_rx_cnt; + return (u_char *)&long_ret; + case LLDP_SNMP_STATS_RX_DISCARDED: + case LLDP_SNMP_STATS_RX_ERRORS: + /* We discard only frame with errors. Therefore, the two values + * are equal */ + long_ret = hardware->h_rx_discarded_cnt; + return (u_char *)&long_ret; + case LLDP_SNMP_STATS_RX_TLVDISCARDED: + case LLDP_SNMP_STATS_RX_TLVUNRECOGNIZED: + /* We discard only unrecognized TLV. Malformed TLV + implies dropping the whole frame */ + long_ret = hardware->h_rx_unrecognized_cnt; + return (u_char *)&long_ret; + case LLDP_SNMP_STATS_RX_AGEOUTS: + long_ret = hardware->h_ageout_cnt; + return (u_char *)&long_ret; + default: + return NULL; + } +} + +#ifdef ENABLE_DOT1 +static u_char * +agent_v_vlan(struct variable *vp, size_t *var_len, struct lldpd_vlan *vlan) +{ + switch (vp->magic) { + case LLDP_SNMP_DOT1_VLANNAME: + *var_len = strlen(vlan->v_name); + return (u_char *)vlan->v_name; + default: + return NULL; + } +} +static u_char * +agent_h_local_vlan(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_vlan *vlan; + + if ((vlan = header_pvindexed_table(vp, name, length, exact, var_len, + write_method)) == NULL) + return NULL; + + return agent_v_vlan(vp, var_len, vlan); +} +static u_char * +agent_h_remote_vlan(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_vlan *vlan; + + if ((vlan = header_tprvindexed_table(vp, name, length, exact, var_len, + write_method)) == NULL) + return NULL; + + return agent_v_vlan(vp, var_len, vlan); +} + +static u_char * +agent_v_ppvid(struct variable *vp, size_t *var_len, struct lldpd_ppvid *ppvid) +{ + static unsigned long long_ret; + + switch (vp->magic) { + case LLDP_SNMP_DOT1_PPVLAN_SUPPORTED: + long_ret = (ppvid->p_cap_status & LLDP_PPVID_CAP_SUPPORTED) ? 1 : 2; + return (u_char *)&long_ret; + case LLDP_SNMP_DOT1_PPVLAN_ENABLED: + long_ret = (ppvid->p_cap_status & LLDP_PPVID_CAP_ENABLED) ? 1 : 2; + return (u_char *)&long_ret; + default: + return NULL; + } +} +static u_char * +agent_h_local_ppvid(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_ppvid *ppvid; + + if ((ppvid = header_pppvidindexed_table(vp, name, length, exact, var_len, + write_method)) == NULL) + return NULL; + + return agent_v_ppvid(vp, var_len, ppvid); +} + +static u_char * +agent_h_remote_ppvid(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_ppvid *ppvid; + + if ((ppvid = header_tprppvidindexed_table(vp, name, length, exact, var_len, + write_method)) == NULL) + return NULL; + + return agent_v_ppvid(vp, var_len, ppvid); +} + +static u_char * +agent_v_pi(struct variable *vp, size_t *var_len, struct lldpd_pi *pi) +{ + switch (vp->magic) { + case LLDP_SNMP_DOT1_PI: + *var_len = pi->p_pi_len; + return (u_char *)pi->p_pi; + default: + return NULL; + } +} +static u_char * +agent_h_local_pi(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_pi *pi; + + if ((pi = header_ppiindexed_table(vp, name, length, exact, var_len, + write_method)) == NULL) + return NULL; + + return agent_v_pi(vp, var_len, pi); +} +static u_char * +agent_h_remote_pi(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_pi *pi; + + if ((pi = header_tprpiindexed_table(vp, name, length, exact, var_len, + write_method)) == NULL) + return NULL; + + return agent_v_pi(vp, var_len, pi); +} +#endif + +static u_char * +agent_v_port(struct variable *vp, size_t *var_len, struct lldpd_port *port) +{ +#ifdef ENABLE_DOT3 + static uint16_t short_ret; + static uint8_t bit; +#endif + static unsigned long long_ret; + + switch (vp->magic) { + case LLDP_SNMP_PIDSUBTYPE: + long_ret = port->p_id_subtype; + return (u_char *)&long_ret; + case LLDP_SNMP_PID: + *var_len = port->p_id_len; + return (u_char *)port->p_id; + case LLDP_SNMP_PORTDESC: + if (!port->p_descr || *port->p_descr == '\0') break; + *var_len = strlen(port->p_descr); + return (u_char *)port->p_descr; +#ifdef ENABLE_DOT3 + case LLDP_SNMP_DOT3_AUTONEG_SUPPORT: + long_ret = 2 - port->p_macphy.autoneg_support; + return (u_char *)&long_ret; + case LLDP_SNMP_DOT3_AUTONEG_ENABLED: + long_ret = 2 - port->p_macphy.autoneg_enabled; + return (u_char *)&long_ret; + case LLDP_SNMP_DOT3_AUTONEG_ADVERTISED: + *var_len = 2; + short_ret = htons(port->p_macphy.autoneg_advertised); + return (u_char *)&short_ret; + case LLDP_SNMP_DOT3_AUTONEG_MAU: + long_ret = port->p_macphy.mau_type; + return (u_char *)&long_ret; + case LLDP_SNMP_DOT3_AGG_STATUS: + bit = swap_bits((port->p_aggregid > 0) ? 3 : 0); + *var_len = 1; + return (u_char *)&bit; + case LLDP_SNMP_DOT3_AGG_ID: + long_ret = port->p_aggregid; + return (u_char *)&long_ret; + case LLDP_SNMP_DOT3_MFS: + if (port->p_mfs) { + long_ret = port->p_mfs; + return (u_char *)&long_ret; + } + break; + case LLDP_SNMP_DOT3_POWER_DEVICETYPE: + if (port->p_power.devicetype) { + long_ret = + (port->p_power.devicetype == LLDP_DOT3_POWER_PSE) ? 1 : 2; + return (u_char *)&long_ret; + } + break; + case LLDP_SNMP_DOT3_POWER_SUPPORT: + if (port->p_power.devicetype) { + long_ret = (port->p_power.supported) ? 1 : 2; + return (u_char *)&long_ret; + } + break; + case LLDP_SNMP_DOT3_POWER_ENABLED: + if (port->p_power.devicetype) { + long_ret = (port->p_power.enabled) ? 1 : 2; + return (u_char *)&long_ret; + } + break; + case LLDP_SNMP_DOT3_POWER_PAIRCONTROL: + if (port->p_power.devicetype) { + long_ret = (port->p_power.paircontrol) ? 1 : 2; + return (u_char *)&long_ret; + } + break; + case LLDP_SNMP_DOT3_POWER_PAIRS: + if (port->p_power.devicetype) { + long_ret = port->p_power.pairs; + return (u_char *)&long_ret; + } + break; + case LLDP_SNMP_DOT3_POWER_CLASS: + if (port->p_power.devicetype && port->p_power.class) { + long_ret = port->p_power.class; + return (u_char *)&long_ret; + } + break; + case LLDP_SNMP_DOT3_POWER_TYPE: + if (port->p_power.devicetype && + port->p_power.powertype != LLDP_DOT3_POWER_8023AT_OFF) { + *var_len = 1; + bit = (((port->p_power.powertype == + LLDP_DOT3_POWER_8023AT_TYPE1) ? + 0 : + 1) + << 7) | + (((port->p_power.devicetype == LLDP_DOT3_POWER_PSE) ? 0 : 1) + << 6); + return (u_char *)&bit; + } + break; + case LLDP_SNMP_DOT3_POWER_SOURCE: + if (port->p_power.devicetype && + port->p_power.powertype != LLDP_DOT3_POWER_8023AT_OFF) { + *var_len = 1; + bit = swap_bits(port->p_power.source % (1 << 2)); + return (u_char *)&bit; + } + break; + case LLDP_SNMP_DOT3_POWER_PRIORITY: + if (port->p_power.devicetype && + port->p_power.powertype != LLDP_DOT3_POWER_8023AT_OFF) { + /* See 30.12.2.1.16. This seems defined in reverse order... */ + long_ret = 4 - port->p_power.priority; + return (u_char *)&long_ret; + } + break; + case LLDP_SNMP_DOT3_POWER_REQUESTED: + if (port->p_power.devicetype && + port->p_power.powertype != LLDP_DOT3_POWER_8023AT_OFF) { + long_ret = port->p_power.requested; + return (u_char *)&long_ret; + } + break; + case LLDP_SNMP_DOT3_POWER_ALLOCATED: + if (port->p_power.devicetype && + port->p_power.powertype != LLDP_DOT3_POWER_8023AT_OFF) { + long_ret = port->p_power.allocated; + return (u_char *)&long_ret; + } + break; +#endif +#ifdef ENABLE_DOT1 + case LLDP_SNMP_DOT1_PVID: + long_ret = port->p_pvid; + return (u_char *)&long_ret; +#endif + default: + break; + } + return NULL; +} +static u_char * +agent_h_remote_port(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_port *port; + u_char *a; + + if ((port = header_tprindexed_table(vp, name, length, exact, var_len, + write_method, 0)) == NULL) + return NULL; + + if ((a = agent_v_port(vp, var_len, port)) != NULL) return a; + TRYNEXT(agent_h_remote_port); +} +static u_char * +agent_h_local_port(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_hardware *hardware; + u_char *a; + + if ((hardware = header_portindexed_table(vp, name, length, exact, var_len, + write_method)) == NULL) + return NULL; + + if ((a = agent_v_port(vp, var_len, &hardware->h_lport)) != NULL) return a; + TRYNEXT(agent_h_local_port); +} + +static u_char * +agent_v_management(struct variable *vp, size_t *var_len, struct lldpd_mgmt *mgmt) +{ + static unsigned long int long_ret; + static oid zeroDotZero[2] = { 0, 0 }; + + switch (vp->magic) { + case LLDP_SNMP_ADDR_LEN: + long_ret = mgmt->m_addrsize + 1; + return (u_char *)&long_ret; + case LLDP_SNMP_ADDR_IFSUBTYPE: + if (mgmt->m_iface != 0) + long_ret = LLDP_MGMT_IFACE_IFINDEX; + else + long_ret = 1; + return (u_char *)&long_ret; + case LLDP_SNMP_ADDR_IFID: + long_ret = mgmt->m_iface; + return (u_char *)&long_ret; + case LLDP_SNMP_ADDR_OID: + *var_len = sizeof(zeroDotZero); + return (u_char *)zeroDotZero; + default: + return NULL; + } +} +static u_char * +agent_h_local_management(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + + struct lldpd_mgmt *mgmt; + + if ((mgmt = header_ipindexed_table(vp, name, length, exact, var_len, + write_method)) == NULL) + return NULL; + + return agent_v_management(vp, var_len, mgmt); +} +static u_char * +agent_h_remote_management(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_mgmt *mgmt; + + if ((mgmt = header_tpripindexed_table(vp, name, length, exact, var_len, + write_method)) == NULL) + return NULL; + + return agent_v_management(vp, var_len, mgmt); +} + +#ifdef ENABLE_CUSTOM +static u_char * +agent_v_custom(struct variable *vp, size_t *var_len, struct lldpd_custom *custom) +{ + switch (vp->magic) { + case LLDP_SNMP_ORG_DEF_INFO: + *var_len = custom->oui_info_len; + return (u_char *)custom->oui_info; + default: + return NULL; + } +} +static u_char * +agent_h_remote_custom(struct variable *vp, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct lldpd_custom *custom; + + if ((custom = header_tprcustomindexed_table(vp, name, length, exact, var_len, + write_method)) == NULL) + return NULL; + + return agent_v_custom(vp, var_len, custom); +} +#endif + +/* + Here is how it works: a agent_h_*() function will handle incoming + requests. It will use an appropriate header_*indexed_table() + function to grab the appropriate structure that was queried (a port, + a chassis, ...). It will then delegate to a agent_v_*() function the + responsability to extract the appropriate answer. + + agent_h_*() functions and header_*indexed_table() are not shared + between remote and not remote version while agent_v_*() functions + are the same for both version. +*/ + +/* For testing purposes, keep this structure ordered by increasing OID! */ +struct variable8 agent_lldp_vars[] = { + /* Scalars */ + { LLDP_SNMP_TXINTERVAL, ASN_INTEGER, RONLY, agent_h_scalars, 3, { 1, 1, 1 } }, + { LLDP_SNMP_TXMULTIPLIER, ASN_INTEGER, RONLY, agent_h_scalars, 3, { 1, 1, 2 } }, + { LLDP_SNMP_REINITDELAY, ASN_INTEGER, RONLY, agent_h_scalars, 3, { 1, 1, 3 } }, + { LLDP_SNMP_TXDELAY, ASN_INTEGER, RONLY, agent_h_scalars, 3, { 1, 1, 4 } }, + { LLDP_SNMP_NOTIFICATION, ASN_INTEGER, RONLY, agent_h_scalars, 3, { 1, 1, 5 } }, + { LLDP_SNMP_LASTUPDATE, ASN_TIMETICKS, RONLY, agent_h_scalars, 3, { 1, 2, 1 } }, + { LLDP_SNMP_STATS_INSERTS, ASN_GAUGE, RONLY, agent_h_scalars, 3, { 1, 2, 2 } }, + { LLDP_SNMP_STATS_DELETES, ASN_GAUGE, RONLY, agent_h_scalars, 3, { 1, 2, 3 } }, + { LLDP_SNMP_STATS_DROPS, ASN_GAUGE, RONLY, agent_h_scalars, 3, { 1, 2, 4 } }, + { LLDP_SNMP_STATS_AGEOUTS, ASN_GAUGE, RONLY, agent_h_scalars, 3, { 1, 2, 5 } }, + /* Stats */ + { LLDP_SNMP_STATS_TX, ASN_COUNTER, RONLY, agent_h_stats, 5, { 1, 2, 6, 1, 2 } }, + { LLDP_SNMP_STATS_RX_DISCARDED, ASN_COUNTER, RONLY, agent_h_stats, 5, + { 1, 2, 7, 1, 2 } }, + { LLDP_SNMP_STATS_RX_ERRORS, ASN_COUNTER, RONLY, agent_h_stats, 5, + { 1, 2, 7, 1, 3 } }, + { LLDP_SNMP_STATS_RX, ASN_COUNTER, RONLY, agent_h_stats, 5, { 1, 2, 7, 1, 4 } }, + { LLDP_SNMP_STATS_RX_TLVDISCARDED, ASN_COUNTER, RONLY, agent_h_stats, 5, + { 1, 2, 7, 1, 5 } }, + { LLDP_SNMP_STATS_RX_TLVUNRECOGNIZED, ASN_COUNTER, RONLY, agent_h_stats, 5, + { 1, 2, 7, 1, 6 } }, + { LLDP_SNMP_STATS_RX_AGEOUTS, ASN_GAUGE, RONLY, agent_h_stats, 5, + { 1, 2, 7, 1, 7 } }, + /* Local chassis */ + { LLDP_SNMP_CIDSUBTYPE, ASN_INTEGER, RONLY, agent_h_local_chassis, 3, + { 1, 3, 1 } }, + { LLDP_SNMP_CID, ASN_OCTET_STR, RONLY, agent_h_local_chassis, 3, { 1, 3, 2 } }, + { LLDP_SNMP_SYSNAME, ASN_OCTET_STR, RONLY, agent_h_local_chassis, 3, + { 1, 3, 3 } }, + { LLDP_SNMP_SYSDESCR, ASN_OCTET_STR, RONLY, agent_h_local_chassis, 3, + { 1, 3, 4 } }, + { LLDP_SNMP_SYSCAP_SUP, ASN_OCTET_STR, RONLY, agent_h_local_chassis, 3, + { 1, 3, 5 } }, + { LLDP_SNMP_SYSCAP_ENA, ASN_OCTET_STR, RONLY, agent_h_local_chassis, 3, + { 1, 3, 6 } }, + /* Local ports */ + { LLDP_SNMP_PIDSUBTYPE, ASN_INTEGER, RONLY, agent_h_local_port, 5, + { 1, 3, 7, 1, 2 } }, + { LLDP_SNMP_PID, ASN_OCTET_STR, RONLY, agent_h_local_port, 5, + { 1, 3, 7, 1, 3 } }, + { LLDP_SNMP_PORTDESC, ASN_OCTET_STR, RONLY, agent_h_local_port, 5, + { 1, 3, 7, 1, 4 } }, + /* Local management address */ + { LLDP_SNMP_ADDR_LEN, ASN_INTEGER, RONLY, agent_h_local_management, 5, + { 1, 3, 8, 1, 3 } }, + { LLDP_SNMP_ADDR_IFSUBTYPE, ASN_INTEGER, RONLY, agent_h_local_management, 5, + { 1, 3, 8, 1, 4 } }, + { LLDP_SNMP_ADDR_IFID, ASN_INTEGER, RONLY, agent_h_local_management, 5, + { 1, 3, 8, 1, 5 } }, + { LLDP_SNMP_ADDR_OID, ASN_OBJECT_ID, RONLY, agent_h_local_management, 5, + { 1, 3, 8, 1, 6 } }, + /* Remote ports */ + { LLDP_SNMP_CIDSUBTYPE, ASN_INTEGER, RONLY, agent_h_remote_chassis, 5, + { 1, 4, 1, 1, 4 } }, + { LLDP_SNMP_CID, ASN_OCTET_STR, RONLY, agent_h_remote_chassis, 5, + { 1, 4, 1, 1, 5 } }, + { LLDP_SNMP_PIDSUBTYPE, ASN_INTEGER, RONLY, agent_h_remote_port, 5, + { 1, 4, 1, 1, 6 } }, + { LLDP_SNMP_PID, ASN_OCTET_STR, RONLY, agent_h_remote_port, 5, + { 1, 4, 1, 1, 7 } }, + { LLDP_SNMP_PORTDESC, ASN_OCTET_STR, RONLY, agent_h_remote_port, 5, + { 1, 4, 1, 1, 8 } }, + { LLDP_SNMP_SYSNAME, ASN_OCTET_STR, RONLY, agent_h_remote_chassis, 5, + { 1, 4, 1, 1, 9 } }, + { LLDP_SNMP_SYSDESCR, ASN_OCTET_STR, RONLY, agent_h_remote_chassis, 5, + { 1, 4, 1, 1, 10 } }, + { LLDP_SNMP_SYSCAP_SUP, ASN_OCTET_STR, RONLY, agent_h_remote_chassis, 5, + { 1, 4, 1, 1, 11 } }, + { LLDP_SNMP_SYSCAP_ENA, ASN_OCTET_STR, RONLY, agent_h_remote_chassis, 5, + { 1, 4, 1, 1, 12 } }, + /* Remote management address */ + { LLDP_SNMP_ADDR_IFSUBTYPE, ASN_INTEGER, RONLY, agent_h_remote_management, 5, + { 1, 4, 2, 1, 3 } }, + { LLDP_SNMP_ADDR_IFID, ASN_INTEGER, RONLY, agent_h_remote_management, 5, + { 1, 4, 2, 1, 4 } }, + { LLDP_SNMP_ADDR_OID, ASN_OBJECT_ID, RONLY, agent_h_remote_management, 5, + { 1, 4, 2, 1, 5 } }, +#ifdef ENABLE_CUSTOM + /* Custom TLVs */ + { LLDP_SNMP_ORG_DEF_INFO, ASN_OCTET_STR, RONLY, agent_h_remote_custom, 5, + { 1, 4, 4, 1, 4 } }, +#endif +#ifdef ENABLE_DOT3 + /* Dot3, local ports */ + { LLDP_SNMP_DOT3_AUTONEG_SUPPORT, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 1, 1, 1 } }, + { LLDP_SNMP_DOT3_AUTONEG_ENABLED, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 1, 1, 2 } }, + { LLDP_SNMP_DOT3_AUTONEG_ADVERTISED, ASN_OCTET_STR, RONLY, agent_h_local_port, + 8, { 1, 5, 4623, 1, 2, 1, 1, 3 } }, + { LLDP_SNMP_DOT3_AUTONEG_MAU, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 1, 1, 4 } }, + { LLDP_SNMP_DOT3_POWER_DEVICETYPE, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 2, 1, 1 } }, + { LLDP_SNMP_DOT3_POWER_SUPPORT, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 2, 1, 2 } }, + { LLDP_SNMP_DOT3_POWER_ENABLED, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 2, 1, 3 } }, + { LLDP_SNMP_DOT3_POWER_PAIRCONTROL, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 2, 1, 4 } }, + { LLDP_SNMP_DOT3_POWER_PAIRS, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 2, 1, 5 } }, + { LLDP_SNMP_DOT3_POWER_CLASS, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 2, 1, 6 } }, + { LLDP_SNMP_DOT3_POWER_TYPE, ASN_OCTET_STR, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 2, 1, 7 } }, + { LLDP_SNMP_DOT3_POWER_SOURCE, ASN_OCTET_STR, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 2, 1, 8 } }, + { LLDP_SNMP_DOT3_POWER_PRIORITY, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 2, 1, 9 } }, + { LLDP_SNMP_DOT3_POWER_REQUESTED, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 2, 1, 10 } }, + { LLDP_SNMP_DOT3_POWER_ALLOCATED, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 2, 1, 11 } }, + { LLDP_SNMP_DOT3_AGG_STATUS, ASN_OCTET_STR, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 3, 1, 1 } }, + { LLDP_SNMP_DOT3_AGG_ID, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 3, 1, 2 } }, + { LLDP_SNMP_DOT3_MFS, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 4623, 1, 2, 4, 1, 1 } }, +#endif +/* Dot3, remote ports */ +#ifdef ENABLE_DOT3 + { LLDP_SNMP_DOT3_AUTONEG_SUPPORT, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 1, 1, 1 } }, + { LLDP_SNMP_DOT3_AUTONEG_ENABLED, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 1, 1, 2 } }, + { LLDP_SNMP_DOT3_AUTONEG_ADVERTISED, ASN_OCTET_STR, RONLY, agent_h_remote_port, + 8, { 1, 5, 4623, 1, 3, 1, 1, 3 } }, + { LLDP_SNMP_DOT3_AUTONEG_MAU, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 1, 1, 4 } }, + { LLDP_SNMP_DOT3_POWER_DEVICETYPE, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 2, 1, 1 } }, + { LLDP_SNMP_DOT3_POWER_SUPPORT, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 2, 1, 2 } }, + { LLDP_SNMP_DOT3_POWER_ENABLED, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 2, 1, 3 } }, + { LLDP_SNMP_DOT3_POWER_PAIRCONTROL, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 2, 1, 4 } }, + { LLDP_SNMP_DOT3_POWER_PAIRS, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 2, 1, 5 } }, + { LLDP_SNMP_DOT3_POWER_CLASS, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 2, 1, 6 } }, + { LLDP_SNMP_DOT3_POWER_TYPE, ASN_OCTET_STR, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 2, 1, 7 } }, + { LLDP_SNMP_DOT3_POWER_SOURCE, ASN_OCTET_STR, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 2, 1, 8 } }, + { LLDP_SNMP_DOT3_POWER_PRIORITY, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 2, 1, 9 } }, + { LLDP_SNMP_DOT3_POWER_REQUESTED, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 2, 1, 10 } }, + { LLDP_SNMP_DOT3_POWER_ALLOCATED, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 2, 1, 11 } }, + { LLDP_SNMP_DOT3_AGG_STATUS, ASN_OCTET_STR, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 3, 1, 1 } }, + { LLDP_SNMP_DOT3_AGG_ID, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 3, 1, 2 } }, + { LLDP_SNMP_DOT3_MFS, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 4623, 1, 3, 4, 1, 1 } }, +#endif +#ifdef ENABLE_LLDPMED + /* LLDP-MED local */ + { LLDP_SNMP_MED_CLASS, ASN_INTEGER, RONLY, agent_h_local_med, 6, + { 1, 5, 4795, 1, 1, 1 } }, + { LLDP_SNMP_MED_POLICY_VID, ASN_INTEGER, RONLY, agent_h_local_med_policy, 8, + { 1, 5, 4795, 1, 2, 1, 1, 2 } }, + { LLDP_SNMP_MED_POLICY_PRIO, ASN_INTEGER, RONLY, agent_h_local_med_policy, 8, + { 1, 5, 4795, 1, 2, 1, 1, 3 } }, + { LLDP_SNMP_MED_POLICY_DSCP, ASN_INTEGER, RONLY, agent_h_local_med_policy, 8, + { 1, 5, 4795, 1, 2, 1, 1, 4 } }, + { LLDP_SNMP_MED_POLICY_UNKNOWN, ASN_INTEGER, RONLY, agent_h_local_med_policy, 8, + { 1, 5, 4795, 1, 2, 1, 1, 5 } }, + { LLDP_SNMP_MED_POLICY_TAGGED, ASN_INTEGER, RONLY, agent_h_local_med_policy, 8, + { 1, 5, 4795, 1, 2, 1, 1, 6 } }, + { LLDP_SNMP_MED_HW, ASN_OCTET_STR, RONLY, agent_h_local_med, 6, + { 1, 5, 4795, 1, 2, 2 } }, + { LLDP_SNMP_MED_FW, ASN_OCTET_STR, RONLY, agent_h_local_med, 6, + { 1, 5, 4795, 1, 2, 3 } }, + { LLDP_SNMP_MED_SW, ASN_OCTET_STR, RONLY, agent_h_local_med, 6, + { 1, 5, 4795, 1, 2, 4 } }, + { LLDP_SNMP_MED_SN, ASN_OCTET_STR, RONLY, agent_h_local_med, 6, + { 1, 5, 4795, 1, 2, 5 } }, + { LLDP_SNMP_MED_MANUF, ASN_OCTET_STR, RONLY, agent_h_local_med, 6, + { 1, 5, 4795, 1, 2, 6 } }, + { LLDP_SNMP_MED_MODEL, ASN_OCTET_STR, RONLY, agent_h_local_med, 6, + { 1, 5, 4795, 1, 2, 7 } }, + { LLDP_SNMP_MED_ASSET, ASN_OCTET_STR, RONLY, agent_h_local_med, 6, + { 1, 5, 4795, 1, 2, 8 } }, + { LLDP_SNMP_MED_LOCATION, ASN_OCTET_STR, RONLY, agent_h_local_med_location, 8, + { 1, 5, 4795, 1, 2, 9, 1, 2 } }, + { LLDP_SNMP_MED_POE_DEVICETYPE, ASN_INTEGER, RONLY, agent_h_local_med_power, 6, + { 1, 5, 4795, 1, 2, 10 } }, + { LLDP_SNMP_MED_POE_PSE_POWERVAL, ASN_GAUGE, RONLY, agent_h_local_med_power, 8, + { 1, 5, 4795, 1, 2, 11, 1, 1 } }, + { LLDP_SNMP_MED_POE_PSE_POWERPRIORITY, ASN_INTEGER, RONLY, + agent_h_local_med_power, 8, { 1, 5, 4795, 1, 2, 11, 1, 2 } }, + { LLDP_SNMP_MED_POE_PSE_POWERSOURCE, ASN_INTEGER, RONLY, + agent_h_local_med_power, 6, { 1, 5, 4795, 1, 2, 12 } }, + { LLDP_SNMP_MED_POE_PD_POWERVAL, ASN_GAUGE, RONLY, agent_h_local_med_power, 6, + { 1, 5, 4795, 1, 2, 13 } }, + { LLDP_SNMP_MED_POE_PD_POWERSOURCE, ASN_INTEGER, RONLY, agent_h_local_med_power, + 6, { 1, 5, 4795, 1, 2, 14 } }, + { LLDP_SNMP_MED_POE_PD_POWERPRIORITY, ASN_INTEGER, RONLY, + agent_h_local_med_power, 6, { 1, 5, 4795, 1, 2, 15 } }, + /* LLDP-MED remote */ + { LLDP_SNMP_MED_CAP_AVAILABLE, ASN_OCTET_STR, RONLY, agent_h_remote_med, 8, + { 1, 5, 4795, 1, 3, 1, 1, 1 } }, + { LLDP_SNMP_MED_CAP_ENABLED, ASN_OCTET_STR, RONLY, agent_h_remote_med, 8, + { 1, 5, 4795, 1, 3, 1, 1, 2 } }, + { LLDP_SNMP_MED_CLASS, ASN_INTEGER, RONLY, agent_h_remote_med, 8, + { 1, 5, 4795, 1, 3, 1, 1, 3 } }, + { LLDP_SNMP_MED_POLICY_VID, ASN_INTEGER, RONLY, agent_h_remote_med_policy, 8, + { 1, 5, 4795, 1, 3, 2, 1, 2 } }, + { LLDP_SNMP_MED_POLICY_PRIO, ASN_INTEGER, RONLY, agent_h_remote_med_policy, 8, + { 1, 5, 4795, 1, 3, 2, 1, 3 } }, + { LLDP_SNMP_MED_POLICY_DSCP, ASN_INTEGER, RONLY, agent_h_remote_med_policy, 8, + { 1, 5, 4795, 1, 3, 2, 1, 4 } }, + { LLDP_SNMP_MED_POLICY_UNKNOWN, ASN_INTEGER, RONLY, agent_h_remote_med_policy, + 8, { 1, 5, 4795, 1, 3, 2, 1, 5 } }, + { LLDP_SNMP_MED_POLICY_TAGGED, ASN_INTEGER, RONLY, agent_h_remote_med_policy, 8, + { 1, 5, 4795, 1, 3, 2, 1, 6 } }, + { LLDP_SNMP_MED_HW, ASN_OCTET_STR, RONLY, agent_h_remote_med, 8, + { 1, 5, 4795, 1, 3, 3, 1, 1 } }, + { LLDP_SNMP_MED_FW, ASN_OCTET_STR, RONLY, agent_h_remote_med, 8, + { 1, 5, 4795, 1, 3, 3, 1, 2 } }, + { LLDP_SNMP_MED_SW, ASN_OCTET_STR, RONLY, agent_h_remote_med, 8, + { 1, 5, 4795, 1, 3, 3, 1, 3 } }, + { LLDP_SNMP_MED_SN, ASN_OCTET_STR, RONLY, agent_h_remote_med, 8, + { 1, 5, 4795, 1, 3, 3, 1, 4 } }, + { LLDP_SNMP_MED_MANUF, ASN_OCTET_STR, RONLY, agent_h_remote_med, 8, + { 1, 5, 4795, 1, 3, 3, 1, 5 } }, + { LLDP_SNMP_MED_MODEL, ASN_OCTET_STR, RONLY, agent_h_remote_med, 8, + { 1, 5, 4795, 1, 3, 3, 1, 6 } }, + { LLDP_SNMP_MED_ASSET, ASN_OCTET_STR, RONLY, agent_h_remote_med, 8, + { 1, 5, 4795, 1, 3, 3, 1, 7 } }, + { LLDP_SNMP_MED_LOCATION, ASN_OCTET_STR, RONLY, agent_h_remote_med_location, 8, + { 1, 5, 4795, 1, 3, 4, 1, 2 } }, + { LLDP_SNMP_MED_POE_DEVICETYPE, ASN_INTEGER, RONLY, agent_h_remote_med_power, 8, + { 1, 5, 4795, 1, 3, 5, 1, 1 } }, + { LLDP_SNMP_MED_POE_PSE_POWERVAL, ASN_GAUGE, RONLY, agent_h_remote_med_power, 8, + { 1, 5, 4795, 1, 3, 6, 1, 1 } }, + { LLDP_SNMP_MED_POE_PSE_POWERSOURCE, ASN_INTEGER, RONLY, + agent_h_remote_med_power, 8, { 1, 5, 4795, 1, 3, 6, 1, 2 } }, + { LLDP_SNMP_MED_POE_PSE_POWERPRIORITY, ASN_INTEGER, RONLY, + agent_h_remote_med_power, 8, { 1, 5, 4795, 1, 3, 6, 1, 3 } }, + { LLDP_SNMP_MED_POE_PD_POWERVAL, ASN_GAUGE, RONLY, agent_h_remote_med_power, 8, + { 1, 5, 4795, 1, 3, 7, 1, 1 } }, + { LLDP_SNMP_MED_POE_PD_POWERSOURCE, ASN_INTEGER, RONLY, + agent_h_remote_med_power, 8, { 1, 5, 4795, 1, 3, 7, 1, 2 } }, + { LLDP_SNMP_MED_POE_PD_POWERPRIORITY, ASN_INTEGER, RONLY, + agent_h_remote_med_power, 8, { 1, 5, 4795, 1, 3, 7, 1, 3 } }, +#endif +/* Dot1, local and remote ports */ +#ifdef ENABLE_DOT1 + { LLDP_SNMP_DOT1_PVID, ASN_INTEGER, RONLY, agent_h_local_port, 8, + { 1, 5, 32962, 1, 2, 1, 1, 1 } }, + { LLDP_SNMP_DOT1_PPVLAN_SUPPORTED, ASN_INTEGER, RONLY, agent_h_local_ppvid, 8, + { 1, 5, 32962, 1, 2, 2, 1, 2 } }, + { LLDP_SNMP_DOT1_PPVLAN_ENABLED, ASN_INTEGER, RONLY, agent_h_local_ppvid, 8, + { 1, 5, 32962, 1, 2, 2, 1, 3 } }, + { LLDP_SNMP_DOT1_VLANNAME, ASN_OCTET_STR, RONLY, agent_h_local_vlan, 8, + { 1, 5, 32962, 1, 2, 3, 1, 2 } }, + { LLDP_SNMP_DOT1_PI, ASN_OCTET_STR, RONLY, agent_h_local_pi, 8, + { 1, 5, 32962, 1, 2, 4, 1, 2 } }, +#endif +#ifdef ENABLE_DOT1 + { LLDP_SNMP_DOT1_PVID, ASN_INTEGER, RONLY, agent_h_remote_port, 8, + { 1, 5, 32962, 1, 3, 1, 1, 1 } }, + { LLDP_SNMP_DOT1_PPVLAN_SUPPORTED, ASN_INTEGER, RONLY, agent_h_remote_ppvid, 8, + { 1, 5, 32962, 1, 3, 2, 1, 2 } }, + { LLDP_SNMP_DOT1_PPVLAN_ENABLED, ASN_INTEGER, RONLY, agent_h_remote_ppvid, 8, + { 1, 5, 32962, 1, 3, 2, 1, 3 } }, + /* Remote vlans */ + { LLDP_SNMP_DOT1_VLANNAME, ASN_OCTET_STR, RONLY, agent_h_remote_vlan, 8, + { 1, 5, 32962, 1, 3, 3, 1, 2 } }, + /* Protocol identity */ + { LLDP_SNMP_DOT1_PI, ASN_OCTET_STR, RONLY, agent_h_remote_pi, 8, + { 1, 5, 32962, 1, 3, 4, 1, 2 } }, +#endif +}; +size_t +agent_lldp_vars_size(void) +{ + return sizeof(agent_lldp_vars) / sizeof(struct variable8); +} + +/** + * Send a notification about a change in one remote neighbor. + * + * @param hardware Interface on which the change has happened. + * @param type Type of change (add, delete, update) + * @param rport Changed remote port + */ +void +agent_notify(struct lldpd_hardware *hardware, int type, struct lldpd_port *rport) +{ + struct lldpd_hardware *h; + + /* OID of the notification */ + oid notification_oid[] = { LLDP_OID, 0, 0, 1 }; + size_t notification_oid_len = OID_LENGTH(notification_oid); + /* OID for snmpTrapOID.0 */ + oid objid_snmptrap[] = { SNMPTRAP_OID }; + size_t objid_snmptrap_len = OID_LENGTH(objid_snmptrap); + + /* Other OID */ + oid inserts_oid[] = { LLDP_OID, 1, 2, 2 }; + size_t inserts_oid_len = OID_LENGTH(inserts_oid); + unsigned long inserts = 0; + + oid deletes_oid[] = { LLDP_OID, 1, 2, 3 }; + size_t deletes_oid_len = OID_LENGTH(deletes_oid); + unsigned long deletes = 0; + + oid drops_oid[] = { LLDP_OID, 1, 2, 4 }; + size_t drops_oid_len = OID_LENGTH(drops_oid); + unsigned long drops = 0; + + oid ageouts_oid[] = { LLDP_OID, 1, 2, 5 }; + size_t ageouts_oid_len = OID_LENGTH(ageouts_oid); + unsigned long ageouts = 0; + + /* We also add some extra. Easy ones. */ + oid locport_oid[] = { LLDP_OID, 1, 3, 7, 1, 4, hardware->h_ifindex }; + size_t locport_oid_len = OID_LENGTH(locport_oid); + oid sysname_oid[] = { LLDP_OID, 1, 4, 1, 1, 9, lastchange(rport), + hardware->h_ifindex, rport->p_chassis->c_index }; + size_t sysname_oid_len = OID_LENGTH(sysname_oid); + oid portdescr_oid[] = { LLDP_OID, 1, 4, 1, 1, 8, lastchange(rport), + hardware->h_ifindex, rport->p_chassis->c_index }; + size_t portdescr_oid_len = OID_LENGTH(portdescr_oid); + + netsnmp_variable_list *notification_vars = NULL; + + if (!hardware->h_cfg->g_snmp) return; + + switch (type) { + case NEIGHBOR_CHANGE_DELETED: + log_debug("snmp", "send notification for neighbor deleted on %s", + hardware->h_ifname); + break; + case NEIGHBOR_CHANGE_UPDATED: + log_debug("snmp", "send notification for neighbor updated on %s", + hardware->h_ifname); + break; + case NEIGHBOR_CHANGE_ADDED: + log_debug("snmp", "send notification for neighbor added on %s", + hardware->h_ifname); + break; + } + + TAILQ_FOREACH (h, &hardware->h_cfg->g_hardware, h_entries) { + inserts += h->h_insert_cnt; + deletes += h->h_delete_cnt; + ageouts += h->h_ageout_cnt; + drops += h->h_drop_cnt; + } + + /* snmpTrapOID */ + snmp_varlist_add_variable(¬ification_vars, objid_snmptrap, + objid_snmptrap_len, ASN_OBJECT_ID, (u_char *)notification_oid, + notification_oid_len * sizeof(oid)); + + snmp_varlist_add_variable(¬ification_vars, inserts_oid, inserts_oid_len, + ASN_GAUGE, (u_char *)&inserts, sizeof(inserts)); + snmp_varlist_add_variable(¬ification_vars, deletes_oid, deletes_oid_len, + ASN_GAUGE, (u_char *)&deletes, sizeof(inserts)); + snmp_varlist_add_variable(¬ification_vars, drops_oid, drops_oid_len, + ASN_GAUGE, (u_char *)&drops, sizeof(drops)); + snmp_varlist_add_variable(¬ification_vars, ageouts_oid, ageouts_oid_len, + ASN_GAUGE, (u_char *)&ageouts, sizeof(ageouts)); + + if (type != NEIGHBOR_CHANGE_DELETED) { + snmp_varlist_add_variable(¬ification_vars, locport_oid, + locport_oid_len, ASN_OCTET_STR, (u_char *)hardware->h_ifname, + strnlen(hardware->h_ifname, IFNAMSIZ)); + if (rport->p_chassis->c_name && *rport->p_chassis->c_name != '\0') { + snmp_varlist_add_variable(¬ification_vars, sysname_oid, + sysname_oid_len, ASN_OCTET_STR, + (u_char *)rport->p_chassis->c_name, + strlen(rport->p_chassis->c_name)); + } + if (rport->p_descr) { + snmp_varlist_add_variable(¬ification_vars, portdescr_oid, + portdescr_oid_len, ASN_OCTET_STR, (u_char *)rport->p_descr, + strlen(rport->p_descr)); + } + } + + log_debug("snmp", "sending SNMP trap (%ld, %ld, %ld)", inserts, deletes, + ageouts); + send_v2trap(notification_vars); + snmp_free_varbind(notification_vars); +} + +/* Logging NetSNMP messages */ +static int +agent_log_callback(int major, int minor, void *serverarg, void *clientarg) +{ + struct snmp_log_message *slm = (struct snmp_log_message *)serverarg; + char *msg = strdup(slm->msg); + (void)major; + (void)minor; + (void)clientarg; + + if (msg && msg[strlen(msg) - 1] == '\n') msg[strlen(msg) - 1] = '\0'; + switch (slm->priority) { + case LOG_EMERG: + log_warnx("libsnmp", "%s", msg ? msg : slm->msg); + break; + case LOG_ALERT: + log_warnx("libsnmp", "%s", msg ? msg : slm->msg); + break; + case LOG_CRIT: + log_warnx("libsnmp", "%s", msg ? msg : slm->msg); + break; + case LOG_ERR: + log_warnx("libsnmp", "%s", msg ? msg : slm->msg); + break; + case LOG_WARNING: + log_warnx("libsnmp", "%s", msg ? msg : slm->msg); + break; + case LOG_NOTICE: + log_info("libsnmp", "%s", msg ? msg : slm->msg); + break; + case LOG_INFO: + log_info("libsnmp", "%s", msg ? msg : slm->msg); + break; + case LOG_DEBUG: + log_debug("libsnmp", "%s", msg ? msg : slm->msg); + break; + } + free(msg); + return SNMP_ERR_NOERROR; +} + +void +agent_init(struct lldpd *cfg, const char *agentx) +{ + int rc; + + log_info("snmp", "enable SNMP subagent"); + netsnmp_enable_subagent(); + + log_debug("snmp", "enable logging"); + snmp_disable_log(); + snmp_enable_calllog(); + snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_LOGGING, + agent_log_callback, NULL); + + scfg = cfg; + + /* We are chrooted, we don't want to handle persistent states */ + netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_PERSIST_STATE, + TRUE); + /* Do not load any MIB */ + setenv("MIBS", "", 1); + setenv("MIBDIRS", "/dev/null", 1); + +#ifdef ENABLE_PRIVSEP + /* We provide our UNIX domain transport */ + log_debug("snmp", "register UNIX domain transport"); + agent_priv_register_domain(); +#endif + + if (agentx) + netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_AGENT_X_SOCKET, agentx); + init_agent("lldpAgent"); + REGISTER_MIB("lldp", agent_lldp_vars, variable8, lldp_oid); + init_snmp("lldpAgent"); + + log_debug("snmp", "register to sysORTable"); + if ((rc = register_sysORTable(lldp_oid, OID_LENGTH(lldp_oid), + "lldpMIB implementation by lldpd")) != 0) + log_warnx("snmp", "unable to register to sysORTable (%d)", rc); +} + +void +agent_shutdown() +{ + log_debug("snmp", "agent shutdown"); + unregister_sysORTable(lldp_oid, OID_LENGTH(lldp_oid)); + snmp_shutdown("lldpAgent"); +} |