#if defined(KERNEL) || defined(_KERNEL)
# undef KERNEL
# undef _KERNEL
# define KERNEL 1
# define _KERNEL 1
#endif
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/file.h>
#if defined(__FreeBSD__) && defined(_KERNEL)
# include <sys/fcntl.h>
# include <sys/filio.h>
#else
# include <sys/ioctl.h>
#endif
#if !defined(_KERNEL)
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
# define _KERNEL
# include <sys/uio.h>
# undef _KERNEL
#endif
#include <sys/socket.h>
#include <net/if.h>
#if defined(__FreeBSD__)
# include <sys/cdefs.h>
# include <sys/proc.h>
#endif
#if defined(_KERNEL)
# include <sys/systm.h>
# if !defined(__SVR4)
# include <sys/mbuf.h>
# endif
#else
# include "ipf.h"
#endif
#include <netinet/in.h>
#include "netinet/ip_compat.h"
#include "netinet/ip_fil.h"
#include "netinet/ip_lookup.h"
#include "netinet/ip_pool.h"
#include "netinet/ip_htable.h"
#include "netinet/ip_dstlist.h"
static int ipf_lookup_addnode(ipf_main_softc_t *, caddr_t, int);
static int ipf_lookup_delnode(ipf_main_softc_t *, caddr_t, int);
static int ipf_lookup_addtable(ipf_main_softc_t *, caddr_t);
static int ipf_lookup_deltable(ipf_main_softc_t *, caddr_t);
static int ipf_lookup_stats(ipf_main_softc_t *, caddr_t);
static int ipf_lookup_flush(ipf_main_softc_t *, caddr_t);
static int ipf_lookup_iterate(ipf_main_softc_t *, void *, int, void *);
static int ipf_lookup_deltok(ipf_main_softc_t *, void *, int, void *);
#define MAX_BACKENDS 3
static ipf_lookup_t *backends[MAX_BACKENDS] = {
&ipf_pool_backend,
&ipf_htable_backend,
&ipf_dstlist_backend
};
typedef struct ipf_lookup_softc_s {
void *ipf_back[MAX_BACKENDS];
} ipf_lookup_softc_t;
void *
ipf_lookup_soft_create(ipf_main_softc_t *softc)
{
ipf_lookup_softc_t *softl;
ipf_lookup_t **l;
int i;
KMALLOC(softl, ipf_lookup_softc_t *);
if (softl == NULL)
return (NULL);
bzero((char *)softl, sizeof(*softl));
for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) {
softl->ipf_back[i] = (*(*l)->ipfl_create)(softc);
if (softl->ipf_back[i] == NULL) {
ipf_lookup_soft_destroy(softc, softl);
return (NULL);
}
}
return (softl);
}
int
ipf_lookup_soft_init(ipf_main_softc_t *softc, void *arg)
{
ipf_lookup_softc_t *softl = (ipf_lookup_softc_t *)arg;
int err = 0;
int i;
for (i = 0; i < MAX_BACKENDS; i++) {
err = (*backends[i]->ipfl_init)(softc, softl->ipf_back[i]);
if (err != 0)
break;
}
return (err);
}
int
ipf_lookup_soft_fini(ipf_main_softc_t *softc, void *arg)
{
ipf_lookup_softc_t *softl = (ipf_lookup_softc_t *)arg;
int i;
for (i = 0; i < MAX_BACKENDS; i++) {
if (softl->ipf_back[i] != NULL)
(*backends[i]->ipfl_fini)(softc,
softl->ipf_back[i]);
}
return (0);
}
void
ipf_lookup_expire(ipf_main_softc_t *softc)
{
ipf_lookup_softc_t *softl = softc->ipf_lookup_soft;
int i;
WRITE_ENTER(&softc->ipf_poolrw);
for (i = 0; i < MAX_BACKENDS; i++)
(*backends[i]->ipfl_expire)(softc, softl->ipf_back[i]);
RWLOCK_EXIT(&softc->ipf_poolrw);
}
void
ipf_lookup_soft_destroy(ipf_main_softc_t *softc, void *arg)
{
ipf_lookup_softc_t *softl = (ipf_lookup_softc_t *)arg;
int i;
for (i = 0; i < MAX_BACKENDS; i++) {
if (softl->ipf_back[i] != NULL)
(*backends[i]->ipfl_destroy)(softc,
softl->ipf_back[i]);
}
KFREE(softl);
}
int
ipf_lookup_ioctl(ipf_main_softc_t *softc, caddr_t data, ioctlcmd_t cmd,
int mode __unused, int uid, void *ctx)
{
int err;
SPL_INT(s);
SPL_NET(s);
switch (cmd)
{
case SIOCLOOKUPADDNODE :
case SIOCLOOKUPADDNODEW :
WRITE_ENTER(&softc->ipf_poolrw);
err = ipf_lookup_addnode(softc, data, uid);
RWLOCK_EXIT(&softc->ipf_poolrw);
break;
case SIOCLOOKUPDELNODE :
case SIOCLOOKUPDELNODEW :
WRITE_ENTER(&softc->ipf_poolrw);
err = ipf_lookup_delnode(softc, data, uid);
RWLOCK_EXIT(&softc->ipf_poolrw);
break;
case SIOCLOOKUPADDTABLE :
WRITE_ENTER(&softc->ipf_poolrw);
err = ipf_lookup_addtable(softc, data);
RWLOCK_EXIT(&softc->ipf_poolrw);
break;
case SIOCLOOKUPDELTABLE :
WRITE_ENTER(&softc->ipf_poolrw);
err = ipf_lookup_deltable(softc, data);
RWLOCK_EXIT(&softc->ipf_poolrw);
break;
case SIOCLOOKUPSTAT :
case SIOCLOOKUPSTATW :
WRITE_ENTER(&softc->ipf_poolrw);
err = ipf_lookup_stats(softc, data);
RWLOCK_EXIT(&softc->ipf_poolrw);
break;
case SIOCLOOKUPFLUSH :
WRITE_ENTER(&softc->ipf_poolrw);
err = ipf_lookup_flush(softc, data);
RWLOCK_EXIT(&softc->ipf_poolrw);
break;
case SIOCLOOKUPITER :
err = ipf_lookup_iterate(softc, data, uid, ctx);
break;
case SIOCIPFDELTOK :
err = ipf_lookup_deltok(softc, data, uid, ctx);
break;
default :
IPFERROR(50001);
err = EINVAL;
break;
}
SPL_X(s);
return (err);
}
static int
ipf_lookup_addnode(ipf_main_softc_t *softc, caddr_t data, int uid)
{
ipf_lookup_softc_t *softl = softc->ipf_lookup_soft;
iplookupop_t op;
ipf_lookup_t **l;
int err;
int i;
err = BCOPYIN(data, &op, sizeof(op));
if (err != 0) {
IPFERROR(50002);
return (EFAULT);
}
if ((op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) &&
(op.iplo_unit != IPLT_ALL)) {
IPFERROR(50003);
return (EINVAL);
}
op.iplo_name[sizeof(op.iplo_name) - 1] = '\0';
for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) {
if (op.iplo_type == (*l)->ipfl_type) {
err = (*(*l)->ipfl_node_add)(softc,
softl->ipf_back[i],
&op, uid);
break;
}
}
if (i == MAX_BACKENDS) {
IPFERROR(50012);
err = EINVAL;
}
return (err);
}
static int
ipf_lookup_delnode(ipf_main_softc_t *softc, caddr_t data, int uid)
{
ipf_lookup_softc_t *softl = softc->ipf_lookup_soft;
iplookupop_t op;
ipf_lookup_t **l;
int err;
int i;
err = BCOPYIN(data, &op, sizeof(op));
if (err != 0) {
IPFERROR(50042);
return (EFAULT);
}
if ((op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) &&
(op.iplo_unit != IPLT_ALL)) {
IPFERROR(50013);
return (EINVAL);
}
op.iplo_name[sizeof(op.iplo_name) - 1] = '\0';
for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) {
if (op.iplo_type == (*l)->ipfl_type) {
err = (*(*l)->ipfl_node_del)(softc, softl->ipf_back[i],
&op, uid);
break;
}
}
if (i == MAX_BACKENDS) {
IPFERROR(50021);
err = EINVAL;
}
return (err);
}
static int
ipf_lookup_addtable(ipf_main_softc_t *softc, caddr_t data)
{
ipf_lookup_softc_t *softl = softc->ipf_lookup_soft;
iplookupop_t op;
ipf_lookup_t **l;
int err, i;
err = BCOPYIN(data, &op, sizeof(op));
if (err != 0) {
IPFERROR(50022);
return (EFAULT);
}
if ((op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) &&
(op.iplo_unit != IPLT_ALL)) {
IPFERROR(50023);
return (EINVAL);
}
op.iplo_name[sizeof(op.iplo_name) - 1] = '\0';
for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) {
if (op.iplo_type == (*l)->ipfl_type) {
err = (*(*l)->ipfl_table_add)(softc,
softl->ipf_back[i],
&op);
break;
}
}
if (i == MAX_BACKENDS) {
IPFERROR(50026);
err = EINVAL;
}
if ((err == 0) && ((op.iplo_arg & LOOKUP_ANON) != 0)) {
err = BCOPYOUT(&op, data, sizeof(op));
if (err != 0) {
IPFERROR(50027);
err = EFAULT;
}
}
return (err);
}
static int
ipf_lookup_deltable(ipf_main_softc_t *softc, caddr_t data)
{
ipf_lookup_softc_t *softl = softc->ipf_lookup_soft;
iplookupop_t op;
ipf_lookup_t **l;
int err, i;
err = BCOPYIN(data, &op, sizeof(op));
if (err != 0) {
IPFERROR(50028);
return (EFAULT);
}
if ((op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) &&
(op.iplo_unit != IPLT_ALL)) {
IPFERROR(50029);
return (EINVAL);
}
op.iplo_name[sizeof(op.iplo_name) - 1] = '\0';
for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) {
if (op.iplo_type == (*l)->ipfl_type) {
err = (*(*l)->ipfl_table_del)(softc,
softl->ipf_back[i],
&op);
break;
}
}
if (i == MAX_BACKENDS) {
IPFERROR(50030);
err = EINVAL;
}
return (err);
}
static int
ipf_lookup_stats(ipf_main_softc_t *softc, caddr_t data)
{
ipf_lookup_softc_t *softl = softc->ipf_lookup_soft;
iplookupop_t op;
ipf_lookup_t **l;
int err;
int i;
err = BCOPYIN(data, &op, sizeof(op));
if (err != 0) {
IPFERROR(50031);
return (EFAULT);
}
if ((op.iplo_unit < 0 || op.iplo_unit > IPL_LOGMAX) &&
(op.iplo_unit != IPLT_ALL)) {
IPFERROR(50032);
return (EINVAL);
}
for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) {
if (op.iplo_type == (*l)->ipfl_type) {
err = (*(*l)->ipfl_stats_get)(softc,
softl->ipf_back[i],
&op);
break;
}
}
if (i == MAX_BACKENDS) {
IPFERROR(50033);
err = EINVAL;
}
return (err);
}
static int
ipf_lookup_flush(ipf_main_softc_t *softc, caddr_t data)
{
ipf_lookup_softc_t *softl = softc->ipf_lookup_soft;
int err, unit, num, type, i;
iplookupflush_t flush;
ipf_lookup_t **l;
err = BCOPYIN(data, &flush, sizeof(flush));
if (err != 0) {
IPFERROR(50034);
return (EFAULT);
}
unit = flush.iplf_unit;
if ((unit < 0 || unit > IPL_LOGMAX) && (unit != IPLT_ALL)) {
IPFERROR(50035);
return (EINVAL);
}
flush.iplf_name[sizeof(flush.iplf_name) - 1] = '\0';
type = flush.iplf_type;
IPFERROR(50036);
err = EINVAL;
num = 0;
for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) {
if (type == (*l)->ipfl_type || type == IPLT_ALL) {
err = 0;
num += (*(*l)->ipfl_flush)(softc,
softl->ipf_back[i],
&flush);
}
}
if (err == 0) {
flush.iplf_count = num;
err = BCOPYOUT(&flush, data, sizeof(flush));
if (err != 0) {
IPFERROR(50037);
err = EFAULT;
}
}
return (err);
}
void
ipf_lookup_deref(ipf_main_softc_t *softc, int type, void *ptr)
{
ipf_lookup_softc_t *softl = softc->ipf_lookup_soft;
int i;
if (ptr == NULL)
return;
for (i = 0; i < MAX_BACKENDS; i++) {
if (type == backends[i]->ipfl_type) {
WRITE_ENTER(&softc->ipf_poolrw);
(*backends[i]->ipfl_table_deref)(softc,
softl->ipf_back[i],
ptr);
RWLOCK_EXIT(&softc->ipf_poolrw);
break;
}
}
}
static int
ipf_lookup_iterate(ipf_main_softc_t *softc, void *data, int uid, void *ctx)
{
ipf_lookup_softc_t *softl = softc->ipf_lookup_soft;
ipflookupiter_t iter;
ipftoken_t *token;
int err, i;
SPL_INT(s);
err = ipf_inobj(softc, data, NULL, &iter, IPFOBJ_LOOKUPITER);
if (err != 0)
return (err);
if (iter.ili_unit < IPL_LOGALL && iter.ili_unit > IPL_LOGMAX) {
IPFERROR(50038);
return (EINVAL);
}
if (iter.ili_ival != IPFGENITER_LOOKUP) {
IPFERROR(50039);
return (EINVAL);
}
SPL_SCHED(s);
token = ipf_token_find(softc, iter.ili_key, uid, ctx);
if (token == NULL) {
SPL_X(s);
IPFERROR(50040);
return (ESRCH);
}
for (i = 0; i < MAX_BACKENDS; i++) {
if (iter.ili_type == backends[i]->ipfl_type) {
err = (*backends[i]->ipfl_iter_next)(softc,
softl->ipf_back[i],
token, &iter);
break;
}
}
SPL_X(s);
if (i == MAX_BACKENDS) {
IPFERROR(50041);
err = EINVAL;
}
WRITE_ENTER(&softc->ipf_tokens);
ipf_token_deref(softc, token);
RWLOCK_EXIT(&softc->ipf_tokens);
return (err);
}
void
ipf_lookup_iterderef(ipf_main_softc_t *softc, u_32_t type, void *data)
{
ipf_lookup_softc_t *softl = softc->ipf_lookup_soft;
struct iplookupiterkey *lkey;
iplookupiterkey_t key;
int i;
key.ilik_key = type;
lkey = &key.ilik_unstr;
if (lkey->ilik_ival != IPFGENITER_LOOKUP)
return;
WRITE_ENTER(&softc->ipf_poolrw);
for (i = 0; i < MAX_BACKENDS; i++) {
if (lkey->ilik_type == backends[i]->ipfl_type) {
(*backends[i]->ipfl_iter_deref)(softc,
softl->ipf_back[i],
lkey->ilik_otype,
lkey->ilik_unit,
data);
break;
}
}
RWLOCK_EXIT(&softc->ipf_poolrw);
}
int
ipf_lookup_deltok(ipf_main_softc_t *softc, void *data, int uid, void *ctx)
{
int error, key;
SPL_INT(s);
SPL_SCHED(s);
error = BCOPYIN(data, &key, sizeof(key));
if (error == 0)
error = ipf_token_del(softc, key, uid, ctx);
SPL_X(s);
return (error);
}
void *
ipf_lookup_res_num(ipf_main_softc_t *softc, int unit, u_int type, u_int number,
lookupfunc_t *funcptr)
{
char name[FR_GROUPLEN];
(void) snprintf(name, sizeof(name), "%u", number);
return (ipf_lookup_res_name(softc, unit, type, name, funcptr));
}
void *
ipf_lookup_res_name(ipf_main_softc_t *softc, int unit, u_int type, char *name,
lookupfunc_t *funcptr)
{
ipf_lookup_softc_t *softl = softc->ipf_lookup_soft;
ipf_lookup_t **l;
void *ptr = NULL;
int i;
READ_ENTER(&softc->ipf_poolrw);
for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++) {
if (type == (*l)->ipfl_type) {
ptr = (*(*l)->ipfl_select_add_ref)(softl->ipf_back[i],
unit, name);
if (ptr != NULL && funcptr != NULL) {
*funcptr = (*l)->ipfl_addr_find;
}
break;
}
}
if (i == MAX_BACKENDS) {
ptr = NULL;
if (funcptr != NULL)
*funcptr = NULL;
}
RWLOCK_EXIT(&softc->ipf_poolrw);
return (ptr);
}
void *
ipf_lookup_find_htable(ipf_main_softc_t *softc, int unit, char *name)
{
ipf_lookup_softc_t *softl = softc->ipf_lookup_soft;
ipf_lookup_t **l;
void *tab = NULL;
int i;
READ_ENTER(&softc->ipf_poolrw);
for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++)
if (IPLT_HASH == (*l)->ipfl_type) {
tab = ipf_htable_find(softl->ipf_back[i], unit, name);
break;
}
RWLOCK_EXIT(&softc->ipf_poolrw);
return (tab);
}
void
ipf_lookup_sync(ipf_main_softc_t *softc, void *ifp)
{
ipf_lookup_softc_t *softl = softc->ipf_lookup_soft;
ipf_lookup_t **l;
int i;
READ_ENTER(&softc->ipf_poolrw);
for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++)
if ((*l)->ipfl_sync != NULL)
(*(*l)->ipfl_sync)(softc, softl->ipf_back[i]);
RWLOCK_EXIT(&softc->ipf_poolrw);
}
#ifndef _KERNEL
void
ipf_lookup_dump(ipf_main_softc_t *softc, void *arg)
{
ipf_lookup_softc_t *softl = softc->ipf_lookup_soft;
ipf_lookup_t **l;
int i;
for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++)
if (IPLT_POOL == (*l)->ipfl_type) {
ipf_pool_dump(softc, softl->ipf_back[i]);
break;
}
for (i = 0, l = backends; i < MAX_BACKENDS; i++, l++)
if (IPLT_HASH == (*l)->ipfl_type) {
ipf_htable_dump(softc, softl->ipf_back[i]);
break;
}
}
#endif