/* * Copyright (c) 2001 Tommy Bohlin * All rights reserved. * * 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 THE 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. */ /* mux.c * * Limitations: * - LM exclusive mode not supported */ #include #include #include /********************************************************************** * Constants **********************************************************************/ static const char id_mux_lsap[]="mux lsap"; static const char id_mux_connection[]="mux connection"; static const char id_mux_sarbuffer[]="mux sar buffer"; static const char id_mux_sendbuf[]="mux sendbuf"; #define LM_CONTROL 0x80 #define LM_CONNECT 0x01 #define LM_CONNECT_CONFIRM 0x81 #define LM_DISCONNECT 0x02 #define LM_ACCESSMODE 0x03 #define LM_ACCESSMODE_CONFIRM 0x83 #define LM_UNSUPPORTED 0xff #define REASON_USER 0x01 #define REASON_LINK_MANAGEMENT 0x05 #define REASON_DISCONNECTED 0x06 #define REASON_NO_PEER 0x08 #define REASON_UNSPECIFIED 0xff #define LSAP_UNCONNECTED 0x70 #define TTP_P 0x80 #define TTP_M 0x80 #define PI_MAXSDUSIZE 1 #define INITIAL_CREDITS 1 /* Is this the level we want? */ /********************************************************************** * Internal functions **********************************************************************/ static SendBuf* makeSendBuf(int length) { SendBuf* s=allocMem(id_mux_sendbuf,sizeof(SendBuf)+length-1); s->length=length; return s; } static void sendDisconnect(LAPPrivate* lapp, int dlsap, int slsap, int reason) { SendBuf* sb=makeSendBuf(6); sb->buf[2]=dlsap|LM_CONTROL; sb->buf[3]=slsap; sb->buf[4]=LM_DISCONNECT; sb->buf[5]=reason; /* User data is not supported */ lapAppendSendBuffer(lapp,sb); } static void sendConnect(LAPPrivate* lapp, int dlsap, int slsap, bool ttp, int credits, const u_char* buf, int len) { SendBuf* sb; sb=makeSendBuf((ttp ? 7 : 6)+len); sb->buf[2]=dlsap|LM_CONTROL; sb->buf[3]=slsap; sb->buf[4]=LM_CONNECT; sb->buf[5]=0; if(ttp) sb->buf[6]=credits; memcpy(sb->buf+(ttp ? 7 : 6),buf,len); lapAppendSendBuffer(lapp,sb); } static void sendConnectConfirm(LAPPrivate* lapp, int dlsap, int slsap, bool ttp, int credits) { SendBuf* sb; sb=makeSendBuf(ttp ? 7 : 6); sb->buf[2]=dlsap|LM_CONTROL; sb->buf[3]=slsap; sb->buf[4]=LM_CONNECT_CONFIRM; sb->buf[5]=0; if(ttp) sb->buf[6]=credits; /* User data not supported */ lapAppendSendBuffer(lapp,sb); } static void sendAccessModeDeny(LAPPrivate* lapp, int dlsap, int slsap) { SendBuf* sb=makeSendBuf(6); sb->buf[2]=dlsap|LM_CONTROL; sb->buf[3]=slsap; sb->buf[4]=LM_ACCESSMODE_CONFIRM; sb->buf[5]=LM_UNSUPPORTED; lapAppendSendBuffer(lapp,sb); } static void disableConnection(ConnectionPrivate* conp) { if((conp->flags&(CST_OPEN|CST_OPENING)) && !(conp->flags&(CST_NOTIFIED))) { conp->flags|=CST_NOTIFIED; sendDisconnect(conp->lap,conp->remoteSel,conp->localSel,REASON_USER); } if(conp->sarBuffer) { freeMem(conp->sarBuffer); conp->sarBuffer=0; } while(conp->sendHead) { SendBuf* sb=conp->sendHead; conp->sendHead=sb->next; freeMem(sb); } if(conp->flags&(CST_OPENING|CST_OPEN)) { conp->flags&=~(CST_OPENING|CST_OPEN); if(conp->con.status) conp->con.status(&conp->con,CONN_CLOSED,0,0); } } static void freeConnection(ConnectionPrivate* conp) { LAPPrivate* lapp=conp->lap; ConnectionPrivate** ch=&lapp->connections; ConnectionPrivate* c; while((c=*ch)) { if(c==conp) { *ch=c->next; break; } else { ch=&c->next; } } if(lapp->nextConn==conp) lapp->nextConn=0; freeMem(conp); } static int parseTTPConnect(ConnectionPrivate* c, const u_char* buf, int len) { int len1; if(len<1) return -1; c->sendCredits=buf[0]&~TTP_P; if(buf[0]&TTP_P) { if(len<2) return -1; len1=buf[1]+2; if(len1>len) return -1; c->sendSduSize=0x7fffffff&getBEParameter(PI_MAXSDUSIZE,0,buf+2,len1-2); return len1; } else { return 1; } } static void lsapControl(LAPPrivate* lapp, int dlsap, int slsap, int op, ConnectionPrivate* con, u_char* buf, int len) { int i; switch(op) { case LM_CONNECT: if(con) { /* Reply to this? */ birda_log("Attempted connect to existing connection\n"); } else { LSAPPrivate* lsapp=lapp->lsaps; while(lsapp && lsapp->lsapSel!=dlsap) lsapp=lsapp->next; if(lsapp) { con=allocMem(id_mux_connection,sizeof(ConnectionPrivate)); con->con.handle=0; con->con.status=0; con->con.data=0; con->lap=lapp; con->localSel=dlsap; con->remoteSel=slsap; con->flags=(lsapp->flags&CST_USER_FLAGS)|CST_OPEN; con->sendSduSize=0; con->recvSduSize=0; con->sarLength=0; con->sarBuffer=0; con->sendHead=0; con->sendCredits=0; con->recvCredits=0; con->wantedRecvCredits=0; con->sendDataSize=lapp->sendParams.dataSize-2; /* LM header bytes */ con->recvDataSize=lapp->recvDataSize-2; /* LM header bytes */ if(con->flags&LM_TINY_TP) { con->recvDataSize--; con->sendDataSize--; con->recvCredits=1; con->wantedRecvCredits=INITIAL_CREDITS; } i=lsapp->flags&LM_TINY_TP ? parseTTPConnect(con,buf,len) : 0; if(i>=0 && lsapp->lsap.accept && lsapp->lsap.accept(&lsapp->lsap,&con->con,buf+i,len-i)) { if(con->recvSduSize) con->sarBuffer=allocMem(id_mux_sarbuffer,con->recvSduSize); con->next=lapp->connections; lapp->connections=con; /* SAR not enabled in the return direction */ sendConnectConfirm(lapp,slsap,dlsap,con->flags&LM_TINY_TP,con->recvCredits); } else { freeMem(con); sendDisconnect(lapp,slsap,dlsap,REASON_UNSPECIFIED); } } else { birda_log("No such LSAP: %d\n",dlsap); sendDisconnect(lapp,slsap,dlsap,REASON_NO_PEER); } } break; case LM_CONNECT_CONFIRM: if(con->flags&CST_OPENING) { i=(con->flags&LM_TINY_TP) ? parseTTPConnect(con,buf,len) : 0; if(i>=0) { con->flags=(con->flags&~CST_OPENING)|CST_OPEN; if(con->recvSduSize) con->sarBuffer=allocMem(id_mux_sarbuffer,con->recvSduSize); if(con->con.status) con->con.status(&con->con,CONN_OPENED,buf+i,len-i); } else { birda_log("failed to exchanged ttp settings\n"); con->flags&=CST_OPENING; /* Do anything more? */ } } break; case LM_DISCONNECT: if(con) { con->flags|=CST_NOTIFIED; disableConnection(con); } break; } } static void unconnData(LAPPrivate* lapp, u_char* buf, int len) { birda_log("Unconnected LSAP data:\n"); showBytes(buf,len); } /********************************************************************** * Called by lap.c **********************************************************************/ void lmDisableConnections(LAPPrivate* lapp) { ConnectionPrivate* c; for(c=lapp->connections;c;c=c->next) disableConnection(c); } void lmFreeConnections(LAPPrivate* lapp) { while(lapp->connections) freeConnection(lapp->connections); } void lmMoveConnectionBuffers(LAPPrivate* lapp, int wanted) { if(lapp->connections) { int flag; ConnectionPrivate* c; if(!lapp->nextConn) lapp->nextConn=lapp->connections; for(flag=0,c=lapp->nextConn;lapp->sendCountflags&CST_OPEN) { int delta=c->wantedRecvCredits-c->recvCredits; SendBuf* s=c->sendHead; if(s) { c->sendHead=s->next; } else if(delta>0) { s=makeSendBuf(5); s->buf[2]=c->remoteSel; s->buf[3]=c->localSel; s->buf[4]=0; } if(delta>0) { if(delta>127) delta=127; s->buf[4]=(s->buf[4]&TTP_M)|delta; c->recvCredits+=delta; } if(s) { lapAppendSendBuffer(lapp,s); flag=1; } } c=c->next ? c->next : lapp->connections; if(c==lapp->nextConn) { if(!flag) return; flag=0; } } } } void lmData(LAPPrivate* lapp, u_char* buf, int len, int expedited) { int cmd,dlsap,slsap,opcode; ConnectionPrivate* conp; if(len<2) return; cmd=LM_CONTROL&buf[0]; dlsap=~LM_CONTROL&buf[0]; slsap=~LM_CONTROL&buf[1]; if(dlsap>=LSAP_UNCONNECTED || slsap>=LSAP_UNCONNECTED) { if(!cmd && dlsap==LSAP_UNCONNECTED && slsap==LSAP_UNCONNECTED) unconnData(lapp,buf+2,len-2); return; } for(conp=lapp->connections;conp;conp=conp->next) if((conp->flags&(CST_OPENING|CST_OPEN)) && conp->localSel==dlsap && conp->remoteSel==slsap) break; /* NB: Should we also discard expedited data? */ if(cmd) { if(len<3 || expedited) return; opcode=buf[2]; switch(opcode) { case LM_CONNECT_CONFIRM: case LM_CONNECT: case LM_DISCONNECT: lsapControl(lapp,dlsap,slsap,opcode,conp,buf+4,len-4); break; case LM_ACCESSMODE: sendAccessModeDeny(lapp,slsap,dlsap); break; case LM_ACCESSMODE_CONFIRM: break; } } else if(!conp) { sendDisconnect(lapp,slsap,dlsap,REASON_DISCONNECTED); } else if(len>2) { if(!(conp->flags&LM_TINY_TP)) { if(conp->con.data) conp->con.data(&conp->con,buf+2,len-2); } else { if(conp->flags&CST_OPEN) { conp->sendCredits+=buf[2]&~TTP_M; if(len>3) { if(conp->recvCredits>0) conp->recvCredits--; if(!conp->recvSduSize) { if(conp->con.data) conp->con.data(&conp->con,buf+3,len-3); } else if(conp->sarLength+len-3>conp->recvSduSize) { sendDisconnect(conp->lap,conp->remoteSel,conp->localSel,REASON_LINK_MANAGEMENT); disableConnection(conp); birda_log("SDU is too long, closing the connection\n"); } else if(buf[2]&TTP_M) { memcpy(conp->sarBuffer+conp->sarLength,buf+3,len-3); conp->sarLength+=len-3; } else if(conp->sarLength) { memcpy(conp->sarBuffer+conp->sarLength,buf+3,len-3); conp->sarLength+=len-3; if(conp->con.data) conp->con.data(&conp->con,conp->sarBuffer,conp->sarLength); conp->sarLength=0; } else { if(conp->con.data) conp->con.data(&conp->con,buf+3,len-3); } } } } } } void lmUnitData(LAPPrivate* lapp, u_char* buf, int len) { if(len>=2 && buf[0]==LSAP_UNCONNECTED && (~LM_CONTROL&buf[1])==LSAP_UNCONNECTED) unconnData(lapp,buf+2,len-2); } /********************************************************************** * External functions **********************************************************************/ LSAP* lapNewLSAP(LAP* lap, int flags) { LAPPrivate* lapp=(LAPPrivate*)lap; LSAPPrivate* lsapp; ConnectionPrivate* c; u_char sel=0; for(sel=0;sellsaps; lsapp && lsapp->lsapSel!=sel; lsapp=lsapp->next); if(lsapp) continue; for(c=lapp->connections; c && !(c->flags&(CST_OPENING|CST_OPEN)) && c->localSel!=sel; c=c->next); if(!c) break; } if(sel==LSAP_UNCONNECTED) return 0; lsapp=allocMem(id_mux_lsap,sizeof(LSAPPrivate)); lsapp->lsap.handle=0; lsapp->lsap.accept=0; lsapp->lsapSel=sel; lsapp->flags=flags; lsapp->lap=lapp; lsapp->next=lapp->lsaps; lapp->lsaps=lsapp; return &lsapp->lsap; } u_char lsapGetSelector(LSAP* lsap) { return ((LSAPPrivate*)lsap)->lsapSel; } void lsapClose(LSAP* lsap) { LSAPPrivate* lsapp=(LSAPPrivate*)lsap; LSAPPrivate* l; LSAPPrivate** lh=&lsapp->lap->lsaps; while((l=*lh)) { if(l==lsapp) { *lh=l->next; freeMem(l); return; } else { lh=&l->next; } } } Connection* lapNewConnection(LAP* lap, int rlsap, int flags, const void* buf0, int len) { const u_char* buf=(u_char*)buf0; LAPPrivate* lapp=(LAPPrivate*)lap; LSAPPrivate* l; ConnectionPrivate* c; u_char llsap=0; if(!(lapp->state&STATE_CONNECTED)) return 0; for(llsap=0;llsaplsaps; l && l->lsapSel!=llsap; l=l->next); if(l) continue; for(c=lapp->connections; c && (!(c->flags&(CST_OPENING|CST_OPEN)) || c->localSel!=llsap || c->remoteSel!=rlsap); c=c->next); if(!c) break; } if(llsap==LSAP_UNCONNECTED) return 0; c=allocMem(id_mux_connection,sizeof(ConnectionPrivate)); c->con.handle=0; c->con.status=0; c->con.data=0; c->lap=lapp; c->localSel=llsap; c->remoteSel=rlsap; c->flags=(flags&CST_USER_FLAGS)|CST_OPENING; c->sendSduSize=0; c->recvSduSize=0; c->sarLength=0; c->sarBuffer=0; c->sendHead=0; c->sendCredits=0; c->recvCredits=0; c->wantedRecvCredits=0; c->sendDataSize=lapp->sendParams.dataSize-2; /* LM header bytes */ c->recvDataSize=lapp->recvDataSize-2; /* LM header bytes */ if(c->flags&LM_TINY_TP) { c->sendDataSize--; c->recvDataSize--; c->recvCredits=1; c->wantedRecvCredits=INITIAL_CREDITS; } c->next=lapp->connections; lapp->connections=c; sendConnect(lapp,rlsap,llsap,c->flags&LM_TINY_TP,c->recvCredits,buf,len); return &c->con; } void connClose(Connection* con) { ConnectionPrivate* conp=(ConnectionPrivate*)con; conp->con.status=0; disableConnection(conp); freeConnection(conp); } int connGetSendDataSize(Connection* con) { ConnectionPrivate* conp=(ConnectionPrivate*)con; return conp->sendSduSize ? conp->sendSduSize : conp->sendDataSize; } int connGetRecvDataSize(Connection* con) { ConnectionPrivate* conp=(ConnectionPrivate*)con; return conp->recvSduSize ? conp->recvSduSize : conp->recvDataSize; } bool connWrite(Connection* con, const void* buf0, int len) { u_char* buf=(u_char*)buf0; ConnectionPrivate* conp=(ConnectionPrivate*)con; int ttp=conp->flags&LM_TINY_TP ? 1 : 0; if(!(conp->flags&(CST_OPENING|CST_OPEN))) return FALSE; if(len>(conp->sendSduSize ? conp->sendSduSize : conp->sendDataSize)) { birda_log("Packet is too large, discarding\n"); return FALSE; } while(len>0) { int len1=lensendDataSize ? len : conp->sendDataSize; SendBuf* sb=makeSendBuf(len1+4+ttp); sb->buf[2]=conp->remoteSel; sb->buf[3]=conp->localSel; if(ttp) sb->buf[4]=len1buf+4+ttp,buf,len1); if(conp->sendHead) conp->sendTail->next=sb; else conp->sendHead=sb; conp->sendTail=sb; sb->next=0; buf+=len1; len-=len1; } return TRUE; } bool connWrite2(Connection* con, const void* buf10, int len1, const void* buf20, int len2) { u_char* buf1=(u_char*)buf10; u_char* buf2=(u_char*)buf20; ConnectionPrivate* conp=(ConnectionPrivate*)con; int ttp=conp->flags&LM_TINY_TP ? 1 : 0; if(!(conp->flags&(CST_OPENING|CST_OPEN))) return FALSE; if(len1+len2>(conp->sendSduSize ? conp->sendSduSize : conp->sendDataSize)) { birda_log("Packet is too large, discarding\n"); return FALSE; } while(len1+len2>0) { int len1x=len1sendDataSize ? len1 : conp->sendDataSize; int len2x=len2sendDataSize-len1x ? len2 : conp->sendDataSize-len1x; SendBuf* sb=makeSendBuf(len1x+len2x+4+ttp); sb->buf[2]=conp->remoteSel; sb->buf[3]=conp->localSel; if(ttp) sb->buf[4]=len2xbuf+4+ttp,buf1,len1x); memcpy(sb->buf+4+ttp+len1x,buf2,len2x); if(conp->sendHead) conp->sendTail->next=sb; else conp->sendHead=sb; conp->sendTail=sb; sb->next=0; buf1+=len1x; len1-=len1x; buf2+=len2x; len2-=len2x; } return TRUE; }