/*
* sppp.c - Solaris STREAMS PPP multiplexing pseudo-driver
*
* 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/sysmacros.h>
#include <sys/ethernet.h>
#include <net/ppp_defs.h>
#include "sppp.h"
#include "s_common.h"
/*
* This is used to tag official Solaris sources. Please do not define
* "INTERNAL_BUILD" when building this software outside of Sun Microsystems.
*/
#ifdef INTERNAL_BUILD
/* MODINFO is limited to 32 characters. */
#else /* INTERNAL_BUILD */
/* LINTED */
#ifdef DEBUG
" DEBUG"
#endif
"\n";
#endif /* INTERNAL_BUILD */
static int sppp_kstat_update(kstat_t *, int);
/*
* sps_list contains the list of active per-stream instance state structures
* ordered on the minor device number (see sppp.h for details). All streams
* opened to this driver are threaded together in this list.
*/
/*
* ppa_list contains the list of active per-attachment instance state
* structures ordered on the ppa id number (see sppp.h for details). All of
* the ppa structures created once per PPPIO_NEWPPA ioctl are threaded together
* in this list. There is exactly one ppa structure for a given PPP interface,
* and multiple sps streams (upper streams) may share a ppa by performing
* an attachment explicitly (PPPIO_ATTACH) or implicitly (DL_ATTACH_REQ).
*/
/*
* map proto (which is an IANA defined ppp network protocol) to
* a bit position indicated by NP_* in ppa_npflag
*/
static uint32_t
{
switch (proto) {
case PPP_IP:
return (NP_IP);
case PPP_IPV6:
return (NP_IPV6);
default:
return (0);
}
}
/*
* sppp_open()
*
* MT-Perimeters:
* exclusive inner, exclusive outer.
*
* Description:
* Common open procedure for module.
*/
/* ARGSUSED */
int
{
return (0); /* already open */
}
return (OPENFAIL);
}
/*
* The sps list is sorted using the minor number as the key. The
* following code walks the list to find the lowest valued minor
* number available to be used.
*/
mn = 0;
break;
}
++mn;
}
/*
* We explicitly disable the automatic queue scheduling for the
* write-side to obtain complete control over queuing during transmit.
* Packets will be queued at the upper write queue and the service
* routine will not be called until it gets scheduled by having the
* lower write service routine call the qenable(WR(uq)) for all streams
* attached to the same ppa instance.
*/
qprocson(q);
return (0);
}
/*
* Free storage used by a PPA. This is not called until the last PPA
* user closes his connection or reattaches to a different PPA.
*/
static void
{
}
break;
}
}
}
/*
* Create a new PPA. Caller must be exclusive on outer perimeter.
*/
sppa_t *
{
const char **cpp;
/*
* NOTE: unit *must* be named for the driver
* name plus the ppa number so that netstat
* can find the statistics.
*/
/*
* Make sure we can allocate a buffer to
* contain the ppa to be sent upstream, as
* well as the actual ppa structure and its
* associated kstat structure.
*/
sizeof (sppp_kstats_t) / sizeof (kstat_named_t), 0);
}
}
return (NULL);
}
/*
* Prepare and install kstat counters. Note that for netstat
* -i to work, there needs to be "ipackets", "opackets",
* "ierrors", and "oerrors" kstat named variables.
*/
cpp++) {
knt++;
}
cpp++) {
knt++;
}
/* link to the next ppa and insert into global list */
break;
}
return (ppa);
}
/*
* sppp_close()
*
* MT-Perimeters:
* exclusive inner, exclusive outer.
*
* Description:
* Common close procedure for module.
*/
int
{
qprocsoff(q);
goto close_unattached;
}
if (IS_SPS_CONTROL(sps)) {
/*
* STREAMS framework always issues I_UNLINK prior to close,
* since we only allow I_LINK under the control stream.
* A given ppa structure has at most one lower stream pointed
* by the ppa_lower_wq field, because we only allow a single
* linkage (I_LINK) to be done on the control stream.
*/
/*
* Walk through all of sibling streams attached to this ppa,
* and remove all references to this ppa. We have exclusive
* access for the entire driver here, so there's no need
* to hold ppa_sib_lock.
*/
cnt++;
/*
* There should be a preallocated hangup
* message here. Fetch it and send it up to
* the stream head. This will cause IP to
* mark the interface as "down."
*/
/*
* M_HANGUP works with IP, but snoop
* is lame and requires M_ERROR. Send
* up a clean error code instead.
*
* XXX if snoop is fixed, fix this, too.
*/
}
cnt++;
}
} else {
}
/* Tell the daemon the bad news. */
} else {
}
/*
* Walk through all of sibling streams attached to the
* same ppa, and remove this stream from the sibling
* streams list. We have exclusive access for the
* entire driver here, so there's no need to hold
* ppa_sib_lock.
*/
} else {
break;
}
}
}
/*
* Check if this is a promiscous stream. If the SPS_PROMISC bit
* is still set, it means that the stream is closed without
* ever having issued DL_DETACH_REQ or DL_PROMISCOFF_REQ.
* In this case, we simply decrement the promiscous counter,
* and it's safe to do it without holding ppa_sib_lock since
* we're exclusive (inner and outer) at this point.
*/
if (IS_SPS_PROMISC(sps)) {
ppa->ppa_promicnt--;
}
}
/* If we're the only one left, then delete now. */
else
ppa->ppa_refcnt--;
break;
}
}
return (0);
}
static void
{
int sap;
int count = 0;
case PPPIO_NPMODE:
if (!IS_SPS_CONTROL(sps)) {
break; /* return EINVAL */
break;
}
/*
* Walk the sibling streams which belong to the same
* ppa, and try to find a stream with matching sap
* number.
*/
break; /* found it */
}
}
break; /* return EINVAL */
} else {
}
}
error = 0; /* return success */
break;
case PPPIO_GIDLE:
break;
} else if (!IS_PPA_TIMESTAMP(ppa)) {
break; /* return EINVAL */
}
ppa->ppa_allocbfail++;
break;
}
}
/*
* Get current timestamp and subtract the tx and rx
* timestamps to get the actual idle time to be
* returned.
*/
error = 0;
break; /* return success (error is 0) */
case PPPIO_GTYPE:
break;
}
}
/*
* Let the requestor know that we are the PPP
* multiplexer (PPPTYP_MUX).
*/
error = 0; /* return success */
break;
case PPPIO_GETSTAT64:
break; /* return EINVAL */
!IS_PPA_LASTMOD(ppa)) {
/*
* We match sps_ioc_id on the M_IOC{ACK,NAK},
* so if the response hasn't come back yet,
* new ioctls must be queued instead.
*/
if (IS_SPS_IOCQ(sps)) {
break;
}
return;
} else {
ppa->ppa_ioctlsfwd++;
/*
* Record the ioctl CMD & ID - this will be
* used to check the ACK or NAK responses
* coming from below.
*/
}
return; /* don't ack or nak the request */
}
ppa->ppa_allocbfail++;
break;
}
}
/*
* Copy the contents of ppp_stats64 structure for this
* ppa and return them to the caller.
*/
error = 0; /* return success */
break;
case PPPIO_GETCSTAT:
break; /* return EINVAL */
!IS_PPA_LASTMOD(ppa)) {
/*
* See comments in PPPIO_GETSTAT64 case
* in sppp_ioctl().
*/
if (IS_SPS_IOCQ(sps)) {
break;
}
return;
} else {
ppa->ppa_ioctlsfwd++;
/*
* Record the ioctl CMD & ID - this will be
* used to check the ACK or NAK responses
* coming from below.
*/
}
return; /* don't ack or nak the request */
}
ppa->ppa_allocbfail++;
break;
}
}
error = 0; /* return success */
break;
}
if (error == 0) {
/* Success; tell the user. */
} else {
/* Failure; send error back upstream. */
}
}
/*
* sppp_uwput()
*
* MT-Perimeters:
* shared inner, shared outer.
*
* Description:
* Upper write-side put procedure. Messages from above arrive here.
*/
void
{
int error;
case M_PCPROTO:
case M_PROTO:
if (IS_SPS_CONTROL(sps)) {
/*
* Intentionally change this to a high priority
* message so it doesn't get queued up. M_PROTO is
* specifically used for signalling between pppd and its
* kernel-level component(s), such as ppptun, so we
* make sure that it doesn't get queued up behind
* data messages.
*/
ppa->ppa_mctlsfwd++;
} else {
ppa->ppa_mctlsfwderr++;
}
} else {
return;
}
break;
case M_DATA:
break;
case M_IOCTL:
case DLIOCRAW:
case DL_IOC_HDR_INFO:
case PPPIO_ATTACH:
case PPPIO_DEBUG:
case PPPIO_DETACH:
case PPPIO_LASTMOD:
case PPPIO_MRU:
case PPPIO_MTU:
case PPPIO_USETIMESTAMP:
case PPPIO_BLOCKNP:
case PPPIO_UNBLOCKNP:
return;
case I_LINK:
case I_UNLINK:
case PPPIO_NEWPPA:
return;
case PPPIO_NPMODE:
case PPPIO_GIDLE:
case PPPIO_GTYPE:
case PPPIO_GETSTAT64:
case PPPIO_GETCSTAT:
/*
* These require additional auto variables to
* handle, so (for optimization reasons)
* they're moved off to a separate function.
*/
sppp_ioctl(q, mp);
return;
case PPPIO_GETSTAT:
break; /* 32 bit interface gone */
default:
break;
break; /* return EINVAL */
}
/*
* See comments in PPPIO_GETSTAT64 case
* in sppp_ioctl().
*/
if (IS_SPS_IOCQ(sps)) {
break;
}
return;
} else {
ppa->ppa_ioctlsfwd++;
/*
* Record the ioctl CMD & ID -
* this will be used to check the
* ACK or NAK responses coming from below.
*/
}
return; /* don't ack or nak the request */
}
/* Failure; send error back upstream. */
break;
case M_FLUSH:
}
} else {
}
break;
default:
break;
}
}
/*
* sppp_uwsrv()
*
* MT-Perimeters:
* exclusive inner, shared outer.
*
* Description:
* Upper write-side service procedure. Note that this procedure does
* not get called when a message is placed on our write-side queue, since
* automatic queue scheduling has been turned off by noenable() when
* the queue was opened. We do this on purpose, as we explicitly control
* the write-side queue. Therefore, this procedure gets called when
* the lower write service procedure qenable() the upper write stream queue.
*/
void
{
continue;
}
/*
* See comments in PPPIO_GETSTAT64 case
* in sppp_ioctl().
*/
if (IS_SPS_IOCQ(sps)) {
break;
} else {
ppa->ppa_ioctlsfwd++;
}
} else if ((nextq =
break;
}
} else {
}
}
}
void
{
} else {
} else {
break;
}
}
}
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_DETACH_REQ is issued prior to marking the
* stream as non-promiscuous, through
* DL_PROMISCOFF_REQ request.
*/
if (IS_SPS_PROMISC(sps)) {
ppa->ppa_promicnt--;
}
}
}
sppa_t *
{
break; /* found the ppa */
}
}
return (ppa);
}
/*
* sppp_inner_ioctl()
*
* MT-Perimeters:
* exclusive inner, shared outer
*
* Description:
* Called by sppp_uwput as a result of receiving ioctls which require
* an exclusive access at the inner perimeter.
*/
static void
{
int count = 0;
int dbgcmd;
case DLIOCRAW:
if (IS_SPS_CONTROL(sps)) {
break; /* return EINVAL */
}
error = 0; /* return success */
break;
case DL_IOC_HDR_INFO:
if (IS_SPS_CONTROL(sps)) {
break; /* return EINVAL */
SPPP_ADDRL))) {
break;
break;
}
ppa->ppa_allocbfail++;
break;
}
error = 0; /* return success */
break;
case PPPIO_ATTACH:
break; /* return EINVAL */
break;
}
/* If there's something here, it's detached. */
}
/*
* If we can't find it, then it's either because the requestor
* has supplied a wrong ppa_id to be attached to, or because
* the control stream for the specified ppa_id has been closed
* before we get here.
*/
break;
}
break;
}
/*
* Preallocate the hangup message so that we're always
* able to send this upstream in the event of a
* catastrophic failure.
*/
break;
}
/*
* There are two ways to attach a stream to a ppa: one is
* through DLPI (DL_ATTACH_REQ) and the other is through
* PPPIO_ATTACH. This is why we need to distinguish whether or
* not a stream was allocated via PPPIO_ATTACH, so that we can
* properly detach it when we receive PPPIO_DETACH ioctl
* request.
*/
/*
* Add this stream to the head of the list of sibling streams
* which belong to the same ppa as specified.
*/
ppa->ppa_refcnt++;
error = 0; /* return success */
break;
case PPPIO_BLOCKNP:
case PPPIO_UNBLOCKNP:
break;
}
if (error != 0)
break;
/*
* Mark proto as blocked in ppa_npflag until the
* corresponding queues for proto have been plumbed.
*/
if (npflagpos != 0) {
} else {
}
} else {
/*
* reset ppa_npflag and release proto
* packets that were being held in control queue.
*/
}
break;
case PPPIO_DEBUG:
break;
break; /* return EINVAL */
break;
}
/*
* We accept PPPDBG_LOG + PPPDBG_DRIVER value as an indication
* that SPS_KDEBUG needs to be enabled for this upper stream.
*/
error = 0; /* return success */
break;
}
/*
* Otherwise, for any other values, we send them down only if
* there is an attachment and if the attachment has something
* linked underneath it.
*/
break;
}
/*
* See comments in PPPIO_GETSTAT64 case
* in sppp_ioctl().
*/
if (IS_SPS_IOCQ(sps)) {
break;
}
return;
} else {
ppa->ppa_ioctlsfwd++;
/*
* Record the ioctl CMD & ID -
* this will be used to check the
* ACK or NAK responses coming from below.
*/
}
return; /* don't ack or nak the request */
case PPPIO_DETACH:
if (!IS_SPS_PIOATTACH(sps)) {
break; /* return EINVAL */
}
/*
* The SPS_PIOATTACH flag set on the stream tells us that
* the ppa field is still valid. In the event that the control
* stream be closed prior to this stream's detachment, the
* SPS_PIOATTACH flag would have been cleared from this stream
* during close; in that case we won't get here.
*/
/*
* We don't actually detach anything until the stream is
* closed or reattached.
*/
error = 0; /* return success */
break;
case PPPIO_LASTMOD:
if (!IS_SPS_CONTROL(sps)) {
break; /* return EINVAL */
}
error = 0; /* return success */
break;
case PPPIO_MRU:
if (!IS_SPS_CONTROL(sps) ||
break; /* return EINVAL */
break;
}
break;
}
}
/*
* If there's something beneath this driver for the ppa, then
* inform it (or them) of the MRU size. Only do this is we
* are not the last PPP module on the stream.
*/
mru);
}
error = 0; /* return success */
break;
case PPPIO_MTU:
if (!IS_SPS_CONTROL(sps) ||
break; /* return EINVAL */
break;
}
break;
}
/*
* If there's something beneath this driver for the ppa, then
* inform it (or them) of the MTU size. Only do this if we
* are not the last PPP module on the stream.
*/
mtu);
}
error = 0; /* return success */
break;
case PPPIO_USETIMESTAMP:
if (!IS_SPS_CONTROL(sps)) {
break; /* return EINVAL */
}
if (!IS_PPA_TIMESTAMP(ppa)) {
}
error = 0;
break;
}
if (error == 0) {
/* Success; tell the user */
} else {
/* Failure; send error back upstream */
}
}
/*
* sppp_outer_ioctl()
*
* MT-Perimeters:
* exclusive inner, exclusive outer
*
* Description:
* Called by sppp_uwput as a result of receiving ioctls which require
* an exclusive access at the outer perimeter.
*/
static void
{
int count = 0;
case I_LINK:
if (!IS_SPS_CONTROL(sps)) {
break; /* return EINVAL */
break;
}
/*
* Unblock upper network streams which now feed this lower
* stream. We don't need to hold ppa_sib_lock here, since we
* are writer at the outer perimeter.
*/
}
}
/*
* Also unblock (run once) our lower read-side queue. This is
* where packets received while doing the I_LINK may be
* languishing; see sppp_lrsrv.
*/
/*
* Send useful information down to the modules which are now
* linked below this driver (for this particular ppa). Only
* do this if we are not the last PPP module on the stream.
*/
if (!IS_PPA_LASTMOD(ppa)) {
ppa->ppa_ppa_id);
}
if (IS_SPS_KDEBUG(sps)) {
"/%d: I_LINK lwq=0x%p sps=0x%p flags=0x%b ppa=0x%p "
}
error = 0; /* return success */
break;
case I_UNLINK:
if (IS_SPS_KDEBUG(sps)) {
"/%d: I_UNLINK lwq=0x%p sps=0x%p flags=0x%b "
}
/*
* While accessing the outer perimeter exclusively, we
* disassociate our ppa's lower_wq from the lower stream linked
* beneath us, and we also disassociate our control stream from
* the q_ptr of the lower stream.
*/
/*
* Unblock streams which now feed back up the control stream,
* and acknowledge the request. We don't need to hold
* ppa_sib_lock here, since we are writer at the outer
* perimeter.
*/
}
}
error = 0; /* return success */
break;
case PPPIO_NEWPPA:
/*
* Do sanity check to ensure that we don't accept PPPIO_NEWPPA
* on a stream which DLPI is used (since certain DLPI messages
* will cause state transition reflected in sps_dlstate,
* changing it from its default DL_UNATTACHED value). In other
* a control stream.
*/
break;
break; /* return EINVAL */
}
/* Get requested unit number (if any) */
else
ppa_id = 0;
/* Get mblk to use for response message */
break;
}
}
/*
* Walk the global ppa list and determine the lowest
* available ppa_id number to be used.
*/
ppa_id = 0;
break;
} else {
break;
++ppa_id;
}
}
break;
}
/* Clear timestamp and lastmod flags */
} else {
break;
}
}
/*
* Return the newly created ppa_id to the requestor and
* acnowledge the request.
*/
if (IS_SPS_KDEBUG(sps)) {
"/%d: PPPIO_NEWPPA ppa_id=%d sps=0x%p flags=0x%b "
}
error = 0;
break;
}
if (error == 0) {
/* Success; tell the user. */
} else {
/* Failure; send error back upstream. */
}
}
/*
* sppp_send()
*
* MT-Perimeters:
* shared inner, shared outer.
*
* Description:
* Called by sppp_uwput to handle M_DATA message type. Returns
* queue_t for putnext, or NULL to mean that the packet was
* handled internally.
*/
static queue_t *
{
int is_promisc;
int msize;
int error = 0;
/*
* We only let M_DATA through if the sender is either the control
* stream (for PPP control packets) or one of the network streams
* (for IP packets) in IP fastpath mode. If this stream is not attached
* to any ppas, then discard data coming down through this stream.
*/
}
if (error != 0) {
return (NULL);
}
/* Log, and send it anyway */
ppa->ppa_otoolongs++;
} else if (msize < PPP_HDRLEN) {
/*
* Log, and send it anyway. We log it because we get things
* in M_DATA form here, which tells us that the sender is
* either IP in fastpath transmission mode, or pppd. In both
* cases, they are currently expected to send the 4-bytes
* PPP header in front of any possible payloads.
*/
ppa->ppa_orunts++;
}
if (IS_SPS_KDEBUG(sps)) {
"/%d: M_DATA send (%d bytes) sps=0x%p flags=0x%b "
}
/*
* Should there be any promiscuous stream(s), send the data up
* for each promiscuous stream that we recognize. Make sure that
* for fastpath, we skip the PPP header in the M_DATA mblk. We skip
* the control stream as we obviously never allow the control stream
* to become promiscous and bind to PPP_ALLSAP.
*/
if (is_promisc) {
}
/*
* Only time-stamp the packet with hrtime if the upper stream
* is configured to do so. PPP control (negotiation) messages
* are never considered link activity; only data is activity.
*/
}
/*
* If there's already a message in the write-side service queue,
* then queue this message there as well, otherwise, try to send
* it down to the module immediately below us.
*/
ppa->ppa_oqdropped++;
}
return (NULL);
}
return (nextq);
}
/*
* sppp_outpkt()
*
* MT-Perimeters:
* shared inner, shared outer (if called from sppp_wput, sppp_dlunitdatareq).
* exclusive inner, shared outer (if called from sppp_wsrv).
*
* Description:
* Called from 1) sppp_uwput when processing a M_DATA fastpath message,
* or 2) sppp_uwsrv when processing the upper write-side service queue.
* For both cases, it prepares to send the data to the module below
* this driver if there is a lower stream linked underneath. If none, then
* the data will be sent upstream via the control channel to pppd.
*
* Returns:
* Non-NULL queue_t if message should be sent now, otherwise
* if *mpp == NULL, then message was freed, otherwise put *mpp
* called both from srv and put procedures.)
*/
static queue_t *
{
if (npmode == NPMODE_QUEUE) {
return (NULL); /* queue it for later */
/*
* This can not be the control stream, as it must always have
* a valid ppa, and its npmode must always be NPMODE_PASS.
*/
if (npmode == NPMODE_DROP) {
} else {
/*
* If we no longer have the control stream, or if the
* mode is set to NPMODE_ERROR, then we need to tell IP
* that the interface need to be marked as down. In
* other words, we tell IP to be quiescent.
*/
}
return (NULL); /* don't queue it */
}
/*
* Do we have a driver stream linked underneath ? If not, we need to
* notify pppd that the link needs to be brought up and configure
* this upper stream to drop subsequent outgoing packets. This is
* for demand-dialing, in which case pppd has done the IP plumbing
* but hasn't linked the driver stream underneath us. Therefore, when
* a packet is sent down the IP interface, a notification message
* will be sent up the control stream to pppd in order for it to
* establish the physical link. The driver stream is then expected
* to be linked underneath after physical link establishment is done.
*/
ppa->ppa_allocbfail++;
return (NULL); /* don't queue it */
}
/* Include the data in the message for logging. */
ppa->ppa_lsneedup++;
/*
* We need to set the mode to NPMODE_DROP, but should only
* do so when this stream is not the control stream.
*/
if (!IS_SPS_CONTROL(sps)) {
}
return (NULL); /* don't queue it */
}
/*
* If so, then try to send it down. The lower queue is only ever
* detached while holding an exclusive lock on the whole driver,
* so we can be confident that the lower queue is still there.
*/
if (IS_SPS_CONTROL(sps)) {
ppa->ppa_opkt_ctl++;
}
}
return (NULL); /* queue it for later */
}
/*
* sppp_lwsrv()
*
* MT-Perimeters:
* exclusive inner, shared outer.
*
* Description:
* Lower write-side service procedure. No messages are ever placed on
* the write queue here, this just back-enables all upper write side
* service procedures.
*/
void
{
}
}
}
/*
* sppp_lrput()
*
* MT-Perimeters:
* shared inner, shared outer.
*
* Description:
* Lower read-side put procedure. Messages from below get here.
* Data messages are handled separately to limit stack usage
* going into IP.
*
* Note that during I_UNLINK processing, it's possible for a downstream
* message to enable upstream data (due to pass_wput() removing the
* SQ_BLOCKED flag), and thus we must protect against a NULL sppa pointer.
* In this case, the only thing above us is passthru, and we might as well
* discard.
*/
void
{
return;
}
}
}
/*
* sppp_lrsrv()
*
* MT-Perimeters:
* exclusive inner, shared outer.
*
* Description:
* Lower read-side service procedure. This is run once after the I_LINK
* occurs in order to clean up any packets that came in while we were
* transferring in the lower stream. Otherwise, it's not used.
*/
void
{
sppp_lrput(q, mp);
}
/*
* sppp_recv_nondata()
*
* MT-Perimeters:
* shared inner, shared outer.
*
* Description:
* All received non-data messages come through here.
*/
static void
{
case M_CTL:
ppa->ppa_ierr_low++;
ppa->ppa_mctlsknown++;
ppa->ppa_oerr_low++;
ppa->ppa_mctlsknown++;
} else {
ppa->ppa_mctlsunknown++;
}
break;
case M_IOCTL:
break;
case M_IOCACK:
case M_IOCNAK:
/*
* Attempt to match up the response with the stream that the
* request came from. If ioc_id doesn't match the one that we
* recorded, then discard this message.
*/
break; /* found the upper stream */
}
}
}
ppa->ppa_ioctlsfwderr++;
break;
}
ppa->ppa_ioctlsfwdok++;
/*
* Clear SPS_IOCQ and enable the lower write side queue,
* this would allow the upper stream service routine
* to start processing the queue for pending messages.
* sppp_lwsrv -> sppp_uwsrv.
*/
break;
case M_HANGUP:
/*
* Free the original mblk_t. We don't really want to send
* a M_HANGUP message upstream, so we need to translate this
* message into something else.
*/
break;
ppa->ppa_allocbfail++;
break;
}
ppa->ppa_lsdown++;
break;
case M_FLUSH:
}
} else {
}
break;
default:
} else {
ppa->ppa_iqdropped++;
}
break;
}
}
/*
* sppp_recv()
*
* MT-Perimeters:
* shared inner, shared outer.
*
* Description:
* Receive function called by sppp_lrput. Finds appropriate
* receive stream and does accounting.
*/
static queue_t *
{
int len;
/*
* If the entire data size of the mblk is less than the length of the
* PPP header, then free it. We can't do much with such message anyway,
* since we can't really determine what the PPP protocol type is.
*/
if (len < PPP_HDRLEN) {
/* Log, and free it */
ppa->ppa_irunts++;
return (NULL);
/* Log, and accept it anyway */
ppa->ppa_itoolongs++;
}
/*
* We need at least be able to read the PPP protocol from the header,
* so if the first message block is too small, then we concatenate the
* rest of the following blocks into one message.
*/
ppa->ppa_allocbfail++;
return (NULL);
}
}
/*
* Hold this packet in the control-queue until
* the matching network-layer upper stream for the PPP protocol (sap)
* has not been plumbed and configured
*/
/*
* proto is currently blocked; Hold up to 4 packets
* in the kernel.
*/
else
return (NULL);
}
/*
* Try to find a matching network-layer upper stream for the specified
* PPP protocol (sap), and if none is found, send this frame up the
* control stream.
*/
ppa->ppa_ipkt_ctl++;
if (IS_SPS_KDEBUG(ctlsps)) {
"/%d: M_DATA recv (%d bytes) sps=0x%p "
"flags=0x%b ppa=0x%p flags=0x%b\n",
}
} else {
ppa->ppa_iqdropped++;
return (NULL);
}
}
if (IS_SPS_KDEBUG(destsps)) {
"/%d: M_DATA recv (%d bytes) sps=0x%p flags=0x%b "
}
/*
* If fastpath is enabled on the network-layer stream, then
* make sure we skip over the PPP header, otherwise, we wrap
* the message in a DLPI message.
*/
if (IS_SPS_FASTPATH(destsps)) {
} else {
} else {
ppa->ppa_allocbfail++;
/* mp already freed by sppp_dladdud */
return (NULL);
}
}
} else {
ppa->ppa_iqdropped++;
return (NULL);
}
}
/*
* sppp_inpkt()
*
* MT-Perimeters:
* shared inner, shared outer.
*
* Description:
* Find the destination upper stream for the received packet, called
* from sppp_recv.
*
* Returns:
* ptr to destination upper network stream, or NULL for control stream.
*/
/* ARGSUSED */
static spppstr_t *
{
int is_promisc;
/*
* From RFC 1661 (Section 2):
*
* The Protocol field is one or two octets, and its value identifies
* the datagram encapsulated in the Information field of the packet.
* The field is transmitted and received most significant octet first.
*
* The structure of this field is consistent with the ISO 3309
* extension mechanism for address fields. All Protocols MUST be odd;
* the least significant bit of the least significant octet MUST equal
* "1". Also, all Protocols MUST be assigned such that the least
* significant bit of the most significant octet equals "0". Frames
* received which don't comply with these rules MUST be treated as
* having an unrecognized Protocol.
*
* Protocol field values in the "0***" to "3***" range identify the
* network-layer protocol of specific packets, and values in the
* "8***" to "b***" range identify packets belonging to the associated
* Network Control Protocols (NCPs), if any.
*
* Protocol field values in the "4***" to "7***" range are used for
* protocols with low volume traffic which have no associated NCP.
* Protocol field values in the "c***" to "f***" range identify packets
* as link-layer Control Protocols (such as LCP).
*/
/*
* We check if this is not a network-layer protocol, and if so,
* then send this packet up the control stream.
*/
if (proto > 0x7fff) {
goto inpkt_done; /* send it up the control stream */
}
/*
* Try to grab the destination upper stream from the network-layer
* stream cache for this ppa for PPP_IP (0x0021) or PPP_IPV6 (0x0057)
* protocol types. Otherwise, if the type is not known to the cache,
* or if its sap can't be matched with any of the upper streams, then
* send this packet up the control stream so that it can be rejected.
*/
}
/*
* Toss this one away up the control stream if there's no matching sap;
* this way the protocol can be rejected (destsps is NULL).
*/
/*
* Only time-stamp the packet with hrtime if the upper stream
* is configured to do so. PPP control (negotiation) messages
* are never considered link activity; only data is activity.
*/
}
/*
* Should there be any promiscuous stream(s), send the data up for
* each promiscuous stream that we recognize. We skip the control
* stream as we obviously never allow the control stream to become
* promiscous and bind to PPP_ALLSAP.
*/
if (is_promisc) {
}
return (destsps);
}
/*
* sppp_kstat_update()
*
* Description:
* Update per-ppa kstat interface statistics.
*/
static int
{
if (rw == KSTAT_WRITE) {
return (EACCES);
}
return (0);
}
/*
* Turn off proto in ppa_npflag to indicate that
* the corresponding network protocol has been plumbed.
* Release proto packets that were being held in the control
* queue in anticipation of this event.
*/
static void
{
int count;
queue_t *q;
return;
while (count > 0) {
continue;
}
count--;
}
if (IS_SPS_FASTPATH(destsps)) {
} else {
ppa->ppa_allocbfail++;
/* mp already freed by sppp_dladdud */
continue;
}
}
} else {
ppa->ppa_iqdropped++;
continue;
}
}
}