/*
* sppp_dlpi.c - Solaris STREAMS PPP multiplexing pseudo-driver DLPI handlers
*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation is hereby granted, provided that the above copyright
* notice appears in all copies.
*
* SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF
* THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES
*
* Copyright (c) 1994 The Australian National University.
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation is hereby granted, provided that the above copyright
* notice appears in all copies. This software is provided without any
* warranty, express or implied. The Australian National University
* makes no representations about the suitability of this software for
* any purpose.
*
* IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
* PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
* THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
* OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
* OR MODIFICATIONS.
*
* This driver is derived from the original SVR4 STREAMS PPP driver
* originally written by Paul Mackerras <paul.mackerras@cs.anu.edu.au>.
*
* Adi Masputra <adi.masputra@sun.com> rewrote and restructured the code
* for improved performance and scalability.
*/
#include <sys/ethernet.h>
#include <net/ppp_defs.h>
#include "s_common.h"
#include "sppp.h"
#if 0
#else
#define DBGERROR(x) ((void)0)
#endif
/* #define DBG_DLPI 1 */
#ifdef DBG_DLPI
struct sppp_dlpi_entry {
const char *sde_name;
};
{ DL_INFO_REQ, "DL_INFO_REQ" },
{ DL_INFO_ACK, "DL_INFO_ACK" },
{ DL_ATTACH_REQ, "DL_ATTACH_REQ" },
{ DL_DETACH_REQ, "DL_DETACH_REQ" },
{ DL_BIND_REQ, "DL_BIND_REQ" },
{ DL_BIND_ACK, "DL_BIND_ACK" },
{ DL_UNBIND_REQ, "DL_UNBIND_REQ" },
{ DL_OK_ACK, "DL_OK_ACK" },
{ DL_ERROR_ACK, "DL_ERROR_ACK" },
{ DL_SUBS_BIND_REQ, "DL_SUBS_BIND_REQ" },
{ DL_SUBS_BIND_ACK, "DL_SUBS_BIND_ACK" },
{ DL_SUBS_UNBIND_REQ, "DL_SUBS_UNBIND_REQ" },
{ DL_ENABMULTI_REQ, "DL_ENABMULTI_REQ" },
{ DL_DISABMULTI_REQ, "DL_DISABMULTI_REQ" },
{ DL_PROMISCON_REQ, "DL_PROMISCON_REQ" },
{ DL_PROMISCOFF_REQ, "DL_PROMISCOFF_REQ" },
{ DL_UNITDATA_REQ, "DL_UNITDATA_REQ" },
{ DL_UNITDATA_IND, "DL_UNITDATA_IND" },
{ DL_UDERROR_IND, "DL_UDERROR_IND" },
{ DL_UDQOS_REQ, "DL_UDQOS_REQ" },
{ DL_CONNECT_REQ, "DL_CONNECT_REQ" },
{ DL_CONNECT_IND, "DL_CONNECT_IND" },
{ DL_CONNECT_RES, "DL_CONNECT_RES" },
{ DL_CONNECT_CON, "DL_CONNECT_CON" },
{ DL_TOKEN_REQ, "DL_TOKEN_REQ" },
{ DL_TOKEN_ACK, "DL_TOKEN_ACK" },
{ DL_DISCONNECT_REQ, "DL_DISCONNECT_REQ" },
{ DL_DISCONNECT_IND, "DL_DISCONNECT_IND" },
{ DL_RESET_REQ, "DL_RESET_REQ" },
{ DL_RESET_IND, "DL_RESET_IND" },
{ DL_RESET_RES, "DL_RESET_RES" },
{ DL_RESET_CON, "DL_RESET_CON" },
{ DL_DATA_ACK_REQ, "DL_DATA_ACK_REQ" },
{ DL_DATA_ACK_IND, "DL_DATA_ACK_IND" },
{ DL_DATA_ACK_STATUS_IND, "DL_DATA_ACK_STATUS_IND" },
{ DL_REPLY_REQ, "DL_REPLY_REQ" },
{ DL_REPLY_IND, "DL_REPLY_IND" },
{ DL_REPLY_STATUS_IND, "DL_REPLY_STATUS_IND" },
{ DL_REPLY_UPDATE_REQ, "DL_REPLY_UPDATE_REQ" },
{ DL_REPLY_UPDATE_STATUS_IND, "DL_REPLY_UPDATE_STATUS_IND" },
{ DL_XID_REQ, "DL_XID_REQ" },
{ DL_XID_IND, "DL_XID_IND" },
{ DL_XID_RES, "DL_XID_RES" },
{ DL_XID_CON, "DL_XID_CON" },
{ DL_TEST_REQ, "DL_TEST_REQ" },
{ DL_TEST_IND, "DL_TEST_IND" },
{ DL_TEST_RES, "DL_TEST_RES" },
{ DL_TEST_CON, "DL_TEST_CON" },
{ DL_PHYS_ADDR_REQ, "DL_PHYS_ADDR_REQ" },
{ DL_PHYS_ADDR_ACK, "DL_PHYS_ADDR_ACK" },
{ DL_SET_PHYS_ADDR_REQ, "DL_SET_PHYS_ADDR_REQ" },
{ DL_GET_STATISTICS_REQ, "DL_GET_STATISTICS_REQ" },
{ DL_GET_STATISTICS_ACK, "DL_GET_STATISTICS_ACK" },
{ 0, NULL }
};
{ DL_UNBOUND, "DL_UNBOUND" },
{ DL_BIND_PENDING, "DL_BIND_PENDING" },
{ DL_UNBIND_PENDING, "DL_UNBIND_PENDING" },
{ DL_IDLE, "DL_IDLE" },
{ DL_UNATTACHED, "DL_UNATTACHED" },
{ DL_ATTACH_PENDING, "DL_ATTACH_PENDING" },
{ DL_DETACH_PENDING, "DL_DETACH_PENDING" },
{ DL_UDQOS_PENDING, "DL_UDQOS_PENDING" },
{ DL_OUTCON_PENDING, "DL_OUTCON_PENDING" },
{ DL_INCON_PENDING, "DL_INCON_PENDING" },
{ DL_CONN_RES_PENDING, "DL_CONN_RES_PENDING" },
{ DL_DATAXFER, "DL_DATAXFER" },
{ DL_USER_RESET_PENDING, "DL_USER_RESET_PENDING" },
{ DL_PROV_RESET_PENDING, "DL_PROV_RESET_PENDING" },
{ DL_RESET_RES_PENDING, "DL_RESET_RES_PENDING" },
{ DL_DISCON8_PENDING, "DL_DISCON8_PENDING" },
{ DL_DISCON9_PENDING, "DL_DISCON9_PENDING" },
{ DL_DISCON11_PENDING, "DL_DISCON11_PENDING" },
{ DL_DISCON12_PENDING, "DL_DISCON12_PENDING" },
{ DL_DISCON13_PENDING, "DL_DISCON13_PENDING" },
{ DL_SUBS_BIND_PND, "DL_SUBS_BIND_PND" },
{ DL_SUBS_UNBIND_PND, "DL_SUBS_UNBIND_PND" },
{ 0, NULL }
};
static const char *
{
break;
}
static const char *
{
break;
}
#else
#define DBGDLPI(x) ((void)0)
#endif /* DBG_DLPI */
/*
* DL_INFO_ACK template for point-to-point interface.
*/
DL_INFO_ACK, /* dl_primitive */
PPP_MAXMTU, /* dl_max_sdu */
0, /* dl_min_sdu */
SPPP_ADDRL, /* dl_addr_length */
/*
* snoop et. al. don't know about DL_OTHER so this entry
* facilities will work with PPP interfaces.
*/
DL_ETHER, /* dl_mac_type */
0, /* dl_reserved */
0, /* dl_current_state */
SPPP_SAPL, /* dl_sap_length */
DL_CLDLS, /* dl_service_mode */
0, /* dl_qos_length */
0, /* dl_qos_offset */
0, /* dl_range_length */
0, /* dl_range_offset */
DL_STYLE2, /* dl_provider_style */
sizeof (dl_info_ack_t), /* dl_addr_offset */
DL_VERSION_2, /* dl_version */
0, /* dl_brdcst_addr_length */
0, /* dl_brdcst_addr_offset */
0 /* dl_growth */
};
/*
* sppp_dlpi_pinfoinit()
*
* Description:
* Initialize dl_pinfo[], called from sppp_attach.
*/
void
sppp_dlpi_pinfoinit(void)
{
}
/*
* sppp_mproto()
*
* MT-Perimeters:
* shared inner, shared outer.
*
* Description:
*/
int
{
int len;
int error = 0;
return (0);
}
if (prim > DL_MAXPRIM) {
DL_MAXPRIM));
error = DL_BADPRIM;
} else {
"bad mproto: primitive %d not supported\n", prim));
"bad mproto: primitive len %d < %d\n", len,
error = DL_BADPRIM;
"bad state %d != %d for primitive %d\n",
error = DL_OUTSTATE;
}
}
if (error != 0) {
return (0);
}
#ifdef DBG_DLPI
{
else
"/%d: Dispatching unknown primitive %d\n",
}
#endif
}
/*
* sppp_dlattachreq()
*
* MT-Perimeters:
* shared inner, shared outer.
*
* Description:
* Perform DL_ATTACH_REQ request, called by sppp_mproto.
*/
static int
{
int error = 0;
if (IS_SPS_PIOATTACH(sps)) {
}
if (error != 0) {
} else {
}
return (0);
}
/*
* sppp_dl_attach_upper()
*
* MT-Perimeters:
* exclusive inner, exclusive outer.
*
* Description:
* Called by qwriter (INNER) from sppp_dlattachreq as the result of
* receiving a DL_ATTACH_REQ message.
*/
static void
{
/* If there's something here, it's detached. */
}
else
}
/*
* If we can't find or create it, then it's either because we're out of
* memory or because the requested PPA is owned by a different zone.
*/
return;
}
/*
* Preallocate the hangup message so that we're always able to
* send this upstream in the event of a catastrophic failure.
*/
return;
}
/*
* Add this stream to the head of the list of sibling streams
* which belong to the specified ppa.
*/
ppa->ppa_refcnt++;
/*
* And if this stream was marked as promiscuous (SPS_PROMISC), then we
* need to update the promiscuous streams count. This should only
* happen when DL_PROMISCON_REQ was issued prior to attachment.
*/
if (IS_SPS_PROMISC(sps)) {
ppa->ppa_promicnt++;
}
ppa->ppa_ppa_id));
}
/*
* sppp_dldetachreq()
*
* MT-Perimeters:
* shared inner, shared outer.
*
* Description:
* Perform DL_DETACH_REQ request, called by sppp_mproto.
*/
/* ARGSUSED */
static int
{
return (0);
}
/*
* sppp_dl_detach_upper()
*
* MT-Perimeters:
* exclusive inner, shared outer.
*
* Description:
* Called by qwriter (INNER) from sppp_dldetachreq as the result of
* receiving a DL_DETACH_REQ message.
*/
/* ARGSUSED */
static void
{
/*
* We don't actually detach from the PPA until closed or
* reattached.
*/
}
/*
* sppp_dlbindreq()
*
* MT-Perimeters:
* shared inner, shared outer.
*
* Description:
* Perform DL_BIND_REQ request, called by sppp_mproto.
*/
static int
{
int error = 0;
error = DL_OUTSTATE;
(req_sap != ETHERTYPE_ALLSAP)) {
error = DL_BADADDR;
}
if (error != 0) {
} else {
}
return (0);
}
/*
* sppp_dl_bind()
*
* MT-Perimeters:
* exclusive inner, shared outer.
*
* Description:
* Called by qwriter (INNER) from sppp_dlbindreq as the result of
* receiving a DL_BIND_REQ message.
*/
static void
{
(req_sap == ETHERTYPE_ALLSAP));
if (req_sap == ETHERTYPE_IP) {
} else if (req_sap == ETHERTYPE_IPV6) {
} else if (req_sap == ETHERTYPE_ALLSAP) {
sap = PPP_ALLSAP;
}
/*
* If there's another stream with the same sap has already been bound
* to the same ppa, then return with DL_NOADDR. However, we do make an
* exception for snoop (req_sap=0x00, sap=0xff) since multiple
* instances of snoop may execute an a given device.
*/
if (sap != PPP_ALLSAP) {
}
}
} else {
EEXIST);
return;
}
}
/*
* Tell the daemon that a DLPI bind has happened on this stream,
* and we'll only do this for PPP_IP or PPP_IPV6 sap (not snoop).
*/
#ifdef DBG_DLPI
"PPP_LINKSTAT_IPV6_BOUND"));
#endif
}
}
/*
* sppp_dlunbindreq()
*
* MT-Perimeters:
* shared inner, shared outer.
*
* Description:
* Perform DL_UNBIND_REQ request, called by sppp_mproto.
*/
/* ARGSUSED */
static int
{
return (0);
}
/*
* sppp_dl_unbind()
*
* MT-Perimeters:
* exclusive inner, shared outer.
*
* Description:
* Called by qwriter (INNER) from sppp_dlunbindreq as the result of
* receiving a DL_UNBIND_REQ message.
*/
static void
{
/* Flush messages on unbind, per DLPI specification. */
if (saydown)
if (saydown)
}
#ifdef DBG_DLPI
"PPP_LINKSTAT_IPV6_UNBOUND"));
#endif
}
}
sps->sps_req_sap = 0;
}
/*
* sppp_dlinforeq()
*
* MT-Perimeters:
* shared inner, shared outer.
*
* Description:
* Perform DL_INFO_REQ request, called by sppp_mproto.
*/
static int
{
/* Exchange current msg for a DL_INFO_ACK. */
/* mexchange already sent up an merror ENOSR */
return (0);
}
/* Fill in DL_INFO_ACK fields and reply */
*dlip = sppp_infoack;
#ifdef DBG_DLPI
{
else
}
#endif
return (0);
}
/*
* sppp_dlunitdatareq()
*
* MT-Perimeters:
* shared inner, shared outer.
*
* Description:
* Handle DL_UNITDATA_REQ request, called by sppp_mproto. This procedure
* gets called for M_PROTO (DLPI) style of transmission. The fact that we
* have acknowledged IP's fastpath probing (DL_IOC_HDR_INFO) does not
* guarantee that IP will always transmit via M_DATA, and it merely implies
* that such situation _may_ happen. In other words, IP may decide to use
* M_PROTO (DLPI) for data transmission should it decide to do so.
* Therefore, we should never place any restrictions or checks against
* streams marked with SPS_FASTPATH, since it is legal for this procedure
* to be entered with or without the bit set.
*/
static int
{
int dladdroff;
int dladdrlen;
int msize;
int error = 0;
/*
* If this stream is not attached to any ppas, then discard data
* coming down through this stream.
*/
}
if (error != 0) {
DL_BADDATA, error);
return (0);
}
/*
* Check if outgoing packet size is larger than allowed. We use
* msgdsize to count all of M_DATA blocks in the message.
*/
/* Log, and send it anyway */
ppa->ppa_otoolongs++;
}
if (IS_SPS_KDEBUG(sps)) {
"/%d: DL_UNITDATA_REQ (%d bytes) sps=0x%p flags=0x%b "
}
/* Allocate a message (M_DATA) to contain PPP header bytes. */
ppa->ppa_allocbfail++;
"DLPI unitdata: can't allocate header buffer\n"));
return (0);
}
/*
* Should there be any promiscuous stream(s), send the data up
* for each promiscuous stream that we recognize.
*/
if (is_promisc) {
B_FALSE);
}
/* Discard DLPI header and keep only IP payload (mp->b_cont). */
/*
* Only time-stamp the packet with hrtime if the upper stream
* is configured to do so.
*/
if (IS_PPA_TIMESTAMP(ppa)) {
}
/*
* Just put this back on the queue and allow the write service
* routine to handle it. We're nested too deeply here to
* rewind the stack sufficiently to prevent overflow. This is
* the slow path anyway.
*/
ppa->ppa_oqdropped++;
} else {
qenable(q);
}
return (0);
}
/*
* sppp_dlpromisconreq()
*
* MT-Perimeters:
* shared inner, shared outer.
*
* Description:
* Perform DL_PROMISCON_REQ request, called by sppp_mproto.
*/
static int
{
/* snoop issues DL_PROMISCON_REQ more than once. */
if (IS_SPS_PROMISC(sps)) {
(level != DL_PROMISC_MULTI)) {
} else {
}
return (0);
}
/*
* sppp_dl_promiscon()
*
* MT-Perimeters:
* exclusive inner, shared outer.
*
* Description:
* Called by qwriter (INNER) from sppp_dlpromisconreq as the result of
* receiving a DL_PROMISCON_REQ message.
*/
static void
{
/*
* We can't be sure that the sps_ppa field is valid, since the DLPI
* spec says that DL_PROMISCON_REQ can be issued at any state, i.e.,
* the request can be issued even before DL_ATTACH_REQ or PPPIO_ATTACH
* be issued to associate this stream with a ppa.
*/
ppa->ppa_promicnt++;
}
}
/*
* sppp_dlpromiscoffreq()
*
* MT-Perimeters:
* shared inner, shared outer.
*
* Description:
* Perform DL_PROMISCOFF_REQ request, called by sppp_mproto.
*/
static int
{
if (!IS_SPS_PROMISC(sps)) {
(level != DL_PROMISC_MULTI)) {
} else {
}
return (0);
}
/*
* sppp_dl_promiscoff()
*
* MT-Perimeters:
* exclusive inner, shared outer.
*
* Description:
* Called by qwriter (INNER) from sppp_dlpromiscoffreq as the result of
* receiving a DL_PROMISCOFF_REQ message.
*/
static void
{
/*
* We can't be guaranteed that the sps_ppa field is still valid, since
* the control stream might have been closed earlier, in which case
* the close procedure would have NULL'd out the sps_ppa.
*/
ppa->ppa_promicnt--;
}
}
/*
* sppp_dlphyreq()
*
* MT-Perimeters:
* shared inner, shared outer.
*
* Description:
* Perform DL_PHYS_ADDR_REQ request, called by sppp_mproto. This doesn't
* return anything useful, but it keeps ifconfig happy.
*/
/* ARGSUSED */
static int
{
return (0);
}
/*
* sppp_dladdether()
*
* Description:
* Prepend an empty Ethernet header to msg for snoop, et al. Free
* the original mblk if alloc fails. Only called for the purpose of sending
* packets up the promiscous stream.
*/
/* ARGSUSED */
static mblk_t *
{
return (NULL);
}
type = ETHERTYPE_IP;
} else {
/*
* For all other protocols, end this up as an ETHERTYPE_PPP
* type of packet. Since we've skipped the PPP headers in the
* caller, make sure that we restore it. We know for sure that
* the PPP header still exists in the message (only skipped),
* since the sender of this message is pppd and it must have
* included the PPP header in front.
*/
}
return (eh);
}
/*
* sppp_dladdud()
*
* Description:
* Prepend DL_UNITDATA_IND mblk to msg, free original alloc fails.
*/
/* ARGSUSED */
mblk_t *
{
return (NULL);
}
dlu->dl_group_address = 0;
if (promisc) {
type = ETHERTYPE_IP;
} else {
/*
* For all other protocols, send this up as an
* ETHERTYPE_PPP type of packet. Since we've skipped
* the PPP headers in the caller, make sure that we
* restore it. We know for sure that the PPP header
* still exists in the message (only skipped), since
* the sender of this message is pppd and it must
* have included the PPP header in front.
*/
}
} else {
}
/*
* Send the DLPI client the data with the SAP they requested,
* (e.g. ETHERTYPE_IP) rather than the PPP protocol (e.g. PPP_IP).
*/
return (dh);
}
/*
* sppp_dlprsendup()
*
* Description:
* For any valid promiscuous streams (marked with SPS_PROMISC and its
* sps_dlstate is DL_IDLE), send data upstream. The caller is expected
* to hold ppa_sib_lock when calling this procedure.
*/
void
{
/* NOTE: caller must hold ppa_sib_lock in RW_READER mode */
/*
* We specifically test to ensure that the DLPI state for the
* promiscous stream is IDLE (DL_IDLE), since such state tells
* us that the promiscous stream has been bound to PPP_ALLSAP.
*/
ppa->ppa_allocbfail++;
continue;
}
if (header) {
}
if (IS_SPS_RAWDATA(sps)) {
/* function frees original message if fails */
} else {
/* function frees original message if fails */
}
} else {
ppa->ppa_allocbfail++;
}
}
}
}