From fb7779747882c719ebb09bda4839618afa17e535 Mon Sep 17 00:00:00 2001 From: Stefan `Sec` Zehl Date: Fri, 18 May 2012 13:15:03 +0200 Subject: [PATCH 01/16] Move all the reader stuff to a new subdir --- tools/{mesh => reader}/beacon-udp.pl | 14 +++-- tools/reader/listen-log.pl | 32 +++++++++++ tools/reader/netlink-notifier.pl | 79 +++++++++++++++++++++++++++ tools/reader/reader-watchdog.pl | 82 ++++++++++++++++++++++++++++ 4 files changed, 201 insertions(+), 6 deletions(-) rename tools/{mesh => reader}/beacon-udp.pl (96%) create mode 100755 tools/reader/listen-log.pl create mode 100644 tools/reader/netlink-notifier.pl create mode 100755 tools/reader/reader-watchdog.pl diff --git a/tools/mesh/beacon-udp.pl b/tools/reader/beacon-udp.pl similarity index 96% rename from tools/mesh/beacon-udp.pl rename to tools/reader/beacon-udp.pl index c54e869..57bd0f0 100755 --- a/tools/mesh/beacon-udp.pl +++ b/tools/reader/beacon-udp.pl @@ -10,6 +10,7 @@ use Digest::CRC qw(crcccitt); use FindBin; use lib "$FindBin::Bin/lib"; +use lib "$FindBin::Bin/../mesh/lib"; use r0ket; $|=1; @@ -162,14 +163,15 @@ while(1){ if(length($pkt) != 16){ # Sanity check $errors++; + print STDERR "Length check\n"; next; }; $ctr++; my $idoff=0; - if(substr($pkt,12,1) eq "\xee"){ - $idoff=1000; - }; +# if(substr($pkt,12,1) eq "\xee"){ +# $idoff=1000; +# }; my $hdr= pack("CCnnNN", 1, # proto (BEACONLOG_SIGHTING) @@ -198,8 +200,8 @@ while(1){ }else{ $typeunknown++; }; - if($idoff){ - $typeunknown++; - }; +# if($idoff){ +# $typeunknown++; +# }; }; r0ket::rest(); diff --git a/tools/reader/listen-log.pl b/tools/reader/listen-log.pl new file mode 100755 index 0000000..5176554 --- /dev/null +++ b/tools/reader/listen-log.pl @@ -0,0 +1,32 @@ +#!/usr/bin/perl +# +# vim:set ts=4 sw=4: + +use strict; +use warnings; +use Socket; + +our $port=514; +my $hispaddr; + +my $socket; + +my($iaddr,$proto,$paddr); +$iaddr = pack('C4', 0,0,0,0); +$proto = getprotobyname('udp'); +$paddr = sockaddr_in($port, $iaddr); # 0 means let kernel pick + +socket($socket, PF_INET, SOCK_DGRAM, $proto) || die "socket: $!"; +bind($socket, $paddr) || die "bind: $!"; + +my ($hisiaddr,$host); +my $buf; +while (1){ + $hispaddr = recv($socket, $buf, 2048, 0) || die "recv: $!"; + ($port, $hisiaddr) = sockaddr_in($hispaddr); +# $host = gethostbyaddr($hisiaddr, AF_INET); + $host=join(".",unpack("CCCC",$hisiaddr)); + $buf =~ y!a-zA-Z0-9.:,; _()[]{}?-!!cd; + print substr(scalar(localtime),11,8)," ",$host," ",$buf,"\n"; +}; + diff --git a/tools/reader/netlink-notifier.pl b/tools/reader/netlink-notifier.pl new file mode 100644 index 0000000..61fe08b --- /dev/null +++ b/tools/reader/netlink-notifier.pl @@ -0,0 +1,79 @@ +#!/usr/bin/perl +# +# vim:set ts=4 sw=4: + +use strict; +use POSIX; +use Socket; + +use FindBin; +use lib "$FindBin::Bin/lib"; +use l0gger; + +l0gger::init(); + + +use constant AF_NETLINK => 16; +#use Socket::Netlink; + +# Netlink setup +socket my $sock,AF_NETLINK,SOCK_RAW,0 #(domain, type, bus//protocol) + or die "socket: $!\n"; +bind $sock, pack("vvVV",AF_NETLINK,0,0,1) #(domain, zero , pid, group); + or die "bind: $!\n"; + +while(1){ + sysread($sock,my $msg, 65535) or die "recv: $!"; + my ($ifname,$state)=(undef,undef); + +# print unpack("H*",substr($message,0,16)),"\n"; # ifi_type etc. + $msg=substr($msg,16); + + my (undef,undef,undef,undef,undef,$type)=unpack("nnnnCC",$msg); + $msg=substr($msg,16); +# print "Type=$type\n"; + next if($type!=16); # RTM_NEWLINK + + while(length($msg)){ + my($len,$type)=unpack("vv",$msg); +# print "len= $len,type=$type\n"; + last if($len<4); + + $msg=substr($msg,4); + if ($type == 3){ # IFLA_IFNAME + $ifname=unpack("Z*",$msg); + }elsif($type == 16){ # IFLA_OPERSTATE + $state=unpack("v",$msg); + }else{ +# print "content=",unpack("H*",substr($msg,0,$len-4)),"\n"; + }; + + $len=(int(($len-1)/4))*4; # 4-byte alignment + $msg=substr($msg,$len); + }; + next if(!defined($ifname) || !defined($state)); +# print "ifname=$ifname, state=$state\n"; +# IF_OPER_UNKNOWN (0): +# Interface is in unknown state, neither driver nor userspace has set +# operational state. Interface must be considered for user data as +# setting operational state has not been implemented in every driver. +# IF_OPER_NOTPRESENT (1): +# Unused in current kernel (notpresent interfaces normally disappear), +# just a numerical placeholder. +# IF_OPER_DOWN (2): +# Interface is unable to transfer data on L1, f.e. ethernet is not +# plugged or interface is ADMIN down. +# IF_OPER_LOWERLAYERDOWN (3): +# Interfaces stacked on an interface that is IF_OPER_DOWN show this +# state (f.e. VLAN). +# IF_OPER_TESTING (4): +# Unused in current kernel. +# IF_OPER_DORMANT (5): +# Interface is L1 up, but waiting for an external event, f.e. for a +# protocol to establish. (802.1X) +# IF_OPER_UP (6): +# Interface is operational up and can be used. + if($state==6){ + l0gger::send("$ifname up"); + }; +} diff --git a/tools/reader/reader-watchdog.pl b/tools/reader/reader-watchdog.pl new file mode 100755 index 0000000..2f2c41a --- /dev/null +++ b/tools/reader/reader-watchdog.pl @@ -0,0 +1,82 @@ +#!/usr/bin/perl + +use strict; +use POSIX ":sys_wait_h"; # for nonblocking read + +use FindBin; +use lib "$FindBin::Bin/lib"; +use lib "$FindBin::Bin/../mesh/lib"; +use r0ket; +use l0gger; + +$|=1; + +my $DEV="/dev"; + +my %run; +my %childs; +my %config; + +my @exec; + +$SIG{CHLD} = sub { + # don't change $! and $? outside handler + local ($!, $?); + my $pid = waitpid(-1, WNOHANG); + return if $pid == -1; + return unless defined $childs{$pid}; + delete $run{$childs{$pid}}; # cleanup? + delete $childs{$pid}; +}; + +# read config + +sub readcfg{ + %config=(); + open(R,"<","reader.cf")|| die "Can't find reader.cf\n"; + while(){ + chomp; + next unless /(.*?)=(.*)/; + $config{$1}=$2; + }; + close(R); + @exec=$config{"prog"},split(/\s+/,$config{"args"}); + delete $config{prog}; + delete $config{args}; +}; + +readcfg(); +l0gger::init(); + +while(1){ + opendir(my $dh, $DEV); + my @paths=grep {/^ttyACM/} readdir($dh); + close $dh; +# print "f: ",join(",",@files),"\n"; + for my $path (@paths){ + next if $run{$path}; + + my $id = eval { + r0ket::r0ket_init($DEV."/".$path); + return r0ket::get_id(); + }; +# print "r0id: $id\n"; + if(!defined $config{$id}){ + print "No config for r0ket $id @ $path, skipping...\n"; + next; + }; + $run{$path}=$id; + + my $pid = fork(); + die "cannot fork" unless defined $pid; + if ($pid == 0) { + exec @exec,'-d',$DEV."/".$path,'-i',$config{$id}; + } else { + print "Started $path : $id @ $pid\n"; + l0gger::send("started $path : $id @ $pid"); + $childs{$pid}=$path; + } + }; + sleep(1); + print join(",",map {$run{$childs{$_}}."@".$childs{$_}."[$_]"} sort {$childs{$a}cmp$childs{$b}} keys %childs),"\n"; +}; From 59bd1bae5ffee775a5b23c539a5d9a07945fdfa2 Mon Sep 17 00:00:00 2001 From: Stefan `Sec` Zehl Date: Sat, 19 May 2012 02:37:48 +0200 Subject: [PATCH 02/16] Support ser2net readers --- tools/mesh/lib/r0ket.pm | 43 ++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/tools/mesh/lib/r0ket.pm b/tools/mesh/lib/r0ket.pm index 013630f..33ea8c5 100755 --- a/tools/mesh/lib/r0ket.pm +++ b/tools/mesh/lib/r0ket.pm @@ -4,11 +4,12 @@ use strict; -use IO::Select; package r0ket; +use IO::Select; +use Socket; use Digest::CRC qw(crcccitt); -use POSIX qw(strftime VTIME VMIN TCSANOW); +use POSIX qw(strftime :termios_h); use Time::HiRes; our $verbose=0; @@ -98,14 +99,16 @@ sub writebeacon{ ### Packet mgmt -our $buffer; +my $buffer; our $firstpkt=1; sub get_data{ my $filter=shift||0; my $rin=''; # Select vector + my $ein=''; # Select vector my ($rout,$eout); vec($rin,fileno($bridge),1) = 1; + vec($ein,fileno($bridge),1) = 1; while(1){ @@ -118,6 +121,7 @@ sub get_data{ }elsif($filter==$type){ return $str; }; + print "got a 2: ",length($str)," $str \n" if ($type==2); next; # If rejected, look for next packet. }; @@ -132,9 +136,8 @@ sub get_data{ redo; # Try parsing the rest. }; }; - my ($nfound,$timeleft) = - select($rout=$rin, undef, $eout=$rin, 1); + select($rout=$rin, undef, $eout=$ein, 1); if($nfound==0){ if($filter==0){ return (0,''); @@ -142,17 +145,18 @@ sub get_data{ print STDERR "No packets for 1 second...\n"; }; }; - if($eout eq $rin){ + if($eout eq $ein){ # Doesn't get triggered? die "Error on bridge socket: $!\n"; }; if($rout eq $rin){ - my $rr; + my $rr=""; sysread($bridge,$rr,1024); # print "len=",length($rr),"\n"; $buffer.=$rr; + die "Nothing to read?" if(length($rr)==0); # Probably device gone. +# print "recv: ",unpack("H*",$rr),"\n"; }; -# print "recv: ",unpack("H*",$rr),"\n"; }; }; @@ -330,6 +334,25 @@ sub r0ket_init{ $ser=$ENV{R0KETBRIDGE} }; }; +if($ser =~ /:/){ + my ($remote, $port, $iaddr, $paddr, $proto, $line); + + $ser =~ /(.*):(.*)/; + $remote = $1; + $port = $2; + $iaddr = inet_aton($remote) || die "no host: $remote"; + $paddr = sockaddr_in($port, $iaddr); + + $proto = getprotobyname("tcp"); + use Fcntl; + socket($bridge, PF_INET, SOCK_STREAM, $proto) || die "socket: $!"; + connect($bridge, $paddr) || die "connect: $!"; + + my $old_flags = fcntl($bridge, F_GETFL, 0) + or die "can't get flags: $!"; + fcntl($bridge, F_SETFL, $old_flags | O_NONBLOCK) + or die "can't set non blocking: $!"; +}else{ if(!defined $ser){ do {$ser=$_ if ( -e $_ ) } for qw(/dev/ttyACM0); }; @@ -343,7 +366,9 @@ sub r0ket_init{ $term->getattr(fileno($bridge)); $term->setcc(VTIME,1); $term->setcc(VMIN,0); + $term->setcc(ECHO,0); $term->setattr(fileno($bridge),TCSANOW); +}; #empty buffer, in case there is old data my $dummy; @@ -389,7 +414,7 @@ sub set_rxlen { }; sub get_id { send_pkt_num("",7); - my $id=get_data(7); + my $id=unpack("H*",get_data(7)); wait_ok(1); return $id; }; From 596b41bbf63252c43f2d6c46ec423005477be76e Mon Sep 17 00:00:00 2001 From: schneider Date: Sat, 19 May 2012 11:30:13 +0200 Subject: [PATCH 03/16] updated fahrplan for sigint --- firmware/l0dable/files/fahrplan.scd | Bin 45348 -> 84052 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/firmware/l0dable/files/fahrplan.scd b/firmware/l0dable/files/fahrplan.scd index 287c261fa5b794c33cc76f8967cf524b67eba069..ef5a97fd715daa2484563adcc9ccdc116f2eb34a 100644 GIT binary patch literal 84052 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 rA6mHshfNGN${k4ZVjNVq4zCJZz%a^+U;88N{K Date: Sat, 19 May 2012 12:44:38 +0200 Subject: [PATCH 04/16] Blink bridge LEDs on recv/send for debugging --- firmware/applications/bridge.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/firmware/applications/bridge.c b/firmware/applications/bridge.c index c3b1b98..db1b3e1 100644 --- a/firmware/applications/bridge.c +++ b/firmware/applications/bridge.c @@ -60,6 +60,8 @@ void main_bridge(void) GLOBAL(lcdbacklight)=10; GLOBAL(privacy) = 3; char input[64]; + char led1=0; + char led2=0; usbCDCInit(); delayms(500); @@ -71,6 +73,7 @@ void main_bridge(void) int l, i, status; CDC_OutBufAvailChar (&l); if(l>0){ + gpioSetValue (RB_LED0, led1);led1=1-led1; CDC_RdOutBuf (input, &l); for(i=0; i 0 ){ + gpioSetValue (RB_LED2, led2);led2=1-led2; puts("\\1"); dump_encoded(len, buf); puts("\\0"); From a2ac6761348756383d24143d0ec5df951637214b Mon Sep 17 00:00:00 2001 From: Stefan `Sec` Zehl Date: Sat, 19 May 2012 18:20:53 +0200 Subject: [PATCH 05/16] Allow multiple commands separated by , in mesh/rf --- tools/mesh/rf | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tools/mesh/rf b/tools/mesh/rf index e0695d9..4d78ca3 100755 --- a/tools/mesh/rf +++ b/tools/mesh/rf @@ -73,6 +73,17 @@ $r0ket::verbose=1; my @fh; my $read; +my @args=@ARGV; +my $sidx=0; +for my $eidx (0..$#args){ + if($args[$eidx] eq ","){ + dwim(@args[$sidx..$eidx-1]); + $sidx=$eidx+1; + } +}; +dwim(@args[$sidx..$#args]); + +sub dwim{ my $cmd=shift; if($cmd =~ /^r/){ @@ -271,6 +282,7 @@ if($cmd =~ /^r/){ }else{ die "Option not understood\n"; }; +}; #if (@fh = $sel->can_read(10)) { # sysread($fh[0],$read,1024); From 99c7d32aa78694885de597473793777b10b1c47f Mon Sep 17 00:00:00 2001 From: Stefan `Sec` Zehl Date: Wed, 30 May 2012 02:27:47 +0200 Subject: [PATCH 06/16] first draft of an openbeacon reader written in C --- tools/reader/obreader.c | 279 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 tools/reader/obreader.c diff --git a/tools/reader/obreader.c b/tools/reader/obreader.c new file mode 100644 index 0000000..1749dd0 --- /dev/null +++ b/tools/reader/obreader.c @@ -0,0 +1,279 @@ +/* obreader.c by Sec + * vim:set cin sm ts=4: + * + * Do whatever you want with this code, but give credit + * + * This program reads packets from an USB-Serial R0ket + * and sends them off via TCP/UDP to a central host + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUFSIZE 100 +#define PORT 2342 +#define SRV_IP "127.0.0.1" + +/* rotating buffer */ +#define ELEMSIZE 32 +unsigned int bufstart=0,bufend=0; /* rotating buffer */ +#define BUFIDX(a) (a*ELEMSIZE) +#define BUFPUSH() do{ bufend++; if(bufend==BUFSIZE){ bufend=0; }; if(bufend==bufstart){ BUFPOP();}; }while(0) +#define BUFPOP() do{ bufstart++; if(bufstart==BUFSIZE){ bufstart=0; }; }while(0) +unsigned char buffer[BUFIDX(BUFSIZE)]; + +static u_int16_t +crc16 (const unsigned char *buffer, int size) +{ + u_int16_t crc = 0xFFFF; + + if (buffer && size) + while (size--) + { + crc = (crc >> 8) | (crc << 8); + crc ^= *buffer++; + crc ^= ((unsigned char) crc) >> 4; + crc ^= crc << 12; + crc ^= (crc & 0xFF) << 5; + } + + return crc; +} + +void setnonblocking(int fd) { + int opts; + + opts = fcntl(fd,F_GETFL); + if (opts < 0) { + perror("fcntl(F_GETFL)"); + exit(EXIT_FAILURE); + } + opts = (opts | O_NONBLOCK); + if (fcntl(fd,F_SETFL,opts) < 0) { + perror("fcntl(F_SETFL)"); + exit(EXIT_FAILURE); + } + return; +} + +/* Reference is https://r0ket.badge.events.ccc.de/tracking:reader */ +void pkt_cleanup(int idx){ + static u_int32_t ctr; + time_t t; + time(&t); + + buffer[BUFIDX(idx)+2]=1; // BEACONLOG_SIGHTING + buffer[BUFIDX(idx)+3]=0; // interface 0 + *(u_int16_t*)(buffer+BUFIDX(idx)+4)=htons(1234); // reader id + *(u_int16_t*)(buffer+BUFIDX(idx)+6)=htons(32); // size + *(u_int32_t*)(buffer+BUFIDX(idx)+8)=htonl(ctr++); + *(u_int32_t*)(buffer+BUFIDX(idx)+12)=htonl(t); + + *(u_int16_t*)(buffer+BUFIDX(idx)+0)=htons(0xffff ^ crc16(buffer+BUFIDX(idx)+2,30)); +} + +void read_r0ket(int fd){ + int r,t,o,x; + static unsigned char data[64]; + static unsigned char offset=0; + static unsigned char firstread=1; + + r=read(fd,data+offset,sizeof(data)-offset); + if(r<0){ + perror("read(device)"); + exit(EXIT_FAILURE); + }; + if(r==0){ + printf("nothing read. Shouldn't happen\n"); + return; + }; +#if 0 + for(t=offset;t0){ + if(!firstread) + printf("ignoring garbage: %02X\n",data[0]); + memmove(data,data+1,sizeof(data)-1); + r--; + }; + if(r==0){ /* no data left */ + return; + }; + + if(data[1]=='1'){ + firstread=0; + /* find frame end */ + for(x=2;x=r-1){ /* no EOF found */ + if(r>60){ + printf("serial frame content overflow\n"); + return; + }; + offset=r; /* keep unused data for next round */ + return; + }; + +// printf("consume %d: ",x); + o=16; + BUFPUSH(); + for(t=2;t=ELEMSIZE) /* "buffer" overflow protection */ + break; + buffer[BUFIDX(bufend)+o]=data[t]; + if(data[t]!='\\') + o++; +// printf("%02x",data[t]); + }; + pkt_cleanup(bufend); + x+=2; /* also consume end of frame marker */ +// printf("\n"); + }else if(data[1]=='7'){ /* beaconid frame */ + /* XXX: do something with beaconid */ + BUFPUSH(); + for(t=0;t<16;t++){ /* clear buffer */ + buffer[BUFIDX(bufend)+16+t]=0; + }; + buffer[BUFIDX(bufend)+16]=22; // RFBPROTO_READER_ANNOUNCE + *(u_int16_t*)(buffer+BUFIDX(bufend)+14)=0; + *(u_int16_t*)(buffer+BUFIDX(bufend)+14)= \ + htons(crc16(buffer+BUFIDX(bufend),14)); + x=8; + }else if(data[1]=='2'){ /* command ack frame */ + x=4; /* just consume, and do nothing */ + }else{ + if(!firstread) + printf("invalid frame type: %02x\n",data[1]); + x=2; + }; + if(x==r) /* all data consumed */ + return; + /* keep unconsumed data */ + memmove(data,data+x,r-x); + offset=r-x; + return; +} + +void write_socket(int sockfd){ + BUFPOP(); + if (send(sockfd, buffer+BUFIDX(bufstart), ELEMSIZE, 0)==-1){ + perror("send"); + exit(EXIT_FAILURE); + }; +} + +int main(int argc, char ** argv) +{ + int c; /* getopt return value */ + char *device="/dev/ttyACM0"; + int devfd,sockfd; /* FD for device & socket */ + int maxfd=0; + int t; + fd_set rset,wset,eset; /* file descriptors for select() */ + struct timeval timeout; /* Timeout for select */ + struct sockaddr_in si_other; /* target socket */ + + /* The big getopt loop */ + while ((c = getopt(argc, argv, "d:")) != EOF) + switch (c) + { + case 'd': + device=(char *)malloc(strlen(optarg)+2); + strcpy(device,optarg); + break; + + default: + fprintf(stderr, "Usage: %s [options] \n\n\ +This program reads packets from an USB-Serial R0ket\n\ +and sends them off via TCP/UDP to a central host\n\n\ + -d Open a different device instead of '%s'\n\ + -h This help\n", + argv[0], + device + ); + exit(255); + } + +/* argc -= optind; argv += optind; *//* only if we want more args */ + + /* Open & prep input device */ + if((devfd=open(device,O_RDWR)) == -1) + perror("open_device"); + + setnonblocking(devfd); + + /* Open & prep outout device */ + if ((sockfd=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1){ + perror("socket"); + exit(EXIT_FAILURE); + }; + memset((char *) &si_other, 0, sizeof(si_other)); + si_other.sin_family = AF_INET; + si_other.sin_port = htons(PORT); + if (inet_aton(SRV_IP, &si_other.sin_addr)==0) { + perror("inet_aton()"); + exit(EXIT_FAILURE); + } + if(connect(sockfd,(struct sockaddr*)&si_other,sizeof(si_other))<0){ + perror("connect"); + exit(EXIT_FAILURE); + }; + setnonblocking(sockfd); + + /* prepare stuff for select */ + if(devfd>maxfd) + maxfd=devfd; + if(sockfd>maxfd) + maxfd=sockfd; + + while(1){ + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + FD_ZERO(&rset); + FD_SET(devfd,&rset); + FD_ZERO(&wset); + if(bufstart!=bufend){ + FD_SET(sockfd,&wset); + }; + FD_ZERO(&eset); + FD_SET(devfd,&eset); + + t = select(maxfd+1, &rset, &wset, &eset, &timeout); + + if (t<0){ + perror("select"); + exit(EXIT_FAILURE); + }; + + if (t==0){ /* timeout */ + printf("[timeout]\n"); + }; + + if (FD_ISSET(devfd,&rset)) + read_r0ket(devfd); + + if (FD_ISSET(sockfd,&wset)) + write_socket(sockfd); + + }; + return(0); +} From 91e8211cd6e5fc987328a9ee81f6eddae26d4a52 Mon Sep 17 00:00:00 2001 From: Stefan `Sec` Zehl Date: Wed, 30 May 2012 20:45:37 +0200 Subject: [PATCH 07/16] Fix uuid output in rf. --- tools/mesh/rf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/mesh/rf b/tools/mesh/rf index 4d78ca3..bb0029c 100755 --- a/tools/mesh/rf +++ b/tools/mesh/rf @@ -181,7 +181,7 @@ if($cmd =~ /^r/){ r0ket::set_rxlen(shift); }elsif ($set =~ /^id/){ my $id=r0ket::get_id(); - print "r0ket id: ",r0ket::hprint($id),"\n"; + print "r0ket id: ",$id,"\n"; }else{ die "Unknown config argument $set\n"; }; From 4f6809b89c019329c524ea97a3f63a6804d97c84 Mon Sep 17 00:00:00 2001 From: Stefan `Sec` Zehl Date: Wed, 30 May 2012 20:47:01 +0200 Subject: [PATCH 08/16] fix BRIDGE-PROTOCOL file (rxlen instead of maclen) --- firmware/BRIDGE-PROTOCOL | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/firmware/BRIDGE-PROTOCOL b/firmware/BRIDGE-PROTOCOL index 4e8a6c6..9f0fcb4 100644 --- a/firmware/BRIDGE-PROTOCOL +++ b/firmware/BRIDGE-PROTOCOL @@ -8,12 +8,17 @@ r->h: \2\0 Settings: h->r:\3\0 r->h: \2\0 + h->r:\4\0 r->h: \2\0 + h->r:\5\0 r->h: \2\0 -h->r:\6\0 + +h->r:\6\0 r->h: \2\0 + h->r:\7\0 r->h: \7\0 +r->h: \2\0 From d23bd1eadad11e0a1f7cc8dd8abf7a161afc8234 Mon Sep 17 00:00:00 2001 From: Stefan `Sec` Zehl Date: Wed, 30 May 2012 20:49:40 +0200 Subject: [PATCH 09/16] Add makefile for obreader.c --- tools/reader/Makefile | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tools/reader/Makefile diff --git a/tools/reader/Makefile b/tools/reader/Makefile new file mode 100644 index 0000000..61e6aad --- /dev/null +++ b/tools/reader/Makefile @@ -0,0 +1,11 @@ +CC = gcc +CFLAGS = -Wall -O2 +EXE = obreader +FILES = obreader.c +OBJS = obreader.o + +all: $(FILES) + $(CC) $(CFLAGS) $(FILES) -o $(EXE) + +clean: + rm -f $(EXE) $(OBJS) From 6b6eb79c5c5eacbb574857654a5057a2783d8ced Mon Sep 17 00:00:00 2001 From: Stefan `Sec` Zehl Date: Wed, 30 May 2012 20:51:07 +0200 Subject: [PATCH 10/16] Allow more chars in listen-log.pl --- tools/reader/listen-log.pl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/reader/listen-log.pl b/tools/reader/listen-log.pl index 5176554..367e997 100755 --- a/tools/reader/listen-log.pl +++ b/tools/reader/listen-log.pl @@ -26,7 +26,8 @@ while (1){ ($port, $hisiaddr) = sockaddr_in($hispaddr); # $host = gethostbyaddr($hisiaddr, AF_INET); $host=join(".",unpack("CCCC",$hisiaddr)); - $buf =~ y!a-zA-Z0-9.:,; _()[]{}?-!!cd; + chomp($buf); + $buf =~ y!a-zA-Z0-9.:,; _()[]{}<>/?-!!cd; print substr(scalar(localtime),11,8)," ",$host," ",$buf,"\n"; }; From 9840ae899428beaa0abeedda534324372c51690d Mon Sep 17 00:00:00 2001 From: Stefan `Sec` Zehl Date: Wed, 30 May 2012 20:54:50 +0200 Subject: [PATCH 11/16] make netlink-notifier executable --- tools/reader/netlink-notifier.pl | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 tools/reader/netlink-notifier.pl diff --git a/tools/reader/netlink-notifier.pl b/tools/reader/netlink-notifier.pl old mode 100644 new mode 100755 From aedb52cff924217eff24f5cd219ac5a9ee9d4c0c Mon Sep 17 00:00:00 2001 From: Stefan `Sec` Zehl Date: Wed, 30 May 2012 20:44:04 +0200 Subject: [PATCH 12/16] obreader now with hearbeat and more stable. --- tools/reader/obreader.c | 240 +++++++++++++++++++++++++++++++++------- 1 file changed, 200 insertions(+), 40 deletions(-) diff --git a/tools/reader/obreader.c b/tools/reader/obreader.c index 1749dd0..3c27997 100644 --- a/tools/reader/obreader.c +++ b/tools/reader/obreader.c @@ -19,10 +19,15 @@ #include #include +#define INTERVAL 1 + #define BUFSIZE 100 #define PORT 2342 #define SRV_IP "127.0.0.1" +#define TYPE_UDP +#undef TYPE_TCP + /* rotating buffer */ #define ELEMSIZE 32 unsigned int bufstart=0,bufend=0; /* rotating buffer */ @@ -31,6 +36,8 @@ unsigned int bufstart=0,bufend=0; /* rotating buffer */ #define BUFPOP() do{ bufstart++; if(bufstart==BUFSIZE){ bufstart=0; }; }while(0) unsigned char buffer[BUFIDX(BUFSIZE)]; +time_t the_time; + static u_int16_t crc16 (const unsigned char *buffer, int size) { @@ -68,20 +75,58 @@ void setnonblocking(int fd) { /* Reference is https://r0ket.badge.events.ccc.de/tracking:reader */ void pkt_cleanup(int idx){ static u_int32_t ctr; - time_t t; - time(&t); buffer[BUFIDX(idx)+2]=1; // BEACONLOG_SIGHTING buffer[BUFIDX(idx)+3]=0; // interface 0 *(u_int16_t*)(buffer+BUFIDX(idx)+4)=htons(1234); // reader id *(u_int16_t*)(buffer+BUFIDX(idx)+6)=htons(32); // size *(u_int32_t*)(buffer+BUFIDX(idx)+8)=htonl(ctr++); - *(u_int32_t*)(buffer+BUFIDX(idx)+12)=htonl(t); + *(u_int32_t*)(buffer+BUFIDX(idx)+12)=htonl(the_time); *(u_int16_t*)(buffer+BUFIDX(idx)+0)=htons(0xffff ^ crc16(buffer+BUFIDX(idx)+2,30)); } -void read_r0ket(int fd){ +void write_r0ket(int fd,char * buf,int len){ + int r; + r=write(fd,buf,len); + if(r!=len){ + printf("wrote only %d bytes of %d to device\n",r,len); + exit(EXIT_FAILURE); + }; +} + +void setup_r0ket(int fd){ + unsigned char buf[256]; + int x,r; + u_int32_t uuid=0; + + write_r0ket(fd,"\\7\\0",4); /* Get UUID */ + write_r0ket(fd,"\\4\001\002\003\002\001\\0",9); /* Set rx_mac */ + write_r0ket(fd,"\\5\x51\\0",5); /* Set channel */ + write_r0ket(fd,"\\6\x10\\0",5); /* Set rx_len */ + + usleep(100000); /* wait 100ms for an answer */ + r=read(fd,buf,sizeof(buf)); + /* try to find uuid in reply. Could be nicer, but it works. */ + if(r>7){ + for(x=0;x=r-1){ /* no EOF found */ if(r>60){ printf("serial frame content overflow\n"); - return; + return 0; }; offset=r; /* keep unused data for next round */ - return; + return 0; }; // printf("consume %d: ",x); @@ -147,15 +193,28 @@ void read_r0ket(int fd){ x+=2; /* also consume end of frame marker */ // printf("\n"); }else if(data[1]=='7'){ /* beaconid frame */ + /* find frame end */ + for(x=2;x=r-1){ /* no EOF found */ + if(r>60){ + printf("serial frame content overflow\n"); + return 0; + }; + offset=r; /* keep unused data for next round */ + return 0; + }; /* XXX: do something with beaconid */ BUFPUSH(); for(t=0;t<16;t++){ /* clear buffer */ buffer[BUFIDX(bufend)+16+t]=0; }; buffer[BUFIDX(bufend)+16]=22; // RFBPROTO_READER_ANNOUNCE - *(u_int16_t*)(buffer+BUFIDX(bufend)+14)=0; - *(u_int16_t*)(buffer+BUFIDX(bufend)+14)= \ - htons(crc16(buffer+BUFIDX(bufend),14)); + *(u_int16_t*)(buffer+BUFIDX(bufend)+16+14)= \ + htons(crc16(buffer+BUFIDX(bufend)+16,14)); + pkt_cleanup(bufend); x=8; }else if(data[1]=='2'){ /* command ack frame */ x=4; /* just consume, and do nothing */ @@ -165,31 +224,33 @@ void read_r0ket(int fd){ x=2; }; if(x==r) /* all data consumed */ - return; + return 0; /* keep unconsumed data */ memmove(data,data+x,r-x); offset=r-x; - return; + return 0; } void write_socket(int sockfd){ BUFPOP(); if (send(sockfd, buffer+BUFIDX(bufstart), ELEMSIZE, 0)==-1){ perror("send"); - exit(EXIT_FAILURE); +// exit(EXIT_FAILURE); }; } -int main(int argc, char ** argv) -{ +int main(int argc, char ** argv){ int c; /* getopt return value */ char *device="/dev/ttyACM0"; - int devfd,sockfd; /* FD for device & socket */ + int devfd=-1,sockfd=-1,listenfd=-1; /* FD for device & socket */ int maxfd=0; - int t; - fd_set rset,wset,eset; /* file descriptors for select() */ - struct timeval timeout; /* Timeout for select */ + int cnt; + fd_set rset,wset; /* file descriptors for select() */ + struct timeval timeout; /* Timeout for select */ struct sockaddr_in si_other; /* target socket */ + time_t ot,heartbeat=0; + + time(&ot); /* The big getopt loop */ while ((c = getopt(argc, argv, "d:")) != EOF) @@ -215,14 +276,17 @@ and sends them off via TCP/UDP to a central host\n\n\ /* argc -= optind; argv += optind; *//* only if we want more args */ /* Open & prep input device */ - if((devfd=open(device,O_RDWR)) == -1) - perror("open_device"); - + if((devfd=open(device,O_RDWR)) == -1){ + perror("open(device)"); + exit(EXIT_FAILURE); + }; setnonblocking(devfd); + setup_r0ket(devfd); /* Open & prep outout device */ +#ifdef TYPE_UDP if ((sockfd=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1){ - perror("socket"); + perror("socket(udp)"); exit(EXIT_FAILURE); }; memset((char *) &si_other, 0, sizeof(si_other)); @@ -237,43 +301,139 @@ and sends them off via TCP/UDP to a central host\n\n\ exit(EXIT_FAILURE); }; setnonblocking(sockfd); - - /* prepare stuff for select */ - if(devfd>maxfd) - maxfd=devfd; - if(sockfd>maxfd) - maxfd=sockfd; +#endif +#ifdef TYPE_TCP + if((listenfd = socket(AF_INET, SOCK_STREAM, 0))<0){ + perror("socket(tcpserver)"); + exit(EXIT_FAILURE); + }; + memset((char *) &si_other, 0, sizeof(si_other)); + si_other.sin_family = AF_INET; + si_other.sin_addr.s_addr = INADDR_ANY; + si_other.sin_port = htons(PORT); + if(bind(listenfd,(struct sockaddr*)&si_other,sizeof(si_other))<0){ + perror("bind"); + exit(EXIT_FAILURE); + }; + if(listen(listenfd,1)!=0){ + perror("listen"); + exit(EXIT_FAILURE); + }; +#endif while(1){ + /* prepare stuff for select */ timeout.tv_sec = 1; timeout.tv_usec = 0; + if(devfd>maxfd) + maxfd=devfd; + if(sockfd>maxfd) + maxfd=sockfd; + if(listenfd>maxfd) + maxfd=listenfd; + FD_ZERO(&rset); + if(devfd==-1){ + fprintf(stderr,"Can't yet deal with disappearing device\n"); + exit(EXIT_FAILURE); + }; FD_SET(devfd,&rset); + if(sockfd!=-1){ +#ifdef TYPE_TCP + FD_SET(sockfd,&rset); +#endif + }else if(listenfd!=-1){ + FD_SET(listenfd,&rset); + }; + FD_ZERO(&wset); - if(bufstart!=bufend){ + if(bufstart!=bufend && sockfd!=-1){ FD_SET(sockfd,&wset); }; - FD_ZERO(&eset); - FD_SET(devfd,&eset); - t = select(maxfd+1, &rset, &wset, &eset, &timeout); + cnt = select(maxfd+1, &rset, &wset, NULL, &timeout); - if (t<0){ + + /* First run timer stuff */ + time(&the_time); + if(the_time-ot>=INTERVAL){ + ot=the_time; + printf("[running: buf=%d, sockfd=%d]\n",(BUFSIZE+bufend-bufstart)%BUFSIZE,sockfd); + }; + + if(the_time-heartbeat>=1){ + heartbeat=the_time; + write_r0ket(devfd,"\\7\\0",4); /* Get UUID */ + }; + + /* Now check select / fds*/ + if (cnt<0){ perror("select"); exit(EXIT_FAILURE); }; - if (t==0){ /* timeout */ + if (cnt==0){ /* timeout */ printf("[timeout]\n"); + continue; }; - if (FD_ISSET(devfd,&rset)) - read_r0ket(devfd); + if (FD_ISSET(devfd,&rset)){ + if(read_r0ket(devfd)<0){ + close(devfd); + devfd=-1; + }; + if(--cnt ==0) continue; + }; - if (FD_ISSET(sockfd,&wset)) + if (sockfd!=-1 && FD_ISSET(sockfd,&rset)){ + int r; + unsigned char dummy[32]; + r=read(sockfd,dummy,32); + if(r<0){ + perror("read(socket)"); + exit(EXIT_FAILURE); + }; + if(r==0){ + printf("eof() on socket\n"); + close(sockfd); + sockfd=-1; + }; + if(r>0){ + printf("read [%d] bytes from socket and ignored them.\n",r); + }; + + if(--cnt ==0) continue; + }; + + if (sockfd!=-1 && FD_ISSET(sockfd,&wset)){ write_socket(sockfd); + if(--cnt ==0) continue; + }; + if (listenfd!=-1 && FD_ISSET(listenfd,&rset)){ + if(sockfd!=-1){ // close old connection + close(sockfd); + }; + unsigned int size=sizeof(si_other); + if((sockfd=accept(listenfd,(struct sockaddr*)&si_other,&size))<0){ + perror("accept"); + continue; // Do not exit, we can handle this :-) + }; + printf("New connection from %s (fd %d)\n", inet_ntoa(si_other.sin_addr), sockfd); + if(--cnt ==0) continue; + }; + + printf("unknwon select left over: cnt=%d, ",cnt); + printf("rset: "); + for(cnt=0;cnt Date: Thu, 31 May 2012 17:23:18 +0200 Subject: [PATCH 13/16] Add crosscompile support to Makefile --- tools/reader/Makefile | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/tools/reader/Makefile b/tools/reader/Makefile index 61e6aad..6b5ed87 100644 --- a/tools/reader/Makefile +++ b/tools/reader/Makefile @@ -1,11 +1,32 @@ -CC = gcc +ifneq "$(TUPLE)" "" +PREFIX=$(TUPLE)- +ARCH=$(shell echo $(TUPLE)|sed 's/-.*//') +else +PREFIX= +ARCH=$(shell uname -m) +endif + +CC = $(PREFIX)gcc +STRIP = $(PREFIX)strip + CFLAGS = -Wall -O2 EXE = obreader FILES = obreader.c -OBJS = obreader.o -all: $(FILES) - $(CC) $(CFLAGS) $(FILES) -o $(EXE) +$(ARCH)-$(EXE): + $(CC) $(CFLAGS) -o $@ $(FILES) + $(STRIP) $@ + + +# Using OpenWRT crossbuild environment, see +# +# for setup instructions. +WRT=~/r0ket/openwrt/trunk/staging_dir + +mips: + STAGING_DIR=$(WRT)/toolchain-mips_r2_gcc-4.6-linaro_uClibc-0.9.33.2 \ + PATH=${PATH}:$(STAGING_DIR)/bin \ + $(MAKE) TUPLE=mips-openwrt-linux clean: - rm -f $(EXE) $(OBJS) + rm -f *-$(EXE) From d02520f6b1542ba67a4da89f5a53a6443559c459 Mon Sep 17 00:00:00 2001 From: Stefan `Sec` Zehl Date: Thu, 31 May 2012 17:24:07 +0200 Subject: [PATCH 14/16] Make server ip a commandline option --- tools/reader/obreader.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tools/reader/obreader.c b/tools/reader/obreader.c index 3c27997..9c46630 100644 --- a/tools/reader/obreader.c +++ b/tools/reader/obreader.c @@ -23,7 +23,7 @@ #define BUFSIZE 100 #define PORT 2342 -#define SRV_IP "127.0.0.1" +char *SRV_IP="127.0.0.1"; #define TYPE_UDP #undef TYPE_TCP @@ -253,7 +253,7 @@ int main(int argc, char ** argv){ time(&ot); /* The big getopt loop */ - while ((c = getopt(argc, argv, "d:")) != EOF) + while ((c = getopt(argc, argv, "s:d:")) != EOF) switch (c) { case 'd': @@ -261,6 +261,11 @@ int main(int argc, char ** argv){ strcpy(device,optarg); break; + case 's': + SRV_IP=(char *)malloc(strlen(optarg)+2); + strcpy(SRV_IP,optarg); + break; + default: fprintf(stderr, "Usage: %s [options] \n\n\ This program reads packets from an USB-Serial R0ket\n\ From 1237f1a2ca749944462679d90473f709de1aa204 Mon Sep 17 00:00:00 2001 From: Stefan `Sec` Zehl Date: Sun, 3 Jun 2012 11:35:44 +0200 Subject: [PATCH 15/16] Fix makefile (dependecies) --- tools/reader/Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/reader/Makefile b/tools/reader/Makefile index 6b5ed87..a5281de 100644 --- a/tools/reader/Makefile +++ b/tools/reader/Makefile @@ -13,7 +13,7 @@ CFLAGS = -Wall -O2 EXE = obreader FILES = obreader.c -$(ARCH)-$(EXE): +$(ARCH)-$(EXE): $(FILES) $(CC) $(CFLAGS) -o $@ $(FILES) $(STRIP) $@ @@ -22,9 +22,10 @@ $(ARCH)-$(EXE): # # for setup instructions. WRT=~/r0ket/openwrt/trunk/staging_dir +STAGING_DIR=$(WRT)/toolchain-mips_r2_gcc-4.6-linaro_uClibc-0.9.33.2 mips: - STAGING_DIR=$(WRT)/toolchain-mips_r2_gcc-4.6-linaro_uClibc-0.9.33.2 \ + STAGING_DIR=$(STAGING_DIR) \ PATH=${PATH}:$(STAGING_DIR)/bin \ $(MAKE) TUPLE=mips-openwrt-linux From 133fc642b7bf0d86dc25944b2d54dca23e2a5ee2 Mon Sep 17 00:00:00 2001 From: Stefan `Sec` Zehl Date: Sun, 3 Jun 2012 11:37:54 +0200 Subject: [PATCH 16/16] Make obreader setup the tty itself --- tools/reader/obreader.c | 72 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/tools/reader/obreader.c b/tools/reader/obreader.c index 9c46630..542d1a4 100644 --- a/tools/reader/obreader.c +++ b/tools/reader/obreader.c @@ -18,6 +18,7 @@ #include #include #include +#include #define INTERVAL 1 @@ -72,6 +73,74 @@ void setnonblocking(int fd) { return; } +void setupserial(int fd){ + struct termios config; + + if(!isatty(fd)) { + fprintf(stderr,"fd %d is not a tty?",fd); + exit(EXIT_FAILURE); + }; + if(tcgetattr(fd, &config) < 0) { + perror("tcgetattr"); + exit(EXIT_FAILURE); + }; + // + // Input flags - Turn off input processing + // convert break to null byte, no CR to NL translation, + // no NL to CR translation, don't mark parity errors or breaks + // no input parity check, don't strip high bit off, + // no XON/XOFF software flow control + // + config.c_iflag &= ~(IGNBRK | BRKINT | ICRNL | + INLCR | PARMRK | INPCK | ISTRIP | IXON); + // + // Output flags - Turn off output processing + // no CR to NL translation, no NL to CR-NL translation, + // no NL to CR translation, no column 0 CR suppression, + // no Ctrl-D suppression, no fill characters, no case mapping, + // no local output processing + // + // config.c_oflag &= ~(OCRNL | ONLCR | ONLRET | + // ONOCR | ONOEOT| OFILL | OLCUC | OPOST); + config.c_oflag = 0; + // + // No line processing: + // echo off, echo newline off, canonical mode off, + // extended input processing off, signal chars off + // + config.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG); + // + // Turn off character processing + // clear current char size mask, no parity checking, + // no output processing, force 8 bit input + // + config.c_cflag &= ~(CSIZE | PARENB); + config.c_cflag |= CS8; + // + // One input byte is enough to return from read() + // Inter-character timer off + // + config.c_cc[VMIN] = 1; + config.c_cc[VTIME] = 0; + // + // Communication speed (simple version, using the predefined + // constants) + // + /* + if(cfsetispeed(&config, B9600) < 0 || cfsetospeed(&config, B9600) < 0) { + perror("cfset[io]speed"); + exit(EXIT_FAILURE); + } + */ + // + // Finally, apply the configuration + // + if(tcsetattr(fd, TCSAFLUSH, &config) < 0) { + perror("tcsetattr"); + exit(EXIT_FAILURE); + }; +}; + /* Reference is https://r0ket.badge.events.ccc.de/tracking:reader */ void pkt_cleanup(int idx){ static u_int32_t ctr; @@ -285,7 +354,8 @@ and sends them off via TCP/UDP to a central host\n\n\ perror("open(device)"); exit(EXIT_FAILURE); }; - setnonblocking(devfd); + setupserial(devfd); + setnonblocking(devfd); setup_r0ket(devfd); /* Open & prep outout device */