mirror of
https://github.com/servalproject/serval-dna.git
synced 2024-12-19 05:07:56 +00:00
Substantial further work towards address abbreviation support.
Abbreviation and expansion functions are now present, although untested.
This commit is contained in:
parent
9d4666f9d4
commit
f8064a1795
13
mphlr.h
13
mphlr.h
@ -591,3 +591,16 @@ extern int overlay_interface_count;
|
||||
|
||||
/* Userland overlay mesh packet codes */
|
||||
#define OF_SELFANNOUNCE 0x01
|
||||
|
||||
#define OVERLAY_ADDRESS_CACHE_SIZE 1024
|
||||
int overlay_abbreviate_address(unsigned char *in,char *out,int *ofs);
|
||||
int overlay_abbreviate_expand_address(unsigned char *in,int *inofs,unsigned char *out,int *ofs);
|
||||
int overlay_address_cache_address(unsigned char *sid);
|
||||
int overlay_abbreviate_cache_lookup(unsigned char *in,unsigned char *out,int *ofs,
|
||||
int prefix_bytes,int index_bytes);
|
||||
int overlay_abbreviate_remember_index(int index_byte_count,unsigned char *in,unsigned char *index_bytes);
|
||||
|
||||
|
||||
#define OA_RESOLVED 0 /* We expanded the abbreviation */
|
||||
#define OA_PLEASEEXPLAIN 1 /* We need the sender to explain their abbreviation */
|
||||
#define OA_UNSUPPORTED 2 /* We cannot expand the abbreviation as we do not understand this code */
|
||||
|
361
overlay_abbreviations.c
Normal file
361
overlay_abbreviations.c
Normal file
@ -0,0 +1,361 @@
|
||||
#include "mphlr.h"
|
||||
|
||||
/*
|
||||
We use 256bit Curve25519 addresses in the overlay mesh. This means we have very large
|
||||
addresses to send around the place on a regular basis, which is bad.
|
||||
|
||||
We allow addresses to be shortened to save bandwidth, especially for low-bandwidth links.
|
||||
|
||||
For this purpose we have special address prefixes 0x00 - 0x0f which are not allowed to
|
||||
occur in unabbreviated addresses.
|
||||
|
||||
0x00 = reserved
|
||||
0x01-0x02 = one to two byte address by index.
|
||||
0x03 = same as last address.
|
||||
0x04 = address matches sender (not currently implemented).
|
||||
0x05 = address by prefix of three bytes.
|
||||
0x06 = address by prefix of seven bytes.
|
||||
0x07 = address by prefix of eleven bytes.
|
||||
0x08 = full address followed by one-byte index allocation.
|
||||
0x09-0x0b = same as 0x05-0x07, but assign single byte index.
|
||||
0x0c = reserved.
|
||||
0x0d = same as 0x07, but assign two byte index.
|
||||
0x0e = full address followed by two-byte index allocation.
|
||||
0x0f = broadcast.
|
||||
|
||||
However, in this implementation we not include support for the two-byte
|
||||
index code, as it requires 64Kx32=2MB storage, which is too much to ask
|
||||
of a mesh potato or a cheap smart phone.
|
||||
|
||||
All indexed abbreviations may reference a token which is used to keep
|
||||
track of the epoch of the abbreviation table.
|
||||
|
||||
This allows us to maintain multiple abbreviation tables at the same time, so
|
||||
that neighbours we have not spoken to for some time will not be so easily
|
||||
confused, as any one epoch will remain valid for some time after it has
|
||||
ceased to be ammended.
|
||||
|
||||
This also allows us to effectively have multiple 256-entry pages, which will
|
||||
be more efficient than using a 16-bit index table in most instances, especially
|
||||
since we have the flexibility to reorder frames in an ensemble to minimise the
|
||||
total length.
|
||||
|
||||
One table is maintained for all interfaces, as we may have neighbours who are
|
||||
reachable via multiple interfaces. It also helps to minimise memory usage.
|
||||
|
||||
A cache of recently seen addresses is also desirable for conclusively resolving
|
||||
abbreviated addresses. Failure will allow the birthday paradox to cause us problems
|
||||
and also allow an attack based on searching for private keys that yield colliding
|
||||
public key prefixes. This would allow an attacker to mess with the routing and
|
||||
divert the traffic of other nodes to themselves. Thus some cache or similar policy
|
||||
is strongly recommended if a node is going to accept address prefixes, especially if
|
||||
it accepts the shorter prefixes. Seven and eleven byte prefixes should be reasonably
|
||||
resistant to this attack.
|
||||
|
||||
There is no reason why the address abbreviation table cannot be used as the cache,
|
||||
excepting for the question of efficiency, as a naive implementation would require
|
||||
a linear search. However, adding a simple index table can mitigate this, and thus
|
||||
presents as a sensible solution.
|
||||
|
||||
If a node receives a packet with an abbreviation that it cannot resolve an
|
||||
abbreviation can ask for clarification, including indicating if it does
|
||||
not support the abbreviation mode specified.
|
||||
|
||||
Abbreviations are link-local, but clarifications will always be requested by
|
||||
attempting to contact the abbreviator via normal mesh routing rules.
|
||||
|
||||
We need to take some care so as to allow other crypto-systems, and thus storing the
|
||||
crypto-system ID byte, although we still limit addresses to 256 bits, so we always need
|
||||
no more than 33 bytes. In any case, indicating the crypto system is not the problem of
|
||||
this section, as we are just concerned with abbreviating and expanding the addresses.
|
||||
|
||||
To decode abbreviations supplied by other nodes we need to try to replicate a copy of
|
||||
their abbreviation table. This is where things get memory intensive (about 8KB per node).
|
||||
However, we only need the table of one-hop neighbours, and it is reasonable to have a
|
||||
constrained set of such tables, with a random replacement algorithm. This allows the
|
||||
structure to be scaled to accomodate varying memory sizes of devices.
|
||||
|
||||
Very memory constrained devices may elect to cache individual entries rather than tables
|
||||
of individual nodes, but we haven't implemented that here.
|
||||
|
||||
If we send only a prefix, then we need to be careful to make sure that receivers
|
||||
get some chance to learn the full address if they need it. The question is whether
|
||||
abbreviation clarification is more or less efficient than sometimes sending the
|
||||
full address. Given that we already have need for the clarification process, let's
|
||||
go that way, and change the policy later if appropriate.
|
||||
|
||||
*/
|
||||
|
||||
typedef struct overlay_address_table {
|
||||
unsigned char epoch;
|
||||
char sids[256][SID_SIZE];
|
||||
/* 0x00 = not set, which thus limits us to using only 255 (0x01-0xff) of the indexes for
|
||||
storing addresses.
|
||||
By spending an extra 256 bytes we reduce, but not eliminate the problem of collisions.
|
||||
Will think about a complete solution later.
|
||||
*/
|
||||
unsigned char byfirstbyte[256][2];
|
||||
/* next free entry in sid[] */
|
||||
unsigned char next_free;
|
||||
} overlay_address_table;
|
||||
|
||||
typedef struct sid {
|
||||
unsigned char b[SID_SIZE];
|
||||
} sid;
|
||||
|
||||
typedef struct overlay_address_cache {
|
||||
int size;
|
||||
int shift; /* Used to calculat lookup function, which is (b[0].b[1].b[2]>>shift) */
|
||||
sid *sids; /* one entry per bucket, to keep things simple. */
|
||||
/* XXX Should have a means of changing the hash function so that naughty people can't try
|
||||
to force our cache to flush with duplicate addresses?
|
||||
But we must use only the first 24 bits of the address due to abbreviation policies,
|
||||
so our options are limited.
|
||||
For now the hash will be the first k bits.
|
||||
*/
|
||||
} overlay_address_cache;
|
||||
|
||||
overlay_address_table *abbrs=NULL;
|
||||
overlay_address_cache *cache=NULL;
|
||||
|
||||
sid overlay_abbreviate_previous_address={{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
|
||||
|
||||
/* The address of the sender of the current frame.
|
||||
The ID is used to lookup the abbreviation indexes.
|
||||
If the ID is -1, then this means that the sender SID has been set, but not looked up.
|
||||
This just saves some time instead of doing the *_id determination each time, when we might not need
|
||||
it on most occassions.
|
||||
*/
|
||||
sid overlay_abbreviate_current_sender={{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
|
||||
int overlay_abbreviate_current_sender_id=-1;
|
||||
|
||||
int overlay_abbreviate_cache_address(unsigned char *sid)
|
||||
{
|
||||
if ((!cache)&&OVERLAY_ADDRESS_CACHE_SIZE)
|
||||
{
|
||||
if (OVERLAY_ADDRESS_CACHE_SIZE>0x1000000)
|
||||
exit(WHY("OVERLAY_ADDRESS_CACHE_SIZE must be no larger than 2^24."));
|
||||
if (OVERLAY_ADDRESS_CACHE_SIZE<0)
|
||||
exit(WHY("OVERLAY_ADDRESS_CACHE_SIZE must be larger than 0."));
|
||||
|
||||
/* Allocate cache */
|
||||
cache=calloc(sizeof(overlay_address_cache),1);
|
||||
if (!cache) return 0;
|
||||
|
||||
/* Allocate address cache */
|
||||
cache->size=OVERLAY_ADDRESS_CACHE_SIZE;
|
||||
cache->sids=calloc(sizeof(sid),cache->size);
|
||||
if (!cache->sids) { free(cache); return 0; }
|
||||
|
||||
/* Work out the number of bits to shift */
|
||||
cache->shift=0;
|
||||
while(cache->size>>cache->shift) cache->shift++;
|
||||
|
||||
if ((1<<cache->shift)!=cache->size)
|
||||
exit(WHY("OVERLAY_ADDRESS_CACHE_SIZE must be a power of two."));
|
||||
}
|
||||
|
||||
if (!cache) return 0;
|
||||
|
||||
/* Work out the index in the cache where this address would go */
|
||||
int index=((sid[0]<<16)|(sid[0]<<8)|sid[2])>>cache->shift;
|
||||
|
||||
/* Does the stored address match the one we have been passed? */
|
||||
if (!bcmp(sid,&cache->sids[index].b[0],SID_SIZE))
|
||||
/* Address is already in cache, so return and let the caller know. */
|
||||
return 1;
|
||||
|
||||
/* Not yet in cache, so store it */
|
||||
bcopy(sid,&cache->sids[index].b[0],SID_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int overlay_abbreviate_try_byindex(unsigned char *in,char *out,int *ofs,int index)
|
||||
{
|
||||
if(!bcmp(in,abbrs->sids[index],SID_SIZE))
|
||||
{
|
||||
/* We can encode this address with one byte */
|
||||
out[(*ofs)++]=0x01;
|
||||
out[(*ofs)++]=index;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
/* Cannot find index in our node list. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
int overlay_abbreviate_address(unsigned char *in,char *out,int *ofs)
|
||||
{
|
||||
int i;
|
||||
int wasInCachedP=overlay_abbreviate_cache_address(in);
|
||||
|
||||
if (!in) return WHY("in==NULL");
|
||||
if (in[0]<0x10) return WHY("Invalid address - 0x00-0x0f are reserved prefixes.");
|
||||
|
||||
if (!abbrs) {
|
||||
// Abbreviation table not setup, so allocate it.
|
||||
// Epoch starts at zero.
|
||||
// XXX We have only one simultaneous epoch here, not that it is a problem.
|
||||
abbrs=calloc(sizeof(overlay_address_table),1);
|
||||
if (!abbrs)
|
||||
{
|
||||
// Could not allocate abbreviation table, so just output full-length address.
|
||||
WHY("calloc() failed.");
|
||||
bcopy(in,&out[*ofs],SID_SIZE);
|
||||
(*ofs)+=SID_SIZE;
|
||||
return 0;
|
||||
}
|
||||
abbrs->next_free=1;
|
||||
}
|
||||
|
||||
/* Try abbreviating by index
|
||||
XXX should search backwards through old epochs
|
||||
XXX If we do, we need a way to indicate a reference to an old epoch */
|
||||
for(i=0;i<2;i++)
|
||||
if (abbrs->byfirstbyte[in[0]][i])
|
||||
{ if (!overlay_abbreviate_try_byindex(in,out,ofs,i)) return 0; }
|
||||
else break;
|
||||
if (i<2&&abbrs->next_free) {
|
||||
// There is a spare slot to abbreviate this address by storing it in an index if we
|
||||
// wish. So let's store it, then send the full address along with the newly allocated
|
||||
// index.
|
||||
|
||||
/* Remember this address */
|
||||
bcopy(in,abbrs->sids[abbrs->next_free],SID_SIZE);
|
||||
abbrs->byfirstbyte[in[0]][i]=abbrs->next_free;
|
||||
|
||||
/* Write address out with index code */
|
||||
out[(*ofs)++]=0x08;
|
||||
bcopy(in,&out[(*ofs)],SID_SIZE);
|
||||
(*ofs)+=SID_SIZE;
|
||||
out[(*ofs)++]=abbrs->next_free;
|
||||
|
||||
/* Tidy things up and return triumphant. */
|
||||
abbrs->next_free++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* No space in our table, so either send address verbatim, or send only a prefix.
|
||||
Go for prefix. Next question is length of prefix. Seven bytes is probably about
|
||||
right as an simple initial policy. */
|
||||
if (wasInCachedP) {
|
||||
/* Prefix addresses that have been seen recently */
|
||||
out[(*ofs)++]=0x06;
|
||||
bcopy(in,&out[(*ofs)],7);
|
||||
(*ofs)+=7;
|
||||
return 0;
|
||||
} else {
|
||||
/* But send full address for those we haven't seen before */
|
||||
bcopy(in,&out[*ofs],SID_SIZE);
|
||||
(*ofs)+=SID_SIZE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int overlay_abbreviate_expand_address(unsigned char *in,int *inofs,unsigned char *out,int *ofs)
|
||||
{
|
||||
int bytes=0;
|
||||
switch(in[0])
|
||||
{
|
||||
case 0x00: case 0x02: case 0x04: case 0x0c:
|
||||
/* Unsupported codes, so tell the sender
|
||||
if the frame was addressed to us as next-hop */
|
||||
(*inofs)++;
|
||||
return OA_UNSUPPORTED;
|
||||
case 0x01: /* single byte index look up */
|
||||
exit(WHY("Unimplemented address abbreviation code"));
|
||||
break;
|
||||
case 0x03: /* Same as last address */
|
||||
(*inofs)++;
|
||||
bcopy(&overlay_abbreviate_previous_address.b[0],&out[*ofs],SID_SIZE);
|
||||
(*ofs)+=SID_SIZE;
|
||||
return OA_RESOLVED;
|
||||
break;
|
||||
case 0x05: case 0x09: /* 3-byte prefix */
|
||||
if (in[0]==0x09) bytes=1;
|
||||
(*inofs)+=3+bytes;
|
||||
return overlay_abbreviate_cache_lookup(in,out,ofs,3,bytes);
|
||||
case 0x06: case 0x0a: /* 7-byte prefix */
|
||||
if (in[0]==0x0a) bytes=1;
|
||||
(*inofs)+=7+bytes;
|
||||
return overlay_abbreviate_cache_lookup(in,out,ofs,7,bytes);
|
||||
case 0x07: case 0x0b: case 0x0d: /* 11-byte prefix */
|
||||
if (in[0]==0x0b) bytes=1;
|
||||
if (in[0]==0x0d) bytes=2;
|
||||
(*inofs)+=11+bytes;
|
||||
return overlay_abbreviate_cache_lookup(in,out,ofs,11,bytes);
|
||||
case 0x0f: /* broadcast */
|
||||
memset(&out[*ofs],0xff,SID_SIZE);
|
||||
(*inofs)++;
|
||||
return 0;
|
||||
case 0x08: case 0x0e: default: /* Full address, optionally followed by index for us to remember */
|
||||
if (in[0]==0x08) bytes=1;
|
||||
if (in[0]==0x0e) bytes=2;
|
||||
bcopy(in,&out[*ofs],SID_SIZE);
|
||||
if (bytes) overlay_abbreviate_remember_index(bytes,in,&in[SID_SIZE]);
|
||||
(*inofs)+=SID_SIZE+bytes;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int overlay_abbreviate_remember_index(int index_byte_count,unsigned char *in,unsigned char *index_bytes)
|
||||
{
|
||||
int zero=0;
|
||||
char sid[SID_SIZE*2+1];
|
||||
int index=index_bytes[0];
|
||||
if (index_byte_count>1) index=(index<<8)|index_bytes[1];
|
||||
|
||||
sid[0]=0; extractSid(in,&zero,sid);
|
||||
fprintf(stderr,"We need to remember that the sender #%d has assigned index #%d to the following:\n [%s]\n",
|
||||
overlay_abbreviate_current_sender_id,index,sid);
|
||||
return WHY("Not implemented");
|
||||
}
|
||||
|
||||
int overlay_abbreviate_cache_lookup(unsigned char *in,unsigned char *out,int *ofs,
|
||||
int prefix_bytes,int index_bytes)
|
||||
{
|
||||
/* Lookup this entry from the cache, and also assign it the specified prefix */
|
||||
|
||||
/* Work out the index in the cache where this address would live */
|
||||
int index=((in[0]<<16)|(in[0]<<8)|in[2])>>cache->shift;
|
||||
|
||||
/* So is it there? */
|
||||
if (bcmp(in,&cache->sids[index].b[0],prefix_bytes))
|
||||
{
|
||||
/* No, it isn't in the cache. */
|
||||
return OA_PLEASEEXPLAIN;
|
||||
}
|
||||
|
||||
/* XXX We should implement associativity in the address cache so that we can spot
|
||||
colliding prefixes and ask the sender to resolve them for us */
|
||||
|
||||
/* It is here, so let's return it */
|
||||
bcopy(&cache->sids[index].b[0],&out[(*ofs)],SID_SIZE);
|
||||
(*ofs)+=SID_SIZE;
|
||||
if (index_bytes) {
|
||||
/* We need to remember it as well, so do that.
|
||||
If this process fails, it is okay, as we can still resolve the address now.
|
||||
It will probably result in waste later though when we get asked to look it up,
|
||||
however the alternative definitely wastes bandwidth now, so let us defer the
|
||||
corrective action in case it is never required.
|
||||
*/
|
||||
overlay_abbreviate_remember_index(index_bytes,&cache->sids[index].b[0],&in[prefix_bytes]);
|
||||
}
|
||||
return OA_RESOLVED;
|
||||
}
|
||||
|
||||
int overlay_abbreviate_set_current_sender(char *in)
|
||||
{
|
||||
bcopy(in,&overlay_abbreviate_current_sender.b[0],SID_SIZE);
|
||||
overlay_abbreviate_current_sender_id=-1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int overlay_abbreviate_set_most_recent_address(char *in)
|
||||
{
|
||||
bcopy(in,&overlay_abbreviate_previous_address.b[0],SID_SIZE);
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user