trill.c revision 640c1670a105457bb0040e8e11037b53ab6ebcfa
/*
* 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
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* This module supports AF_TRILL sockets and TRILL layer-2 forwarding.
*/
#include <sys/socketvar.h>
#include <sys/ethernet.h>
#include <sys/mac_client.h>
#include <sys/mac_provider.h>
#include <sys/mac_client_priv.h>
#include "trill_impl.h"
static void trill_stop_recv(trill_sock_t *);
uint16_t);
static void trill_sock_unref(trill_sock_t *);
static void trill_kstats_init(trill_sock_t *, const char *);
static list_t trill_inst_list;
static krwlock_t trill_inst_rwlock;
static smod_reg_t sinfo = {
"trill",
NULL,
};
/* modldrv structure */
static struct modlsockmod sockmod = {
};
/* modlinkage structure */
static struct modlinkage ml = {
&sockmod,
};
#define VALID_NICK(n) ((n) != RBRIDGE_NICKNAME_NONE && \
(n) != RBRIDGE_NICKNAME_UNUSED)
static mblk_t *
{
int extra_hdr_len;
struct ether_vlan_header *ethvlanhdr;
/* When sending on the PVID, we must not give a VLAN ID */
tci = TRILL_NO_TCI;
/*
* Create new Ethernet header and include additional space
*/
return (NULL);
}
if (tci != TRILL_NO_TCI) {
/* LINTED: alignment */
}
if (!trill_hdr_ok) {
/* LINTED: alignment */
}
return (hdr_mp);
}
/*
* TRILL local recv function. TRILL data frames that should be received
* by the local system are decapsulated here and passed to bridging for
* learning and local system receive. Only called when we are the forwarder
* on the link (multi-dest frames) or the frame was destined for us.
*/
static void
{
struct ether_header *inner_ethhdr;
/* LINTED: alignment */
DB_CKSUMFLAGS(mp) = 0;
/*
* Transmit the decapsulated frame on the link via Bridging.
* Bridging does source address learning and appropriate forwarding.
*/
}
/*
* Determines the outgoing link to reach a RBridge having the given nick
* Assumes caller has acquired the trill instance rwlock.
*/
static trill_sock_t *
{
break;
}
}
return (tsp);
}
/*
* TRILL destination forwarding function. Transmits the TRILL data packet
* to the next-hop, adjacent RBridge. Consumes passed mblk_t.
*/
static void
{
struct ether_header *ethhdr;
int ethtype;
int ethhdrlen;
goto dest_fwd_fail;
/*
* For broadcast links by using the dest address of
* the RBridge to forward the frame should result in
* savings. When the link is a bridged LAN or there are
* many end stations the frame will not always be flooded.
*/
goto dest_fwd_fail;
/* LINTED: alignment */
/* Pullup Ethernet and TRILL header (w/o TRILL options) */
ethhdrlen = sizeof (struct ether_header) +
goto dest_fwd_fail;
/* LINTED: alignment */
/* Update TRILL header with ingress and egress nicks for new frames */
if (!has_trill_hdr) {
/* We are creating a new TRILL frame */
goto dest_fwd_fail;
}
/* Set hop count and update header in packet */
trillhdr->th_hopcount--;
/* Clear checksum flag and transmit frame on the link */
DB_CKSUMFLAGS(fwd_mp) = 0;
} else {
}
return;
}
/*
* TRILL multi-destination forwarding. Transmits the packet to the adjacencies
* on the distribution tree determined by the egress nick. Source addr (saddr)
* is NULL for new TRILL packets originating from us.
*/
static void
{
int idx;
/* Lookup the egress nick info, this is the DT root */
goto fail_multidest_fwd;
/* Send a copy to all our adjacencies on the DT root */
/* Check for a valid adjacency node */
continue;
/* Do not forward back to adjacency that sent the pkt to us */
ETHERADDRL) == 0)) {
continue;
}
/* Check if adj is marked as reaching inner VLAN downstream */
if ((inner_vlan != VLAN_ID_NONE) &&
inner_vlan)) {
continue;
}
/*
* Save the nick and look ahead to see if we should forward the
* frame to more adjacencies. We avoid doing a copy for this
* nick and use the passed mblk when we can consume the passed
* mblk.
*/
continue;
}
break;
B_TRUE, egressnick);
}
if (nicksaved) {
B_TRUE, egressnick);
return;
}
if (free_mblk) {
}
}
/*
* TRILL data receive function. Forwards the received frame if necessary
* and also determines if the received frame should be consumed locally.
* Consumes passed mblk.
*/
static void
{
struct ether_header *ethhdr;
int inner_vlan = VLAN_ID_NONE;
int tci;
int idx;
/* Copy Ethernet source address before modifying packet */
/* Pull up TRILL header if necessary. */
min_size = sizeof (trill_header_t);
goto fail;
/* LINTED: alignment */
trill_header_t *, trillhdr);
goto fail;
}
/* Drop if unknown or invalid nickname */
trill_header_t *, trillhdr);
goto fail;
}
/* Drop if we received a packet with our nick as ingress */
goto fail;
/* Re-pull any TRILL options and inner Ethernet header */
sizeof (struct ether_header);
goto fail;
/* LINTED: alignment */
}
trillhdrlen = sizeof (trill_header_t) +
/*
* Get the inner Ethernet header, plus the inner VLAN header if there
* is one.
*/
/* LINTED: alignment */
min_size += sizeof (struct ether_vlan_extinfo);
goto fail;
/* LINTED: alignment */
/* LINTED: alignment */
}
}
if (!trillhdr->th_multidest) {
/* Inner MacDA must be unicast */
goto fail;
/* Ingress and Egress nicks must be different */
goto fail;
trill_header_t *, trillhdr);
} else if (trillhdr->th_hopcount > 0) {
} else {
goto fail;
}
return;
}
/*
* Multi-destination frame: perform checks verifying we have
* received a valid multi-destination frame before receiving the
* frame locally and forwarding the frame to other RBridges.
*
* Check if we received this multi-destination frame on a
* adjacency in the distribution tree indicated by the frame's
* egress nickname.
*/
goto fail;
continue;
break;
}
}
goto fail;
}
/*
* Reverse path forwarding check. Check if the ingress RBridge
* that has forwarded the frame advertised the use of the
* distribution tree specified in the egress nick.
*/
goto fail;
break;
}
/*
* Allow receipt of forwarded frame with the highest
* tree root RBridge as the egress RBridge when the
* ingress RBridge has not advertised the use of any
* distribution trees.
*/
goto fail;
}
}
/* Check hop count before doing any forwarding */
if (trillhdr->th_hopcount == 0)
goto fail;
/* Forward frame using the distribution tree specified by egress nick */
/* Tell forwarding not to free if we're the link forwarder. */
B_FALSE);
/*
* Send de-capsulated frame locally if we are the link forwarder (also
* does bridge learning).
*/
return;
fail:
trill_sock_t *, tsock);
}
static void
{
return;
}
/*
* If another thread is closing the socket then wait. Our callers
* expect us to return only after the socket is closed.
*/
goto stop_retry;
}
/*
* Set state and flags to block new bind or close calls
* while we close the socket.
*/
/* Wait until all AF_TRILL socket transmit operations are done */
while (tsock->ts_sockthreadcount > 0)
/*
* We are guaranteed to be the only thread closing on the
* socket while the TSF_CLOSEWAIT flag is set, all others cv_wait
* for us to finish.
*/
/*
* Release lock before bridge_trill_lnunref to prevent deadlock
* between trill_ctrl_input thread waiting to acquire ts_socklock
* and bridge_trill_lnunref waiting for the trill thread to finish.
*/
/*
* Release TRILL link reference from Bridging. On return from
* bridge_trill_lnunref we can be sure there are no active TRILL data
* threads for this link.
*/
/* Set socket as unbound & wakeup threads waiting for socket to close */
}
static int
{
int err = 0;
return (EINVAL);
goto bind_error;
}
goto bind_error;
}
sizeof (struct sockaddr_dl));
sizeof (datalink_id_t));
goto bind_error;
}
return (err);
}
static int
{
/* If a bind has not been done, we can't unbind. */
return (EINVAL);
return (0);
}
static void
{
} else {
}
}
/*
* This is called when the bridge module receives a TRILL-encapsulated packet
* on a given link or a packet identified as "TRILL control." We must verify
* that it's for us (it almost certainly will be), and then either decapsulate
* (if it's to our nickname), forward (if it's to someone else), or send up one
* of the sockets (if it's control traffic).
*
* Sadly, on Ethernet, the control traffic is identified by Outer.MacDA, and
* not by TRILL header information.
*/
static void
{
/*
* Only receive packet if the source address is not multicast (which is
* bogus).
*/
goto discard;
/*
* Check if this is our own packet reflected back. It should not be.
*/
goto discard;
/* Only receive unicast packet if addressed to us */
goto discard;
/* TRILL data packets */
} else {
/* Send received control packet upstream */
}
return;
}
/*
* This is called when the bridge module discovers that the destination address
* for a packet is not local -- it's through some remote node. We must verify
* that the remote node isn't our nickname (it shouldn't be), add a TRILL
* header, and then use the IS-IS data to determine which link and which
* next-hop RBridge should be used for output. We then transmit on that link.
*
* The egress_nick is RBRIDGE_NICKNAME_NONE for the "unknown destination" case.
*/
static void
{
int vlan = VLAN_ID_NONE;
/* egress_nick = RBRIDGE_NICKNAME_NONE is valid */
goto discard;
/* Check if our own nick is valid before we do any forwarding */
if (!VALID_NICK(ournick))
goto discard;
/*
* For Multi-Destination forwarding determine our choice of
* root distribution tree. If we didn't choose a distribution
* tree (dtroots_count=0) then we use the highest priority tree
* root (t_treeroot) else we drop the packet without forwarding.
*/
if (egress_nick == RBRIDGE_NICKNAME_NONE) {
goto discard;
/*
* Use the first DT configured for now. In future we
* should have DT selection code here.
*/
}
if (!VALID_NICK(dtnick)) {
goto discard;
}
}
/*
* Retrieve VLAN ID of the native frame used for VLAN
* pruning of multi-destination frames.
*/
if (hdr_info->mhi_istagged) {
}
if (egress_nick == RBRIDGE_NICKNAME_NONE) {
} else {
}
return;
}
/*
* This is called when the bridge module has completely torn down a bridge
* instance and all of the attached links. We need to make the TRILL instance
* go away at this point.
*/
static void
{
}
/*
* This is called when the bridge module is tearing down a link, but before the
* actual tear-down starts. When this function returns, we must make sure that
* we will not initiate any new transmits on this link.
*/
static void
{
}
static void
trill_init(void)
{
}
static void
trill_fini(void)
{
}
/* Loadable module configuration entry points */
int
_init(void)
{
int rc;
trill_init();
trill_fini();
return (rc);
}
int
{
}
int
_fini(void)
{
int rc;
trill_fini();
return (rc);
}
static void
{
int i;
char kstatname[KSTAT_STRLEN];
static const char *sock_kstats_list[] = { TRILL_KSSOCK_NAMES };
char link_name[MAXNAMELEN];
int num;
int err;
return;
}
for (i = 0; i < num; i++) {
}
}
}
static trill_sock_t *
trill_do_open(int flags)
{
}
return (tsock);
}
static int
{
/* Allocate some memory (speculatively) before taking locks */
if (can_create)
break;
}
}
/* Register TRILL instance with bridging */
return (ENOENT);
}
}
/* If we didn't need the preallocated memory, then discard now. */
return (0);
}
static void
{
return;
}
static void
{
}
}
static void
{
/* Remove socket from TRILL instance socket list */
}
static void
{
int i;
if (!lockheld)
for (i = RBRIDGE_NICKNAME_MIN; i < RBRIDGE_NICKNAME_MAX; i++) {
}
if (!lockheld)
}
static void
{
}
static void
{
}
}
static trill_node_t *
{
if (!VALID_NICK(nick))
return (NULL);
if (nick_entry != NULL) {
}
return (nick_entry);
}
static int
{
if (!lockheld)
if (VALID_NICK(nick)) {
if (nick_entry != NULL) {
rc = 0;
}
}
if (!lockheld)
return (rc);
}
static int
{
int size;
/* First make sure we have at least the header available */
return (EFAULT);
if (!VALID_NICK(nick)) {
&tnihdr);
return (EINVAL);
}
if (size > TNI_MAXSIZE)
return (EINVAL);
return (EFAULT);
}
if (self) {
} else {
}
tip->ti_nodecount++;
return (0);
}
static int
{
int error = 0;
switch (cmd) {
case TRILL_DESIGVLAN: {
return (EFAULT);
break;
}
case TRILL_VLANFWDER: {
return (EINVAL);
return (EFAULT);
break;
}
case TRILL_SETNICK:
return (EINVAL);
break;
case TRILL_GETNICK:
return (EINVAL);
mode) != 0)
break;
case TRILL_ADDNICK:
break;
break;
case TRILL_DELNICK: {
break;
return (EFAULT);
break;
}
case TRILL_DELALL:
break;
break;
case TRILL_TREEROOT: {
break;
return (EFAULT);
if (!VALID_NICK(treeroot))
return (EINVAL);
break;
}
case TRILL_HWADDR:
break;
mode) != 0)
return (EFAULT);
break;
case TRILL_NEWBRIDGE: {
char bname[MAXLINKNAMELEN];
return (ENOTSUP);
/* ts_tip can only be set once */
return (EEXIST);
return (EFAULT);
break;
}
case TRILL_GETBRIDGE: {
char bname[MAXLINKNAMELEN];
/* ts_tip can only be set once */
return (EEXIST);
return (EFAULT);
break;
}
case TRILL_LISTNICK: {
return (EINVAL);
return (EFAULT);
if (nick >= RBRIDGE_NICKNAME_MAX) {
break;
}
while (++nick < RBRIDGE_NICKNAME_MAX) {
} else {
}
break;
}
}
if (nick >= RBRIDGE_NICKNAME_MAX)
return (EFAULT);
break;
}
/*
* Port flush: this is used when we lose AF on a port. We must discard
* all regular bridge forwarding entries on this port with the
* indicated VLAN.
*/
case TRILL_PORTFLUSH: {
return (EINVAL);
break;
}
/*
* Nick flush: this is used when we lose AF on a port. We must discard
* all bridge TRILL forwarding entries on this port with the indicated
* VLAN.
*/
case TRILL_NICKFLUSH: {
return (EINVAL);
break;
}
case TRILL_GETMTU:
break;
return (EFAULT);
break;
default:
break;
}
return (error);
}
/*
* Sends received packet back upstream on the TRILL socket.
* Consumes passed mblk_t.
*/
static void
{
int udi_size;
struct T_unitdata_ind *tudi;
struct sockaddr_dl *sdl;
char *lladdr;
int error;
if (tsock->ts_flow_ctrld) {
return;
}
udi_size = sizeof (struct T_unitdata_ind) +
sizeof (struct sockaddr_dl);
return;
}
/* LINTED: alignment */
tudi->OPT_length = 0;
sizeof (struct sockaddr_dl);
/* Information of the link on which packet was received. */
/* LINTED: alignment */
lladdr += ETHERADDRL;
/* LINTED: alignment */
} else if (error != 0) {
} else {
}
}
/* ARGSUSED */
static void
{
struct sock_proto_props sopp;
sopp.sopp_wroff = 0;
sopp.sopp_minpsz = 0;
}
/* ARGSUSED */
static int
{
return (0);
}
/* ARGSUSED */
static int
{
int error;
else
return (error);
}
/* ARGSUSED */
static int
{
struct sockaddr_dl *laddr;
goto eproto;
/*
* The name is a datalink_id_t, the address is an Ethernet address, and
* the selector value is the VLAN ID.
*/
goto eproto;
goto eproto;
}
/*
* Safe to dereference VLAN now, as we've checked the user's specified
* values, and alignment is now guaranteed.
*/
tci = TRILL_NO_TCI;
} else {
/* LINTED: alignment */
}
} else {
}
}
/* Wake up any threads blocking on us */
return (0);
return (EPROTO);
}
/* ARGSUSED */
static int
{
int rc;
switch (cmd) {
/* List of unprivileged TRILL ioctls */
case TRILL_GETNICK:
case TRILL_GETBRIDGE:
case TRILL_LISTNICK:
break;
default:
if (secpolicy_dl_config(cr) != 0)
return (EPERM);
break;
}
/* Lock ensures socket state is unchanged during ioctl handling */
return (rc);
}
static void
{
}
static sock_downcalls_t sock_trill_downcalls = {
trill_activate, /* sd_activate */
sock_accept_notsupp, /* sd_accept */
trill_bind, /* sd_bind */
sock_listen_notsupp, /* sd_listen */
sock_connect_notsupp, /* sd_connect */
sock_getpeername_notsupp, /* sd_getpeername */
sock_getsockname_notsupp, /* sd_getsockname */
sock_getsockopt_notsupp, /* sd_getsockopt */
sock_setsockopt_notsupp, /* sd_setsockopt */
trill_send, /* sd_send */
NULL, /* sd_send_uio */
NULL, /* sd_recv_uio */
NULL, /* sd_poll */
sock_shutdown_notsupp, /* sd_shutdown */
trill_clr_flowctrl, /* sd_setflowctrl */
trill_ioctl, /* sd_ioctl */
trill_close /* sd_close */
};
/* ARGSUSED */
static sock_lower_handle_t
{
return (NULL);
}
return ((sock_lower_handle_t)tsock);
}