From 4db6b7f7a5074345a7b743930f6708a490d67e50 Mon Sep 17 00:00:00 2001 From: Daniel Adolfsson Date: Sun, 15 Dec 2019 02:22:14 +0100 Subject: [PATCH] Add support for rewriting target As discussed in issue #53 --- src/addr.c | 81 ++++++++++++++++++++++++++++++++------------------- src/addr.h | 1 + src/conf.c | 48 ++++++++++++++++++------------ src/iface.c | 2 +- src/ndppd.c | 3 +- src/proxy.c | 3 ++ src/rule.h | 3 ++ src/session.c | 26 ++++++++++------- src/session.h | 1 + 9 files changed, 107 insertions(+), 61 deletions(-) diff --git a/src/addr.c b/src/addr.c index f7a059c..1f31309 100644 --- a/src/addr.c +++ b/src/addr.c @@ -18,6 +18,7 @@ #include #ifndef __linux__ +# include # include # define s6_addr32 __u6_addr.__u6_addr32 #endif @@ -86,9 +87,9 @@ bool nd_addr_match(nd_addr_t *first, nd_addr_t *second, unsigned pflen) else if (pflen == 128) return nd_addr_eq(first, second); - for (unsigned i = 0, top = (pflen - 1) >> 5U; i <= top; i++) + for (unsigned i = 0, top = (pflen - 1) >> 5; i <= top; i++) { - uint32_t mask = i < top ? 0xffffffff : ndL_masks[(pflen - 1) & 31U]; + uint32_t mask = i < top ? 0xffffffff : ndL_masks[(pflen - 1) & 31]; if ((first->s6_addr32[i] ^ second->s6_addr32[i]) & mask) return false; @@ -97,6 +98,34 @@ bool nd_addr_match(nd_addr_t *first, nd_addr_t *second, unsigned pflen) return true; } +void nd_addr_combine(const nd_addr_t *first, const nd_addr_t *second, unsigned pflen, nd_addr_t *result) +{ + if (pflen == 0) + { + *result = *second; + return; + } + + if (pflen >= 128) + { + *result = *first; + return; + } + + for (unsigned i = 0, top = (pflen - 1) >> 5; i < 4; i++) + { + if (i == top) + { + uint32_t mask = ndL_masks[(pflen - 1) & 31]; + result->s6_addr32[i] = (first->s6_addr32[i] & mask) | (second->s6_addr32[i] & ~mask); + } + else if (i < top) + result->s6_addr32[i] = first->s6_addr32[i]; + else + result->s6_addr32[i] = second->s6_addr32[i]; + } +} + static int ndL_count_bits(uint32_t n) { n = (n & 0x55555555) + ((n >> 1) & 0x55555555); @@ -115,39 +144,31 @@ int nd_mask_to_pflen(nd_addr_t *netmask) void nd_mask_from_pflen(unsigned pflen, nd_addr_t *netmask) { - if (pflen >= 97) + if (pflen == 0) + { + netmask->s6_addr32[0] = 0; + netmask->s6_addr32[1] = 0; + netmask->s6_addr32[2] = 0; + netmask->s6_addr32[3] = 0; + return; + } + + if (pflen >= 128) { netmask->s6_addr32[0] = 0xffffffff; netmask->s6_addr32[1] = 0xffffffff; netmask->s6_addr32[2] = 0xffffffff; - netmask->s6_addr32[3] = ndL_masks[pflen - 97]; + netmask->s6_addr32[3] = 0xffffffff; + return; } - else if (pflen >= 65) + + for (unsigned i = 0, top = (pflen - 1) >> 5; i < 4; i++) { - netmask->s6_addr32[0] = 0xffffffff; - netmask->s6_addr32[1] = 0xffffffff; - netmask->s6_addr32[2] = ndL_masks[pflen - 65]; - netmask->s6_addr32[3] = 0x00000000; - } - else if (pflen >= 33) - { - netmask->s6_addr32[0] = 0xffffffff; - netmask->s6_addr32[1] = ndL_masks[pflen - 33]; - netmask->s6_addr32[2] = 0x00000000; - netmask->s6_addr32[3] = 0x00000000; - } - else if (pflen >= 1) - { - netmask->s6_addr32[0] = ndL_masks[pflen - 1]; - netmask->s6_addr32[1] = 0x00000000; - netmask->s6_addr32[2] = 0x00000000; - netmask->s6_addr32[3] = 0x00000000; - } - else - { - netmask->s6_addr32[0] = 0x00000000; - netmask->s6_addr32[1] = 0x00000000; - netmask->s6_addr32[2] = 0x00000000; - netmask->s6_addr32[3] = 0x00000000; + if (i == top) + netmask->s6_addr32[i] = ndL_masks[(pflen - 1) & 31]; + else if (i < top) + netmask->s6_addr32[i] = 0xffffffff; + else + netmask->s6_addr32[i] = 0; } } diff --git a/src/addr.h b/src/addr.h index 656091b..0604afa 100644 --- a/src/addr.h +++ b/src/addr.h @@ -27,5 +27,6 @@ bool nd_addr_match(nd_addr_t *first, nd_addr_t *second, unsigned pflen); bool nd_addr_eq(nd_addr_t *first, nd_addr_t *second); int nd_mask_to_pflen(nd_addr_t *netmask); void nd_mask_from_pflen(unsigned pflen, nd_addr_t *netmask); +void nd_addr_combine(const nd_addr_t *first, const nd_addr_t *second, unsigned pflen, nd_addr_t *result); #endif // NDPPD_ADDR_H diff --git a/src/conf.c b/src/conf.c index dd921a9..2a8891c 100644 --- a/src/conf.c +++ b/src/conf.c @@ -98,11 +98,13 @@ enum }; static bool ndL_parse_rule(ndL_state_t *state, nd_proxy_t *proxy); +static bool ndL_parse_rewrite(ndL_state_t *state, nd_rule_t *rule); static bool ndL_parse_proxy(ndL_state_t *state, void *unused); static const ndL_cfinfo_t ndL_cfinfo_table[] = { { "proxy", NDL_DEFAULT, NDL_NONE, 0, 0, 0, (ndL_cfcb_t)ndL_parse_proxy }, { "rule", NDL_PROXY, NDL_NONE, 0, 0, 0, (ndL_cfcb_t)ndL_parse_rule }, + { "rewrite", NDL_RULE, NDL_NONE, 0, 0, 0, (ndL_cfcb_t)ndL_parse_rewrite }, { "invalid-ttl", NDL_DEFAULT, NDL_INT, (uintptr_t)&nd_conf_invalid_ttl, 1000, 3600000, NULL }, { "valid-ttl", NDL_DEFAULT, NDL_INT, (uintptr_t)&nd_conf_valid_ttl, 10000, 3600000, NULL }, { "renew", NDL_DEFAULT, NDL_INT, (uintptr_t)&nd_conf_renew, 0, 0, NULL }, @@ -264,7 +266,7 @@ static bool ndL_accept_bool(ndL_state_t *state, bool *value) return true; } -static bool ndL_accept_int(ndL_state_t *state, int *value) +static bool ndL_accept_int(ndL_state_t *state, int *value, int min, int max) { ndL_state_t tmp = *state; @@ -281,8 +283,11 @@ static bool ndL_accept_int(ndL_state_t *state, int *value) long longval = strtoll(buf, NULL, 10) * n; - if (longval < INT_MIN || longval > INT_MAX) + if (longval < min || longval > max) + { + ndL_error(state, "Expected a number between %d and %d", min, max); return false; + } *value = (int)longval; *state = tmp; @@ -375,20 +380,11 @@ static bool ndL_parse_rule(ndL_state_t *state, nd_proxy_t *proxy) if (ndL_accept(state, "/", 0)) { - // Just for accurate logging if there is an error. - ndL_state_t tmp = *state; - - if (!ndL_accept_int(state, &rule->prefix)) + if (!ndL_accept_int(state, &rule->prefix, 0, 128)) { ndL_error(state, "Expected prefix"); return false; } - - if (rule->prefix < 0 || rule->prefix > 128) - { - ndL_error(&tmp, "Invalid prefix (%d)", rule->prefix); - return false; - } } else { @@ -404,6 +400,27 @@ static bool ndL_parse_rule(ndL_state_t *state, nd_proxy_t *proxy) return ndL_parse_block(state, NDL_RULE, rule); } +static bool ndL_parse_rewrite(ndL_state_t *state, nd_rule_t *rule) +{ + if (!ndL_accept_addr(state, &rule->rewrite_tgt)) + return false; + + if (ndL_accept(state, "/", 0)) + { + if (!ndL_accept_int(state, &rule->rewrite_pflen, 0, 128)) + { + ndL_error(state, "Expected prefix"); + return false; + } + } + else + { + rule->rewrite_pflen = 128; + } + + return true; +} + static bool ndL_parse_proxy(ndL_state_t *state, __attribute__((unused)) void *unused) { char ifname[IF_NAMESIZE]; @@ -481,16 +498,11 @@ static bool ndL_parse_block(ndL_state_t *state, int scope, void *ptr) break; case NDL_INT: - if (!ndL_accept_int(state, (int *)(ptr + t->offset))) + if (!ndL_accept_int(state, (int *)(ptr + t->offset), t->min, t->max)) { ndL_error(&saved_state, "Expected an integer"); return false; } - if (*(int *)(ptr + t->offset) < t->min || *(int *)(ptr + t->offset) > t->max) - { - ndL_error(&saved_state, "Invalid range; must be between %d and %d", t->min, t->max); - return false; - } break; case NDL_ADDR: diff --git a/src/iface.c b/src/iface.c index 17c45c3..c004d88 100644 --- a/src/iface.c +++ b/src/iface.c @@ -103,7 +103,7 @@ static void ndL_handle_na(nd_iface_t *iface, ndL_icmp6_msg_t *msg) struct nd_neighbor_advert *na = (struct nd_neighbor_advert *)&msg->icmp6_hdr; nd_session_t *session; - ND_LL_SEARCH(iface->sessions, session, next_in_iface, nd_addr_eq(&session->tgt, &na->nd_na_target)); + ND_LL_SEARCH(iface->sessions, session, next_in_iface, nd_addr_eq(&session->real_tgt, &na->nd_na_target)); if (!session) return; diff --git a/src/ndppd.c b/src/ndppd.c index aa3fdc8..940097a 100644 --- a/src/ndppd.c +++ b/src/ndppd.c @@ -19,17 +19,16 @@ #include #include /**/ -#include #include #include #include #include #include -#include #include #include #include +#include "addr.h" #include "conf.h" #include "iface.h" #include "io.h" diff --git a/src/proxy.c b/src/proxy.c index 63c690b..208c175 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -118,6 +118,9 @@ bool nd_proxy_startup() { if (rule->ifname[0] && !(rule->iface = nd_iface_open(rule->ifname, 0))) return false; + + nd_log_error("RULE REWRITE %s/%d", nd_aton(&rule->rewrite_tgt), rule->rewrite_pflen); + } } diff --git a/src/rule.h b/src/rule.h index 167596e..a43921a 100644 --- a/src/rule.h +++ b/src/rule.h @@ -31,6 +31,9 @@ struct nd_rule nd_addr_t addr; int prefix; + nd_addr_t rewrite_tgt; + int rewrite_pflen; + nd_iface_t *iface; bool is_auto; bool autowire; diff --git a/src/session.c b/src/session.c index 430f757..4df92bd 100644 --- a/src/session.c +++ b/src/session.c @@ -90,11 +90,13 @@ nd_session_t *nd_session_create(nd_rule_t *rule, nd_addr_t *tgt) session->state_time = nd_current_time; session->tgt = *tgt; + nd_addr_combine(&rule->rewrite_tgt, tgt, rule->rewrite_pflen, &session->real_tgt); + if (rule->is_auto) { nd_rt_route_t *route = nd_rt_find_route(tgt, rule->table); - if (!route || route->oif == rule->proxy->iface->index ||!(session->iface = nd_iface_open(NULL, route->oif))) + if (!route || route->oif == rule->proxy->iface->index || !(session->iface = nd_iface_open(NULL, route->oif))) { session->state = ND_STATE_INVALID; return session; @@ -112,8 +114,7 @@ nd_session_t *nd_session_create(nd_rule_t *rule, nd_addr_t *tgt) session->state = ND_STATE_INCOMPLETE; session->ons_count = 1; session->ons_time = nd_current_time; - nd_iface_write_ns(session->iface, tgt); - + nd_iface_write_ns(session->iface, &session->real_tgt); } else { @@ -135,11 +136,12 @@ void nd_session_update(nd_session_t *session) { session->state = ND_STATE_INVALID; session->state_time = nd_current_time; - nd_log_debug("session [%s] %s INCOMPLETE -> INVALID", session->rule->proxy->ifname, nd_aton(&session->tgt)); + nd_log_debug("session [%s] %s INCOMPLETE -> INVALID", // + session->rule->proxy->ifname, nd_aton(&session->tgt)); break; } - nd_iface_write_ns(session->iface, &session->tgt); + nd_iface_write_ns(session->iface, &session->real_tgt); break; case ND_STATE_INVALID: @@ -157,7 +159,8 @@ void nd_session_update(nd_session_t *session) ND_LL_DELETE(session->rule->proxy->sessions, session, next_in_proxy); ND_LL_PREPEND(ndL_free_sessions, session, next_in_proxy); - nd_log_debug("session [%s] %s INVALID -> (deleted)", session->rule->proxy->ifname, nd_aton(&session->tgt)); + nd_log_debug("session [%s] %s INVALID -> (deleted)", // + session->rule->proxy->ifname, nd_aton(&session->tgt)); break; case ND_STATE_VALID: @@ -168,12 +171,13 @@ void nd_session_update(nd_session_t *session) session->state_time = nd_current_time; session->ons_time = nd_current_time; - nd_log_debug("session [%s] %s VALID -> STALE", session->rule->proxy->ifname, nd_aton(&session->tgt)); + nd_log_debug("session [%s] %s VALID -> STALE", // + session->rule->proxy->ifname, nd_aton(&session->tgt)); if (nd_conf_keepalive || nd_current_time - session->ins_time < nd_conf_valid_ttl) { session->ons_count = 1; - nd_iface_write_ns(session->iface, &session->tgt); + nd_iface_write_ns(session->iface, &session->real_tgt); } else { @@ -187,7 +191,9 @@ void nd_session_update(nd_session_t *session) { session->state = ND_STATE_INVALID; session->state_time = nd_current_time; - nd_log_debug("session [%s] %s STALE -> INVALID", session->rule->proxy->ifname, nd_aton(&session->tgt)); + + nd_log_debug("session [%s] %s STALE -> INVALID", // + session->rule->proxy->ifname, nd_aton(&session->tgt)); } else { @@ -206,7 +212,7 @@ void nd_session_update(nd_session_t *session) session->ons_count++; session->ons_time = nd_current_time; - nd_iface_write_ns(session->iface, &session->tgt); + nd_iface_write_ns(session->iface, &session->real_tgt); } break; } diff --git a/src/session.h b/src/session.h index 212a810..4db47ae 100644 --- a/src/session.h +++ b/src/session.h @@ -41,6 +41,7 @@ struct nd_session nd_session_t *next_in_iface; nd_rule_t *rule; nd_addr_t tgt; + nd_addr_t real_tgt; 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.