pfildrv.c revision 381a2a9a387f449fab7d0c7e97c4184c26963abf
/*
* Copyright (C) 2000, 2003 by Darren Reed.
*
* See the IPFILTER.LICENCE file for details on licencing.
*
* Copyright 2006 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 "compat.h"
#include "qif.h"
#include "pfil.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 = {
};
extern int nulldev();
extern int nodev();
#if SOLARIS2 < 10
static int pfil_identify(dev_info_t *);
#endif
#ifdef DDI_DEFINE_STREAM_OPS
#else
nodev, /* cb_open */
nodev, /* cb_close */
nodev, /* cb_strategy */
nodev, /* cb_print */
nodev, /* cb_dump */
nodev, /* cb_read */
nodev, /* cb_write */
nodev, /* cb_ioctl */
nodev, /* cb_devmap */
nodev, /* cb_mmap */
nodev, /* cb_segmap */
nochpoll, /* cb_chpoll */
ddi_prop_op, /* cb_prop_op */
&pfilinfo, /* cb_stream */
D_MP /* cb_flag */
};
static struct dev_ops pfil_devops =
{
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
pfil_info, /* devo_getinfo */
#if SOLARIS2 >= 10
#else
pfil_identify, /* devo_identify */
#endif
nulldev, /* devo_probe */
pfil_attach, /* devo_attach */
pfil_detach, /* devo_detach */
nodev, /* devo_reset */
&pfil_ops, /* devo_cb_ops */
NULL /* devo_bus_ops */
};
#endif
};
/************************************************************************
* 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);
#if SOLARIS2 >= 8
#else
#endif
}
/* ------------------------------------------------------------------------ */
/* 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: pfilmodopen */
/* 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 pass packet because it's not a type subject to */
/* firewall rules (i.e. internal STREAMS messages), */
/* 0 == pass packet, else > 0 indicates passing */
/* prohibited (possibly due to an error occuring in */
/* this function.) */
/* 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 ((flags & PFIL_GROUP) != 0)
/*
* 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 SOLARIS2 >= 8
#endif
/*
* 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;
}
}
/*
* 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 */
}
}
#ifdef IRE_ILL_CN
#else
#endif
if (mlen == 0)
#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
/*
* If there is more than one copy of this message traversing the
* STREAMS stack (ie the packet is being used for snoop data), the
* IP header isn't on a 32bit aligned address, or the IP header
* isn't contain within a single block, then make a copy which
* meets our requirements and do a freemsg on the one passed in
* since we're no longer using it or passing it up.
*/
#if SOLARIS2 >= 8
&& sap != IP6_DL_SAP
#endif
)) {
mblk_t *b;
previous_nm = NULL;
/*
* Duplicate the message block descriptors up to (and
* including if the offset is non-zero) the block where
* IP begins.
*/
if (nmt)
return ENOBUFS;
}
if (nmt)
else
previous_nm = nm;
/*
* Set the length so the block only contains what
* appears before IP.
*/
if (b == m) {
break;
}
}
if (nmt)
return ENOBUFS;
}
if (nmt)
else
m = nm;
off = 0;
}
/* XXX - might not be aligned (from ppp?) */
}
#if SOLARIS2 >= 8
else if (sap == IP6_DL_SAP) {
/* XXX - might not be aligned (from ppp?) */
if (plen == 0)
return EMSGSIZE; /* Jumbo gram */
}
#endif
else {
sap = -1;
}
#if SOLARIS2 >= 8
#endif
|| sap == -1
) {
#ifdef PFILDEBUG
#endif
return EINVAL;
}
#if SOLARIS2 >= 8
else if (sap == IP6_DL_SAP)
#endif
if ((
#if SOLARIS2 >= 8
#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.
*/
goto forced_copy;
return ENOBUFS;
}
}
/*
* Discard any excess data.
*/
/*
* 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
}
#endif
err = 0;
/*LINTED: E_CONSTANT_CONDITION*/
break;
/*
* fr_pullup may have allocated a new buffer.
*/
}
/*
* Functions called via pfil_func should only return values >= 0, so
* convert any that are < 0 to be > 0 and preserve the absolute value.
*/
if (err < 0)
/*
* 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
}
}
#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
/* ------------------------------------------------------------------------ */
/* Function: pfil_property_update */
/* Returns: int - DDI_SUCCESS == success, else failure */
/* Parameters: modinfop(I) - pointer to module informatio buffer */
/* */
/* Fetch configuration file values that have been entered into the */
/* pfil.conf driver file. */
/* ------------------------------------------------------------------------ */
{
char *list, *s, *t;
int err;
"ddi-no-autodetach", 1) == -1) {
return DDI_FAILURE;
}
0, "qif_ipmp_set", &list);
#ifdef IPFDEBUG
err);
#endif
if (err == DDI_SUCCESS) {
t = NULL;
s = list;
do {
if (t != NULL)
s = t + 1;
t = strchr(s, ';');
if (t != NULL)
*t = '\0';
qif_ipmp_update(s);
} while (t != NULL);
}
return DDI_SUCCESS;
}
#if SOLARIS2 == 8
{
return 0;
}
#endif