2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <assert.h>
2N/A#include <string.h>
2N/A#include <strings.h>
2N/A#include <lldp.h>
2N/A#include <liblldp.h>
2N/A#include <sys/types.h>
2N/A#include <sys/vlan.h>
2N/A#include <unistd.h>
2N/A#include <netinet/in.h>
2N/A#include <inttypes.h>
2N/A#include <sys/types.h>
2N/A#include <sys/socket.h>
2N/A#include <netinet/in.h>
2N/A#include <arpa/inet.h>
2N/A#include <liblldp_lldpd.h>
2N/A
2N/Avoid
2N/Alldp_firsttlv(uint8_t *pdu, int pdulen, lldp_tlv_t *tlv)
2N/A{
2N/A uint8_t *end = pdu + pdulen;
2N/A
2N/A tlv->lt_value = NULL;
2N/A if (pdu + 2 > end)
2N/A return;
2N/A
2N/A tlv->lt_type = LLDP_TLV_TYPE(pdu);
2N/A tlv->lt_len = LLDP_TLV_LEN(pdu);
2N/A if (pdu + 2 + tlv->lt_len > end)
2N/A return;
2N/A tlv->lt_value = pdu + 2;
2N/A}
2N/A
2N/Avoid
2N/Alldp_nexttlv(uint8_t *pdu, int pdulen, lldp_tlv_t *old, lldp_tlv_t *new)
2N/A{
2N/A uint8_t *cur = old->lt_value;
2N/A uint8_t *end = pdu + pdulen;
2N/A
2N/A new->lt_value = NULL;
2N/A if (cur + old->lt_len + 2 > end)
2N/A return;
2N/A cur += old->lt_len;
2N/A new->lt_type = LLDP_TLV_TYPE(cur);
2N/A new->lt_len = LLDP_TLV_LEN(cur);
2N/A if (cur + 2 + new->lt_len > end)
2N/A return;
2N/A new->lt_value = cur + 2;
2N/A}
2N/A
2N/Avoid
2N/Alldp_set_typelen(uint8_t *lldpdu, uint8_t type, uint16_t len)
2N/A{
2N/A uint16_t tl = type;
2N/A
2N/A /*
2N/A * `len' always includes 7 bits of type and 9 bits of length of
2N/A * TLV, i.e., 2 bytes of type and length, so subtract 2 from `len'
2N/A * to get the actual length of the value.
2N/A */
2N/A len -= LLDP_TLVHDR_SZ;
2N/A tl = tl << 9;
2N/A tl |= (len & 0x01FF);
2N/A
2N/A *(uint16_t *)(void *)lldpdu = htons(tl);
2N/A}
2N/A
2N/Avoid
2N/Alldp_set_orgspecid_subtype(uint8_t *lldpdu, uint8_t subtype, uint32_t oui,
2N/A uint16_t len)
2N/A{
2N/A uint32_t ouistype = subtype;
2N/A
2N/A /* first set the org. specific tlv type */
2N/A lldp_set_typelen(lldpdu, LLDP_ORGSPECIFIC_TLVTYPE, len);
2N/A lldpdu += LLDP_TLVHDR_SZ;
2N/A
2N/A ouistype |= (oui << 8);
2N/A *(uint32_t *)(void *)lldpdu = htonl(ouistype);
2N/A}
2N/A
2N/Aint
2N/Alldp_end2pdu(uint8_t *lldpdu, size_t pdusize, size_t *msglen)
2N/A{
2N/A size_t tlvlen = LLDP_TLVHDR_SZ;
2N/A
2N/A if (tlvlen > pdusize)
2N/A return (ENOBUFS);
2N/A lldp_set_typelen(lldpdu, LLDP_TLVTYPE_END, tlvlen);
2N/A *msglen += tlvlen;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * +--------+----------------+--------------+-------------+
2N/A * | TLV | TLV information| Chassis ID | Chassis ID |
2N/A * | Type | string len | TLV subtype | |
2N/A * +--------+----------------+--------------+-------------+
2N/A * 7bits 9 bits 8 bits 1 to 255 octets
2N/A */
2N/A
2N/A/*
2N/A * Depending on the value of the chassis ID subtype, the chassis ID can
2N/A * contain alphanumeric data or binary data.
2N/A */
2N/Achar *
2N/Alldp_chassisID2str(lldp_chassisid_t *cid, char *cstr, size_t clen)
2N/A{
2N/A int i = 0;
2N/A int len = 0;
2N/A char *cp = cstr;
2N/A
2N/A bzero(cstr, clen);
2N/A switch (cid->lc_subtype) {
2N/A case LLDP_CHASSIS_ID_IFALIAS:
2N/A case LLDP_CHASSIS_ID_IFNAME:
2N/A case LLDP_CHASSIS_ID_LOCAL:
2N/A (void) strncpy(cstr, (char *)(cid->lc_cid), cid->lc_cidlen);
2N/A break;
2N/A case LLDP_CHASSIS_ID_CHASSIS_COMPONENT:
2N/A case LLDP_CHASSIS_ID_PORT_COMPONENT:
2N/A case LLDP_CHASSIS_ID_MACADDRESS:
2N/A /* convert the series of bytes into numeric string */
2N/A for (i = 0; i < cid->lc_cidlen; i++) {
2N/A if (i == 0) {
2N/A len = snprintf(cp, clen, "%02x",
2N/A cid->lc_cid[i]);
2N/A } else {
2N/A len = snprintf(cp, clen, ":%02x",
2N/A cid->lc_cid[i]);
2N/A }
2N/A cp += len;
2N/A clen -= 2;
2N/A }
2N/A break;
2N/A case LLDP_CHASSIS_ID_IPADDRESS:
2N/A cstr = (char *)inet_ntop(cid->lc_cid[i], &cid->lc_cid[i+1],
2N/A cstr, clen);
2N/A break;
2N/A default:
2N/A return (NULL);
2N/A }
2N/A return (cstr);
2N/A}
2N/A
2N/Achar *
2N/Alldp_chassis_subtype2str(uint8_t type)
2N/A{
2N/A switch (type) {
2N/A case LLDP_CHASSIS_ID_CHASSIS_COMPONENT:
2N/A return ("ChassisComponent");
2N/A case LLDP_CHASSIS_ID_IFALIAS:
2N/A return ("InterfaceAlias");
2N/A case LLDP_CHASSIS_ID_IFNAME:
2N/A return ("InterfaceName");
2N/A case LLDP_CHASSIS_ID_LOCAL:
2N/A return ("Local");
2N/A case LLDP_CHASSIS_ID_PORT_COMPONENT:
2N/A return ("PortComponent");
2N/A case LLDP_CHASSIS_ID_MACADDRESS:
2N/A return ("MacAddress");
2N/A case LLDP_CHASSIS_ID_IPADDRESS:
2N/A return ("NetworkAddress");
2N/A }
2N/A return ("Unknown");
2N/A}
2N/A
2N/Aint
2N/Alldp_nvlist2chassisid(nvlist_t *tlv_nvl, lldp_chassisid_t *cid)
2N/A{
2N/A int err;
2N/A nvlist_t *nvl;
2N/A uint8_t *cidarr;
2N/A
2N/A if ((err = nvlist_lookup_nvlist(tlv_nvl, LLDP_NVP_CHASSISID,
2N/A &nvl)) != 0) {
2N/A return (err);
2N/A }
2N/A
2N/A if ((err = nvlist_lookup_uint8(nvl, LLDP_NVP_CHASSISID_TYPE,
2N/A &cid->lc_subtype)) != 0) {
2N/A return (err);
2N/A }
2N/A if ((err = nvlist_lookup_byte_array(nvl, LLDP_NVP_CHASSISID_VALUE,
2N/A &cidarr, &cid->lc_cidlen)) != 0) {
2N/A return (err);
2N/A }
2N/A (void) memcpy(cid->lc_cid, cidarr, cid->lc_cidlen);
2N/A
2N/A return (err);
2N/A}
2N/A
2N/Aint
2N/Alldp_tlv2chassisid(lldp_tlv_t *tlv, lldp_chassisid_t *cid)
2N/A{
2N/A char cidstr[LLDP_MAX_CHASSISIDSTRLEN];
2N/A
2N/A if (tlv->lt_len < 2 || tlv->lt_len > 256)
2N/A return (EPROTO);
2N/A
2N/A cid->lc_subtype = *tlv->lt_value;
2N/A cid->lc_cidlen = tlv->lt_len - 1;
2N/A (void) memcpy(cid->lc_cid, tlv->lt_value + 1, cid->lc_cidlen);
2N/A
2N/A /* check to see if chassis ID is framed correctly */
2N/A if (lldp_chassisID2str(cid, cidstr, sizeof (cidstr)) == NULL)
2N/A return (EPROTO);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Alldp_chassisid2pdu(lldp_chassisid_t *cid, uint8_t *lldpdu, size_t pdusize,
2N/A size_t *msglen)
2N/A{
2N/A size_t tlvlen;
2N/A
2N/A tlvlen = LLDP_TLVHDR_SZ + cid->lc_cidlen + sizeof (cid->lc_subtype);
2N/A if (tlvlen > pdusize)
2N/A return (ENOBUFS);
2N/A lldp_set_typelen(lldpdu, LLDP_TLVTYPE_CHASSIS_ID, tlvlen);
2N/A lldpdu += LLDP_TLVHDR_SZ;
2N/A bcopy(&cid->lc_subtype, lldpdu, sizeof (cid->lc_subtype));
2N/A lldpdu += sizeof (cid->lc_subtype);
2N/A bcopy(cid->lc_cid, lldpdu, cid->lc_cidlen);
2N/A *msglen += tlvlen;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * +--------+----------------+--------------+-------------+
2N/A * | TLV | TLV information| Port ID | Port ID |
2N/A * | Type | string len | TLV subtype | |
2N/A * +--------+----------------+--------------+-------------+
2N/A * 7bits 9 bits 8 bits 1 to 255 octets
2N/A */
2N/A
2N/A/*
2N/A * Depending on the value of the port ID subtype, the port ID can
2N/A * contain alphanumeric data or binary data.
2N/A */
2N/Achar *
2N/Alldp_portID2str(lldp_portid_t *pid, char *pstr, size_t plen)
2N/A{
2N/A int i = 0;
2N/A int len = 0;
2N/A char *cp = pstr;
2N/A
2N/A bzero(pstr, plen);
2N/A switch (pid->lp_subtype) {
2N/A case LLDP_PORT_ID_IFALIAS:
2N/A case LLDP_PORT_ID_PORT_COMPONENT:
2N/A case LLDP_PORT_ID_IFNAME:
2N/A case LLDP_PORT_ID_LOCAL:
2N/A (void) strncpy(pstr, (char *)(pid->lp_pid), pid->lp_pidlen);
2N/A break;
2N/A case LLDP_PORT_ID_MACADDRESS:
2N/A case LLDP_PORT_ID_AGENT_CICRUITID:
2N/A /* convert the series of bytes into numeric string */
2N/A for (i = 0; i < pid->lp_pidlen; i++) {
2N/A if (i == 0) {
2N/A len = snprintf(cp, plen, "%02x",
2N/A pid->lp_pid[i]);
2N/A } else {
2N/A len = snprintf(cp, plen, ":%02x",
2N/A pid->lp_pid[i]);
2N/A }
2N/A cp += len;
2N/A plen -= 2;
2N/A }
2N/A break;
2N/A case LLDP_PORT_ID_IPADDRESS:
2N/A pstr = (char *)inet_ntop(pid->lp_pid[i], &pid->lp_pid[i+1],
2N/A pstr, plen);
2N/A break;
2N/A default:
2N/A return (NULL);
2N/A }
2N/A return (pstr);
2N/A}
2N/A
2N/Achar *
2N/Alldp_port_subtype2str(uint8_t type)
2N/A{
2N/A switch (type) {
2N/A case LLDP_PORT_ID_IFALIAS:
2N/A return ("InterfaceAlias");
2N/A case LLDP_PORT_ID_PORT_COMPONENT:
2N/A return ("PortComponent");
2N/A case LLDP_PORT_ID_IFNAME:
2N/A return ("InterfaceName");
2N/A case LLDP_PORT_ID_LOCAL:
2N/A return ("Local");
2N/A case LLDP_PORT_ID_MACADDRESS:
2N/A return ("MacAddress");
2N/A case LLDP_PORT_ID_AGENT_CICRUITID:
2N/A return ("AgentCircuitId");
2N/A case LLDP_PORT_ID_IPADDRESS:
2N/A return ("NetworkAddress");
2N/A }
2N/A return ("Unknown");
2N/A}
2N/A
2N/A/* Get the port ID TLV */
2N/Aint
2N/Alldp_nvlist2portid(nvlist_t *tlv_nvl, lldp_portid_t *pid)
2N/A{
2N/A int err = 0;
2N/A nvlist_t *nvl;
2N/A uint8_t *pidarr;
2N/A
2N/A if ((err = nvlist_lookup_nvlist(tlv_nvl, LLDP_NVP_PORTID,
2N/A &nvl)) != 0) {
2N/A return (err);
2N/A }
2N/A if ((err = nvlist_lookup_uint8(nvl, LLDP_NVP_PORTID_TYPE,
2N/A &pid->lp_subtype)) != 0) {
2N/A return (err);
2N/A }
2N/A
2N/A if ((err = nvlist_lookup_byte_array(nvl, LLDP_NVP_PORTID_VALUE,
2N/A &pidarr, &pid->lp_pidlen)) != 0) {
2N/A return (err);
2N/A }
2N/A (void) memcpy(pid->lp_pid, pidarr, pid->lp_pidlen);
2N/A
2N/A return (err);
2N/A}
2N/A
2N/Aint
2N/Alldp_tlv2portid(lldp_tlv_t *tlv, lldp_portid_t *pid)
2N/A{
2N/A char pidstr[LLDP_MAX_PORTIDSTRLEN];
2N/A
2N/A if (tlv->lt_len < 2 || tlv->lt_len > 256)
2N/A return (EPROTO);
2N/A
2N/A pid->lp_subtype = *tlv->lt_value;
2N/A pid->lp_pidlen = tlv->lt_len - 1;
2N/A (void) memcpy(pid->lp_pid, tlv->lt_value + 1, pid->lp_pidlen);
2N/A
2N/A /* check to see if portID is framed correctly */
2N/A if (lldp_portID2str(pid, pidstr, sizeof (pidstr)) == NULL)
2N/A return (EPROTO);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Alldp_portid2pdu(lldp_portid_t *pid, uint8_t *lldpdu, size_t pdusize,
2N/A size_t *msglen)
2N/A{
2N/A size_t tlvlen;
2N/A
2N/A tlvlen = LLDP_TLVHDR_SZ + pid->lp_pidlen + sizeof (pid->lp_subtype);
2N/A if (tlvlen > pdusize)
2N/A return (ENOBUFS);
2N/A
2N/A lldp_set_typelen(lldpdu, LLDP_TLVTYPE_PORT_ID, tlvlen);
2N/A lldpdu += LLDP_TLVHDR_SZ;
2N/A bcopy(&pid->lp_subtype, lldpdu, sizeof (pid->lp_subtype));
2N/A lldpdu += sizeof (pid->lp_subtype);
2N/A bcopy(pid->lp_pid, lldpdu, pid->lp_pidlen);
2N/A *msglen += tlvlen;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * +--------+----------------+----------------------------+
2N/A * | TLV | TLV information| time to live (TTL) |
2N/A * | Type | string len | |
2N/A * +--------+----------------+----------------------------+
2N/A * 7bits 9 bits 2 octets
2N/A */
2N/A
2N/A/* Get the TTL TLV */
2N/Aint
2N/Alldp_nvlist2ttl(nvlist_t *tlv_nvl, uint16_t *ttl)
2N/A{
2N/A return (nvlist_lookup_uint16(tlv_nvl, LLDP_NVP_TTL, ttl));
2N/A}
2N/A
2N/Aint
2N/Alldp_tlv2ttl(lldp_tlv_t *tlv, uint16_t *ttl)
2N/A{
2N/A /*
2N/A * As per 9.2.7.7.2 of IEEE802.1AB, it's fine if the length
2N/A * is greater than two. However we copy only the first 2 bytes
2N/A */
2N/A if (tlv->lt_len < 2)
2N/A return (EPROTO);
2N/A
2N/A (void) memcpy(ttl, tlv->lt_value, sizeof (uint16_t));
2N/A *ttl = ntohs(*ttl);
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Alldp_ttl2pdu(uint16_t ttl, uint8_t *lldpdu, size_t pdusize, size_t *msglen)
2N/A{
2N/A size_t tlvlen;
2N/A
2N/A tlvlen = LLDP_TLVHDR_SZ + sizeof (uint16_t);
2N/A if (tlvlen > pdusize)
2N/A return (ENOBUFS);
2N/A lldp_set_typelen(lldpdu, LLDP_TLVTYPE_TTL, tlvlen);
2N/A lldpdu += LLDP_TLVHDR_SZ;
2N/A bcopy(&ttl, lldpdu, sizeof (ttl));
2N/A *msglen += tlvlen;
2N/A return (0);
2N/A}
2N/A
2N/A/* General routine to add a string to an LLDPDU */
2N/Astatic int
2N/Alldp_str2pdu(uint_t tlvtype, const char *str, uint8_t *lldpdu, size_t pdusize,
2N/A size_t *msglen)
2N/A{
2N/A size_t tlvlen;
2N/A
2N/A tlvlen = strlen(str) + LLDP_TLVHDR_SZ;
2N/A /* check to see if we can accomodate this info in the pdu */
2N/A if (tlvlen > pdusize)
2N/A return (ENOBUFS);
2N/A lldp_set_typelen(lldpdu, tlvtype, tlvlen);
2N/A lldpdu += LLDP_TLVHDR_SZ;
2N/A bcopy(str, lldpdu, tlvlen - LLDP_TLVHDR_SZ);
2N/A *msglen += tlvlen;
2N/A return (0);
2N/A}
2N/A
2N/A/* General routine to parse a string from LLDPDU */
2N/Astatic int
2N/Alldp_tlv2str(lldp_tlv_t *tlv, char *str)
2N/A{
2N/A (void) memcpy(str, tlv->lt_value, tlv->lt_len);
2N/A str[tlv->lt_len] = '\0';
2N/A /*
2N/A * If the TLV information string length value is not exactly equal to
2N/A * the sum of the lengths of all fields contained in the TLV
2N/A * information string then we discard the LLDPDU.
2N/A */
2N/A if (strlen(str) != tlv->lt_len)
2N/A return (EPROTO);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Port Description TLV
2N/A * +--------+----------------+----------------------------+
2N/A * | TLV | TLV information| port description |
2N/A * | Type | string len | |
2N/A * +--------+----------------+----------------------------+
2N/A * 7bits 9 bits 0 to 255 octets
2N/A */
2N/A
2N/A/* Get the port desc TLV */
2N/Aint
2N/Alldp_nvlist2portdescr(nvlist_t *tlv_nvl, char **portdescr)
2N/A{
2N/A return (nvlist_lookup_string(tlv_nvl, LLDP_NVP_PORTDESC, portdescr));
2N/A}
2N/A
2N/Aint
2N/Alldp_tlv2portdescr(lldp_tlv_t *tlv, char *pdescr)
2N/A{
2N/A if (tlv->lt_len > (LLDP_MAX_PORTDESCLEN - 1))
2N/A return (EPROTO);
2N/A
2N/A return (lldp_tlv2str(tlv, pdescr));
2N/A}
2N/A
2N/Aint
2N/Alldp_portdescr2pdu(const char *pdesc, uint8_t *lldpdu, size_t pdusize,
2N/A size_t *msglen)
2N/A{
2N/A return (lldp_str2pdu(LLDP_TLVTYPE_PORT_DESC, pdesc, lldpdu,
2N/A pdusize, msglen));
2N/A}
2N/A
2N/A/*
2N/A * System Name TLV
2N/A * +--------+----------------+----------------------------+
2N/A * | TLV | TLV information| system name |
2N/A * | Type | string len | |
2N/A * +--------+----------------+----------------------------+
2N/A * 7bits 9 bits 0 to 255 octets
2N/A */
2N/A
2N/A/* Get the system Name TLV */
2N/Aint
2N/Alldp_nvlist2sysname(nvlist_t *tlv_nvl, char **sysname)
2N/A{
2N/A return (nvlist_lookup_string(tlv_nvl, LLDP_NVP_SYSNAME, sysname));
2N/A}
2N/A
2N/Aint
2N/Alldp_tlv2sysname(lldp_tlv_t *tlv, char *sysname)
2N/A{
2N/A if (tlv->lt_len > (LLDP_MAX_SYSNAMELEN - 1))
2N/A return (EPROTO);
2N/A
2N/A return (lldp_tlv2str(tlv, sysname));
2N/A}
2N/A
2N/Aint
2N/Alldp_sysname2pdu(const char *sname, uint8_t *lldpdu, size_t pdusize,
2N/A size_t *msglen)
2N/A{
2N/A return (lldp_str2pdu(LLDP_TLVTYPE_SYS_NAME, sname, lldpdu,
2N/A pdusize, msglen));
2N/A}
2N/A
2N/A/*
2N/A * +--------+----------------+----------------------------+
2N/A * | TLV | TLV information| system description |
2N/A * | Type | string len | |
2N/A * +--------+----------------+----------------------------+
2N/A * 7bits 9 bits 0 to 255 octets
2N/A */
2N/A
2N/A/* Get the system description TLV */
2N/Aint
2N/Alldp_nvlist2sysdescr(nvlist_t *tlv_nvl, char **sysdescr)
2N/A{
2N/A return (nvlist_lookup_string(tlv_nvl, LLDP_NVP_SYSDESCR, sysdescr));
2N/A}
2N/A
2N/Aint
2N/Alldp_tlv2sysdescr(lldp_tlv_t *tlv, char *sysdescr)
2N/A{
2N/A if (tlv->lt_len > (LLDP_MAX_SYSDESCLEN - 1))
2N/A return (EPROTO);
2N/A
2N/A return (lldp_tlv2str(tlv, sysdescr));
2N/A}
2N/A
2N/Aint
2N/Alldp_sysdescr2pdu(const char *sdesc, uint8_t *lldpdu, size_t pdusize,
2N/A size_t *msglen)
2N/A{
2N/A return (lldp_str2pdu(LLDP_TLVTYPE_SYS_DESC, sdesc, lldpdu,
2N/A pdusize, msglen));
2N/A}
2N/A
2N/A/*
2N/A * +--------+----------------+--------------+---------------+
2N/A * | TLV | TLV information| system | enabled |
2N/A * | Type | string len | capabilities | capabilities |
2N/A * +--------+----------------+--------------+---------------+
2N/A * 7bits 9 bits 2 octets 2 octets
2N/A */
2N/Avoid
2N/Alldp_syscapab2str(uint16_t capab, char *buf, size_t sz)
2N/A{
2N/A int i;
2N/A uint_t mask = 1;
2N/A size_t nbytes = 0;
2N/A char *syscapab[] = {LLDP_SYSCAPAB_OTHER_NAME,
2N/A LLDP_SYSCAPAB_REPEATER_NAME,
2N/A LLDP_SYSCAPAB_MACBRIDGE_NAME,
2N/A LLDP_SYSCAPAB_WLAN_AP_NAME,
2N/A LLDP_SYSCAPAB_ROUTER_NAME,
2N/A LLDP_SYSCAPAB_TELEPHONE_NAME,
2N/A LLDP_SYSCAPAB_DOCSIS_CD_NAME,
2N/A LLDP_SYSCAPAB_STATION_NAME,
2N/A LLDP_SYSCAPAB_CVLAN_NAME,
2N/A LLDP_SYSCAPAB_SVLAN_NAME,
2N/A LLDP_SYSCAPAB_TPMR_NAME};
2N/A
2N/A bzero(buf, sz);
2N/A if (capab == 0)
2N/A return;
2N/A for (i = 0; i < LLDP_MAX_SYSCAPAB_TYPE; i++, mask <<= 1) {
2N/A if (capab & mask) {
2N/A if (nbytes > 0)
2N/A (void) strlcat(buf, ",", sz);
2N/A nbytes = strlcat(buf, syscapab[i], sz);
2N/A if (nbytes >= sz)
2N/A return;
2N/A }
2N/A }
2N/A}
2N/A
2N/A/* Get the sys capab TLV */
2N/Aint
2N/Alldp_nvlist2syscapab(nvlist_t *tlv_nvl, lldp_syscapab_t *sc)
2N/A{
2N/A nvlist_t *nvl;
2N/A int err = 0;
2N/A
2N/A if ((err = nvlist_lookup_nvlist(tlv_nvl, LLDP_NVP_SYSCAPAB, &nvl)) != 0)
2N/A return (err);
2N/A
2N/A if ((err = nvlist_lookup_uint16(nvl, LLDP_NVP_SUPPORTED_SYSCAPAB,
2N/A &sc->ls_sup_syscapab)) != 0) {
2N/A return (err);
2N/A }
2N/A err = nvlist_lookup_uint16(nvl, LLDP_NVP_ENABLED_SYSCAPAB,
2N/A &sc->ls_enab_syscapab);
2N/A return (err);
2N/A}
2N/A
2N/Aint
2N/Alldp_tlv2syscapab(lldp_tlv_t *tlv, lldp_syscapab_t *sc)
2N/A{
2N/A if (tlv->lt_len != 4)
2N/A return (EPROTO);
2N/A
2N/A (void) memcpy(sc, tlv->lt_value, sizeof (lldp_syscapab_t));
2N/A sc->ls_sup_syscapab = ntohs(sc->ls_sup_syscapab);
2N/A sc->ls_enab_syscapab = ntohs(sc->ls_enab_syscapab);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Alldp_syscapab2pdu(lldp_syscapab_t *sc, uint8_t *lldpdu, size_t pdusize,
2N/A size_t *msglen)
2N/A{
2N/A size_t tlvlen;
2N/A
2N/A tlvlen = LLDP_TLVHDR_SZ + sizeof (uint16_t) + sizeof (uint16_t);
2N/A if (tlvlen > pdusize)
2N/A return (ENOBUFS);
2N/A lldp_set_typelen(lldpdu, LLDP_TLVTYPE_SYS_CAPAB, tlvlen);
2N/A lldpdu += LLDP_TLVHDR_SZ;
2N/A
2N/A sc->ls_sup_syscapab = htons(sc->ls_sup_syscapab);
2N/A sc->ls_enab_syscapab = htons(sc->ls_enab_syscapab);
2N/A
2N/A bcopy(&sc->ls_sup_syscapab, lldpdu, sizeof (uint16_t));
2N/A lldpdu += sizeof (uint16_t);
2N/A
2N/A bcopy(&sc->ls_enab_syscapab, lldpdu, sizeof (uint16_t));
2N/A
2N/A *msglen += tlvlen;
2N/A return (0);
2N/A}
2N/A/*
2N/A * +-----+-------+-------+-------+--------+-----------+----------+------+----+
2N/A * | TLV | TLV | mgmt. | mgmt | mgmt | interface |interface | OID | |
2N/A * | Type| info. |address|address|address | numbering | number |length| OID|
2N/A * | |length |length |subtype| | subtype | | | |
2N/A * +-----+-------+-------+-------+--------+-----------+----------+------+----+
2N/A * 7bits 9bits 1 1 1-31 1 4 1 0-128
2N/A */
2N/Aint
2N/Alldp_tlv2mgmtaddr(lldp_tlv_t *tlv, lldp_mgmtaddr_t *maddr)
2N/A{
2N/A uint8_t *value = tlv->lt_value;
2N/A
2N/A if (tlv->lt_len < 9 || tlv->lt_len > 167)
2N/A return (EPROTO);
2N/A
2N/A bzero(maddr, sizeof (lldp_mgmtaddr_t));
2N/A maddr->lm_addrlen = *value - 1;
2N/A maddr->lm_subtype = *++value;
2N/A
2N/A (void) memcpy(&maddr->lm_addr, ++value, maddr->lm_addrlen);
2N/A value += maddr->lm_addrlen;
2N/A maddr->lm_iftype = *value;
2N/A (void) memcpy(&maddr->lm_ifnumber, ++value,
2N/A sizeof (maddr->lm_ifnumber));
2N/A maddr->lm_ifnumber = ntohl(maddr->lm_ifnumber);
2N/A value += sizeof (maddr->lm_ifnumber);
2N/A maddr->lm_oidlen = *value;
2N/A (void) memcpy(&maddr->lm_oid, ++value, maddr->lm_oidlen);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Alldp_sysport_mgmtaddr2pdu(uint8_t *macaddr, size_t macaddrlen, uint32_t pid,
2N/A uint8_t *lldpdu, size_t pdusize, size_t *msglen)
2N/A{
2N/A size_t tlvlen;
2N/A
2N/A if (macaddr == NULL || macaddrlen == 0)
2N/A return (EINVAL);
2N/A
2N/A tlvlen = LLDP_TLVHDR_SZ + macaddrlen + 8;
2N/A if (tlvlen > pdusize)
2N/A return (ENOBUFS);
2N/A lldp_set_typelen(lldpdu, LLDP_TLVTYPE_MGMT_ADDR, tlvlen);
2N/A lldpdu += LLDP_TLVHDR_SZ;
2N/A *lldpdu++ = macaddrlen + 1;
2N/A *lldpdu++ = LLDP_MGMTADDR_TYPE_ALL802;
2N/A bcopy(macaddr, lldpdu, macaddrlen);
2N/A lldpdu += macaddrlen;
2N/A *lldpdu++ = LLDP_MGMTADDR_IFTYPE_SYSPORT;
2N/A *(uint32_t *)(void *)lldpdu = htonl(pid);
2N/A lldpdu += sizeof (uint32_t);
2N/A *lldpdu = 0;
2N/A *msglen += tlvlen;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Alldp_mgmtaddr2pdu(lldp_mgmtaddr_t *maddr, uint8_t *lldpdu, size_t pdusize,
2N/A size_t *msglen)
2N/A{
2N/A size_t tlvlen;
2N/A
2N/A tlvlen = LLDP_TLVHDR_SZ + maddr->lm_addrlen +
2N/A maddr->lm_oidlen + 8;
2N/A if (tlvlen > pdusize)
2N/A return (ENOBUFS);
2N/A lldp_set_typelen(lldpdu, LLDP_TLVTYPE_MGMT_ADDR, tlvlen);
2N/A lldpdu += LLDP_TLVHDR_SZ;
2N/A *lldpdu++ = maddr->lm_addrlen + 1;
2N/A *lldpdu++ = maddr->lm_subtype;
2N/A bcopy(maddr->lm_addr, lldpdu, maddr->lm_addrlen);
2N/A lldpdu += maddr->lm_addrlen;
2N/A *lldpdu++ = maddr->lm_iftype;
2N/A *(uint32_t *)(void *)lldpdu = htonl(maddr->lm_ifnumber);
2N/A lldpdu += sizeof (uint32_t);
2N/A *lldpdu++ = maddr->lm_oidlen;
2N/A if (maddr->lm_oidlen != 0)
2N/A bcopy(maddr->lm_oid, lldpdu, maddr->lm_oidlen);
2N/A *msglen += tlvlen;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/* Get the organization specific OUI and subtype info */
2N/Avoid
2N/Alldp_get_ouistype(lldp_tlv_t *tlv, uint32_t *oui, uint32_t *subtype)
2N/A{
2N/A uint32_t ouistype;
2N/A
2N/A /* 3 bytes OUI + a byte of subtype */
2N/A ouistype = ntohl(*(uint32_t *)(void *)tlv->lt_value);
2N/A *oui = ((ouistype & 0xFFFFFF00) >> 8);
2N/A *subtype = (ouistype & 0x000000FF);
2N/A}
2N/A
2N/A/*
2N/A * +--------+----------------+----------+--------+------------+------------+
2N/A * | TLV | TLV information| 802.1OUI| 802.1 |aggregation | aggregated |
2N/A * | Type | string len | 00-80-C2| subtype| status | port ID |
2N/A * +--------+----------------+----------+--------+------------+------------+
2N/A * 7bits 9 bits 3 octets 1 octet 1 octet 4 octets
2N/A */
2N/Aint
2N/Alldp_nvlist2aggr(nvlist_t *tlv_nvl, lldp_aggr_t *aggr)
2N/A{
2N/A nvlist_t *anvl;
2N/A int err;
2N/A
2N/A if ((err = lldp_get_nested_nvl(tlv_nvl, LLDP_NVP_ORGANIZATION,
2N/A LLDP_8021_OUI_LIST, LLDP_NVP_AGGR, &anvl)) != 0)
2N/A return (err);
2N/A
2N/A if ((err = nvlist_lookup_uint8(anvl, LLDP_NVP_AGGR_STATUS,
2N/A &aggr->la_status)) != 0) {
2N/A return (err);
2N/A }
2N/A return (nvlist_lookup_uint32(anvl, LLDP_NVP_AGGR_ID, &aggr->la_id));
2N/A}
2N/A
2N/Aint
2N/Alldp_tlv2aggr(lldp_tlv_t *tlv, lldp_aggr_t *ainfop)
2N/A{
2N/A uint8_t *value = tlv->lt_value;
2N/A
2N/A /*
2N/A * Should have the status and aggregation id.
2N/A */
2N/A if (tlv->lt_len != (LLDP_ORGSPECHDR_SZ + sizeof (uint8_t) +
2N/A sizeof (uint32_t))) {
2N/A return (EPROTO);
2N/A }
2N/A
2N/A /* Move past the OUI and subtype */
2N/A value += LLDP_ORGSPECHDR_SZ;
2N/A
2N/A bzero(ainfop, sizeof (lldp_aggr_t));
2N/A (void) memcpy(&ainfop->la_status, value, sizeof (ainfop->la_status));
2N/A value++;
2N/A (void) memcpy(&ainfop->la_id, value, sizeof (ainfop->la_id));
2N/A ainfop->la_id = ntohl(ainfop->la_id);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Alldp_aggr2pdu(lldp_aggr_t *aggrp, uint8_t *lldpdu, size_t pdusize,
2N/A size_t *msglen)
2N/A{
2N/A size_t tlvlen;
2N/A
2N/A tlvlen = LLDP_TLVHDR_SZ + LLDP_ORGSPECHDR_SZ + sizeof (uint8_t) +
2N/A sizeof (uint32_t);
2N/A if (tlvlen > pdusize)
2N/A return (ENOBUFS);
2N/A lldp_set_orgspecid_subtype(lldpdu, LLDP_802dot1OUI_LINK_AGGR_SUBTYPE,
2N/A LLDP_802dot1_OUI, tlvlen);
2N/A lldpdu += LLDP_TLVHDR_SZ + LLDP_ORGSPECHDR_SZ;
2N/A
2N/A *lldpdu = aggrp->la_status;
2N/A lldpdu += sizeof (aggrp->la_status);
2N/A
2N/A *(uint32_t *)(void *)lldpdu = htonl(aggrp->la_id);
2N/A *msglen += tlvlen;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * +--------+----------------+--------------+-------------+---------------+
2N/A * | TLV | TLV information| 802.3OUI | 802.3 | Maximum 802.3 |
2N/A * | Type | string len | 00-12-0F | subtype | Frame size |
2N/A * +--------+----------------+--------------+-------------+---------------+
2N/A * 7bits 9 bits 3 octets 1 octet 2 octets
2N/A */
2N/Aint
2N/Alldp_nvlist2maxfsz(nvlist_t *tlv_nvl, uint16_t *fsz)
2N/A{
2N/A nvlist_t *fnvl;
2N/A int err;
2N/A
2N/A if ((err = lldp_get_nested_nvl(tlv_nvl, LLDP_NVP_ORGANIZATION,
2N/A LLDP_8023_OUI_LIST, LLDP_NVP_MAXFRAMESZ, &fnvl)) != 0)
2N/A return (err);
2N/A return (nvlist_lookup_uint16(fnvl, LLDP_NVP_MAXFRAMESZ, fsz));
2N/A}
2N/A
2N/Aint
2N/Alldp_tlv2maxfsz(lldp_tlv_t *tlv, uint16_t *fsz)
2N/A{
2N/A uint8_t *value = tlv->lt_value;
2N/A
2N/A /* frame size is 2 bytes */
2N/A if (tlv->lt_len != (LLDP_ORGSPECHDR_SZ + sizeof (uint16_t)))
2N/A return (EPROTO);
2N/A
2N/A /* Move past the OUI and subtype */
2N/A value += LLDP_ORGSPECHDR_SZ;
2N/A
2N/A /* extract the frame size */
2N/A (void) memcpy(fsz, value, sizeof (uint16_t));
2N/A *fsz = ntohs(*fsz);
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Alldp_maxfsz2pdu(uint16_t fsz, uint8_t *lldpdu, size_t pdusize, size_t *msglen)
2N/A{
2N/A size_t tlvlen;
2N/A
2N/A tlvlen = LLDP_TLVHDR_SZ + LLDP_ORGSPECHDR_SZ + sizeof (fsz);
2N/A if (tlvlen > pdusize)
2N/A return (ENOBUFS);
2N/A lldp_set_orgspecid_subtype(lldpdu, LLDP_802dot3OUI_MAXFRAMESZ_SUBTYPE,
2N/A LLDP_802dot3_OUI, tlvlen);
2N/A lldpdu += LLDP_TLVHDR_SZ + LLDP_ORGSPECHDR_SZ;
2N/A *(uint16_t *)(void *)lldpdu = htons(fsz);
2N/A *msglen += tlvlen;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * +--------+----------------+--------------+-------------+-------------+
2N/A * | TLV | TLV information| 802.1OUI | 802.1 |port VLAN ID |
2N/A * | Type | string len | 00-80-C2 | subtype | (PVID) |
2N/A * +--------+----------------+--------------+-------------+-------------+
2N/A * 7bits 9 bits 3 octets 1 octet 2 octets
2N/A */
2N/Aint
2N/Alldp_nvlist2pvid(nvlist_t *tlv_nvl, uint16_t *pvid)
2N/A{
2N/A nvlist_t *pnvl;
2N/A int err;
2N/A
2N/A if ((err = lldp_get_nested_nvl(tlv_nvl, LLDP_NVP_ORGANIZATION,
2N/A LLDP_8021_OUI_LIST, LLDP_NVP_PVID, &pnvl)) != 0)
2N/A return (err);
2N/A return (nvlist_lookup_uint16(pnvl, LLDP_NVP_PVID, pvid));
2N/A}
2N/A
2N/Aint
2N/Alldp_tlv2pvid(lldp_tlv_t *tlv, uint16_t *pvid)
2N/A{
2N/A uint8_t *value = tlv->lt_value;
2N/A
2N/A /* sizeof pvid is 2 bytes */
2N/A if (tlv->lt_len != (LLDP_ORGSPECHDR_SZ + sizeof (uint16_t)))
2N/A return (EPROTO);
2N/A
2N/A /* Move past the OUI and subtype */
2N/A value += LLDP_ORGSPECHDR_SZ;
2N/A
2N/A (void) memcpy(pvid, value, sizeof (uint16_t));
2N/A *pvid = ntohs(*pvid);
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Alldp_pvid2pdu(uint16_t pvid, uint8_t *lldpdu, size_t pdusize, size_t *msglen)
2N/A{
2N/A size_t tlvlen;
2N/A
2N/A tlvlen = LLDP_TLVHDR_SZ + LLDP_ORGSPECHDR_SZ + sizeof (pvid);
2N/A if (tlvlen > pdusize)
2N/A return (ENOBUFS);
2N/A lldp_set_orgspecid_subtype(lldpdu, LLDP_802dot1OUI_PVID_SUBTYPE,
2N/A LLDP_802dot1_OUI, tlvlen);
2N/A lldpdu += LLDP_TLVHDR_SZ + LLDP_ORGSPECHDR_SZ;
2N/A *(uint16_t *)(void *)lldpdu = htons(pvid);
2N/A *msglen += tlvlen;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * +--------+----------------+----------+--------+-----+----------+------+
2N/A * | TLV | TLV information| 802.1OUI| 802.1 |VLAN |VLAN name | VLAN |
2N/A * | Type | string len | 00-80-C2| subtype| ID | Len | name |
2N/A * +--------+----------------+----------+--------+-----+----------+------+
2N/A * 7bits 9 bits 3 octets 1 octet 2oct 1 octet 0-32 octets
2N/A */
2N/A/* Caller frees vlan memory */
2N/Aint
2N/Alldp_nvlist2vlan(nvlist_t *tlv_nvl, lldp_vlan_info_t **vinfo, int *count)
2N/A{
2N/A nvlist_t *vnvl;
2N/A nvpair_t *nvp;
2N/A lldp_vlan_info_t *vinfop;
2N/A char vlanname[LLDP_MAX_VLANNAMELEN];
2N/A char *vidstr;
2N/A int cnt = 0;
2N/A
2N/A *count = 0;
2N/A *vinfo = NULL;
2N/A if (lldp_get_nested_nvl(tlv_nvl, LLDP_NVP_ORGANIZATION,
2N/A LLDP_8021_OUI_LIST, LLDP_NVP_VLANNAME, &vnvl) != 0)
2N/A return (ENOENT);
2N/A
2N/A for (nvp = nvlist_next_nvpair(vnvl, NULL); nvp != NULL;
2N/A nvp = nvlist_next_nvpair(vnvl, nvp)) {
2N/A cnt++;
2N/A }
2N/A if (cnt == 0)
2N/A return (ENOENT);
2N/A
2N/A if ((*vinfo = calloc(cnt, sizeof (lldp_vlan_info_t))) == NULL)
2N/A return (ENOMEM);
2N/A
2N/A *count = cnt;
2N/A vinfop = *vinfo;
2N/A for (nvp = nvlist_next_nvpair(vnvl, NULL); nvp != NULL;
2N/A nvp = nvlist_next_nvpair(vnvl, nvp)) {
2N/A (void) strlcpy(vlanname, nvpair_name(nvp), sizeof (vlanname));
2N/A if ((vidstr = strrchr(vlanname, '_')) == NULL)
2N/A continue;
2N/A *vidstr++ = '\0';
2N/A (void) strlcpy(vinfop->lvi_name, vlanname,
2N/A sizeof (vinfop->lvi_name));
2N/A vinfop->lvi_vlen = strlen(vlanname);
2N/A vinfop->lvi_vid = atoi(vidstr);
2N/A vinfop++;
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Alldp_tlv2vlan(lldp_tlv_t *tlv, lldp_vlan_info_t *lvip)
2N/A{
2N/A uint8_t *value = tlv->lt_value;
2N/A
2N/A if (tlv->lt_len < 7 || tlv->lt_len > 39)
2N/A return (EPROTO);
2N/A
2N/A /* Move past the OUI and subtype */
2N/A value += LLDP_ORGSPECHDR_SZ;
2N/A
2N/A bzero(lvip, sizeof (lldp_vlan_info_t));
2N/A (void) memcpy(&lvip->lvi_vid, value, sizeof (lvip->lvi_vid));
2N/A lvip->lvi_vid = ntohs(lvip->lvi_vid);
2N/A value += sizeof (uint16_t);
2N/A
2N/A lvip->lvi_vlen = *value;
2N/A
2N/A if (tlv->lt_len != (LLDP_ORGSPECHDR_SZ + sizeof (lvip->lvi_vid) +
2N/A sizeof (lvip->lvi_vlen) + lvip->lvi_vlen)) {
2N/A return (EPROTO);
2N/A }
2N/A
2N/A value++;
2N/A (void) memcpy(lvip->lvi_name, value, lvip->lvi_vlen);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Alldp_vlan2pdu(lldp_vlan_info_t *lvi, uint8_t *lldpdu, size_t pdusize,
2N/A size_t *msglen)
2N/A{
2N/A size_t tlvlen;
2N/A
2N/A tlvlen = LLDP_TLVHDR_SZ + LLDP_ORGSPECHDR_SZ +
2N/A sizeof (uint16_t) + sizeof (uint8_t) + lvi->lvi_vlen;
2N/A if (tlvlen > pdusize)
2N/A return (ENOBUFS);
2N/A lldp_set_orgspecid_subtype(lldpdu,
2N/A LLDP_802dot1OUI_VLAN_NAME_SUBTYPE, LLDP_802dot1_OUI,
2N/A tlvlen);
2N/A lldpdu += LLDP_TLVHDR_SZ + LLDP_ORGSPECHDR_SZ;
2N/A
2N/A *(uint16_t *)(void *)lldpdu = htons(lvi->lvi_vid);
2N/A lldpdu += sizeof (uint16_t);
2N/A *lldpdu = lvi->lvi_vlen;
2N/A lldpdu += sizeof (lvi->lvi_vlen);
2N/A bcopy(lvi->lvi_name, lldpdu, lvi->lvi_vlen);
2N/A *msglen += tlvlen;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * +------+------------+-----------+---------+---------+-------+------+-------+
2N/A * | TLV | TLV info | OracleOUI | Orcale | Reser- |Vlan ID |PortID |Port |
2N/A * | Type | string len | 00-03-BA | subtype | ved | |subtype | ID |
2N/A * +------+------------+-----------+---------+--------+--------+------+-------+
2N/A * 7 bits 9 bits 3 octets 8 bits 4 octets 2octets 8bits 1-255
2N/A * octets
2N/A */
2N/A/* Caller frees vnic memory */
2N/Aint
2N/Alldp_nvlist2vnic(nvlist_t *tlv_nvl, lldp_vnic_info_t **vinfo, int *count)
2N/A{
2N/A nvlist_t *vnvl;
2N/A nvlist_t *vlnvl;
2N/A nvpair_t *nvp;
2N/A int cnt = 0;
2N/A lldp_vnic_info_t *vnic;
2N/A char *name;
2N/A
2N/A *count = 0;
2N/A *vinfo = NULL;
2N/A if (lldp_get_nested_nvl(tlv_nvl, LLDP_NVP_ORGANIZATION,
2N/A LLDP_ORACLE_OUI_LIST, LLDP_NVP_VNICNAME, &vnvl) != 0)
2N/A return (ENOENT);
2N/A
2N/A for (nvp = nvlist_next_nvpair(vnvl, NULL); nvp != NULL;
2N/A nvp = nvlist_next_nvpair(vnvl, nvp)) {
2N/A cnt++;
2N/A }
2N/A if (cnt == 0)
2N/A return (ENOENT);
2N/A
2N/A if ((*vinfo = calloc(cnt, sizeof (lldp_vnic_info_t))) == NULL)
2N/A return (ENOMEM);
2N/A
2N/A *count = cnt;
2N/A vnic = *vinfo;
2N/A for (nvp = nvlist_next_nvpair(vnvl, NULL); nvp != NULL;
2N/A nvp = nvlist_next_nvpair(vnvl, nvp)) {
2N/A
2N/A name = nvpair_name(nvp);
2N/A (void) nvpair_value_nvlist(nvp, &vlnvl);
2N/A (void) memcpy(vnic->lvni_name, name, LLDP_MAX_VLANNAMELEN);
2N/A vnic->lvni_linkid = DATALINK_INVALID_LINKID;
2N/A vnic->lvni_vid = VLAN_ID_NONE;
2N/A (void) nvlist_lookup_uint16(vlnvl, LLDP_NVP_VNIC_VLANID,
2N/A &vnic->lvni_vid);
2N/A (void) nvlist_lookup_uint32(vlnvl, LLDP_NVP_VNIC_LINKID,
2N/A &vnic->lvni_linkid);
2N/A (void) lldp_nvlist2portid(vlnvl, &vnic->lvni_portid);
2N/A vnic++;
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Alldp_tlv2vnic(lldp_tlv_t *tlv, lldp_vnic_info_t *lvip)
2N/A{
2N/A lldp_portid_t *pidp;
2N/A uint8_t *value = tlv->lt_value;
2N/A
2N/A if (tlv->lt_len < LLDP_MIN_VNICTLV_LEN ||
2N/A tlv->lt_len > LLDP_MAX_VNICTLV_LEN) {
2N/A return (EPROTO);
2N/A }
2N/A
2N/A /* Move past the OUI and subtype */
2N/A value += LLDP_ORGSPECHDR_SZ;
2N/A
2N/A /* Move past the reserved bits */
2N/A value += sizeof (uint32_t);
2N/A
2N/A bzero(lvip, sizeof (lldp_vnic_info_t));
2N/A /* Get the VLAN ID */
2N/A (void) memcpy(&lvip->lvni_vid, value, sizeof (lvip->lvni_vid));
2N/A lvip->lvni_vid = ntohs(lvip->lvni_vid);
2N/A value += sizeof (lvip->lvni_vid);
2N/A
2N/A /* get the port id info */
2N/A pidp = &lvip->lvni_portid;
2N/A pidp->lp_subtype = *value;
2N/A value += sizeof (pidp->lp_subtype);
2N/A pidp->lp_pidlen = tlv->lt_len - 11;
2N/A (void) memcpy(pidp->lp_pid, value, pidp->lp_pidlen);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Alldp_vnic2pdu(lldp_vnic_info_t *lvi, uint8_t *lldpdu, size_t pdusize,
2N/A size_t *msglen)
2N/A{
2N/A lldp_portid_t *pid;
2N/A size_t tlvlen;
2N/A
2N/A pid = &lvi->lvni_portid;
2N/A tlvlen = LLDP_TLVHDR_SZ + LLDP_ORGSPECHDR_SZ + sizeof (uint32_t) +
2N/A sizeof (uint16_t) + sizeof (uint8_t) + pid->lp_pidlen;
2N/A
2N/A if (tlvlen > pdusize)
2N/A return (ENOBUFS);
2N/A lldp_set_orgspecid_subtype(lldpdu,
2N/A LLDP_ORACLEOUI_VNIC_SUBTYPE, LLDP_ORACLE_OUI, tlvlen);
2N/A lldpdu += LLDP_TLVHDR_SZ + LLDP_ORGSPECHDR_SZ;
2N/A
2N/A /* Reserved bits */
2N/A lldpdu += sizeof (uint32_t);
2N/A
2N/A /* Vlan ID */
2N/A *(uint16_t *)(void *)lldpdu = htons(lvi->lvni_vid);
2N/A lldpdu += sizeof (uint16_t);
2N/A
2N/A /* Port ID subtype */
2N/A *lldpdu = pid->lp_subtype;
2N/A lldpdu += sizeof (uint8_t);
2N/A
2N/A /* Port ID */
2N/A bcopy(pid->lp_pid, lldpdu, pid->lp_pidlen);
2N/A
2N/A *msglen += tlvlen;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * +--------+----------------+----------+--------+------------------------+
2N/A * | TLV | TLV information| 802.1OUI| 802.1 |Reserved| Application |
2N/A * | Type | string len | 00-80-C2|subtype | | Priority Table|
2N/A * +--------+----------------+----------+--------+------------------------+
2N/A * 7bits 9 bits 3-octets 1-octet 8-bits Multiple of 3 octet
2N/A * Caller frees memory.
2N/A */
2N/Aint
2N/Alldp_nvlist2appln(nvlist_t *tlv_nvl, lldp_appln_t **appln, uint_t *nappln)
2N/A{
2N/A nvlist_t *anvl;
2N/A nvpair_t *nvp;
2N/A lldp_appln_t *app;
2N/A char idstr[LLDP_STRSIZE], *selstr;
2N/A int cnt = 0;
2N/A
2N/A *nappln = 0;
2N/A *appln = NULL;
2N/A if (lldp_get_nested_nvl(tlv_nvl, LLDP_NVP_ORGANIZATION,
2N/A LLDP_8021_OUI_LIST, LLDP_NVP_APPLN, &anvl) != 0) {
2N/A return (ENOENT);
2N/A }
2N/A
2N/A for (nvp = nvlist_next_nvpair(anvl, NULL); nvp != NULL;
2N/A nvp = nvlist_next_nvpair(anvl, nvp)) {
2N/A cnt++;
2N/A }
2N/A if (cnt == 0)
2N/A return (ENOENT);
2N/A
2N/A if ((*appln = calloc(cnt, sizeof (lldp_appln_t))) == NULL)
2N/A return (ENOMEM);
2N/A
2N/A *nappln = cnt;
2N/A app = *appln;
2N/A for (nvp = nvlist_next_nvpair(anvl, NULL); nvp != NULL;
2N/A nvp = nvlist_next_nvpair(anvl, nvp)) {
2N/A (void) strlcpy(idstr, nvpair_name(nvp), sizeof (idstr));
2N/A if ((selstr = strchr(idstr, '_')) == NULL)
2N/A continue;
2N/A *selstr++ = '\0';
2N/A app->la_id = atoi(idstr);
2N/A app->la_sel = atoi(selstr);
2N/A (void) nvpair_value_uint8(nvp, &app->la_pri);
2N/A app++;
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Specifically get a specific application info from application TLV.
2N/A */
2N/Aint
2N/Alldp_nvlist2app(nvlist_t *nvl, uint16_t id, uint8_t sel, lldp_appln_t *ainfop)
2N/A{
2N/A lldp_appln_t *app;
2N/A lldp_appln_t *appln;
2N/A uint_t nappln;
2N/A int err = 0;
2N/A int count;
2N/A
2N/A if ((err = lldp_nvlist2appln(nvl, &appln, &nappln)) != 0)
2N/A return (err);
2N/A
2N/A app = appln;
2N/A for (count = 0; count < nappln; count++) {
2N/A if (app->la_id == id && app->la_sel == sel) {
2N/A bcopy(app, ainfop, sizeof (lldp_appln_t));
2N/A free(appln);
2N/A return (0);
2N/A }
2N/A }
2N/A free(appln);
2N/A return (-1);
2N/A}
2N/A
2N/A/* Specifically get FCoE application priority */
2N/Aint
2N/Alldp_nvlist2fcoepri(nvlist_t *nvl, uint8_t *pri)
2N/A{
2N/A lldp_appln_t appln;
2N/A int err = 0;
2N/A
2N/A if ((err = lldp_nvlist2app(nvl, DCBX_FCOE_APPLICATION_ID1,
2N/A DCBX_FCOE_APPLICATION_SF, &appln)) == 0 ||
2N/A (err = lldp_nvlist2app(nvl, DCBX_FCOE_APPLICATION_ID2,
2N/A DCBX_FCOE_APPLICATION_SF, &appln)) == 0) {
2N/A *pri = appln.la_pri;
2N/A }
2N/A
2N/A return (err);
2N/A}
2N/A
2N/A/* Caller frees memory */
2N/Aint
2N/Alldp_tlv2appln(lldp_tlv_t *tlv, lldp_appln_t **appln, uint_t *nappln)
2N/A{
2N/A int i, cnt;
2N/A lldp_appln_t *app;
2N/A uint8_t u8;
2N/A uint8_t *value = tlv->lt_value;
2N/A
2N/A /* Should have at least one application priority specified. */
2N/A if (tlv->lt_len < (LLDP_ORGSPECHDR_SZ + sizeof (uint8_t) + 3))
2N/A return (EPROTO);
2N/A
2N/A /* Move past the OUI and subtype */
2N/A value += LLDP_ORGSPECHDR_SZ;
2N/A
2N/A /*
2N/A * Get the number of applications, 5 is the OUI, Subtype and Reserved,
2N/A * 3 is the size of one application priority.
2N/A */
2N/A if (((tlv->lt_len - (LLDP_ORGSPECHDR_SZ + 1)) % 3) != 0)
2N/A return (EPROTO);
2N/A
2N/A cnt = (tlv->lt_len - (LLDP_ORGSPECHDR_SZ + 1)) / 3;
2N/A if ((*appln = calloc(cnt, sizeof (lldp_appln_t))) == NULL)
2N/A return (ENOMEM);
2N/A
2N/A /* move past the reserved byte */
2N/A value++;
2N/A *nappln = cnt;
2N/A app = *appln;
2N/A for (i = 0; i < cnt; i++, app++) {
2N/A u8 = *value;
2N/A app->la_pri = (u8 & 0xE0) >> 5;
2N/A app->la_sel = u8 & 0x3;
2N/A value++;
2N/A app->la_id = ntohs(*(uint16_t *)(void *)value);
2N/A value += sizeof (app->la_id);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Alldp_appln2pdu(lldp_appln_t *appln, uint_t nappln, uint8_t *lldpdu,
2N/A size_t pdusize, size_t *msglen)
2N/A{
2N/A size_t tlvlen;
2N/A size_t len;
2N/A lldp_appln_t *app;
2N/A int i;
2N/A
2N/A len = nappln * (3 * sizeof (uint8_t));
2N/A tlvlen = LLDP_TLVHDR_SZ + LLDP_ORGSPECHDR_SZ + sizeof (uint8_t) + len;
2N/A if (tlvlen > pdusize)
2N/A return (ENOBUFS);
2N/A
2N/A lldp_set_orgspecid_subtype(lldpdu, LLDP_802dot1OUI_APPLN_SUBTYPE,
2N/A LLDP_802dot1_OUI, tlvlen);
2N/A lldpdu += LLDP_TLVHDR_SZ + LLDP_ORGSPECHDR_SZ;
2N/A
2N/A /* Reserved */
2N/A *(uint8_t *)(void *)lldpdu = 0;
2N/A lldpdu++;
2N/A
2N/A app = appln;
2N/A for (i = 0; i < nappln; i++) {
2N/A *(uint8_t *)(void *)lldpdu = (app->la_pri << 5) | app->la_sel;
2N/A lldpdu++;
2N/A *(uint16_t *)(void *)lldpdu = htons(app->la_id);
2N/A lldpdu += sizeof (uint16_t);
2N/A app++;
2N/A }
2N/A *msglen += tlvlen;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * +-----+---------------+----------+--------+-----+----+-----+---+------+
2N/A * |TLV |TLV information| 802.1OUI| 802.1 |Will-|MBC |Reser|PFC|PFC |
2N/A * |Type | string len | 00-80-C2| subtype|ing | |ved |cap|enable|
2N/A * +-----+---------------+----------+--------+-----+----+-----+---+------+
2N/A * 7-bits 9 bits 3 octets 1 octet 1b 1b 2b 4b 1-octet
2N/A */
2N/Aint
2N/Alldp_nvlist2pfc(nvlist_t *tlv_nvl, lldp_pfc_t *pfc)
2N/A{
2N/A nvlist_t *pnvl;
2N/A int err;
2N/A
2N/A if ((err = lldp_get_nested_nvl(tlv_nvl, LLDP_NVP_ORGANIZATION,
2N/A LLDP_8021_OUI_LIST, LLDP_NVP_PFC, &pnvl)) != 0) {
2N/A return (err);
2N/A }
2N/A if ((err = nvlist_lookup_boolean_value(pnvl, LLDP_NVP_WILLING,
2N/A &pfc->lp_willing)) != 0) {
2N/A return (err);
2N/A }
2N/A if ((err = nvlist_lookup_boolean_value(pnvl, LLDP_NVP_PFC_MBC,
2N/A &pfc->lp_mbc)) != 0) {
2N/A return (err);
2N/A }
2N/A if ((err = nvlist_lookup_uint8(pnvl, LLDP_NVP_PFC_CAP,
2N/A &pfc->lp_cap)) != 0) {
2N/A return (err);
2N/A }
2N/A if ((err = nvlist_lookup_uint8(pnvl, LLDP_NVP_PFC_ENABLE,
2N/A &pfc->lp_enable)) != 0) {
2N/A return (err);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Alldp_tlv2pfc(lldp_tlv_t *tlv, lldp_pfc_t *pfc)
2N/A{
2N/A uint8_t u8;
2N/A uint8_t *value = tlv->lt_value;
2N/A
2N/A /*
2N/A * Should have the Willing(1b), MBC(1b), Reserved (2b), PFC Cap(4b),
2N/A * PFC enable (8b).
2N/A */
2N/A if (tlv->lt_len != (LLDP_ORGSPECHDR_SZ + sizeof (uint16_t)))
2N/A return (EPROTO);
2N/A
2N/A /* Move past the OUI and subtype */
2N/A value += LLDP_ORGSPECHDR_SZ;
2N/A
2N/A u8 = *value;
2N/A pfc->lp_willing = u8 >> 7;
2N/A pfc->lp_mbc = (u8 >> 6) & 0x1;
2N/A pfc->lp_cap = u8 & 0xF;
2N/A value++;
2N/A pfc->lp_enable = *value;
2N/A
2N/A /* Validate PFC : PFC can't be enabled for TCs not supported */
2N/A if ((pfc->lp_enable >> pfc->lp_cap) > 0)
2N/A return (EINVAL);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/* ARGSUSED */
2N/Aint
2N/Alldp_pfc2pdu(lldp_pfc_t *pfc, uint8_t *lldpdu, size_t pdusize,
2N/A size_t *msglen)
2N/A{
2N/A size_t tlvlen;
2N/A uint8_t val;
2N/A
2N/A tlvlen = LLDP_TLVHDR_SZ + LLDP_ORGSPECHDR_SZ + sizeof (uint16_t);
2N/A if (tlvlen > pdusize)
2N/A return (ENOBUFS);
2N/A lldp_set_orgspecid_subtype(lldpdu, LLDP_802dot1OUI_PFC_SUBTYPE,
2N/A LLDP_802dot1_OUI, tlvlen);
2N/A lldpdu += LLDP_TLVHDR_SZ + LLDP_ORGSPECHDR_SZ;
2N/A
2N/A val = pfc->lp_willing << 7 | pfc->lp_mbc << 6 | pfc->lp_cap;
2N/A *(uint8_t *)(void *)lldpdu = val;
2N/A lldpdu += sizeof (uint8_t);
2N/A *(uint8_t *)(void *)lldpdu = pfc->lp_enable;
2N/A
2N/A *msglen += tlvlen;
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Alldp_nvlist2pending(nvlist_t *tlv_nvl, boolean_t *pending)
2N/A{
2N/A return (nvlist_lookup_boolean_value(tlv_nvl, LLDP_NVP_PFC_PENDING,
2N/A pending));
2N/A}
2N/A
2N/Aint
2N/Alldp_nvlist2evb(nvlist_t *tlv_nvl, lldp_evb_t *evb)
2N/A{
2N/A nvlist_t *pnvl;
2N/A int err;
2N/A
2N/A if ((err = lldp_get_nested_nvl(tlv_nvl, LLDP_NVP_ORGANIZATION,
2N/A LLDP_8021_OUI_LIST, LLDP_NVP_EVB, &pnvl)) != 0) {
2N/A return (err);
2N/A }
2N/A if ((err = nvlist_lookup_boolean_value(pnvl, LLDP_NVP_EVBS_SGID,
2N/A &evb->le_sgid)) != 0) {
2N/A return (err);
2N/A }
2N/A if ((err = nvlist_lookup_boolean_value(pnvl, LLDP_NVP_EVBS_RRREQ,
2N/A &evb->le_rrreq)) != 0) {
2N/A return (err);
2N/A }
2N/A if ((err = nvlist_lookup_uint8(pnvl, LLDP_NVP_EVBS_RRSTAT,
2N/A &evb->le_rrstat)) != 0) {
2N/A return (err);
2N/A }
2N/A if ((err = nvlist_lookup_boolean_value(pnvl, LLDP_NVP_EVBB_BGID,
2N/A &evb->le_bgid)) != 0) {
2N/A return (err);
2N/A }
2N/A if ((err = nvlist_lookup_boolean_value(pnvl, LLDP_NVP_EVBB_RRCAP,
2N/A &evb->le_rrcap)) != 0) {
2N/A return (err);
2N/A }
2N/A if ((err = nvlist_lookup_boolean_value(pnvl, LLDP_NVP_EVBB_RRCTR,
2N/A &evb->le_rrctr)) != 0) {
2N/A return (err);
2N/A }
2N/A if ((err = nvlist_lookup_uint8(pnvl, LLDP_NVP_EVB_MODE,
2N/A &evb->le_mode)) != 0) {
2N/A return (err);
2N/A }
2N/A if ((err = nvlist_lookup_boolean_value(pnvl, LLDP_NVP_EVB_ROLRWD,
2N/A &evb->le_rol_rwd)) != 0) {
2N/A return (err);
2N/A }
2N/A if ((err = nvlist_lookup_uint8(pnvl, LLDP_NVP_EVB_RWD,
2N/A &evb->le_rwd)) != 0) {
2N/A return (err);
2N/A }
2N/A if ((err = nvlist_lookup_boolean_value(pnvl, LLDP_NVP_EVB_ROLRKA,
2N/A &evb->le_rol_rka)) != 0) {
2N/A return (err);
2N/A }
2N/A if ((err = nvlist_lookup_uint8(pnvl, LLDP_NVP_EVB_RKA,
2N/A &evb->le_rka)) != 0) {
2N/A return (err);
2N/A }
2N/A if ((err = nvlist_lookup_uint8(pnvl, LLDP_NVP_EVB_R,
2N/A &evb->le_r)) != 0) {
2N/A return (err);
2N/A }
2N/A if ((err = nvlist_lookup_uint8(pnvl, LLDP_NVP_EVB_RTE,
2N/A &evb->le_rte)) != 0) {
2N/A return (err);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Alldp_nvlist2ets(nvlist_t *tlv_nvl, lldp_ets_t *ets, boolean_t config)
2N/A{
2N/A nvlist_t *envl;
2N/A int err;
2N/A uint_t n;
2N/A char *etsname;
2N/A uint8_t *arr;
2N/A
2N/A etsname = (config ? LLDP_NVP_ETSCFG : LLDP_NVP_ETSRECO);
2N/A if ((err = lldp_get_nested_nvl(tlv_nvl, LLDP_NVP_ORGANIZATION,
2N/A LLDP_8021_OUI_LIST, etsname, &envl)) != 0) {
2N/A return (err);
2N/A }
2N/A bzero(ets, sizeof (*ets));
2N/A if (config) {
2N/A if ((err = nvlist_lookup_boolean_value(envl, LLDP_NVP_WILLING,
2N/A &ets->le_willing)) != 0) {
2N/A return (err);
2N/A }
2N/A
2N/A if ((err = nvlist_lookup_boolean_value(envl, LLDP_NVP_ETS_CBS,
2N/A &ets->le_cbs)) != 0) {
2N/A return (err);
2N/A }
2N/A
2N/A if ((err = nvlist_lookup_uint8(envl, LLDP_NVP_ETS_TCS,
2N/A &ets->le_ntcs)) != 0) {
2N/A return (err);
2N/A }
2N/A }
2N/A if ((err = nvlist_lookup_uint8_array(envl, LLDP_NVP_ETS_BAT,
2N/A &arr, &n)) != 0) {
2N/A return (err);
2N/A }
2N/A bcopy(arr, ets->le_bat, n);
2N/A assert(n == 8);
2N/A if ((err = nvlist_lookup_uint8_array(envl, LLDP_NVP_ETS_TSA,
2N/A &arr, &n)) != 0) {
2N/A return (err);
2N/A }
2N/A bcopy(arr, ets->le_tsa, n);
2N/A assert(n == 8);
2N/A if ((err = nvlist_lookup_uint8_array(envl, LLDP_NVP_ETS_PAT,
2N/A &arr, &n)) != 0) {
2N/A return (err);
2N/A }
2N/A bcopy(arr, ets->le_pat, n);
2N/A assert(n == 8);
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Alldp_tlv2ets(lldp_tlv_t *tlv, lldp_ets_t *ets, boolean_t config)
2N/A{
2N/A uint8_t u8;
2N/A uint8_t *value = tlv->lt_value;
2N/A int i;
2N/A
2N/A if (tlv->lt_len != LLDP_ETS_INFOSTR_LEN)
2N/A return (EPROTO);
2N/A
2N/A value += LLDP_ORGSPECHDR_SZ;
2N/A bzero(ets, sizeof (*ets));
2N/A
2N/A if (config) {
2N/A u8 = *value;
2N/A ets->le_willing = u8 >> 7;
2N/A ets->le_cbs = (u8 >> 6) & 0x1;
2N/A ets->le_ntcs = ((u8 & 0x7) == 0 ? 8 : (u8 & 0x7));
2N/A }
2N/A
2N/A value++;
2N/A for (i = 0; i < 4; i++) {
2N/A ets->le_pat[i * 2] = *value >> 4;
2N/A ets->le_pat[(i * 2) + 1] = *value & 0x0F;
2N/A value++;
2N/A }
2N/A bcopy(value, ets->le_bat, sizeof (ets->le_bat));
2N/A value += sizeof (uint64_t);
2N/A bcopy(value, ets->le_tsa, sizeof (ets->le_tsa));
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Alldp_ets2pdu(lldp_ets_t *ets, uint8_t *lldpdu, size_t pdusize,
2N/A size_t *msglen, boolean_t config)
2N/A{
2N/A size_t tlvlen;
2N/A uint8_t subtype;
2N/A uint8_t val;
2N/A int i;
2N/A
2N/A tlvlen = LLDP_TLVHDR_SZ + LLDP_ETS_INFOSTR_LEN;
2N/A if (tlvlen > pdusize)
2N/A return (ENOBUFS);
2N/A
2N/A subtype = (config ? LLDP_802dot1OUI_ETSCFG_SUBTYPE :
2N/A LLDP_802dot1OUI_ETSRECO_SUBTYPE);
2N/A lldp_set_orgspecid_subtype(lldpdu, subtype, LLDP_802dot1_OUI, tlvlen);
2N/A lldpdu += LLDP_TLVHDR_SZ + LLDP_ORGSPECHDR_SZ;
2N/A
2N/A if (config) {
2N/A val = ets->le_willing << 7 | ets->le_cbs << 6;
2N/A /* if we support 8 TCs then MAX TCs will be set to 0 */
2N/A if (ets->le_ntcs != 8)
2N/A val |= ets->le_ntcs;
2N/A *lldpdu = val;
2N/A }
2N/A lldpdu += sizeof (uint8_t);
2N/A for (i = 0; i < 8; i++) {
2N/A switch ((i + 1) % 2) {
2N/A case 0:
2N/A *lldpdu |= ets->le_pat[i];
2N/A lldpdu++;
2N/A break;
2N/A case 1:
2N/A *lldpdu = ets->le_pat[i] << 4;
2N/A break;
2N/A }
2N/A }
2N/A bcopy(ets->le_bat, lldpdu, sizeof (ets->le_bat));
2N/A lldpdu += sizeof (uint64_t);
2N/A bcopy(ets->le_tsa, lldpdu, sizeof (ets->le_tsa));
2N/A *msglen += tlvlen;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * +-----+---------------+----------+--------+----------+--+---+----+----+
2N/A * |TLV |TLV information| 802.1OUI| 802.1 |EVB Bridge|R |RTE|EVB |ROL |
2N/A * |Type | string len | 00-80-C2| subtype|status | | |mode| |
2N/A * +-----+---------------+----------+--------+----------+--+---+----+----+
2N/A * 7-bits 9 bits 3 octets 1 octet 1 octet 3b 5b 2b 1b
2N/A * +---+--------+---+---+
2N/A * |RWD|Reserved|ROL|RKA|
2N/A * | | | | |
2N/A * +---+--------+-------+
2N/A * 5b 2b 1b 5b
2N/A */
2N/Aint
2N/Alldp_tlv2evb(lldp_tlv_t *tlv, lldp_evb_t *evb)
2N/A{
2N/A uint8_t u8;
2N/A uint8_t *value = tlv->lt_value;
2N/A
2N/A if (tlv->lt_len != (LLDP_ORGSPECHDR_SZ + LLDP_EVB_TLVLEN))
2N/A return (EPROTO);
2N/A
2N/A /* Move past the OUI and subtype */
2N/A value += LLDP_ORGSPECHDR_SZ;
2N/A
2N/A u8 = *value;
2N/A evb->le_bgid = LLDP_EVBB_BGID(u8);
2N/A evb->le_rrcap = LLDP_EVBB_RRCAP(u8);
2N/A evb->le_rrctr = LLDP_EVBB_RRCTR(u8);
2N/A value++;
2N/A
2N/A u8 = *value;
2N/A evb->le_sgid = LLDP_EVBS_SGID(u8);
2N/A evb->le_rrreq = LLDP_EVBS_RRREQ(u8);
2N/A evb->le_rrstat = LLDP_EVBS_RRSTAT(u8);
2N/A value++;
2N/A
2N/A u8 = *value;
2N/A evb->le_r = LLDP_EVB_R(u8);
2N/A evb->le_rte = LLDP_EVB_RTE(u8);
2N/A value++;
2N/A
2N/A u8 = *value;
2N/A evb->le_mode = LLDP_EVB_MODE(u8);
2N/A evb->le_rol_rwd = LLDP_EVB_ROL_RWD(u8) == 1;
2N/A evb->le_rwd = LLDP_EVB_RWD(u8);
2N/A value++;
2N/A
2N/A u8 = *value;
2N/A evb->le_rol_rka = LLDP_EVB_ROL_RKA(u8) == 1;
2N/A evb->le_rka = LLDP_EVB_RKA(u8);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Alldp_evb2pdu(lldp_evb_t *evb, uint8_t *lldpdu, size_t pdusize,
2N/A size_t *msglen)
2N/A{
2N/A size_t tlvlen;
2N/A uint8_t val;
2N/A uint8_t rolrwd = evb->le_rol_rwd ? 1 : 0;
2N/A uint8_t rolrka = evb->le_rol_rka ? 1 : 0;
2N/A uint8_t rrreq = evb->le_rrreq ? 1 : 0;
2N/A uint8_t rrcap = evb->le_rrcap ? 1 : 0;
2N/A uint8_t rrctr = evb->le_rrctr ? 1 : 0;
2N/A
2N/A tlvlen = LLDP_TLVHDR_SZ + LLDP_ORGSPECHDR_SZ + LLDP_EVB_TLVLEN;
2N/A if (tlvlen > pdusize)
2N/A return (ENOBUFS);
2N/A lldp_set_orgspecid_subtype(lldpdu, LLDP_802dot1OUI_EVB_SUBTYPE,
2N/A LLDP_802dot1_OUI, tlvlen);
2N/A lldpdu += LLDP_TLVHDR_SZ + LLDP_ORGSPECHDR_SZ;
2N/A
2N/A val = evb->le_bgid << 2 | rrcap << 1 | rrctr;
2N/A *(uint8_t *)(void *)lldpdu = val;
2N/A lldpdu += sizeof (uint8_t);
2N/A
2N/A val = evb->le_sgid << 3 | rrreq << 2 | evb->le_rrstat;
2N/A *(uint8_t *)(void *)lldpdu = val;
2N/A lldpdu += sizeof (uint8_t);
2N/A
2N/A val = evb->le_r << 5 | evb->le_rte;
2N/A *(uint8_t *)(void *)lldpdu = val;
2N/A *(uint8_t *)(void *)lldpdu = val;
2N/A lldpdu += sizeof (uint8_t);
2N/A
2N/A val = evb->le_mode << 6 | rolrwd << 5 | evb->le_rwd;
2N/A *(uint8_t *)(void *)lldpdu = val;
2N/A lldpdu += sizeof (uint8_t);
2N/A
2N/A val = rolrka << 5 | evb->le_rka;
2N/A *(uint8_t *)(void *)lldpdu = val;
2N/A lldpdu += sizeof (uint8_t);
2N/A
2N/A *msglen += tlvlen;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * For unknown TLVs, just return the byte array as a string
2N/A */
2N/Aint
2N/Alldp_tlv2unknown(lldp_tlv_t *tlv, char *bstr, size_t blen)
2N/A{
2N/A uint8_t *pdu = tlv->lt_value - 2; /* Print from the TLV start */
2N/A int pdulen = tlv->lt_len + 2;
2N/A char *cp = bstr;
2N/A int len = 0;
2N/A int i;
2N/A
2N/A if (blen < tlv->lt_len + 2)
2N/A return (ENOSPC);
2N/A
2N/A /* convert the series of bytes into numeric string */
2N/A for (i = 0; i < pdulen; i++)
2N/A len += snprintf(cp + len, blen - len, "%02x", pdu[i]);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * +-----+-------+-------+-------+--------+-----------+----------+------+----+
2N/A * | TLV | TLV | mgmt. | mgmt | mgmt | interface |interface | OID | |
2N/A * | Type| info. |address|address|address | numbering | number |length| OID|
2N/A * | |length |length |subtype| | subtype | | | |
2N/A * +-----+-------+-------+-------+--------+-----------+----------+------+----+
2N/A * 7bits 9bits 1 1 1-31 1 4 1 0-128
2N/A */
2N/A/* caller frees the memory */
2N/Aint
2N/Alldp_nvlist2mgmtaddr(nvlist_t *nvl, const char *str, lldp_mgmtaddr_t **maddrpp,
2N/A int *count)
2N/A{
2N/A nvlist_t *mnvl = NULL, *mgmtnvl = NULL;
2N/A nvpair_t *nvp;
2N/A uint_t arrsz;
2N/A uint8_t *arr;
2N/A lldp_mgmtaddr_t *maddrp;
2N/A
2N/A *maddrpp = NULL;
2N/A *count = 0;
2N/A if (nvlist_lookup_nvlist(nvl, LLDP_NVP_MGMTADDR, &mnvl) != 0)
2N/A return (ENOENT);
2N/A for (nvp = nvlist_next_nvpair(mnvl, NULL); nvp != NULL;
2N/A nvp = nvlist_next_nvpair(mnvl, nvp)) {
2N/A if (str == NULL) {
2N/A ++*count;
2N/A } else if (strcmp(str, nvpair_name(nvp)) == 0) {
2N/A ++*count;
2N/A break;
2N/A }
2N/A }
2N/A if (*count == 0)
2N/A return (ENOENT);
2N/A if ((*maddrpp = calloc(*count, sizeof (lldp_mgmtaddr_t))) == NULL)
2N/A return (ENOMEM);
2N/A maddrp = *maddrpp;
2N/A for (nvp = nvlist_next_nvpair(mnvl, NULL); nvp != NULL;
2N/A nvp = nvlist_next_nvpair(mnvl, nvp)) {
2N/A if (str != NULL && strcmp(str, nvpair_name(nvp)) != 0)
2N/A continue;
2N/A (void) nvpair_value_nvlist(nvp, &mgmtnvl);
2N/A (void) nvlist_lookup_uint8(mgmtnvl, LLDP_NVP_MGMTADDRTYPE,
2N/A &maddrp->lm_subtype);
2N/A (void) nvlist_lookup_byte_array(mgmtnvl,
2N/A LLDP_NVP_MGMTADDRVALUE, &arr, &arrsz);
2N/A maddrp->lm_addrlen = arrsz;
2N/A bcopy(arr, maddrp->lm_addr, maddrp->lm_addrlen);
2N/A (void) nvlist_lookup_uint8(mgmtnvl, LLDP_NVP_MGMTADDR_IFTYPE,
2N/A &maddrp->lm_iftype);
2N/A (void) nvlist_lookup_uint32(mgmtnvl, LLDP_NVP_MGMTADDR_IFNUM,
2N/A &maddrp->lm_ifnumber);
2N/A (void) nvlist_lookup_byte_array(mgmtnvl,
2N/A LLDP_NVP_MGMTADDR_OIDSTR, &arr, &arrsz);
2N/A maddrp->lm_oidlen = arrsz;
2N/A bcopy(arr, maddrp->lm_oid, maddrp->lm_oidlen);
2N/A maddrp++;
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/Achar *
2N/Alldp_maddr_subtype2str(uint8_t type)
2N/A{
2N/A switch (type) {
2N/A case LLDP_MGMTADDR_TYPE_IPV4:
2N/A return ("IPv4 Address");
2N/A case LLDP_MGMTADDR_TYPE_IPV6:
2N/A return ("IPv6 Address");
2N/A case LLDP_MGMTADDR_TYPE_ALL802:
2N/A return ("MAC address");
2N/A }
2N/A return ("Unknown");
2N/A}
2N/A
2N/Achar *
2N/Alldp_maddr_ifsubtype2str(uint8_t type)
2N/A{
2N/A switch (type) {
2N/A case LLDP_MGMTADDR_IFTYPE_UNKNOWN:
2N/A return ("Unknown");
2N/A case LLDP_MGMTADDR_IFTYPE_IFINDEX:
2N/A return ("IfIndex");
2N/A case LLDP_MGMTADDR_IFTYPE_SYSPORT:
2N/A return ("System Port Number");
2N/A }
2N/A return ("Unknown");
2N/A}
2N/A
2N/Avoid
2N/Alldp_mgmtaddr2str(lldp_mgmtaddr_t *map, char *buf, size_t bufsize)
2N/A{
2N/A uint_t nbytes = 0, i;
2N/A
2N/A *buf = '\0';
2N/A switch (map->lm_subtype) {
2N/A case LLDP_MGMTADDR_TYPE_IPV4:
2N/A if (inet_ntop(AF_INET, map->lm_addr, buf, bufsize) != NULL)
2N/A return;
2N/A break;
2N/A case LLDP_MGMTADDR_TYPE_IPV6:
2N/A if (inet_ntop(AF_INET6, map->lm_addr, buf, bufsize) != NULL)
2N/A return;
2N/A break;
2N/A case LLDP_MGMTADDR_TYPE_ALL802:
2N/A for (i = 0; i < map->lm_addrlen; i++) {
2N/A nbytes += snprintf(buf + nbytes, bufsize - nbytes,
2N/A "%02x", map->lm_addr[i]);
2N/A }
2N/A return;
2N/A }
2N/A}
2N/A
2N/Aint
2N/Alldp_nvlist2unknowntlv(nvlist_t *nvl, int type, lldp_unknowntlv_t **ukpp,
2N/A uint_t *len)
2N/A{
2N/A nvpair_t *nvp;
2N/A uint8_t *arr;
2N/A uint_t tlvtype;
2N/A lldp_unknowntlv_t *ukp;
2N/A
2N/A *ukpp = NULL;
2N/A *len = 0;
2N/A for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
2N/A nvp = nvlist_next_nvpair(nvl, nvp)) {
2N/A tlvtype = atoi(nvpair_name(nvp));
2N/A if (type == -1) {
2N/A ++*len;
2N/A } else if (tlvtype == type) {
2N/A ++*len;
2N/A break;
2N/A }
2N/A }
2N/A if (*len == 0)
2N/A return (0);
2N/A if ((*ukpp = calloc(*len, sizeof (lldp_unknowntlv_t))) == NULL)
2N/A return (ENOMEM);
2N/A ukp = *ukpp;
2N/A for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
2N/A nvp = nvlist_next_nvpair(nvl, nvp)) {
2N/A tlvtype = atoi(nvpair_name(nvp));
2N/A if (type != -1 && tlvtype != type)
2N/A continue;
2N/A ukp->lu_type = tlvtype;
2N/A (void) nvpair_value_byte_array(nvp, &arr, &ukp->lu_len);
2N/A bcopy(arr, ukp->lu_value, ukp->lu_len);
2N/A ukp++;
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * If `str' is non-NULL, searches the `nvl' for a particular organization
2N/A * specific tlv (i.e. tlv with a specific OUI and subtype. On the other hand
2N/A * if `str' is NULL, it returns all the unrecognized org. specific tlv in a
2N/A * `orgpp' of length `len'.
2N/A */
2N/Aint
2N/Alldp_nvlist2unrec_orginfo(nvlist_t *nvl, const char *str,
2N/A lldp_unrec_orginfo_t **orgpp, uint_t *len)
2N/A{
2N/A nvpair_t *nvp;
2N/A uint8_t *arr;
2N/A lldp_unrec_orginfo_t *orgp;
2N/A
2N/A *orgpp = NULL;
2N/A *len = 0;
2N/A for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
2N/A nvp = nvlist_next_nvpair(nvl, nvp)) {
2N/A if (str == NULL) {
2N/A ++*len;
2N/A } else if (strcmp(str, nvpair_name(nvp)) == 0) {
2N/A ++*len;
2N/A break;
2N/A }
2N/A }
2N/A if (*len == 0)
2N/A return (0);
2N/A if ((*orgpp = calloc(*len, sizeof (lldp_unrec_orginfo_t))) == NULL)
2N/A return (ENOMEM);
2N/A orgp = *orgpp;
2N/A for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
2N/A nvp = nvlist_next_nvpair(nvl, nvp)) {
2N/A char *name, *index_str;
2N/A
2N/A name = nvpair_name(nvp);
2N/A if (str != NULL && strcmp(str, name) != 0)
2N/A continue;
2N/A
2N/A /* `arr' containes the entire 'org. specific tlv' */
2N/A (void) nvpair_value_byte_array(nvp, &arr, &orgp->lo_len);
2N/A bcopy(arr, orgp->lo_value, orgp->lo_len);
2N/A /* copy over the oui */
2N/A bcopy(arr + 2, orgp->lo_oui, LLDP_OUI_LEN);
2N/A orgp->lo_subtype = *(arr + 5);
2N/A
2N/A index_str = strrchr(name, '_');
2N/A if (index_str == NULL)
2N/A continue;
2N/A orgp->lo_index = atoi(++index_str);
2N/A orgp++;
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Alldp_del_nested_nvl(nvlist_t *invl, const char *name1, const char *name2,
2N/A const char *name3)
2N/A{
2N/A nvlist_t *nvl1 = NULL;
2N/A nvlist_t *nvl2 = NULL;
2N/A
2N/A if (invl == NULL || name1 == NULL)
2N/A return (EINVAL);
2N/A
2N/A if (name2 == NULL && name3 == NULL)
2N/A return (nvlist_remove(invl, name1, DATA_TYPE_NVLIST));
2N/A
2N/A if (nvlist_lookup_nvlist(invl, name1, &nvl1) != 0)
2N/A return (ENOENT);
2N/A
2N/A if (name3 == NULL)
2N/A return (nvlist_remove(nvl1, name2, DATA_TYPE_NVLIST));
2N/A
2N/A if (nvlist_lookup_nvlist(nvl1, name2, &nvl2) != 0)
2N/A return (ENOENT);
2N/A
2N/A return (nvlist_remove(nvl2, name3, DATA_TYPE_NVLIST));
2N/A}
2N/A
2N/Aint
2N/Alldp_get_nested_nvl(nvlist_t *invl, const char *name1, const char *name2,
2N/A const char *name3, nvlist_t **onvl)
2N/A{
2N/A nvlist_t *nvl1 = NULL, *nvl2 = NULL, *nvl3 = NULL;
2N/A
2N/A if (name1 == NULL || (name2 == NULL && name3 != NULL))
2N/A return (EINVAL);
2N/A *onvl = NULL;
2N/A if (nvlist_lookup_nvlist(invl, name1, &nvl1) != 0)
2N/A return (ENOENT);
2N/A if (name2 == NULL) {
2N/A *onvl = nvl1;
2N/A return (0);
2N/A }
2N/A if (nvlist_lookup_nvlist(nvl1, name2, &nvl2) != 0)
2N/A return (ENOENT);
2N/A if (name3 == NULL) {
2N/A *onvl = nvl2;
2N/A return (0);
2N/A }
2N/A if (nvlist_lookup_nvlist(nvl2, name3, &nvl3) != 0)
2N/A return (ENOENT);
2N/A *onvl = nvl3;
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Alldp_merge_nested_nvl(nvlist_t *dst, nvlist_t *src, const char *name1,
2N/A const char *name2, const char *name3)
2N/A{
2N/A nvlist_t *mdnvl, *msnvl;
2N/A int err;
2N/A
2N/A err = lldp_create_nested_nvl(dst, name1, name2, name3, &mdnvl);
2N/A if (err == 0)
2N/A err = lldp_get_nested_nvl(src, name1, name2, name3, &msnvl);
2N/A if (err != 0)
2N/A return (err);
2N/A
2N/A return (nvlist_merge(mdnvl, msnvl, 0));
2N/A}
2N/A
2N/Aint
2N/Alldp_create_nested_nvl(nvlist_t *invl, const char *name1, const char *name2,
2N/A const char *name3, nvlist_t **onvl)
2N/A{
2N/A nvlist_t *nvl1 = NULL;
2N/A nvlist_t *nvl2 = NULL;
2N/A nvlist_t *nvl3 = NULL;
2N/A
2N/A *onvl = NULL;
2N/A if (nvlist_lookup_nvlist(invl, name1, &nvl1) != 0) {
2N/A if (nvlist_alloc(&nvl1, NV_UNIQUE_NAME, 0) != 0)
2N/A return (ENOMEM);
2N/A if (nvlist_add_nvlist(invl, name1, nvl1) != 0) {
2N/A nvlist_free(nvl1);
2N/A return (ENOMEM);
2N/A }
2N/A nvlist_free(nvl1);
2N/A (void) nvlist_lookup_nvlist(invl, name1, &nvl1);
2N/A }
2N/A if (name2 == NULL) {
2N/A *onvl = nvl1;
2N/A return (0);
2N/A }
2N/A /* Get the OUI list for `name2' */
2N/A if (nvlist_lookup_nvlist(nvl1, name2, &nvl2) != 0) {
2N/A if (nvlist_alloc(&nvl2, NV_UNIQUE_NAME, 0) != 0)
2N/A return (ENOMEM);
2N/A if (nvlist_add_nvlist(nvl1, name2, nvl2) != 0) {
2N/A nvlist_free(nvl2);
2N/A return (ENOMEM);
2N/A }
2N/A nvlist_free(nvl2);
2N/A (void) nvlist_lookup_nvlist(nvl1, name2, &nvl2);
2N/A }
2N/A if (name3 == NULL) {
2N/A *onvl = nvl2;
2N/A return (0);
2N/A }
2N/A
2N/A if (nvlist_lookup_nvlist(nvl2, name3, &nvl3) != 0) {
2N/A if (nvlist_alloc(&nvl3, NV_UNIQUE_NAME, 0) != 0)
2N/A return (ENOMEM);
2N/A if (nvlist_add_nvlist(nvl2, name3, nvl3) != 0) {
2N/A nvlist_free(nvl3);
2N/A return (ENOMEM);
2N/A }
2N/A nvlist_free(nvl3);
2N/A (void) nvlist_lookup_nvlist(nvl2, name3, &nvl3);
2N/A }
2N/A *onvl = nvl3;
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Alldp_nvlist2infovalid(nvlist_t *tlv_nvl, uint16_t *time)
2N/A{
2N/A return (nvlist_lookup_uint16(tlv_nvl, LLDP_NVP_RXINFOVALID_FOR, time));
2N/A}
2N/A
2N/Aint
2N/Alldp_nvlist2nexttx(nvlist_t *tlv_nvl, uint16_t *time)
2N/A{
2N/A return (nvlist_lookup_uint16(tlv_nvl, LLDP_NVP_NEXTTX_IN, time));
2N/A}
2N/A
2N/A/* DCBx Appln to String functions */
2N/Achar *
2N/Adcbx_appln_sel2str(int sel)
2N/A{
2N/A switch (sel) {
2N/A case DCB_APPLICATION_SF_ETHERTYPE:
2N/A return ("Ethertype");
2N/A case DCB_APPLICATION_SF_TCP_SCTP:
2N/A return ("Port Number over TCP/SCTP");
2N/A case DCB_APPLICATION_SF_UDP_DCCP:
2N/A return ("Port Number over UDP/DCCP");
2N/A case DCB_APPLICATION_SF_TCP_SCTP_UDP_DCCP:
2N/A return ("Port Number over TCP/SCTP/UDP/DCCP");
2N/A default:
2N/A return ("Reserved");
2N/A }
2N/A}
2N/A
2N/Achar *
2N/Alldp_evb_rrstat2str(uint8_t rrstat)
2N/A{
2N/A switch (rrstat) {
2N/A case 0:
2N/A return ("RR Non-Enabled");
2N/A case 1:
2N/A return ("RR Enabled");
2N/A case 2:
2N/A case 3:
2N/A return ("RR Unknown");
2N/A default :
2N/A break;
2N/A }
2N/A return ("Invalid");
2N/A}
2N/A
2N/Aint
2N/Alldp_arr2str(uint8_t *arr, uint_t alen, char *buf, uint_t bufsize)
2N/A{
2N/A int cnt, len;
2N/A
2N/A for (cnt = 0, len = 0; cnt < alen; cnt++) {
2N/A if (cnt > 0)
2N/A len += snprintf(buf + len, bufsize - len, ",");
2N/A len += snprintf(buf + len, bufsize - len, "%d", arr[cnt]);
2N/A if (len >= bufsize)
2N/A return (ENOBUFS);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Alldp_str2arr(char *buf, uint8_t *arr, uint_t alen)
2N/A{
2N/A char *str, *lasts;
2N/A int cnt;
2N/A char tbuf[LLDP_STRSIZE];
2N/A char *p = tbuf;
2N/A
2N/A cnt = 0;
2N/A (void) strncpy(tbuf, buf, strlen(buf));
2N/A tbuf[strlen(buf)] = '\0';
2N/A while ((str = strtok_r(p, ",", &lasts)) != NULL) {
2N/A p = NULL;
2N/A if (cnt >= alen)
2N/A return (ENOBUFS);
2N/A arr[cnt++] = (uint8_t)strtol(str, NULL, 10);
2N/A }
2N/A
2N/A return (0);
2N/A
2N/A}