Files
seta75D 2e8a93c394 Init
2021-10-11 18:20:23 -03:00

1128 lines
28 KiB
C

#ifndef lint
static char sccsid[] = "@(#)nit_if.c 1.1 92/07/30 Copyr 1988 Sun Micro";
#endif lint
/*
* Streams driver interface to Ethernet
*
* This code is experimental, but becoming less so as time goes on.
* Its intent is to supply a hook for experimenting with the streams
* networking interface.
*
* The design is intended to support a packet-filtering streams
* module layered immediately on top of this driver. This setup
* has the consequence that many packets potentially enter the
* read queue that are destined to be thrown away immediately.
* It would be nice to find ways to reduce or remove this inefficiency.
*/
/* #define SNITDEBUG */
#include "snit.h"
#if NSNIT > 0
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/debug.h>
#include <sys/user.h>
#include <sys/mbuf.h> /* incoming packets are in mbufs */
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/ioctl.h>
#include <net/nit.h> /* for nit_ii */
#include <net/if.h>
#include <net/if_arp.h>
#include <net/nit_if.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
struct mbuf *cp_mblks_to_mbufs();
/*
* Stereotyped streams glue.
*/
int snit_open();
int snit_close(); /* really void, not int */
int snit_put(); /* really void, not int */
/*
* Set high and low water marks.
*
* Since we don't supply service procedures, the high and low
* water values given in snit_minfo are never examined. However,
* we do send these values to the stream head in an M_SETOPTS
* message to alter its buffering behavior. We send these messages
* at open time and after getting a new snapshot length to use.
*
* One can think of these values as being composed from three pieces.
* - The number of outstanding messages desired at high/low water.
* - An estimate of the message size. (Using ETHERMTU here is actually
* overstating things.)
* - A correction factor to balance number of packets against packet
* size. This value grows as packet size decreases, but more slowly.
* It's really a seat-of-the-pants kludge.
*/
#define SNIT_HIWAT(msgsize, fudge) (32 * msgsize * fudge)
#define SNIT_LOWAT(msgsize, fudge) ( 8 * msgsize * fudge)
#define SNIT_PRE_PAD 16
struct module_info snit_minfo = {
19, /* module id XXX: define real value */
"snit",
0,
ETHERMTU + sizeof (struct ether_header),
SNIT_HIWAT(ETHERMTU, 1),
SNIT_LOWAT(ETHERMTU, 1)
};
struct qinit snit_rinit = {
NULL, /* putp (only the "hardware" below us) */
NULL, /* srvp (snit_intr performs this role) */
snit_open,
snit_close,
NULL,
&snit_minfo,
NULL
};
struct qinit snit_winit = {
snit_put,
NULL, /* srvp (everything handled in put proc) */
NULL,
NULL,
NULL,
&snit_minfo,
NULL
};
struct streamtab snit_info = {
&snit_rinit,
&snit_winit,
NULL,
NULL
};
/*
* Per-device instance state information.
*
* Each instance is dynamically allocated from the dblk pool.
* ni_mb cross-references back to the mblk whose associated dblk
* contains the instance. Open instances are linked together
* through the ni_next field into a list ordered by the ni_unit
* field.
*/
typedef struct ni_softc {
struct ni_softc *ni_next; /* link to next open instance */
mblk_t *ni_mb; /* cross-link to containing mblk */
queue_t *ni_rq; /* cross-link to read queue */
struct ifnet *ni_ifp; /* interface to which we're bound */
u_long ni_snap; /* snapshot length */
u_long ni_flags; /* as defined in <net/nit_if.h> */
u_int ni_drops; /* packets dropped due to !canput */
int ni_unit; /* minor dev # of this instance */
} ni_softc;
ni_softc *nisoftc; /* head of list of open instances */
/*
* XXX: verify permissions for writing.
* (Or are the vnode's permissions good enough?)
*/
/* ARGSUSED */
int
snit_open(q, dev, oflag, sflag)
struct queue *q;
dev_t dev;
int oflag,
sflag;
{
register int unit;
register ni_softc *nip;
register ni_softc **inspp;
register mblk_t *mp;
#ifdef SNITDEBUG
printf("snit_open: q: %x, WR(q): %x, dev: %x, oflag: %x, sflag: %x\n",
q, WR(q), dev, oflag, sflag);
#endif SNITDEBUG
/*
* This is an exclusive open device, so a valid open request
* will require inserting a new device instance into the list
* rooted at nisoftc. This list is kept sorted on the ni_unit
* field. The code below finds an unused minor device number
* or verifies that the one we're given us free, depending on
* whether or not the open is a clone open. While doing so,
* it records the list insertion point and checks for various
* error conditions.
*/
if (sflag == CLONEOPEN) {
/*
* Clone open: find an unused minor device instance.
*/
unit = 0;
for (inspp = &nisoftc; nip = *inspp; inspp = &nip->ni_next) {
if (unit < nip->ni_unit)
break;
unit++;
}
if (unit == minor(0xffff) + 1) {
/*
* All minor devices in use.
*/
u.u_error = ENXIO; /* gag */
return (OPENFAIL);
}
}
else if (sflag) {
/*
* Opening as a module is illegal.
*/
return (OPENFAIL);
}
else {
/*
* Regular open: verify that the indicated minor
* device instance is free.
*/
unit = minor(dev);
for (inspp = &nisoftc; nip = *inspp; inspp = &nip->ni_next) {
if (unit < nip->ni_unit)
break;
if (unit > nip->ni_unit)
continue;
u.u_error = EBUSY; /* gag */
return (OPENFAIL);
}
}
/*
* ASSERT: unit is in range and is currently unused, and
* inspp points to the proper list insertion point for
* unit's softc structure.
*/
/*
* Allocate space for per-open-instance information
* and set up internal cross-links.
*
* XXX: If the allocation fails, I suppose we could
* try a bufcall...
*/
mp = allocb((int) sizeof *nip, BPRI_MED);
if (! mp) {
u.u_error = ENOMEM; /* gag */
return (OPENFAIL);
}
mp->b_wptr += sizeof *nip;
nip = (ni_softc *)mp->b_rptr;
nip->ni_mb = mp;
/*
* Link it into the list of open instances.
*/
nip->ni_next = *inspp;
*inspp = nip;
/*
* Set up cross-links between device and queues.
* The streams framework uses (at least to the extent
* of demanding non-garbage) the links from queues
* back to device.
*/
nip->ni_rq = q;
q->q_ptr = (char *)nip;
WR(q)->q_ptr = (char *)nip;
nip->ni_drops = 0;
nip->ni_snap = 0;
nip->ni_unit = unit;
nip->ni_flags = 0;
nip->ni_ifp = NULL;
/*
* Send our notion of reasonable high and low water
* marks upstream to whichever module is willing to
* look at it. If the allocation fails, we give up
* on the idea out of laziness.
*
* XXX: Timing issue -- really, we would like this
* message to reach the next module upstream
* that supplies a service procedure. However,
* we have no guarantee that this module has
* been pushed in place yet. What to do?
* (If we are to believe AT&T's documentation,
* the issue is moot, since the documentation
* claims that this message "alters some
* characteristics of the stream head".)
*/
if (mp = allocb(sizeof (struct stroptions), BPRI_LO)) {
register struct stroptions *sop;
mp->b_datap->db_type = M_SETOPTS;
sop = (struct stroptions *)mp->b_rptr;
sop->so_flags = SO_HIWAT | SO_LOWAT;
sop->so_hiwat = SNIT_HIWAT(ETHERMTU, 1);
sop->so_lowat = SNIT_LOWAT(ETHERMTU, 1);
mp->b_wptr += sizeof (struct stroptions);
putnext(q, mp);
}
#ifdef SNITDEBUG
snitdump(nisoftc);
#endif SNITDEBUG
return (unit);
}
snit_close(q)
register queue_t *q;
{
register ni_softc *nip,
*np;
ni_softc **delpp;
register struct ifnet *ifp;
#ifdef SNITDEBUG
printf("snit_close: q: %x, WR(q): %x ", q, WR(q));
#endif SNITDEBUG
/*
* Flush outstanding traffic on its way
* downstream to us.
*/
flushq(WR(q), FLUSHALL);
nip = (ni_softc *)q->q_ptr;
ifp = nip->ni_ifp;
#ifdef SNITDEBUG
printf("nip: %x, nip->ni_rq: %x\n", nip, nip->ni_rq);
printf(" %d packets dropped to satisfy flow control\n",
nip->ni_drops);
#endif SNITDEBUG
/*
* Undo our request for promiscuous mode if necessary.
*/
if ((nip->ni_flags & NI_PROMISC) && ifp)
(void) ifpromisc(ifp, 0);
/*
* Unhook the softc structure from the things that point
* to it and then free it.
*/
for (delpp = &nisoftc; np = *delpp; delpp = &np->ni_next)
if (np == nip)
break;
if (!np)
panic("snit_close nonexistent instance");
*delpp = nip->ni_next;
q->q_ptr = NULL;
freeb(nip->ni_mb);
#ifdef SNITDEBUG
snitdump(nisoftc);
#endif SNITDEBUG
}
/*
* Write-side put procedure. It handles the usual streams stuff and:
* - the NIOCBIND ioctl to bind us to a given interface, and
* - M_PROTO messages, whose bodies are handed to the interface
* for transmission, after stripping off the first mblk to
* obtain the destination address.
*/
snit_put(q, mp)
queue_t *q;
mblk_t *mp;
{
switch (mp->b_datap->db_type) {
case M_FLUSH:
#ifdef SNITDEBUG
printf("snit_put: M_FLUSH q: %x, mp: %x\n", q, mp);
#endif SNITDEBUG
if (*mp->b_rptr & FLUSHW) {
/*
* Question: the lp example passed FLUSHDATA
* as arg to flushq; the emd driver passes
* FLUSHALL. What are the implications of
* this difference (which amounts to preserving/
* not preserving control messages)?
*/
flushq(q, FLUSHALL);
/*
* If we actually had a write in progress,
* we would want to flush local copies of
* pending outgoing messages as well.
*/
*mp->b_rptr &= ~FLUSHW;
}
if (*mp->b_rptr & FLUSHR) {
/*
* N.B.: FLUSHW has been turned off by the
* time we get here. (This is necessary to
* prevent a FLUSHW from circling back through
* the stream head to hit us a second time.)
*/
qreply(q, mp);
}
else
freemsg(mp);
break;
case M_IOCTL:
snit_ioctl(q, mp);
break;
case M_PROTO: {
/*
* Transmit a packet. Obtain the destination address
* from the M_PROTO block in front, and the body from
* the M_DATA blocks that follow. If not in this format,
* return M_ERROR (EINVAL).
*/
struct sockaddr *saddr = (struct sockaddr *)mp->b_rptr;
mblk_t *mnp = mp->b_cont;
struct mbuf *m;
struct ifnet *ifp;
ifp = ((ni_softc *)q->q_ptr)->ni_ifp;
if (!mnp || mnp->b_datap->db_type != M_DATA ||
(mp->b_wptr - mp->b_rptr) < sizeof (struct sockaddr) ||
ifp == NULL || saddr->sa_family != AF_UNSPEC) {
mp->b_datap->db_type = M_ERROR;
mp->b_rptr = mp->b_wptr = mp->b_datap->db_base;
*mp->b_wptr++ = EINVAL;
qreply(q, mp);
break;
}
/*
* Convert body into the form the rest of the system expects.
*/
if (!(m = cp_mblks_to_mbufs(mnp))) {
/*
* cp_mblks_to_mbufs has already freed
* the chain starting at mnp.
*/
freeb(mp);
break;
}
/*
* Keep the data-link header fields in network order.
*/
/*
* Would like to report errors back up, but can't do it
* synchronously and sending up an M_ERROR is overkill.
*/
(void) (*ifp->if_output)(ifp, m, saddr);
/*
* The interface output routine will (eventually)
* free the chain starting at mnp.
*/
freeb(mp);
break;
}
case M_DATA:
/*
* Erroneous: we require an M_PROTO header at
* the front, so we know where to send it.
*
* Fall through...
*/
default:
/*
* Is there anything else we should handle here?
*/
freemsg(mp); /* Wing it. */
break;
}
}
/*
* Handle write-side M_IOCTL messages.
*/
snit_ioctl(q, mp)
queue_t *q;
mblk_t *mp;
{
register ni_softc *nip = (ni_softc *)q->q_ptr;
register struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
register int cmd = iocp->ioc_cmd;
#ifdef SNITDEBUG
printf("snit%d M_IOCTL, cmd: %x\n", nip->ni_unit, iocp->ioc_cmd);
#endif SNITDEBUG
/*
* If the ioctl requires that the stream already
* be bound to an interface, verify that it is.
*/
switch (cmd) {
case SIOCGIFADDR:
case SIOCADDMULTI:
case SIOCDELMULTI:
case NIOCSFLAGS:
/*
* Return an error if we're not currently
* bound to an interface.
*/
if (!nip->ni_ifp) {
mp->b_datap->db_type = M_IOCNAK;
iocp->ioc_error = ENOTCONN;
qreply(q, mp);
return;
}
default:
break;
}
/*
* Process the command.
*/
switch (cmd) {
/*
* Ioctls bridging between this stream
* and the underlying 4.2 networking code.
*/
case SIOCGIFADDR:
case SIOCADDMULTI:
case SIOCDELMULTI: {
register struct ifnet *ifp = nip->ni_ifp;
register struct ifreq *ifr;
/*
* Error if bad arg length or no ifp ioctl routine.
*/
if ((iocp->ioc_count < sizeof (struct ifreq)) ||
(ifp->if_ioctl == NULL)) {
mp->b_datap->db_type = M_IOCNAK;
iocp->ioc_error = EINVAL;
break;
}
ifr = (struct ifreq *)mp->b_cont->b_rptr;
/*
* Have the interface fill in the info.
*/
iocp->ioc_error = (*ifp->if_ioctl)(ifp, cmd, ifr);
if (iocp->ioc_error)
mp->b_datap->db_type = M_IOCNAK;
else {
mp->b_datap->db_type = M_IOCACK;
iocp->ioc_count = sizeof *ifr;
}
break;
}
/*
* Ioctls specific to this driver.
*/
case NIOCBIND: {
/*
* Bind to a particular network interface.
*
* XXX: Disallow changing after bound? If not,
* should consider serializing wrt incoming
* packets, or else reporting interface id
* of each packet (supposing that headers
* are turned on).
*
* Alternatively (and this sounds better),
* expect the caller to flush the read queue
* after changing binding.
*/
register struct ifnet *ifp;
register struct ifreq *ifr;
/*
* Verify argument length.
*/
if (iocp->ioc_count < sizeof (struct ifreq)) {
mp->b_datap->db_type = M_IOCNAK;
iocp->ioc_error = EINVAL;
break;
}
ifr = (struct ifreq *)mp->b_cont->b_rptr;
ifp = ifunit(ifr->ifr_name, sizeof ifr->ifr_name);
if (!ifp) {
/*
* Bogus interface. Leave previous
* binding intact.
*/
mp->b_datap->db_type = M_IOCNAK;
iocp->ioc_error = ENXIO;
break;
}
/*
* Install the new binding and
* prepare to acknowledge the ioctl.
*
* XXX: Flush previously queued packets?
* Probably caller's responsibility.
*/
nip->ni_ifp = ifp;
mp->b_datap->db_type = M_IOCACK;
iocp->ioc_count = 0;
break;
}
/*
* Set new flag values. Most of the work here is concerned
* with handling transitions into and out of promiscuous mode.
*/
case NIOCSFLAGS: {
u_long flags;
register int error = 0;
/*
* Verify argument length.
*/
if (iocp->ioc_count < sizeof (long)) {
mp->b_datap->db_type = M_IOCNAK;
iocp->ioc_error = EINVAL;
break;
}
flags = *(u_long *)mp->b_cont->b_rptr;
if (flags & NI_PROMISC) {
if (iocp->ioc_uid) {
/*
* Insufficient privilege: reject.
*/
error = EPERM;
}
/*
* If promiscuous mode is being requested, ideally
* we should set u.u_acflag here, but can't.
*/
else if (!(nip->ni_flags & NI_PROMISC))
error = ifpromisc(nip->ni_ifp, 1);
if (error) {
mp->b_datap->db_type = M_IOCNAK;
iocp->ioc_error = error;
break;
}
}
else if (nip->ni_flags & NI_PROMISC) {
/*
* Transition out of promiscuous mode.
*/
error = ifpromisc(nip->ni_ifp, 0);
if (error) {
mp->b_datap->db_type = M_IOCNAK;
iocp->ioc_error = error;
break;
}
}
nip->ni_flags &= ~NI_USERBITS;
nip->ni_flags |= (flags & NI_USERBITS);
#ifdef SNITDEBUG
printf("NIOCSFLAGS: new flags %x\n", nip->ni_flags);
#endif SNITDEBUG
mp->b_datap->db_type = M_IOCACK;
iocp->ioc_count = 0;
break;
}
case NIOCGFLAGS:
/*
* Verify argument length.
*/
if (iocp->ioc_count < sizeof (long)) {
mp->b_datap->db_type = M_IOCNAK;
iocp->ioc_error = EINVAL;
break;
}
*(u_long *)mp->b_cont->b_rptr = nip->ni_flags & NI_USERBITS;
mp->b_datap->db_type = M_IOCACK;
iocp->ioc_count = sizeof (u_long);
break;
/*
* Set new snapshot length. If we're not requested to pass
* everything up (snaplen == 0), we force it to be at least
* sizeof (struct ether_header), which simplifies life elsewhere.
*
* Since the snapshot length dramatically affects flow control
* characteristics, we reset the high and low water mark values
* based on the new snaplen. This is a bit tacky. A better way
* to proceed might be to define a new ioctl specifically for
* setting high and low water mark values. This would have the
* advantage of letting the user take into account the entire
* set of modules pushed onto the stream. (The facilities for
* setting flow control values properly for a given stream are
* really quite unsatisfactory, especially when trying to handle
* multiple modules with service procedures on the same stream.)
*/
case NIOCSSNAP: {
mblk_t *msp;
/*
* Verify argument length.
*/
if (iocp->ioc_count < sizeof (long)) {
mp->b_datap->db_type = M_IOCNAK;
iocp->ioc_error = EINVAL;
break;
}
nip->ni_snap = *(u_long *)mp->b_cont->b_rptr;
if (nip->ni_snap != 0 &&
nip->ni_snap < sizeof (struct ether_header))
nip->ni_snap = sizeof (struct ether_header);
mp->b_datap->db_type = M_IOCACK;
iocp->ioc_count = 0;
/*
* Build an M_SETOPTS message with revised flow control
* values and fire it upstream. If allocation fails,
* give up in disgust.
*/
if (msp = allocb(sizeof (struct stroptions), BPRI_MED)) {
register struct stroptions *sop;
int snap,
fudge;
/*
* XXX Heuristic: Set fudge to allow more packets
* in the system for smaller packet sizes. This
* is ugly and should be replaced by a better
* method of setting flow control characteristics.
*/
snap = nip->ni_snap != 0 ? nip->ni_snap : ETHERMTU;
fudge = snap <= 100 ? 4 :
snap <= 400 ? 2 :
1;
msp->b_datap->db_type = M_SETOPTS;
sop = (struct stroptions *)msp->b_rptr;
sop->so_flags = SO_HIWAT | SO_LOWAT;
sop->so_hiwat = SNIT_HIWAT(snap, fudge);
sop->so_lowat = SNIT_LOWAT(snap, fudge);
msp->b_wptr += sizeof (struct stroptions);
qreply(q, msp);
}
break;
}
case NIOCGSNAP:
/*
* Verify argument length.
*/
if (iocp->ioc_count < sizeof (long)) {
mp->b_datap->db_type = M_IOCNAK;
iocp->ioc_error = EINVAL;
break;
}
*(u_long *)mp->b_cont->b_rptr = nip->ni_snap;
mp->b_datap->db_type = M_IOCACK;
iocp->ioc_count = sizeof (u_long);
break;
default:
/*
* We're at the end of the line and
* must NACK all unrecognized ioctls.
*/
mp->b_datap->db_type = M_IOCNAK;
iocp->ioc_count = 0;
break;
}
/*
* Send the response back upstream.
*/
qreply(q, mp);
}
/*
* Feed-in from do_protocol(). Invoked at splimp. From the
* viewpoint of the streams subsystem, this routine acts like
* a device interrupt routine. (If this were a streams module
* as opposed to driver, this would be the read-side service
* procedure.)
*
* For now, at least, we use the same interface as that to
* nit_tap. Since the packet has already been stuffed into
* an mbuf by this point, using this interface implies
* extra copying.
*/
/*
* Find next device instance to get a copy of the packet.
* It must be open, on the same interface as the packet,
* and have room for the packet in its queue. The packet
* must also be directed to this host or this device instance
* must be in promiscuous mode.
*
* The macro also sneaks in some statistics gathering.
*/
#define nextrecipient(nip, ifp, nif) { \
for ( ; (nip); (nip) = (nip)->ni_next) \
if ((nip)->ni_ifp == (ifp) && \
(!(nif)->nif_promisc || ((nip)->ni_flags & NI_PROMISC)) && \
(canput((nip)->ni_rq) ? 1 : ((nip)->ni_drops++, 0))) \
break; \
}
snit_intr(ifp, m, nif)
struct ifnet *ifp;
struct mbuf *m;
struct nit_if *nif;
{
mblk_t *snit_cpmsg();
register mblk_t *mp,
*mnp;
mblk_t *mhp = NULL;
register ni_softc *nip,
*nnip;
register u_long cursnap;
/*
* Get the first target device instance. If there isn't
* one, we're done.
*/
nip = nisoftc;
nextrecipient(nip, ifp, nif);
if (!nip)
return;
/*
* Make the first message copy, recording how much of it we
* actually transcribe. We use this value below to determine
* whether the next device instance needs a fresh copy or can
* share this one -- if the snaplens are the same, we can share.
*
* This strategy could be improved at the price of examining
* device instances in snaplen order. We could also consider
* unconditional sharing with b_[rw]ptr values adjusted to
* reflect snaplens, but this could lead to excessive storage
* committment. (The tradeoffs are unclear. Consider a couple
* rarpd streams followed by an etherfind stream. The rarpd
* instances need entire packets (snaplen == 0), but immediately
* throw most away. If packet storage is shared with the etherfind
* instance, the upstream buffering module will gather together
* lots of big buffers with little of each used.)
*/
cursnap = nip->ni_snap;
mp = snit_cpmsg(nif, m, cursnap);
if (!mp) {
nip->ni_drops++;
#ifdef SNITDEBUG
printf("snit%d: snit_cpmsg failed\n", nip->ni_unit);
#endif SNITDEBUG
return;
}
/*
* Loop through all open device instances, passing
* a copy of the packet to each. At each iteration,
* if there's still another instance to go, produce
* a new copy before passing the current one on.
* This convolution is necessary to prevent the
* next module up in line from destroying the packet
* before we can copy it.
*/
for ( ; nip && mp; nip = nnip, mp = mnp) {
nnip = nip->ni_next;
nextrecipient(nnip, ifp, nif);
if (nnip) {
/* Make the next copy. */
if (nnip->ni_snap == cursnap) {
/* Sharable. */
mnp = dupmsg(mp);
if (!mnp) {
nnip->ni_drops++;
#ifdef SNITDEBUG
printf("snit%d: dupmsg failed\n",
nnip->ni_unit);
#endif SNITDEBUG
}
}
else {
/* Need distinct copy (but see above). */
mnp = snit_cpmsg(nif, m, nnip->ni_snap);
if (!mnp) {
nnip->ni_drops++;
#ifdef SNITDEBUG
printf("snit%d: snit_cpmsg failed\n",
nnip->ni_unit);
#endif SNITDEBUG
}
else
cursnap = nnip->ni_snap;
}
}
/*
* If the current instance expects any of the nit headers,
* allocate space for them and fill them in. Unfortunately,
* the header can't share the dblk holding the packet itself,
* since the message types differ. Since each instance can
* request different headers (and the drops header can have
* a different value for each instance), it's not feasible
* to share headers among instances either.
*/
if (nip->ni_flags & (NI_TIMESTAMP|NI_LEN|NI_DROPS)) {
/*
* Calculate space required for header
* then allocate it.
*/
register int hlen = 0;
if (nip->ni_flags & NI_TIMESTAMP)
hlen += sizeof (struct nit_iftime);
if (nip->ni_flags & NI_DROPS)
hlen += sizeof (struct nit_ifdrops);;
if (nip->ni_flags & NI_LEN)
hlen += sizeof (struct nit_iflen);
hlen += SNIT_PRE_PAD;
mhp = allocb(hlen, BPRI_MED);
if (!mhp) {
freemsg(mp);
nip->ni_drops++;
#ifdef SNITDEBUG
printf("snit%d: len %d (ifhdr) allocb failed\n",
nip->ni_unit, hlen);
#endif SNITDEBUG
continue;
}
mhp->b_rptr += SNIT_PRE_PAD;
mhp->b_wptr += SNIT_PRE_PAD;
/*
* Fill it in. Ordering of the individual
* headers is important and must correspond
* to the commentary in nit_if.h.
*/
mhp->b_datap->db_type = M_PROTO;
if (nip->ni_flags & NI_TIMESTAMP) {
struct nit_iftime *ntp;
ntp = (struct nit_iftime *)mhp->b_wptr;
uniqtime(&ntp->nh_timestamp);
mhp->b_wptr += sizeof (struct nit_iftime);
}
if (nip->ni_flags & NI_DROPS) {
struct nit_ifdrops *ndp;
ndp = (struct nit_ifdrops *)mhp->b_wptr;
ndp->nh_drops = nip->ni_drops;
mhp->b_wptr += sizeof (struct nit_ifdrops);
}
if (nip->ni_flags & NI_LEN) {
struct nit_iflen *nlp;
nlp = (struct nit_iflen *)mhp->b_wptr;
nlp->nh_pktlen =
nif->nif_hdrlen + nif->nif_bodylen;
mhp->b_wptr += sizeof (struct nit_iflen);
}
/*
* Paste the headers on to the front of the packet.
*/
linkb(mhp, mp);
mp = mhp;
}
/*
* Shove the packet into the next module's read queue.
*/
putnext(nip->ni_rq, mp);
}
}
#undef nextrecipient
/*
* Copy the first snaplen bytes of the packet denoted by nif (both
* header and body) into mblks. See the comments below for more
* details on the semantics of snaplen.
*
* We attempt to use buffer space as efficiently as possible
* by using a single dblk for the header and body, placing them
* at the end of the dblk (but leaving enough room at the end
* to maintain correct alignment for the start of the header).
* This placement maximizes the chance that there's sufficient
* space at the front to prepend headers.
*
* This strategy is based on knowledge of expected use (it's
* likely that additional headers will be pasted on at the
* beginning, as opposed to the more usual case of having them
* be stripped off; the packet body should be contiguous to make
* life easier for the packet filtering module that's likely to
* be next in line; etc.) and observations drawn from previous
* versions that exposed trouble with running out of buffer
* resources.
*/
mblk_t *
snit_cpmsg(nif, m, snaplen)
register struct nit_if *nif;
struct mbuf *m;
register u_long snaplen;
{
register mblk_t *mp;
register int blen,
rlen;
/*
* Get rounded length, taking the snapshot length into account.
* Note that a snapshot length of zero implies "deliver the whole
* thing". Nonzero values are constrained elsewhere to be large
* enough that the entire link level header is always included.
*
* precondition: snaplen >= sizeof (struct ether_header)
*/
blen = nif->nif_bodylen;
if (snaplen != 0) {
if (blen > snaplen - sizeof (struct ether_header))
blen = snaplen - sizeof (struct ether_header);
}
rlen = roundup(blen + nif->nif_hdrlen, sizeof (u_long));
/*
* Get a dblk and arrange to copy to its end.
*/
if (!(mp = allocb(rlen, BPRI_MED)))
return (NULL);
mp->b_datap->db_type = M_DATA;
mp->b_rptr = mp->b_wptr = mp->b_datap->db_lim - rlen;
/*
* Transcribe packet header.
*/
bcopy(nif->nif_header, (caddr_t)mp->b_wptr,
nif->nif_hdrlen);
mp->b_wptr += nif->nif_hdrlen;
/*
* Transcribe packet body.
*/
if (blen > 0) {
(void) m_cpytoc(m, 0, blen, (caddr_t)mp->b_wptr);
mp->b_wptr += blen;
}
return (mp);
}
/*
* XXX: move these routines to a place where other drivers/modules
* can take advantage of them.
*/
/*
* Free the contents of an MCL_LOANED mbuf constructed
* with cp_mblks_to_mbufs below; i.e., free the underlying
* mblk.
*
* N.B.: only the mblk itself is freed; anything it points
* to is left intact.
*/
free_mbuffed_mblk(arg)
int arg;
{
freeb((mblk_t *)arg);
}
/*
* Copy the mblk chain headed at mp into mbufs by wrapping each
* mblk into an MCL_LOANED mbuf. Ignore the mblk b_next field,
* following only the b_cont link. Set the type of all the resulting
* mbufs to MT_DATA.
*
* N.B.: it is the responsibility of the ultimate consumer of the
* resulting mbuf chain to free it. Since the chain consists of
* MCL_LOANED mbufs, freeing it will free the original mblk chain
* as well, so the caller should _not_ free the original chain.
*
* If resources aren't immediately available, return NULL.
*/
struct mbuf *
cp_mblks_to_mbufs(mp)
mblk_t *mp;
{
struct mbuf *head = NULL;
register struct mbuf *m,
**mm;
register mblk_t *mnp;
mm = &head;
for (mnp = mp; mnp; mnp = mnp->b_cont) {
m = mclgetx(free_mbuffed_mblk, (int)mnp,
(caddr_t)(mnp->b_rptr), mnp->b_wptr - mnp->b_rptr,
M_DONTWAIT);
if (!m) {
/*
* Must free the mbuf chain we have so far
* (and by extension, the mblks embedded in
* it), and the unconsumed part of the mblk
* chain.
*/
if (head)
m_freem(head);
freemsg(mnp);
return (NULL);
}
*mm = m;
mm = &m->m_next;
}
return (head);
}
#ifdef SNITDEBUG
/*
* Dump the addresses and unit numbers of ni_softc instances
* present on the list rooted at nip.
*/
snitdump(nip)
register ni_softc *nip;
{
printf(" snitdump:\n");
while (nip) {
printf(" %x\t%d\n", nip, nip->ni_unit);
nip = nip->ni_next;
}
}
#endif SNITDEBUG
#endif NSNIT > 0