/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
*/
#include <sys/nvpair.h>
#include <lldp.h>
#include <libdlvnic.h>
#include <libscf.h>
#include <sys/dls_mgmt.h>
#include <libnvpair.h>
#include "vdp_impl.h"
#include <netpacket/packet.h>
#include <sys/stropts.h>
#include <sys/mac_flow.h>
static int __vdp_logging = 0;
static list_t vdp_inst_list;
static pthread_mutex_t vdp_inst_list_lock;
static pthread_cond_t vdp_inst_list_cv;
static void vdp_tlv_fltr_to_str(char *, int, vdp_profile_t *);
/* PRINTFLIKE2 */
void
vdp_logmsg(int pri, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (__vdp_logging == 0)
return;
if (__vdp_logging == VDP_SYS_LOG)
vsyslog(pri, fmt, ap);
else
(void) vfprintf(stderr, fmt, ap);
va_end(ap);
}
/*
* Global initializations done once
*/
void
vdp_init(int log)
{
(void) pthread_mutex_init(&vdp_inst_list_lock, NULL);
(void) pthread_cond_init(&vdp_inst_list_cv, NULL);
list_create(&vdp_inst_list, sizeof (vdp_inst_t),
offsetof(vdp_inst_t, vdp_inst_node));
srand((unsigned)time(0));
__vdp_logging = log;
}
/* Given a linkid for a VSI, return its profile */
static vdp_profile_t *
vdp_profile_lookup_by_linkid(vdp_inst_t *vdp_instp, datalink_id_t id)
{
vdp_profile_t *vdp_profp;
for (vdp_profp = list_head(&vdp_instp->vdp_inst_profiles);
vdp_profp != NULL;
vdp_profp = list_next(&vdp_instp->vdp_inst_profiles, vdp_profp)) {
if (vdp_profp->vdp_prof_vnic_id == id) {
if (vdp_profp->vdp_prof_deleted)
continue;
logtrace("profile for vnic %u on %s found\n", id,
vdp_instp->vdp_inst_phy_link_name);
return (vdp_profp);
}
}
logtrace("profile for vnic %u on %s not found\n", id,
vdp_instp->vdp_inst_phy_link_name);
return (NULL);
}
/* Given a VSIID (MAC address) return the VSI profile */
static vdp_profile_t *
vdp_profile_lookup_by_vsiid(vdp_inst_t *vdp_instp, char *vsiid)
{
vdp_profile_t *vdp_profp;
for (vdp_profp = list_head(&vdp_instp->vdp_inst_profiles);
vdp_profp != NULL;
vdp_profp = list_next(&vdp_instp->vdp_inst_profiles, vdp_profp)) {
if (bcmp(vsiid, vdp_profp->vdp_prof_vsiid, 16) == 0) {
logtrace("vdp_profp found based on vsiid\n");
return (vdp_profp);
}
}
return (NULL);
}
/*
* Create the profile for VSI. The profile is the TLVs that will
* be sent in a VDP request.
*/
/* ARGSUSED */
static void
vdp_create_profile_tlv(vdp_inst_t *vdp_instp, vdp_profile_t *vdp_profp,
boolean_t is_associate)
{
vdp_tlv_t *vdp_tlvp = &vdp_profp->vdp_prof_tlv;
char *wptr;
/* Type-Length of the VDP Request */
vdp_tlvp->vdp_tlv_type_len = is_associate ? VDP_TLV_ASSOC :
VDP_TLV_DEASSOC;
vdp_tlvp->vdp_tlv_type_len =
vdp_tlvp->vdp_tlv_type_len << VDP_TLV_TYPE_SHIFT;
vdp_tlvp->vdp_tlv_type_len |=
(sizeof (vdp_tlv_t) + sizeof (vtf_mac_t) -
sizeof (vdp_tlvp->vdp_tlv_type_len)) & VDP_TLV_LEN_MASK;
vdp_tlvp->vdp_tlv_type_len = htons(vdp_tlvp->vdp_tlv_type_len);
/* Reason code */
vdp_tlvp->vdp_tlv_reason = 0;
/* 3-byte VSI Type ID */
vdp_hton24(vdp_tlvp->vdp_tlv_vsi_type, vdp_profp->vdp_prof_id_type);
/* VSI Version */
vdp_tlvp->vdp_tlv_vsi_type_ver = vdp_profp->vdp_prof_id_vers;
/* VSIID format */
vdp_tlvp->vdp_tlv_vsiid_format = vdp_profp->vdp_prof_vsiid_format;
/* VSIID */
bcopy(&vdp_profp->vdp_prof_vsiid[10], &vdp_tlvp->vdp_tlv_vsiid[10],
ETHERADDRL);
/* VSI Filter Info Format */
vdp_tlvp->vdp_tlv_fltr_format = vdp_profp->vdp_prof_fltr_info_format;
/* No. of Filter Info. entries */
vdp_tlvp->vdp_tlv_fltr_entries =
htons(vdp_profp->vdp_prof_fltr_entries);
/* On the wire VDP packet */
wptr = vdp_profp->vdp_prof_wire_tlv;
bzero(wptr, sizeof (vdp_profp->vdp_prof_wire_tlv));
/* ECP header offset */
wptr += vdp_instp->vdp_inst_ecp_offset;
/* VSI Manager ID */
bcopy(&vdp_profp->vdp_prof_mgr_tlv, wptr, sizeof (vdp_tlv_mgr_t));
wptr += sizeof (vdp_tlv_mgr_t);
/* Oracle org specific OUI, if needed */
if (vdp_profp->vdp_prof_mgr_enc_tlv.vtme_type_len > 0) {
bcopy(&vdp_profp->vdp_prof_mgr_enc_tlv, wptr,
sizeof (vdp_tlv_mgrenc_t));
wptr += sizeof (vdp_tlv_mgrenc_t);
}
/* VDP profile created above */
bcopy(&vdp_profp->vdp_prof_tlv, wptr, sizeof (vdp_tlv_t));
wptr += sizeof (vdp_tlv_t);
bcopy(&vdp_profp->vdp_prof_tlv_fltr_mac, wptr, sizeof (vtf_mac_t));
wptr += sizeof (vtf_mac_t);
vdp_profp->vdp_prof_tlv_len = wptr - vdp_profp->vdp_prof_wire_tlv;
assert(vdp_profp->vdp_prof_tlv_len <= ETHERMTU);
}
/* Remove a profile from a VDP instance */
void
vdp_free_profp(vdp_profile_t *vdp_profp)
{
vdp_inst_t *vdp_instp = vdp_profp->vdp_prof_instp;
list_remove(&vdp_instp->vdp_inst_profiles, vdp_profp);
free(vdp_profp);
if (list_is_empty(&vdp_instp->vdp_inst_profiles)) {
vdp_instp->vdp_inst_state = VDP_REC_DELETED;
(void) pthread_cond_signal(&vdp_instp->vdp_inst_cv);
}
}
/*
* Process a timeout for a VDP profile. This includes sending the initial
* request, sending KeepAlives and timing out a VDP profile
*/
static void
vdp_process_timeout(iu_tq_t *tq, void *arg)
{
vdp_profile_t *vdp_profp = arg;
vdp_inst_t *vdp_instp = vdp_profp->vdp_prof_instp;
boolean_t ret = B_FALSE;
/*
* vdp_inst_lock is held by the thread
* that called iu_expire_timers.
* When the VDP ACK comes the ECP callback function will
* reschedule the timeout and move the state to VDP_WAIT_SYS_CMD.
*/
vdp_profp->vdp_prof_timer_id = -1;
top:
if (vdp_profp->vdp_prof_proc_state == VDP_PROF_INIT) {
assert(vdp_profp->vdp_prof_cmd_pending != VDP_TLV_NONE);
/* Initial state */
vdp_create_profile_tlv(vdp_instp, vdp_profp,
vdp_profp->vdp_prof_cmd_pending == VDP_TLV_ASSOC);
vdp_profp->vdp_prof_cmd_pending = VDP_TLV_NONE;
vdp_profp->vdp_prof_timer_id =
iu_schedule_timer_ms(tq, VDP_RESP_WAIT_DELAY_MSEC,
vdp_process_timeout, arg);
(void) pthread_mutex_unlock(&vdp_instp->vdp_inst_lock);
ret = ecp_send(vdp_instp->vdp_inst_ecp_handle,
vdp_profp->vdp_prof_wire_tlv, vdp_profp->vdp_prof_tlv_len);
(void) pthread_mutex_lock(&vdp_instp->vdp_inst_lock);
if (ret == B_FALSE) {
vdp_profp->vdp_prof_proc_state = VDP_PROF_TIMEDOUT;
vdp_profp->vdp_prof_vsi_state = VDP_TLV_TIMEDOUT;
(void) iu_cancel_timer(tq,
vdp_profp->vdp_prof_timer_id, NULL);
vdp_profp->vdp_prof_timer_id = -1;
if (vdp_profp->vdp_prof_deleted) {
vdp_free_profp(vdp_profp);
}
} else {
VDP_PROF_STAT_UPDATE(vdp_profp, opkts, 1);
vdp_profp->vdp_prof_proc_state = VDP_PROF_PROCESSING;
}
} else if (vdp_profp->vdp_prof_proc_state == VDP_PROF_WAIT_SYS_CMD) {
if (vdp_profp->vdp_prof_cmd_pending != VDP_TLV_NONE) {
vdp_profp->vdp_prof_proc_state = VDP_PROF_INIT;
goto top;
} else {
vdp_profp->vdp_prof_proc_state = VDP_PROF_TX_KEEP_ALIVE;
vdp_profp->vdp_prof_timer_id =
iu_schedule_timer_ms(tq, VDP_RESP_WAIT_DELAY_MSEC,
vdp_process_timeout, arg);
}
(void) pthread_mutex_unlock(&vdp_instp->vdp_inst_lock);
ret = ecp_send(vdp_instp->vdp_inst_ecp_handle,
vdp_profp->vdp_prof_wire_tlv, vdp_profp->vdp_prof_tlv_len);
(void) pthread_mutex_lock(&vdp_instp->vdp_inst_lock);
if (ret == B_FALSE) {
vdp_profp->vdp_prof_proc_state = VDP_PROF_TIMEDOUT;
vdp_profp->vdp_prof_vsi_state = VDP_TLV_TIMEDOUT;
(void) iu_cancel_timer(tq,
vdp_profp->vdp_prof_timer_id, NULL);
vdp_profp->vdp_prof_timer_id = -1;
if (vdp_profp->vdp_prof_deleted) {
vdp_free_profp(vdp_profp);
}
} else {
VDP_PROF_STAT_UPDATE(vdp_profp, kas, 1);
VDP_PROF_STAT_UPDATE(vdp_profp, opkts, 1);
}
} else {
/*
* VDP ACK did not come
*/
vdp_profp->vdp_prof_proc_state = VDP_PROF_TIMEDOUT;
vdp_profp->vdp_prof_vsi_state = VDP_TLV_TIMEDOUT;
vdp_profp->vdp_prof_cmd_pending = VDP_TLV_NONE;
/*
* If prof was deleted -- free it
*/
if (vdp_profp->vdp_prof_deleted) {
vdp_free_profp(vdp_profp);
}
}
}
/* The VDP state machine associated with a VDP instance */
static void *
vdp_inst_timeout_process_thr(void *arg)
{
vdp_inst_t *vdp_instp = arg;
int timeout; /* In msec */
struct timespec ntime;
(void) pthread_mutex_lock(&vdp_instp->vdp_inst_lock);
/* LINTED */
while (B_TRUE) {
/*
* Get the next timeout
*/
/*
* List is empty and has been marked
* deleted. This must have happened as the
* vsi timedout and was deleted by this thread
* which is returning now.
*/
if (vdp_instp->vdp_inst_state == VDP_REC_DELETED) {
goto vdp_rec_deleted;
}
assert(vdp_instp->vdp_inst_tq != NULL);
timeout = iu_earliest_timer(vdp_instp->vdp_inst_tq);
if (timeout == INFTIM) {
(void) pthread_cond_wait(&vdp_instp->vdp_inst_cv,
&vdp_instp->vdp_inst_lock);
} else {
int new_secs;
int new_msecs;
new_secs = timeout / MILLISEC;
new_msecs = timeout % MILLISEC;
(void) clock_gettime(CLOCK_REALTIME, &ntime);
ntime.tv_sec += new_secs;
ntime.tv_sec +=
(ntime.tv_nsec + (new_msecs * 1000000)) / NANOSEC;
ntime.tv_nsec =
(ntime.tv_nsec + (new_msecs * 1000000)) % NANOSEC;
(void) pthread_cond_timedwait(&vdp_instp->vdp_inst_cv,
&vdp_instp->vdp_inst_lock, &ntime);
}
vdp_rec_deleted:
if (vdp_instp->vdp_inst_state == VDP_REC_DELETED) {
/*
* When each VSI instance is destroyed
* timer is cleaned up
*/
iu_tq_destroy(vdp_instp->vdp_inst_tq);
vdp_instp->vdp_inst_tq = NULL;
(void) pthread_mutex_unlock(&vdp_instp->vdp_inst_lock);
(void) pthread_mutex_lock(&vdp_inst_list_lock);
(void) pthread_mutex_lock(&vdp_instp->vdp_inst_lock);
logtrace("VDP instance main thread exiting \n");
ecp_fini(vdp_instp->vdp_inst_ecp_handle);
list_remove(&vdp_inst_list, vdp_instp);
(void) pthread_mutex_unlock(&vdp_instp->vdp_inst_lock);
/* No more VSI instance */
if (list_is_empty(&vdp_inst_list))
(void) pthread_cond_signal(&vdp_inst_list_cv);
(void) pthread_mutex_unlock(&vdp_inst_list_lock);
(void) close(vdp_instp->vdp_inst_sockfd);
free(vdp_instp);
pthread_exit(NULL);
} else {
(void) iu_expire_timers(vdp_instp->vdp_inst_tq);
}
}
/* LINTED */
}
/*
* The callback registerd to receive VDP packets from ECP. This routine
* validates the VDP packet, gets the associated VDP profile and processes
* the VDP packet in the context of the VDP profile. If the VDP profile
* uses Oracle org specific OUI, i.e the Oracle encoding to generate
* VSI Type ID in its request, it also expects the peer to return the
* OUI in its response. If the peer doesn't return the OUI it is assumed
* that the peer doesn't understand the Oracle encoding and a DEASSOC
* is issued regardless of the peer's response.
*/
/* ARGSUSED */
static void
vdp_ecp_cb(void *instp, ushort_t seq, ushort_t type, char *pdu, int len)
{
vdp_inst_t *vdp_instp = instp;
vdp_profile_t *vdp_profp;
uint8_t tlv_type;
uint8_t vdp_tlv_type;
uint16_t tlv_len;
char *tlv_vsiid;
boolean_t send_deassoc = B_FALSE;
boolean_t oracle_oui = B_FALSE;
(void) pthread_mutex_lock(&vdp_instp->vdp_inst_lock);
if (vdp_instp->vdp_inst_state == VDP_REC_DELETED)
goto done;
assert(type != ECP_ACK);
if (len < sizeof (vdp_tlv_mgr_t))
goto done;
tlv_type = LLDP_TLV_TYPE(pdu);
tlv_len = LLDP_TLV_LEN(pdu);
logtrace("vdp_ecp_cb: tlv_len %d tlv_type %d \n", tlv_len, tlv_type);
/*
* The first TLV should always be the manage ID TLV
*/
if (tlv_type != VDP_TLV_MGR_ID)
goto done;
pdu += sizeof (vdp_tlv_mgr_t);
len -= sizeof (vdp_tlv_mgr_t);
if (len < sizeof (vdp_tlv_mgrenc_t))
goto done;
tlv_type = LLDP_TLV_TYPE(pdu);
tlv_len = LLDP_TLV_LEN(pdu);
logtrace("vdp_ecp_cb: tlv_len %d tlv_type %d \n", tlv_len, tlv_type);
/*
* If we don't get a request/response using our org specific
* TLV, we will send a deassoc.
*/
if (tlv_type == VDP_TLV_ORG_SPEC) {
pdu += sizeof (uint16_t);
if (tlv_len ==
(sizeof (vdp_tlv_mgrenc_t) - sizeof (uint16_t)) &&
VDP_ORGTLV_OUI(pdu) == VDP_ORACLE_OUI &&
VDP_ORGTLV_STYPE(pdu) == VDP_ORACLEOUI_VSIMGR_SUBTYPE &&
*((uint8_t *)(pdu + VDP_ORG_HSIZE)) ==
MVE_ORACLE_VSIMGRID_V1) {
oracle_oui = B_TRUE;
}
pdu += tlv_len;
tlv_type = LLDP_TLV_TYPE(pdu);
tlv_len = LLDP_TLV_LEN(pdu);
logtrace("vdp_ecp_cb: tlv_len %d tlv_type %d \n", tlv_len,
tlv_type);
len -= sizeof (vdp_tlv_mgrenc_t);
}
if (len < sizeof (vdp_tlv_t))
goto done;
tlv_type = LLDP_TLV_TYPE(pdu);
tlv_len = LLDP_TLV_LEN(pdu);
logtrace("vdp_ecp_cb: tlv_len %d tlv_type %d\n", tlv_len, tlv_type);
tlv_vsiid = pdu + VDP_TLV_VSIID_OFFSET;
vdp_profp = vdp_profile_lookup_by_vsiid(vdp_instp, tlv_vsiid);
if (vdp_profp == NULL) {
logtrace("vdp_ecp_cb: could not find vdp_profp \n");
(void) pthread_mutex_unlock(&vdp_instp->vdp_inst_lock);
return;
} else if (vdp_profp->vdp_prof_deleted) {
if (vdp_profp->vdp_prof_timer_id != -1)
(void) iu_adjust_timer(vdp_instp->vdp_inst_tq,
vdp_profp->vdp_prof_timer_id, 0);
else
vdp_profp->vdp_prof_timer_id =
iu_schedule_timer_ms(vdp_instp->vdp_inst_tq,
0, vdp_process_timeout, vdp_profp);
(void) pthread_cond_signal(&vdp_instp->vdp_inst_cv);
(void) pthread_mutex_unlock(&vdp_instp->vdp_inst_lock);
return;
}
VDP_PROF_STAT_UPDATE(vdp_profp, ipkts, 1);
/*
* Verify that the ACK is indeed for the TLV that we sent out.
* Except for VDP_TLV_DEASSOC which can be unsolicited.
*/
vdp_tlv_type = LLDP_TLV_TYPE(&vdp_profp->vdp_prof_tlv);
/*
* To Do: ADD CODE TO CHECK for non zero resp code
*/
if ((tlv_type != VDP_TLV_DEASSOC) && (vdp_tlv_type != tlv_type)) {
logtrace("vdp_ecp_cb: expecting tlv type %s got %s \n",
vdp_tlv_type_to_str(vdp_tlv_type),
vdp_tlv_type_to_str(tlv_type));
(void) pthread_mutex_unlock(&vdp_instp->vdp_inst_lock);
return;
}
/*
* If we have sent MVE_ORACLE_VSIMGRID_V1 OUI, then we expect to
* get it back, else send a DEASSOC.
*/
if (vdp_profp->vdp_prof_mgr_enc_tlv.vtme_type_len > 0 &&
!oracle_oui) {
send_deassoc = B_TRUE;
}
if (!iu_cancel_timer(vdp_instp->vdp_inst_tq,
vdp_profp->vdp_prof_timer_id, NULL)) {
logtrace("vdp_ecp_cb: vdp_profp KA timer not found \n");
}
vdp_profp->vdp_prof_timer_id = -1;
if (tlv_type != VDP_TLV_DEASSOC) {
vdp_profp->vdp_prof_timer_id = -1;
if (send_deassoc) {
vdp_profp->vdp_prof_proc_state = VDP_PROF_WAIT_SYS_CMD;
vdp_profp->vdp_prof_cmd_pending = VDP_TLV_DEASSOC;
vdp_profp->vdp_prof_timer_id =
iu_schedule_timer_ms(vdp_instp->vdp_inst_tq,
0, vdp_process_timeout, vdp_profp);
(void) pthread_cond_signal(&vdp_instp->vdp_inst_cv);
goto done;
}
if (vdp_profp->vdp_prof_proc_state == VDP_PROF_PROCESSING) {
vdp_profp->vdp_prof_proc_state = VDP_PROF_WAIT_SYS_CMD;
vdp_profp->vdp_prof_vsi_state = tlv_type;
}
if (vdp_profp->vdp_prof_proc_state == VDP_PROF_TX_KEEP_ALIVE) {
vdp_profp->vdp_prof_proc_state = VDP_PROF_WAIT_SYS_CMD;
}
if (vdp_profp->vdp_prof_proc_state == VDP_PROF_WAIT_SYS_CMD) {
if (vdp_profp->vdp_prof_cmd_pending != VDP_TLV_NONE) {
vdp_create_profile_tlv(vdp_instp,
vdp_profp, B_FALSE);
vdp_profp->vdp_prof_proc_state = VDP_PROF_INIT;
vdp_profp->vdp_prof_timer_id =
iu_schedule_timer_ms(vdp_instp->vdp_inst_tq,
0, vdp_process_timeout, vdp_profp);
(void) pthread_cond_signal(
&vdp_instp->vdp_inst_cv);
} else {
vdp_profp->vdp_prof_timer_id =
iu_schedule_timer_ms(vdp_instp->vdp_inst_tq,
VDP_REINIT_KEEP_ALIVE_TIME_MSEC,
vdp_process_timeout, vdp_profp);
(void) pthread_cond_signal(
&vdp_instp->vdp_inst_cv);
}
}
} else {
assert(tlv_type == VDP_TLV_DEASSOC);
vdp_profp->vdp_prof_proc_state = VDP_PROF_TX_DEASSOC;
vdp_profp->vdp_prof_vsi_state = VDP_TLV_DEASSOC;
if (vdp_profp->vdp_prof_cmd_pending == VDP_TLV_DEASSOC)
vdp_profp->vdp_prof_cmd_pending = VDP_TLV_NONE;
if (vdp_profp->vdp_prof_deleted) {
vdp_free_profp(vdp_profp);
}
}
done:
(void) pthread_mutex_unlock(&vdp_instp->vdp_inst_lock);
}
/*
* Initialize a VDP instance. There is a VDP instance per port. The VDP
* instance could have multiple VDP profiles, one for each VSP/VNIC
* on the physical port. Initialization also getting an ECP handle
* and setting the callback function for ECP to invoke.
*/
static int
vdp_inst_init(vdp_inst_t *vdp_instp, vdpt_arg_t *vdp_argp,
vdp_link_info_t *link_infop)
{
struct sockaddr_ll sll;
int error;
pthread_attr_t attr;
struct ether_addr dst_addr = LLDP_CUSTOMER_BRIDGE_MCAST_ADDR;
struct packet_mreq pmreq;
bzero(vdp_instp, sizeof (vdp_inst_t));
(void) pthread_mutex_init(&vdp_instp->vdp_inst_lock, NULL);
(void) pthread_cond_init(&vdp_instp->vdp_inst_cv, NULL);
list_create(&vdp_instp->vdp_inst_profiles, sizeof (vdp_profile_t),
offsetof(vdp_profile_t, vdp_profile_node));
(void) strlcpy(vdp_instp->vdp_inst_phy_link_name,
link_infop->vli_phy_link_name, MAXLINKNAMELEN);
vdp_instp->vdp_inst_phy_id = vdp_argp->vdpt_phy_link_id;
vdp_instp->vdp_inst_lphysaddr_len = link_infop->vli_phy_link_maclen;
bcopy(link_infop->vli_phy_link_mac, vdp_instp->vdp_inst_lphysaddr,
vdp_instp->vdp_inst_lphysaddr_len);
vdp_instp->vdp_inst_sockfd = socket(PF_PACKET, SOCK_RAW, ETHERTYPE_ECP);
if (vdp_instp->vdp_inst_sockfd < 0) {
vdp_instp->vdp_inst_sockfd = 0;
logerr("PF_PACKET socket creation failed %d\n", errno);
goto failed;
}
(void) memset(&sll, 0, sizeof (sll));
sll.sll_family = AF_PACKET;
sll.sll_ifindex = (int32_t)vdp_instp->vdp_inst_phy_id;
sll.sll_protocol = ETHERTYPE_ECP;
if (bind(vdp_instp->vdp_inst_sockfd, (struct sockaddr *)&sll,
sizeof (sll)) == -1) {
(void) close(vdp_instp->vdp_inst_sockfd);
logerr("PF_PACKET socket bind failed %d\n", errno);
goto failed;
}
bzero(&pmreq, sizeof (pmreq));
pmreq.mr_ifindex = vdp_instp->vdp_inst_phy_id;
pmreq.mr_type = PACKET_MR_MULTICAST;
pmreq.mr_alen = ETHERADDRL;
bcopy(&dst_addr, pmreq.mr_address, ETHERADDRL);
if (setsockopt(vdp_instp->vdp_inst_sockfd, SOL_PACKET,
PACKET_ADD_MEMBERSHIP, &pmreq, sizeof (pmreq)) != 0) {
logerr("PF_PACKET Failed to enable multicast membership\n");
(void) close(vdp_instp->vdp_inst_sockfd);
return (errno);
}
bzero(&pmreq, sizeof (pmreq));
pmreq.mr_ifindex = vdp_instp->vdp_inst_phy_id;
pmreq.mr_type = PACKET_MR_PROMISC;
pmreq.mr_alen = ETHERADDRL;
if (setsockopt(vdp_instp->vdp_inst_sockfd, SOL_PACKET,
PACKET_DROP_MEMBERSHIP, &pmreq, sizeof (pmreq)) != 0) {
logerr("PF_PACKET Failed to disable promisc\n");
(void) close(vdp_instp->vdp_inst_sockfd);
return (errno);
}
vdp_instp->vdp_inst_state = VDP_REC_INIT;
vdp_instp->vdp_inst_tq = iu_tq_create();
(void) pthread_attr_init(&attr);
(void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
(void) pthread_create(&vdp_instp->vdp_inst_timeout_process_thread,
&attr, vdp_inst_timeout_process_thr, (void *)vdp_instp);
vdp_instp->vdp_inst_ecp_handle = ecp_init(&dst_addr,
(struct ether_addr *)vdp_instp->vdp_inst_lphysaddr, ECP_VDP,
vdp_instp->vdp_inst_sockfd, ECP_MAX_TRIES, vdp_ecp_cb, vdp_instp,
__vdp_logging, &error);
vdp_instp->vdp_inst_ecp_offset = ecp_offset();
(void) pthread_mutex_lock(&vdp_instp->vdp_inst_lock);
list_insert_tail(&vdp_inst_list, vdp_instp);
return (0);
failed:
(void) pthread_mutex_destroy(&vdp_instp->vdp_inst_lock);
(void) pthread_cond_destroy(&vdp_instp->vdp_inst_cv);
list_destroy(&vdp_instp->vdp_inst_profiles);
return (errno);
}
/*
* Return the VDP instance assocaiated with the port specified in vdp_arg,
* if present. vdp_inst_lock is held upon successful lookup.
*/
static vdp_inst_t *
vdp_inst_lookup(vdpt_arg_t *vdp_arg, vdp_link_info_t *link_infop)
{
vdp_inst_t *vdp_instp;
(void) pthread_mutex_lock(&vdp_inst_list_lock);
for (vdp_instp = list_head(&vdp_inst_list); vdp_instp != NULL;
vdp_instp = list_next(&vdp_inst_list, vdp_instp)) {
(void) pthread_mutex_lock(&vdp_instp->vdp_inst_lock);
if (vdp_instp->vdp_inst_state == VDP_REC_DELETED) {
(void) pthread_mutex_unlock(&vdp_instp->vdp_inst_lock);
continue;
}
if (vdp_instp->vdp_inst_phy_id == vdp_arg->vdpt_phy_link_id)
break;
(void) pthread_mutex_unlock(&vdp_instp->vdp_inst_lock);
}
if ((vdp_instp == NULL) && (link_infop != NULL)) {
vdp_instp = malloc(sizeof (vdp_inst_t));
if (vdp_instp == NULL) {
logdebug("vdp_inst_lookup: malloc failed\n");
} else {
if (vdp_inst_init(vdp_instp, vdp_arg,
link_infop) != 0) {
free(vdp_instp);
vdp_instp = NULL;
logerr("vdp_inst_init failed\n");
}
}
}
assert(vdp_instp == NULL ||
vdp_instp->vdp_inst_state != VDP_REC_DELETED);
done:
(void) pthread_mutex_unlock(&vdp_inst_list_lock);
return (vdp_instp);
}
static void
vdp_inst_release(vdp_inst_t *vdp_instp)
{
(void) pthread_mutex_unlock(&vdp_instp->vdp_inst_lock);
}
/*
* Return the stats associated with an instance or profile specified
* in vdp_argp.
*/
int
vdp_vdpstats(vdpt_arg_t *vdp_argp, vdp_stat_t *vstats)
{
vdp_inst_t *vdp_instp;
vdp_profile_t *vdp_profp;
if (vdp_argp->vdpt_phy_link_id == DATALINK_INVALID_LINKID)
return (EINVAL);
vdp_instp = vdp_inst_lookup(vdp_argp, NULL);
if (vdp_instp == NULL)
return (EINVAL);
if (vdp_argp->vdpt_virt_link_id != DATALINK_INVALID_LINKID) {
vdp_profp = vdp_profile_lookup_by_linkid(vdp_instp,
vdp_argp->vdpt_virt_link_id);
if (vdp_profp == NULL) {
vdp_inst_release(vdp_instp);
return (EINVAL);
}
vstats->vs_ipkts = vdp_profp->vdp_prof_ipkts;
vstats->vs_opkts = vdp_profp->vdp_prof_opkts;
vstats->vs_kas = vdp_profp->vdp_prof_kas;
} else {
vstats->vs_ipkts = vdp_instp->vdp_inst_ipkts;
vstats->vs_opkts = vdp_instp->vdp_inst_opkts;
vstats->vs_kas = vdp_instp->vdp_inst_kas;
}
vdp_inst_release(vdp_instp);
logtrace("ID %u: i %llu, o %llu, k %llu\n",
vdp_argp->vdpt_phy_link_id,
vstats->vs_ipkts, vstats->vs_opkts, vstats->vs_kas);
return (0);
}
/* Return the ECP stats associated with a VDP instance */
int
vdp_ecpstats(vdpt_arg_t *vdp_argp, ecp_stat_t *estats)
{
vdp_inst_t *vdp_instp;
if (vdp_argp->vdpt_phy_link_id == DATALINK_INVALID_LINKID)
return (EINVAL);
vdp_instp = vdp_inst_lookup(vdp_argp, NULL);
if (vdp_instp == NULL)
return (EINVAL);
ecp_stats(vdp_instp->vdp_inst_ecp_handle, estats);
vdp_inst_release(vdp_instp);
return (0);
}
/* Return the VDP timer values */
/* ARGSUSED */
void
vdp_vdptimers(vdpt_arg_t *vdp_argp, vdp_timers_t *vtimers)
{
vtimers->vt_rol_rwd = B_FALSE;
vtimers->vt_rka = VDP_KEEP_ALIVE_EXPONENT;
vtimers->vt_rwd = VDP_RESOURCE_WAIT_DELAY_EXPONENT;
}
/* Return the ECP timer values associated with a VDP instance */
/* ARGSUSED */
void
vdp_ecptimers(vdpt_arg_t *vdp_argp, ecp_timers_t *etimers)
{
ecp_timers(etimers);
}
/* Get the ECP information for all the VDP instances */
vdpd_ecpinfo_t *
vdp_ecpinfo(vdpt_arg_t *vdp_argp, int *nelem)
{
vdp_inst_t *vdp_instp;
vdp_profile_t *vdp_profp;
size_t index = 0;
vdpd_ecpinfo_t *list;
vdpd_ecpinfo_t *tmp;
ecp_config_t ecpc;
*nelem = 0;
list = (vdpd_ecpinfo_t *)malloc(sizeof (vdpd_ecpinfo_t));
if (list == NULL)
return (NULL);
(void) pthread_mutex_lock(&vdp_inst_list_lock);
for (vdp_instp = list_head(&vdp_inst_list); vdp_instp != NULL;
vdp_instp = list_next(&vdp_inst_list, vdp_instp)) {
(void) pthread_mutex_lock(&vdp_instp->vdp_inst_lock);
if (vdp_instp->vdp_inst_state == VDP_REC_DELETED) {
(void) pthread_mutex_unlock(&vdp_instp->vdp_inst_lock);
continue;
}
logtrace("Physical NIC %s \n",
vdp_instp->vdp_inst_phy_link_name);
if ((vdp_argp->vdpt_phy_link_id != DATALINK_INVALID_LINKID) &&
(vdp_argp->vdpt_phy_link_id !=
vdp_instp->vdp_inst_phy_id)) {
(void) pthread_mutex_unlock(&vdp_instp->vdp_inst_lock);
continue;
}
for (vdp_profp = list_head(&vdp_instp->vdp_inst_profiles);
vdp_profp != NULL;
vdp_profp = list_next(&vdp_instp->vdp_inst_profiles,
vdp_profp)) {
if ((vdp_argp->vdpt_virt_link_id !=
DATALINK_INVALID_LINKID) &&
(vdp_argp->vdpt_virt_link_id !=
vdp_profp->vdp_prof_vnic_id))
continue;
ecp_get_config(vdp_instp->vdp_inst_ecp_handle, &ecpc);
list[index].ve_ver = VDPT_ECPCFG_VERSION;
(void) strcpy(list[index].ve_phys_link_name,
vdp_instp->vdp_inst_phy_link_name);
list[index].ve_req_seq = ecpc.ecpc_req_seq;
list[index].ve_ack_seq = ecpc.ecpc_ack_seq;
list[index].ve_last_ack_seq =
ecpc.ecpc_last_ack_seq;
list[index].ve_max_tx_attempts =
ecpc.ecpc_max_tx_attempts;
list[index++].ve_ack_timeout_msec =
ECP_ACK_TIME_OUT;
tmp = realloc(list,
(sizeof (vdpd_ecpinfo_t)) * (index + 1));
if (tmp == NULL) {
free(list);
list = NULL;
(void) pthread_mutex_unlock(
&vdp_instp->vdp_inst_lock);
(void) pthread_mutex_unlock(
&vdp_inst_list_lock);
return (NULL);
}
list = tmp;
/*
* Only one entry per port
*/
break;
}
(void) pthread_mutex_unlock(&vdp_instp->vdp_inst_lock);
}
if (index == 0) {
free(list);
list = NULL;
} else {
list[index].ve_phys_link_name[0] = '\0';
}
(void) pthread_mutex_unlock(&vdp_inst_list_lock);
*nelem = index;
return (list);
}
/* Get the VDP information for all the VDP instances */
vdpd_vdpinfo_t *
vdp_vdpinfo(vdpt_arg_t *vdp_argp, int *nelem)
{
vdp_inst_t *vdp_instp;
vdp_profile_t *vdp_profp;
size_t index = 0;
vdpd_vdpinfo_t *list;
*nelem = 0;
list = (vdpd_vdpinfo_t *)malloc(sizeof (vdpd_vdpinfo_t));
if (list == NULL)
return (NULL);
(void) pthread_mutex_lock(&vdp_inst_list_lock);
for (vdp_instp = list_head(&vdp_inst_list); vdp_instp != NULL;
vdp_instp = list_next(&vdp_inst_list, vdp_instp)) {
(void) pthread_mutex_lock(&vdp_instp->vdp_inst_lock);
if (vdp_instp->vdp_inst_state == VDP_REC_DELETED) {
(void) pthread_mutex_unlock(&vdp_instp->vdp_inst_lock);
continue;
}
logtrace("Physical NIC %s \n",
vdp_instp->vdp_inst_phy_link_name);
if ((vdp_argp->vdpt_phy_link_id != DATALINK_INVALID_LINKID) &&
(vdp_argp->vdpt_phy_link_id !=
vdp_instp->vdp_inst_phy_id)) {
(void) pthread_mutex_unlock(&vdp_instp->vdp_inst_lock);
continue;
}
for (vdp_profp = list_head(&vdp_instp->vdp_inst_profiles);
vdp_profp != NULL;
vdp_profp = list_next(&vdp_instp->vdp_inst_profiles,
vdp_profp)) {
logtrace("\tVNIC:%s\t Prof-ID:%d\t Vsi State:%d\n",
vdp_profp->vdp_prof_vnic_name, 0xFF,
vdp_profp->vdp_prof_vsi_state);
if ((vdp_argp->vdpt_virt_link_id !=
DATALINK_INVALID_LINKID) &&
(vdp_argp->vdpt_virt_link_id !=
vdp_profp->vdp_prof_vnic_id))
continue;
list[index].vi_ver = VDPT_TLVL_VERSION;
(void) strcpy(list[index].vi_phys_link_name,
vdp_instp->vdp_inst_phy_link_name);
(void) strcpy(list[index].vi_virt_link_name,
vdp_profp->vdp_prof_vnic_name);
list[index].vi_vsi_state =
vdp_profp->vdp_prof_vsi_state;
(void) strcpy(list[index].vi_vsi_state_str,
vdp_tlv_type_to_str(
list[index].vi_vsi_state));
list[index].vi_vsiid_format =
vdp_profp->vdp_prof_vsiid_format;
bcopy((char *restrict)vdp_profp->vdp_prof_vsiid,
(char *restrict)list[index].vi_vsiid,
sizeof (vdp_profp->vdp_prof_vsiid));
list[index].vi_vsitypeid =
vdp_profp->vdp_prof_id_type;
list[index].vi_vsitypevers =
vdp_profp->vdp_prof_id_vers;
list[index].vi_cmd_pending =
vdp_profp->vdp_prof_cmd_pending;
(void) strcpy(list[index].vi_cmd_pending_str,
vdp_tlv_type_to_str(
list[index].vi_cmd_pending));
vdp_tlv_fltr_to_str(list[index].vi_fltr_info_str,
sizeof (list[index].vi_fltr_info_str),
vdp_profp);
list[index].vi_vdp_keep_alive_time_sec =
VDP_REINIT_KEEP_ALIVE_TIME_MSEC / MILLISEC;
list[index].vi_vdp_keep_alive_time_msec =
(VDP_REINIT_KEEP_ALIVE_TIME_MSEC % MILLISEC);
list[index].vi_vdp_resp_timeout_sec =
VDP_RESP_WAIT_DELAY_MSEC / MILLISEC;
list[index++].vi_vdp_resp_timeout_msec =
(VDP_RESP_WAIT_DELAY_MSEC % MILLISEC);
list = realloc(list,
(sizeof (vdpd_vdpinfo_t)) * (index + 1));
if (list == NULL) {
(void) pthread_mutex_unlock(
&vdp_instp->vdp_inst_lock);
(void) pthread_mutex_unlock(
&vdp_inst_list_lock);
return (NULL);
}
}
(void) pthread_mutex_unlock(&vdp_instp->vdp_inst_lock);
}
if (index == 0) {
free(list);
list = NULL;
} else {
list[index].vi_phys_link_name[0] = '\0';
list[index].vi_virt_link_name[0] = '\0';
}
(void) pthread_mutex_unlock(&vdp_inst_list_lock);
*nelem = index;
return (list);
}
static boolean_t
i_vdp_tlv_del_locked(vdp_inst_t *vdp_instp, vdp_profile_t *vdp_profp)
{
vdp_profp->vdp_prof_deleted = B_TRUE;
/*
* Delete the profile immediately
* If the TLV has timedout
* OR
* It has been disassociated. In such a case it is
* we need to check that the proc state is not VDP_PROF_INIT
* because than the thread is doing a send and has dropped the lock
*/
if (vdp_profp->vdp_prof_vsi_state == VDP_TLV_TIMEDOUT ||
((vdp_profp->vdp_prof_vsi_state == VDP_TLV_DEASSOC) &&
(vdp_profp->vdp_prof_proc_state != VDP_PROF_INIT))) {
/*
* Since the previous request never made it to the bridge
* OR the bridge has already deleted the association
* just delete the profile right now.
*/
(void) iu_cancel_timer(vdp_instp->vdp_inst_tq,
vdp_profp->vdp_prof_timer_id, NULL);
vdp_profp->vdp_prof_vsi_state = VDP_TLV_DEASSOC;
vdp_profp->vdp_prof_timer_id = -1;
vdp_free_profp(vdp_profp);
return (B_TRUE);
}
/*
* We have exclusive access to the rec/profile
* Mark it as deleted and signal the thread
*/
/*
* Since we are adding to cmd_pending -- do not create a profile tlv
*/
vdp_profp->vdp_prof_cmd_pending = VDP_TLV_DEASSOC;
if (vdp_profp->vdp_prof_proc_state == VDP_PROF_WAIT_SYS_CMD) {
(void) iu_adjust_timer(vdp_instp->vdp_inst_tq,
vdp_profp->vdp_prof_timer_id, 0);
(void) pthread_cond_signal(&vdp_instp->vdp_inst_cv);
}
return (B_FALSE);
}
/* Send DEASSOC on all the VSI/VDP profiles */
int
vdp_tlv_fini()
{
vdp_inst_t *vdp_instp;
vdp_profile_t *vdp_profp;
(void) pthread_mutex_lock(&vdp_inst_list_lock);
restart:
for (vdp_instp = list_head(&vdp_inst_list); vdp_instp != NULL;
vdp_instp = list_next(&vdp_inst_list, vdp_instp)) {
(void) pthread_mutex_lock(&vdp_instp->vdp_inst_lock);
if (vdp_instp->vdp_inst_state == VDP_REC_DELETED) {
(void) pthread_mutex_unlock(&vdp_instp->vdp_inst_lock);
continue;
}
for (vdp_profp = list_head(&vdp_instp->vdp_inst_profiles);
vdp_profp != NULL;
vdp_profp = list_next(&vdp_instp->vdp_inst_profiles,
vdp_profp)) {
if (vdp_profp->vdp_prof_deleted)
continue;
if (i_vdp_tlv_del_locked(vdp_instp, vdp_profp)) {
(void) pthread_mutex_unlock(
&vdp_instp->vdp_inst_lock);
goto restart;
}
}
(void) pthread_mutex_unlock(&vdp_instp->vdp_inst_lock);
}
while (!list_is_empty(&vdp_inst_list))
(void) pthread_cond_wait(&vdp_inst_list_cv,
&vdp_inst_list_lock);
(void) pthread_mutex_unlock(&vdp_inst_list_lock);
return (0);
}
/* Delete a VSI/VDP profile from a VDP instance */
int
vdp_tlv_del(vdpt_arg_t *vdp_argp)
{
vdp_inst_t *vdp_instp;
vdp_profile_t *vdp_profp;
/* Returns the record locked */
vdp_instp = vdp_inst_lookup(vdp_argp, NULL);
if (vdp_instp == NULL)
return (0);
vdp_profp = vdp_profile_lookup_by_linkid(vdp_instp,
vdp_argp->vdpt_virt_link_id);
if (vdp_profp == NULL) {
vdp_inst_release(vdp_instp);
return (0);
}
(void) i_vdp_tlv_del_locked(vdp_instp, vdp_profp);
vdp_inst_release(vdp_instp);
logtrace("profile for vnic %u Deleted\n",
vdp_argp->vdpt_virt_link_id);
return (0);
}
/* Add a VSI/VDP profile to a VDP instance. */
int
vdp_tlv_add(vdpt_arg_t *vdp_argp, vdp_link_info_t *link_infop)
{
vdp_inst_t *vdp_instp;
vdp_profile_t *vdp_profp;
uint16_t vdp_mac_vid_ps_pc;
uint8_t tlv_ps = 0;
uint8_t tlv_pcp = 0;
/* Returns the record locked */
vdp_instp = vdp_inst_lookup(vdp_argp, link_infop);
if (vdp_instp == NULL)
return (ENOMEM);
vdp_profp = vdp_profile_lookup_by_linkid(vdp_instp,
vdp_argp->vdpt_virt_link_id);
if (vdp_profp != NULL) {
vdp_inst_release(vdp_instp);
return (EEXIST);
}
vdp_profp = malloc(sizeof (vdp_profile_t));
if (vdp_profp == NULL) {
vdp_inst_release(vdp_instp);
return (ENOMEM);
}
bzero(vdp_profp, sizeof (vdp_profile_t));
vdp_profp->vdp_prof_vnic_id = vdp_argp->vdpt_virt_link_id;
vdp_profp->vdp_prof_vsi_state = VDP_TLV_DEASSOC;
vdp_profp->vdp_prof_cmd_pending = VDP_TLV_ASSOC;
vdp_profp->vdp_prof_proc_state = VDP_PROF_INIT;
(void) strlcpy(vdp_profp->vdp_prof_vnic_name,
link_infop->vli_virt_link_name, MAXLINKNAMELEN);
vdp_profp->vdp_prof_vsiid_format = vdp_argp->vdpt_tlv_vsiid_format;
if (vdp_profp->vdp_prof_vsiid_format != VDP_TLV_VSIID_FMT_MAC) {
vdp_inst_release(vdp_instp);
free(vdp_profp);
return (ENOTSUP);
}
/* Use the VSI's MAC address as the VSIID */
bcopy(link_infop->vli_virt_link_mac, &vdp_profp->vdp_prof_vsiid[10],
ETHERADDRL);
vdp_profp->vdp_prof_vnic_id = vdp_argp->vdpt_virt_link_id;
if (vdp_argp->vdpt_tlv_fltr_entries != 1) {
vdp_inst_release(vdp_instp);
free(vdp_profp);
return (ENOTSUP);
}
vdp_profp->vdp_prof_fltr_entries = vdp_argp->vdpt_tlv_fltr_entries;
vdp_profp->vdp_prof_fltr_info_format =
vdp_argp->vdpt_tlv_fltr_info_format;
bzero(&vdp_profp->vdp_prof_tlv_fltr_mac, sizeof (vtf_mac_t));
logtrace("VID is %u\n\n", vdp_argp->vdpt_virt_link_vid);
if (vdp_argp->vdpt_tlv_fltr_info_format == VDP_FLTR_FMT_MAC_VID) {
bcopy(link_infop->vli_virt_link_mac,
vdp_profp->vdp_prof_tlv_fltr_mac.vtf_mac_addr, ETHERADDRL);
} else if (vdp_argp->vdpt_tlv_fltr_info_format != VDP_FLTR_FMT_VID) {
vdp_inst_release(vdp_instp);
free(vdp_profp);
return (ENOTSUP);
}
vdp_mac_vid_ps_pc = (tlv_ps) << VTF_PS_SHIFT;
vdp_mac_vid_ps_pc |= (tlv_pcp) << VTF_PCP_SHIFT;
vdp_mac_vid_ps_pc |= vdp_argp->vdpt_virt_link_vid;
vdp_profp->vdp_prof_tlv_fltr_mac.vtf_mac_ps_pcp_vid =
htons(vdp_mac_vid_ps_pc);
vdp_profp->vdp_prof_id_type = vdp_argp->vdpt_tlv_type_id;
vdp_profp->vdp_prof_id_vers = vdp_argp->vdpt_tlv_type_version;
/* Get the VSI Manager ID TLV */
bzero(&vdp_profp->vdp_prof_mgr_tlv, sizeof (vdp_tlv_mgr_t));
vdp_profp->vdp_prof_mgr_tlv.vdp_mgr_tlv_type_len = VDP_TLV_MGR_ID;
vdp_profp->vdp_prof_mgr_tlv.vdp_mgr_tlv_type_len <<=
VDP_TLV_TYPE_SHIFT;
vdp_profp->vdp_prof_mgr_tlv.vdp_mgr_tlv_type_len |=
VDP_VSI_MGR_ID_LEN;
vdp_profp->vdp_prof_mgr_tlv.vdp_mgr_tlv_type_len =
htons(vdp_profp->vdp_prof_mgr_tlv.vdp_mgr_tlv_type_len);
if (strcmp(vdp_argp->vdpt_mgr_id, "::") != 0) {
bcopy(vdp_argp->vdpt_mgr_id,
vdp_profp->vdp_prof_mgr_tlv.vdp_mgr_tlv_id,
VDP_VSI_MGR_ID_LEN);
}
bzero(&vdp_profp->vdp_prof_mgr_enc_tlv, sizeof (vdp_tlv_mgrenc_t));
/* Add the Oracle specific encoding TLV */
if (vdp_argp->vdpt_mgrenc != MVE_ENCODING_NONE) {
logtrace("adding encoding\n");
vdp_profp->vdp_prof_mgr_enc_tlv.vtme_type_len =
VDP_TLV_ORG_SPEC;
vdp_profp->vdp_prof_mgr_enc_tlv.vtme_type_len <<=
VDP_TLV_TYPE_SHIFT;
/* sizeof (uint16_t) is for typelen */
vdp_profp->vdp_prof_mgr_enc_tlv.vtme_type_len |=
(sizeof (vdp_tlv_mgrenc_t) - sizeof (uint16_t));
vdp_profp->vdp_prof_mgr_enc_tlv.vtme_type_len =
htons(vdp_profp->vdp_prof_mgr_enc_tlv.vtme_type_len);
/* ouistype is 3-byte OUI + 1 byte subtype */
vdp_hton24(vdp_profp->vdp_prof_mgr_enc_tlv.vtme_ouistype,
VDP_ORACLE_OUI);
vdp_profp->vdp_prof_mgr_enc_tlv.vtme_ouistype[3] =
VDP_ORACLEOUI_VSIMGR_SUBTYPE;
vdp_profp->vdp_prof_mgr_enc_tlv.vtme_encid =
vdp_argp->vdpt_mgrenc;
}
vdp_profp->vdp_prof_instp = vdp_instp;
list_insert_tail(&vdp_instp->vdp_inst_profiles, vdp_profp);
/*
* insert in the timer queue
*/
vdp_profp->vdp_prof_timer_id =
iu_schedule_timer_ms(vdp_instp->vdp_inst_tq, 0,
vdp_process_timeout, vdp_profp);
(void) pthread_cond_signal(&vdp_instp->vdp_inst_cv);
vdp_inst_release(vdp_instp);
logtrace("profile for vnic %s Added\n",
link_infop->vli_virt_link_name);
return (0);
}
char *
vdp_tlv_type_to_str(int tlv_type)
{
switch (tlv_type) {
case VDP_TLV_NONE:
return ("NONE");
case VDP_TLV_PRE_ASSOC:
return ("PRE_ASSOC");
case VDP_TLV_PRE_ASSOC_W_RR:
return ("PRE_ASSOC_W_RR");
case VDP_TLV_ASSOC:
return ("ASSOC");
case VDP_TLV_DEASSOC:
return ("DEASSOC");
case VDP_TLV_MGR_ID:
return ("MGR_ID");
case VDP_TLV_TIMEDOUT:
return ("TIMEDOUT");
default:
return ("Unknown");
}
/* NOTREACHED */
return (NULL);
}
char *
vdp_vsiid_fmt_to_str(int vsiid_fmt_type)
{
switch (vsiid_fmt_type) {
case VDP_TLV_VSIID_FMT_IPV4:
return ("IPV4");
case VDP_TLV_VSIID_FMT_IPV6:
return ("IPV6");
case VDP_TLV_VSIID_FMT_MAC:
return ("MAC");
case VDP_TLV_VSIID_FMT_LOCAL:
return ("LOCAL");
case VDP_TLV_VSIID_FMT_UUID:
return ("UUID");
default:
return ("Unknown");
}
/* NOTREACHED */
return (NULL);
}
char *
vdp_tlv_fltr_fmt_to_str(int tlv_fltr_fmt)
{
switch (tlv_fltr_fmt) {
case VDP_FLTR_FMT_VID:
return ("VID");
case VDP_FLTR_FMT_MAC_VID:
return ("MAC_VID");
case VDP_FLTR_FMT_GID_VID:
return ("GID_VID");
case VDP_FLTR_FMT_GID_MAC_VID:
return ("GID_MAC_VID");
default:
return ("Unknown");
}
/* NOTREACHED */
return (NULL);
}
static void
vdp_tlv_fltr_to_str(char *buf, int n, vdp_profile_t *vdp_profp)
{
switch (vdp_profp->vdp_prof_fltr_info_format) {
case VDP_FLTR_FMT_MAC_VID: {
uint8_t *addrp = (uint8_t *)
vdp_profp->vdp_prof_tlv_fltr_mac.vtf_mac_addr;
(void) snprintf(buf, n, "%x:%x:%x:%x:%x:%x/%d",
addrp[0], addrp[1], addrp[2], addrp[3], addrp[4], addrp[5],
ntohs(vdp_profp->vdp_prof_tlv_fltr_mac.vtf_mac_ps_pcp_vid));
break;
}
default:
(void) snprintf(buf, n, "Unknown");
break;
}
}
void
vdp_reason2str(uint8_t reason, char *reasonstr, int len)
{
boolean_t request = B_TRUE;
uint8_t reastat = reason & 0x0F;
uint8_t flags = reason >> 4;
if ((flags & 0x4) != 0)
request = B_FALSE;
if (request) {
len = snprintf(reasonstr, 256, "Request");
if (flags != 0) {
len += snprintf(reasonstr + len, 256, "; Flags (%x):",
flags);
if (flags & 0x1) {
len += snprintf(reasonstr + len, 256 - len,
" M-Bit");
}
if (flags & 0x2) {
(void) snprintf(reasonstr + len, 256 - len,
" S-Bit");
}
}
} else {
len = snprintf(reasonstr, 256, "Response :");
switch (reastat) {
case 0:
len += snprintf(reasonstr + len, 256, "Success");
break;
case 1:
len += snprintf(reasonstr + len, 256, "Invalid Format");
break;
case 2:
len += snprintf(reasonstr + len, 256,
"Insufficient Resources");
break;
case 3:
len += snprintf(reasonstr + len, 256,
"Unable to contact VSI Manager");
break;
case 4:
len += snprintf(reasonstr + len, 256, "Other failure");
break;
case 5:
len += snprintf(reasonstr + len, 256,
"Invalid VID, GroupID, or MAC address");
break;
default:
len += snprintf(reasonstr + len, 256, "Unknown Error");
break;
}
if ((flags & 0x0F) != 0) {
len += snprintf(reasonstr + len, 256, "; Flags (%x):",
flags);
if (flags & 0x1) {
len += snprintf(reasonstr + len, 256,
" Hard Error");
}
if (flags & 0x2)
(void) snprintf(reasonstr + len, 256, " Keep");
}
}
}
char *
vdp_vsimgr_encoding(uint8_t encoding)
{
if (encoding == MVE_ORACLE_VSIMGRID_V1)
return ("oracle_vsimgr_v1");
return ("Unknown encoding");
}