/*
* Copyright (c) 2013 Mindaugas Rasiukevicius <rmind at NetBSD org>
* Copyright (c) 1996 Matthew R. Green
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* pfil_head_get: returns the packer filter head for a given key.
*/
pfil_head_t *
pfil_head_get(int type, void *key)
{
pfil_head_t *ph;
LIST_FOREACH(ph, &pfil_head_list, ph_list) {
if (ph->ph_type == type && ph->ph_key == key)
break;
}
return ph;
}
static pfil_listset_t *
pfil_hook_get(int dir, pfil_head_t *ph)
{
switch (dir) {
case PFIL_IN:
return &ph->ph_in;
case PFIL_OUT:
return &ph->ph_out;
case PFIL_IFADDR:
return &ph->ph_ifaddr;
case PFIL_IFNET:
return &ph->ph_ifevent;
}
return NULL;
}
static int
pfil_list_add(pfil_listset_t *phlistset, pfil_polyfunc_t func, void *arg,
int flags)
{
u_int nhooks;
pfil_list_t *newlist, *oldlist;
pfil_hook_t *pfh;
mutex_enter(&pfil_mtx);
/* Check if we have a free slot. */
nhooks = phlistset->active->nhooks;
if (nhooks == MAX_HOOKS) {
mutex_exit(&pfil_mtx);
return ENOSPC;
}
KASSERT(nhooks < MAX_HOOKS);
/* Make sure the hook is not already added. */
for (u_int i = 0; i < nhooks; i++) {
pfh = &oldlist->hooks[i];
if (pfh->pfil_func == func && pfh->pfil_arg == arg) {
mutex_exit(&pfil_mtx);
return EEXIST;
}
}
/* create new pfil_list_t copied from old */
memcpy(newlist, oldlist, sizeof(pfil_list_t));
psref_target_init(&newlist->psref, pfil_psref_class);
/*
* Finally, add the hook. Note: for PFIL_IN we insert the hooks in
* reverse order of the PFIL_OUT so that the same path is followed
* in or out of the kernel.
*/
if (flags & PFIL_IN) {
/* XXX: May want to revisit this later; */
size_t len = sizeof(pfil_hook_t) * nhooks;
pfh = &newlist->hooks[0];
memmove(&newlist->hooks[1], pfh, len);
} else {
pfh = &newlist->hooks[nhooks];
}
newlist->nhooks++;
pfh->pfil_func = func;
pfh->pfil_arg = arg;
/* switch from oldlist to newlist */
atomic_store_release(&phlistset->active, newlist);
#ifdef NET_MPSAFE
pserialize_perform(pfil_psz);
#endif
mutex_exit(&pfil_mtx);
/* Wait for all readers */
#ifdef NET_MPSAFE
psref_target_destroy(&oldlist->psref, pfil_psref_class);
#endif
return 0;
}
/*
* pfil_add_hook: add a function (hook) to the packet filter head.
* The possible flags are:
*
* PFIL_IN call on incoming packets
* PFIL_OUT call on outgoing packets
* PFIL_ALL call on all of the above
*/
int
pfil_add_hook(pfil_func_t func, void *arg, int flags, pfil_head_t *ph)
{
int error = 0;
/*
* pfil_add_ihook: add an interface-event function (hook) to the packet
* filter head. The possible flags are:
*
* PFIL_IFADDR call on interface reconfig (cmd is ioctl #)
* PFIL_IFNET call on interface attach/detach (cmd is PFIL_IFNET_*)
*/
int
pfil_add_ihook(pfil_ifunc_t func, void *arg, int flags, pfil_head_t *ph)
{
pfil_listset_t *phlistset;