Initial check in

git-svn-id: http://serval-dna.googlecode.com/svn/trunk/trunk/dna@2 6d01b88e-114a-4e5d-930d-818638e25ad7
This commit is contained in:
paul.gardner.stephen 2010-07-13 12:15:46 +00:00
commit f225465dfc
20 changed files with 3788 additions and 0 deletions

36
Makefile Normal file
View File

@ -0,0 +1,36 @@
OBJS= dna.o server.o client.o peers.o ciphers.o responses.o packetformats.o dataformats.o hlrdata.o srandomdev.o simulate.o batman.o
HDRS= Makefile mphlr.h
LDFLAGS=
DEFS= -DPACKAGE_NAME=\"\" -DPACKAGE_TARNAME=\"\" -DPACKAGE_VERSION=\"\" -DPACKAGE_STRING=\"\" -DPACKAGE_BUGREPORT=\"\" -DPACKAGE_URL=\"\" -DHAVE_LIBC=1 -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_STDIO_H=1 -DHAVE_ERRNO_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRINGS_H=1 -DHAVE_UNISTD_H=1 -DHAVE_STRING_H=1 -DHAVE_ARPA_INET_H=1 -DHAVE_SYS_SOCKET_H=1 -DHAVE_SYS_MMAN_H=1 -DHAVE_SYS_TIME_H=1 -DHAVE_POLL_H=1 -DHAVE_NETDB_H=1
all: dna
%.o: %.c $(HDRS)
$(CC) $(DEFS) -Os -g -Wall -c $<
dna: $(OBJS)
$(CC) -Os -g -Wall -o dna $(OBJS) $(LDFLAGS)
testserver: dna
clear
rm hlr.dat
./dna -vvv -S 1 -f hlr.dat
testcreate: dna
clear
./dna -vvv -d 0427679796 -C
@touch testcreate
testget: dna testcreate
clear
./dna -vvv -d 0427679796 -R dids | tee testget
testset: dna testget
clear
# Try writing a value to a variable
echo "short value" >shortvalue.txt
./dna -vvv -s `cat testget | cut -f2 -d: | tail -1` -i 0 -W note=@shortvalue.txt
testbigset: testget
clear
./dna -vvv -s `cat testget | cut -f2 -d: | tail -1` -i 0 -W note=@411.txt

36
Makefile.in Normal file
View File

@ -0,0 +1,36 @@
OBJS= dna.o server.o client.o peers.o ciphers.o responses.o packetformats.o dataformats.o hlrdata.o srandomdev.o simulate.o batman.o
HDRS= Makefile mphlr.h
LDFLAGS= @LDFLAGS@
DEFS= @DEFS@
all: dna
%.o: %.c $(HDRS)
$(CC) $(DEFS) -Os -g -Wall -c $<
dna: $(OBJS)
$(CC) -Os -g -Wall -o dna $(OBJS) $(LDFLAGS)
testserver: dna
clear
rm hlr.dat
./dna -vvv -S 1 -f hlr.dat
testcreate: dna
clear
./dna -vvv -d 0427679796 -C
@touch testcreate
testget: dna testcreate
clear
./dna -vvv -d 0427679796 -R dids | tee testget
testset: dna testget
clear
# Try writing a value to a variable
echo "short value" >shortvalue.txt
./dna -vvv -s `cat testget | cut -f2 -d: | tail -1` -i 0 -W note=@shortvalue.txt
testbigset: testget
clear
./dna -vvv -s `cat testget | cut -f2 -d: | tail -1` -i 0 -W note=@411.txt

38
TODO Normal file
View File

@ -0,0 +1,38 @@
Pending:
- Parallelise large-value reads and writes
- Fix out-of-order problem for writing large values to stdout (file is fine)
- Non-mmap() mode for Mesh Potato and JFFS
- Return early if all peers have responded (what about broadcast address virtual-peers?)
- Activation flag in HLR records so that create without further action does not make a completed record.
- async mode: fork()/background after first response with file output (for pipelining with MP IVRs)
- async mode: work with broadcast requests
- async mode: work with instance=-1 requests
- SID based Peer targeting, e.g., for writeItem
- cache peer:SID mappings to allow later unicasting
- consider NACK for peers with no results to return
Done:
20100623 - testdna script now uses /bin/sh instead of /bin/csh facilitating testing on more embedded targets
20100623 - Retries for requests (already deals with SET, including multi-packet SET?)
20100623 - Multi-packet GET retry policy (okay, it is as bad as TFTP with retries, but that is probably okay for now,
at least we have the potential to pipe-line)
20100623 - Multi-packet set doesn't send multiple packets (ok to confirm each in turn)
20100623 - Output to file confirmed functional
20100623 - BATMAN peer list retrieval
20100623 - excluding previous responders from re-broadcast requests
20100611 - Added code for template based file output when reading variables
20100611 - Added code to allow user specified peers
20100601 - Space retries properly in time, and allow multiple recvfroms() per retry.
20100601 - Do not request results from peers that have already responsed.
20100601 - Ensure broadcasts do not result in duplicate identical results
20100601 - broadcast get: compulsory retry regime
20100531 - Simulated packet loss / BER for server for testing.
20100531 - "response set" structure for efficient chaining of responses, as well as making it easier to manage retries
20100529 - SET working (replace and noreplace modes)
20100529 - Get rid of unnecessary timeouts
20100529 - Regression test script
20100529 - Batch-mode responses (for instance=-1)
20100529 - Multi-instance requests now working (and with no unnecessary delays)
20100529 - Search by SID now works
20100529 - Reading all instances of a variable by SID without timeout, but actually reading them all

141
batman.c Normal file
View File

@ -0,0 +1,141 @@
/*
Serval Distributed Numbering Architecture (DNA)
Copyright (C) 2010 Paul Gardner-Stephen
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "mphlr.h"
int getBatmanPeerList(char *socket_path,in_addr_t peers[],int *peer_count,int peer_max)
{
int sock;
struct sockaddr_un socket_address;
unsigned char buf[16384]; /* big enough for a gigabit jumbo frame or loopback godzilla-gram */
int ofs=0;
int bytes=0;
struct pollfd fds;
char cmd[30];
int notDone=1;
int res;
int tries=0;
askagain:
/* Make socket */
sock=socket(AF_LOCAL,SOCK_STREAM,0);
memset(&socket_address,0,sizeof(struct sockaddr_un));
socket_address.sun_family=AF_LOCAL;
if (strlen(socket_path)>256) return setReason("BATMAN socket path too long");
strcpy(socket_address.sun_path,socket_path);
/* Connect the socket */
if (connect(sock,(struct sockaddr*)&socket_address,sizeof(socket_address))<0)
return setReason("connect() to BATMAN socket failed.");
memset(&cmd[0],0,30);
snprintf(cmd,30,"d:%c",1);
if (write(sock,cmd,30)!=30)
{ close(sock); return setReason("write() command failed to BATMAN socket."); }
fds.fd=sock;
fds.events=POLLIN;
getmore:
while(notDone)
{
switch (poll(&fds,1,1500)) {
case 1: /* Excellent - we have a response */ break;
case 0: if (debug>1) fprintf(stderr,"BATMAN did not respond to peer enquiry.\n");
close(sock);
if (tries++<=3) goto askagain;
return setReason("No response from BATMAN.");
default: /* some sort of error, e.g., lost connection */
close(sock);
return setReason("poll() of BATMAN socket failed.");
}
res=read(sock,&buf[bytes],16383-bytes); close(sock);
ofs=0;
if (res<1) {
if (bytes)
{
/* Got a partial response, then a dead line.
Should probably ask again unless we have tried too many times.
*/
if (debug>2) fprintf(stderr,"Trying again after cold drop.\n");
close(sock);
bytes=0;
if (tries++<=3) goto askagain;
else return setReason("failed to read() from BATMAN socket (too many tries).");
}
return setReason("failed to read() from BATMAN socket.");
}
if (!res) return 0;
if (debug>1) fprintf(stderr,"BATMAN has responded with %d bytes.\n",res);
if (debug>2) dump("BATMAN says",&buf[bytes],res);
if (res<80 /*||buf[bytes]!='B' -- turns out we can't rely on this, either */
||buf[bytes+res-1]!=0x0a||buf[bytes+res-4]!='E')
{
/* Jolly BATMAN on Android sometimes sends us bung packets from time to
time. Sometimes it is fragmenting, other times it is just plain
odd.
If this happens, we should just ask again.
We should also try to reassemble fragments.
Sometimes we get cold-drops and resumes, too. Yay.
*/
if (buf[bytes+res-4]!='E') {
/* no end marker, so try adding record to the end. */
if (debug>2) fprintf(stderr,"Data has no end marker, accumulating.\n");
bytes+=res;
goto getmore;
}
close(sock);
goto askagain;
}
bytes+=res;
while(ofs<bytes)
{
if(debug>1) fprintf(stderr,"New line @ %d\n",ofs);
/* Check for IP address of peers */
if (isdigit(buf[ofs]))
{
int i;
for(i=0;ofs+i<bytes;i++)
if (buf[i+ofs]==' ') {
buf[i+ofs]=0;
if (*peer_count<peer_max) peers[(*peer_count)++]=inet_addr((char *)&buf[ofs]);
if (debug>1) fprintf(stderr,"Found BATMAN peer '%s'\n",&buf[ofs]);
buf[ofs+i]=' ';
break;
}
}
/* Check for end of transmission */
if (buf[ofs]=='E') { notDone=0; }
/* Skip to next line */
while(buf[ofs]>=' '&&(ofs<bytes)) ofs++;
while(buf[ofs]<' '&&(ofs<bytes)) ofs++;
}
bytes=0;
}
return 0;
}

26
ciphers.c Normal file
View File

@ -0,0 +1,26 @@
/*
Serval Distributed Numbering Architecture (DNA)
Copyright (C) 2010 Paul Gardner-Stephen
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "mphlr.h"
int packetDecipher(unsigned char *packet,int len,int cipher)
{
if (cipher) return setReason("Unknown packet cipher");
else return 0; /* plain text */
}

604
client.c Normal file
View File

@ -0,0 +1,604 @@
/*
Serval Distributed Numbering Architecture (DNA)
Copyright (C) 2010 Paul Gardner-Stephen
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "mphlr.h"
int packetSendFollowup(struct in_addr destination,
unsigned char *packet,int packet_len)
{
struct sockaddr_in peer_addr;
peer_addr.sin_family=AF_INET;
peer_addr.sin_port = htons(4110);
peer_addr.sin_addr.s_addr=destination.s_addr;
if (!serverMode) {
sock=socket(PF_INET,SOCK_DGRAM,0);
if (sock<0) {
fprintf(stderr,"Could not create UDP socket.\n");
exit(-3);
}
}
int r=sendto(sock,packet,packet_len,0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));
if (r<packet_len) {
if (debug) fprintf(stderr,"Could not send to %s (r=%d, packet_len=%d)\n",inet_ntoa(destination),r,packet_len);
perror("sendto");
} else {
if (debug>1) fprintf(stderr,"Sent request to client %s\n",inet_ntoa(destination));
}
return 0;
}
int packetSendRequest(int method,unsigned char *packet,int packet_len,int batchP,
unsigned char *transaction_id,struct response_set *responses)
{
int i;
int cumulative_timeout=0; /* ms */
int this_timeout=125; /* ms */
int peer_low,peer_high;
struct timeval time_in,now;
/* Prepare ephemeral UDP socket (hence no binding)
If in server mode, then we already have a socket available to us and appropriately bound */
if (!serverMode) {
sock=socket(PF_INET,SOCK_DGRAM,0);
if (sock<0) {
fprintf(stderr,"Could not create UDP socket.\n");
exit(-3);
}
}
/* Deal with special case */
if (method==REQ_REPLY)
{
int r=sendto(sock,packet,packet_len,0,(struct sockaddr *)&recvaddr,sizeof(recvaddr));
if (r<packet_len) {
if (debug) fprintf(stderr,"Could not send to client %s\n",inet_ntoa(client_addr));
} else {
if (debug>1) fprintf(stderr,"Sent request to client %s\n",inet_ntoa(client_addr));
}
return 0;
}
getPeerList();
gettimeofday(&time_in,NULL);
/* REQ_SERIAL & REQ_PARALLEL work in fundamentally different ways,
but it turns out the retry/timeout code is the dominant part.
So we do a bit of fiddling around to make one loop that can handle both */
if (method==REQ_SERIAL) {
peer_low=0; peer_high=peer_count-1;
/* If there are too many peers to allow sending to each three times, then we should
adjust our incremental timeout accordingly, so far as is practicable */
if (this_timeout*peer_count*3>timeout)
{
this_timeout=timeout/(3*peer_count);
if (this_timeout<10) this_timeout=10; /* 10ms minimum sending interval */
}
} else
{ peer_low=-1; peer_high=-1;}
while(cumulative_timeout<=timeout)
{
/* If not in serial mode, then send request to everyone immediately.
Make sure we only ask once in parallel mode, since it will always ask everyone */
if (method==REQ_PARALLEL) sendToPeers(packet,packet_len,method,0,responses);
else if (method!=REQ_SERIAL)
for(i=0;i<peer_count;i++) sendToPeers(packet,packet_len,method,i,responses);
/* If in serial mode, send request to peers in turn until one responds positively,
otherwise just deal with the reply fetching loop to listen to as many or few reply. */
for(i=peer_low;i<=peer_high;i++) {
struct response *rr;
if (i>-1) sendToPeers(packet,packet_len,REQ_SERIAL,i,responses);
/* Placing the timeout calculation here means that the total timeout is shared among
all peers in a serial request, but round-robining after each time-step.
We adjust this_timeout if there are many peers to allow 3 sends to each peer where possible.
*/
cumulative_timeout+=this_timeout;
int timeout_remaining=this_timeout;
while(1)
{
/* Wait for response */
int r=getReplyPackets(method,i,batchP,responses,transaction_id,timeout_remaining);
if (r&&debug>1) fprintf(stderr,"getReplyPackets(): Returned on timeout\n");
switch(method)
{
case REQ_PARALLEL:
/* XXX We could stop once all peers have replied.
(need to update the test script if we do that, so that it tests with multiple
peers and so tests that we wait if not all peers have responded) */
break;
case REQ_FIRSTREPLY:
if (debug>1) fprintf(stderr,"Returning with first reply (REQ_FIRSTREPLY)\n");
if (!r) return 0;
break;
case REQ_SERIAL:
if (!r) {
/* Stop if we have an affirmative response.
XXX - doesn't allow for out of order replies. */
if (debug>1) dumpResponses(responses);
rr=responses->last_response;
while (rr)
{
if (rr->checked) break;
if (debug>1)
fprintf(stderr,"Got a response code 0x%02x, checking if that is what we need.\n",rr->code);
switch (rr->code)
{
case ACTION_OKAY: case ACTION_DATA:
/* bingo */
if (!batchP) return 0;
break;
}
rr->checked=1;
rr=rr->prev;
}
/* not what we are after, so clear response and try with next peer */
clearResponses(responses);
}
break;
}
/* Wait for the previous timeout to really expire,
(this is for the case where all peers have replied) */
{
int elapsed_usecs=0;
int cumulative_usecs=cumulative_timeout*1000;
int remaining_usecs;
gettimeofday(&now,NULL);
elapsed_usecs=(now.tv_sec-time_in.tv_sec)*1000000;
elapsed_usecs+=(now.tv_usec-time_in.tv_usec);
remaining_usecs=cumulative_usecs-elapsed_usecs;
if (remaining_usecs<=0) break;
else timeout_remaining=remaining_usecs/1000;
}
}
}
cumulative_timeout+=this_timeout;
}
if (debug>1) if (cumulative_timeout>=timeout)
fprintf(stderr,"Request timed out after retries (timeout=%d, elapsed=%d).\n",
timeout,cumulative_timeout);
return 0;
}
/* Create a new HLR entry on a peer.
Should always try localhost as first peer, which is why
sendToPeers() does it that way. We just have to remember to
ask for serialised attempts, rather than all at once.
*/
int requestNewHLR(char *did,char *pin,char *sid)
{
unsigned char packet[8000];
int packet_len=0;
struct response_set responses;
unsigned char transaction_id[8];
bzero(&responses,sizeof(responses));
/* Prepare the request packet */
if (packetMakeHeader(packet,8000,&packet_len,NULL)) return -1;
bcopy(&packet[8],transaction_id,8);
if (packetSetDid(packet,8000,&packet_len,did)) return -1;
if (packetAddHLRCreateRequest(packet,8000,&packet_len)) return -1;
if (packetFinalise(packet,8000,&packet_len)) return -1;
/* Send it to peers, starting with ourselves, one at a time until one succeeds.
XXX - This could take a while if we have long timeouts for each. */
if (packetSendRequest(REQ_SERIAL,packet,packet_len,NONBATCH,transaction_id,&responses)) return -1;
/* Extract response */
if (debug>2) dumpResponses(&responses);
if (!responses.response_count) {
printf("NOREPLY\n");
return -1;
}
switch(responses.responses->code)
{
case ACTION_DECLINED:
printf("DECLINED\n");
return -1;
break;
case ACTION_OKAY:
{
char sid[128];
int ofs=0;
extractSid(&responses.responses->sid[0],&ofs,&sid[0]);
printf("OK:%s\n",sid);
}
return 0;
break;
default:
printf("ERROR:Unknown response 0x%02x\n",responses.responses->code);
return -1;
}
return setReason("Request creation of new HLR not implemented");
}
int getReplyPackets(int method,int peer,int batchP,
struct response_set *responses,
unsigned char *transaction_id,int timeout)
{
/* set timeout alarm */
/* get packets until timeout, or until we get a packet from the specified peer
if method==REQ_SERIAL. If REQ_SERIAL we also reject packets from other
senders as they must be spoofs.
*/
struct timeval t;
int timeout_secs;
int timeout_usecs;
int to=timeout;
if (debug>1) printf("getReplyPackets(policy=%d)\n",method);
/* Work out when the timeout will expire */
gettimeofday(&t,NULL);
timeout_secs=t.tv_sec; timeout_usecs=t.tv_usec;
if (to>1000) { timeout_secs+=(to/1000); to=to%1000; }
timeout_usecs+=to*1000; if (timeout_usecs>1000000) { timeout_secs++; timeout_usecs-=1000000; }
while(1) {
unsigned char buffer[16384];
socklen_t recvaddrlen=sizeof(recvaddr);
struct pollfd fds;
client_port=((struct sockaddr_in*)&recvaddr)->sin_port;
bzero((void *)&recvaddr,sizeof(recvaddr));
fds.fd=sock; fds.events=POLLIN;
while (poll(&fds,1,10 /* wait for 10ms at a time */)<1)
{
gettimeofday(&t,NULL);
if (t.tv_sec>timeout_secs) return 1;
if (t.tv_sec==timeout_secs&&t.tv_usec>=timeout_usecs) return 1;
}
client_port=((struct sockaddr_in*)&recvaddr)->sin_port;
int len=recvfrom(sock,buffer,sizeof(buffer),0,&recvaddr,&recvaddrlen);
client_addr=((struct sockaddr_in*)&recvaddr)->sin_addr;
if (debug) fprintf(stderr,"Received reply from %s (len=%d).\n",inet_ntoa(client_addr),len);
if (debug>1) dump("recvaddr",(unsigned char *)&recvaddr,recvaddrlen);
if (debug>2) dump("packet",(unsigned char *)buffer,len);
if (dropPacketP(len)) {
if (debug) fprintf(stderr,"Simulation mode: Dropped packet due to simulated link parameters.\n");
continue;
}
if (!packetOk(buffer,len,transaction_id)) {
/* Packet passes tests - extract responses and append them to the end of the response list */
if (extractResponses(client_addr,buffer,len,responses))
return setReason("Problem extracting response fields from reply packets");
if (method==REQ_SERIAL||method==REQ_FIRSTREPLY) {
if (!batchP) return 0;
/* In batch mode we need ACTION_DONE to mark end of transmission.
While it gets sent last, out-of-order delivery means we can't rely on
such a nice arrangement. */
{
/* XXX inefficient for long lists.
XXX can be made better by working backwards from end using double-linked list and
remembering the previous length of the list */
struct response *r=responses->responses;
while(r)
{
if (r->code==ACTION_DONE) return 0;
r=r->next;
}
}
}
else {
if (debug>1) printf("Waiting for more packets, since called with policy %d\n",method);
}
} else {
if (debug) setReason("Ignoring invalid packet");
}
}
}
int writeItem(char *sid,int var_id,int instance,unsigned char *value,
int value_start,int value_length,int flags)
{
unsigned char packet[8000];
int packet_len=0;
struct response_set responses;
struct response *r;
unsigned char transaction_id[8];
bzero(&responses,sizeof(responses));
if (debug>1) fprintf(stderr,"Writing %d bytes of var %02x/%02x @ 0x%d flags=%d\n",
value_length,var_id,instance,value_start,flags);
if (!sid) {
printf("ERROR:Must use SID when writing values.\n");
return -1;
}
/* Split long writes into many short writes.
(since each write is acknowledged, we don't have to worry about batch mode) */
if (value_length-value_start>MAX_DATA_BYTES)
{
int o;
if (debug) fprintf(stderr,"Writing large value (%d bytes)\n",value_length-value_start);
for(o=value_start;o<value_length;o+=MAX_DATA_BYTES)
{
int bytes=MAX_DATA_BYTES;
if (o+bytes>value_length) bytes=value_length-o;
if (debug>1) fprintf(stderr," writing [%d,%d)\n",o,o+bytes-1);
if (writeItem(sid,var_id,instance,&value[o-value_start],o,bytes,
flags|((o>value_start)?SET_FRAGMENT:0)))
{
if (debug) fprintf(stderr," - writing installment failed\n");
return setReason("Failure during multi-packet write of long-value");
}
}
printf("OK:%s\n",sid);
return 0;
}
/* Prepare the request packet */
if (packetMakeHeader(packet,8000,&packet_len,NULL)) return -1;
bcopy(&packet[8],transaction_id,8);
if (packetSetSid(packet,8000,&packet_len,sid)) return -1;
if (packetAddVariableWrite(packet,8000,&packet_len,var_id,instance,
value,value_start,value_length,flags)) return -1;
if (packetFinalise(packet,8000,&packet_len)) return -1;
/* XXX should be able to target to the peer holding the SID, if we have it.
In any case, we */
if (packetSendRequest(REQ_FIRSTREPLY,packet,packet_len,NONBATCH,transaction_id,&responses)) return -1;
r=responses.responses;
while(r)
{
int slen;
char sid[SID_SIZE*2+1];
extractSid(r->sid,&slen,sid);
switch(r->code)
{
case ACTION_ERROR:
/* We allocate an extra byte to allow us to do this */
r->response[r->response_len]=0;
printf("ERROR:%s\n",(char *)r->response);
break;
case ACTION_OKAY: printf("ERROR:Unexpected OK response\n"); break;
case ACTION_DECLINED: printf("DECLINED:%s\n",sid); break;
case ACTION_WROTE:
/* Supress success messages when writing fragments */
if (!(flags&SET_FRAGMENT)) printf("WROTE:%s\n",sid); break;
case ACTION_DATA: printf("ERROR:DATA reponse not implemented\n"); break;
case ACTION_GET: printf("ERROR:You cant respond with GET\n"); break;
case ACTION_SET: printf("ERROR:You cant respond with SET\n"); break;
case ACTION_DEL: printf("ERROR:You cant respond with DEL\n"); break;
case ACTION_CREATEHLR: printf("ERROR:You cant respond with CREATEHLR\n"); break;
case ACTION_PAD: /* ignore it */ break;
case ACTION_EOT: /* ignore it */ break;
default: printf("ERROR:Unexpected response code 0x%02x\n",r->code);
}
fflush(stdout);
r=r->next;
}
return 0;
}
int requestItem(char *did,char *sid,char *item,int instance,unsigned char *buffer,int buffer_length,int *len,
unsigned char *transaction_id)
{
unsigned char packet[8000];
int packet_len=0;
struct response *r;
struct response_set responses;
bzero(&responses,sizeof(responses));
/* Prepare the request packet */
if (packetMakeHeader(packet,8000,&packet_len,transaction_id)) return -1;
if (did&&(!sid))
{ if (packetSetDid(packet,8000,&packet_len,did)) return -1; }
else if (sid&&(!did))
{ if (packetSetSid(packet,8000,&packet_len,sid)) return -1; }
else return setReason("You must request items by DID or SID, not neither, nor both");
if (packetAddVariableRequest(packet,8000,&packet_len,
item,instance,0,buffer_length)) return -1;
if (packetFinalise(packet,8000,&packet_len)) return -1;
int method=REQ_PARALLEL;
if (sid) method=REQ_FIRSTREPLY;
if (packetSendRequest(method,packet,packet_len,(instance==-1)?BATCH:NONBATCH,transaction_id,&responses)) return -1;
r=responses.responses;
while(r)
{
char sid[SID_SIZE*2+1];
int slen=0;
extractSid(r->sid,&slen,sid);
switch(r->code)
{
case ACTION_OKAY: printf("OK:%s\n",sid); break;
case ACTION_DECLINED: printf("DECLINED:%s\n",sid); break;
case ACTION_DATA:
/* Display data.
The trick is knowing the format of the data.
Fortunately we get the variable id etc.
XXX Need to deal with fragmented values.
(perhaps the fragments should get auto-assembled when accepting the responses)
*/
switch(r->var_id)
{
case VAR_DIDS:
{
char did[DID_MAXSIZE+1];
int dlen=0;
did[0]=0;
extractDid(r->response,&dlen,did);
printf("DIDS:%s:%d:%s\n",sid,r->var_instance,did);
}
break;
case VAR_NOTE:
default:
/* ASCII formatted variable types */
{
int v=0;
int i=0;
FILE *outputfile=stdout;
while(vars[v].name&&vars[v].id!=r->var_id) v++;
if (!vars[v].id) printf("0x%02x",r->var_id);
while(vars[v].name[i]) fputc(toupper(vars[v].name[i++]),stdout);
printf(":%s:%d:",sid,r->var_instance);
if (outputtemplate)
{
char outputname[8192];
snprintf(outputname,8192,outputtemplate,sid,r->var_id,r->var_instance);
outputfile=fopen(outputname,"w");
if (!outputfile) printf("ERROR:Could not open output file '%s'",outputname);
if (debug) fprintf(stderr,"Writing output to '%s'\n",outputname);
}
if (outputfile) fwrite(r->response,r->value_bytes,1,outputfile);
if (r->value_bytes<r->value_len)
{
/* Partial response, so ask for the rest of it */
unsigned char packet[8000];
int packet_len=0;
struct response *rr;
struct response_set responses;
int offset,max_bytes;
int recv_map[1+(r->value_len/MAX_DATA_BYTES)];
int recv_map_size=1+(r->value_len/MAX_DATA_BYTES);
int needMoreData;
int tries=0;
/* work out EXACTLY how many installments we need */
while (((recv_map_size-1)*MAX_DATA_BYTES)>=r->value_len) recv_map_size--;
recv_map[0]=0; /* we received the first installment, so mark it off ... */
/* ... but we haven't received the rest */
for(i=1;i<recv_map_size;i++) recv_map[i]=0;
/* Ask for all remaining pieces in parallel, then keep track of what has arrived
XXX - Not yet implemented. Currently uses a slow serial method, worse than TFTP */
needMoreData=recv_map_size-1;
while(needMoreData&&(tries++<15))
{
if (debug>1) fprintf(stderr,"Multi-packet request: try %d, %d fragments remaining.\n",tries,needMoreData);
needMoreData=0;
for(i=0;i<recv_map_size;i++)
if (!recv_map[i])
{
needMoreData++;
offset=i*MAX_DATA_BYTES;
if (debug>1) fprintf(stderr,"Asking for variable segment @ offset %d\n",offset);
/* Send accumulated request direct to the responder */
if (packet_len>=MAX_DATA_BYTES)
{
if (packetFinalise(packet,8000,&packet_len)) return -1;
packetSendFollowup(r->sender,packet,packet_len);
packet_len=0;
}
/* Prepare a new request packet if one is not currently being built */
if (!packet_len)
{
if (packetMakeHeader(packet,8000,&packet_len,transaction_id)) return -1;
if (packetSetSid(packet,8000,&packet_len,sid)) return setReason("SID went mouldy during multi-packet get");
}
max_bytes=65535-offset;
if (max_bytes>buffer_length) max_bytes=buffer_length;
if (packetAddVariableRequest(packet,8000,&packet_len,
item,r->var_instance,offset,max_bytes)) return -1;
}
/* Send accumulated request direct to the responder */
if (packet_len)
{
if (packetFinalise(packet,8000,&packet_len)) return -1;
packetSendFollowup(r->sender,packet,packet_len);
packet_len=0;
}
/* Collect responses to our multiple requests */
bzero(&responses,sizeof(responses));
/* XXX should target specific peer that sent first piece */
getReplyPackets(REQ_PARALLEL,i,0,&responses,transaction_id,250);
rr=responses.responses;
while(rr)
{
if (rr->code==ACTION_DATA&&rr->var_id==r->var_id&&rr->var_instance==r->var_instance)
{
int piece=rr->value_offset/MAX_DATA_BYTES;
if (!recv_map[piece])
{
if (debug>1) fprintf(stderr,"Extracted value fragment @ offset %d, with %d bytes\n",rr->value_offset,rr->value_bytes);
if (debug>2) dump("Fragment",rr->response,rr->value_bytes);
fseek(outputfile,rr->value_offset,SEEK_SET);
fwrite(rr->response,rr->value_bytes,1,outputfile);
recv_map[piece]=1;
}
else
{
if (debug>1) fprintf(stderr,"DUPLICATE value fragment @ offset %d, with %d bytes\n",rr->value_offset,rr->value_bytes);
}
}
rr=rr->next;
}
clearResponses(&responses);
}
}
if (outputtemplate) fclose(outputfile); else fflush(outputfile);
printf("\n");
break;
}
}
break;
case ACTION_DONE:
printf("DONE:%s:%d\n",sid,r->response[0]);
break;
case ACTION_GET: printf("ERROR:You cant respond with GET\n"); break;
case ACTION_SET: printf("ERROR:You cant respond with SET\n"); break;
case ACTION_WROTE: printf("ERROR:You cant respond with WROTE\n"); break;
case ACTION_DEL: printf("ERROR:You cant respond with DEL\n"); break;
case ACTION_CREATEHLR: printf("ERROR:You cant respond with CREATEHLR\n"); break;
case ACTION_PAD: /* ignore it */ break;
case ACTION_EOT: /* ignore it */ break;
default: printf("ERROR:Unexpected response code 0x%02x\n",r->code);
}
fflush(stdout);
r=r->next;
}
return -1;
}

14
configure.in Normal file
View File

@ -0,0 +1,14 @@
dnl Process this file with autoconf to produce a configure script.
AC_INIT(dna.c)
dnl Checks for programs.
AC_PROG_CC
AC_CHECK_LIB(c,srandomdev)
AC_CHECK_HEADERS(stdio.h errno.h stdlib.h strings.h unistd.h string.h arpa/inet.h sys/socket.h sys/mman.h sys/time.h poll.h netdb.h)
AC_CHECK_LIB(nsl,callrpc,[LDFLAGS="$LDFLAGS -lnsl"])
AC_CHECK_LIB(socket,socket,[LDFLAGS="$LDFLAGS -lsocket"])
AC_OUTPUT(Makefile)

191
dataformats.c Normal file
View File

@ -0,0 +1,191 @@
/*
Serval Distributed Numbering Architecture (DNA)
Copyright (C) 2010 Paul Gardner-Stephen
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "mphlr.h"
int extractDid(unsigned char *packet,int *ofs,char *did)
{
int d=0;
int highP=1;
int nybl;
nybl=0;
while(nybl!=0xf&&(*ofs<(OFS_SIDDIDFIELD+SIDDIDFIELD_LEN))&&(d<64))
{
if (highP) nybl=packet[*ofs]>>4; else nybl=packet[*ofs]&0xf;
if (nybl<0xa) did[d++]='0'+nybl;
else
switch(nybl) {
case 0xa: did[d++]='*'; break;
case 0xb: did[d++]='#'; break;
case 0xc: did[d++]='+'; break;
}
if (highP) highP=0; else { (*ofs)++; highP=1; }
}
if (d>63) return setReason("DID too long");
did[d]=0;
return 0;
}
int stowDid(unsigned char *packet,int *ofs,char *did)
{
int highP=1;
int nybl;
int d=0;
int len=0;
while(did[d]&&(len<DID_MAXSIZE))
{
switch(did[d])
{
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
nybl=did[d]-'0'; break;
case '*': nybl=0xa; break;
case '#': nybl=0xb; break;
case '+': nybl=0xc; break;
default:
setReason("Illegal digits in DID number");
return -1;
}
if (highP) { packet[*ofs]=nybl<<4; highP=0; }
else {
packet[(*ofs)++]|=nybl; highP=1;
}
d++; len++;
}
if (len>=DID_MAXSIZE)
{
setReason("DID number too long");
return -1;
}
/* Append end of number code, filling the whole byte for fast and easy comparison */
if (highP) packet[(*ofs)++]=0xff;
else packet[(*ofs)++]|=0x0f;
/* Fill remainder of field with randomness to protect any encryption */
for(;len<DID_MAXSIZE;len++) packet[(*ofs)++]=random()&0xff;
return 0;
}
int extractSid(unsigned char *packet,int *ofs,char *sid)
{
int i=0;
int d=0;
for(i=0;i<SID_SIZE;i++)
{
sid[d++]=hexdigit[packet[*ofs]>>4];
sid[d++]=hexdigit[packet[*ofs]&0xf];
(*ofs)++;
}
sid[64]=0;
return 0;
}
int stowSid(unsigned char *packet,int ofs,char *sid)
{
int i;
if (strlen(sid)!=64) return setReason("Asked to stow invalid SID (should be 64 hex digits)");
for(i=0;i<SID_SIZE;i++)
{
if (hexvalue(sid[i<<1])<0) return -1;
packet[ofs]=hexvalue(sid[i<<1])<<4;
if (hexvalue(sid[(i<<1)+1])<0) return -1;
packet[ofs++]|=hexvalue(sid[(i<<1)+1]);
}
return 0;
}
int packetGetID(unsigned char *packet,int len,char *did,char *sid)
{
int ofs=HEADERFIELDS_LEN;
switch(packet[ofs])
{
case 0: /* DID */
ofs++;
if (extractDid(packet,&ofs,did)) return setReason("Could not decode DID");
if (debug>1) fprintf(stderr,"Decoded DID as %s\n",did);
return 0;
break;
case 1: /* SID */
ofs++;
if (len<(OFS_SIDDIDFIELD+SID_SIZE)) return setReason("Packet too short");
if (extractSid(packet,&ofs,sid)) return setReason("Could not decode SID");
return 0;
break;
default: /* no idea */
break;
return setReason("Unknown ID key");
}
return setReason("Impossible event #1 just occurred");
}
/*
One of the goals of our packet format is to make it very difficult to mount a known plain-text
attack against the ciphered part of the packet.
One defence is to make sure that no fixed fields are actually left zero.
We accomplish this by filling "zero" fields with randomised data that meets a simple test condition.
We have chosen to use the condition that if the modulo 256 sum of the bytes equals zero, then the packet
is assumed to be zero/empty.
The following two functions allow us to test this, and also to fill a field with safe "zero" data.
*/
int isFieldZeroP(unsigned char *packet,int start,int count)
{
int mod=0;
int i;
for(i=start;i<start+count;i++)
{
mod+=packet[i];
mod&=0xff;
}
if (debug>3) {
if (mod) fprintf(stderr,"Field [%d,%d) is non-zero (mod=0x%02x)\n",start,start+count,mod);
else fprintf(stderr,"Field [%d,%d) is zero\n",start,start+count);
}
if (mod) return 0; else return 1;
}
int safeZeroField(unsigned char *packet,int start,int count)
{
int mod=0;
int i;
if (debug>3) fprintf(stderr,"Known plain-text counter-measure: safe-zeroing [%d,%d)\n",
start,start+count);
for(i=start;i<(start+count-1);i++)
{
packet[i]=random()&0xff;
mod+=packet[i];
mod&=0xff;
}
/* set final byte so that modulo sum is zero */
packet[i]=(0x100-mod)&0xff;
return 0;
}

351
dna.c Normal file
View File

@ -0,0 +1,351 @@
/*
Serval Distributed Numbering Architecture (DNA)
Copyright (C) 2010 Paul Gardner-Stephen
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "mphlr.h"
char *outputtemplate=NULL;
int debug=0;
int timeout=3000; /* 3000ms request timeout */
int serverMode=0;
int clientMode=0;
int hexdigit[16]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
struct mphlr_variable vars[]={
/* Variables that can have only a single value */
{VAR_EOR,"eor","Marks end of record"},
{VAR_CREATETIME,"createtime","Time HLR record was created"},
{VAR_CREATOR,"creator","Device that created this HLR record"},
{VAR_REVISION,"revision","Revision number of this HLR record"},
{VAR_REVISOR,"revisor","Device that revised this HLR record"},
{VAR_PIN,"pin","Secret PIN for this HLR record"},
/* GSM encoded audio, so a 16KB MPHLR maximum size shouldn't
pose a problem. 8KB = ~4.5 seconds, which is a long time
to say your name in, leaving 8KB for other variables. */
{VAR_VOICESIG,"voicesig","Voice signature of this subscriber"},
{VAR_HLRMASTER,"hlrmaster","Location where the master copy of this HLR record is maintained."},
/* Variables that can take multiple values */
{VAR_DIDS,"dids","Numbers claimed by this subscriber"},
{VAR_LOCATIONS,"locations","Locations where this subscriber wishes to receive calls"},
{VAR_IEMIS,"iemis","GSM IEMIs claimed by this subscriber"},
{VAR_TEMIS,"temis","GSM TEMIs claimed by this subscriber"},
/* Each entry here has a flag byte (unread, ...) */
{VAR_CALLS_IN,"callsin","Calls received by this subscriber"},
{VAR_CALLS_MISSED,"callsmissed","Calls missed by this subscriber"},
{VAR_CALLS_OUT,"callsout","Calls made by this subscriber"},
{VAR_SMESSAGES,"smessages","SMS received by this subscriber"},
{VAR_DID2SUBSCRIBER,"did2subscriber","Preferred subscribers for commonly called DIDs"},
{VAR_HLRBACKUPS,"hlrbackups","Locations where backups of this HLR record are maintained."},
{VAR_NOTE,"note","Free-form notes on this HLR record"},
{0x00,NULL,NULL}
};
int sock;
#ifndef HAVE_BZERO
/* OpenWRT doesn't have bzero */
void bzero(void *m,size_t len)
{
unsigned char *c=m;
int i;
for(i=0;i<len;i++) c[i]=0;
}
#endif
int dump(char *name,unsigned char *addr,int len)
{
int i,j;
fprintf(stderr,"Dump of %s\n",name);
for(i=0;i<len;i+=16)
{
fprintf(stderr," %04x :",i);
for(j=0;j<16&&(i+j)<len;j++) fprintf(stderr," %02x",addr[i+j]);
for(;j<16;j++) fprintf(stderr," ");
fprintf(stderr," ");
for(j=0;j<16&&(i+j)<len;j++) fprintf(stderr,"%c",addr[i+j]>=' '&&addr[i+j]<0x7f?addr[i+j]:'.');
fprintf(stderr,"\n");
}
return 0;
}
int dumpResponses(struct response_set *responses)
{
struct response *r;
if (!responses) {fprintf(stderr,"Response set is NULL\n"); return 0; }
fprintf(stderr,"Response set claims to contain %d entries.\n",responses->response_count);
r=responses->responses;
while(r)
{
fprintf(stderr," response code 0x%02x\n",r->code);
if (r->next)
if (r->next->prev!=r) fprintf(stderr," !! response chain is broken\n");
r=r->next;
}
return 0;
}
int setReason(char *msg)
{
fprintf(stderr,"Error: %s\n",msg);
return -1;
}
int hexvalue(unsigned char c)
{
if (c>='0'&&c<='9') return c-'0';
if (c>='A'&&c<='F') return c-'A'+10;
if (c>='a'&&c<='f') return c-'a'+10;
return setReason("Invalid hex digit in SID");
}
int parseAssignment(unsigned char *text,int *var_id,unsigned char *value,int *value_len)
{
/* Parse an assignment.
Valid formats are:
var=@file - value comes from named file.
var=[[$]value] - value comes from string, and may be empty. $ means value is in hex
Values are length limited to 65535 bytes.
*/
int i,v;
int max_len=*value_len;
int vlen=0;
int tlen=strlen((char *)text);
if (tlen>3072) {
return setReason("Variable assignment string is too long, use =@file to read value from a file");
}
/* Identify which variable */
for(i=0;i<tlen;i++) if (text[i]=='=') break;
for(v=0;vars[v].name;v++) if (!strncasecmp(vars[v].name,(char *)text,i)) break;
if (!vars[v].name) return setReason("Illegal variable name in assignment");
*var_id=vars[v].id;
i++;
switch(text[i])
{
case '$': /* hex */
i++;
while(i<tlen) {
int b=hexvalue(text[i++])<<4;
if (i>=tlen) return setReason("Variable value has an odd number of hex digits.");
b|=hexvalue(text[i++]);
if (b<0) return setReason("That doesn't look like hex to me");
if (vlen>=max_len) return setReason("Variable hex value too long");
value[vlen++]=b;
}
*value_len=vlen;
return 0;
break;
case '@': /* file */
{
FILE *f=fopen((char *)&text[i+1],"r");
int flen;
fseek(f,0,SEEK_END);
flen=ftell(f);
if (flen>max_len) return setReason("Variable value from file too long");
fseek(f,0,SEEK_SET);
vlen=fread(value,1,flen,f);
if (vlen!=flen) return setReason("Could not read all of file");
fclose(f);
*value_len=vlen;
return 0;
}
break;
default: /* literal string */
vlen=strlen((char *)&text[i]);
if (vlen>max_len) return setReason("Variable value too long");
bcopy(&text[i],value,vlen);
*value_len=vlen;
return 0;
}
return 0;
}
int usage(char *complaint)
{
fprintf(stderr,"dna: %s\n",complaint);
fprintf(stderr,"usage:\n");
fprintf(stderr," dna [-v ...] -S <hlr size in MB> [-f HLR backing file]\n");
fprintf(stderr,"or\n");
fprintf(stderr," dna <-d|-s> id [-p pin] [-i variable instance] <-R variable[=value]>\n");
fprintf(stderr," [-v ...] [-t request timeout in ms] [-O output file name template]\n");
fprintf(stderr,"or\n");
fprintf(stderr," dna <-d|-s> id [-p pin] [-i variable instance] <-W|-U|-D variable[=[$|@]value]>\n");
fprintf(stderr," [-v ...] [-t request timeout in ms]\n");
fprintf(stderr,"or\n");
fprintf(stderr," dna [-v ...] [-t timeout] -d did -C\n");
fprintf(stderr,"\n");
fprintf(stderr," -v - increase verbosity.\n");
fprintf(stderr," -b - Specify BATMAN socket to obtain peer list.\n");
fprintf(stderr," -n - Do not detach from foreground in server mode.\n");
fprintf(stderr," -S - Run in server mode with an HLR of the specified size.\n");
fprintf(stderr," -f - Use the specified file as a permanent store for HLR data.\n");
fprintf(stderr," -d - Search by Direct Inward Dial (DID) number.\n");
fprintf(stderr," -s - Search by Subscriber ID (SID) number.\n");
fprintf(stderr," -p - Specify additional DNA nodes to query.\n");
fprintf(stderr," -P - Authenticate using the supplied pin.\n");
fprintf(stderr," -R - Read a variable value.\n");
fprintf(stderr," -O - Place read variable value into files using argument as a template.\n");
fprintf(stderr," The following template codes can be used (interpretted by sprintf):\n");
fprintf(stderr," %%1$s - Subscriber ID\n");
fprintf(stderr," %%2$d - Variable ID (0-255)\n");
fprintf(stderr," %%3$d - Variable instance number (0-255)\n");
fprintf(stderr," -W - Write a variable value, keeping previous values.\n");
fprintf(stderr," -U - Update a variable value, replacing the previous value.\n");
fprintf(stderr," -D - Delete a variable value.\n");
fprintf(stderr," $value means interpret value as hexidecimal bytes.\n");
fprintf(stderr," @value means read value from file called value.\n");
fprintf(stderr," -C - Request the creation of a new subscriber with the specified DID.\n");
fprintf(stderr," -t - Specify the request timeout period.\n");
fprintf(stderr,"\n");
exit(-1);
}
int main(int argc,char **argv)
{
int c;
char *pin=NULL;
char *did=NULL;
char *sid=NULL;
char *hlr_file=NULL;
int instance=-1;
int foregroundMode=0;
srandomdev();
while((c=getopt(argc,argv,"b:B:S:f:d:i:np:P:s:t:vR:W:U:D:CO:")) != -1 )
{
switch(c)
{
case 'n': /* don't detach from foreground in server mode */
foregroundMode=1; break;
case 'b': /* talk peers on a BATMAN mesh */
batman_socket=strdup(optarg);
break;
case 'B': /* Set simulated Bit Error Rate for bench-testing */
simulatedBER=atof(optarg);
fprintf(stderr,"WARNING: Bit error injection enabled -- this will cause packet loss and is intended only for testing.\n");
break;
case 'S':
if (atof(optarg)<0.1||atof(optarg)>16384) usage("HLR must be 0.1MB - 16384MB in size.");
hlr_size=(int)(atof(optarg)*1048576.0);
serverMode=1;
break;
case 'i':
instance=atoi(optarg);
if (instance<-1||instance>255) usage("Illegal variable instance ID.");
break;
case 'f':
if (clientMode||(!serverMode)) usage("Only servers use backing files");
hlr_file=strdup(optarg);
break;
case 'p': /* additional peers to query */
if (additionalPeer(optarg)) exit(-3);
break;
case 'P': /* Supply pin */
pin=strdup(optarg);
clientMode=1;
break;
case 'd': /* Ask by DID */
clientMode=1;
did=strdup(optarg);
break;
case 's': /* Ask by subscriber ID */
clientMode=1;
sid=strdup(optarg);
break;
case 't': /* request timeout (ms) */
timeout=atoi(optarg);
break;
case 'v': /* Increase verbosity */
debug++;
break;
case 'R': /* read a variable */
{
unsigned char buffer[65535];
int len=0;
requestItem(did,sid,(char *)optarg,instance,buffer,sizeof(buffer),&len,NULL);
}
break;
case 'W': /* write a variable */
{
int var_id;
unsigned char value[65536];
int value_len=65535;
if (parseAssignment((unsigned char *)optarg,&var_id,value,&value_len)) return -1;
value[value_len]=0;
return writeItem(did?did:sid,var_id,instance,value,0,value_len,SET_NOREPLACE);
}
break;
case 'U': /* write or update a variable */
{
int var_id;
unsigned char value[65536];
int value_len=65535;
if (parseAssignment((unsigned char *)optarg,&var_id,value,&value_len)) return -1;
value[value_len]=0;
return writeItem(did?did:sid,var_id,instance,value,0,value_len,SET_REPLACE);
}
break;
case 'C': /* create a new HLR entry */
{
if (optind<argc) usage("Extraneous options after HLR creation request");
if ((!did)||(sid)) usage("Specify exactly one DID and no SID to create a new HLR entry");
return requestNewHLR(did,pin,sid);
}
break;
case 'O': /* output to templated files */
if (outputtemplate) usage("You can only specify -O once");
outputtemplate=strdup(optarg);
break;
default:
usage("Invalid option");
break;
}
}
if (optind<argc) usage("Extraneous options at end of command");
if (hlr_file&&clientMode) usage("Only servers use backing files");
if (serverMode&&clientMode) usage("You asked me to be both server and client. That's silly.");
if (serverMode) return server(hlr_file,hlr_size,foregroundMode);
if (!clientMode) usage("Mesh Potato Home Location Register (HLR) Tool.");
/* Client mode: */
return 0;
}

437
hlrdata.c Normal file
View File

@ -0,0 +1,437 @@
/*
Serval Distributed Numbering Architecture (DNA)
Copyright (C) 2010 Paul Gardner-Stephen
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "mphlr.h"
int bcompare(unsigned char *a,unsigned char *b,size_t len)
{
int i;
for(i=0;i<len;i++) if (a[i]<b[i]) return -1; else if (a[i]>b[i]) return 1;
return 0;
}
int nextHlr(unsigned char *hlr,int *ofs)
{
int record_length;
if (!ofs) return setReason("nextHlr passed NULL pointer.");
if (*ofs>=hlr_size) return -1;
/* Get length of this record */
record_length =hlr[(*ofs)+3]<<0;
record_length|=hlr[(*ofs)+2]<<8;
record_length|=hlr[(*ofs)+1]<<16;
record_length|=hlr[(*ofs)+0]<<24;
if (!record_length) return 0;
(*ofs)+=record_length;
return 0;
}
char sid_string[SID_SIZE*2+1];
char *hlrSid(unsigned char *hlr,int ofs)
{
int o=ofs+4;
extractSid(hlr,&o,sid_string);
return sid_string;
}
int findHlr(unsigned char *hlr,int *ofs,char *sid,char *did)
{
unsigned int record_length;
int match=0;
int records_searched=0;
int pid_len=0;
unsigned char packed_id[40];
if ((*ofs)>=hlr_size) return 0;
if (debug>1) fprintf(stderr,"Searching for HLR record sid=[%s]/did=[%s]\n",sid?sid:"NULL",did?did:"NULL");
if (did&&did[0]) {
/* Make packed version of DID so that we can compare faster with the DIDs in the HLR */
if (stowDid(packed_id,&pid_len,did)) return setReason("DID appears to be invalid");
/* Find significant length of packed DID */
for(pid_len=0;pid_len<DID_MAXSIZE;pid_len++) if ((packed_id[pid_len]&0x0f)==0x0f) { pid_len++; break; }
if (debug>1) dump("Searching for DID records that match",packed_id,pid_len);
}
if (sid&&sid[0]) {
/* Make packed version of SID for fast comparison */
if (stowSid(packed_id,pid_len,sid)) return setReason("SID appears to be invalid");
pid_len=SID_SIZE;
}
while(!match)
{
/* Get length of this record */
record_length =hlr[(*ofs)+3]<<0;
record_length|=hlr[(*ofs)+2]<<8;
record_length|=hlr[(*ofs)+1]<<16;
record_length|=hlr[(*ofs)+0]<<24;
if (!record_length) return 0;
if (debug>1) fprintf(stderr,"Considering HLR entry @ 0x%x\n",*ofs);
records_searched++;
if (sid&&sid[0]) {
/* Lookup by SID, so just see if it matches */
if (!bcompare(packed_id,&hlr[(*ofs)+4],SID_SIZE)) {
if (debug>1) fprintf(stderr,"Found requested SID at address 0x%x.\n",*ofs);
match=1;
}
}
if (did&&did[0]) {
/* Lookup by did, so see if there are any matching DID entries for this subscriber */
int rofs=(*ofs);
struct hlrentry_handle *h=openhlrentry(hlr,rofs);
while(h)
{
/* Search through variables for matching DIDs */
if (debug>2) {
fprintf(stderr,"Considering variable 0x%02x, instance %d.\n",
h->var_id,h->var_instance);
dump("variable value",h->value,h->value_len);
}
if (h->var_id==VAR_DIDS) { /* DID entry */
if (debug>2) fprintf(stderr,"Checking DID against record DID\n");
if (!bcompare(packed_id,h->value,pid_len)) {
if (debug>1) fprintf(stderr,"Found matching DID in HLR record #%d\n",records_searched);
match=1;
break;
}
}
else
{
if (debug>2) fprintf(stderr,"Skipping non-DID variable while searching for DID.\n");
}
h=hlrentrygetent(h);
}
}
/* For each match ... */
if (match)
{
if (debug>1) fprintf(stderr,"Returning HLR entry @ 0x%x\n",*ofs);
return 1;
}
/* Consider next record */
(*ofs)+=record_length;
if ((*ofs)>=hlr_size) return 0;
}
return 0;
}
int createHlr(char *did,char *sid) {
int i;
int record_offset=0;
/* Generate random SID */
for(i=0;i<64;i++) sid[i]=hexdigit[random()&0xf]; sid[64]=0;
if (debug>1) fprintf(stderr,"Creating new HLR entry with sid %s\n",sid);
/* Find first free byte of HLR */
findHlr(hlr,&record_offset,NULL,NULL);
if (record_offset>=hlr_size)
{
/* No space */
return setReason("No space in HLR for a new record");
}
else
{
/* We have found space, but is it enough? */
int bytes=hlr_size-record_offset;
if (bytes<1024) return setReason("<1KB space in HLR");
/* Write shiny fresh new record.
32bit - record length
32 bytes - SID
Total length = 4+32=36 bytes.
*/
if (stowSid(hlr,record_offset+4,sid)) return setReason("Could not store SID in new HLR entry");
/* Write length last of all to make entry valid */
hlr[record_offset]=0;
hlr[record_offset+1]=0;
hlr[record_offset+2]=0;
hlr[record_offset+3]=36;
/* Store the DID */
{
unsigned char packeddid[DID_MAXSIZE];
int pdidlen=0;
stowDid(packeddid,&pdidlen,did);
/* Work out reduced length of DID */
for(pdidlen=1;pdidlen<DID_MAXSIZE;pdidlen++) if (packeddid[pdidlen-1]==0xff) break;
hlrSetVariable(hlr,record_offset,VAR_DIDS,0x00,packeddid,pdidlen);
}
if (debug) fprintf(stderr,"Created new 36 byte HLR record for DID=[%s] @ 0x%x with SID=[%s]\n",
did,record_offset,sid);
if (debug>2) dump("after HLR create",&hlr[0],256);
return 0;
}
return setReason("Unreachable code turned out not to be");
}
struct hlrentry_handle hlr_handle;
int hlrGetRecordLength(unsigned char *hlr,int hofs)
{
int record_length;
record_length =hlr[hofs+3]<<0;
record_length|=hlr[hofs+2]<<8;
record_length|=hlr[hofs+1]<<16;
record_length|=hlr[hofs+0]<<24;
if (debug>2) fprintf(stderr,"HLR record @ 0x%x is %d bytes long.\n",hofs,record_length);
return record_length;
}
int hlrSetRecordLength(unsigned char *hlr,int hofs,int length)
{
hlr[hofs+3]=length&0xff;
hlr[hofs+2]=(length>>8)&0xff;
hlr[hofs+1]=(length>>16)&0xff;
hlr[hofs+0]=(length>>24)&0xff;
return 0;
}
/*
XXX We could return a fancy struct, and maybe we should.
But returning a long long and using the two 32bit halves is easy for now, and has a
certain efficiency to it. */
struct hlrentry_handle *openhlrentry(unsigned char *hlr,int hofs)
{
int record_length=hlrGetRecordLength(hlr,hofs);
/* If record has zero length, then open fails */
if (!record_length)
{
if (debug>2) fprintf(stderr,"HLR record is zero length -- aborting.\n");
return NULL;
}
bzero(&hlr_handle,sizeof(hlr_handle));
hlr_handle.record_length=record_length;
hlr_handle.hlr=hlr;
hlr_handle.hlr_offset=hofs;
hlr_handle.var_id=-1;
hlr_handle.var_instance=-1;
hlr_handle.value=NULL;
hlr_handle.value_len=-1;
hlr_handle.entry_offset=0;
/* Return offset of start of HLR entry and the offset of the first variable */
return hlrentrygetent(&hlr_handle);
}
struct hlrentry_handle *hlrentrygetent(struct hlrentry_handle *h)
{
if (!h) return NULL;
if (h->entry_offset==0)
{
/* First entry */
if (debug>2) fprintf(stderr,"Considering first entry of HLR record.\n");
h->entry_offset=HLR_RECORD_LEN_SIZE+SID_SIZE;
}
else
{
/* subsequent entry */
if (debug>2) fprintf(stderr,"Considering entry @ 0x%x\n",h->entry_offset);
h->entry_offset+=1+2+h->value_len+(h->var_id&0x80?1:0);
}
/* XXX Check if end of record */
if (h->entry_offset>=h->record_length) {
if (debug>2) fprintf(stderr,"Reached end of HLR record (%d>=%d).\n",h->entry_offset,h->record_length);
return NULL;
}
/* XXX Extract variable */
int ptr=h->hlr_offset+h->entry_offset;
if (debug>2) fprintf(stderr,"Extracting HLR variable @ 0x%x\n",ptr);
h->var_id=hlr[ptr];
h->value_len=(hlr[ptr+1]<<8)+hlr[ptr+2];
ptr+=3;
if (h->var_id&0x80) h->var_instance=hlr[ptr++];
h->value=&h->hlr[ptr];
return h;
}
int hlrGetVariable(unsigned char *hlr,int hofs,int varid,int varinstance,
unsigned char *value,int *len)
{
struct hlrentry_handle *h;
int hlr_offset=-1;
h=openhlrentry(hlr,hofs);
/* Find the place in the HLR record where this variable is */
while(h)
{
if ((h->var_id<varid)
||(h->var_id==varid&&h->var_instance<varinstance))
hlr_offset=h->entry_offset;
else
{
/* Value is here if anywhere */
if (h->var_id>varid||h->var_instance>varinstance)
return setReason("No such variable instance");
if (h->value_len>*len) return setReason("Value too long for buffer");
bcopy(h->value,value,h->value_len);
*len=h->value_len;
return 0;
break;
}
h=hlrentrygetent(h);
}
return setReason("No such variable instance");
}
int hlrSetVariable(unsigned char *hlr,int hofs,int varid,int varinstance,
unsigned char *value,int len)
{
/* hlr & hofs identify the start of a HLR entry. */
struct hlrentry_handle *h;
int hlr_offset=-1;
int hlr_size=hlrGetRecordLength(hlr,hofs);
if (debug) fprintf(stderr,"hlrSetVariable(varid=%02x, instance=%02x, len=%d)\n",
varid,varinstance,len);
h=openhlrentry(hlr,hofs);
/* Find the place in the HLR record where this variable should go */
while(h)
{
if (debug>1) fprintf(stderr,"h->var_id=%02x, h->h->var_instance=%02x, h->entry_offset=%x\n",
h->var_id,h->var_instance,h->entry_offset);
if ((h->var_id<varid)
||(h->var_id==varid&&h->var_instance<varinstance))
{
hlr_offset=h->entry_offset;
if (debug>1) fprintf(stderr,"Found variable instance prior: hlr_offset=%d.\n",hlr_offset);
}
else
{
/* Value goes here */
if (debug>1) fprintf(stderr,"Found variable instance to overwrite: hlr_offset=%d.\n",hlr_offset);
hlr_offset=h->entry_offset;
break;
}
h=hlrentrygetent(h);
}
/* XXX Race condition: If power is lost half way through here, then it is possible for
the record to be left in an inconsistent state */
if (h&&hlr_offset>-1)
{
if (debug>2) printf("hlr_offset=%d\n",hlr_offset);
if (h&&h->var_id==varid&&h->var_instance==varinstance)
{
/* Replace existing value */
if (debug) fprintf(stderr,"Replacing value in HLR\n");
int existing_size=1+2+(h->var_id&0x80?1:0)+h->value_len;
hlrMakeSpace(hlr,hofs,hlr_offset,1+2+len+(varid&0x80?1:0)-existing_size);
}
else
{
/* Insert value here */
if (debug) fprintf(stderr,"Inserting value in HLR\n");
hlrMakeSpace(hlr,hofs,hlr_offset,1+2+len+(varid&0x80?1:0));
}
}
else
{
/* HLR record has no entries, or this entry needs to go at the end,
so insert value at end of the record */
if (debug) fprintf(stderr,"Inserting value at end of HLR @ 0x%x\n",hlr_size);
hlrMakeSpace(hlr,hofs,hlr_size,1+2+len+(varid&0x80?1:0));
hlr_offset=hlr_size;
}
return hlrStowValue(hlr,hofs,hlr_offset,varid,varinstance,value,len);
}
int hlrStowValue(unsigned char *hlr,int hofs,int hlr_offset,
int varid,int varinstance,unsigned char *value,int len)
{
int ptr=hofs+hlr_offset;
hlr[ptr++]=varid;
hlr[ptr++]=(len>>8)&0xff;
hlr[ptr++]=len&0xff;
if (varid&0x80) hlr[ptr++]=varinstance;
bcopy(value,&hlr[ptr],len);
ptr+=len;
return 0;
}
int hlrMakeSpace(unsigned char *hlr,int hofs,int hlr_offset,int bytes)
{
/* Deal with easy case first */
if (!bytes) return 0;
/* Shift rest of HLR up/down.
If down, back-fill bytes with zeros. */
bcopy(&hlr[hofs+hlr_offset],&hlr[hofs+hlr_offset+bytes],
hlr_size-(hofs+hlr_offset+bytes));
if (bytes<0) bzero(&hlr[hlr_size-bytes],0-bytes);
/* Update record length */
int length=hlrGetRecordLength(hlr,hofs);
length+=bytes;
hlrSetRecordLength(hlr,hofs,length);
if (debug>1) fprintf(stderr,"hlrMakeSpace: HLR entry now %d bytes long.\n",length);
return 0;
}
int hlrDump(unsigned char *hlr,int hofs)
{
struct hlrentry_handle *h=openhlrentry(hlr,hofs);
fprintf(stderr,"Dumping HLR entry @ 0x%x\n",hofs);
while(h)
{
fprintf(stderr," var=%02x",h->var_id);
if (h->var_id&0x80) fprintf(stderr,"/%02x",h->var_instance);
fprintf(stderr," len=%d\n",h->value_len);
h=hlrentrygetent(h);
}
return 0;
}

1
meshpotato_build Normal file
View File

@ -0,0 +1 @@
staging_dir/toolchain-mips_gcc4.1.2/bin/mips-linux-gcc -Os -o dna package/dna/src/*.c -DHAVE_SYS_MMAN_H -DHAVE_STDINT_H -DHAVE_STDINT_H -DHAVE_SYS_STAT_H -DHAVE_POLL_H -DHAVE_ARPA_INET_H -DHAVE_BZERO

319
mphlr.h Normal file
View File

@ -0,0 +1,319 @@
/*
Serval Distributed Numbering Architecture (DNA)
Copyright (C) 2010 Paul Gardner-Stephen
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#include <string.h>
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#else
typedef unsigned int in_addr_t;
struct in_addr {
in_addr_t s_addr;
};
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <net/if.h>
#include <fcntl.h>
#include <getopt.h>
#include <ctype.h>
/* OpenWRT libc doesn't have bcopy, but has memmove */
#define bcopy(A,B,C) memmove(B,A,C)
#define BATCH 1
#define NONBATCH 0
#define REQ_SERIAL 0
#define REQ_PARALLEL -1
#define REQ_FIRSTREPLY -2
#define REQ_REPLY -101
#define SET_NOREPLACE 1
#define SET_REPLACE 2
#define SET_NOCREATE 3
#define SET_FRAGMENT 0x80
#define WITHDATA 1
#define WITHOUTDATA 0
/* Limit packet payloads to minimise packet loss of big packets in mesh networks */
#define MAX_DATA_BYTES 256
extern int debug;
extern int timeout;
extern int hlr_size;
extern unsigned char *hlr;
double simulatedBER;
extern int serverMode;
extern struct sockaddr recvaddr;
extern struct in_addr client_addr;
extern int client_port;
#define MAX_PEERS 1024
extern int peer_count;
extern in_addr_t peers[MAX_PEERS];
struct mphlr_variable {
unsigned char id;
char *name;
char *desc;
};
extern char *outputtemplate;
extern char *batman_socket;
/* HLR records can be upto 4GB, so 4x8bits are needed to encode the size */
#define HLR_RECORD_LEN_SIZE 4
/* Packet format:
16 bit - Magic value 0x4110
16 bit - Version number (0001 initially)
16 bit - Payload length
16 bit - Cipher method (0000 = clear text)
Ciphered payload follows:
(needs to have no predictable data to protect against known plain-text attacks)
64bit transaction id (random)
8bit - payload rotation (random, to help protect encryption from cribs)
Remainder of payload, after correcting for rotation:
33byte did|subscriber id
16byte salt
16byte hash of PIN+salt
Remainder of packet is interpretted as a series of operations
8 bit operation:
00 = get, 01 = set, 02 = delete, 03 = update,
80 = decline, 81 = okay (+optional result),
f0 = xfer HLR record
fe = random padding follows (to help protect cryptography from cribs)
ff = end of transaction
get - 8 bit variable value
*/
#define SID_SIZE 32
#define DID_MAXSIZE 32
#define SIDDIDFIELD_LEN (SID_SIZE+1)
#define PINFIELD_LEN 32
#define HEADERFIELDS_LEN (2+2+2+2+8+1)
#define OFS_TRANSIDFIELD (2+2+2+2)
#define TRANSID_SIZE 8
#define OFS_ROTATIONFIELD (OFS_TRANSIDFIELD+TRANSID_SIZE)
#define OFS_SIDDIDFIELD HEADERFIELDS_LEN
#define OFS_PINFIELD (OFS_SIDDIDFIELD+SIDDIDFIELD_LEN)
#define OFS_PAYLOAD (OFS_PINFIELD+16+16)
struct response {
int code;
unsigned char sid[32];
struct in_addr sender;
unsigned char *response;
int response_len;
int var_id;
int var_instance;
int value_len;
int value_offset;
int value_bytes;
struct response *next,*prev;
/* who sent it? */
unsigned short peer_id;
/* have we checked it to see if it allows us to stop requesting? */
unsigned char checked;
};
struct response_set {
struct response *responses;
struct response *last_response;
int response_count;
/* Bit mask of peers who have replied */
unsigned char *reply_bitmask;
};
struct hlrentry_handle {
int record_length;
unsigned char *hlr;
int hlr_offset;
int var_id;
int var_instance;
unsigned char *value;
int value_len;
int entry_offset;
};
/* Array of variables that can be placed in an MPHLR */
#define VAR_EOR 0x00
#define VAR_CREATETIME 0x01
#define VAR_CREATOR 0x02
#define VAR_REVISION 0x03
#define VAR_REVISOR 0x04
#define VAR_PIN 0x05
#define VAR_VOICESIG 0x08
#define VAR_HLRMASTER 0x0f
#define VAR_DIDS 0x80
#define VAR_LOCATIONS 0x81
#define VAR_IEMIS 0x82
#define VAR_TEMIS 0x83
#define VAR_CALLS_IN 0x90
#define VAR_CALLS_MISSED 0x91
#define VAR_CALLS_OUT 0x92
#define VAR_SMESSAGES 0xa0
#define VAR_DID2SUBSCRIBER 0xb0
#define VAR_HLRBACKUPS 0xf0
#define VAR_NOTE 0xff
extern struct mphlr_variable vars[];
#define ACTION_GET 0x00
#define ACTION_SET 0x01
#define ACTION_DEL 0x02
#define ACTION_INSERT 0x03
#define ACTION_CREATEHLR 0x0f
#define ACTION_DONE 0x7e
#define ACTION_ERROR 0x7f
#define ACTION_DECLINED 0x80
#define ACTION_OKAY 0x81
#define ACTION_DATA 0x82
#define ACTION_WROTE 0x83
#define ACTION_XFER 0xf0
#define ACTION_PAD 0xfe
#define ACTION_EOT 0xff
extern int hexdigit[16];
/* Make sure we have space to put bytes of the packet as we go along */
#define CHECK_PACKET_LEN(B) {if (((*packet_len)+(B))>=packet_maxlen) { setReason("Packet composition ran out of space."); return -1; } }
extern int sock;
int stowSid(unsigned char *packet,int ofs,char *sid);
int stowDid(unsigned char *packet,int *ofs,char *did);
int isFieldZeroP(unsigned char *packet,int start,int count);
void srandomdev();
int respondSimple(char *sid,int action,unsigned char *action_text,int action_len,
unsigned char *transaction_id);
int requestItem(char *did,char *sid,char *item,int instance,unsigned char *buffer,int buffer_length,int *len,
unsigned char *transaction_id);
int requestNewHLR(char *did,char *pin,char *sid);
int server(char *backing_file,int size,int foregroundMode);
int setReason(char *msg);
int hexvalue(unsigned char c);
int dump(char *name,unsigned char *addr,int len);
int packetOk(unsigned char *packet,int len,unsigned char *transaction_id);
int process_packet(unsigned char *packet,int len,struct sockaddr *sender,int sender_len);
int packetMakeHeader(unsigned char *packet,int packet_maxlen,int *packet_len,unsigned char *transaction_id);
int packetSetDid(unsigned char *packet,int packet_maxlen,int *packet_len,char *did);
int packetSetSid(unsigned char *packet,int packet_maxlen,int *packet_len,char *sid);
int packetFinalise(unsigned char *packet,int packet_maxlen,int *packet_len);
int packetAddHLRCreateRequest(unsigned char *packet,int packet_maxlen,int *packet_len);
int extractResponses(struct in_addr sender,unsigned char *buffer,int len,struct response_set *responses);
int packetAddVariableRequest(unsigned char *packet,int packet_maxlen,int *packet_len,
char *item,int instance,int start_offset,int max_offset);
int packetGetID(unsigned char *packet,int len,char *did,char *sid);
int getPeerList();
int sendToPeers(unsigned char *packet,int packet_len,int method,int peerId,struct response_set *responses);
int getReplyPackets(int method,int peer,int batchP,
struct response_set *responses,
unsigned char *transaction_id,int timeout);
int clearResponse(struct response **response);
int nextHlr(unsigned char *hlr,int *ofs);
int findHlr(unsigned char *hlr,int *ofs,char *sid,char *did);
int createHlr(char *did,char *sid);
struct hlrentry_handle *openhlrentry(unsigned char *hlr,int hofs);
struct hlrentry_handle *hlrentrygetent(struct hlrentry_handle *h);
int hlrStowValue(unsigned char *hlr,int hofs,int hlr_offset,
int varid,int varinstance,unsigned char *value,int len);
int hlrMakeSpace(unsigned char *hlr,int hofs,int hlr_offset,int bytes);
int packageVariableSegment(unsigned char *data,int *dlen,struct hlrentry_handle *h,
int offset,int buffer_size);
int packetDecipher(unsigned char *packet,int len,int cipher);
int safeZeroField(unsigned char *packet,int start,int count);
int unpackageVariableSegment(unsigned char *data,int dlen,int flags,struct response *r);
int extractSid(unsigned char *packet,int *ofs,char *sid);
int hlrSetVariable(unsigned char *hlr,int hofs,int varid,int varinstance,
unsigned char *value,int len);
int extractDid(unsigned char *packet,int *ofs,char *did);
char *hlrSid(unsigned char *hlr,int ofs);
int parseAssignment(unsigned char *text,int *var_id,unsigned char *value,int *value_len);
int writeItem(char *id,int var_id,int instance,unsigned char *value,int value_start,int value_len,int policy);
int packetAddVariableWrite(unsigned char *packet,int packet_maxlen,int *packet_len,
int itemId,int instance,unsigned char *value,int start_offset,int value_len,int flags);
int processRequest(unsigned char *packet,int len,struct sockaddr *sender,int sender_len,
unsigned char *transaction_id,char *did,char *sid);
int extractRequest(unsigned char *packet,int *packet_ofs,int packet_len,
int *itemId,int *instance,unsigned char *value,
int *start_offset,int *max_offset,int *flags);
int hlrGetVariable(unsigned char *hlr,int hofs,int varid,int varinstance,
unsigned char *value,int *len);
int packetSendRequest(int method,unsigned char *packet,int packet_len,int batchP,
unsigned char *transaction_id,struct response_set *responses);
int dumpResponses(struct response_set *responses);
int eraseLastResponse(struct response_set *responses);
int dropPacketP(int packet_len);
int clearResponses(struct response_set *responses);
int responseFromPeerP(struct response_set *responses,int peerId);
int responseFromPeer(struct response_set *responses,int peerId);
int additionalPeer(char *peer);
int getBatmanPeerList(char *socket_path,in_addr_t peers[],int *peer_count,int peer_max);
int hlrDump(unsigned char *hlr,int hofs);

31
openwrt-makefile Normal file
View File

@ -0,0 +1,31 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=dna
PKG_RELEASE:=1
PKG_VERSION:=1
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
include $(INCLUDE_DIR)/package.mk
define Package/dna
    SECTION:=utils
    CATEGORY:=Utilities
    TITLE:=dna
    DESCRIPTION:=Serval Distributed Numbering Architecture Reference Implementation
endef
define Build/Compile
    mkdir -p $(PKG_BUILD_DIR)
    $(CP) ./src/* $(PKG_BUILD_DIR)/
    $(TARGET_CC) $(PKG_BUILD_DIR)/*.c -o $(PKG_BUILD_DIR)/dna
    ## $(MAKE) CC=$(TARGET_CC) -C $(PKG_BUILD_DIR)
endef
define Package/dna/install
    $(INSTALL_DIR) $(1)/usr/bin
    $(INSTALL_BIN) $(PKG_BUILD_DIR)/dna $(1)/usr/bin
endef
$(eval $(call BuildPackage,dna))

518
packetformats.c Normal file
View File

@ -0,0 +1,518 @@
/*
Serval Distributed Numbering Architecture (DNA)
Copyright (C) 2010 Paul Gardner-Stephen
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "mphlr.h"
int process_packet(unsigned char *packet,int len,struct sockaddr *sender,int sender_len)
{
int authenticatedP=0;
char did[128];
char sid[128];
unsigned char *transaction_id=&packet[OFS_TRANSIDFIELD];
did[0]=0; sid[0]=0;
/* Get DID or SID */
if (packetGetID(packet,len,did,sid)) return setReason("Could not parse DID or SID");
/* Check for PIN */
if (!isFieldZeroP(packet,OFS_PINFIELD,16))
{
/* Authentication has been attempted.
If it is incorrect, then we need to return with ACTION_DECLINED
*/
if (debug>1) fprintf(stderr,"A PIN has been supplied.\n");
/* Can only authenticate by SID, not DID (since DIDs are ambiguous) */
if (packet[OFS_SIDDIDFIELD]!=1) return setReason("You can only authenticate against a SID");
/* XXX check authentication */
return setReason("Authentication not yet supported");
}
else
{
/* No attempt at authentication was made */
authenticatedP=0;
if (debug>1) fprintf(stderr,"No PIN was supplied.\n");
}
if (serverMode) return processRequest(packet,len,sender,sender_len,transaction_id,did,sid);
return 0;
}
int packetOk(unsigned char *packet,int len,unsigned char *transaction_id)
{
/* Make sure that the packet is meant for us, and is not mal-formed */
int version;
int cipher;
int length;
int payloadRotation;
if (len<HEADERFIELDS_LEN) return setReason("Packet is too short");
if (packet[0]!=0x41||packet[1]!=0x10) return setReason("Packet has incorrect magic value");
version=(packet[2]<<8)|packet[3];
length=(packet[4]<<8)|packet[5];
cipher=(packet[6]<<8)|packet[7];
if (version!=1) return setReason("Unknown packet format version");
if (cipher!=0) return setReason("Unknown packet cipher");
if (length!=len) return setReason("Packet length incorrect");
if (cipher) if (packetDecipher(packet,len,cipher)) return setReason("Could not decipher packet");
/* Make sure the transaction ID matches */
if (transaction_id)
{
int i;
for(i=0;i<8;i++) if (packet[OFS_TRANSIDFIELD+i]!=transaction_id[i]) return setReason("transaction ID mismatch");
}
/* Unrotate the payload */
payloadRotation=packet[OFS_ROTATIONFIELD];
{
unsigned char temp[256];
bcopy(&packet[len-payloadRotation],&temp[0],payloadRotation);
bcopy(&packet[HEADERFIELDS_LEN],&packet[HEADERFIELDS_LEN+payloadRotation],
len-(HEADERFIELDS_LEN)-payloadRotation);
bcopy(&temp[0],&packet[HEADERFIELDS_LEN],payloadRotation);
}
if (debug>1) fprintf(stderr,"Packet passes sanity checks and is ready for decoding.\n");
if (debug>2) dump("unrotated packet",packet,len);
return 0;
}
int packetMakeHeader(unsigned char *packet,int packet_maxlen,int *packet_len,
unsigned char *transaction_id)
{
int i;
CHECK_PACKET_LEN(OFS_PAYLOAD);
/* 0x4110 magic value */
packet[0]=0x41;
packet[1]=0x10;
/* encoding version */
packet[2]=0x00;
packet[3]=0x01;
/* Payload length (to be filled in later) */
packet[4]=0x00;
packet[5]=0x00;
/* Payload cipher (0x0000 = plain text) */
packet[6]=0x00;
packet[7]=0x00;
/* Add 64bit transaction id */
if (transaction_id)
/* Use supplied transaction ID */
for(i=0;i<8;i++) packet[OFS_TRANSIDFIELD+i]=transaction_id[i];
else
/* No transaction ID supplied, so create random transaction ID */
for(i=0;i<8;i++) packet[OFS_TRANSIDFIELD+i]=random()&0xff;
/* payload rotation (not yet applied) */
packet[14]=0x00;
*packet_len=HEADERFIELDS_LEN;
/* Clear did/subscriber ID, salt and hashed pin fields.
However, we cannot zero them, because that would provide significant knowable plain-text
for a known plain text attack.
Thus, instead we fill it with random date, but make the modulo sum of each field == 0x00
to indicate that no PIN has been provided. */
safeZeroField(packet,*packet_len,SIDDIDFIELD_LEN); *packet_len+=SIDDIDFIELD_LEN;
safeZeroField(packet,*packet_len,16); *packet_len+=16;
safeZeroField(packet,*packet_len,16); *packet_len+=16;
return 0;
}
int packetSetDid(unsigned char *packet,int packet_maxlen,int *packet_len,char *did)
{
/* Set the subject field to the supplied DID.
DIDs get encoded 4bits per digit (0-9,#,*,+,SPARE1,ESCAPE,END)
*/
int ofs=OFS_SIDDIDFIELD; /* where the DID/subscriber ID gets written */
/* Put DID (ie not SID) marker into packet */
packet[ofs++]=0x00;
return stowDid(packet,&ofs,did);
}
int packetSetSid(unsigned char *packet,int packet_maxlen,int *packet_len,char *sid)
{
/* Convert and store hex formatted sid */
int ofs=OFS_SIDDIDFIELD; /* where the DID/subscriber ID gets written */
if (strlen(sid)!=64) {
if (debug) fprintf(stderr,"Invalid SID: [%s] - should be 64 hex digits\n",sid);
return setReason("SID must consist of 64 hex digits");
}
packet[ofs++]=0x01; /* SID */
return stowSid(packet,ofs,sid);
}
int packetFinalise(unsigned char *packet,int packet_maxlen,int *packet_len)
{
/* Add any padding bytes and EOT to packet */
int paddingBytes=rand()&0xf;
if (paddingBytes)
{
CHECK_PACKET_LEN(2+paddingBytes);
packet[(*packet_len)++]=ACTION_PAD;
packet[(*packet_len)++]=paddingBytes;
while(paddingBytes--) packet[(*packet_len)++]=random()&0xff;
}
packet[(*packet_len)++]=ACTION_EOT;
/* Set payload length */
packet[4]=((*packet_len)>>8)&0xff;
packet[5]=((*packet_len)&0xff);
/* Work out by how much to rotate the packet payload.
The purpose of the rotation is to make it more difficult to
conduct a known-plaintext attack against any ciphers that we
may later support.
*/
int payloadRotation=(*packet_len)-HEADERFIELDS_LEN;
if (payloadRotation>0xff) payloadRotation=0xff;
payloadRotation=random()%payloadRotation;
if (debug>2)
fprintf(stderr,"Known Plaintext counter-measure: rotating packet payload by 0x%02x bytes.\n",
payloadRotation);
if (debug>2) dump("unrotated packet",packet,*packet_len);
/* Now rotate the payload */
{
unsigned char temp[256];
/*Copy first part of payload to a temporary buffer */
bcopy(&packet[HEADERFIELDS_LEN],&temp[0],payloadRotation);
/* Copy the main part of the payload left by the rotation factor */
bcopy(&packet[HEADERFIELDS_LEN+payloadRotation],&packet[HEADERFIELDS_LEN],
(*packet_len)-(HEADERFIELDS_LEN)-payloadRotation);
/* Copy the temporary buffer to the end of the packet to complete the rotation */
bcopy(&temp[0],&packet[(*packet_len)-payloadRotation],payloadRotation);
}
packet[OFS_ROTATIONFIELD]=payloadRotation;
if (debug>3) dump("rotated packet",packet,*packet_len);
return 0;
}
int packetAddHLRCreateRequest(unsigned char *packet,int packet_maxlen,int *packet_len)
{
int packet_len_in=*packet_len;
CHECK_PACKET_LEN(1);
packet[(*packet_len)++]=ACTION_CREATEHLR;
if (debug>2) dump("Variable request octets (HLR create)",&packet[packet_len_in],(*packet_len)-packet_len_in);
return 0;
}
int packetAddVariableRequest(unsigned char *packet,int packet_maxlen,int *packet_len,
char *item,int instance,int start_offset,int bytes)
{
/* Work out which item type we are asking for */
int itemId;
int packet_len_in=*packet_len;
for(itemId=0;vars[itemId].name;itemId++)
if (!strcmp(item,vars[itemId].name)) {
break;
}
/* Sanity check the request */
if (!vars[itemId].name) {
if (debug) fprintf(stderr,"`%s' is not a known HLR variable.\n",item);
return setReason("Requested unknown HLR variable");
}
itemId=vars[itemId].id;
if (instance<-1) return setReason("Asked for illegal variable value instance");
if (instance>0xfe) return setReason("Asked for illegal variable value instance");
if ((itemId<0x80)&&instance) return setReason("Asked for secondary value of single-value variable");
if (start_offset<0||start_offset>0xffff) return setReason("Asked for illegal variable value starting offset");
if (bytes<0||(start_offset+bytes)>0xffff) {
if (debug) fprintf(stderr,"Asked for %d bytes at offset %d\n",bytes,start_offset);
return setReason("Asked for illegal variable value ending offset");
}
/* Add request to the packet */
CHECK_PACKET_LEN(1+1+((itemId&0x80)?1:0)+2+2);
packet[(*packet_len)++]=ACTION_GET;
packet[(*packet_len)++]=itemId;
if (instance==-1) instance=0xff;
if (itemId&0x80) packet[(*packet_len)++]=instance;
packet[(*packet_len)++]=start_offset>>8;
packet[(*packet_len)++]=start_offset&0xff;
packet[(*packet_len)++]=bytes>>8;
packet[(*packet_len)++]=bytes&0xff;
if (debug>2) dump("Variable request octets (var)",&packet[packet_len_in],(*packet_len)-packet_len_in);
return 0;
}
int packetAddVariableWrite(unsigned char *packet,int packet_maxlen,
int *packet_len,
int itemId,int instance,unsigned char *value,
int start_offset,int value_len,int flags)
{
/* Work out which item type we are asking for */
int packet_len_in=*packet_len;
int max_offset=start_offset+value_len-1;
if (debug>1) printf("packetAddVariableWrite(start=%d,len=%d,flags=%d)\n",start_offset,value_len,flags);
/* Sanity check */
if (instance<0) return setReason("Asked for illegal variable value instance");
if (instance>0xfe) return setReason("Asked for illegal variable value instance");
if ((itemId<0x80)&&instance) return setReason("Asked for secondary value of single-value variable");
if (start_offset<0||start_offset>0xffff) return setReason("Asked for illegal variable value starting offset");
if (max_offset<0||max_offset>0xffff) return setReason("Asked for illegal variable value ending offset");
/* Add request to the packet */
CHECK_PACKET_LEN(1+1+((itemId&0x80)?1:0)+2+2+1);
packet[(*packet_len)++]=ACTION_SET;
packet[(*packet_len)++]=itemId;
if (itemId&0x80) packet[(*packet_len)++]=instance;
packet[(*packet_len)++]=start_offset>>8;
packet[(*packet_len)++]=start_offset&0xff;
packet[(*packet_len)++]=value_len>>8;
packet[(*packet_len)++]=value_len&0xff;
packet[(*packet_len)++]=flags;
if (debug>2) dump("Packet with var write header",&packet[0],*packet_len);
CHECK_PACKET_LEN(value_len);
bcopy(&value[0],&packet[*packet_len],value_len);
(*packet_len)+=value_len;
if (debug>2) dump("Variable request octets (write)",&packet[packet_len_in],(*packet_len)-packet_len_in);
return 0;
}
int extractRequest(unsigned char *packet,int *packet_ofs,int packet_len,
int *itemId,int *instance,unsigned char *value,
int *start_offset,int *bytes,int *flags)
{
if (*packet_ofs<0||(*packet_ofs)+6>=packet_len)
return setReason("mal-formed request packet (packet too short/bad offset)");
*itemId=packet[(*packet_ofs)++];
if ((*itemId)&0x80) *instance=packet[(*packet_ofs)++];
if (*instance==0xff) *instance=-1;
*start_offset=packet[(*packet_ofs)++]<<8;
*start_offset|=packet[(*packet_ofs)++];
*bytes=packet[(*packet_ofs)++]<<8;
*bytes|=packet[(*packet_ofs)++];
*flags=packet[(*packet_ofs)++];
if (debug>2) printf("Write flags = 0x%02x\n",*flags);
if (*packet_ofs<0||(*packet_ofs)+(*bytes)>=packet_len)
{
if (debug) fprintf(stderr,"Packet offset is %d, length is %d, and asked for %d bytes.\n",*packet_ofs,packet_len,*bytes);
return setReason("mal-formed request packet (too short for claimed data)");
}
bcopy(&packet[*packet_ofs],value,*bytes);
(*packet_ofs)+=*bytes;
return 0;
}
int extractResponses(struct in_addr sender,unsigned char *buffer,int len,struct response_set *responses)
{
int ofs=OFS_PAYLOAD;
while(ofs<len)
{
/* XXX should allocate responses from a temporary and bounded slab of memory */
struct response *r=calloc(sizeof(struct response),1);
if (!r) exit(setReason("calloc() failed."));
r->code=buffer[ofs];
r->sender=sender;
/* XXX doesn't make sure it is SID instead of DID */
bcopy(&buffer[HEADERFIELDS_LEN+1],r->sid,SID_SIZE);
switch(buffer[ofs])
{
case ACTION_EOT:
if (debug>1) fprintf(stderr,"Reached response packet EOT.\n");
case ACTION_DECLINED: case ACTION_OKAY:
case ACTION_CREATEHLR:
r->response_len=0; break;
case ACTION_GET:
/* Followed by variable # to fetch.
XXX If variable number >=0x80 then get instance information */
r->response_len=1; break;
case ACTION_ERROR:
r->response_len=buffer[++ofs];
break;
case ACTION_DATA:
/* Extract variable value */
unpackageVariableSegment(&buffer[ofs+1],len-ofs,WITHDATA,r);
break;
case ACTION_DONE:
r->value_offset=buffer[ofs+1];
r->response_len=1;
break;
case ACTION_PAD:
/* Skip padding bytes */
r->response_len=1+buffer[ofs+1];
break;
case ACTION_WROTE:
/* Extract info about the variable segment that was written.
This uses the same format as the request to write it, but without the data */
unpackageVariableSegment(&buffer[ofs+1],len-ofs,WITHOUTDATA,r);
r->response=NULL;
break;
case ACTION_SET:
case ACTION_DEL:
case ACTION_XFER:
default:
free(r);
if (debug>1) fprintf(stderr,"Encountered unimplemented response code 0x%02x @ 0x%x\n",buffer[ofs],ofs);
return setReason("Encountered unimplemented response type");
}
ofs++;
if (r->response_len) {
/* extract bytes of response */
unsigned char *rr;
if (r->response) rr=r->response; else rr=&buffer[ofs];
r->response=malloc(r->response_len+1);
if (!r->response) exit(setReason("malloc() failed."));
bcopy(&rr[0],r->response,r->response_len);
ofs+=r->response_len;
}
/* Work out peer ID */
r->sender=sender;
for(r->peer_id=0;r->peer_id<peer_count;r->peer_id++)
{
if (sender.s_addr==peers[r->peer_id]) break;
}
if (r->peer_id>peer_count) r->peer_id=-1;
/* Link new response into chain */
if (debug>2) printf("Linking response into response set.\n");
r->prev=responses->last_response;
if (responses->last_response)
responses->last_response->next=r;
else
responses->responses=r;
responses->last_response=r;
responses->response_count++;
responseFromPeer(responses,r->peer_id);
if (debug>2) dumpResponses(responses);
}
return 0;
}
int packageVariableSegment(unsigned char *data,int *dlen,struct hlrentry_handle *h,
int offset,int buffer_size)
{
int bytes;
int dlen_in=*dlen;
if ((buffer_size-(*dlen))<8) return setReason("Insufficient buffer space for packageVariableSegment()");
/* Figure out how many bytes we need to package */
bytes=buffer_size-(*dlen)-8;
if ((h->value_len-offset)<bytes) bytes=h->value_len-offset;
if (bytes<0) bytes=0;
if (debug>1) fprintf(stderr,"Packaging %d bytes of variable\n",bytes);
/* Describe variable */
/* Variable id and instance # (if required) */
data[(*dlen)++]=h->var_id;
if (h->var_id&0x80) data[(*dlen)++]=h->var_instance;
/* Variable length */
data[(*dlen)++]=h->value_len>>8;
data[(*dlen)++]=h->value_len&0xff;
/* Start offset in this segment */
data[(*dlen)++]=(offset>>8)&0xff;
data[(*dlen)++]=offset&0xff;
/* Number of bytes in this segment */
data[(*dlen)++]=(bytes>>8)&0xff;
data[(*dlen)++]=bytes&0xff;
if (debug>1) fprintf(stderr,"Packaging %d bytes\n",bytes);
/* Package the variable value itself (or part thereof) */
bcopy(&h->value[offset],&data[*dlen],bytes);
(*dlen)+=bytes;
if (debug>2) dump("Variable segment octets",&data[dlen_in],(*dlen)-dlen_in);
return 0;
}
int unpackageVariableSegment(unsigned char *data,int dlen,int flags,struct response *r)
{
r->response_len=0;
if (dlen<7) return setReason("unpackageVariableSegment() fed insufficient data");
r->var_id=data[r->response_len++];
if (r->var_id&0x80) r->var_instance=data[r->response_len++]; else r->var_instance=0;
if (r->var_instance==0xff) r->var_instance=-1;
r->value_len=data[r->response_len++]<<8;
r->value_len|=data[r->response_len++];
r->value_offset=data[r->response_len++]<<8;
r->value_offset|=data[r->response_len++];
r->value_bytes=data[r->response_len++]<<8;
r->value_bytes|=data[r->response_len++];
r->response=&data[r->response_len];
r->response_len+=r->value_bytes;
if (flags!=WITHOUTDATA)
if (r->response_len>dlen)
return setReason("unpackageVariableSegment() fed insufficient or corrupt data");
return 0;
}

126
peers.c Normal file
View File

@ -0,0 +1,126 @@
/*
Serval Distributed Numbering Architecture (DNA)
Copyright (C) 2010 Paul Gardner-Stephen
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "mphlr.h"
char *batman_socket=NULL;
int peer_count=0;
in_addr_t peers[MAX_PEERS];
unsigned char peer_replied[MAX_PEERS];
in_addr_t nominated_peers[256];
int nom_peer_count=0;
int additionalPeer(char *peer)
{
in_addr_t pa;
if (nom_peer_count>255) return setReason("Too many peers. You can only nominate 255 peers in this version.");
pa=inet_addr(peer);
if (pa==INADDR_NONE) return setReason("Invalid peer address specified.");
nominated_peers[nom_peer_count++]=pa;
return 0;
}
int getPeerList()
{
/* Generate the list of known peers.
If using BATMAN layer 3, this needs to be the list of exact IP addresses of the peers,
as we cannot reliably broadcast.
Once BATMAN Advanced is available, we will be able to do that.
In the mean time, we need to query BATMANd to find the known list of peers. This is not
quite as easy as we might wish.
Also, while using layer 3 routing we should keep note of which nodes have repied so that
we can not waste bandwidth by resending to them. For this purpose we maintain a set of
flags, peer_replied[], which is set to zero by us, and then set non-zero if that peer
solicits a reply, letting us know that we can suppress resends to that address.
Broadcasting to interfaces is a special problem for managing replies, as we should never mark those
peers as replied. We will do this by setting their peer_replied[] flag to 2 instead of zero.
*/
int i;
peer_count=0;
/* Add user specified peers */
for(i=0;i<nom_peer_count;i++) peers[peer_count++]=nominated_peers[i];
/* Add ourselves as a peer */
peers[peer_count]=inet_addr("127.0.0.1");
peer_replied[peer_count++]=0;
/* XXX Add broadcast address of every running interface */
/* XXX Query BATMANd for other peers */
if (batman_socket) getBatmanPeerList(batman_socket,peers,&peer_count,MAX_PEERS);
return 0;
}
int sendToPeers(unsigned char *packet,int packet_len,int method,int peerId,struct response_set *r)
{
/* The normal version of BATMAN works at layer 3, so we cannot simply use an ethernet broadcast
to get the message out. BATMAN Advanced might solve this, though.
So, in the mean time, we need to explicitly send the request to each peer.
If this is a re-send, we don't want to bother the peers who have already responded,
so check the peer_replied[] flags.
*/
int i;
int maxPeer=peer_count-1;
int n=0;
struct sockaddr_in peer_addr;
peer_addr.sin_family=AF_INET;
peer_addr.sin_port = htons(4110);
if (method==REQ_PARALLEL) i=0; else { i=peerId; maxPeer=i; }
for(;i<=maxPeer;i++)
if (!responseFromPeerP(r,i))
{
peer_addr.sin_addr.s_addr=peers[i];
if (debug>1) fprintf(stderr,"Sending packet to peer #%d\n",i);
int r=sendto(sock,packet,packet_len,0,(struct sockaddr *)&peer_addr,sizeof(peer_addr));
if (r<packet_len)
{
/* XXX something bad happened */
if (debug) fprintf(stderr,"Could not send to peer %s\n",inet_ntoa(peer_addr.sin_addr));
}
else
{
if (debug>1) fprintf(stderr,"Sent request to peer %s\n",inet_ntoa(peer_addr.sin_addr));
n++;
/* If sending to only one peer, return now */
if (method==i) break;
}
}
else
if (debug>1) fprintf(stderr,"Peer %s has already replied, so not sending again.\n",
inet_ntoa(peer_addr.sin_addr));
if (debug) fprintf(stderr,"Sent request to %d peers.\n",n);
return 0;
}

104
responses.c Normal file
View File

@ -0,0 +1,104 @@
/*
Serval Distributed Numbering Architecture (DNA)
Copyright (C) 2010 Paul Gardner-Stephen
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "mphlr.h"
int clearResponse(struct response **response)
{
while(*response)
{
struct response *r=*response;
*response=(*response)->next;
if (r->response) free(r->response);
r->response=NULL;
free(r);
}
return 0;
}
int eraseLastResponse(struct response_set *responses)
{
if (!responses) return -1;
if (responses->last_response)
{
struct response *newtail;
if (responses->last_response->prev) responses->last_response->prev->next=NULL;
newtail=responses->last_response->prev;
if (responses->responses==responses->last_response) responses->responses=NULL;
clearResponse(&responses->last_response);
responses->last_response=newtail;
responses->response_count--;
}
return 0;
}
int responseFromPeer(struct response_set *responses,int peerId)
{
if (peerId<0||peerId>peer_count) return -1;
if (!responses) return -1;
if (!responses->reply_bitmask)
{
responses->reply_bitmask=calloc(1,(peer_count>>3)+(peer_count&7)?1:0);
if (!responses->reply_bitmask) return -1;
}
int byte=peerId>>3;
int bit=peerId&7;
responses->reply_bitmask[byte]|=1<<bit;
return 0;
}
int responseFromPeerP(struct response_set *responses,int peerId)
{
if (!responses) return 0;
if (!responses->reply_bitmask) return 0;
if (peerId<0||peerId>peer_count) return 0;
int byte=peerId>>3;
int bit=peerId&7;
return responses->reply_bitmask[byte]&(1<<bit);
}
int clearResponses(struct response_set *responses)
{
struct response *r;
if (!responses) return -1;
r=responses->responses;
while(r)
{
struct response *rr=r;
r=r->next;
free(rr);
}
if (responses->reply_bitmask) free(responses->reply_bitmask);
responses->reply_bitmask=NULL;
responses->last_response=NULL;
responses->responses=NULL;
responses->response_count=0;
return 0;
}

382
server.c Normal file
View File

@ -0,0 +1,382 @@
/*
Serval Distributed Numbering Architecture (DNA)
Copyright (C) 2010 Paul Gardner-Stephen
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "mphlr.h"
unsigned char *hlr=NULL;
int hlr_size=0;
struct sockaddr recvaddr;
struct in_addr client_addr;
int client_port;
int server(char *backing_file,int size,int foregroundMode)
{
struct sockaddr_in bind_addr;
/* Get backing store */
if (!backing_file)
{
/* transitory storage of HLR data, so just malloc() the memory */
hlr=calloc(size,1);
if (!hlr) exit(setReason("Failed to calloc() HLR database."));
if (debug) fprintf(stderr,"Allocated %d byte temporary HLR store\n",size);
}
else
{
unsigned char zero[8192];
FILE *f=fopen(backing_file,"r+");
if (!f) f=fopen(backing_file,"w+");
if (!f) exit(setReason("Could not open backing file."));
bzero(&zero[0],8192);
fseek(f,0,SEEK_END);
errno=0;
while(ftell(f)<size)
{
int r;
fseek(f,0,SEEK_END);
if ((r=fwrite(zero,8192,1,f))!=1)
{
perror("fwrite");
exit(setReason("Could not enlarge backing file to requested size (short write)"));
}
fseek(f,0,SEEK_END);
}
if (errno) perror("fseek");
if (fwrite("",1,1,f)!=1)
{
fprintf(stderr,"Failed to set backing file size.\n");
perror("fwrite");
}
hlr=(unsigned char *)mmap(NULL,size,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_NORESERVE,fileno(f),0);
if (hlr==MAP_FAILED) {
perror("mmap");
exit(setReason("Memory mapping of HLR backing file failed."));
}
if (debug) fprintf(stderr,"Allocated %d byte HLR store backed by file `%s'\n",
size,backing_file);
}
hlr_size=size;
sock=socket(PF_INET,SOCK_DGRAM,0);
if (sock<0) {
fprintf(stderr,"Could not create UDP socket.\n");
perror("socket");
exit(-3);
}
bind_addr.sin_family = AF_INET;
bind_addr.sin_port = htons( 4110 );
bind_addr.sin_addr.s_addr = htonl( INADDR_ANY );
if(bind(sock,(struct sockaddr *)&bind_addr,sizeof(bind_addr))) {
fprintf(stderr,"MP HLR server could not bind to UDP port 4110\n");
perror("bind");
exit(-3);
}
/* Detach from the console */
if (!foregroundMode) daemon(0,0);
while(1) {
unsigned char buffer[16384];
socklen_t recvaddrlen=sizeof(recvaddr);
struct pollfd fds;
client_port=((struct sockaddr_in*)&recvaddr)->sin_port;
bzero((void *)&recvaddr,sizeof(recvaddr));
fds.fd=sock; fds.events=POLLIN;
/* Wait patiently for packets to arrive */
while (poll(&fds,1,1000)<1) sleep(0);
int len=recvfrom(sock,buffer,sizeof(buffer),0,&recvaddr,&recvaddrlen);
client_addr=((struct sockaddr_in*)&recvaddr)->sin_addr;
if (debug) fprintf(stderr,"Received packet from %s (len=%d).\n",inet_ntoa(client_addr),len);
if (debug>1) dump("recvaddr",(unsigned char *)&recvaddr,recvaddrlen);
if (debug>3) dump("packet",(unsigned char *)buffer,len);
if (dropPacketP(len)) {
if (debug) fprintf(stderr,"Simulation mode: Dropped packet due to simulated link parameters.\n");
continue;
}
if (!packetOk(buffer,len,NULL)) process_packet(buffer,len,&recvaddr,recvaddrlen);
else {
if (debug) setReason("Ignoring invalid packet");
}
if (debug>1) fprintf(stderr,"Finished processing packet, waiting for next one.\n");
}
}
int processRequest(unsigned char *packet,int len,
struct sockaddr *sender,int sender_len,
unsigned char *transaction_id,char *did,char *sid)
{
/* Find HLR entry by DID or SID, unless creating */
int ofs,rofs;
int records_searched=0;
int prev_pofs=0;
int pofs=OFS_PAYLOAD;
while(pofs<len)
{
if (debug>1) fprintf(stderr," processRequest: len=%d, pofs=%d, pofs_prev=%d\n",len,pofs,prev_pofs);
/* Avoid infinite loops */
if (pofs<=prev_pofs) break;
prev_pofs=pofs;
if (packet[pofs]==ACTION_CREATEHLR)
{
/* Creating an HLR requires an initial DID number and definately no SID -
you can't choose a SID. */
if (debug>1) fprintf(stderr,"Creating a new HLR record. did='%s', sid='%s'\n",did,sid);
if (!did[0]) return respondSimple(NULL,ACTION_DECLINED,NULL,0,transaction_id);
if (sid[0]) return respondSimple(sid,ACTION_DECLINED,NULL,0,transaction_id);
if (debug>1) fprintf(stderr,"Verified that create request supplies DID but not SID\n");
{
char sid[128];
/* make HLR with new random SID and initial DID */
if (!createHlr(did,sid))
return respondSimple(sid,ACTION_OKAY,NULL,0,transaction_id);
else
return respondSimple(NULL,ACTION_DECLINED,NULL,0,transaction_id);
}
pofs+=1;
pofs+=1+SID_SIZE;
}
else
{
switch(packet[pofs])
{
case ACTION_PAD: /* Skip padding */
pofs++;
pofs+=1+packet[pofs];
break;
case ACTION_EOT: /* EOT */
pofs=len;
break;
case ACTION_SET:
ofs=0;
if (debug>1) fprintf(stderr,"Looking for hlr entries with sid='%s' / did='%s'\n",sid,did);
while(findHlr(hlr,&ofs,sid,did))
{
if (debug>1) fprintf(stderr,"findHlr found a match for writing at 0x%x\n",ofs);
if (debug>2) hlrDump(hlr,ofs);
/* XXX consider taking action on this HLR
(check PIN first depending on the action requested) */
int itemId,instance,start_offset,bytes,flags;
unsigned char value[9000],oldvalue[65536];
int oldr,oldl;
/* XXX Doesn't verify PIN authentication */
/* Get write request */
if ((!sid)||(!sid[0])) {
setReason("You can only set variables by SID");
return
respondSimple(NULL,ACTION_ERROR,(unsigned char *)"SET requires authentication by SID",0,transaction_id);
}
pofs++; rofs=pofs;
if (extractRequest(packet,&pofs,len,
&itemId,&instance,value,
&start_offset,&bytes,&flags))
{
setReason("Could not extract ACTION_SET request");
return
respondSimple(NULL,ACTION_ERROR,(unsigned char *)"Mal-formed SET request",0,transaction_id);
}
/* Get the stored value */
oldl=65536;
oldr=hlrGetVariable(hlr,ofs,itemId,instance,oldvalue,&oldl);
if (oldr) {
if (flags==SET_NOREPLACE) {
oldl=0;
} else {
setReason("Tried to SET_NOCREATE/SET_REPLACE a non-existing value");
return
respondSimple(NULL,ACTION_ERROR,
(unsigned char *)"Cannot SET NOCREATE/REPLACE a value that does not exist",
0,transaction_id);
}
} else {
if (flags==SET_NOREPLACE) {
setReason("Tried to SET_NOREPLACE an existing value");
if (debug>1) dump("Existing value",oldvalue,oldl);
return
respondSimple(NULL,ACTION_ERROR,
(unsigned char *)"Cannot SET NOREPLACE; a value exists",
0,transaction_id);
}
}
/* Replace the changed portion of the stored value */
if ((start_offset+bytes)>oldl) {
bzero(&oldvalue[oldl],start_offset+bytes-oldl);
oldl=start_offset+bytes;
}
bcopy(&value[0],&oldvalue[start_offset],bytes);
/* Write new value back */
if (hlrSetVariable(hlr,ofs,itemId,instance,oldvalue,oldl))
{
setReason("Failed to write variable");
return
respondSimple(NULL,ACTION_ERROR,(unsigned char *)"Failed to SET variable",0,transaction_id);
}
if (debug>2) { fprintf(stderr,"HLR after writing:\n"); hlrDump(hlr,ofs); }
/* Reply that we wrote the fragment */
respondSimple(sid,ACTION_WROTE,&packet[rofs],6,
transaction_id);
/* Advance to next record and keep searching */
if (nextHlr(hlr,&ofs)) break;
}
break;
case ACTION_GET:
ofs=0;
if (debug>1) fprintf(stderr,"Looking for hlr entries with sid='%s' / did='%s'\n",sid,did);
while(findHlr(hlr,&ofs,sid,did))
{
if (debug>1) fprintf(stderr,"findHlr found a match at 0x%x\n",ofs);
if (debug>2) hlrDump(hlr,ofs);
/* XXX consider taking action on this HLR
(check PIN first depending on the action requested) */
/* Form a reply packet containing the requested data */
int var_id=packet[pofs+1];
int instance=packet[pofs+2];
int offset=(packet[pofs+3]<<8)+packet[pofs+4];
int sendDone=0;
struct hlrentry_handle *h;
if (instance==0xff) instance=-1;
if (debug>1) fprintf(stderr,"Responding to ACTION_GET (var_id=%02x, instance=%02x, pofs=0x%x, len=%d)\n",var_id,instance,pofs,len);
if (debug>2) dump("Request bytes",&packet[pofs],8);
/* Step through HLR to find any matching instances of the requested variable */
h=openhlrentry(hlr,ofs);
if (debug>1) fprintf(stderr,"openhlrentry(hlr,%d) returned %p\n",ofs,h);
while(h)
{
/* Is this the variable? */
if (debug>2) fprintf(stderr," considering var_id=%02x, instance=%02x\n",
h->var_id,h->var_instance);
if (h->var_id==var_id)
{
if (h->var_instance==instance||instance==-1)
{
/* Limit transfer size to MAX_DATA_BYTES, plus an allowance for variable packing. */
unsigned char data[MAX_DATA_BYTES+16];
int dlen=0;
if (debug>1) fprintf(stderr,"Sending matching variable value instance (instance #%d), value offset %d.\n",
h->var_instance,offset);
if (packageVariableSegment(data,&dlen,h,offset,MAX_DATA_BYTES+16))
return setReason("packageVariableSegment() failed.");
respondSimple(hlrSid(hlr,ofs),ACTION_DATA,data,dlen,transaction_id);
if (instance==-1) sendDone++;
}
else
if (debug>2) fprintf(stderr,"Ignoring variable instance %d (not %d)\n",
h->var_instance,instance);
}
else
if (debug>2) fprintf(stderr,"Ignoring variable ID %d (not %d)\n",
h->var_id,var_id);
h=hlrentrygetent(h);
}
if (sendDone)
{
unsigned char data[1];
data[0]=sendDone&0xff;
respondSimple(hlrSid(hlr,ofs),ACTION_DONE,data,1,transaction_id);
}
/* Advance to next record and keep searching */
if (nextHlr(hlr,&ofs)) break;
}
pofs+=7;
break;
default:
setReason("Asked to perform unsupported action");
if (debug) fprintf(stderr,"Packet offset = 0x%x\n",pofs);
if (debug) dump("Packet",packet,len);
return -1;
}
}
}
if (debug>1) fprintf(stderr,"Searched %d HLR entries.\n",records_searched);
return 0;
}
int respondSimple(char *sid,int action,unsigned char *action_text,int action_len,
unsigned char *transaction_id)
{
unsigned char packet[8000];
int pl=0;
int *packet_len=&pl;
int packet_maxlen=8000;
int i;
/* ACTION_ERROR is associated with an error message.
For syntactic simplicity, we do not require the respondSimple() call to provide
the length of the error message. */
if (action==ACTION_ERROR) {
action_len=strlen((char *)action_text);
/* Make sure the error text isn't too long.
IF it is, trim it, as we still need to communicate the error */
if (action_len>255) action_len=255;
}
/* Prepare the request packet */
if (packetMakeHeader(packet,8000,packet_len,transaction_id)) return -1;
if (sid&&sid[0])
{ if (packetSetSid(packet,8000,packet_len,sid))
return setReason("invalid SID in reply"); }
else
{ if (packetSetDid(packet,8000,packet_len,""))
return setReason("Could not set empty DID in reply"); }
CHECK_PACKET_LEN(1+1+action_len);
packet[(*packet_len)++]=action;
if (action==ACTION_ERROR) packet[(*packet_len)++]=action_len;
for(i=0;i<action_len;i++) packet[(*packet_len)++]=action_text[i];
if (debug>2) dump("Simple response octets",action_text,action_len);
if (packetFinalise(packet,8000,packet_len)) return -1;
if (debug) fprintf(stderr,"Sending response of %d bytes.\n",*packet_len);
if (packetSendRequest(REQ_REPLY,packet,*packet_len,NONBATCH,transaction_id,NULL)) return -1;
return 0;
}

41
simulate.c Normal file
View File

@ -0,0 +1,41 @@
/*
Serval Distributed Numbering Architecture (DNA)
Copyright (C) 2010 Paul Gardner-Stephen
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "mphlr.h"
double simulatedBER=0;
/*
We use this function to simulate a lossy link so that we can easily bench-test the
retransmission protocols.
*/
int dropPacketP(int packet_len)
{
int i,b;
long berThreshold=0x7fffffff*simulatedBER;
if (!simulatedBER) return 0;
for(i=0;i<packet_len;i++)
for(b=0;b<8;b++)
if (random()<=berThreshold) return 1;
return 0;
}

77
srandomdev.c Normal file
View File

@ -0,0 +1,77 @@
/*
Serval Distributed Numbering Architecture (DNA)
Copyright (C) 2010 Paul Gardner-Stephen
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/*-
* Copyright (c) 2006 Verdens Gang AS
* Copyright (c) 2006-2008 Linpro AS
* All rights reserved.
*
* Author: Dag-Erling SmÃ\u017ergrav <des@linpro.no>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id$
*/
#ifndef HAVE_SRANDOMDEV
#include <sys/time.h>
#include <fcntl.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
void
srandomdev(void)
{
struct timeval tv;
unsigned int seed;
int fd;
if ((fd = open("/dev/urandom", O_RDONLY)) >= 0) {
read(fd, &seed, sizeof seed);
close(fd);
} else {
gettimeofday(&tv, NULL);
/* NOTE: intentional use of uninitialized variable */
seed ^= (getpid() << 16) ^ tv.tv_sec ^ tv.tv_usec;
}
srandom(seed);
}
#endif

315
testdna Executable file
View File

@ -0,0 +1,315 @@
#!/bin/bash
# Stop any existing DNA server
pids=`ps -ef | grep "\./dna" | awk '{ print $2;}' | wc -l`
if [ $pids -gt 0 ]; then
kill `ps -ef | grep "\./dna" | awk '{ print $2;}'`
fi
# Start DNA server
if [ -e hlr.dat ]; then
rm hlr.dat
fi
./dna -n -vvv -S 1 -f hlr.dat > dnatest.log 2>&1 &
sleep 1
# Test creating a new subscriber record
# Test: Should return in <<3 sec, rather than time out
# Test: Should return OK:SID
/usr/bin/time -p ./dna -d 0427679796 -C > dnatest.tmp 2>&1
grep "^real" dnatest.tmp | awk '{ if ($2<0.5) print "OK: Create HLR was fast."; else print "FAIL: Create took too long."; }'
sid=`grep "^OK:" dnatest.tmp | cut -f2 -d:`
if [ "x$sid" = "x" ]; then
echo "FAIL: Create HLR failed (no OK:SID reply)"
echo -n "Got: "
cat dnatest.tmp
echo
echo "FATAL: Cannot continue"
kill %1
exit 3
else
echo "OK: Create HLR returned a SID"
fi
# Test getting short variables
/usr/bin/time -p ./dna -d 0427679796 -R dids > dnatest.tmp 2>&1
grep "^real" dnatest.tmp | awk '{ if ($2<2.9) printf("FAIL: Read variable didnt wait for all replies (only waited %f sec)\n",$2); else print "OK: Read variable waited for all replies."; }'
ok=`grep "^DIDS:${sid}:0:0427679796" dnatest.tmp | wc -l`
if [ $ok -lt 1 ]; then
echo "FAIL: Read variable didn't find existing value"
echo "FAIL: Search by DID may not work."
echo -n "Got: " ; cat dnatest.tmp | egrep -v "^real|^sys|^user"
else
echo "OK: Read variable correctly read an existing value"
if [ $ok -gt 1 ]; then
echo "FAIL: Compulsory retry policy does not filter duplicate results (got $ok copies)"
echo "FAIL: Search by DID has problems"
else
echo "OK: Compulsory retry policy filters duplicate results."
echo "OK: Search by DID works."
fi
fi
# Test setting short variables
/usr/bin/time -p ./dna -s $sid -i 0 -W note="a short literal value" > dnatest.tmp 2>&1
grep "^real" dnatest.tmp | awk '{ if ($2<0.5) print "OK: Set variable by SID was fast."; else print "FAIL: Set variable by SID was too slow."; }'
ok=`grep "^WROTE:$sid" dnatest.tmp | wc -l`
if [ $ok -ne 1 ]; then
echo "FAIL: Set new variable value failed."
echo -n "Got: " ; cat dnatest.tmp | egrep -v "^real|^sys|^user"
echo "Wanted: WROTE:$sid"
else
echo "OK: Set new variable value succeeded."
fi
# Test getting short variable by SID, and that setting variables really works.
/usr/bin/time -p ./dna -s $sid -i 0 -R note > dnatest.tmp 2>&1
grep "^real" dnatest.tmp | awk '{ if ($2<0.5) print "OK: Get variable by SID was fast."; else printf("FAIL: Get variable by SID was too slow (%s sec)\n",$2); }'
ok=`grep "^NOTE:${sid}:0:a short literal value" dnatest.tmp | wc -l`
if [ $ok -ne 1 ]; then
echo "FAIL: Set variable value could not be read back."
echo -n "Got: " ; cat dnatest.tmp | egrep -v "^real|^sys|^user"
echo "FAIL: Search by SID may not work."
else
echo "OK: Set variable value could be read back."
echo "OK: Search by SID works."
fi
# Test directing output to a file
echo "WARN: Output to file does not work with DID lists (known issue)"
if [ -e dnatest.dat ]; then
rm dnatest.dat
fi
/usr/bin/time -p ./dna -s $sid -O dnatest.dat -i 0 -R note > dnatest.tmp 2>&1
grep "^real" dnatest.tmp | awk '{ if ($2<0.5) print "OK: Get variable by SID was fast."; else printf("FAIL: Get variable by SID was too slow (%s sec)\n",$2); }'
ok=`grep "^NOTE:${sid}:0" dnatest.tmp | wc -l`
if [ $ok -ne 1 ]; then
echo "FAIL: Set variable value could not be read back without output going to file."
else
echo "OK: Set variable value could be read back with output going to file."
fi
ok=`grep "a short literal value" dnatest.dat | wc -l`
if [ $ok -ne 1 ]; then
echo "FAIL: Variable value could not be read from output file."
else
echo "OK: Variable values can be read from output files."
fi
rm dnatest.dat
# Test setting multiple instances of short variables
/usr/bin/time -p ./dna -s $sid -i 1 -W note='$414243' >dnatest.tmp 2>&1
ok=`grep "^WROTE:$sid" dnatest.tmp | wc -l`
if [ $ok -ne 1 ]; then
echo "FAIL: Set new variable value by instance failed."
echo -n "Got: " ; cat dnatest.tmp | egrep -v "^real|^sys|^user"
else
echo "OK: Set new variable value by instance succeeded."
fi
# Test reading multiple instances of a variable by SID
echo "NOTE:${sid}:0:a short literal value" >dnatest.txt
echo "NOTE:${sid}:1:ABC" >>dnatest.txt
echo "DONE:${sid}:2" >>dnatest.txt
./dna -s $sid -i -1 -R note > dnatest.tmp 2>&1
ok=`diff dnatest.tmp dnatest.txt | wc -l`
if [ $ok -ne 0 ]; then
echo "FAIL: Reading multiple variable instances has problems."
echo diff dnatest.tmp dnatest.txt
diff dnatest.tmp dnatest.txt
set ok=`grep ':ABC$' dnatest.tmp | wc -l`
if [ $ok != 1 ]; then
echo "FAIL: Specifying variable values by hex may have problems."
else
echo "OK: Specifying variable values by hex works."
fi
diff dnatest.tmp dnatest.txt
else
echo "OK: Reading multiple variable instances works."
echo "OK: Specifying variable values by hex works."
fi
if [ -e dnatest.tmp ]; then
rm dnatest.tmp
fi
/usr/bin/time -p ./dna -s $sid -R notes > dnatest.tmp 2>&1
grep "^real" dnatest.tmp | awk '{ if ($2<0.5) print "OK: Reading multiple variable instances by SID is fast."; else print "FAIL: Reading multiple variable instances by SID was too slow."; }'
# Try overwriting an existing variable value without update mode
/usr/bin/time -p ./dna -s $sid -i 0 -W note="replacement short literal value" > dnatest.tmp 2>&1
grep "^real" dnatest.tmp | awk '{ if ($2<0.5) print "OK: Set variable by SID was fast."; else print "FAIL: Set variable by SID was too slow."; }'
ok=`grep "^WROTE:$sid" dnatest.tmp | wc -l`
if [ $ok -eq 1 ]; then
echo "FAIL: Accidental overwrite variable value was allowed."
else
echo "OK: Accidental overwrite variable value prevented."
fi
# Try overwriting an existing variable value with update mode
/usr/bin/time -p ./dna -s $sid -i 0 -U note="replacement short literal value" > dnatest.tmp 2>&1
grep "^real" dnatest.tmp | awk '{ if ($2<0.5) print "OK: Set variable by SID was fast."; else print "FAIL: Set variable by SID was too slow."; }'
ok=`grep "^WROTE:$sid" dnatest.tmp | wc -l`
if [ $ok -ne 1 ]; then
echo "FAIL: Purposeful overwrite variable value failed."
else
echo "OK: Purposeful overwrite variable value works."
fi
# Try writing a long value that is too long to fit in one packet.
if [ -e dnatest.in ]; then
rm dnatest.in
fi
i=0
while [ $i -lt 100 ]; do
echo "${i}ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-${i}" >>dnatest.in
let i=i+1
done
/usr/bin/time -p ./dna -s $sid -i 0 -U note="@dnatest.in" > dnatest.tmp 2>&1
ok=`grep "^WROTE:$sid" dnatest.tmp | wc -l`
if [ $ok -ne 1 ]; then
echo "FAIL: Setting long variable values failed."
echo "FAIL: Setting variable value from a file failed."
else
echo "OK: Setting long variable value might work (assuming it can be read back)."
echo "OK: Setting variable value from a file work (assuming it can be read back)."
fi
if [ -e dnatest.out ]; then
rm dnatest.out
fi
/usr/bin/time -p ./dna -vvv -s $sid -O dnatest.out -i 0 -R note 2>&1 | tee dnatest.longread.log > dnatest.tmp 2>&1
ok=`grep "^NOTE:${sid}:0" dnatest.tmp | wc -l`
if [ $ok -ne 1 ]; then
echo "FAIL: Reading long variable values failed."
else
echo "OK: Reading long variable values might work."
fi
ok=`diff dnatest.in dnatest.out | wc -l`
if [ $ok -ne 0 ]; then
echo "FAIL: Long variable value could not be read via multi-packet transactions."
else
echo "OK: Long variable values can be read via multi-packet transactions."
fi
ls -l dnatest.out dnatest.in
rm dnatest.out dnatest.in
#
# Run tests that exercise the bit error simulation
#
# Kill old server
kill %1
wait
# Start DNA server with a BER of 10E-3
rm hlr.dat
./dna -n -vvv -B 0.001 -S 1 -f hlr.dat >> dnatest.log 2>&1 &
sleep 1
# Test creating a new subscriber record
# Test: should run in variable time, but never longer than it needs
# Test: Should return OK:SID
iterations=10
i=0
fails=0
totaltime=0
maxtime=0
while [ $i -lt $iterations ]; do
/usr/bin/time -p ./dna -B 0.001 -d 0427679796 -C > dnatest.tmp 2>&1
thistime=`grep "^real" dnatest.tmp | awk '{ printf("%d\n",$2*1000/'$iterations');}'`
let totaltime=totaltime+thistime
if [ $thistime -gt $maxtime ]; then
maxtime=$thistime
fi
sid=`grep "^OK:" dnatest.tmp | cut -f2 -d:`
if [ "x$sid" = "x" ]; then
let fails=fails+1
else
echo " OK after ${thistime}0 ms"
fi
let i=i+1
done
if [ $totaltime -lt 25 ]; then
echo "FAIL: Packet loss injection may have problems (no packet loss injected)"
else
echo "OK: Packet loss injection (test mode) works."
fi
if [ $fails -gt 4 ]; then
echo "FAIL: Create HLR too unreliable when faced with packet loss ($fails out of $iterations failed)"
else
echo "OK: Create HLR works in the face of intense packet loss (simulated BER = 10E-3), avg time = $totaltime ms"
fi
if [ $totaltime -gt 3000 -o $maxtime -gt 330 ]; then
echo "FAIL: Timeout routinely or grossly exceeded when employing retries in the face of intense packet loss"
else
echo "OK: Timeout behaves correctly in the face of packet loss"
fi
# Try writing a long value that is too long to fit in one packet.
# Kill old server
kill %1
wait
# Start DNA server with a BER of 10E-4
ber=0.00001
rm hlr.dat
./dna -n -vvv -B $ber -S 1 -f hlr.dat >> dnatest.log 2>&1 &
sleep 1
if [ -e dnatest.in ]; then
rm dnatest.in
fi
i=0
while [ $i -lt 100 ]; do
echo "${i}ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-${i}" >>dnatest.in
let i=i+1
done
/usr/bin/time -p ./dna -B $ber -s $sid -i 0 -U note="@dnatest.in" > dnatest.tmp 2>&1
ok=`grep "^WROTE:$sid" dnatest.tmp | wc -l`
if [ $ok -ne 1 ]; then
echo "FAIL: Setting long variable values failed in the face of packet loss"
echo "FAIL: Setting variable value from a file failed in the face of packet loss."
else
echo "OK: Setting long variable value might work with packet loss (assuming it can be read back)."
echo "OK: Setting variable value from a file work with packet loss (assuming it can be read back)."
fi
if [ -e dnatest.out ]; then
rm dnatest.out
fi
/usr/bin/time -p ./dna -B $ber -s $sid -O dnatest.out -i 0 -R note > dnatest.tmp 2>&1
ok=`grep "^NOTE:${sid}:0" dnatest.tmp | wc -l`
if [ $ok -ne 1 ]; then
echo "FAIL: Reading long variable values failed when faced with packet loss (either READ or WRITE cycle failed)."
else
echo "OK: Reading long variable values might work when faced with packet loss."
fi
if [ ! -e dnatest.out ]; then
echo "FAIL: Long variable value could not be read via multi-packet transactions when faced with packet loss."
rm dnatest.in
else
ok=`diff dnatest.in dnatest.out | wc -l`
if [ $ok -ne 0 ]; then
echo "FAIL: Long variable value could not be read via multi-packet transactions when faced with packet loss."
else
echo "OK: Long variable values can be read via multi-packet transactions when faced with packet loss."
fi
ls -l dnatest.out dnatest.in
rm dnatest.out dnatest.in
fi
# Kill server
kill %1
rm dnatest.tmp
echo "FAIL: No test for parallel mode with multiple peers"
echo "FAIL: No test for fast parallel return if all peers reply"
echo "FAIL: No test for slow parallel return if not all peers reply"
echo "FAIL: No test for serial request with many peers such that the timeout is insufficient to query them all"
echo "FAIL: No test for user specified peer list"
echo "FAIL: No test for BATMAN integration."
echo "FAIL: No test for writing output to files with templated file names."