#if defined(KERNEL) || defined(_KERNEL)
# undef KERNEL
# undef _KERNEL
# define KERNEL 1
# define _KERNEL 1
#endif
#include <sys/errno.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/file.h>
#if !defined(_KERNEL) && !defined(__KERNEL__)
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# define _KERNEL
# include <sys/uio.h>
# undef _KERNEL
#else
# include <sys/systm.h>
# if defined(NetBSD) && (__NetBSD_Version__ >= 104000000)
# include <sys/proc.h>
# endif
#endif
#include <sys/time.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#if defined(_KERNEL) && !defined(__SVR4)
# include <sys/mbuf.h>
#endif
#if defined(__SVR4)
# include <sys/filio.h>
# include <sys/byteorder.h>
# ifdef _KERNEL
# include <sys/dditypes.h>
# endif
# include <sys/stream.h>
# include <sys/kmem.h>
#endif
#if defined(__FreeBSD__)
# include <sys/malloc.h>
#endif
#include <net/if.h>
#include <netinet/in.h>
#include "netinet/ip_compat.h"
#include "netinet/ip_fil.h"
#include "netinet/ip_nat.h"
#include "netinet/ip_lookup.h"
#include "netinet/ip_dstlist.h"
#ifdef HAS_SYS_MD5_H
# include <sys/md5.h>
#else
# include "md5.h"
#endif
typedef struct ipf_dstl_softc_s {
ippool_dst_t *dstlist[LOOKUP_POOL_SZ];
ippool_dst_t **tails[LOOKUP_POOL_SZ];
ipf_dstl_stat_t stats;
} ipf_dstl_softc_t;
static void *ipf_dstlist_soft_create(ipf_main_softc_t *);
static void ipf_dstlist_soft_destroy(ipf_main_softc_t *, void *);
static int ipf_dstlist_soft_init(ipf_main_softc_t *, void *);
static void ipf_dstlist_soft_fini(ipf_main_softc_t *, void *);
static int ipf_dstlist_addr_find(ipf_main_softc_t *, void *, int,
void *, u_int);
static size_t ipf_dstlist_flush(ipf_main_softc_t *, void *,
iplookupflush_t *);
static int ipf_dstlist_iter_deref(ipf_main_softc_t *, void *, int, int,
void *);
static int ipf_dstlist_iter_next(ipf_main_softc_t *, void *, ipftoken_t *,
ipflookupiter_t *);
static int ipf_dstlist_node_add(ipf_main_softc_t *, void *,
iplookupop_t *, int);
static int ipf_dstlist_node_del(ipf_main_softc_t *, void *,
iplookupop_t *, int);
static int ipf_dstlist_stats_get(ipf_main_softc_t *, void *,
iplookupop_t *);
static int ipf_dstlist_table_add(ipf_main_softc_t *, void *,
iplookupop_t *);
static int ipf_dstlist_table_del(ipf_main_softc_t *, void *,
iplookupop_t *);
static int ipf_dstlist_table_deref(ipf_main_softc_t *, void *, void *);
static void *ipf_dstlist_table_find(void *, int, char *);
static void ipf_dstlist_table_free(ipf_dstl_softc_t *, ippool_dst_t *);
static void ipf_dstlist_table_remove(ipf_main_softc_t *,
ipf_dstl_softc_t *, ippool_dst_t *);
static void ipf_dstlist_table_clearnodes(ipf_dstl_softc_t *,
ippool_dst_t *);
static ipf_dstnode_t *ipf_dstlist_select(fr_info_t *, ippool_dst_t *);
static void *ipf_dstlist_select_ref(void *, int, char *);
static void ipf_dstlist_node_free(ipf_dstl_softc_t *, ippool_dst_t *, ipf_dstnode_t *);
static int ipf_dstlist_node_deref(void *, ipf_dstnode_t *);
static void ipf_dstlist_expire(ipf_main_softc_t *, void *);
static void ipf_dstlist_sync(ipf_main_softc_t *, void *);
ipf_lookup_t ipf_dstlist_backend = {
IPLT_DSTLIST,
ipf_dstlist_soft_create,
ipf_dstlist_soft_destroy,
ipf_dstlist_soft_init,
ipf_dstlist_soft_fini,
ipf_dstlist_addr_find,
ipf_dstlist_flush,
ipf_dstlist_iter_deref,
ipf_dstlist_iter_next,
ipf_dstlist_node_add,
ipf_dstlist_node_del,
ipf_dstlist_stats_get,
ipf_dstlist_table_add,
ipf_dstlist_table_del,
ipf_dstlist_table_deref,
ipf_dstlist_table_find,
ipf_dstlist_select_ref,
ipf_dstlist_select_node,
ipf_dstlist_expire,
ipf_dstlist_sync
};
static void *
ipf_dstlist_soft_create(ipf_main_softc_t *softc)
{
ipf_dstl_softc_t *softd;
int i;
KMALLOC(softd, ipf_dstl_softc_t *);
if (softd == NULL) {
IPFERROR(120028);
return (NULL);
}
bzero((char *)softd, sizeof(*softd));
for (i = 0; i <= IPL_LOGMAX; i++)
softd->tails[i] = &softd->dstlist[i];
return (softd);
}
static void
ipf_dstlist_soft_destroy(ipf_main_softc_t *softc, void *arg)
{
ipf_dstl_softc_t *softd = arg;
KFREE(softd);
}
static int
ipf_dstlist_soft_init(ipf_main_softc_t *softc, void *arg)
{
return (0);
}
static void
ipf_dstlist_soft_fini(ipf_main_softc_t *softc, void *arg)
{
ipf_dstl_softc_t *softd = arg;
int i;
for (i = -1; i <= IPL_LOGMAX; i++) {
while (softd->dstlist[i + 1] != NULL) {
ipf_dstlist_table_remove(softc, softd,
softd->dstlist[i + 1]);
}
}
ASSERT(softd->stats.ipls_numderefnodes == 0);
}
static int
ipf_dstlist_addr_find(ipf_main_softc_t *softc, void *arg1, int arg2,
void *arg3, u_int arg4)
{
return (-1);
}
static size_t
ipf_dstlist_flush(ipf_main_softc_t *softc, void *arg, iplookupflush_t *fop)
{
ipf_dstl_softc_t *softd = arg;
ippool_dst_t *node, *next;
int n, i;
for (n = 0, i = -1; i <= IPL_LOGMAX; i++) {
if (fop->iplf_unit != IPLT_ALL && fop->iplf_unit != i)
continue;
for (node = softd->dstlist[i + 1]; node != NULL; node = next) {
next = node->ipld_next;
if ((*fop->iplf_name != '\0') &&
strncmp(fop->iplf_name, node->ipld_name,
FR_GROUPLEN))
continue;
ipf_dstlist_table_remove(softc, softd, node);
n++;
}
}
return (n);
}
static int
ipf_dstlist_iter_deref(ipf_main_softc_t *softc, void *arg, int otype,
int unit, void *data)
{
if (data == NULL) {
IPFERROR(120001);
return (EINVAL);
}
if (unit < -1 || unit > IPL_LOGMAX) {
IPFERROR(120002);
return (EINVAL);
}
switch (otype)
{
case IPFLOOKUPITER_LIST :
ipf_dstlist_table_deref(softc, arg, (ippool_dst_t *)data);
break;
case IPFLOOKUPITER_NODE :
ipf_dstlist_node_deref(arg, (ipf_dstnode_t *)data);
break;
}
return (0);
}
static int
ipf_dstlist_iter_next(ipf_main_softc_t *softc, void *arg,
ipftoken_t *token, ipflookupiter_t *iter)
{
ipf_dstnode_t zn, *nextnode = NULL, *node = NULL;
ippool_dst_t zero, *next = NULL, *dsttab = NULL;
ipf_dstl_softc_t *softd = arg;
int err = 0;
void *hint;
switch (iter->ili_otype)
{
case IPFLOOKUPITER_LIST :
dsttab = token->ipt_data;
if (dsttab == NULL) {
next = softd->dstlist[(int)iter->ili_unit + 1];
} else {
next = dsttab->ipld_next;
}
if (next != NULL) {
ATOMIC_INC32(next->ipld_ref);
token->ipt_data = next;
hint = next->ipld_next;
} else {
bzero((char *)&zero, sizeof(zero));
next = &zero;
token->ipt_data = NULL;
hint = NULL;
}
break;
case IPFLOOKUPITER_NODE :
node = token->ipt_data;
if (node == NULL) {
dsttab = ipf_dstlist_table_find(arg, iter->ili_unit,
iter->ili_name);
if (dsttab == NULL) {
IPFERROR(120004);
err = ESRCH;
nextnode = NULL;
} else {
if (dsttab->ipld_dests == NULL)
nextnode = NULL;
else
nextnode = *dsttab->ipld_dests;
dsttab = NULL;
}
} else {
nextnode = node->ipfd_next;
}
if (nextnode != NULL) {
MUTEX_ENTER(&nextnode->ipfd_lock);
nextnode->ipfd_ref++;
MUTEX_EXIT(&nextnode->ipfd_lock);
token->ipt_data = nextnode;
hint = nextnode->ipfd_next;
} else {
bzero((char *)&zn, sizeof(zn));
nextnode = &zn;
token->ipt_data = NULL;
hint = NULL;
}
break;
default :
IPFERROR(120003);
err = EINVAL;
break;
}
if (err != 0)
return (err);
switch (iter->ili_otype)
{
case IPFLOOKUPITER_LIST :
if (dsttab != NULL)
ipf_dstlist_table_deref(softc, arg, dsttab);
err = COPYOUT(next, iter->ili_data, sizeof(*next));
if (err != 0) {
IPFERROR(120005);
err = EFAULT;
}
break;
case IPFLOOKUPITER_NODE :
if (node != NULL)
ipf_dstlist_node_deref(arg, node);
err = COPYOUT(nextnode, iter->ili_data, sizeof(*nextnode));
if (err != 0) {
IPFERROR(120006);
err = EFAULT;
}
break;
}
if (hint == NULL)
ipf_token_mark_complete(token);
return (err);
}
static int
ipf_dstlist_node_add(ipf_main_softc_t *softc, void *arg,
iplookupop_t *op, int uid)
{
ipf_dstl_softc_t *softd = arg;
ipf_dstnode_t *node, **nodes;
ippool_dst_t *d;
frdest_t dest;
int err;
if (op->iplo_size < sizeof(frdest_t)) {
IPFERROR(120007);
return (EINVAL);
}
err = COPYIN(op->iplo_struct, &dest, sizeof(dest));
if (err != 0) {
IPFERROR(120009);
return (EFAULT);
}
d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
if (d == NULL) {
IPFERROR(120010);
return (ESRCH);
}
switch (dest.fd_addr.adf_family)
{
case AF_INET :
case AF_INET6 :
break;
default :
IPFERROR(120019);
return (EINVAL);
}
if (dest.fd_name < -1 || dest.fd_name > 128) {
IPFERROR(120018);
return (EINVAL);
}
KMALLOCS(node, ipf_dstnode_t *, sizeof(*node) + dest.fd_name);
if (node == NULL) {
softd->stats.ipls_nomem++;
IPFERROR(120008);
return (ENOMEM);
}
bzero((char *)node, sizeof(*node) + dest.fd_name);
bcopy(&dest, &node->ipfd_dest, sizeof(dest));
node->ipfd_size = sizeof(*node) + dest.fd_name;
if (dest.fd_name > 0) {
err = COPYIN((char *)op->iplo_struct + sizeof(dest),
node->ipfd_names, dest.fd_name);
if (err != 0) {
IPFERROR(120017);
KFREES(node, node->ipfd_size);
return (EFAULT);
}
node->ipfd_dest.fd_name = 0;
} else {
node->ipfd_dest.fd_name = -1;
}
if (d->ipld_nodes == d->ipld_maxnodes) {
KMALLOCS(nodes, ipf_dstnode_t **,
sizeof(*nodes) * (d->ipld_maxnodes + 1));
if (nodes == NULL) {
softd->stats.ipls_nomem++;
IPFERROR(120022);
KFREES(node, node->ipfd_size);
return (ENOMEM);
}
if (d->ipld_dests != NULL) {
bcopy(d->ipld_dests, nodes,
sizeof(*nodes) * d->ipld_maxnodes);
KFREES(d->ipld_dests, sizeof(*nodes) * d->ipld_nodes);
nodes[0]->ipfd_pnext = nodes;
}
d->ipld_dests = nodes;
d->ipld_maxnodes++;
}
d->ipld_dests[d->ipld_nodes] = node;
d->ipld_nodes++;
if (d->ipld_nodes == 1) {
node->ipfd_pnext = d->ipld_dests;
} else if (d->ipld_nodes > 1) {
node->ipfd_pnext = &d->ipld_dests[d->ipld_nodes - 2]->ipfd_next;
}
*node->ipfd_pnext = node;
MUTEX_INIT(&node->ipfd_lock, "ipf dst node lock");
node->ipfd_uid = uid;
node->ipfd_ref = 1;
if (node->ipfd_dest.fd_name == 0)
(void) ipf_resolvedest(softc, node->ipfd_names,
&node->ipfd_dest, AF_INET);
#ifdef USE_INET6
if (node->ipfd_dest.fd_name == 0 &&
node->ipfd_dest.fd_ptr == (void *)-1)
(void) ipf_resolvedest(softc, node->ipfd_names,
&node->ipfd_dest, AF_INET6);
#endif
softd->stats.ipls_numnodes++;
return (0);
}
static int
ipf_dstlist_node_deref(void *arg, ipf_dstnode_t *node)
{
ipf_dstl_softc_t *softd = arg;
int ref;
MUTEX_ENTER(&node->ipfd_lock);
ref = --node->ipfd_ref;
MUTEX_EXIT(&node->ipfd_lock);
if (ref > 0)
return (0);
if ((node->ipfd_flags & IPDST_DELETE) != 0)
softd->stats.ipls_numderefnodes--;
MUTEX_DESTROY(&node->ipfd_lock);
KFREES(node, node->ipfd_size);
softd->stats.ipls_numnodes--;
return (0);
}
static int
ipf_dstlist_node_del(ipf_main_softc_t *softc, void *arg, iplookupop_t *op,
int uid)
{
ipf_dstl_softc_t *softd = arg;
ipf_dstnode_t *node;
frdest_t frd, *temp;
ippool_dst_t *d;
size_t size;
int err;
d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
if (d == NULL) {
IPFERROR(120012);
return (ESRCH);
}
err = COPYIN(op->iplo_struct, &frd, sizeof(frd));
if (err != 0) {
IPFERROR(120011);
return (EFAULT);
}
size = sizeof(*temp) + frd.fd_name;
KMALLOCS(temp, frdest_t *, size);
if (temp == NULL) {
softd->stats.ipls_nomem++;
IPFERROR(120026);
return (ENOMEM);
}
err = COPYIN(op->iplo_struct, temp, size);
if (err != 0) {
IPFERROR(120027);
KFREES(temp, size);
return (EFAULT);
}
MUTEX_ENTER(&d->ipld_lock);
for (node = *d->ipld_dests; node != NULL; node = node->ipfd_next) {
if ((uid != 0) && (node->ipfd_uid != uid))
continue;
if (node->ipfd_size != size)
continue;
if (!bcmp(&node->ipfd_dest.fd_ip6, &frd.fd_ip6,
size - offsetof(frdest_t, fd_ip6))) {
ipf_dstlist_node_free(softd, d, node);
MUTEX_EXIT(&d->ipld_lock);
KFREES(temp, size);
return (0);
}
}
MUTEX_EXIT(&d->ipld_lock);
KFREES(temp, size);
return (ESRCH);
}
static void
ipf_dstlist_node_free(ipf_dstl_softc_t *softd, ippool_dst_t *d,
ipf_dstnode_t *node)
{
int i;
for (i = 0; i < d->ipld_nodes; i++)
if (d->ipld_dests[i] == node)
break;
if (d->ipld_nodes - i > 1) {
bcopy(&d->ipld_dests[i + 1], &d->ipld_dests[i],
sizeof(*d->ipld_dests) * (d->ipld_nodes - i - 1));
}
d->ipld_nodes--;
if (node->ipfd_pnext != NULL)
*node->ipfd_pnext = node->ipfd_next;
if (node->ipfd_next != NULL)
node->ipfd_next->ipfd_pnext = node->ipfd_pnext;
node->ipfd_pnext = NULL;
node->ipfd_next = NULL;
if ((node->ipfd_flags & IPDST_DELETE) == 0) {
softd->stats.ipls_numderefnodes++;
node->ipfd_flags |= IPDST_DELETE;
}
ipf_dstlist_node_deref(softd, node);
}
static int
ipf_dstlist_stats_get(ipf_main_softc_t *softc, void *arg, iplookupop_t *op)
{
ipf_dstl_softc_t *softd = arg;
ipf_dstl_stat_t stats;
int unit, i, err = 0;
if (op->iplo_size != sizeof(ipf_dstl_stat_t)) {
IPFERROR(120023);
return (EINVAL);
}
stats = softd->stats;
unit = op->iplo_unit;
if (unit == IPL_LOGALL) {
for (i = 0; i <= IPL_LOGMAX; i++)
stats.ipls_list[i] = softd->dstlist[i];
} else if (unit >= 0 && unit <= IPL_LOGMAX) {
void *ptr;
if (op->iplo_name[0] != '\0')
ptr = ipf_dstlist_table_find(softd, unit,
op->iplo_name);
else
ptr = softd->dstlist[unit + 1];
stats.ipls_list[unit] = ptr;
} else {
IPFERROR(120024);
err = EINVAL;
}
if (err == 0) {
err = COPYOUT(&stats, op->iplo_struct, sizeof(stats));
if (err != 0) {
IPFERROR(120025);
return (EFAULT);
}
}
return (0);
}
static int
ipf_dstlist_table_add(ipf_main_softc_t *softc, void *arg, iplookupop_t *op)
{
ipf_dstl_softc_t *softd = arg;
ippool_dst_t user, *d, *new;
int unit, err;
d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
if (d != NULL) {
IPFERROR(120013);
return (EEXIST);
}
err = COPYIN(op->iplo_struct, &user, sizeof(user));
if (err != 0) {
IPFERROR(120021);
return (EFAULT);
}
KMALLOC(new, ippool_dst_t *);
if (new == NULL) {
softd->stats.ipls_nomem++;
IPFERROR(120014);
return (ENOMEM);
}
bzero((char *)new, sizeof(*new));
MUTEX_INIT(&new->ipld_lock, "ipf dst table lock");
strncpy(new->ipld_name, op->iplo_name, FR_GROUPLEN);
unit = op->iplo_unit;
new->ipld_unit = unit;
new->ipld_policy = user.ipld_policy;
new->ipld_seed = ipf_random();
new->ipld_ref = 1;
new->ipld_pnext = softd->tails[unit + 1];
*softd->tails[unit + 1] = new;
softd->tails[unit + 1] = &new->ipld_next;
softd->stats.ipls_numlists++;
return (0);
}
static int
ipf_dstlist_table_del(ipf_main_softc_t *softc, void *arg, iplookupop_t *op)
{
ippool_dst_t *d;
d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
if (d == NULL) {
IPFERROR(120015);
return (ESRCH);
}
if (d->ipld_dests != NULL) {
IPFERROR(120016);
return (EBUSY);
}
ipf_dstlist_table_remove(softc, arg, d);
return (0);
}
static void
ipf_dstlist_table_remove(ipf_main_softc_t *softc, ipf_dstl_softc_t *softd,
ippool_dst_t *d)
{
if (softd->tails[d->ipld_unit + 1] == &d->ipld_next)
softd->tails[d->ipld_unit + 1] = d->ipld_pnext;
if (d->ipld_pnext != NULL)
*d->ipld_pnext = d->ipld_next;
if (d->ipld_next != NULL)
d->ipld_next->ipld_pnext = d->ipld_pnext;
d->ipld_pnext = NULL;
d->ipld_next = NULL;
ipf_dstlist_table_clearnodes(softd, d);
softd->stats.ipls_numdereflists++;
d->ipld_flags |= IPDST_DELETE;
ipf_dstlist_table_deref(softc, softd, d);
}
static void
ipf_dstlist_table_free(ipf_dstl_softc_t *softd, ippool_dst_t *d)
{
MUTEX_DESTROY(&d->ipld_lock);
if ((d->ipld_flags & IPDST_DELETE) != 0)
softd->stats.ipls_numdereflists--;
softd->stats.ipls_numlists--;
if (d->ipld_dests != NULL) {
KFREES(d->ipld_dests,
d->ipld_maxnodes * sizeof(*d->ipld_dests));
}
KFREE(d);
}
static int
ipf_dstlist_table_deref(ipf_main_softc_t *softc, void *arg, void *table)
{
ippool_dst_t *d = table;
d->ipld_ref--;
if (d->ipld_ref > 0)
return (d->ipld_ref);
ipf_dstlist_table_free(arg, d);
return (0);
}
static void
ipf_dstlist_table_clearnodes(ipf_dstl_softc_t *softd, ippool_dst_t *dst)
{
ipf_dstnode_t *node;
if (dst->ipld_dests == NULL)
return;
while ((node = *dst->ipld_dests) != NULL) {
ipf_dstlist_node_free(softd, dst, node);
}
}
static void *
ipf_dstlist_table_find(void *arg, int unit, char *name)
{
ipf_dstl_softc_t *softd = arg;
ippool_dst_t *d;
for (d = softd->dstlist[unit + 1]; d != NULL; d = d->ipld_next) {
if ((d->ipld_unit == unit) &&
!strncmp(d->ipld_name, name, FR_GROUPLEN)) {
return (d);
}
}
return (NULL);
}
static void *
ipf_dstlist_select_ref(void *arg, int unit, char *name)
{
ippool_dst_t *d;
d = ipf_dstlist_table_find(arg, unit, name);
if (d != NULL) {
MUTEX_ENTER(&d->ipld_lock);
d->ipld_ref++;
MUTEX_EXIT(&d->ipld_lock);
}
return (d);
}
static ipf_dstnode_t *
ipf_dstlist_select(fr_info_t *fin, ippool_dst_t *d)
{
ipf_dstnode_t *node, *sel;
int connects;
u_32_t hash[4];
MD5_CTX ctx;
int family;
int x;
if (d == NULL || d->ipld_dests == NULL || *d->ipld_dests == NULL)
return (NULL);
family = fin->fin_family;
MUTEX_ENTER(&d->ipld_lock);
switch (d->ipld_policy)
{
case IPLDP_ROUNDROBIN:
sel = d->ipld_selected;
if (sel == NULL) {
sel = *d->ipld_dests;
} else {
sel = sel->ipfd_next;
if (sel == NULL)
sel = *d->ipld_dests;
}
break;
case IPLDP_CONNECTION:
if (d->ipld_selected == NULL) {
sel = *d->ipld_dests;
break;
}
sel = d->ipld_selected;
connects = 0x7fffffff;
node = sel->ipfd_next;
if (node == NULL)
node = *d->ipld_dests;
while (node != d->ipld_selected) {
if (node->ipfd_states == 0) {
sel = node;
break;
}
if (node->ipfd_states < connects) {
sel = node;
connects = node->ipfd_states;
}
node = node->ipfd_next;
if (node == NULL)
node = *d->ipld_dests;
}
break;
case IPLDP_RANDOM :
x = ipf_random() % d->ipld_nodes;
sel = d->ipld_dests[x];
break;
case IPLDP_HASHED :
MD5Init(&ctx);
MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed));
MD5Update(&ctx, (u_char *)&fin->fin_src6,
sizeof(fin->fin_src6));
MD5Update(&ctx, (u_char *)&fin->fin_dst6,
sizeof(fin->fin_dst6));
MD5Final((u_char *)hash, &ctx);
x = ntohl(hash[0]) % d->ipld_nodes;
sel = d->ipld_dests[x];
break;
case IPLDP_SRCHASH :
MD5Init(&ctx);
MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed));
MD5Update(&ctx, (u_char *)&fin->fin_src6,
sizeof(fin->fin_src6));
MD5Final((u_char *)hash, &ctx);
x = ntohl(hash[0]) % d->ipld_nodes;
sel = d->ipld_dests[x];
break;
case IPLDP_DSTHASH :
MD5Init(&ctx);
MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed));
MD5Update(&ctx, (u_char *)&fin->fin_dst6,
sizeof(fin->fin_dst6));
MD5Final((u_char *)hash, &ctx);
x = ntohl(hash[0]) % d->ipld_nodes;
sel = d->ipld_dests[x];
break;
default :
sel = NULL;
break;
}
if (sel && sel->ipfd_dest.fd_addr.adf_family != family)
sel = NULL;
d->ipld_selected = sel;
MUTEX_EXIT(&d->ipld_lock);
return (sel);
}
int
ipf_dstlist_select_node(fr_info_t *fin, void *group, u_32_t *addr,
frdest_t *pfdp)
{
#ifdef USE_MUTEXES
ipf_main_softc_t *softc = fin->fin_main_soft;
#endif
ippool_dst_t *d = group;
ipf_dstnode_t *node;
frdest_t *fdp;
READ_ENTER(&softc->ipf_poolrw);
node = ipf_dstlist_select(fin, d);
if (node == NULL) {
RWLOCK_EXIT(&softc->ipf_poolrw);
return (-1);
}
if (pfdp != NULL) {
bcopy(&node->ipfd_dest, pfdp, sizeof(*pfdp));
} else {
if (fin->fin_family == AF_INET) {
addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0];
} else if (fin->fin_family == AF_INET6) {
addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0];
addr[1] = node->ipfd_dest.fd_addr.adf_addr.i6[1];
addr[2] = node->ipfd_dest.fd_addr.adf_addr.i6[2];
addr[3] = node->ipfd_dest.fd_addr.adf_addr.i6[3];
}
}
fdp = &node->ipfd_dest;
if (fdp->fd_ptr == NULL)
fdp->fd_ptr = fin->fin_ifp;
MUTEX_ENTER(&node->ipfd_lock);
node->ipfd_states++;
MUTEX_EXIT(&node->ipfd_lock);
RWLOCK_EXIT(&softc->ipf_poolrw);
return (0);
}
static void
ipf_dstlist_expire(ipf_main_softc_t *softc, void *arg)
{
return;
}
void
ipf_dstlist_sync(ipf_main_softc_t *softc, void *arg)
{
ipf_dstl_softc_t *softd = arg;
ipf_dstnode_t *node;
ippool_dst_t *list;
int i;
int j;
for (i = 0; i < IPL_LOGMAX; i++) {
for (list = softd->dstlist[i]; list != NULL;
list = list->ipld_next) {
for (j = 0; j < list->ipld_maxnodes; j++) {
node = list->ipld_dests[j];
if (node == NULL)
continue;
if (node->ipfd_dest.fd_name == -1)
continue;
(void) ipf_resolvedest(softc,
node->ipfd_names,
&node->ipfd_dest,
AF_INET);
}
}
}
}