/*
* 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
* 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
*/
/*
*/
#include <lldp.h>
#include <libdlvnic.h>
#include <libscf.h>
#include <sys/dls_mgmt.h>
#include <libnvpair.h>
#include "vdp_impl.h"
#include <sys/mac_flow.h>
static int __vdp_logging = 0;
static void vdp_tlv_fltr_to_str(char *, int, vdp_profile_t *);
/* PRINTFLIKE2 */
void
{
if (__vdp_logging == 0)
return;
if (__vdp_logging == VDP_SYS_LOG)
else
}
/*
* Global initializations done once
*/
void
{
__vdp_logging = log;
}
/* Given a linkid for a VSI, return its profile */
static vdp_profile_t *
{
if (vdp_profp->vdp_prof_deleted)
continue;
return (vdp_profp);
}
}
return (NULL);
}
/* Given a VSIID (MAC address) return the VSI profile */
static vdp_profile_t *
{
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
{
char *wptr;
/* Type-Length of the VDP Request */
/* Reason code */
vdp_tlvp->vdp_tlv_reason = 0;
/* 3-byte VSI Type ID */
/* VSI Version */
/* VSIID format */
/* VSIID */
/* VSI Filter Info Format */
/* No. of Filter Info. entries */
/* On the wire VDP packet */
/* ECP header offset */
/* VSI Manager ID */
wptr += sizeof (vdp_tlv_mgr_t);
/* Oracle org specific OUI, if needed */
sizeof (vdp_tlv_mgrenc_t));
wptr += sizeof (vdp_tlv_mgrenc_t);
}
/* VDP profile created above */
}
/* Remove a profile from a VDP instance */
void
{
}
}
/*
* Process a timeout for a VDP profile. This includes sending the initial
* request, sending KeepAlives and timing out a VDP profile
*/
static void
{
/*
* 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.
*/
top:
/* Initial state */
(void) iu_cancel_timer(tq,
if (vdp_profp->vdp_prof_deleted) {
}
} else {
}
goto top;
} else {
}
(void) iu_cancel_timer(tq,
if (vdp_profp->vdp_prof_deleted) {
}
} else {
}
} else {
/*
* VDP ACK did not come
*/
/*
* If prof was deleted -- free it
*/
if (vdp_profp->vdp_prof_deleted) {
}
}
}
/* The VDP state machine associated with a VDP instance */
static void *
{
/* 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.
*/
goto vdp_rec_deleted;
}
} else {
int new_secs;
int new_msecs;
}
/*
* When each VSI instance is destroyed
* timer is cleaned up
*/
(void) pthread_mutex_lock(&vdp_inst_list_lock);
logtrace("VDP instance main thread exiting \n");
/* 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);
} else {
}
}
/* 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
{
char *tlv_vsiid;
goto done;
if (len < sizeof (vdp_tlv_mgr_t))
goto done;
/*
* 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, we will send a deassoc.
*/
if (tlv_type == VDP_TLV_ORG_SPEC) {
if (tlv_len ==
(sizeof (vdp_tlv_mgrenc_t) - sizeof (uint16_t)) &&
oracle_oui = B_TRUE;
}
tlv_type);
len -= sizeof (vdp_tlv_mgrenc_t);
}
goto done;
logtrace("vdp_ecp_cb: could not find vdp_profp \n");
return;
} else if (vdp_profp->vdp_prof_deleted) {
vdp_profp->vdp_prof_timer_id, 0);
else
0, vdp_process_timeout, vdp_profp);
return;
}
/*
* Verify that the ACK is indeed for the TLV that we sent out.
* Except for VDP_TLV_DEASSOC which can be unsolicited.
*/
/*
* To Do: ADD CODE TO CHECK for non zero resp code
*/
logtrace("vdp_ecp_cb: expecting tlv type %s got %s \n",
return;
}
/*
* If we have sent MVE_ORACLE_VSIMGRID_V1 OUI, then we expect to
* get it back, else send a DEASSOC.
*/
!oracle_oui) {
}
logtrace("vdp_ecp_cb: vdp_profp KA timer not found \n");
}
if (tlv_type != VDP_TLV_DEASSOC) {
if (send_deassoc) {
0, vdp_process_timeout, vdp_profp);
goto done;
}
}
}
0, vdp_process_timeout, vdp_profp);
(void) pthread_cond_signal(
&vdp_instp->vdp_inst_cv);
} else {
(void) pthread_cond_signal(
&vdp_instp->vdp_inst_cv);
}
}
} else {
if (vdp_profp->vdp_prof_deleted) {
}
}
done:
}
/*
* Initialize a VDP instance. There is a VDP instance per port. The VDP
* on the physical port. Initialization also getting an ECP handle
* and setting the callback function for ECP to invoke.
*/
static int
{
int error;
if (vdp_instp->vdp_inst_sockfd < 0) {
vdp_instp->vdp_inst_sockfd = 0;
goto failed;
}
sizeof (sll)) == -1) {
goto failed;
}
logerr("PF_PACKET Failed to enable multicast membership\n");
return (errno);
}
logerr("PF_PACKET Failed to disable promisc\n");
return (errno);
}
(void) pthread_attr_init(&attr);
__vdp_logging, &error);
return (0);
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 *
{
(void) pthread_mutex_lock(&vdp_inst_list_lock);
continue;
}
break;
}
logdebug("vdp_inst_lookup: malloc failed\n");
} else {
link_infop) != 0) {
logerr("vdp_inst_init failed\n");
}
}
}
done:
(void) pthread_mutex_unlock(&vdp_inst_list_lock);
return (vdp_instp);
}
static void
{
}
/*
* Return the stats associated with an instance or profile specified
* in vdp_argp.
*/
int
{
return (EINVAL);
return (EINVAL);
return (EINVAL);
}
} else {
}
logtrace("ID %u: i %llu, o %llu, k %llu\n",
return (0);
}
/* Return the ECP stats associated with a VDP instance */
int
{
return (EINVAL);
return (EINVAL);
return (0);
}
/* Return the VDP timer values */
/* ARGSUSED */
void
{
}
/* Return the ECP timer values associated with a VDP instance */
/* ARGSUSED */
void
{
}
/* Get the ECP information for all the VDP instances */
{
*nelem = 0;
return (NULL);
(void) pthread_mutex_lock(&vdp_inst_list_lock);
continue;
}
logtrace("Physical NIC %s \n",
(vdp_argp->vdpt_phy_link_id !=
vdp_instp->vdp_inst_phy_id)) {
continue;
}
vdp_profp)) {
if ((vdp_argp->vdpt_virt_link_id !=
continue;
(void) pthread_mutex_unlock(
(void) pthread_mutex_unlock(
return (NULL);
}
/*
* Only one entry per port
*/
break;
}
}
if (index == 0) {
} else {
}
(void) pthread_mutex_unlock(&vdp_inst_list_lock);
return (list);
}
/* Get the VDP information for all the VDP instances */
{
*nelem = 0;
return (NULL);
(void) pthread_mutex_lock(&vdp_inst_list_lock);
continue;
}
logtrace("Physical NIC %s \n",
(vdp_argp->vdpt_phy_link_id !=
vdp_instp->vdp_inst_phy_id)) {
continue;
}
vdp_profp)) {
logtrace("\tVNIC:%s\t Prof-ID:%d\t Vsi State:%d\n",
if ((vdp_argp->vdpt_virt_link_id !=
continue;
sizeof (vdp_profp->vdp_prof_vsiid));
(void) pthread_mutex_unlock(
(void) pthread_mutex_unlock(
return (NULL);
}
}
}
if (index == 0) {
} else {
}
(void) pthread_mutex_unlock(&vdp_inst_list_lock);
return (list);
}
static boolean_t
{
/*
* 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
*/
/*
* Since the previous request never made it to the bridge
* OR the bridge has already deleted the association
* just delete the profile right now.
*/
return (B_TRUE);
}
/*
* 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_timer_id, 0);
}
return (B_FALSE);
}
int
{
(void) pthread_mutex_lock(&vdp_inst_list_lock);
continue;
}
vdp_profp)) {
if (vdp_profp->vdp_prof_deleted)
continue;
(void) pthread_mutex_unlock(
goto restart;
}
}
}
while (!list_is_empty(&vdp_inst_list))
(void) pthread_cond_wait(&vdp_inst_list_cv,
(void) pthread_mutex_unlock(&vdp_inst_list_lock);
return (0);
}
int
{
/* Returns the record locked */
return (0);
return (0);
}
logtrace("profile for vnic %u Deleted\n",
return (0);
}
int
{
/* Returns the record locked */
return (ENOMEM);
return (EEXIST);
}
return (ENOMEM);
}
return (ENOTSUP);
}
/* Use the VSI's MAC address as the VSIID */
return (ENOTSUP);
}
return (ENOTSUP);
}
/* Get the VSI Manager ID TLV */
}
/* Add the Oracle specific encoding TLV */
logtrace("adding encoding\n");
/* sizeof (uint16_t) is for typelen */
(sizeof (vdp_tlv_mgrenc_t) - sizeof (uint16_t));
/* ouistype is 3-byte OUI + 1 byte subtype */
}
/*
* insert in the timer queue
*/
logtrace("profile for vnic %s Added\n",
return (0);
}
char *
{
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 *
{
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 *
{
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
{
switch (vdp_profp->vdp_prof_fltr_info_format) {
case VDP_FLTR_FMT_MAC_VID: {
break;
}
default:
break;
}
}
void
{
if ((flags & 0x4) != 0)
if (request) {
if (flags != 0) {
flags);
if (flags & 0x1) {
" M-Bit");
}
if (flags & 0x2) {
" S-Bit");
}
}
} else {
switch (reastat) {
case 0:
break;
case 1:
break;
case 2:
"Insufficient Resources");
break;
case 3:
"Unable to contact VSI Manager");
break;
case 4:
break;
case 5:
"Invalid VID, GroupID, or MAC address");
break;
default:
break;
}
if ((flags & 0x0F) != 0) {
flags);
if (flags & 0x1) {
" Hard Error");
}
if (flags & 0x2)
}
}
}
char *
{
if (encoding == MVE_ORACLE_VSIMGRID_V1)
return ("oracle_vsimgr_v1");
return ("Unknown encoding");
}