Changeset View
Changeset View
Standalone View
Standalone View
sys/netgraph/ng_bridge.c
Show First 20 Lines • Show All 97 Lines • ▼ Show 20 Lines | struct ng_bridge_link_kernel_stats { | ||||
counter_u64_t recvUnknown; /* pkts rec'd with unknown dest addr */ | counter_u64_t recvUnknown; /* pkts rec'd with unknown dest addr */ | ||||
counter_u64_t recvRunts; /* pkts rec'd less than 14 bytes */ | counter_u64_t recvRunts; /* pkts rec'd less than 14 bytes */ | ||||
counter_u64_t recvInvalid; /* pkts rec'd with bogus source addr */ | counter_u64_t recvInvalid; /* pkts rec'd with bogus source addr */ | ||||
counter_u64_t xmitOctets; /* total octets xmit'd on link */ | counter_u64_t xmitOctets; /* total octets xmit'd on link */ | ||||
counter_u64_t xmitPackets; /* total pkts xmit'd on link */ | counter_u64_t xmitPackets; /* total pkts xmit'd on link */ | ||||
counter_u64_t xmitMulticasts; /* multicast pkts xmit'd on link */ | counter_u64_t xmitMulticasts; /* multicast pkts xmit'd on link */ | ||||
counter_u64_t xmitBroadcasts; /* broadcast pkts xmit'd on link */ | counter_u64_t xmitBroadcasts; /* broadcast pkts xmit'd on link */ | ||||
counter_u64_t loopDrops; /* pkts dropped due to loopback */ | counter_u64_t loopDrops; /* pkts dropped due to loopback */ | ||||
counter_u64_t loopDetects; /* number of loop detections */ | u_int64_t loopDetects; /* number of loop detections */ | ||||
counter_u64_t memoryFailures; /* times couldn't get mem or mbuf */ | counter_u64_t memoryFailures; /* times couldn't get mem or mbuf */ | ||||
}; | }; | ||||
/* Per-link private data */ | /* Per-link private data */ | ||||
struct ng_bridge_link { | struct ng_bridge_link { | ||||
hook_p hook; /* netgraph hook */ | hook_p hook; /* netgraph hook */ | ||||
u_int16_t loopCount; /* loop ignore timer */ | u_int16_t loopCount; /* loop ignore timer */ | ||||
unsigned int learnMac : 1, /* autolearn macs */ | unsigned int learnMac : 1, /* autolearn macs */ | ||||
▲ Show 20 Lines • Show All 300 Lines • ▼ Show 20 Lines | ng_bridge_newhook(node_p node, hook_p hook, const char *name) | ||||
link->stats.recvUnknown = counter_u64_alloc(M_WAITOK); | link->stats.recvUnknown = counter_u64_alloc(M_WAITOK); | ||||
link->stats.recvRunts = counter_u64_alloc(M_WAITOK); | link->stats.recvRunts = counter_u64_alloc(M_WAITOK); | ||||
link->stats.recvInvalid = counter_u64_alloc(M_WAITOK); | link->stats.recvInvalid = counter_u64_alloc(M_WAITOK); | ||||
link->stats.xmitOctets = counter_u64_alloc(M_WAITOK); | link->stats.xmitOctets = counter_u64_alloc(M_WAITOK); | ||||
link->stats.xmitPackets = counter_u64_alloc(M_WAITOK); | link->stats.xmitPackets = counter_u64_alloc(M_WAITOK); | ||||
link->stats.xmitMulticasts = counter_u64_alloc(M_WAITOK); | link->stats.xmitMulticasts = counter_u64_alloc(M_WAITOK); | ||||
link->stats.xmitBroadcasts = counter_u64_alloc(M_WAITOK); | link->stats.xmitBroadcasts = counter_u64_alloc(M_WAITOK); | ||||
link->stats.loopDrops = counter_u64_alloc(M_WAITOK); | link->stats.loopDrops = counter_u64_alloc(M_WAITOK); | ||||
link->stats.loopDetects = counter_u64_alloc(M_WAITOK); | |||||
link->stats.memoryFailures = counter_u64_alloc(M_WAITOK); | link->stats.memoryFailures = counter_u64_alloc(M_WAITOK); | ||||
link->hook = hook; | link->hook = hook; | ||||
if (isUplink) { | if (isUplink) { | ||||
link->learnMac = 0; | link->learnMac = 0; | ||||
link->sendUnknown = 1; | link->sendUnknown = 1; | ||||
if (priv->numLinks == 0) /* if the first link is an uplink */ | if (priv->numLinks == 0) /* if the first link is an uplink */ | ||||
priv->sendUnknown = 0; /* switch to restrictive mode */ | priv->sendUnknown = 0; /* switch to restrictive mode */ | ||||
Show All 19 Lines | static void ng_bridge_clear_link_stats(struct ng_bridge_link_kernel_stats * p) | ||||
counter_u64_zero(p->recvUnknown); | counter_u64_zero(p->recvUnknown); | ||||
counter_u64_zero(p->recvRunts); | counter_u64_zero(p->recvRunts); | ||||
counter_u64_zero(p->recvInvalid); | counter_u64_zero(p->recvInvalid); | ||||
counter_u64_zero(p->xmitOctets); | counter_u64_zero(p->xmitOctets); | ||||
counter_u64_zero(p->xmitPackets); | counter_u64_zero(p->xmitPackets); | ||||
counter_u64_zero(p->xmitMulticasts); | counter_u64_zero(p->xmitMulticasts); | ||||
counter_u64_zero(p->xmitBroadcasts); | counter_u64_zero(p->xmitBroadcasts); | ||||
counter_u64_zero(p->loopDrops); | counter_u64_zero(p->loopDrops); | ||||
counter_u64_zero(p->loopDetects); | p->loopDetects = 0; | ||||
counter_u64_zero(p->memoryFailures); | counter_u64_zero(p->memoryFailures); | ||||
}; | }; | ||||
static int | static int | ||||
ng_bridge_reset_link(hook_p hook, void *arg __unused) | ng_bridge_reset_link(hook_p hook, void *arg __unused) | ||||
{ | { | ||||
link_p priv = NG_HOOK_PRIVATE(hook); | link_p priv = NG_HOOK_PRIVATE(hook); | ||||
▲ Show 20 Lines • Show All 100 Lines • ▼ Show 20 Lines | #define FETCH(x) rs->x = counter_u64_fetch(link->stats.x) | ||||
FETCH(recvUnknown); | FETCH(recvUnknown); | ||||
FETCH(recvRunts); | FETCH(recvRunts); | ||||
FETCH(recvInvalid); | FETCH(recvInvalid); | ||||
FETCH(xmitOctets); | FETCH(xmitOctets); | ||||
FETCH(xmitPackets); | FETCH(xmitPackets); | ||||
FETCH(xmitMulticasts); | FETCH(xmitMulticasts); | ||||
FETCH(xmitBroadcasts); | FETCH(xmitBroadcasts); | ||||
FETCH(loopDrops); | FETCH(loopDrops); | ||||
FETCH(loopDetects); | rs->loopDetects = link->stats.loopDetects; | ||||
FETCH(memoryFailures); | FETCH(memoryFailures); | ||||
#undef FETCH | #undef FETCH | ||||
} | } | ||||
if (msg->header.cmd != NGM_BRIDGE_GET_STATS) | if (msg->header.cmd != NGM_BRIDGE_GET_STATS) | ||||
ng_bridge_clear_link_stats(&link->stats); | ng_bridge_clear_link_stats(&link->stats); | ||||
break; | break; | ||||
} | } | ||||
case NGM_BRIDGE_GET_TABLE: | case NGM_BRIDGE_GET_TABLE: | ||||
Show All 29 Lines | case NGM_BRIDGE_SET_PERSISTENT: | ||||
{ | { | ||||
priv->persistent = 1; | priv->persistent = 1; | ||||
break; | break; | ||||
} | } | ||||
case NGM_BRIDGE_MOVE_HOST: | case NGM_BRIDGE_MOVE_HOST: | ||||
{ | { | ||||
struct ng_bridge_move_host *mh; | struct ng_bridge_move_host *mh; | ||||
hook_p hook; | hook_p hook; | ||||
struct ng_bridge_host *host; | |||||
if (msg->header.arglen < sizeof(*mh)) { | if (msg->header.arglen < sizeof(*mh)) { | ||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
} | } | ||||
mh = (struct ng_bridge_move_host *)msg->data; | mh = (struct ng_bridge_move_host *)msg->data; | ||||
hook = (mh->hook[0] == 0) | hook = (mh->hook[0] == 0) | ||||
? lasthook | ? lasthook | ||||
: ng_findhook(node, mh->hook); | : ng_findhook(node, mh->hook); | ||||
if (hook == NULL) { | if (hook == NULL) { | ||||
error = ENOENT; | error = ENOENT; | ||||
break; | break; | ||||
} | } | ||||
host = ng_bridge_get(priv, mh->addr); | |||||
if (host != NULL) { | |||||
error = EADDRINUSE; | |||||
break; | |||||
} | |||||
donner: Check is now part of the insert_or_move function.
Current check was only for "fresh insert". | |||||
error = ng_bridge_put(priv, mh->addr, NG_HOOK_PRIVATE(hook)); | error = ng_bridge_put(priv, mh->addr, NG_HOOK_PRIVATE(hook)); | ||||
break; | break; | ||||
} | } | ||||
default: | default: | ||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
} | } | ||||
break; | break; | ||||
▲ Show 20 Lines • Show All 130 Lines • ▼ Show 20 Lines | if ((eh->ether_shost[0] & 1) != 0) { | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
/* Is link disabled due to a loopback condition? */ | /* Is link disabled due to a loopback condition? */ | ||||
if (ctx.incoming->loopCount != 0) { | if (ctx.incoming->loopCount != 0) { | ||||
counter_u64_add(ctx.incoming->stats.loopDrops, 1); | counter_u64_add(ctx.incoming->stats.loopDrops, 1); | ||||
NG_FREE_ITEM(item); | NG_FREE_ITEM(item); | ||||
NG_FREE_M(ctx.m); | NG_FREE_M(ctx.m); | ||||
return (ELOOP); /* XXX is this an appropriate error? */ | return (ELOOP); | ||||
} | } | ||||
/* Update stats */ | /* Update stats */ | ||||
counter_u64_add(ctx.incoming->stats.recvPackets, 1); | counter_u64_add(ctx.incoming->stats.recvPackets, 1); | ||||
counter_u64_add(ctx.incoming->stats.recvOctets, ctx.m->m_pkthdr.len); | counter_u64_add(ctx.incoming->stats.recvOctets, ctx.m->m_pkthdr.len); | ||||
if ((ctx.manycast = (eh->ether_dhost[0] & 1)) != 0) { | if ((ctx.manycast = (eh->ether_dhost[0] & 1)) != 0) { | ||||
if (ETHER_EQUAL(eh->ether_dhost, ng_bridge_bcast_addr)) { | if (ETHER_EQUAL(eh->ether_dhost, ng_bridge_bcast_addr)) { | ||||
counter_u64_add(ctx.incoming->stats.recvBroadcasts, 1); | counter_u64_add(ctx.incoming->stats.recvBroadcasts, 1); | ||||
ctx.manycast = 2; | ctx.manycast = 2; | ||||
} else | } else | ||||
counter_u64_add(ctx.incoming->stats.recvMulticasts, 1); | counter_u64_add(ctx.incoming->stats.recvMulticasts, 1); | ||||
} | } | ||||
/* Look up packet's source Ethernet address in hashtable */ | /* Look up packet's source Ethernet address in hashtable */ | ||||
if ((host = ng_bridge_get(priv, eh->ether_shost)) != NULL) { | if ((host = ng_bridge_get(priv, eh->ether_shost)) != NULL) | ||||
/* Update time since last heard from this host. | /* Update time since last heard from this host. | ||||
* This is safe without locking, because it's | * This is safe without locking, because it's | ||||
* the only operation during shared access. | * the only operation during shared access. | ||||
*/ | */ | ||||
host->staleness = 0; | host->staleness = 0; | ||||
/* Did host jump to a different link? */ | if ((host == NULL && ctx.incoming->learnMac) || | ||||
if (host->link != ctx.incoming) { | (host != NULL && host->link != ctx.incoming)) { | ||||
/* | |||||
* If the host's old link was recently established | |||||
* on the old link and it's already jumped to a new | |||||
* link, declare a loopback condition. | |||||
*/ | |||||
if (host->age < priv->conf.minStableAge) { | |||||
/* Log the problem */ | |||||
if (priv->conf.debugLevel >= 2) { | |||||
struct ifnet *ifp = ctx.m->m_pkthdr.rcvif; | |||||
char suffix[32]; | |||||
if (ifp != NULL) | |||||
snprintf(suffix, sizeof(suffix), | |||||
" (%s)", ifp->if_xname); | |||||
else | |||||
*suffix = '\0'; | |||||
log(LOG_WARNING, "ng_bridge: %s:" | |||||
" loopback detected on %s%s\n", | |||||
ng_bridge_nodename(node), | |||||
NG_HOOK_NAME(hook), suffix); | |||||
} | |||||
/* Mark link as linka non grata */ | |||||
ctx.incoming->loopCount = priv->conf.loopTimeout; | |||||
counter_u64_add(ctx.incoming->stats.loopDetects, 1); | |||||
Done Inline ActionsloopDetects was part of the to be shared routine revdata. donner: loopDetects was part of the to be shared routine revdata.
Now it's part of the control message… | |||||
/* Forget all hosts on this link */ | |||||
ng_bridge_remove_hosts(priv, ctx.incoming); | |||||
/* Drop packet */ | |||||
counter_u64_add(ctx.incoming->stats.loopDrops, 1); | |||||
NG_FREE_ITEM(item); | |||||
NG_FREE_M(ctx.m); | |||||
return (ELOOP); /* XXX appropriate? */ | |||||
} | |||||
/* Move host over to new link */ | |||||
host->link = ctx.incoming; | |||||
host->age = 0; | |||||
} | |||||
} else if (ctx.incoming->learnMac) { | |||||
struct ng_mesg *msg; | struct ng_mesg *msg; | ||||
struct ng_bridge_move_host *mh; | struct ng_bridge_move_host *mh; | ||||
int error = 0; | int error = 0; | ||||
NG_MKMESSAGE(msg, NGM_BRIDGE_COOKIE, NGM_BRIDGE_MOVE_HOST, | NG_MKMESSAGE(msg, NGM_BRIDGE_COOKIE, NGM_BRIDGE_MOVE_HOST, | ||||
sizeof(*mh), M_NOWAIT); | sizeof(*mh), M_NOWAIT); | ||||
if (msg == NULL) { | if (msg == NULL) { | ||||
counter_u64_add(ctx.incoming->stats.memoryFailures, 1); | counter_u64_add(ctx.incoming->stats.memoryFailures, 1); | ||||
NG_FREE_ITEM(item); | NG_FREE_ITEM(item); | ||||
NG_FREE_M(ctx.m); | NG_FREE_M(ctx.m); | ||||
return (ENOMEM); | return (ENOMEM); | ||||
} | } | ||||
mh = (struct ng_bridge_move_host *)msg->data; | mh = (struct ng_bridge_move_host *)msg->data; | ||||
strncpy(mh->hook, NG_HOOK_NAME(ctx.incoming->hook), | strncpy(mh->hook, NG_HOOK_NAME(ctx.incoming->hook), | ||||
sizeof(mh->hook)); | sizeof(mh->hook)); | ||||
memcpy(mh->addr, eh->ether_shost, sizeof(mh->addr)); | memcpy(mh->addr, eh->ether_shost, sizeof(mh->addr)); | ||||
NG_SEND_MSG_ID(error, node, msg, NG_NODE_ID(node), | NG_SEND_MSG_ID(error, node, msg, NG_NODE_ID(node), | ||||
NG_NODE_ID(node)); | NG_NODE_ID(node)); | ||||
if (error) | if (error) | ||||
counter_u64_add(ctx.incoming->stats.memoryFailures, 1); | counter_u64_add(ctx.incoming->stats.memoryFailures, 1); | ||||
} | } | ||||
if (host != NULL && host->link != ctx.incoming) { | |||||
if (host->age < priv->conf.minStableAge) { | |||||
/* Drop packet on instable links */ | |||||
counter_u64_add(ctx.incoming->stats.loopDrops, 1); | |||||
NG_FREE_ITEM(item); | |||||
NG_FREE_M(ctx.m); | |||||
return (ELOOP); | |||||
} | |||||
} | |||||
/* Run packet through ipfw processing, if enabled */ | /* Run packet through ipfw processing, if enabled */ | ||||
#if 0 | #if 0 | ||||
if (priv->conf.ipfw[linkNum] && V_fw_enable && V_ip_fw_chk_ptr != NULL) { | if (priv->conf.ipfw[linkNum] && V_fw_enable && V_ip_fw_chk_ptr != NULL) { | ||||
/* XXX not implemented yet */ | /* XXX not implemented yet */ | ||||
} | } | ||||
#endif | #endif | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 83 Lines • ▼ Show 20 Lines | ng_bridge_disconnect(hook_p hook) | ||||
counter_u64_free(link->stats.recvUnknown); | counter_u64_free(link->stats.recvUnknown); | ||||
counter_u64_free(link->stats.recvRunts); | counter_u64_free(link->stats.recvRunts); | ||||
counter_u64_free(link->stats.recvInvalid); | counter_u64_free(link->stats.recvInvalid); | ||||
counter_u64_free(link->stats.xmitOctets); | counter_u64_free(link->stats.xmitOctets); | ||||
counter_u64_free(link->stats.xmitPackets); | counter_u64_free(link->stats.xmitPackets); | ||||
counter_u64_free(link->stats.xmitMulticasts); | counter_u64_free(link->stats.xmitMulticasts); | ||||
counter_u64_free(link->stats.xmitBroadcasts); | counter_u64_free(link->stats.xmitBroadcasts); | ||||
counter_u64_free(link->stats.loopDrops); | counter_u64_free(link->stats.loopDrops); | ||||
counter_u64_free(link->stats.loopDetects); | |||||
counter_u64_free(link->stats.memoryFailures); | counter_u64_free(link->stats.memoryFailures); | ||||
free(link, M_NETGRAPH_BRIDGE); | free(link, M_NETGRAPH_BRIDGE); | ||||
priv->numLinks--; | priv->numLinks--; | ||||
/* If no more hooks, go away */ | /* If no more hooks, go away */ | ||||
if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) | if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) | ||||
&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))) | && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))) | ||||
&& !priv->persistent) { | && !priv->persistent) { | ||||
Show All 25 Lines | ng_bridge_get(priv_cp priv, const u_char *addr) | ||||
SLIST_FOREACH(host, &priv->tab[bucket], next) { | SLIST_FOREACH(host, &priv->tab[bucket], next) { | ||||
if (ETHER_EQUAL(host->addr, addr)) | if (ETHER_EQUAL(host->addr, addr)) | ||||
return (host); | return (host); | ||||
} | } | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
/* | /* | ||||
* Add a new host entry to the table. This assumes the host doesn't | * Add a host entry to the table. If it already exists, move it | ||||
* already exist in the table. Returns 0 on success. | * to the new link. Returns 0 on success. | ||||
*/ | */ | ||||
static int | static int | ||||
ng_bridge_put(priv_p priv, const u_char *addr, link_p link) | ng_bridge_put(priv_p priv, const u_char *addr, link_p link) | ||||
{ | { | ||||
const int bucket = HASH(addr, priv->hashMask); | const int bucket = HASH(addr, priv->hashMask); | ||||
struct ng_bridge_host *host; | struct ng_bridge_host *host; | ||||
#ifdef INVARIANTS | if ((host = ng_bridge_get(priv, addr)) != NULL) { | ||||
/* Assert that entry does not already exist in hashtable */ | /* Host already on the correct link? */ | ||||
KASSERT(ng_bridge_get(priv, addr) == NULL, | if (host->link == link) | ||||
("%s: entry %6D exists in table", __func__, addr, ":")); | return 0; | ||||
#endif | |||||
/* Move old host over to new link */ | |||||
if (host->age >= priv->conf.minStableAge) { | |||||
host->link = link; | |||||
host->age = 0; | |||||
return (0); | |||||
} | |||||
/* | |||||
* If the host was recently moved to the old link and | |||||
* it's now jumping to a new link, declare a loopback | |||||
* condition. | |||||
*/ | |||||
if (priv->conf.debugLevel >= 2) | |||||
log(LOG_WARNING, "ng_bridge: %s:" | |||||
" loopback detected on %s\n", | |||||
ng_bridge_nodename(priv->node), | |||||
NG_HOOK_NAME(link->hook)); | |||||
/* Mark link as linka non grata */ | |||||
link->loopCount = priv->conf.loopTimeout; | |||||
link->stats.loopDetects++; | |||||
/* Forget all hosts on this link */ | |||||
ng_bridge_remove_hosts(priv, link); | |||||
return (ELOOP); | |||||
} | |||||
/* Allocate and initialize new hashtable entry */ | /* Allocate and initialize new hashtable entry */ | ||||
host = malloc(sizeof(*host), M_NETGRAPH_BRIDGE, M_NOWAIT); | host = malloc(sizeof(*host), M_NETGRAPH_BRIDGE, M_NOWAIT); | ||||
if (host == NULL) | if (host == NULL) | ||||
return (ENOMEM); | return (ENOMEM); | ||||
bcopy(addr, host->addr, ETHER_ADDR_LEN); | bcopy(addr, host->addr, ETHER_ADDR_LEN); | ||||
host->link = link; | host->link = link; | ||||
host->staleness = 0; | host->staleness = 0; | ||||
▲ Show 20 Lines • Show All 183 Lines • Show Last 20 Lines |
Check is now part of the insert_or_move function.
Current check was only for "fresh insert".