Use hash table for session tracking

This commit is contained in:
Daniel Adolfsson 2019-12-19 11:26:50 +01:00
parent 5ba34bdfae
commit eb18db8184
7 changed files with 109 additions and 77 deletions

View File

@ -116,14 +116,24 @@ void nd_addr_combine(const nd_addr_t *first, const nd_addr_t *second, unsigned p
}
}
static int ndL_count_bits(uint32_t n)
uint32_t nd_addr_hash(const nd_addr_t *addr)
{
n = (n & 0x55555555) + ((n >> 1) & 0x55555555);
n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
n = (n & 0x0f0f0f0f) + ((n >> 4) & 0x0f0f0f0f);
n = (n & 0x00ff00ff) + ((n >> 8) & 0x00ff00ff);
n = (n & 0x0000ffff) + ((n >> 16) & 0x0000ffff);
return n;
uint32_t hash = 7;
hash = 31 * hash + addr->u32[0];
hash = 31 * hash + addr->u32[1];
hash = 31 * hash + addr->u32[2];
hash = 31 * hash + addr->u32[3];
return hash;
}
static int ndL_count_bits(uint32_t value)
{
value = (value & 0x55555555) + ((value >> 1) & 0x55555555);
value = (value & 0x33333333) + ((value >> 2) & 0x33333333);
value = (value & 0x0f0f0f0f) + ((value >> 4) & 0x0f0f0f0f);
value = (value & 0x00ff00ff) + ((value >> 8) & 0x00ff00ff);
value = (value & 0x0000ffff) + ((value >> 16) & 0x0000ffff);
return value;
}
int nd_mask_to_pflen(const nd_addr_t *netmask)

View File

@ -108,9 +108,9 @@ static void ndL_handle_na(nd_iface_t *iface, struct icmp6_hdr *ih, size_t len)
struct nd_neighbor_advert *na = (struct nd_neighbor_advert *)ih;
nd_session_t *session;
ND_LL_SEARCH(iface->sessions, session, next_in_iface,
nd_addr_eq(&session->real_tgt, (nd_addr_t *)&na->nd_na_target));
nd_log_trace("Handle NA tgt=%s", nd_ntoa((nd_addr_t *)&na->nd_na_target));
nd_session_t *session = nd_session_find_r((nd_addr_t *)&na->nd_na_target, iface);
if (!session) {
return;
@ -517,8 +517,6 @@ nd_iface_t *nd_iface_open(const char *name, unsigned index)
*iface = (nd_iface_t){
.index = index,
.refcount = 1,
.old_allmulti = -1,
.old_promisc = -1,
.lladdr = *lladdr,
};
@ -609,12 +607,13 @@ ssize_t nd_iface_send_na(nd_iface_t *iface, const nd_addr_t *dst, const nd_lladd
.ip.ip6_dst = *(struct in6_addr *)dst,
.na.nd_na_type = ND_NEIGHBOR_ADVERT,
.na.nd_na_target = *(struct in6_addr *)tgt,
.na.nd_na_flags_reserved = ND_NA_FLAG_OVERRIDE,
.opt.nd_opt_type = ND_OPT_TARGET_LINKADDR,
.opt.nd_opt_len = 1,
.lladdr = *(struct ether_addr *)tgt_ll,
};
if (nd_addr_is_multicast(dst)) {
if (!nd_addr_is_multicast(dst)) {
msg.na.nd_na_flags_reserved |= ND_NA_FLAG_SOLICITED;
}

View File

@ -261,7 +261,9 @@ int main(int argc, char *argv[])
nd_rt_query_routes();
bool querying_routes = true;
while (1) {
long last_session_update = 0;
for(;;) {
if (nd_current_time >= nd_rt_dump_timeout) {
nd_rt_dump_timeout = 0;
}
@ -272,13 +274,16 @@ int main(int argc, char *argv[])
nd_rt_query_addresses();
}
if (nd_current_time - last_session_update > 100) {
nd_session_update_all();
last_session_update = nd_current_time;
}
if (!nd_io_poll()) {
/* TODO: Error */
break;
}
nd_proxy_update_all();
gettimeofday(&t1, 0);
nd_current_time = t1.tv_sec * 1000 + t1.tv_usec / 1000;
}

View File

@ -96,16 +96,15 @@ struct nd_proxy {
nd_iface_t *iface;
nd_rule_t *rules;
nd_session_t *sessions;
bool router;
};
struct nd_session {
nd_session_t *next_in_proxy;
nd_session_t *next_in_iface;
nd_session_t *next;
nd_session_t *next_r;
nd_rule_t *rule;
nd_addr_t tgt;
nd_addr_t real_tgt;
nd_addr_t tgt_r;
int ons_count; /* Number of outgoing NS messages. */
long ons_time; /* Last time we sent a NS message. */
long ins_time; /* Last time this session was the target of an incoming NS. */
@ -143,11 +142,7 @@ struct nd_iface {
uint index;
int old_allmulti;
int old_promisc;
nd_proxy_t *proxy;
nd_session_t *sessions; // All sessions expecting NA messages to arrive here.
#ifndef __linux__
nd_io_t *bpf_io;
@ -271,6 +266,8 @@ void nd_addr_combine(const nd_addr_t *first, const nd_addr_t *second, unsigned p
bool nd_addr_is_unspecified(const nd_addr_t *addr);
uint32_t nd_addr_hash(const nd_addr_t *addr);
/*! Returns the string representation of link-layer address <tt>addr</tt>.
*
* @note This function returns a pointer to static data. It uses three different static arrays
@ -288,7 +285,6 @@ nd_proxy_t *nd_proxy_create(const char *ifname);
void nd_proxy_handle_ns(nd_proxy_t *proxy, const nd_addr_t *src, const nd_addr_t *dst, const nd_addr_t *tgt,
const nd_lladdr_t *src_ll);
bool nd_proxy_startup();
void nd_proxy_update_all();
/*
* session.c
@ -298,6 +294,9 @@ nd_session_t *nd_session_create(nd_rule_t *rule, const nd_addr_t *tgt);
void nd_session_update(nd_session_t *session);
void nd_session_handle_ns(nd_session_t *session, const nd_addr_t *src, const nd_lladdr_t *src_ll);
void nd_session_handle_na(nd_session_t *session);
nd_session_t *nd_session_find(const nd_addr_t *tgt, const nd_proxy_t *proxy);
nd_session_t *nd_session_find_r(const nd_addr_t *tgt, const nd_iface_t *iface);
void nd_session_update_all();
/*
* rule.c

View File

@ -56,42 +56,23 @@ void nd_proxy_handle_ns(nd_proxy_t *proxy, const nd_addr_t *src, const nd_addr_t
{
(void)dst;
if (nd_addr_is_unspecified(src)) {
nd_log_trace("Handle NS src=(unspecified), dst=%s, tgt=%s", nd_ntoa(dst), nd_ntoa(tgt));
} else {
nd_log_trace("Handle NS src=%s [%s], dst=%s, tgt=%s", //
nd_ntoa(src), nd_ll_ntoa(src_ll), nd_ntoa(dst), nd_ntoa(tgt));
}
nd_log_trace("Handle NS src=%s [%s], dst=%s, tgt=%s", //
nd_ntoa(src), nd_ll_ntoa(src_ll), nd_ntoa(dst), nd_ntoa(tgt));
nd_session_t *session;
nd_session_t *session = nd_session_find(tgt, proxy);
ND_LL_FOREACH_NODEF (proxy->sessions, session, next_in_proxy) {
if (nd_addr_eq(&session->tgt, tgt)) {
nd_session_handle_ns(session, src, src_ll);
if (!session) {
nd_rule_t *rule;
ND_LL_SEARCH(proxy->rules, rule, next, nd_addr_match(&rule->addr, tgt, rule->prefix));
if (!rule) {
return;
}
session = nd_session_create(rule, tgt);
}
// If we get down here it means we don't have any valid sessions we can use.
// See if we can find one more more matching rules.
nd_rule_t *rule;
ND_LL_SEARCH(proxy->rules, rule, next, nd_addr_match(&rule->addr, tgt, rule->prefix));
if (!rule) {
return;
}
nd_session_handle_ns(nd_session_create(rule, tgt), src, src_ll);
}
void nd_proxy_update_all()
{
ND_LL_FOREACH (ndL_proxies, proxy, next) {
ND_LL_FOREACH_S (proxy->sessions, session, tmp, next_in_proxy) {
nd_session_update(session);
}
}
nd_session_handle_ns(session, src, src_ll);
}
bool nd_proxy_startup()

View File

@ -28,7 +28,15 @@ extern int nd_conf_retrans_limit;
extern int nd_conf_retrans_time;
extern bool nd_conf_keepalive;
#ifndef NDPPD_SESSION_BUCKETS
# define NDPPD_SESSION_BUCKETS 64
#endif
#define NDL_BUCKET(a) (nd_addr_hash(a) % NDPPD_SESSION_BUCKETS)
static nd_session_t *ndL_free_sessions;
static nd_session_t *ndL_sessions[NDPPD_SESSION_BUCKETS];
static nd_session_t *ndL_sessions_r[NDPPD_SESSION_BUCKETS];
static void ndL_up(nd_session_t *session)
{
@ -70,7 +78,7 @@ void nd_session_handle_ns(nd_session_t *session, const nd_addr_t *src, const nd_
void nd_session_handle_na(nd_session_t *session)
{
if (session->state != ND_STATE_VALID) {
nd_log_debug("session [%s] %s -> VALID", session->rule->proxy->ifname, nd_ntoa(&session->tgt));
nd_log_debug("Session [%s] %s -> VALID", session->rule->proxy->ifname, nd_ntoa(&session->tgt));
ndL_up(session);
session->state = ND_STATE_VALID;
@ -83,7 +91,7 @@ nd_session_t *nd_session_create(nd_rule_t *rule, const nd_addr_t *tgt)
nd_session_t *session = ndL_free_sessions;
if (session) {
ND_LL_DELETE(ndL_free_sessions, session, next_in_proxy);
ND_LL_DELETE(ndL_free_sessions, session, next);
} else {
session = ND_ALLOC(nd_session_t);
}
@ -94,9 +102,10 @@ nd_session_t *nd_session_create(nd_rule_t *rule, const nd_addr_t *tgt)
.tgt = *tgt,
};
nd_addr_combine(&rule->rewrite_tgt, tgt, rule->rewrite_pflen, &session->real_tgt);
nd_addr_combine(&rule->rewrite_tgt, tgt, rule->rewrite_pflen, &session->tgt_r);
ND_LL_PREPEND(rule->proxy->sessions, session, next_in_proxy);
ND_LL_PREPEND(ndL_sessions[NDL_BUCKET(&session->tgt)], session, next);
ND_LL_PREPEND(ndL_sessions_r[NDL_BUCKET(&session->tgt_r)], session, next_r);
if (rule->mode == ND_MODE_AUTO) {
nd_rt_route_t *route = nd_rt_find_route(tgt, rule->table);
@ -110,12 +119,10 @@ nd_session_t *nd_session_create(nd_rule_t *rule, const nd_addr_t *tgt)
}
if (session->iface) {
ND_LL_PREPEND(session->iface->sessions, session, next_in_iface);
session->state = ND_STATE_INCOMPLETE;
session->ons_count = 1;
session->ons_time = nd_current_time;
nd_iface_send_ns(session->iface, &session->real_tgt);
nd_iface_send_ns(session->iface, &session->tgt_r);
} else if (rule->mode == ND_MODE_STATIC) {
session->state = ND_STATE_VALID;
}
@ -139,7 +146,7 @@ void nd_session_update(nd_session_t *session)
break;
}
nd_iface_send_ns(session->iface, &session->real_tgt);
nd_iface_send_ns(session->iface, &session->tgt_r);
break;
case ND_STATE_INVALID:
@ -150,12 +157,12 @@ void nd_session_update(nd_session_t *session)
ndL_down(session);
if (session->iface) {
ND_LL_DELETE(session->iface->sessions, session, next_in_iface);
nd_iface_close(session->iface);
}
ND_LL_DELETE(session->rule->proxy->sessions, session, next_in_proxy);
ND_LL_PREPEND(ndL_free_sessions, session, next_in_proxy);
ND_LL_DELETE(ndL_sessions[NDL_BUCKET(&session->tgt)], session, next);
ND_LL_DELETE(ndL_sessions_r[NDL_BUCKET(&session->tgt_r)], session, next_r);
ND_LL_PREPEND(ndL_free_sessions, session, next);
nd_log_debug("session [%s] %s INVALID -> (deleted)", //
session->rule->proxy->ifname, nd_ntoa(&session->tgt));
@ -175,7 +182,7 @@ void nd_session_update(nd_session_t *session)
if (nd_conf_keepalive || nd_current_time - session->ins_time < nd_conf_valid_ttl) {
session->ons_count = 1;
nd_iface_send_ns(session->iface, &session->real_tgt);
nd_iface_send_ns(session->iface, &session->tgt_r);
} else {
session->ons_count = 0;
}
@ -207,8 +214,34 @@ void nd_session_update(nd_session_t *session)
session->ons_count++;
session->ons_time = nd_current_time;
nd_iface_send_ns(session->iface, &session->real_tgt);
nd_iface_send_ns(session->iface, &session->tgt_r);
}
break;
}
}
nd_session_t *nd_session_find(const nd_addr_t *tgt, const nd_proxy_t *proxy)
{
nd_session_t *session;
ND_LL_SEARCH(ndL_sessions[NDL_BUCKET(tgt)], session, next,
session->rule->proxy == proxy && nd_addr_eq(&session->tgt, tgt));
return session;
}
nd_session_t *nd_session_find_r(const nd_addr_t *tgt, const nd_iface_t *iface)
{
nd_session_t *session;
ND_LL_SEARCH(ndL_sessions_r[NDL_BUCKET(tgt)], session, next_r,
session->iface == iface && nd_addr_eq(&session->tgt_r, tgt));
return session;
}
void nd_session_update_all()
{
for (int i = 0; i < NDPPD_SESSION_BUCKETS; i++) {
ND_LL_FOREACH(ndL_sessions[i], session, next) {
nd_session_update(session);
}
}
}

23
test.sh
View File

@ -62,19 +62,24 @@ case "$1" in
ip netns exec ndppd.rt ip link set ndppd.rt1 up
ip netns exec ndppd.1 ip link set ndppd.1 up
ip netns exec ndppd.0 ip -6 addr add dead::1 dev ndppd.0
ip netns exec ndppd.0 ip -6 route add default dev ndppd.0
ip netns exec ndppd.0 ip -6 addr add dead::1/64 dev ndppd.0
ip netns exec ndppd.0 ip -6 addr add dead::2/64 dev ndppd.0
ip netns exec ndppd.rt ip -6 addr add dead:: dev ndppd.rt0
ip netns exec ndppd.rt ip -6 route add dead::/64 dev ndppd.rt0
while ! ip netns exec ndppd.0 ip -6 route add default dev ndppd.0 src dead::1 &>/dev/null; do
sleep 1
done
ip netns exec ndppd.rt ip -6 addr add dead:1:: dev ndppd.rt1
ip netns exec ndppd.rt ip -6 route add dead:1::/64 dev ndppd.rt1
ip netns exec ndppd.rt ip -6 addr add dead::/64 dev ndppd.rt0
ip netns exec ndppd.rt ip -6 addr add dead:1::/64 dev ndppd.rt1
ip netns exec ndppd.1 ip -6 addr add dead:1::1 dev ndppd.1
ip netns exec ndppd.1 ip -6 route add default dev ndppd.1
ip netns exec ndppd.1 ip -6 addr add dead:1::1/64 dev ndppd.1
ip netns exec ndppd.1 ip -6 addr add dead:1::2/64 dev ndppd.1
ip netns exec ndppd.rt sysctl net.ipv6.conf.all.forwarding=1
while ! ip netns exec ndppd.1 ip -6 route add default dev ndppd.1 src dead:1::1 &>/dev/null; do
sleep 1
done
ip netns exec ndppd.rt sysctl -q net.ipv6.conf.all.forwarding=1
;;
"ndppd")