/*
* ethernet specific multiplexor for nonet
*
* this line discipline gets pushed onto an ethernet channel
* to demultiplex/multiplex nonet conversations.
*/
#include "u.h"
#include "lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "errno.h"
#include "../port/nonet.h"
#define DPRINT if(pnonet)print
extern int pnonet;
static void etherparse(uchar*, char*);
static void noetherclose(Queue*);
static void noetheriput(Queue*, Block*);
static void noetheropen(Queue*, Stream*);
static void noetheroput(Queue*, Block*);
/*
* ethernet header of a packet
*/
#define EHDRSIZE (ETHERHDRSIZE + NO_HDRSIZE)
#define EMAXBODY (ETHERMAXTU - EHDRSIZE) /* maximum ethernet packet body */
#define ETHER_TYPE 0x900 /* most significant byte last */
/*
* the ethernet multiplexor stream module definition
*/
Qinfo noetherinfo =
{
noetheriput,
noetheroput,
noetheropen,
noetherclose,
"noether"
};
/*
* perform the ether specific part of nonetconnect. just stick
* the address into the prototype header.
*/
void
noetherconnect(Noconv *cp, char *ea)
{
Etherpkt *eh;
eh = (Etherpkt*)cp->media->rptr;
etherparse(eh->d, ea);
eh->type[0] = ETHER_TYPE>>8;
eh->type[1] = ETHER_TYPE & 0xff;
}
/*
* set up an ether interface
*/
static void
noetheropen(Queue *q, Stream *s)
{
streamenter(s);
nonetnewifc(q, s, ETHERMAXTU, ETHERMINTU, ETHERHDRSIZE, noetherconnect);
}
/*
* tear down an ether interface
*/
static void
noetherclose(Queue *q)
{
Noifc *ifc;
ifc = (Noifc*)q->ptr;
nonetfreeifc(ifc);
}
/*
* configure the system
*/
static void
noetheroput(Queue *q, Block *bp)
{
Noifc *ifc;
ifc = (Noifc*)q->ptr;
if(bp->type != M_DATA){
if(streamparse("config", bp)){
if(*bp->rptr == 0)
strcpy(ifc->name, "nonet");
else
strncpy(ifc->name, (char *)bp->rptr, sizeof(ifc->name));
} else
PUTNEXT(q, bp);
return;
}
PUTNEXT(q, bp);
}
/*
* respond to a misaddressed message with a close
*/
void
noetherbad(Noifc *ifc, Block *bp, int circuit)
{
Etherpkt *eh, *neh;
Nohdr *nh, *nnh;
Block *nbp;
int r;
Noconv *cp, *ep;
/*
* crack the packet header
*/
eh = (Etherpkt*)bp->rptr;
nh = (Nohdr*)eh->data;
/* print("bad %.2ux%.2ux%.2ux%.2ux%.2ux%.2ux c %d m %d f %d\n",
eh->s[0], eh->s[1], eh->s[2], eh->s[3], eh->s[4],
eh->s[5], circuit, nh->mid, nh->flag); /**/
if(nh->flag & NO_RESET)
goto out;
/*
* only one reset per message
*/
r = (nh->remain[1]<<8) | nh->remain[0];
if(r<0)
goto out;
/*
* craft an error reply
*/
/* print("sending reset\n"); /**/
nbp = allocb(60);
nbp->flags |= S_DELIM;
nbp->wptr = nbp->rptr + 60;
memset(bp->rptr, 0, 60);
neh = (Etherpkt *)nbp->rptr;
nnh = (Nohdr*)neh->data;
memmove(neh, eh, EHDRSIZE);
nnh->circuit[0] ^= 1;
nnh->remain[0] = nnh->remain[1] = 0;
nnh->flag = NO_HANGUP | NO_RESET;
nnh->ack = nh->mid;
nnh->mid = nh->ack;
memmove(neh->s, eh->d, sizeof(neh->s));
memmove(neh->d, eh->s, sizeof(neh->d));
nonetcksum(nbp, ETHERHDRSIZE);
PUTNEXT(ifc->wq, nbp);
out:
freeb(bp);
}
/*
* Input a packet and use the ether address to select the correct
* nonet device to pass it to.
*
* Simplifying assumption: one put == one packet && the complete header
* is in the first block. If this isn't true, demultiplexing will not work.
*/
static void
noetheriput(Queue *q, Block *bp)
{
Noifc *ifc;
int circuit;
Noconv *cp, *ep;
Etherpkt *eh, *peh;
Nohdr *nh;
ulong s;
Block *nbp;
int next;
Nocall *clp;
if(bp->type != M_DATA){
PUTNEXT(q, bp);
return;
}
ifc = (Noifc*)q->ptr;
eh = (Etherpkt*)bp->rptr;
nh = (Nohdr*)eh->data;
circuit = (nh->circuit[2]<<16) | (nh->circuit[1]<<8) | nh->circuit[0];
s = (nh->sum[1]<<8) | nh->sum[0];
if(s && s!=nonetcksum(bp, ETHERHDRSIZE)){
/* print("checksum error %ux %ux\n", s, (nh->sum[1]<<8) | nh->sum[0]); /**/
freeb(bp);
return;
}
/*
* look for an existing circuit.
*/
ep = &ifc->conv[conf.nnoconv];
for(cp = &ifc->conv[0]; cp < ep; cp++){
nbp = cp->media;
if(nbp == 0)
continue;
peh = (Etherpkt*)nbp->rptr;
if(circuit==cp->rcvcircuit && memcmp(peh->d, eh->s, sizeof(eh->s))==0){
if(!canqlock(cp)){
freeb(bp);
return;
}
peh = (Etherpkt*)nbp->rptr;
if(circuit==cp->rcvcircuit
&& memcmp(peh->d, eh->s, sizeof(eh->s))==0){
bp->rptr += ifc->hsize;
nonetrcvmsg(cp, bp);
qunlock(cp);
return;
}
qunlock(cp);
}
}
/*
* if not a new call, then its misaddressed
*/
if((nh->flag & NO_NEWCALL) == 0){
noetherbad(ifc, bp, circuit);
return;
}
/*
* Queue call in a circular queue and wakeup a listener.
*/
DPRINT("call in\n");
lock(&ifc->lock);
next = (ifc->wptr + 1) % Nnocalls;
if(next == ifc->rptr){
/* no room in the queue */
unlock(&ifc->lock);
freeb(bp);
return;
}
clp = &ifc->call[ifc->wptr];
sprint(clp->raddr, "%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux",
eh->s[0], eh->s[1], eh->s[2], eh->s[3], eh->s[4], eh->s[5]);
clp->circuit = circuit^1;
bp->rptr += ifc->hsize;
clp->msg = bp;
ifc->wptr = next;
unlock(&ifc->lock);
wakeup(&ifc->listenr);
}
/*
* parse an ethernet address (assumed to be 12 ascii hex digits)
*/
static void
etherparse(uchar *to, char *from)
{
ulong ip;
uchar nip[4];
int tdig;
int fdig;
int i;
#ifdef MAGNUM
/* Dot means ip address */
if(strchr(from, '.')) {
ip = ipparse(from);
if(ip == 0)
error(Ebadnet);
hnputl(nip, ip);
if(arp_lookup(nip, to))
return;
}
#endif
if(strlen(from) != 12)
error(Ebadnet);
for(i = 0; i < 6; i++){
fdig = (*from++)&0x7f;
tdig = fdig >= 'a' ? ((fdig - 'a') + 10)
: (fdig >= 'A' ? ((fdig - 'A') + 10) : (fdig - '0'));
fdig = (*from++)&0x7f;
tdig <<= 4;
tdig |= fdig >= 'a' ? ((fdig - 'a') + 10)
: (fdig >= 'A' ? ((fdig - 'A') + 10) : (fdig - '0'));
*to++ = tdig;
}
}