/*
* Copyright (C) 1993-2001, 2003 by Darren Reed.
*
* See the IPFILTER.LICENCE file for details on licencing.
*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Copyright (c) 2014, Joyent, Inc. All rights reserved.
*/
/*
* ipfilter kernel module mutexes and locking:
*
* Enabling ipfilter creates a per-netstack ipf_stack_t object that is
* stored in the ipf_stacks list, which is protected by ipf_stack_lock.
* ipf_stack_t objects are accessed in three contexts:
*
* 1) administering that filter (eg: ioctls handled with iplioctl())
* 2) reading log data (eg: iplread() / iplwrite())
* 3) filtering packets (eg: ipf_hook4_* and ipf_hook6_* pfhooks
* functions)
*
* Each ipf_stack_t has a RW lock, ifs_ipf_global, protecting access to the
* whole structure. The structure also has locks protecting the various
* data structures used for filtering. The following guidelines should be
* followed for ipf_stack_t locks:
*
* - ipf_stack_lock must be held when accessing the ipf_stacks list
* - ipf_stack_lock should be held before acquiring ifs_ipf_global for
* a stack (the exception to this is ipf_stack_destroy(), which removes
* the ipf_stack_t from the list, then drops ipf_stack_lock before
* acquiring ifs_ipf_global)
* - ifs_ipf_global must be held when accessing an ipf_stack_t in that list:
* - The write lock is held only during stack creation / destruction
* - The read lock should be held for all other accesses
* - To alter the filtering data in the administrative context, one must:
* - acquire the read lock for ifs_ipf_global
* - then acquire the write lock for the data in question
* - In the filtering path, the read lock needs to be held for each type of
* filtering data used
* - ifs_ipf_global does not need to be held in the filtering path:
* - The filtering hooks don't need to modify the stack itself
* - The ipf_stack_t will not be destroyed until the hooks are unregistered.
* This requires a write lock on the hook, ensuring that no active hooks
* (eg: the filtering path) are running, and that the hooks won't be run
* afterward.
*
* Note that there is a deadlock possible when calling net_hook_register()
* or net_hook_unregister() with ifs_ipf_global held: see the comments in
* iplattach() and ipldetach() for details.
*/
#include <sys/dditypes.h>
#include <sys/autoconf.h>
#include <sys/byteorder.h>
#if SOLARIS2 >= 6
#include <net/if_types.h>
#endif
#include <netinet/in_systm.h>
#include <netinet/if_ether.h>
#include "netinet/ip_compat.h"
#include "netinet/ip_state.h"
#include "netinet/ipf_stack.h"
void *, void **));
#if SOLARIS2 < 10
#endif
IPLOOKUP_NAME, NULL };
extern void *ipf_state; /* DDI state */
nodev, /* strategy */
nodev, /* print */
nodev, /* dump */
iplwrite, /* write */
iplioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
NULL,
#if SOLARIS2 > 4
nodev, /* aread */
nodev, /* awrite */
#endif
};
0,
#if SOLARIS2 >= 10
#else
#endif
nodev, /* reset */
(struct bus_ops *)0,
NULL,
ddi_quiesce_not_needed, /* quiesce */
};
extern struct mod_ops mod_driverops;
#if SOLARIS2 >= 6
{ 0, 0 },
{ IFT_OTHER, 0 },
{ IFT_1822, 0 },
{ IFT_HDH1822, 0 },
{ IFT_X25DDN, 0 },
{ IFT_X25, 0 },
{ IFT_ETHER, 14 },
{ IFT_ISO88023, 0 },
{ IFT_ISO88024, 0 },
{ IFT_ISO88025, 0 },
{ IFT_ISO88026, 0 },
{ IFT_STARLAN, 0 },
{ IFT_P10, 0 },
{ IFT_P80, 0 },
{ IFT_HY, 0 },
{ IFT_FDDI, 24 },
{ IFT_LAPB, 0 },
{ IFT_SDLC, 0 },
{ IFT_T1, 0 },
{ IFT_CEPT, 0 },
{ IFT_ISDNBASIC, 0 },
{ IFT_ISDNPRIMARY, 0 },
{ IFT_PTPSERIAL, 0 },
{ IFT_PPP, 0 },
{ IFT_LOOP, 0 },
{ IFT_EON, 0 },
{ IFT_XETHER, 0 },
{ IFT_NSIP, 0 },
{ IFT_SLIP, 0 },
{ IFT_ULTRA, 0 },
{ IFT_DS3, 0 },
{ IFT_SIP, 0 },
{ IFT_FRELAY, 0 },
{ IFT_RS232, 0 },
{ IFT_PARA, 0 },
{ IFT_ARCNET, 0 },
{ IFT_ARCNETPLUS, 0 },
{ IFT_ATM, 0 },
{ IFT_MIOX25, 0 },
{ IFT_SONET, 0 },
{ IFT_X25PLE, 0 },
{ IFT_ISO88022LLC, 0 },
{ IFT_LOCALTALK, 0 },
{ IFT_SMDSDXI, 0 },
{ IFT_FRELAYDCE, 0 },
{ IFT_V35, 0 },
{ IFT_HSSI, 0 },
{ IFT_HIPPI, 0 },
{ IFT_MODEM, 0 },
{ IFT_AAL5, 0 },
{ IFT_SONETPATH, 0 },
{ IFT_SONETVT, 0 },
{ IFT_SMDSICIP, 0 },
{ IFT_PROPVIRTUAL, 0 },
{ IFT_PROPMUX, 0 },
};
#endif /* SOLARIS2 >= 6 */
{ "pass", KSTAT_DATA_ULONG },
{ "block", KSTAT_DATA_ULONG },
{ "nomatch", KSTAT_DATA_ULONG },
{ "short", KSTAT_DATA_ULONG },
{ "pass, logged", KSTAT_DATA_ULONG },
{ "block, logged", KSTAT_DATA_ULONG },
{ "nomatch, logged", KSTAT_DATA_ULONG },
{ "logged", KSTAT_DATA_ULONG },
{ "skip", KSTAT_DATA_ULONG },
{ "return sent", KSTAT_DATA_ULONG },
{ "acct", KSTAT_DATA_ULONG },
{ "bad frag state alloc", KSTAT_DATA_ULONG },
{ "new frag state kept", KSTAT_DATA_ULONG },
{ "new frag state compl. pkt", KSTAT_DATA_ULONG },
{ "bad pkt state alloc", KSTAT_DATA_ULONG },
{ "new pkt kept state", KSTAT_DATA_ULONG },
{ "cachehit", KSTAT_DATA_ULONG },
{ "tcp cksum bad", KSTAT_DATA_ULONG },
{{ "pullup ok", KSTAT_DATA_ULONG },
{ "pullup nok", KSTAT_DATA_ULONG }},
{ "src != route", KSTAT_DATA_ULONG },
{ "ttl invalid", KSTAT_DATA_ULONG },
{ "bad ip pkt", KSTAT_DATA_ULONG },
{ "ipv6 pkt", KSTAT_DATA_ULONG },
{ "dropped:pps ceiling", KSTAT_DATA_ULONG },
{ "ip upd. fail", KSTAT_DATA_ULONG }
};
static void
{
sizeof (filter_kstats_t) / sizeof (kstat_named_t), 0);
sizeof (filter_kstats_t));
}
sizeof (filter_kstats_t) / sizeof (kstat_named_t), 0);
sizeof (filter_kstats_t));
}
#ifdef IPFDEBUG
#endif
}
static void
{
int i;
for (i = 0; i < 2; i++) {
}
}
}
static int
{
return (EIO);
if (rwflag == KSTAT_WRITE)
return (EACCES);
return (0);
}
int
_init()
{
int ipfinst;
#ifdef IPFDEBUG
#endif
return (ipfinst);
}
int
_fini(void)
{
int ipfinst;
#ifdef IPFDEBUG
#endif
return (ipfinst);
}
int
{
int ipfinst;
#ifdef IPFDEBUG
#endif
return (ipfinst);
}
#if SOLARIS2 < 10
{
#ifdef IPFDEBUG
#endif
return (DDI_IDENTIFIED);
return (DDI_NOT_IDENTIFIED);
}
#endif
/*
* Initialize things for IPF for each stack instance
*/
static void *
{
#ifdef IPFDEBUG
global);
#endif
/*
* Initialize mutex's
*/
#ifdef IPFDEBUG
#endif
/*
* Lock people out while we set things up.
*/
/* Limit to global stack */
if (ipf_stacks != NULL)
ipf_stacks = ifs;
return (ifs);
}
static void *
{
/*
* Create two ipfilter stacks for a zone - the first can only be
* controlled from the global zone, and the second is owned by
* the zone itself. There is no need to create a GZ-controlled
* stack for the global zone, since we're already in the global
* zone. See the "GZ-controlled and per-zone stacks" comment block in
* ip_fil_solaris.c for details.
*/
if (zid != GLOBAL_ZONEID)
}
/*
* Find an ipfilter stack for the given zone. Return the GZ-controlled or
* per-zone stack if set by an earlier SIOCIPFZONESET ioctl call. See the
* "GZ-controlled and per-zone stacks" comment block in ip_fil_solaris.c for
* details.
*
* This function returns with the ipf_stack_t's ifs_ipf_global
* read lock held (if the stack is found). See the "ipfilter kernel module
* mutexes and locking" comment block at the top of this file.
*/
{
/*
* If we're in the GZ, determine if we're acting on a zone's stack,
* and whether or not that stack is the GZ-controlled or in-zone
* one. See the "GZ and per-zone stacks" note at the top of this
* file.
*/
if (orig_zone == GLOBAL_ZONEID &&
/* Global zone, and we've set the zoneid for this fd already */
/* There's only a per-zone stack for the GZ */
} else {
}
} else {
/*
* Non-global zone or GZ without having set a zoneid: act on
* the per-zone stack of the zone that this ioctl originated
* from.
*/
}
break;
}
}
return (ifs);
}
{
/*
* Make sure we're the only one's modifying things. With
* this lock others should just fall out of the loop.
*/
return (-1);
}
/*
* Make sure there is no active filter rule.
*/
return (-1);
}
return (0);
}
static int ipf_detach_check_all()
{
if (ipf_detach_check_zone(ifs) != 0)
break;
}
/*
* Remove ipf kstats for both the per-zone ipf stack and the
* GZ-controlled stack for the same zone, if it exists.
*/
/* ARGSUSED */
static void
{
/*
* The GZ-controlled stack
*/
/*
* The per-zone stack
*/
}
/*
* Destroy things for ipf for one stack.
*/
/* ARGSUSED */
static void
{
#ifdef IPFDEBUG
#endif
/*
* Make sure we're the only one's modifying things. With
* this lock others should just fall out of the loop.
*/
return;
}
printf("ipf_stack_destroy_one: ipldetach failed\n");
}
}
/*
* Destroy things for ipf for both the per-zone ipf stack and the
* GZ-controlled stack for the same zone, if it exists. See the "GZ-controlled
* and per-zone stacks" comment block in ip_fil_solaris.c for details.
*/
/* ARGSUSED */
static void
{
/*
* The GZ-controlled stack
*/
/*
* The per-zone stack
*/
}
{
char *s;
int i;
int instance;
#ifdef IPFDEBUG
#endif
switch (cmd)
{
case DDI_ATTACH:
/* Only one instance of ipf (instance 0) can be attached. */
if (instance > 0)
return (DDI_FAILURE);
#ifdef IPFDEBUG
#endif
(void) ipf_property_g_update(dip);
!= 0) {
return (DDI_FAILURE);
}
for (i = 0; ((s = ipf_devfiles[i]) != NULL); i++) {
s = strrchr(s, '/');
if (s == NULL)
continue;
s++;
DDI_PSEUDO, 0) == DDI_FAILURE)
goto attach_failed;
}
ipf_dev_info = dip;
goto attach_failed;
goto attach_failed;
}
#ifdef IPFDEBUG
#endif
return (DDI_SUCCESS);
/* NOTREACHED */
default:
break;
}
return (DDI_FAILURE);
}
{
int i;
#ifdef IPFDEBUG
#endif
switch (cmd) {
case DDI_DETACH:
if (ipf_detach_check_all() != 0)
return (DDI_FAILURE);
/*
* Undo what we did in ipf_attach, freeing resources
* and removing things we installed. The system
* framework guarantees we are not active with this devinfo
* node in any other entry points at this time.
*/
i = ddi_get_instance(dip);
if (i > 0) {
return (DDI_FAILURE);
}
(void) net_instance_unregister(ipfncb);
return (DDI_SUCCESS);
/* NOTREACHED */
default:
break;
}
return (DDI_FAILURE);
}
/*ARGSUSED*/
{
int error;
error = DDI_FAILURE;
#ifdef IPFDEBUG
#endif
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
*result = ipf_dev_info;
error = DDI_SUCCESS;
break;
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)0;
error = DDI_SUCCESS;
break;
default:
break;
}
return (error);
}
/*
* Fetch configuration file values that have been entered into the ipf.conf
* driver file.
*/
{
#ifdef DDI_NO_AUTODETACH
return (DDI_FAILURE);
}
#else
return (DDI_FAILURE);
}
#endif
return (DDI_SUCCESS);
}
int
{
char *name;
int *i32p;
one = 1;
if (err == DDI_PROP_NOT_FOUND)
continue;
#ifdef IPFDEBUG
#endif
if (err != DDI_PROP_SUCCESS) {
continue;
}
}
}
}
return (rv);
}