Add support for rewriting target

As discussed in issue #53
This commit is contained in:
Daniel Adolfsson 2019-12-15 02:22:14 +01:00
parent 87cd8abe9e
commit 4db6b7f7a5
9 changed files with 107 additions and 61 deletions

View File

@ -18,6 +18,7 @@
#include <string.h> #include <string.h>
#ifndef __linux__ #ifndef __linux__
# include <netinet/in.h>
# include <sys/socket.h> # include <sys/socket.h>
# define s6_addr32 __u6_addr.__u6_addr32 # define s6_addr32 __u6_addr.__u6_addr32
#endif #endif
@ -86,9 +87,9 @@ bool nd_addr_match(nd_addr_t *first, nd_addr_t *second, unsigned pflen)
else if (pflen == 128) else if (pflen == 128)
return nd_addr_eq(first, second); 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) if ((first->s6_addr32[i] ^ second->s6_addr32[i]) & mask)
return false; return false;
@ -97,6 +98,34 @@ bool nd_addr_match(nd_addr_t *first, nd_addr_t *second, unsigned pflen)
return true; 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) static int ndL_count_bits(uint32_t n)
{ {
n = (n & 0x55555555) + ((n >> 1) & 0x55555555); 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) 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[0] = 0xffffffff;
netmask->s6_addr32[1] = 0xffffffff; netmask->s6_addr32[1] = 0xffffffff;
netmask->s6_addr32[2] = 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; if (i == top)
netmask->s6_addr32[1] = 0xffffffff; netmask->s6_addr32[i] = ndL_masks[(pflen - 1) & 31];
netmask->s6_addr32[2] = ndL_masks[pflen - 65]; else if (i < top)
netmask->s6_addr32[3] = 0x00000000; netmask->s6_addr32[i] = 0xffffffff;
} else
else if (pflen >= 33) netmask->s6_addr32[i] = 0;
{
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;
} }
} }

View File

@ -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); bool nd_addr_eq(nd_addr_t *first, nd_addr_t *second);
int nd_mask_to_pflen(nd_addr_t *netmask); int nd_mask_to_pflen(nd_addr_t *netmask);
void nd_mask_from_pflen(unsigned 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 #endif // NDPPD_ADDR_H

View File

@ -98,11 +98,13 @@ enum
}; };
static bool ndL_parse_rule(ndL_state_t *state, nd_proxy_t *proxy); 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 bool ndL_parse_proxy(ndL_state_t *state, void *unused);
static const ndL_cfinfo_t ndL_cfinfo_table[] = { static const ndL_cfinfo_t ndL_cfinfo_table[] = {
{ "proxy", NDL_DEFAULT, NDL_NONE, 0, 0, 0, (ndL_cfcb_t)ndL_parse_proxy }, { "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 }, { "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 }, { "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 }, { "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 }, { "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; 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; 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; 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; return false;
}
*value = (int)longval; *value = (int)longval;
*state = tmp; *state = tmp;
@ -375,20 +380,11 @@ static bool ndL_parse_rule(ndL_state_t *state, nd_proxy_t *proxy)
if (ndL_accept(state, "/", 0)) if (ndL_accept(state, "/", 0))
{ {
// Just for accurate logging if there is an error. if (!ndL_accept_int(state, &rule->prefix, 0, 128))
ndL_state_t tmp = *state;
if (!ndL_accept_int(state, &rule->prefix))
{ {
ndL_error(state, "Expected prefix"); ndL_error(state, "Expected prefix");
return false; return false;
} }
if (rule->prefix < 0 || rule->prefix > 128)
{
ndL_error(&tmp, "Invalid prefix (%d)", rule->prefix);
return false;
}
} }
else 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); 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) static bool ndL_parse_proxy(ndL_state_t *state, __attribute__((unused)) void *unused)
{ {
char ifname[IF_NAMESIZE]; char ifname[IF_NAMESIZE];
@ -481,16 +498,11 @@ static bool ndL_parse_block(ndL_state_t *state, int scope, void *ptr)
break; break;
case NDL_INT: 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"); ndL_error(&saved_state, "Expected an integer");
return false; 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; break;
case NDL_ADDR: case NDL_ADDR:

View File

@ -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; struct nd_neighbor_advert *na = (struct nd_neighbor_advert *)&msg->icmp6_hdr;
nd_session_t *session; 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) if (!session)
return; return;

View File

@ -19,17 +19,16 @@
#include <getopt.h> #include <getopt.h>
#include <stdint.h> #include <stdint.h>
/**/ /**/
#include <netinet/in.h>
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/file.h> #include <sys/file.h>
#include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h> #include <sys/time.h>
#include <unistd.h> #include <unistd.h>
#include "addr.h"
#include "conf.h" #include "conf.h"
#include "iface.h" #include "iface.h"
#include "io.h" #include "io.h"

View File

@ -118,6 +118,9 @@ bool nd_proxy_startup()
{ {
if (rule->ifname[0] && !(rule->iface = nd_iface_open(rule->ifname, 0))) if (rule->ifname[0] && !(rule->iface = nd_iface_open(rule->ifname, 0)))
return false; return false;
nd_log_error("RULE REWRITE %s/%d", nd_aton(&rule->rewrite_tgt), rule->rewrite_pflen);
} }
} }

View File

@ -31,6 +31,9 @@ struct nd_rule
nd_addr_t addr; nd_addr_t addr;
int prefix; int prefix;
nd_addr_t rewrite_tgt;
int rewrite_pflen;
nd_iface_t *iface; nd_iface_t *iface;
bool is_auto; bool is_auto;
bool autowire; bool autowire;

View File

@ -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->state_time = nd_current_time;
session->tgt = *tgt; session->tgt = *tgt;
nd_addr_combine(&rule->rewrite_tgt, tgt, rule->rewrite_pflen, &session->real_tgt);
if (rule->is_auto) if (rule->is_auto)
{ {
nd_rt_route_t *route = nd_rt_find_route(tgt, rule->table); 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; session->state = ND_STATE_INVALID;
return session; 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->state = ND_STATE_INCOMPLETE;
session->ons_count = 1; session->ons_count = 1;
session->ons_time = nd_current_time; session->ons_time = nd_current_time;
nd_iface_write_ns(session->iface, tgt); nd_iface_write_ns(session->iface, &session->real_tgt);
} }
else else
{ {
@ -135,11 +136,12 @@ void nd_session_update(nd_session_t *session)
{ {
session->state = ND_STATE_INVALID; session->state = ND_STATE_INVALID;
session->state_time = nd_current_time; 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; break;
} }
nd_iface_write_ns(session->iface, &session->tgt); nd_iface_write_ns(session->iface, &session->real_tgt);
break; break;
case ND_STATE_INVALID: 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_DELETE(session->rule->proxy->sessions, session, next_in_proxy);
ND_LL_PREPEND(ndL_free_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; break;
case ND_STATE_VALID: case ND_STATE_VALID:
@ -168,12 +171,13 @@ void nd_session_update(nd_session_t *session)
session->state_time = nd_current_time; session->state_time = nd_current_time;
session->ons_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) if (nd_conf_keepalive || nd_current_time - session->ins_time < nd_conf_valid_ttl)
{ {
session->ons_count = 1; session->ons_count = 1;
nd_iface_write_ns(session->iface, &session->tgt); nd_iface_write_ns(session->iface, &session->real_tgt);
} }
else else
{ {
@ -187,7 +191,9 @@ void nd_session_update(nd_session_t *session)
{ {
session->state = ND_STATE_INVALID; session->state = ND_STATE_INVALID;
session->state_time = nd_current_time; 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 else
{ {
@ -206,7 +212,7 @@ void nd_session_update(nd_session_t *session)
session->ons_count++; session->ons_count++;
session->ons_time = nd_current_time; session->ons_time = nd_current_time;
nd_iface_write_ns(session->iface, &session->tgt); nd_iface_write_ns(session->iface, &session->real_tgt);
} }
break; break;
} }

View File

@ -41,6 +41,7 @@ struct nd_session
nd_session_t *next_in_iface; nd_session_t *next_in_iface;
nd_rule_t *rule; nd_rule_t *rule;
nd_addr_t tgt; nd_addr_t tgt;
nd_addr_t real_tgt;
int ons_count; // Number of outgoing NS messages. int ons_count; // Number of outgoing NS messages.
long ons_time; // Last time we sent a NS message. long ons_time; // Last time we sent a NS message.
long ins_time; // Last time this session was the target of an incoming NS. long ins_time; // Last time this session was the target of an incoming NS.