Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/netgraph/ng_bridge.c
34372 views
1
/*-
2
* Copyright (c) 2000 Whistle Communications, Inc.
3
* All rights reserved.
4
*
5
* Subject to the following obligations and disclaimer of warranty, use and
6
* redistribution of this software, in source or object code forms, with or
7
* without modifications are expressly permitted by Whistle Communications;
8
* provided, however, that:
9
* 1. Any and all reproductions of the source or object code must include the
10
* copyright notice above and the following disclaimer of warranties; and
11
* 2. No rights are granted, in any manner or form, to use Whistle
12
* Communications, Inc. trademarks, including the mark "WHISTLE
13
* COMMUNICATIONS" on advertising, endorsements, or otherwise except as
14
* such appears in the above copyright notice or in the software.
15
*
16
* THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
17
* TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
18
* REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
19
* INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
20
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
21
* WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
22
* REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
23
* SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
24
* IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
25
* RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
26
* WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
27
* PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
28
* SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
29
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31
* THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
32
* OF SUCH DAMAGE.
33
*
34
* Author: Archie Cobbs <[email protected]>
35
*/
36
37
/*
38
* ng_bridge(4) netgraph node type
39
*
40
* The node performs standard intelligent Ethernet bridging over
41
* each of its connected hooks, or links. A simple loop detection
42
* algorithm is included which disables a link for priv->conf.loopTimeout
43
* seconds when a host is seen to have jumped from one link to
44
* another within priv->conf.minStableAge seconds.
45
*
46
* We keep a hashtable that maps Ethernet addresses to host info,
47
* which is contained in struct ng_bridge_host's. These structures
48
* tell us on which link the host may be found. A host's entry will
49
* expire after priv->conf.maxStaleness seconds.
50
*
51
* This node is optimzed for stable networks, where machines jump
52
* from one port to the other only rarely.
53
*/
54
55
#include <sys/param.h>
56
#include <sys/systm.h>
57
#include <sys/kernel.h>
58
#include <sys/lock.h>
59
#include <sys/malloc.h>
60
#include <sys/mbuf.h>
61
#include <sys/errno.h>
62
#include <sys/rwlock.h>
63
#include <sys/syslog.h>
64
#include <sys/socket.h>
65
#include <sys/ctype.h>
66
#include <sys/types.h>
67
#include <sys/counter.h>
68
69
#include <net/if.h>
70
#include <net/if_var.h>
71
#include <net/ethernet.h>
72
#include <net/vnet.h>
73
74
#include <netinet/in.h>
75
#include <netgraph/ng_message.h>
76
#include <netgraph/netgraph.h>
77
#include <netgraph/ng_parse.h>
78
#include <netgraph/ng_bridge.h>
79
80
#ifdef NG_SEPARATE_MALLOC
81
static MALLOC_DEFINE(M_NETGRAPH_BRIDGE, "netgraph_bridge",
82
"netgraph bridge node");
83
#else
84
#define M_NETGRAPH_BRIDGE M_NETGRAPH
85
#endif
86
87
/* Counter based stats */
88
struct ng_bridge_link_kernel_stats {
89
counter_u64_t recvOctets; /* total octets rec'd on link */
90
counter_u64_t recvPackets; /* total pkts rec'd on link */
91
counter_u64_t recvMulticasts; /* multicast pkts rec'd on link */
92
counter_u64_t recvBroadcasts; /* broadcast pkts rec'd on link */
93
counter_u64_t recvUnknown; /* pkts rec'd with unknown dest addr */
94
counter_u64_t recvRunts; /* pkts rec'd less than 14 bytes */
95
counter_u64_t recvInvalid; /* pkts rec'd with bogus source addr */
96
counter_u64_t xmitOctets; /* total octets xmit'd on link */
97
counter_u64_t xmitPackets; /* total pkts xmit'd on link */
98
counter_u64_t xmitMulticasts; /* multicast pkts xmit'd on link */
99
counter_u64_t xmitBroadcasts; /* broadcast pkts xmit'd on link */
100
counter_u64_t loopDrops; /* pkts dropped due to loopback */
101
u_int64_t loopDetects; /* number of loop detections */
102
counter_u64_t memoryFailures; /* times couldn't get mem or mbuf */
103
};
104
105
/* Per-link private data */
106
struct ng_bridge_link {
107
hook_p hook; /* netgraph hook */
108
u_int16_t loopCount; /* loop ignore timer */
109
unsigned int learnMac : 1, /* autolearn macs */
110
sendUnknown : 1;/* send unknown macs out */
111
struct ng_bridge_link_kernel_stats stats; /* link stats */
112
};
113
typedef struct ng_bridge_link const *link_cp; /* read only access */
114
115
/* Per-node private data */
116
struct ng_bridge_private {
117
struct ng_bridge_bucket *tab; /* hash table bucket array */
118
struct ng_bridge_config conf; /* node configuration */
119
node_p node; /* netgraph node */
120
u_int numHosts; /* num entries in table */
121
u_int numBuckets; /* num buckets in table */
122
u_int hashMask; /* numBuckets - 1 */
123
int numLinks; /* num connected links */
124
unsigned int persistent : 1, /* can exist w/o hooks */
125
sendUnknown : 1;/* links receive unknowns by default */
126
struct callout timer; /* one second periodic timer */
127
struct unrhdr *linkUnit; /* link unit number allocator */
128
struct unrhdr *uplinkUnit; /* uplink unit number allocator */
129
};
130
typedef struct ng_bridge_private *priv_p;
131
typedef struct ng_bridge_private const *priv_cp; /* read only access */
132
133
/* Information about a host, stored in a hash table entry */
134
struct ng_bridge_host {
135
u_char addr[6]; /* ethernet address */
136
link_p link; /* link where addr can be found */
137
u_int16_t age; /* seconds ago entry was created */
138
u_int16_t staleness; /* seconds ago host last heard from */
139
SLIST_ENTRY(ng_bridge_host) next; /* next entry in bucket */
140
};
141
142
/* Hash table bucket declaration */
143
SLIST_HEAD(ng_bridge_bucket, ng_bridge_host);
144
145
/* [up]link prefix matching */
146
struct ng_link_prefix {
147
const char * const prefix;
148
size_t len;
149
};
150
151
static const struct ng_link_prefix link_pfx = {
152
.prefix = NG_BRIDGE_HOOK_LINK_PREFIX,
153
.len = sizeof(NG_BRIDGE_HOOK_LINK_PREFIX) - 1,
154
};
155
static const struct ng_link_prefix uplink_pfx = {
156
.prefix = NG_BRIDGE_HOOK_UPLINK_PREFIX,
157
.len = sizeof(NG_BRIDGE_HOOK_UPLINK_PREFIX) - 1,
158
};
159
160
/* Netgraph node methods */
161
static ng_constructor_t ng_bridge_constructor;
162
static ng_rcvmsg_t ng_bridge_rcvmsg;
163
static ng_shutdown_t ng_bridge_shutdown;
164
static ng_newhook_t ng_bridge_newhook;
165
static ng_rcvdata_t ng_bridge_rcvdata;
166
static ng_disconnect_t ng_bridge_disconnect;
167
168
/* Other internal functions */
169
static const struct ng_link_prefix *ng_get_link_prefix(const char *name);
170
static void ng_bridge_free_link(link_p link);
171
static struct ng_bridge_host *ng_bridge_get(priv_cp priv, const u_char *addr);
172
static int ng_bridge_put(priv_p priv, const u_char *addr, link_p link);
173
static void ng_bridge_rehash(priv_p priv);
174
static void ng_bridge_remove_hosts(priv_p priv, link_p link);
175
static void ng_bridge_timeout(node_p node, hook_p hook, void *arg1, int arg2);
176
static const char *ng_bridge_nodename(node_cp node);
177
178
/* Ethernet broadcast */
179
static const u_char ng_bridge_bcast_addr[ETHER_ADDR_LEN] =
180
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
181
182
/* Compare Ethernet addresses using 32 and 16 bit words instead of bytewise */
183
#define ETHER_EQUAL(a,b) (((const u_int32_t *)(a))[0] \
184
== ((const u_int32_t *)(b))[0] \
185
&& ((const u_int16_t *)(a))[2] \
186
== ((const u_int16_t *)(b))[2])
187
188
/* Minimum and maximum number of hash buckets. Must be a power of two. */
189
#define MIN_BUCKETS (1 << 5) /* 32 */
190
#define MAX_BUCKETS (1 << 14) /* 16384 */
191
192
/* Configuration default values */
193
#define DEFAULT_LOOP_TIMEOUT 60
194
#define DEFAULT_MAX_STALENESS (15 * 60) /* same as ARP timeout */
195
#define DEFAULT_MIN_STABLE_AGE 1
196
197
/******************************************************************
198
NETGRAPH PARSE TYPES
199
******************************************************************/
200
201
/*
202
* How to determine the length of the table returned by NGM_BRIDGE_GET_TABLE
203
*/
204
static int
205
ng_bridge_getTableLength(const struct ng_parse_type *type,
206
const u_char *start, const u_char *buf)
207
{
208
const struct ng_bridge_host_ary *const hary
209
= (const struct ng_bridge_host_ary *)(buf - sizeof(u_int32_t));
210
211
return hary->numHosts;
212
}
213
214
/* Parse type for struct ng_bridge_host_ary */
215
static const struct ng_parse_struct_field ng_bridge_host_type_fields[]
216
= NG_BRIDGE_HOST_TYPE_INFO(&ng_parse_enaddr_type);
217
static const struct ng_parse_type ng_bridge_host_type = {
218
&ng_parse_struct_type,
219
&ng_bridge_host_type_fields
220
};
221
static const struct ng_parse_array_info ng_bridge_hary_type_info = {
222
&ng_bridge_host_type,
223
ng_bridge_getTableLength
224
};
225
static const struct ng_parse_type ng_bridge_hary_type = {
226
&ng_parse_array_type,
227
&ng_bridge_hary_type_info
228
};
229
static const struct ng_parse_struct_field ng_bridge_host_ary_type_fields[]
230
= NG_BRIDGE_HOST_ARY_TYPE_INFO(&ng_bridge_hary_type);
231
static const struct ng_parse_type ng_bridge_host_ary_type = {
232
&ng_parse_struct_type,
233
&ng_bridge_host_ary_type_fields
234
};
235
236
/* Parse type for struct ng_bridge_config */
237
static const struct ng_parse_struct_field ng_bridge_config_type_fields[]
238
= NG_BRIDGE_CONFIG_TYPE_INFO;
239
static const struct ng_parse_type ng_bridge_config_type = {
240
&ng_parse_struct_type,
241
&ng_bridge_config_type_fields
242
};
243
244
/* Parse type for struct ng_bridge_link_stat */
245
static const struct ng_parse_struct_field ng_bridge_stats_type_fields[]
246
= NG_BRIDGE_STATS_TYPE_INFO;
247
static const struct ng_parse_type ng_bridge_stats_type = {
248
&ng_parse_struct_type,
249
&ng_bridge_stats_type_fields
250
};
251
/* Parse type for struct ng_bridge_move_host */
252
static const struct ng_parse_struct_field ng_bridge_move_host_type_fields[]
253
= NG_BRIDGE_MOVE_HOST_TYPE_INFO(&ng_parse_enaddr_type);
254
static const struct ng_parse_type ng_bridge_move_host_type = {
255
&ng_parse_struct_type,
256
&ng_bridge_move_host_type_fields
257
};
258
259
/* List of commands and how to convert arguments to/from ASCII */
260
static const struct ng_cmdlist ng_bridge_cmdlist[] = {
261
{
262
NGM_BRIDGE_COOKIE,
263
NGM_BRIDGE_SET_CONFIG,
264
"setconfig",
265
&ng_bridge_config_type,
266
NULL
267
},
268
{
269
NGM_BRIDGE_COOKIE,
270
NGM_BRIDGE_GET_CONFIG,
271
"getconfig",
272
NULL,
273
&ng_bridge_config_type
274
},
275
{
276
NGM_BRIDGE_COOKIE,
277
NGM_BRIDGE_RESET,
278
"reset",
279
NULL,
280
NULL
281
},
282
{
283
NGM_BRIDGE_COOKIE,
284
NGM_BRIDGE_GET_STATS,
285
"getstats",
286
&ng_parse_uint32_type,
287
&ng_bridge_stats_type
288
},
289
{
290
NGM_BRIDGE_COOKIE,
291
NGM_BRIDGE_CLR_STATS,
292
"clrstats",
293
&ng_parse_uint32_type,
294
NULL
295
},
296
{
297
NGM_BRIDGE_COOKIE,
298
NGM_BRIDGE_GETCLR_STATS,
299
"getclrstats",
300
&ng_parse_uint32_type,
301
&ng_bridge_stats_type
302
},
303
{
304
NGM_BRIDGE_COOKIE,
305
NGM_BRIDGE_GET_TABLE,
306
"gettable",
307
NULL,
308
&ng_bridge_host_ary_type
309
},
310
{
311
NGM_BRIDGE_COOKIE,
312
NGM_BRIDGE_SET_PERSISTENT,
313
"setpersistent",
314
NULL,
315
NULL
316
},
317
{
318
NGM_BRIDGE_COOKIE,
319
NGM_BRIDGE_MOVE_HOST,
320
"movehost",
321
&ng_bridge_move_host_type,
322
NULL
323
},
324
{ 0 }
325
};
326
327
/* Node type descriptor */
328
static struct ng_type ng_bridge_typestruct = {
329
.version = NG_ABI_VERSION,
330
.name = NG_BRIDGE_NODE_TYPE,
331
.constructor = ng_bridge_constructor,
332
.rcvmsg = ng_bridge_rcvmsg,
333
.shutdown = ng_bridge_shutdown,
334
.newhook = ng_bridge_newhook,
335
.rcvdata = ng_bridge_rcvdata,
336
.disconnect = ng_bridge_disconnect,
337
.cmdlist = ng_bridge_cmdlist,
338
};
339
NETGRAPH_INIT(bridge, &ng_bridge_typestruct);
340
341
/******************************************************************
342
NETGRAPH NODE METHODS
343
******************************************************************/
344
345
/*
346
* Node constructor
347
*/
348
static int
349
ng_bridge_constructor(node_p node)
350
{
351
priv_p priv;
352
353
/* Allocate and initialize private info */
354
priv = malloc(sizeof(*priv), M_NETGRAPH_BRIDGE, M_WAITOK | M_ZERO);
355
ng_callout_init(&priv->timer);
356
357
/* Allocate and initialize hash table, etc. */
358
priv->tab = malloc(MIN_BUCKETS * sizeof(*priv->tab),
359
M_NETGRAPH_BRIDGE, M_WAITOK | M_ZERO);
360
priv->numBuckets = MIN_BUCKETS;
361
priv->hashMask = MIN_BUCKETS - 1;
362
priv->conf.debugLevel = 1;
363
priv->conf.loopTimeout = DEFAULT_LOOP_TIMEOUT;
364
priv->conf.maxStaleness = DEFAULT_MAX_STALENESS;
365
priv->conf.minStableAge = DEFAULT_MIN_STABLE_AGE;
366
priv->sendUnknown = 1; /* classic bridge */
367
368
NG_NODE_SET_PRIVATE(node, priv);
369
priv->node = node;
370
371
/* Allocators for links. Historically "uplink0" is not allowed. */
372
priv->linkUnit = new_unrhdr(0, INT_MAX, NULL);
373
priv->uplinkUnit = new_unrhdr(1, INT_MAX, NULL);
374
375
/* Start timer; timer is always running while node is alive */
376
ng_callout(&priv->timer, node, NULL, hz, ng_bridge_timeout, NULL, 0);
377
378
/* Done */
379
return (0);
380
}
381
382
/*
383
* Method for attaching a new hook
384
*/
385
static int
386
ng_bridge_newhook(node_p node, hook_p hook, const char *name)
387
{
388
const priv_p priv = NG_NODE_PRIVATE(node);
389
link_p link;
390
bool isUplink;
391
uint32_t linkNum;
392
struct unrhdr *unit;
393
394
const struct ng_link_prefix *pfx = ng_get_link_prefix(name);
395
if (pfx == NULL)
396
return (EINVAL); /* not a valid prefix */
397
398
isUplink = (pfx == &uplink_pfx);
399
unit = isUplink ? priv->uplinkUnit : priv->linkUnit;
400
401
if (strlen(name) > pfx->len) { /* given number */
402
char linkName[NG_HOOKSIZ];
403
int rvnum __diagused;
404
405
linkNum = strtoul(name + pfx->len, NULL, 10);
406
/* Validate by comparing against the reconstucted name. */
407
snprintf(linkName, sizeof(linkName), "%s%u", pfx->prefix,
408
linkNum);
409
if (strcmp(linkName, name) != 0)
410
return (EINVAL);
411
if (linkNum == 0 && isUplink)
412
return (EINVAL);
413
rvnum = alloc_unr_specific(unit, linkNum);
414
MPASS(rvnum == linkNum);
415
} else {
416
/* auto-assign and update hook name */
417
linkNum = alloc_unr(unit);
418
MPASS(linkNum != -1);
419
snprintf(NG_HOOK_NAME(hook), NG_HOOKSIZ, "%s%u", pfx->prefix,
420
linkNum);
421
}
422
423
if (NG_PEER_NODE(hook) == node) {
424
free_unr(unit, linkNum);
425
return (ELOOP);
426
}
427
428
link = malloc(sizeof(*link), M_NETGRAPH_BRIDGE, M_NOWAIT | M_ZERO);
429
if (link == NULL) {
430
free_unr(unit, linkNum);
431
return (ENOMEM);
432
}
433
434
#define NG_BRIDGE_COUNTER_ALLOC(f) do { \
435
link->stats.f = counter_u64_alloc(M_NOWAIT); \
436
if (link->stats.f == NULL) \
437
goto nomem; \
438
} while (0)
439
NG_BRIDGE_COUNTER_ALLOC(recvOctets);
440
NG_BRIDGE_COUNTER_ALLOC(recvPackets);
441
NG_BRIDGE_COUNTER_ALLOC(recvMulticasts);
442
NG_BRIDGE_COUNTER_ALLOC(recvBroadcasts);
443
NG_BRIDGE_COUNTER_ALLOC(recvUnknown);
444
NG_BRIDGE_COUNTER_ALLOC(recvRunts);
445
NG_BRIDGE_COUNTER_ALLOC(recvInvalid);
446
NG_BRIDGE_COUNTER_ALLOC(xmitOctets);
447
NG_BRIDGE_COUNTER_ALLOC(xmitPackets);
448
NG_BRIDGE_COUNTER_ALLOC(xmitMulticasts);
449
NG_BRIDGE_COUNTER_ALLOC(xmitBroadcasts);
450
NG_BRIDGE_COUNTER_ALLOC(loopDrops);
451
NG_BRIDGE_COUNTER_ALLOC(memoryFailures);
452
#undef NG_BRIDGE_COUNTER_ALLOC
453
454
link->hook = hook;
455
if (isUplink) {
456
link->learnMac = 0;
457
link->sendUnknown = 1;
458
if (priv->numLinks == 0) /* if the first link is an uplink */
459
priv->sendUnknown = 0; /* switch to restrictive mode */
460
} else {
461
link->learnMac = 1;
462
link->sendUnknown = priv->sendUnknown;
463
}
464
465
NG_HOOK_SET_PRIVATE(hook, link);
466
priv->numLinks++;
467
return (0);
468
469
nomem:
470
free_unr(unit, linkNum);
471
ng_bridge_free_link(link);
472
return (ENOMEM);
473
}
474
475
/*
476
* Receive a control message
477
*/
478
static void
479
ng_bridge_clear_link_stats(struct ng_bridge_link_kernel_stats *p)
480
{
481
counter_u64_zero(p->recvOctets);
482
counter_u64_zero(p->recvPackets);
483
counter_u64_zero(p->recvMulticasts);
484
counter_u64_zero(p->recvBroadcasts);
485
counter_u64_zero(p->recvUnknown);
486
counter_u64_zero(p->recvRunts);
487
counter_u64_zero(p->recvInvalid);
488
counter_u64_zero(p->xmitOctets);
489
counter_u64_zero(p->xmitPackets);
490
counter_u64_zero(p->xmitMulticasts);
491
counter_u64_zero(p->xmitBroadcasts);
492
counter_u64_zero(p->loopDrops);
493
p->loopDetects = 0;
494
counter_u64_zero(p->memoryFailures);
495
}
496
497
static void
498
ng_bridge_free_link(link_p link)
499
{
500
counter_u64_free(link->stats.recvOctets);
501
counter_u64_free(link->stats.recvPackets);
502
counter_u64_free(link->stats.recvMulticasts);
503
counter_u64_free(link->stats.recvBroadcasts);
504
counter_u64_free(link->stats.recvUnknown);
505
counter_u64_free(link->stats.recvRunts);
506
counter_u64_free(link->stats.recvInvalid);
507
counter_u64_free(link->stats.xmitOctets);
508
counter_u64_free(link->stats.xmitPackets);
509
counter_u64_free(link->stats.xmitMulticasts);
510
counter_u64_free(link->stats.xmitBroadcasts);
511
counter_u64_free(link->stats.loopDrops);
512
counter_u64_free(link->stats.memoryFailures);
513
free(link, M_NETGRAPH_BRIDGE);
514
}
515
516
static int
517
ng_bridge_reset_link(hook_p hook, void *arg __unused)
518
{
519
link_p priv = NG_HOOK_PRIVATE(hook);
520
521
priv->loopCount = 0;
522
ng_bridge_clear_link_stats(&priv->stats);
523
return (1);
524
}
525
526
static int
527
ng_bridge_rcvmsg(node_p node, item_p item, hook_p lasthook)
528
{
529
const priv_p priv = NG_NODE_PRIVATE(node);
530
struct ng_mesg *resp = NULL;
531
int error = 0;
532
struct ng_mesg *msg;
533
534
NGI_GET_MSG(item, msg);
535
switch (msg->header.typecookie) {
536
case NGM_BRIDGE_COOKIE:
537
switch (msg->header.cmd) {
538
case NGM_BRIDGE_GET_CONFIG:
539
{
540
struct ng_bridge_config *conf;
541
542
NG_MKRESPONSE(resp, msg,
543
sizeof(struct ng_bridge_config), M_NOWAIT);
544
if (resp == NULL) {
545
error = ENOMEM;
546
break;
547
}
548
conf = (struct ng_bridge_config *)resp->data;
549
*conf = priv->conf; /* no sanity checking needed */
550
break;
551
}
552
case NGM_BRIDGE_SET_CONFIG:
553
{
554
struct ng_bridge_config *conf;
555
556
if (msg->header.arglen
557
!= sizeof(struct ng_bridge_config)) {
558
error = EINVAL;
559
break;
560
}
561
conf = (struct ng_bridge_config *)msg->data;
562
priv->conf = *conf;
563
break;
564
}
565
case NGM_BRIDGE_RESET:
566
{
567
/* Flush all entries in the hash table */
568
ng_bridge_remove_hosts(priv, NULL);
569
570
/* Reset all loop detection counters and stats */
571
NG_NODE_FOREACH_HOOK(node, ng_bridge_reset_link, NULL);
572
break;
573
}
574
case NGM_BRIDGE_GET_STATS:
575
case NGM_BRIDGE_CLR_STATS:
576
case NGM_BRIDGE_GETCLR_STATS:
577
{
578
hook_p hook;
579
link_p link;
580
char linkName[NG_HOOKSIZ];
581
int linkNum;
582
583
/* Get link number */
584
if (msg->header.arglen != sizeof(u_int32_t)) {
585
error = EINVAL;
586
break;
587
}
588
linkNum = *((int32_t *)msg->data);
589
if (linkNum < 0)
590
snprintf(linkName, sizeof(linkName),
591
"%s%u", NG_BRIDGE_HOOK_UPLINK_PREFIX, -linkNum);
592
else
593
snprintf(linkName, sizeof(linkName),
594
"%s%u", NG_BRIDGE_HOOK_LINK_PREFIX, linkNum);
595
596
if ((hook = ng_findhook(node, linkName)) == NULL) {
597
error = ENOTCONN;
598
break;
599
}
600
link = NG_HOOK_PRIVATE(hook);
601
602
/* Get/clear stats */
603
if (msg->header.cmd != NGM_BRIDGE_CLR_STATS) {
604
struct ng_bridge_link_stats *rs;
605
606
NG_MKRESPONSE(resp, msg,
607
sizeof(link->stats), M_NOWAIT);
608
if (resp == NULL) {
609
error = ENOMEM;
610
break;
611
}
612
rs = (struct ng_bridge_link_stats *)resp->data;
613
#define FETCH(x) rs->x = counter_u64_fetch(link->stats.x)
614
FETCH(recvOctets);
615
FETCH(recvPackets);
616
FETCH(recvMulticasts);
617
FETCH(recvBroadcasts);
618
FETCH(recvUnknown);
619
FETCH(recvRunts);
620
FETCH(recvInvalid);
621
FETCH(xmitOctets);
622
FETCH(xmitPackets);
623
FETCH(xmitMulticasts);
624
FETCH(xmitBroadcasts);
625
FETCH(loopDrops);
626
rs->loopDetects = link->stats.loopDetects;
627
FETCH(memoryFailures);
628
#undef FETCH
629
}
630
if (msg->header.cmd != NGM_BRIDGE_GET_STATS)
631
ng_bridge_clear_link_stats(&link->stats);
632
break;
633
}
634
case NGM_BRIDGE_GET_TABLE:
635
{
636
struct ng_bridge_host_ary *ary;
637
struct ng_bridge_host *host;
638
int i = 0, bucket;
639
640
NG_MKRESPONSE(resp, msg, sizeof(*ary)
641
+ (priv->numHosts * sizeof(*ary->hosts)), M_NOWAIT);
642
if (resp == NULL) {
643
error = ENOMEM;
644
break;
645
}
646
ary = (struct ng_bridge_host_ary *)resp->data;
647
ary->numHosts = priv->numHosts;
648
for (bucket = 0; bucket < priv->numBuckets; bucket++) {
649
SLIST_FOREACH(host, &priv->tab[bucket], next) {
650
memcpy(ary->hosts[i].addr,
651
host->addr,
652
sizeof(ary->hosts[i].addr));
653
ary->hosts[i].age = host->age;
654
ary->hosts[i].staleness = host->staleness;
655
strncpy(ary->hosts[i].hook,
656
NG_HOOK_NAME(host->link->hook),
657
sizeof(ary->hosts[i].hook));
658
i++;
659
}
660
}
661
break;
662
}
663
case NGM_BRIDGE_SET_PERSISTENT:
664
{
665
priv->persistent = 1;
666
break;
667
}
668
case NGM_BRIDGE_MOVE_HOST:
669
{
670
struct ng_bridge_move_host *mh;
671
hook_p hook;
672
673
if (msg->header.arglen < sizeof(*mh)) {
674
error = EINVAL;
675
break;
676
}
677
mh = (struct ng_bridge_move_host *)msg->data;
678
hook = (mh->hook[0] == 0)
679
? lasthook
680
: ng_findhook(node, mh->hook);
681
if (hook == NULL) {
682
error = ENOENT;
683
break;
684
}
685
error = ng_bridge_put(priv, mh->addr, NG_HOOK_PRIVATE(hook));
686
break;
687
}
688
default:
689
error = EINVAL;
690
break;
691
}
692
break;
693
default:
694
error = EINVAL;
695
break;
696
}
697
698
/* Done */
699
NG_RESPOND_MSG(error, node, item, resp);
700
NG_FREE_MSG(msg);
701
return (error);
702
}
703
704
/*
705
* Receive data on a hook
706
*/
707
struct ng_bridge_send_ctx {
708
link_p foundFirst, incoming;
709
struct mbuf * m;
710
int manycast, error;
711
};
712
713
/*
714
* Update stats and send out
715
*/
716
static inline int
717
ng_bridge_send_data(link_cp dst, int manycast, struct mbuf *m, item_p item) {
718
int error = 0;
719
size_t len = m->m_pkthdr.len;
720
721
if(item != NULL)
722
NG_FWD_NEW_DATA(error, item, dst->hook, m);
723
else
724
NG_SEND_DATA_ONLY(error, dst->hook, m);
725
726
if (error) {
727
if (error == ENOMEM)
728
counter_u64_add(dst->stats.memoryFailures, 1);
729
/* The packet is still ours */
730
if (item != NULL)
731
NG_FREE_ITEM(item);
732
if (m != NULL)
733
NG_FREE_M(m);
734
return (error);
735
}
736
737
counter_u64_add(dst->stats.xmitPackets, 1);
738
counter_u64_add(dst->stats.xmitOctets, len);
739
switch (manycast) {
740
default: /* unknown unicast */
741
break;
742
case 1: /* multicast */
743
counter_u64_add(dst->stats.xmitMulticasts, 1);
744
break;
745
case 2: /* broadcast */
746
counter_u64_add(dst->stats.xmitBroadcasts, 1);
747
break;
748
}
749
return (0);
750
}
751
752
/*
753
* Loop body for sending to multiple destinations
754
* return 0 to stop looping
755
*/
756
static int
757
ng_bridge_send_ctx(hook_p dst, void *arg)
758
{
759
struct ng_bridge_send_ctx *ctx = arg;
760
link_p destLink = NG_HOOK_PRIVATE(dst);
761
struct mbuf *m2 = NULL;
762
int error = 0;
763
764
/* Skip incoming link */
765
if (destLink == ctx->incoming) {
766
return (1);
767
}
768
769
/* Skip sending unknowns to undesired links */
770
if (!ctx->manycast && !destLink->sendUnknown)
771
return (1);
772
773
if (ctx->foundFirst == NULL) {
774
/*
775
* This is the first usable link we have found.
776
* Reserve it for the originals.
777
* If we never find another we save a copy.
778
*/
779
ctx->foundFirst = destLink;
780
return (1);
781
}
782
783
/*
784
* It's usable link but not the reserved (first) one.
785
* Copy mbuf info for sending.
786
*/
787
m2 = m_dup(ctx->m, M_NOWAIT);
788
if (m2 == NULL) {
789
counter_u64_add(ctx->incoming->stats.memoryFailures, 1);
790
ctx->error = ENOBUFS;
791
return (0); /* abort loop, do not try again and again */
792
}
793
794
/* Send packet */
795
error = ng_bridge_send_data(destLink, ctx->manycast, m2, NULL);
796
if (error)
797
ctx->error = error;
798
return (1);
799
}
800
801
static int
802
ng_bridge_rcvdata(hook_p hook, item_p item)
803
{
804
const node_p node = NG_HOOK_NODE(hook);
805
const priv_p priv = NG_NODE_PRIVATE(node);
806
struct ng_bridge_host *host;
807
struct ether_header *eh;
808
struct ng_bridge_send_ctx ctx = { 0 };
809
810
NGI_GET_M(item, ctx.m);
811
812
ctx.incoming = NG_HOOK_PRIVATE(hook);
813
/* Sanity check packet and pull up header */
814
if (ctx.m->m_pkthdr.len < ETHER_HDR_LEN) {
815
counter_u64_add(ctx.incoming->stats.recvRunts, 1);
816
NG_FREE_ITEM(item);
817
NG_FREE_M(ctx.m);
818
return (EINVAL);
819
}
820
if (ctx.m->m_len < ETHER_HDR_LEN && !(ctx.m = m_pullup(ctx.m, ETHER_HDR_LEN))) {
821
counter_u64_add(ctx.incoming->stats.memoryFailures, 1);
822
NG_FREE_ITEM(item);
823
return (ENOBUFS);
824
}
825
eh = mtod(ctx.m, struct ether_header *);
826
if ((eh->ether_shost[0] & 1) != 0) {
827
counter_u64_add(ctx.incoming->stats.recvInvalid, 1);
828
NG_FREE_ITEM(item);
829
NG_FREE_M(ctx.m);
830
return (EINVAL);
831
}
832
833
/* Is link disabled due to a loopback condition? */
834
if (ctx.incoming->loopCount != 0) {
835
counter_u64_add(ctx.incoming->stats.loopDrops, 1);
836
NG_FREE_ITEM(item);
837
NG_FREE_M(ctx.m);
838
return (ELOOP);
839
}
840
841
/* Update stats */
842
counter_u64_add(ctx.incoming->stats.recvPackets, 1);
843
counter_u64_add(ctx.incoming->stats.recvOctets, ctx.m->m_pkthdr.len);
844
if ((ctx.manycast = (eh->ether_dhost[0] & 1)) != 0) {
845
if (ETHER_EQUAL(eh->ether_dhost, ng_bridge_bcast_addr)) {
846
counter_u64_add(ctx.incoming->stats.recvBroadcasts, 1);
847
ctx.manycast = 2;
848
} else
849
counter_u64_add(ctx.incoming->stats.recvMulticasts, 1);
850
}
851
852
/* Look up packet's source Ethernet address in hashtable */
853
if ((host = ng_bridge_get(priv, eh->ether_shost)) != NULL)
854
/* Update time since last heard from this host.
855
* This is safe without locking, because it's
856
* the only operation during shared access.
857
*/
858
if (__predict_false(host->staleness > 0))
859
host->staleness = 0;
860
861
if ((host == NULL && ctx.incoming->learnMac) ||
862
(host != NULL && host->link != ctx.incoming)) {
863
struct ng_mesg *msg;
864
struct ng_bridge_move_host *mh;
865
int error = 0;
866
867
NG_MKMESSAGE(msg, NGM_BRIDGE_COOKIE, NGM_BRIDGE_MOVE_HOST,
868
sizeof(*mh), M_NOWAIT);
869
if (msg == NULL) {
870
counter_u64_add(ctx.incoming->stats.memoryFailures, 1);
871
NG_FREE_ITEM(item);
872
NG_FREE_M(ctx.m);
873
return (ENOMEM);
874
}
875
mh = (struct ng_bridge_move_host *)msg->data;
876
strncpy(mh->hook, NG_HOOK_NAME(ctx.incoming->hook),
877
sizeof(mh->hook));
878
memcpy(mh->addr, eh->ether_shost, sizeof(mh->addr));
879
NG_SEND_MSG_ID(error, node, msg, NG_NODE_ID(node),
880
NG_NODE_ID(node));
881
if (error)
882
counter_u64_add(ctx.incoming->stats.memoryFailures, 1);
883
}
884
885
if (host != NULL && host->link != ctx.incoming) {
886
if (host->age < priv->conf.minStableAge) {
887
/* Drop packet on instable links */
888
counter_u64_add(ctx.incoming->stats.loopDrops, 1);
889
NG_FREE_ITEM(item);
890
NG_FREE_M(ctx.m);
891
return (ELOOP);
892
}
893
}
894
895
/*
896
* If unicast and destination host known, deliver to host's link,
897
* unless it is the same link as the packet came in on.
898
*/
899
if (!ctx.manycast) {
900
/* Determine packet destination link */
901
if ((host = ng_bridge_get(priv, eh->ether_dhost)) != NULL) {
902
link_p destLink = host->link;
903
904
/* If destination same as incoming link, do nothing */
905
if (destLink == ctx.incoming) {
906
NG_FREE_ITEM(item);
907
NG_FREE_M(ctx.m);
908
return (0);
909
}
910
911
/* Deliver packet out the destination link */
912
return (ng_bridge_send_data(destLink, ctx.manycast, ctx.m, item));
913
}
914
915
/* Destination host is not known */
916
counter_u64_add(ctx.incoming->stats.recvUnknown, 1);
917
}
918
919
/* Distribute unknown, multicast, broadcast pkts to all other links */
920
NG_NODE_FOREACH_HOOK(node, ng_bridge_send_ctx, &ctx);
921
922
/* Finally send out on the first link found */
923
if (ctx.foundFirst != NULL) {
924
int error = ng_bridge_send_data(ctx.foundFirst, ctx.manycast, ctx.m, item);
925
if (error)
926
ctx.error = error;
927
} else { /* nothing to send at all */
928
NG_FREE_ITEM(item);
929
NG_FREE_M(ctx.m);
930
}
931
932
return (ctx.error);
933
}
934
935
/*
936
* Shutdown node
937
*/
938
static int
939
ng_bridge_shutdown(node_p node)
940
{
941
const priv_p priv = NG_NODE_PRIVATE(node);
942
943
/*
944
* Shut down everything including the timer. Even if the
945
* callout has already been dequeued and is about to be
946
* run, ng_bridge_timeout() won't be fired as the node
947
* is already marked NGF_INVALID, so we're safe to free
948
* the node now.
949
*/
950
KASSERT(priv->numLinks == 0 && priv->numHosts == 0,
951
("%s: numLinks=%d numHosts=%d",
952
__func__, priv->numLinks, priv->numHosts));
953
ng_uncallout(&priv->timer, node);
954
delete_unrhdr(priv->linkUnit);
955
delete_unrhdr(priv->uplinkUnit);
956
NG_NODE_SET_PRIVATE(node, NULL);
957
NG_NODE_UNREF(node);
958
free(priv->tab, M_NETGRAPH_BRIDGE);
959
free(priv, M_NETGRAPH_BRIDGE);
960
return (0);
961
}
962
963
/*
964
* Hook disconnection.
965
*/
966
static int
967
ng_bridge_disconnect(hook_p hook)
968
{
969
char *name = NG_HOOK_NAME(hook);
970
const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
971
link_p link = NG_HOOK_PRIVATE(hook);
972
const struct ng_link_prefix *pfx = ng_get_link_prefix(name);
973
uint32_t linkNum;
974
975
/* Remove all hosts associated with this link */
976
ng_bridge_remove_hosts(priv, link);
977
978
/* Free associated link information */
979
ng_bridge_free_link(link);
980
priv->numLinks--;
981
982
linkNum = strtoul(name + pfx->len, NULL, 10);
983
free_unr(pfx == &link_pfx ? priv->linkUnit: priv->uplinkUnit, linkNum);
984
985
/* If no more hooks, go away */
986
if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
987
&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))
988
&& !priv->persistent) {
989
ng_rmnode_self(NG_HOOK_NODE(hook));
990
}
991
return (0);
992
}
993
994
/******************************************************************
995
HASH TABLE FUNCTIONS
996
******************************************************************/
997
998
/*
999
* Hash algorithm
1000
*/
1001
#define HASH(addr,mask) ( (((const u_int16_t *)(addr))[0] \
1002
^ ((const u_int16_t *)(addr))[1] \
1003
^ ((const u_int16_t *)(addr))[2]) & (mask) )
1004
1005
/*
1006
* Find a host entry in the table.
1007
*/
1008
static struct ng_bridge_host *
1009
ng_bridge_get(priv_cp priv, const u_char *addr)
1010
{
1011
const int bucket = HASH(addr, priv->hashMask);
1012
struct ng_bridge_host *host;
1013
1014
SLIST_FOREACH(host, &priv->tab[bucket], next) {
1015
if (ETHER_EQUAL(host->addr, addr))
1016
return (host);
1017
}
1018
return (NULL);
1019
}
1020
1021
/*
1022
* Add a host entry to the table. If it already exists, move it
1023
* to the new link. Returns 0 on success.
1024
*/
1025
static int
1026
ng_bridge_put(priv_p priv, const u_char *addr, link_p link)
1027
{
1028
const int bucket = HASH(addr, priv->hashMask);
1029
struct ng_bridge_host *host;
1030
1031
if ((host = ng_bridge_get(priv, addr)) != NULL) {
1032
/* Host already on the correct link? */
1033
if (host->link == link)
1034
return 0;
1035
1036
/* Move old host over to new link */
1037
if (host->age >= priv->conf.minStableAge) {
1038
host->link = link;
1039
host->age = 0;
1040
return (0);
1041
}
1042
/*
1043
* If the host was recently moved to the old link and
1044
* it's now jumping to a new link, declare a loopback
1045
* condition.
1046
*/
1047
if (priv->conf.debugLevel >= 2)
1048
log(LOG_WARNING, "ng_bridge: %s:"
1049
" loopback detected on %s\n",
1050
ng_bridge_nodename(priv->node),
1051
NG_HOOK_NAME(link->hook));
1052
1053
/* Mark link as linka non grata */
1054
link->loopCount = priv->conf.loopTimeout;
1055
link->stats.loopDetects++;
1056
1057
/* Forget all hosts on this link */
1058
ng_bridge_remove_hosts(priv, link);
1059
return (ELOOP);
1060
}
1061
1062
/* Allocate and initialize new hashtable entry */
1063
host = malloc(sizeof(*host), M_NETGRAPH_BRIDGE, M_NOWAIT);
1064
if (host == NULL)
1065
return (ENOMEM);
1066
bcopy(addr, host->addr, ETHER_ADDR_LEN);
1067
host->link = link;
1068
host->staleness = 0;
1069
host->age = 0;
1070
1071
/* Add new element to hash bucket */
1072
SLIST_INSERT_HEAD(&priv->tab[bucket], host, next);
1073
priv->numHosts++;
1074
1075
/* Resize table if necessary */
1076
ng_bridge_rehash(priv);
1077
return (0);
1078
}
1079
1080
/*
1081
* Resize the hash table. We try to maintain the number of buckets
1082
* such that the load factor is in the range 0.25 to 1.0.
1083
*
1084
* If we can't get the new memory then we silently fail. This is OK
1085
* because things will still work and we'll try again soon anyway.
1086
*/
1087
static void
1088
ng_bridge_rehash(priv_p priv)
1089
{
1090
struct ng_bridge_bucket *newTab;
1091
int oldBucket, newBucket;
1092
int newNumBuckets;
1093
u_int newMask;
1094
1095
/* Is table too full or too empty? */
1096
if (priv->numHosts > priv->numBuckets
1097
&& (priv->numBuckets << 1) <= MAX_BUCKETS)
1098
newNumBuckets = priv->numBuckets << 1;
1099
else if (priv->numHosts < (priv->numBuckets >> 2)
1100
&& (priv->numBuckets >> 2) >= MIN_BUCKETS)
1101
newNumBuckets = priv->numBuckets >> 2;
1102
else
1103
return;
1104
newMask = newNumBuckets - 1;
1105
1106
/* Allocate and initialize new table */
1107
newTab = malloc(newNumBuckets * sizeof(*newTab),
1108
M_NETGRAPH_BRIDGE, M_NOWAIT | M_ZERO);
1109
if (newTab == NULL)
1110
return;
1111
1112
/* Move all entries from old table to new table */
1113
for (oldBucket = 0; oldBucket < priv->numBuckets; oldBucket++) {
1114
struct ng_bridge_bucket *const oldList = &priv->tab[oldBucket];
1115
1116
while (!SLIST_EMPTY(oldList)) {
1117
struct ng_bridge_host *const host
1118
= SLIST_FIRST(oldList);
1119
1120
SLIST_REMOVE_HEAD(oldList, next);
1121
newBucket = HASH(host->addr, newMask);
1122
SLIST_INSERT_HEAD(&newTab[newBucket], host, next);
1123
}
1124
}
1125
1126
/* Replace old table with new one */
1127
if (priv->conf.debugLevel >= 3) {
1128
log(LOG_INFO, "ng_bridge: %s: table size %d -> %d\n",
1129
ng_bridge_nodename(priv->node),
1130
priv->numBuckets, newNumBuckets);
1131
}
1132
free(priv->tab, M_NETGRAPH_BRIDGE);
1133
priv->numBuckets = newNumBuckets;
1134
priv->hashMask = newMask;
1135
priv->tab = newTab;
1136
return;
1137
}
1138
1139
/******************************************************************
1140
MISC FUNCTIONS
1141
******************************************************************/
1142
1143
static const struct ng_link_prefix *
1144
ng_get_link_prefix(const char *name)
1145
{
1146
static const struct ng_link_prefix *pfxs[] =
1147
{ &link_pfx, &uplink_pfx, };
1148
1149
for (u_int i = 0; i < nitems(pfxs); i++)
1150
if (strncmp(pfxs[i]->prefix, name, pfxs[i]->len) == 0)
1151
return (pfxs[i]);
1152
1153
return (NULL);
1154
}
1155
1156
/*
1157
* Remove all hosts associated with a specific link from the hashtable.
1158
* If linkNum == -1, then remove all hosts in the table.
1159
*/
1160
static void
1161
ng_bridge_remove_hosts(priv_p priv, link_p link)
1162
{
1163
int bucket;
1164
1165
for (bucket = 0; bucket < priv->numBuckets; bucket++) {
1166
struct ng_bridge_host **hptr = &SLIST_FIRST(&priv->tab[bucket]);
1167
1168
while (*hptr != NULL) {
1169
struct ng_bridge_host *const host = *hptr;
1170
1171
if (link == NULL || host->link == link) {
1172
*hptr = SLIST_NEXT(host, next);
1173
free(host, M_NETGRAPH_BRIDGE);
1174
priv->numHosts--;
1175
} else
1176
hptr = &SLIST_NEXT(host, next);
1177
}
1178
}
1179
}
1180
1181
/*
1182
* Handle our once-per-second timeout event. We do two things:
1183
* we decrement link->loopCount for those links being muted due to
1184
* a detected loopback condition, and we remove any hosts from
1185
* the hashtable whom we haven't heard from in a long while.
1186
*/
1187
static int
1188
ng_bridge_unmute(hook_p hook, void *arg)
1189
{
1190
link_p link = NG_HOOK_PRIVATE(hook);
1191
node_p node = NG_HOOK_NODE(hook);
1192
priv_p priv = NG_NODE_PRIVATE(node);
1193
int *counter = arg;
1194
1195
if (link->loopCount != 0) {
1196
link->loopCount--;
1197
if (link->loopCount == 0 && priv->conf.debugLevel >= 2) {
1198
log(LOG_INFO, "ng_bridge: %s:"
1199
" restoring looped back %s\n",
1200
ng_bridge_nodename(node), NG_HOOK_NAME(hook));
1201
}
1202
}
1203
(*counter)++;
1204
return (1);
1205
}
1206
1207
static void
1208
ng_bridge_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1209
{
1210
const priv_p priv = NG_NODE_PRIVATE(node);
1211
int bucket;
1212
int counter = 0;
1213
1214
/* Update host time counters and remove stale entries */
1215
for (bucket = 0; bucket < priv->numBuckets; bucket++) {
1216
struct ng_bridge_host **hptr = &SLIST_FIRST(&priv->tab[bucket]);
1217
1218
while (*hptr != NULL) {
1219
struct ng_bridge_host *const host = *hptr;
1220
1221
/* Remove hosts we haven't heard from in a while */
1222
if (++host->staleness >= priv->conf.maxStaleness) {
1223
*hptr = SLIST_NEXT(host, next);
1224
free(host, M_NETGRAPH_BRIDGE);
1225
priv->numHosts--;
1226
} else {
1227
if (host->age < 0xffff)
1228
host->age++;
1229
hptr = &SLIST_NEXT(host, next);
1230
counter++;
1231
}
1232
}
1233
}
1234
KASSERT(priv->numHosts == counter,
1235
("%s: hosts: %d != %d", __func__, priv->numHosts, counter));
1236
1237
/* Decrease table size if necessary */
1238
ng_bridge_rehash(priv);
1239
1240
/* Decrease loop counter on muted looped back links */
1241
counter = 0;
1242
NG_NODE_FOREACH_HOOK(node, ng_bridge_unmute, &counter);
1243
KASSERT(priv->numLinks == counter,
1244
("%s: links: %d != %d", __func__, priv->numLinks, counter));
1245
1246
/* Register a new timeout, keeping the existing node reference */
1247
ng_callout(&priv->timer, node, NULL, hz, ng_bridge_timeout, NULL, 0);
1248
}
1249
1250
/*
1251
* Return node's "name", even if it doesn't have one.
1252
*/
1253
static const char *
1254
ng_bridge_nodename(node_cp node)
1255
{
1256
static char name[NG_NODESIZ];
1257
1258
if (NG_NODE_HAS_NAME(node))
1259
snprintf(name, sizeof(name), "%s", NG_NODE_NAME(node));
1260
else
1261
snprintf(name, sizeof(name), "[%x]", ng_node2ID(node));
1262
return name;
1263
}
1264
1265