1315 lines
34 KiB
C
1315 lines
34 KiB
C
/*
|
|
* Copyright (c) 2006-2011 the Boeing Company
|
|
* ng_wlan is based on ng_hub, which is:
|
|
* Copyright (c) 2004 Ruslan Ermilov
|
|
* 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
|
|
*
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/errno.h>
|
|
#ifdef MULTICAST_LOOKUPS
|
|
#include <netinet/in_systm.h> /* in.h */
|
|
#include <netinet/in.h> /* IN_MULTICAST(), etc */
|
|
#include <netinet/ip.h> /* struct ip */
|
|
#include <net/ethernet.h> /* struct ether_header */
|
|
#endif /* MULTICAST_LOOKUPS */
|
|
|
|
#include <netgraph/ng_message.h>
|
|
#include <netgraph/ng_parse.h>
|
|
#include <netgraph/netgraph.h>
|
|
/* #include <netgraph/ng_wlan.h> */
|
|
#include "ng_wlan.h"
|
|
#include "ng_wlan_tag.h"
|
|
|
|
#ifdef NG_SEPARATE_MALLOC
|
|
MALLOC_DEFINE(M_NETGRAPH_WLAN, "netgraph_wlan", "netgraph WLAN node ");
|
|
#else
|
|
#define M_NETGRAPH_WLAN M_NETGRAPH
|
|
#endif
|
|
|
|
#ifdef WLAN_GIANT_LOCK
|
|
struct mtx ng_wlan_giant;
|
|
#endif
|
|
|
|
#ifdef MULTICAST_LOOKUPS
|
|
#define mtod_off(m,off,t) ((t)(mtod((m),caddr_t)+(off)))
|
|
#define IP_MCAST_HDR_OFFSET ETHER_HDR_LEN
|
|
#define IP_MCAST_MIN_LEN (IP_MCAST_HDR_OFFSET + sizeof(struct ip))
|
|
#endif /* MULTICAST_LOOKUPS */
|
|
|
|
/*
|
|
* WLAN node data types
|
|
*/
|
|
/* Hash table entry for wlan connectivity */
|
|
struct ng_wlan_hent {
|
|
ng_ID_t l_id;
|
|
ng_ID_t g_id;
|
|
int linked;
|
|
u_int64_t delay;
|
|
u_int64_t bandwidth;
|
|
u_int16_t per;
|
|
u_int16_t duplicate;
|
|
u_int32_t jitter;
|
|
u_int16_t burst;
|
|
SLIST_ENTRY(ng_wlan_hent) next;
|
|
};
|
|
|
|
/* Hash table bucket declaration */
|
|
/* struct ng_wlan_bucket {
|
|
struct ng_wlan_hent *slh_first;
|
|
};*/
|
|
SLIST_HEAD(ng_wlan_bucket, ng_wlan_hent);
|
|
|
|
#define MIN_BUCKETS 256
|
|
#define HASH(a, b) ( ((a << 16) + b) % MIN_BUCKETS )
|
|
|
|
#define IS_PEER_KSOCKET(h) \
|
|
(NG_PEER_NODE(h) != NULL && \
|
|
NG_PEER_NODE(h)->nd_type->name[0] == 'k' && \
|
|
NG_PEER_NODE(h)->nd_type->name[1] == 's')
|
|
|
|
/* WLAN node private data */
|
|
struct ng_wlan_private {
|
|
struct ng_wlan_bucket *tab;
|
|
#ifndef FREEBSD411
|
|
struct mtx ng_wlan_tab_lock;
|
|
#ifdef MULTICAST_LOOKUPS
|
|
struct ng_wlan_mcast_bucket *mcast_tab;
|
|
struct mtx ng_wlan_mcast_tab_lock;
|
|
int multicast_enabled;
|
|
#endif
|
|
#endif /* !FREEBSD411 */
|
|
int persistent;
|
|
u_int16_t mer; /* multicast error rate */
|
|
u_int16_t mburst; /* multicast burst rate */
|
|
};
|
|
typedef struct ng_wlan_private *priv_p;
|
|
|
|
/*
|
|
* Local function declarations
|
|
*/
|
|
static int ng_wlan_lookup(node_p node, hook_p hook1, hook_p hook2,
|
|
struct ng_wlan_tag *tag);
|
|
static int ng_wlan_unlink(node_p node, ng_ID_t node1, ng_ID_t node2);
|
|
static int ng_wlan_link(node_p node, ng_ID_t node1, ng_ID_t node2,
|
|
struct ng_wlan_set_data *data);
|
|
|
|
#ifdef MULTICAST_LOOKUPS
|
|
static int ng_wlan_mcast_lookup(node_p node, hook_p hook1, hook_p hook2,
|
|
u_int32_t group, u_int32_t source);
|
|
static int ng_wlan_mcast_link(node_p node, ng_ID_t node1, ng_ID_t node2,
|
|
u_int32_t group, u_int32_t source, int unlink);
|
|
|
|
/* Hash table entry for multicast connectivity */
|
|
struct ng_wlan_mcast_hent {
|
|
ng_ID_t l_id;
|
|
ng_ID_t g_id;
|
|
u_int32_t group;
|
|
u_int32_t source;
|
|
int linked;
|
|
SLIST_ENTRY(ng_wlan_mcast_hent) next;
|
|
};
|
|
|
|
SLIST_HEAD(ng_wlan_mcast_bucket, ng_wlan_mcast_hent);
|
|
#define MCAST_HASH(a, b, g) ( (((a << 16) + b) & g) % MIN_BUCKETS )
|
|
#endif /* MULTICAST_LOOKUPS */
|
|
|
|
/*
|
|
* Netgraph node methods
|
|
*/
|
|
#ifndef FREEBSD411
|
|
static int ng_wlan_modevent(module_t mod, int type, void *unused);
|
|
#endif
|
|
static ng_constructor_t ng_wlan_constructor;
|
|
static ng_rcvmsg_t ng_wlan_rcvmsg;
|
|
static ng_shutdown_t ng_wlan_rmnode;
|
|
static ng_newhook_t ng_wlan_newhook;
|
|
static ng_rcvdata_t ng_wlan_rcvdata;
|
|
#ifndef FREEBSD411
|
|
static ng_rcvdata_t ng_wlan_rcvdata_ks;
|
|
#endif
|
|
static ng_disconnect_t ng_wlan_disconnect;
|
|
|
|
/* Parse types */
|
|
static const struct ng_parse_struct_field ng_wlan_link_type_fields[]
|
|
= NG_WLAN_CONFIG_TYPE_INFO;
|
|
static const struct ng_parse_type ng_wlan_link_type = {
|
|
&ng_parse_struct_type,
|
|
&ng_wlan_link_type_fields
|
|
};
|
|
static const struct ng_parse_struct_field ng_wlan_set_type_fields[]
|
|
= NG_WLAN_SET_DATA_TYPE_INFO;
|
|
static const struct ng_parse_type ng_wlan_set_type = {
|
|
&ng_parse_struct_type,
|
|
&ng_wlan_set_type_fields
|
|
};
|
|
static const struct ng_parse_struct_field ng_wlan_mer_type_fields[]
|
|
= NG_WLAN_MER_TYPE_INFO;
|
|
static const struct ng_parse_type ng_wlan_mer_type = {
|
|
&ng_parse_struct_type,
|
|
&ng_wlan_mer_type_fields
|
|
};
|
|
#ifdef MULTICAST_LOOKUPS
|
|
static const struct ng_parse_struct_field ng_wlan_multicast_set_type_fields[]
|
|
= NG_WLAN_MULTICAST_SET_DATA_TYPE_INFO;
|
|
static const struct ng_parse_type ng_wlan_multicast_set_type = {
|
|
&ng_parse_struct_type,
|
|
&ng_wlan_multicast_set_type_fields
|
|
};
|
|
#endif /* MULTICAST_LOOKUPS */
|
|
|
|
/* List of commands and how to convert arguments to/from ASCII */
|
|
static const struct ng_cmdlist ng_wlan_cmdlist[] = {
|
|
{
|
|
NGM_WLAN_COOKIE,
|
|
NGM_WLAN_LINK_NODES,
|
|
"link",
|
|
&ng_wlan_link_type,
|
|
NULL
|
|
},
|
|
{
|
|
NGM_WLAN_COOKIE,
|
|
NGM_WLAN_UNLINK_NODES,
|
|
"unlink",
|
|
&ng_wlan_link_type,
|
|
NULL
|
|
},
|
|
{
|
|
NGM_WLAN_COOKIE,
|
|
NGM_WLAN_NODES_SET,
|
|
"set",
|
|
&ng_wlan_set_type,
|
|
NULL
|
|
},
|
|
{
|
|
NGM_WLAN_COOKIE,
|
|
NGM_WLAN_NODES_UNSET,
|
|
"unset",
|
|
&ng_wlan_link_type,
|
|
NULL
|
|
},
|
|
{
|
|
NGM_WLAN_COOKIE,
|
|
NGM_WLAN_NODES_GET,
|
|
"get",
|
|
&ng_wlan_link_type,
|
|
&ng_wlan_set_type
|
|
},
|
|
{
|
|
NGM_WLAN_COOKIE,
|
|
NGM_WLAN_MER,
|
|
"mer",
|
|
&ng_wlan_mer_type,
|
|
NULL
|
|
},
|
|
#ifdef MULTICAST_LOOKUPS
|
|
{
|
|
NGM_WLAN_COOKIE,
|
|
NGM_WLAN_MULTICAST_SET,
|
|
"mcastset",
|
|
&ng_wlan_multicast_set_type,
|
|
NULL
|
|
},
|
|
{
|
|
NGM_WLAN_COOKIE,
|
|
NGM_WLAN_MULTICAST_UNSET,
|
|
"mcastunset",
|
|
&ng_wlan_multicast_set_type,
|
|
NULL
|
|
},
|
|
{
|
|
NGM_WLAN_COOKIE,
|
|
NGM_WLAN_MULTICAST_GET,
|
|
"mcastget",
|
|
&ng_wlan_multicast_set_type,
|
|
&ng_wlan_multicast_set_type
|
|
},
|
|
#endif /* MULTICAST_LOOKUPS */
|
|
{ 0 }
|
|
};
|
|
|
|
/*
|
|
* Netgraph node type descriptor
|
|
*/
|
|
static struct ng_type ng_wlan_typestruct = {
|
|
.version = NG_ABI_VERSION,
|
|
.name = NG_WLAN_NODE_TYPE,
|
|
#ifndef FREEBSD411
|
|
.mod_event = ng_wlan_modevent,
|
|
#endif
|
|
.constructor = ng_wlan_constructor,
|
|
.rcvmsg = ng_wlan_rcvmsg,
|
|
.shutdown = ng_wlan_rmnode,
|
|
.newhook = ng_wlan_newhook,
|
|
.rcvdata = ng_wlan_rcvdata,
|
|
.disconnect = ng_wlan_disconnect,
|
|
.cmdlist = ng_wlan_cmdlist,
|
|
};
|
|
NETGRAPH_INIT(wlan, &ng_wlan_typestruct);
|
|
|
|
#ifndef FREEBSD411
|
|
/*
|
|
* Function implementations
|
|
*/
|
|
static int
|
|
ng_wlan_modevent(module_t mod, int type, void *unused)
|
|
{
|
|
int error = 0;
|
|
|
|
switch (type) {
|
|
case MOD_LOAD:
|
|
#ifdef WLAN_GIANT_LOCK
|
|
mtx_init(&ng_wlan_giant, "ng_wlan_giant", NULL, MTX_DEF);
|
|
#endif
|
|
break;
|
|
case MOD_UNLOAD:
|
|
#ifdef WLAN_GIANT_LOCK
|
|
mtx_destroy(&ng_wlan_giant);
|
|
#endif
|
|
break;
|
|
default:
|
|
error = EOPNOTSUPP;
|
|
break;
|
|
}
|
|
|
|
return (error);
|
|
}
|
|
#endif /* !FREEBSD411 */
|
|
|
|
#ifdef FREEBSD411
|
|
static int
|
|
ng_wlan_constructor(node_p *nodep)
|
|
#else
|
|
static int
|
|
ng_wlan_constructor(node_p node)
|
|
#endif
|
|
{
|
|
priv_p priv;
|
|
#ifdef FREEBSD411
|
|
int error=0;
|
|
#endif
|
|
|
|
/* initialize the hash table */
|
|
MALLOC( priv, priv_p,
|
|
sizeof(struct ng_wlan_private),
|
|
M_NETGRAPH_WLAN, M_NOWAIT | M_ZERO);
|
|
if (priv == NULL)
|
|
return (ENOMEM);
|
|
|
|
MALLOC( priv->tab, struct ng_wlan_bucket *,
|
|
MIN_BUCKETS * sizeof(struct ng_wlan_bucket),
|
|
M_NETGRAPH_WLAN, M_NOWAIT | M_ZERO);
|
|
if (priv->tab == NULL) {
|
|
FREE(priv, M_NETGRAPH_WLAN);
|
|
return (ENOMEM);
|
|
}
|
|
#ifdef FREEBSD411
|
|
/* Call the generic node constructor. */
|
|
if ((error=ng_make_node_common(&ng_wlan_typestruct, nodep))) {
|
|
FREE(priv->tab, M_NETGRAPH_WLAN);
|
|
FREE(priv, M_NETGRAPH_WLAN);
|
|
return(error);
|
|
}
|
|
NG_NODE_SET_PRIVATE(*nodep, priv);
|
|
#else /* FREEBSD411 */
|
|
#ifdef MULTICAST_LOOKUPS
|
|
priv->multicast_enabled = 0; /* turned off, until ng_wlan_mcast_link()*/
|
|
/* initialize multicast hash table */
|
|
MALLOC( priv->mcast_tab, struct ng_wlan_mcast_bucket *,
|
|
MIN_BUCKETS * sizeof(struct ng_wlan_mcast_bucket),
|
|
M_NETGRAPH_WLAN, M_NOWAIT | M_ZERO);
|
|
if (priv->mcast_tab == NULL) {
|
|
FREE(priv->tab, M_NETGRAPH_WLAN);
|
|
FREE(priv, M_NETGRAPH_WLAN);
|
|
return (ENOMEM);
|
|
}
|
|
mtx_init(&priv->ng_wlan_mcast_tab_lock, "ng_wlan_mcast_tab_lock", NULL,
|
|
MTX_DEF);
|
|
#endif /* MULTICAST_LOOKUPS */
|
|
mtx_init(&priv->ng_wlan_tab_lock, "ng_wlan_tab_lock", NULL, MTX_DEF);
|
|
NG_NODE_SET_PRIVATE(node, priv);
|
|
#endif /* FREEBSD411 */
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
ng_wlan_newhook(node_p node, hook_p hook, const char *name)
|
|
{
|
|
const priv_p priv = NG_NODE_PRIVATE(node);
|
|
|
|
/* ksocket hooks "ks0", "ks1", etc. get special receive function */
|
|
if (name[0] == 'k' && name[1] == 's') {
|
|
#ifndef FREEBSD411
|
|
NG_HOOK_SET_RCVDATA(hook, ng_wlan_rcvdata_ks);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
if (strcmp(name, "anchor") == 0) {
|
|
if (priv->persistent)
|
|
return(EISCONN);
|
|
priv->persistent = 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Receive a control message.
|
|
*/
|
|
#ifdef FREEBSD411
|
|
static int
|
|
ng_wlan_rcvmsg(node_p node, struct ng_mesg *msg,
|
|
const char *retaddr, struct ng_mesg **rptr)
|
|
#else
|
|
static int
|
|
ng_wlan_rcvmsg(node_p node, item_p item, hook_p lasthook)
|
|
#endif
|
|
{
|
|
const priv_p priv = NG_NODE_PRIVATE(node);
|
|
struct ng_mesg *resp = NULL;
|
|
int error = 0;
|
|
struct ng_wlan_config *nodes;
|
|
struct ng_wlan_set_data *set_data;
|
|
struct ng_wlan_tag tag;
|
|
u_int32_t node1, node2;
|
|
struct ng_hook h1, h2;
|
|
struct ng_node n1, n2;
|
|
#ifndef FREEBSD411
|
|
struct ng_mesg *msg;
|
|
#ifdef MULTICAST_LOOKUPS
|
|
struct ng_wlan_multicast_set_data *mcsd;
|
|
u_int32_t group, src;
|
|
int unlink;
|
|
#endif /* MULTICAST_LOOKUPS */
|
|
#endif
|
|
|
|
#ifndef FREEBSD411
|
|
#ifdef WLAN_GIANT_LOCK
|
|
mtx_lock(&ng_wlan_giant);
|
|
#else
|
|
mtx_lock(&priv->ng_wlan_tab_lock);
|
|
#ifdef MULTICAST_LOOKUPS
|
|
mtx_lock(&priv->ng_wlan_mcast_tab_lock);
|
|
#endif /* MULTICAST_LOOKUPS */
|
|
#endif
|
|
NGI_GET_MSG(item, msg);
|
|
#endif /* !FREEBSD411 */
|
|
|
|
switch (msg->header.typecookie) {
|
|
case NGM_WLAN_COOKIE:
|
|
switch (msg->header.cmd) {
|
|
/* all of these messages take (node1=a,node2=b) param */
|
|
case NGM_WLAN_LINK_NODES:
|
|
case NGM_WLAN_UNLINK_NODES:
|
|
case NGM_WLAN_NODES_UNSET:
|
|
case NGM_WLAN_NODES_GET:
|
|
if (msg->header.arglen
|
|
!= sizeof(struct ng_wlan_config)) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
nodes = (struct ng_wlan_config *)msg->data;
|
|
node1 = nodes->node1;
|
|
node2 = nodes->node2;
|
|
if (msg->header.cmd == NGM_WLAN_NODES_GET) {
|
|
NG_MKRESPONSE(resp, msg, sizeof(*set_data),
|
|
M_NOWAIT);
|
|
if (resp == NULL) {
|
|
error = ENOMEM;
|
|
break;
|
|
}
|
|
set_data = (struct ng_wlan_set_data*)resp->data;
|
|
bzero(set_data, sizeof(*set_data));
|
|
/* make fake peer/node structures for lookup */
|
|
#ifdef FREEBSD411
|
|
h1.peer = &h2; h2.peer = &h1;
|
|
h1.node = &n1; h2.node = &n2;
|
|
n1.ID = node1; n2.ID = node2;
|
|
#else
|
|
h1.hk_peer = &h2; h2.hk_peer = &h1;
|
|
h1.hk_node = &n1; h2.hk_node = &n2;
|
|
n1.nd_ID = node1; n2.nd_ID = node2;
|
|
#endif
|
|
if (ng_wlan_lookup(node, &h1, &h2, &tag)) {
|
|
set_data->node1 = node1;
|
|
set_data->node2 = node2;
|
|
WLAN_TAG_COPY(set_data, (&tag));
|
|
} /* if not found, node1/node2 will be zero */
|
|
break;
|
|
}
|
|
if (msg->header.cmd == NGM_WLAN_LINK_NODES)
|
|
error = ng_wlan_link(node, node1, node2, NULL);
|
|
else
|
|
error = ng_wlan_unlink(node, node1, node2);
|
|
break;
|
|
case NGM_WLAN_NODES_SET:
|
|
if (msg->header.arglen
|
|
!= sizeof(struct ng_wlan_set_data)) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
set_data = (struct ng_wlan_set_data *)msg->data;
|
|
node1 = set_data->node1;
|
|
node2 = set_data->node2;
|
|
if (set_data->delay > NG_WLAN_MAX_DELAY ||
|
|
set_data->bandwidth > NG_WLAN_MAX_BW ||
|
|
set_data->per > NG_WLAN_MAX_PER ||
|
|
set_data->duplicate > NG_WLAN_MAX_DUP ||
|
|
set_data->jitter > NG_WLAN_MAX_JITTER ||
|
|
set_data->burst > NG_WLAN_MAX_BURST) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
error = ng_wlan_link(node, node1, node2, set_data);
|
|
break;
|
|
case NGM_WLAN_MER:
|
|
if (msg->header.arglen != sizeof(struct ng_wlan_mer)) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
priv->mer = *((u_int16_t *)msg->data);
|
|
priv->mburst = *((u_int16_t *)&msg->data[2]);
|
|
break;
|
|
case NGM_WLAN_MULTICAST_SET:
|
|
case NGM_WLAN_MULTICAST_UNSET:
|
|
case NGM_WLAN_MULTICAST_GET:
|
|
#ifndef MULTICAST_LOOKUPS
|
|
error = ENOTSUP;
|
|
break;
|
|
#else
|
|
if (msg->header.arglen
|
|
!= sizeof(struct ng_wlan_multicast_set_data)) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
unlink = (msg->header.cmd == NGM_WLAN_MULTICAST_UNSET);
|
|
mcsd = (struct ng_wlan_multicast_set_data *)msg->data;
|
|
node1 = mcsd->node1;
|
|
node2 = mcsd->node2;
|
|
group = mcsd->group;
|
|
src = mcsd->source;
|
|
if (msg->header.cmd == NGM_WLAN_MULTICAST_GET) {
|
|
NG_MKRESPONSE(resp, msg, sizeof(*mcsd),
|
|
M_NOWAIT);
|
|
if (resp == NULL) {
|
|
error = ENOMEM;
|
|
break;
|
|
}
|
|
mcsd = (struct ng_wlan_multicast_set_data*)
|
|
resp->data;
|
|
bzero(mcsd, sizeof(*mcsd));
|
|
/* make fake peer/node structures for lookup */
|
|
#ifdef FREEBSD411
|
|
h1.peer = &h2; h2.peer = &h1;
|
|
h1.node = &n1; h2.node = &n2;
|
|
n1.ID = node1; n2.ID = node2;
|
|
#else
|
|
h1.hk_peer = &h2; h2.hk_peer = &h1;
|
|
h1.hk_node = &n1; h2.hk_node = &n2;
|
|
n1.nd_ID = node1; n2.nd_ID = node2;
|
|
#endif
|
|
if (ng_wlan_mcast_lookup(node, &h1, &h2, group,
|
|
src)){
|
|
mcsd->node1 = node1;
|
|
mcsd->node2 = node2;
|
|
mcsd->group = group;
|
|
} /* if not found, node1/node2 will be zero */
|
|
break;
|
|
}
|
|
error = ng_wlan_mcast_link(node, node1, node2, group,
|
|
src, unlink);
|
|
break;
|
|
#endif /* MULTICAST_LOOKUPS */
|
|
default:
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
|
|
#ifndef FREEBSD411
|
|
NG_RESPOND_MSG(error, node, item, resp);
|
|
#endif
|
|
NG_FREE_MSG(msg);
|
|
|
|
#ifndef FREEBSD411
|
|
#ifdef WLAN_GIANT_LOCK
|
|
mtx_unlock(&ng_wlan_giant);
|
|
#else
|
|
mtx_unlock(&priv->ng_wlan_tab_lock);
|
|
#ifdef MULTICAST_LOOKUPS
|
|
mtx_unlock(&priv->ng_wlan_mcast_tab_lock);
|
|
#endif /* MULTICAST_LOOKUPS */
|
|
#endif
|
|
#endif
|
|
return(error);
|
|
}
|
|
|
|
|
|
#ifdef FREEBSD411
|
|
/*
|
|
* Handle incoming data from connected netgraph hooks.
|
|
* FreeBSD 4.11 version uses netgraph metadata.
|
|
* Does not support ksocket backchannel, multicast lookups.
|
|
*/
|
|
static int
|
|
ng_wlan_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
|
|
{
|
|
const node_p node = NG_HOOK_NODE(hook);
|
|
const priv_p priv = NG_NODE_PRIVATE(node);
|
|
int error = 0;
|
|
hook_p hook2;
|
|
struct mbuf *m2;
|
|
int nhooks;
|
|
struct ng_wlan_tag *tag = NULL;
|
|
|
|
/* Checking for NG_INVALID flag fixes race upon shutdown */
|
|
if ((NG_NODE_NOT_VALID(node)) ||
|
|
((nhooks = NG_NODE_NUMHOOKS(node)) == 1)) {
|
|
NG_FREE_DATA(m, meta);
|
|
return (0);
|
|
}
|
|
|
|
/* Meta information is not preserved by this node but replaced with
|
|
* its own data. This sets meta = NULL */
|
|
NG_FREE_META(meta);
|
|
|
|
/* Count number of linked nodes, not just number of hooks */
|
|
nhooks = 0;
|
|
LIST_FOREACH(hook2, &node->hooks, hooks)
|
|
{
|
|
/* TODO: maintain a count of the number of linked nodes */
|
|
if (hook2 == hook)
|
|
continue;
|
|
if (!ng_wlan_lookup(node, hook, hook2, NULL))
|
|
continue;
|
|
nhooks++;
|
|
}
|
|
if (nhooks==0) /* Nobody to receive the data */
|
|
goto rcvdata_free_item_error;
|
|
LIST_FOREACH(hook2, &node->hooks, hooks)
|
|
{
|
|
if (hook2 == hook)
|
|
continue;
|
|
/* Allocate a meta+tag for sending with the data, which may or
|
|
may not be used. If used, the ptr is set to NULL for the
|
|
next loop iteration; unused (non-NULL ptr) will be freed
|
|
after loop.
|
|
*/
|
|
if (!meta) {
|
|
MALLOC(meta, meta_p, WLAN_META_SIZE,
|
|
M_NETGRAPH, M_NOWAIT | M_ZERO);
|
|
if (!meta) goto rcvdata_free_item_error_nobufs;
|
|
meta->used_len = (u_short) WLAN_META_SIZE;
|
|
meta->allocated_len = (u_short) WLAN_META_SIZE;
|
|
meta->flags = 0;
|
|
meta->priority = WLAN_META_PRIORITY;
|
|
meta->discardability = -1;
|
|
tag = (struct ng_wlan_tag*)meta->options;
|
|
tag->meta_hdr.cookie = NGM_WLAN_COOKIE;
|
|
tag->meta_hdr.type = NG_TAG_WLAN;
|
|
tag->meta_hdr.len = sizeof(struct ng_wlan_tag);
|
|
}
|
|
WLAN_TAG_ZERO(tag);
|
|
|
|
if ( !ng_wlan_lookup(node, hook, hook2, tag)) {
|
|
/* determine if peers are connected, fill in tag data */
|
|
continue;
|
|
}
|
|
if ((m->m_flags & M_MCAST) && (priv->mer > 0) && tag) {
|
|
tag->per = priv->mer; /* use configured mcast error */
|
|
tag->burst = priv->mburst; /* use conf mcast burst */
|
|
}
|
|
|
|
if (--nhooks == 0) { /* nhooks is really number of links */
|
|
if (tag && TAG_HAS_DATA(tag)) {
|
|
/* send metadata and set meta = NULL */
|
|
NG_SEND_DATA(error, hook2, m, meta);
|
|
tag = NULL; /* tag used */
|
|
} else {
|
|
/* Don't send any metadata */
|
|
NG_SEND_DATA_ONLY(error, hook2, m);
|
|
}
|
|
break; /* no need to loop and malloc */
|
|
} else {
|
|
if ((m2 = m_dup(m, M_DONTWAIT)) == NULL)
|
|
goto rcvdata_free_item_error_nobufs;
|
|
if (tag && TAG_HAS_DATA(tag)) {
|
|
/* send metadata and set meta = NULL */
|
|
NG_SEND_DATA(error, hook2, m2, meta);
|
|
tag = NULL; /* tag used */
|
|
} else {
|
|
/* Don't send any metadata */
|
|
NG_SEND_DATA_ONLY(error, hook2, m2);
|
|
if (error) /* XXX free mbuf? */
|
|
continue; /* don't give up */
|
|
}
|
|
} /* end if nhooks==0 */
|
|
} /* end FOREACH hook */
|
|
|
|
if (meta) /* cleanup unused meta+tag */
|
|
NG_FREE_META(meta);
|
|
|
|
goto rcvdata_out;
|
|
|
|
rcvdata_free_item_error_nobufs:
|
|
error = ENOBUFS;
|
|
rcvdata_free_item_error:
|
|
NG_FREE_DATA(m, meta);
|
|
|
|
rcvdata_out:
|
|
return (error);
|
|
}
|
|
|
|
#else /* FREEBSD411 */
|
|
/*
|
|
* Handle incoming data from connected netgraph hooks.
|
|
* FreeBSD 7.0 version uses mbuf tags; has additional features:
|
|
* - ksocket backchannel for connecting two ng_wlans together
|
|
* - multicast lookups for different forwarding behavior for multicast packets
|
|
*/
|
|
static int
|
|
ng_wlan_rcvdata(hook_p hook, item_p item)
|
|
{
|
|
const node_p node = NG_HOOK_NODE(hook);
|
|
int error = 0;
|
|
hook_p hook2;
|
|
struct mbuf *m2;
|
|
int nhooks;
|
|
struct ng_wlan_tag *tag = NULL;
|
|
struct mbuf *m;
|
|
const priv_p priv = NG_NODE_PRIVATE(node);
|
|
ng_ID_t srcid;
|
|
node_p peer;
|
|
#ifdef MULTICAST_LOOKUPS
|
|
u_int32_t group, src;
|
|
struct ip *ip;
|
|
struct ether_header *eh;
|
|
#endif /* MULTICAST_LOOKUPS */
|
|
|
|
/* Checking for NG_INVALID flag fixes race upon shutdown */
|
|
if ((NG_NODE_NOT_VALID(node)) ||
|
|
((nhooks = NG_NODE_NUMHOOKS(node)) == 1)) {
|
|
NG_FREE_ITEM(item);
|
|
return (0);
|
|
}
|
|
|
|
#ifdef WLAN_GIANT_LOCK
|
|
mtx_lock(&ng_wlan_giant);
|
|
#else
|
|
mtx_lock(&priv->ng_wlan_tab_lock);
|
|
#endif
|
|
m = NGI_M(item); /* 'item' still owns it... we are peeking */
|
|
|
|
#ifdef MULTICAST_LOOKUPS
|
|
mtx_lock(&priv->ng_wlan_mcast_tab_lock);
|
|
src = group = 0;
|
|
if (priv->multicast_enabled &&
|
|
(m->m_flags & M_MCAST) && (m->m_flags & M_PKTHDR)) {
|
|
/* disassociate mbuf from item (now we must free it) */
|
|
NGI_GET_M(item, m);
|
|
/* Get group of packets sent to non-local multicast addresses */
|
|
if ((m->m_pkthdr.len >= IP_MCAST_MIN_LEN) &&
|
|
(m = m_pullup(m, IP_MCAST_MIN_LEN)) != NULL) {
|
|
eh = mtod_off(m, 0, struct ether_header *);
|
|
if (ETHER_IS_MULTICAST(eh->ether_dhost) &&
|
|
ntohs(eh->ether_type) == ETHERTYPE_IP) {
|
|
ip = mtod_off(m, IP_MCAST_HDR_OFFSET,
|
|
struct ip *);
|
|
if ((ip->ip_v == IPVERSION) &&
|
|
IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) &&
|
|
!(IN_LOCAL_GROUP(ntohl(ip->ip_dst.s_addr)))) {
|
|
group = ntohl(ip->ip_dst.s_addr);
|
|
src = NG_NODE_ID(NG_PEER_NODE(hook));
|
|
}
|
|
}
|
|
} else if (!m) { /* m_pullup failed, free item and leave */
|
|
error = EINVAL;
|
|
goto rcvdata_free_item_error;
|
|
}
|
|
NGI_M(item) = m; /* give mbuf back to item */
|
|
}
|
|
#endif /* MULTICAST_LOOKUPS */
|
|
|
|
/* Count number of linked nodes, not just number of hooks */
|
|
nhooks = 0;
|
|
LIST_FOREACH(hook2, &node->nd_hooks, hk_hooks)
|
|
{
|
|
/* TODO: maintain a count of the number of linked nodes */
|
|
if (hook2 == hook)
|
|
continue;
|
|
if (IS_PEER_KSOCKET(hook2)) { /* count all ksockets */
|
|
nhooks++;
|
|
continue;
|
|
}
|
|
#ifdef MULTICAST_LOOKUPS
|
|
/* count hook using multicast lookup if packet is multicast */
|
|
if ( group > 0 ) {
|
|
if (!ng_wlan_mcast_lookup(node, hook, hook2, group, src) ||
|
|
!ng_wlan_lookup(node, hook, hook2, NULL))
|
|
continue;
|
|
/* use normal unicast lookup */
|
|
} else
|
|
#endif /* MULTICAST_LOOKUPS */
|
|
if (!ng_wlan_lookup(node, hook, hook2, NULL))
|
|
continue;
|
|
nhooks++;
|
|
}
|
|
if (nhooks==0) /* Nobody to receive the data */
|
|
goto rcvdata_free_item_error;
|
|
|
|
LIST_FOREACH(hook2, &node->nd_hooks, hk_hooks)
|
|
{
|
|
if (hook2 == hook)
|
|
continue;
|
|
/* Allocate a tag for prepending to the mbuf, which may or
|
|
may not be used. If used, the ptr is set to NULL for the
|
|
next loop iteration; unused (non-NULL ptr) will be freed
|
|
after loop.
|
|
*/
|
|
if (!tag)
|
|
tag = (struct ng_wlan_tag *)m_tag_alloc(NGM_WLAN_COOKIE,
|
|
NG_TAG_WLAN, TAGSIZE, M_NOWAIT | M_ZERO);
|
|
if (!tag) goto rcvdata_free_item_error_nobufs;
|
|
WLAN_TAG_ZERO(tag);
|
|
|
|
/* check for ksocket backchannel to another ng_wlan */
|
|
srcid = 0;
|
|
if (IS_PEER_KSOCKET(hook2)) {
|
|
/* this hook is connected to a ksocket
|
|
* set srcid for prepending the mbuf */
|
|
peer = NG_PEER_NODE(hook2);
|
|
srcid = (NG_NODE_ID(peer) << 8) +
|
|
NG_NODE_ID(NG_PEER_NODE(hook));
|
|
} else
|
|
#ifdef MULTICAST_LOOKUPS
|
|
if ( group > 0 ) {
|
|
if (!ng_wlan_mcast_lookup(node, hook, hook2, group, src) ||
|
|
!ng_wlan_lookup(node, hook, hook2, tag))
|
|
continue; /* multicast lookup failed */
|
|
/* multicast lookup success - tag data filled in */
|
|
} else
|
|
#endif /* MULTICAST_LOOKUPS */
|
|
if ( !ng_wlan_lookup(node, hook, hook2, tag)) {
|
|
/* determine if peers are connected, fill in tag data */
|
|
continue;
|
|
}
|
|
if ((m->m_flags & M_MCAST) && (priv->mer > 0) && tag) {
|
|
tag->per = priv->mer; /* use configured mcast error */
|
|
tag->burst = priv->mburst; /* use conf mcast burst */
|
|
}
|
|
|
|
if (--nhooks == 0) { /* nhooks is really number of links */
|
|
if (srcid > 0) { /* add srcid for ksockets */
|
|
NGI_GET_M(item, m);
|
|
M_PREPEND(m, sizeof(ng_ID_t), M_DONTWAIT);
|
|
if (!m) goto rcvdata_free_item_error_nobufs;
|
|
mtod(m, ng_ID_t*)[0] = htonl(srcid);
|
|
NGI_M(item) = m;
|
|
} else if (tag && TAG_HAS_DATA(tag)) {
|
|
m_tag_prepend(m, &tag->tag);
|
|
tag = NULL; /* tag used */
|
|
}
|
|
NG_FWD_ITEM_HOOK(error, item, hook2);
|
|
break; /* no need to loop and malloc */
|
|
} else {
|
|
if ((m2 = m_dup(m, M_DONTWAIT)) == NULL)
|
|
goto rcvdata_free_item_error_nobufs;
|
|
if (srcid > 0) { /* add srcid for ksockets */
|
|
M_PREPEND(m2, sizeof(ng_ID_t), M_DONTWAIT);
|
|
if (!m2) goto rcvdata_free_item_error_nobufs;
|
|
mtod(m2, ng_ID_t*)[0] = htonl(srcid);
|
|
} else if (tag && TAG_HAS_DATA(tag)) {
|
|
m_tag_prepend(m2, &tag->tag);
|
|
tag = NULL; /* tag used */
|
|
}
|
|
NG_SEND_DATA_ONLY(error, hook2, m2);
|
|
if (error) /* XXX free mbuf? */
|
|
continue; /* don't give up */
|
|
} /* end if nhooks==0 */
|
|
} /* end FOREACH hook */
|
|
|
|
if (tag) /* cleanup unused tag */
|
|
m_tag_free(&tag->tag);
|
|
|
|
/* assume item has been freed by fwd above (nhooks==0) */
|
|
goto rcvdata_out;
|
|
|
|
rcvdata_free_item_error_nobufs:
|
|
error = ENOBUFS;
|
|
rcvdata_free_item_error:
|
|
NG_FREE_ITEM(item);
|
|
|
|
rcvdata_out:
|
|
#ifdef WLAN_GIANT_LOCK
|
|
mtx_unlock(&ng_wlan_giant);
|
|
#else
|
|
mtx_unlock(&priv->ng_wlan_tab_lock);
|
|
#ifdef MULTICAST_LOOKUPS
|
|
mtx_unlock(&priv->ng_wlan_mcast_tab_lock);
|
|
#endif /* MULTICAST_LOOKUPS */
|
|
#endif
|
|
return (error);
|
|
}
|
|
#endif /* FREEBSD411 */
|
|
|
|
#ifndef FREEBSD411
|
|
/*
|
|
* Handle incoming data from hooks connected to kernel sockets
|
|
*/
|
|
static int
|
|
ng_wlan_rcvdata_ks(hook_p hook, item_p item)
|
|
{
|
|
const node_p node = NG_HOOK_NODE(hook);
|
|
const priv_p priv = NG_NODE_PRIVATE(node);
|
|
int error = 0;
|
|
hook_p hook2;
|
|
struct mbuf *m, *m2;
|
|
int nhooks;
|
|
struct ng_wlan_tag *tag = NULL;
|
|
ng_ID_t srcid;
|
|
struct ng_hook hooklookup, hooklookup2;
|
|
struct ng_node nodelookup;
|
|
|
|
/* Checking for NG_INVALID flag fixes race upon shutdown */
|
|
if ((NG_NODE_NOT_VALID(node)) ||
|
|
((nhooks = NG_NODE_NUMHOOKS(node)) == 1)) {
|
|
NG_FREE_ITEM(item);
|
|
return (0);
|
|
}
|
|
|
|
#ifndef FREEBSD411
|
|
#ifdef WLAN_GIANT_LOCK
|
|
mtx_lock(&ng_wlan_giant);
|
|
#else
|
|
mtx_lock(&priv->ng_wlan_tab_lock);
|
|
#endif
|
|
#endif
|
|
/* this packet came from another system, so we read the
|
|
* netgraph ID from the mbuf for use in lookups */
|
|
NGI_GET_M(item, m);
|
|
if (m->m_pkthdr.len < sizeof(ng_ID_t)) { /* too short */
|
|
error = EINVAL;
|
|
goto rcvdata_ks_free_item_error;
|
|
}
|
|
if (m->m_len < sizeof(ng_ID_t) &&
|
|
(m = m_pullup(m, sizeof(ng_ID_t))) == NULL) {
|
|
goto rcvdata_ks_free_item_error_nobufs;
|
|
}
|
|
srcid = ntohl(*mtod(m, ng_ID_t*));
|
|
m_adj(m, sizeof(ng_ID_t));
|
|
NGI_M(item) = (m);
|
|
/* build fake hooks/node for performing lookup */
|
|
hooklookup2.hk_node = &nodelookup;
|
|
hooklookup.hk_peer = &hooklookup2;
|
|
nodelookup.nd_ID = srcid;
|
|
|
|
/* Count number of linked nodes, not just number of hooks */
|
|
nhooks = 0;
|
|
LIST_FOREACH(hook2, &node->nd_hooks, hk_hooks) {
|
|
/* TODO: maintain a count of the number of linked nodes */
|
|
if (hook2 == hook)
|
|
continue;
|
|
/* ksockets not counted here -- they'll be skipped */
|
|
if (!ng_wlan_lookup(node, &hooklookup, hook2, NULL))
|
|
continue;
|
|
nhooks++;
|
|
}
|
|
if (nhooks==0) /* Nobody to receive the data */
|
|
goto rcvdata_ks_free_item_error;
|
|
|
|
|
|
LIST_FOREACH(hook2, &node->nd_hooks, hk_hooks) {
|
|
if (hook2 == hook)
|
|
continue;
|
|
/* Allocate a tag for prepending to the mbuf, which may or
|
|
may not be used. If used, the ptr is set to NULL for the
|
|
next loop iteration; unused (non-NULL ptr) will be freed
|
|
after loop.
|
|
*/
|
|
if (!tag)
|
|
tag = (struct ng_wlan_tag *)m_tag_alloc(NGM_WLAN_COOKIE,
|
|
NG_TAG_WLAN, TAGSIZE, M_NOWAIT | M_ZERO);
|
|
if (!tag) goto rcvdata_ks_free_item_error_nobufs;
|
|
WLAN_TAG_ZERO(tag);
|
|
|
|
/* don't send data to other ksockets */
|
|
if (IS_PEER_KSOCKET(hook2)) {
|
|
continue;
|
|
/* determine if peers are connected */
|
|
} else if ( !ng_wlan_lookup(node, &hooklookup, hook2, tag)) {
|
|
continue;
|
|
}
|
|
|
|
if (--nhooks == 0) { /* nhooks is really number of links */
|
|
if (tag && TAG_HAS_DATA(tag)) {
|
|
m_tag_prepend(m, &tag->tag);
|
|
tag = NULL; /* tag used */
|
|
}
|
|
NG_FWD_ITEM_HOOK(error, item, hook2);
|
|
} else {
|
|
if ((m2 = m_dup(m, M_DONTWAIT)) == NULL)
|
|
goto rcvdata_ks_free_item_error_nobufs;
|
|
if (tag && TAG_HAS_DATA(tag)) {
|
|
m_tag_prepend(m2, &tag->tag);
|
|
tag = NULL; /* tag used */
|
|
}
|
|
NG_SEND_DATA_ONLY(error, hook2, m2);
|
|
if (error) /* XXX free mbuf? */
|
|
continue; /* don't give up */
|
|
}
|
|
}
|
|
if (tag) /* cleanup unused tag */
|
|
m_tag_free(&tag->tag);
|
|
|
|
goto rcvdata_ks_out;
|
|
|
|
rcvdata_ks_free_item_error_nobufs:
|
|
error = ENOBUFS;
|
|
rcvdata_ks_free_item_error:
|
|
NG_FREE_ITEM(item);
|
|
|
|
rcvdata_ks_out:
|
|
#ifndef FREEBSD411
|
|
#ifdef WLAN_GIANT_LOCK
|
|
mtx_unlock(&ng_wlan_giant);
|
|
#else
|
|
mtx_unlock(&priv->ng_wlan_tab_lock);
|
|
#endif
|
|
#endif
|
|
return (error);
|
|
}
|
|
#endif /* !FREEBSD411 */
|
|
|
|
|
|
static int
|
|
ng_wlan_disconnect(hook_p hook)
|
|
{
|
|
#ifdef FREEBSD411
|
|
const priv_p priv = hook->node->private;
|
|
#else
|
|
const priv_p priv = hook->hk_node->nd_private;
|
|
#endif
|
|
|
|
if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 &&
|
|
NG_NODE_IS_VALID(NG_HOOK_NODE(hook)) && !priv->persistent)
|
|
#ifdef FREEBSD411
|
|
ng_rmnode(NG_HOOK_NODE(hook));
|
|
#else
|
|
ng_rmnode_self(NG_HOOK_NODE(hook));
|
|
#endif
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
ng_wlan_rmnode(node_p node)
|
|
{
|
|
const priv_p priv = NG_NODE_PRIVATE(node);
|
|
int b, s;
|
|
struct ng_wlan_hent *tmp;
|
|
#ifdef MULTICAST_LOOKUPS
|
|
struct ng_wlan_mcast_hent *mtmp;
|
|
#endif /* MULTICAST_LOOKUPS */
|
|
s=splimp();
|
|
|
|
#ifdef FREEBSD411
|
|
node->flags |= NG_INVALID;
|
|
ng_cutlinks(node);
|
|
ng_unname(node);
|
|
#else
|
|
node->nd_flags |= NGF_INVALID;
|
|
#endif
|
|
NG_NODE_SET_PRIVATE(node, NULL);
|
|
NG_NODE_UNREF(node);
|
|
/* empty any link lists */
|
|
for (b = 0; b < MIN_BUCKETS; b++) {
|
|
tmp = SLIST_FIRST(&priv->tab[b]);
|
|
while (tmp) {
|
|
SLIST_REMOVE_HEAD(&priv->tab[b], next);
|
|
FREE(tmp, M_NETGRAPH_WLAN);
|
|
tmp = SLIST_FIRST(&priv->tab[b]);
|
|
}
|
|
}
|
|
FREE(priv->tab, M_NETGRAPH_WLAN);
|
|
#ifndef FREEBSD411
|
|
mtx_destroy(&priv->ng_wlan_tab_lock);
|
|
#endif
|
|
priv->tab = NULL;
|
|
#ifdef MULTICAST_LOOKUPS
|
|
/* empty any multicast entry link lists */
|
|
for (b = 0; b < MIN_BUCKETS; b++) {
|
|
mtmp = SLIST_FIRST(&priv->mcast_tab[b]);
|
|
while (mtmp) {
|
|
SLIST_REMOVE_HEAD(&priv->mcast_tab[b], next);
|
|
FREE(mtmp, M_NETGRAPH_WLAN);
|
|
mtmp = SLIST_FIRST(&priv->mcast_tab[b]);
|
|
}
|
|
}
|
|
FREE(priv->mcast_tab, M_NETGRAPH_WLAN);
|
|
mtx_destroy(&priv->ng_wlan_mcast_tab_lock);
|
|
#endif /* MULTICAST_LOOKUPS */
|
|
FREE(priv, M_NETGRAPH_WLAN);
|
|
|
|
splx(s);
|
|
return 0;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* WLAN FUNCTIONS *
|
|
**********************************************************************/
|
|
|
|
#define NODE_SORT(a, b, l, g) do { \
|
|
if (a > b) { \
|
|
g = a; \
|
|
l = b; \
|
|
} else { \
|
|
g = b; \
|
|
l = a; \
|
|
} \
|
|
} while (0);
|
|
|
|
/*
|
|
* Returns 1 if peers are linked, 0 if unlinked (default).
|
|
*/
|
|
static int
|
|
ng_wlan_lookup(node_p node, hook_p hook1, hook_p hook2,
|
|
struct ng_wlan_tag *tag)
|
|
{
|
|
const priv_p priv = NG_NODE_PRIVATE(node);
|
|
struct ng_wlan_hent *hent;
|
|
node_p node1, node2;
|
|
ng_ID_t l_id, g_id;
|
|
int bucket;
|
|
|
|
if (!hook1 || !hook2)
|
|
return 0;
|
|
node1 = NG_PEER_NODE(hook1);
|
|
node2 = NG_PEER_NODE(hook2);
|
|
if (!node1 || !node2)
|
|
return 0;
|
|
|
|
NODE_SORT(NG_NODE_ID(node1), NG_NODE_ID(node2), l_id, g_id);
|
|
bucket = HASH(l_id, g_id);
|
|
|
|
/* mtx_lock(&priv->ng_wlan_tab_lock); */
|
|
SLIST_FOREACH(hent, &priv->tab[bucket], next) {
|
|
if ((hent->l_id == l_id) && (hent->g_id == g_id)) {
|
|
/* optionally fill in tag with link data*/
|
|
if (tag && hent->linked) {
|
|
tag->delay = hent->delay;
|
|
tag->bandwidth = hent->bandwidth;
|
|
tag->per = hent->per;
|
|
tag->duplicate = hent->duplicate;
|
|
tag->jitter = hent->jitter;
|
|
tag->burst = hent->burst;
|
|
}
|
|
/* mtx_unlock(&priv->ng_wlan_tab_lock); */
|
|
return (hent->linked); /* linked or not linked flag */
|
|
}
|
|
}
|
|
/* mtx_unlock(&priv->ng_wlan_tab_lock); */
|
|
return 0; /* not linked (not found) */
|
|
}
|
|
|
|
#ifdef MULTICAST_LOOKUPS
|
|
/*
|
|
* Returns 1 if peers are linked for this multicast group,
|
|
* 0 if unlinked (default).
|
|
*/
|
|
static int
|
|
ng_wlan_mcast_lookup(node_p node, hook_p hook1, hook_p hook2,
|
|
u_int32_t group, u_int32_t source)
|
|
{
|
|
const priv_p priv = NG_NODE_PRIVATE(node);
|
|
struct ng_wlan_mcast_hent *hent;
|
|
node_p node1, node2;
|
|
ng_ID_t l_id, g_id;
|
|
int bucket;
|
|
|
|
if (!hook1 || !hook2)
|
|
return 0;
|
|
|
|
node1 = NG_PEER_NODE(hook1);
|
|
node2 = NG_PEER_NODE(hook2);
|
|
if (!node1 || !node2)
|
|
return 0;
|
|
|
|
NODE_SORT(NG_NODE_ID(node1), NG_NODE_ID(node2), l_id, g_id);
|
|
bucket = MCAST_HASH(l_id, g_id, group);
|
|
|
|
SLIST_FOREACH(hent, &priv->mcast_tab[bucket], next) {
|
|
if ((hent->l_id == l_id) && (hent->g_id == g_id) &&
|
|
(hent->group == group) && (hent->source == source)) {
|
|
return (hent->linked);
|
|
}
|
|
}
|
|
return 0; /* not linked (not found) */
|
|
}
|
|
|
|
/*
|
|
* Link/unlink to peers for a given multicast group.
|
|
*/
|
|
static int
|
|
ng_wlan_mcast_link(node_p node, ng_ID_t node1, ng_ID_t node2,
|
|
u_int32_t group, u_int32_t source, int unlink)
|
|
{
|
|
const priv_p priv = NG_NODE_PRIVATE(node);
|
|
ng_ID_t l_id, g_id;
|
|
int bucket;
|
|
struct ng_wlan_mcast_hent *hent;
|
|
|
|
NODE_SORT(node1, node2, l_id, g_id);
|
|
bucket = MCAST_HASH(l_id, g_id, group);
|
|
priv->multicast_enabled = 1; /* turn on multicast lookups,
|
|
this is never turned off */
|
|
|
|
/* Look for existing entry */
|
|
SLIST_FOREACH(hent, &priv->mcast_tab[bucket], next) {
|
|
if ((hent->l_id == l_id) && (hent->g_id == g_id) &&
|
|
(hent->group == group) && (hent->source == source))
|
|
break;
|
|
}
|
|
|
|
/* Unlink called but no entry exists */
|
|
if (!hent && unlink) {
|
|
return 0;
|
|
}
|
|
|
|
/* Allocate and initialize a new hash table entry */
|
|
if (!hent) {
|
|
MALLOC( hent, struct ng_wlan_mcast_hent *,
|
|
sizeof(*hent), M_NETGRAPH_WLAN, M_NOWAIT);
|
|
if (hent == NULL) {
|
|
return(ENOBUFS);
|
|
}
|
|
hent->l_id = l_id;
|
|
hent->g_id = g_id;
|
|
hent->group = group;
|
|
hent->source = source;
|
|
/* Add the new element to the hash bucket */
|
|
SLIST_INSERT_HEAD(&priv->mcast_tab[bucket], hent, next);
|
|
}
|
|
|
|
if (unlink)
|
|
hent->linked = 0;
|
|
else
|
|
hent->linked = 1;
|
|
return 0;
|
|
}
|
|
#endif /* MULTICAST_LOOKUPS */
|
|
|
|
/*
|
|
* Link two peers together.
|
|
* Once two peers have been linked together, the link can be flagged as
|
|
* linked/unlinked in their hash table entry. Set link data if supplied.
|
|
*/
|
|
static int
|
|
ng_wlan_link(node_p node, ng_ID_t node1, ng_ID_t node2,
|
|
struct ng_wlan_set_data *data)
|
|
{
|
|
const priv_p priv = NG_NODE_PRIVATE(node);
|
|
ng_ID_t l_id, g_id;
|
|
int bucket;
|
|
struct ng_wlan_hent *hent;
|
|
|
|
NODE_SORT(node1, node2, l_id, g_id);
|
|
bucket = HASH(l_id, g_id);
|
|
/* mtx_lock(&priv->ng_wlan_tab_lock); */
|
|
|
|
/* Look for existing entry */
|
|
SLIST_FOREACH(hent, &priv->tab[bucket], next) {
|
|
if ((hent->l_id == l_id) && (hent->g_id == g_id))
|
|
break;
|
|
}
|
|
/* Allocate and initialize a new hash table entry */
|
|
if (!hent) {
|
|
MALLOC( hent, struct ng_wlan_hent *,
|
|
sizeof(*hent), M_NETGRAPH_WLAN, M_NOWAIT | M_ZERO);
|
|
if (hent == NULL) {
|
|
/* mtx_unlock(&priv->ng_wlan_tab_lock); */
|
|
return(ENOBUFS);
|
|
}
|
|
hent->l_id = l_id;
|
|
hent->g_id = g_id;
|
|
/* Add the new element to the hash bucket */
|
|
SLIST_INSERT_HEAD(&priv->tab[bucket], hent, next);
|
|
}
|
|
|
|
hent->linked = 1;
|
|
if (data) {
|
|
hent->delay = data->delay;
|
|
hent->bandwidth = data->bandwidth;
|
|
hent->per = data->per;
|
|
hent->duplicate = data->duplicate;
|
|
hent->jitter = data->jitter;
|
|
hent->burst = data->burst;
|
|
} else {
|
|
WLAN_TAG_ZERO(hent);
|
|
}
|
|
/* mtx_unlock(&priv->ng_wlan_tab_lock); */
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Unlink two previously-linked peers.
|
|
* because singly-linked list is not optimized for removals, we just
|
|
* unset the "linked" flag. Link data is zeroed.
|
|
*/
|
|
static int
|
|
ng_wlan_unlink(node_p node, ng_ID_t node1, ng_ID_t node2)
|
|
{
|
|
const priv_p priv = NG_NODE_PRIVATE(node);
|
|
ng_ID_t l_id, g_id;
|
|
int bucket;
|
|
struct ng_wlan_hent *hent;
|
|
|
|
NODE_SORT(node1, node2, l_id, g_id);
|
|
bucket = HASH(l_id, g_id);
|
|
|
|
/* Look for existing entry */
|
|
/* mtx_lock(&priv->ng_wlan_tab_lock); */
|
|
SLIST_FOREACH(hent, &priv->tab[bucket], next) {
|
|
/* entry exists in hash table, unset linked flag */
|
|
if ((hent->l_id == l_id) && (hent->g_id == g_id)) {
|
|
hent->linked = 0;
|
|
WLAN_TAG_ZERO(hent);
|
|
/* mtx_unlock(&priv->ng_wlan_tab_lock); */
|
|
return(0);
|
|
}
|
|
}
|
|
/* Entry does not exist in the hash table, do nothing. */
|
|
/* mtx_unlock(&priv->ng_wlan_tab_lock); */
|
|
return 0;
|
|
}
|
|
|