pfildrv.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* Copyright (C) 2000, 2003 by Darren Reed.
*
* See the IPFILTER.LICENCE file for details on licencing.
*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/ethernet.h>
#include <netinet/in_systm.h>
#if SOLARIS2 >= 8
#endif
#include <pfild.h>
#define MINSDUSZ 1
char _depends_on[] = "drv/ip";
static struct module_info pfil_minfo = {
};
int pfildebug = 0;
int pfil_installed = 0;
#ifdef IRE_ILL_CN
#endif
/************************************************************************
*/
#ifdef IRE_ILL_CN
static void pfil_remif(queue_t *q);
static void _dump_s_ill(s_ill_t *);
static void _dump_s_ill_all(void);
#endif
static struct qinit pfil_rinit = {
};
static struct qinit pfil_winit = {
};
struct streamtab pfil_dev_strtab = {
};
#if SOLARIS2 < 10
static int pfil_identify(dev_info_t *);
#endif
static void pfil_update_ifaddrs(mblk_t *);
static void pfil_update_ifaddrset(mblk_t *);
};
/************************************************************************
* STREAMS module information
*/
static struct qinit pfilmod_rinit = {
};
static struct qinit pfilmod_winit = {
};
struct streamtab pfil_mod_strtab = {
};
};
static struct modlstrmod modlstrmod = {
&fsw
};
/************************************************************************
* STREAMS externally visible information for _init() and _info ()
*/
static struct modlinkage modlinkage = {
};
/************************************************************************
* STREAMS device functions
*/
static dev_info_t *pfil_dev_info;
/* ------------------------------------------------------------------------ */
/* Function: pfil_attach */
/* Returns: int - DDI_SUCCESS for success, otherwise DDI_FAILURE */
/* Parameters: devi(I) - pointer to packet information */
/* cmd(I) - DDI command to process */
/* */
/* Called when the driver has been attached, just create the device file. */
/* ------------------------------------------------------------------------ */
/*ARGUSED*/
{
/* LINTED: E_CONSTANT_CONDITION */
if (cmd != DDI_ATTACH)
return (DDI_FAILURE);
}
/* ------------------------------------------------------------------------ */
/* Function: pfil_detach */
/* Returns: int - DDI_SUCCESS for success, otherwise DDI_FAILURE */
/* Parameters: devi(I) - pointer to device information */
/* cmd(I) - DDI command to process */
/* */
/* Nothing to do here(?) except return that everything is ok. */
/* ------------------------------------------------------------------------ */
/*ARGUSED*/
{
/* LINTED: E_CONSTANT_CONDITION */
if (cmd != DDI_DETACH)
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/* ------------------------------------------------------------------------ */
/* Function: pfil_info */
/* Returns: int - DDI_SUCCESS (success), DDI_FAILURE (failure) */
/* Parameters: dip(I) - pointer to device information */
/* cmd(I) - DDI command to process */
/* arg(I) - paramter to the command to be processed */
/* res(O) - pointer to storage for returning results */
/* */
/* Handles information queries made by the kernel of the STREAMS device. */
/* ------------------------------------------------------------------------ */
/*ARGUSED*/
void **res)
{
int result = DDI_FAILURE;
/* LINTED: E_CONSTANT_CONDITION */
switch (infocmd)
{
case DDI_INFO_DEVT2DEVINFO:
if (pfil_dev_info != NULL) {
*res = (void *)pfil_dev_info;
}
break;
case DDI_INFO_DEVT2INSTANCE:
break;
default :
break;
}
return result;
}
#if SOLARIS2 < 10
/* ------------------------------------------------------------------------ */
/* Function: pfil_identify */
/* Returns: int - DDI_IDENTIFIED (success), DDI_NOT_IDENTIFIED (failure)*/
/* Parameters: devi(I) - pointer to a dev_info structure */
/* */
/* Check to see if this module is correctly associated with the device info */
/* structure passed in. */
/* ------------------------------------------------------------------------ */
{
int result = DDI_NOT_IDENTIFIED;
/*LINTED: E_CONSTANT_CONDITION*/
return result;
}
#endif
/* ------------------------------------------------------------------------ */
/* Function: pfildevopen */
/* Returns: int - 0 == sucess, else failure */
/* Parameters: q(I) - pointer to STREAMS queue */
/* devp(I) - pointer to a device number */
/* oflag(I) - file mode open flags (always 0 for module opens) */
/* sflag(I) - flag indicating how the open is being made */
/* crp(I) - pointer to message credentials from the user */
/* */
/* Perform any action required to open the STREAMS device, supporting it */
/* being opened in a cloning fashion. */
/* ------------------------------------------------------------------------ */
/*ARGSUSED*/
{
int result = 0;
/* LINTED: E_CONSTANT_CONDITION */
/*
* As per recommendation on man page open(9e)
*/
if (result == 0)
qprocson(q);
return result;
}
/* ------------------------------------------------------------------------ */
/* Function: pfildevclose */
/* Returns: int - always returns 0. */
/* Parameters: q(I) - pointer to STREAMS queue */
/* flag(I) - file status flag */
/* crp(I) - pointer to message credentials from the user */
/* */
/* Perform any action required to close the STREAMS device. */
/* ------------------------------------------------------------------------ */
/*ARGSUSED*/
{
/* LINTED: E_CONSTANT_CONDITION */
qprocsoff(q);
#ifdef IRE_ILL_CN
if (q == pfildq)
#endif
return 0;
}
/************************************************************************
* STREAMS module functions
*/
/* ------------------------------------------------------------------------ */
/* Function: pfil_strmodopen */
/* Returns: int - 0 == success, else error */
/* Parameters: q(I) - pointer to read-side STREAMS queue */
/* devp(I) - pointer to a device number */
/* oflag(I) - file status open flags (always 0 for module open)*/
/* sflag(I) - flag indicating how the open is being made */
/* crp(I) - pointer to message credentials from the user */
/* */
/* open() entry hook for the STREAMS module. */
/* ------------------------------------------------------------------------ */
/*ARGSUSED*/
{
/* LINTED: E_CONSTANT_CONDITION */
/*
* As per recommendation on man page open(9e)
*/
return ENXIO;
qprocson(q);
return 0;
}
/* ------------------------------------------------------------------------ */
/* Function: pfilmodclose */
/* Returns: int - always returns 0. */
/* Parameters: q(I) - pointer to read-side STREAMS queue */
/* flag(I) - file status flag */
/* crp(I) - pointer to message credentials from the user */
/* */
/* close() entry hook for the STREAMS module. qif_delete() takes care of */
/* setting q_ptr back to NULL for both this and the write side queue. */
/* ------------------------------------------------------------------------ */
/*ARGSUSED*/
{
/* LINTED: E_CONSTANT_CONDITION */
qprocsoff(q);
#ifdef IRE_ILL_CN
pfil_remif(q);
#endif
qif_delete(q->q_ptr, q);
return 0;
}
/************************************************************************
* other support functions
*/
/* ------------------------------------------------------------------------ */
/* Function: pfil_precheck */
/* Returns: int - < 0 is error in this function, 0 == pass packet, else */
/* (> 0) indicates passing prohibited */
/* Parameters: q(I) - pointer to STREAMS queue */
/* mp(I) - pointer to STREAMS message */
/* qif(I) - pointer to per-queue interface information */
/* Locks: pfil_rw */
/* */
/* In here we attempt to determine if there is an IP packet within an mblk */
/* that is being passed along and if there is, ensure that it falls on a 32 */
/* bit aligned address and at least all of the layer 3 header is in one */
/* buffer, preferably all the layer 4 too if we recognise it. Finally, if */
/* we can be sure that the buffer passes some sanity checks, pass it on to */
/* ------------------------------------------------------------------------ */
/*ARGSUSED*/
{
#if SOLARIS2 >= 8
#endif
#ifndef sparc
#endif
/*
* If there is only M_DATA for a packet going out, then any header
* information (which would otherwise appear in an M_PROTO mblk before
* the M_DATA) is prepended before the IP header. We need to set the
* offset to account for this.
*/
m = NULL;
/*
* If the message protocol block indicates that there isn't a data
* block following it, just return back.
*/
{
case M_PROTO :
case M_PCPROTO :
{
off = 0;
m = mt;
} else {
return -1;
}
} else {
if (m == NULL) {
return -3; /* No data blocks */
}
}
break;
}
case M_DATA :
m = mt;
break;
default :
return -2;
}
/*
* Find the first data block, count the data blocks in this chain and
* the total amount of data.
*/
off = 0; /* Any non-M_DATA cancels the offset */
if (m == NULL) {
return -3; /* No data blocks */
}
/*
* This is a complete kludge to try and work around some bizarre
* packets which drop through into pfil_donotip.
*/
off += 2;
}
}
/*
* If there is more than one copy of this message traversing the
* STREAMS stack (ie packet is being used for snoop data) then make a
* copy of it for our use so we become the sole owner of the new
* message and do a freemsg() on the one passed in as we're no longer
* using it or passing it up.
*/
return -3;
}
if (mt != m)
else {
}
freemsg(m);
m = new;
}
/*
* We might have a 1st data block which is really M_PROTO, i.e. it is
* only big enough for the link layer header
*/
m = m->b_cont;
if (m == NULL) {
return -4; /* not enough data for IP */
}
}
if (mlen == 0)
#ifdef IRE_ILL_CN
#else
#endif
if (sap == ETHERTYPE_IP) {
/* XXX - might not be aligned (from ppp?) */
sap = 0;
}
#if SOLARIS2 >= 8
else if (sap == IP6_DL_SAP) {
/* XXX - might not be aligned (from ppp?) */
if (plen == 0)
return EMSGSIZE; /* Jumbo gram */
sap = IP6_DL_SAP;
}
#endif
else {
hlen = 0;
sap = -1;
}
#ifdef PFILDEBUG
/*LINTED: E_CONSTANT_CONDITION*/
"!IP Filter[%s]: out %d len %ld/%ld sap %d ip %p b_rptr %p off %ld m %p/%d/%d/%p mt %p/%d/%d/%p\n",
#endif
/*
* Ok, the IP header isn't on a 32bit aligned address so fix this.
*/
int off2;
/*
* If we have already tried to realign the IP header and we
* are back here, then the attempt has failed, so stop now
* rather than try again (could keep on retrying with no
* benefit.)
*/
if (realigned) {
return EINVAL;
}
realigned = 1;
return EINVAL;
}
/*
* XXX - Now I understand how pullupmsg() & STREAMS messages
* work better, this can possibly be junked in favour of using
* pullupmsg() which will preserve all the dblk bits correctly,
* as is done in fr_pullup in the ipf code.
*/
/*
* Junk using pullupmsg()
*/
if (off2)
return ENOBUFS;
}
m1 = m;
}
/*
* check if the buffer we're changing is chained in-
*/
freemsg(m);
} else {
if (m == mt) {
if (m1)
}
}
off = 0;
goto tryagain;
}
#if SOLARIS2 >= 8
#endif
) {
#ifdef PFILDEBUG
#endif
return EINVAL;
}
/*
* The code in IPFilter assumes that both the ip_off and ip_len
* fields are in host byte order, so convert them here to fulfill
* that expectation.
*
* If the target compile host is non-SPARC, assume it is a little
* to both be host byte ordered.
*/
#ifndef sparc
# if SOLARIS2 >= 8
if (sap == IP6_DL_SAP) {
} else {
# endif
# if SOLARIS2 >= 8
}
# endif
#endif
if (sap == 0)
#if SOLARIS2 >= 8
else if (sap == IP6_DL_SAP)
#endif
if ((
#if SOLARIS2 >= 8
((sap == 0) &&
#endif
/*
*/
#ifndef sparc
# if SOLARIS2 >= 8
if (sap == IP6_DL_SAP) {
} else {
# endif
# if SOLARIS2 >= 8
}
# endif
#endif
return EINVAL;
}
/*
* If we don't have enough data in the mblk or we haven't yet copied
* enough (above), then copy some more.
*/
return -5;
}
}
err = 0;
/*LINTED: E_CONSTANT_CONDITION*/
break;
}
/*
* If we still have a STREAMS message after calling the filtering
* hooks, return the byte order of the fields changed above on
* platforms where this is required. They are refetched from the
* packet headers because the callback (pfil_func) may have changed
* them in some way.
*/
#ifndef sparc
# if SOLARIS2 >= 8
if (sap == IP6_DL_SAP) {
} else {
# endif
# if SOLARIS2 >= 8
}
# endif
}
#endif
return err;
}
/************************************************************************
* kernel module initialization
*/
/* ------------------------------------------------------------------------ */
/* Function: _init */
/* Returns: int - DDI_SUCCESS == success, else failure */
/* Parameters: Nil. */
/* */
/* Initialise the kernel module and if that succeeds, call other init */
/* routines, elsewhere, that handle initialisation of the more generic */
/* components. */
/* ------------------------------------------------------------------------ */
int _init(void)
{
int result;
result = pfil_nd_init();
/* LINTED: E_CONSTANT_CONDITION */
if (result != 0)
return DDI_FAILURE;
if (qif_startup() == -1)
return DDI_FAILURE;
#ifdef IRE_ILL_CN
#endif
pfil_startup();
/* LINTED: E_CONSTANT_CONDITION */
return result;
}
/* ------------------------------------------------------------------------ */
/* Function: _fini */
/* Returns: int - DDI_SUCCESS == success, else failure */
/* Parameters: Nil. */
/* */
/* Called when the OS attempts to unload the module, it should only be */
/* allowed to succeed if pfil is not currently in the middle of any STREAMS */
/* "connections". If it isn't then turn ourselves off and remove the module*/
/* ------------------------------------------------------------------------ */
int _fini(void)
{
int result;
return EBUSY;
/* LINTED: E_CONSTANT_CONDITION */
if (result == DDI_SUCCESS) {
pfil_nd_fini();
qif_stop();
#ifdef IRE_ILL_CN
#endif
pfil_installed = 0;
}
return result;
}
/* ------------------------------------------------------------------------ */
/* Function: _info */
/* Returns: int - DDI_SUCCESS == success, else failure */
/* Parameters: modinfop(I) - pointer to module informatio buffer */
/* */
/* Standard _info() implementation that just calls mod_info on its linkage */
/* structure so information can be copied back into the modinfop struct. */
/* ------------------------------------------------------------------------ */
{
int result;
/* LINTED: E_CONSTANT_CONDITION */
return result;
}
/************************************************************************
* Sun Solaris ON build specific routines follow here.
*/
#ifdef IRE_ILL_CN
{
}
static void
_dump_s_ill_all(void)
{
/* who care locks */
if (pfildebug > 10) {
}
}
}
/*
* Allocate an s_ill_t for this interface (name) if needed.
* Populate the read queue pointer.
*/
{
/* XXX: Need to check for duplicate */
/* If replumbed, rq will be updated */
/* interface already there */
break;
}
}
if (!ill) {
if (!ill) {
(int)sizeof(s_ill_t));
return;
}
s_ill_g_head = ill;
}
}
/*
* Deactivate any s_ill_t for this interface (queue pair).
* Called when a module is being closed (popped).
*/
{
}
#endif /* IRE_ILL_CN */
#ifdef PFILDEBUG
/* ------------------------------------------------------------------------ */
/* Function: pfil_donotip */
/* Returns: Nil */
/* Parameters: out(I) - in(0)/out(1) flag for direction of message */
/* qif(I) - pointer to per-queue interface information */
/* q(I) - pointer to STREAMS queue */
/* m(I) - pointer to STREAMS message block where IP starts */
/* mt(I) - pointer to the start of the STREAMS message */
/* ip(I) - pointer to the start of the IP header */
/* off(I) - offset from start of message to start of IP header */
/* */
/* This function is here solely for dumping out the contents of an mblk and */
/* showing what related information is known about it, to aid in debugging */
/* processing of messages going by that fail to be recognised properly. */
/* ------------------------------------------------------------------------ */
void pfil_donotip(int out, qif_t *qif, queue_t *q, mblk_t *m, mblk_t *mt, struct ip *ip, size_t off)
{
int i;
outb[0] = '\0';
m = m->b_cont;
/*LINTED: E_CONSTANT_CONDITION*/
"!IP %s:%d %ld %p %p %p ip %p b_rptr %p off %ld m %p/%d/%d/%p mt %p/%d/%d/%p\n",
/*LINTED: E_CONSTANT_CONDITION*/
while (m != mt) {
i = 0;
t = outb;
t += strlen((char *)t);
(void)sprintf((char *)t, "%02x%s", *s++,
((i & 3) == 3) ? " " : "");
t += ((i & 3) == 3) ? 3 : 2;
}
*t++ = '\n';
*t = '\0';
/*LINTED: E_CONSTANT_CONDITION*/
}
i = 0;
t = outb;
s = m->b_rptr;
t += strlen((char *)t);
for (; (i < 100) && (s < m->b_wptr); i++) {
(void)sprintf((char *)t, "%02x%s", *s++,
((i & 3) == 3) ? " " : "");
t += ((i & 3) == 3) ? 3 : 2;
}
*t++ = '\n';
*t = '\0';
/*LINTED: E_CONSTANT_CONDITION*/
}
#endif