/* * Copyright (c) Martin Schoeberl, martin@jopdesign.com * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Martin Schoeberl * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * */ package ejip123.legacy; /** * Ppp.java * * communicate with jop via serial line. */ import joprt.RtThread; import ejip123.util.Dbg; import ejip123.util.Serial; import util.Timer; import ejip123.LinkLayer; import ejip123.Packet; import ejip123.PacketPool; /** * Ppp driver. */ public class Ppp extends LinkLayer{ private static final int MAX_BUF = 1500+4; // 1500 is PPP information field /** * period for thread in us. */ private static final int IP = 0x0021; // Internet Protocol packet private static final int IPCP = 0x8021; // Internet Protocol Configuration Protocol packet private static final int CCP = 0x80fd; // Compression Configuration Protocol packet private static final int LCP = 0xc021; // Link Configuration Protocol packet private static final int PAP = 0xc023; // Password Authentication Protocol packet private static final int REQ = 1; // Request options list private static final int ACK = 2; // Acknowledge options list private static final int NAK = 3; // Not acknowledge options list private static final int REJ = 4; // Reject options list private static final int TERM = 5; // Termination private static final int NEG_SEND= 3000; // Period of send negotiation (in ms) private static final int IP_SEND= 10000; // Send timout for ip for reconnect (in ms) /** * receive buffer */ private static int[] rbuf; /** * send buffer */ private static int[] sbuf; /** * bytes received. */ private static int cnt; /** * a ppp packet is in the receive buffer. */ private static boolean ready; /** * bytes to be sent. 0 means txFree */ private static int scnt; /** * allready sent bytes. */ private static int sent; /** * flag (0x7e, ~) received */ private static boolean flag; /** * number for lcp id */ private static int lcpId; /** * state machine */ private static int state; /** * reject counter */ private static int rejCnt; private static final int MAX_REJ = 5; /** * remote ip address. */ private static int ipRemote; /** * request for reconnect. */ private static boolean reconnectRequest; /** * request for a disconnect. */ private static boolean disconnectRequest; private static int connCount; /** * The one and only reference to this object. */ private static Ppp single; private static Serial ser; private static RtThread rth; /** * private constructor. The singleton object is created in init(). */ private Ppp() { } /** * allocate buffer, start serial buffer and slip Thread. */ public static LinkLayer init(int prio, int us, Serial serPort) { if (single != null) return single; // already called init() rbuf = new int[MAX_BUF]; sbuf = new int[MAX_BUF]; cnt = 0; ready = false; flag = false; scnt = 0; sent = 0; connCount = 0; lcpId = 0x11; ipRemote = 0; reconnectRequest = false; disconnectRequest = false; initStr(); ser = serPort; // new Serial(serAddr, 10, 3000); single = new Ppp(); single.setIp(0); rth = new RtThread(prio, us) { public void run() { for (;;) { waitForNextPeriod(); single.loop(); } } }; return single; } /** * Returns IP address, 0 if not connected. */ public int getIp() { if (state==CONNECTED) { return super.getIp(); } else { return 0; } } /** * Set connection strings and connect. */ public void startConnection(StringBuffer dialstr, StringBuffer connect, StringBuffer user, StringBuffer passwd) { int i, j; // System.out.println("start Conn"); // TODO correct dial string in Strecken data // copyStr(dialstr, dial); con.setLength(0); con.append("AT+CGDCONT=1,\"IP\",\""); i = connect.length(); for (j=0; j<i; ++j) con.append(connect.charAt(j)); con.append("\"\r"); // Util.copyStr(user, uid); // Util.copyStr(passwd, pwd); reconnectRequest = true; connCount = 0; setIp(0); } /** * Forces the connection to be anew established. */ public void reconnect() { // System.out.println("reconnect"); reconnectRequest = true; connCount = 0; setIp(0); } /** * Force a disconnect */ public void disconnect() { disconnectRequest = true; setIp(0); } /** * main loop. However this loop NEVER returns! * TODO: change to a loop based version to use PPP without threads. */ protected void loop() { connect(); } private static StringBuffer dial; private static StringBuffer con; private static StringBuffer uid; private static StringBuffer pwd; private static String ok; private static String connect; private static String ath; private static String pin; private static String flow; private static StringBuffer strBuf; private static void initStr() { con = new StringBuffer(40); dial = new StringBuffer(20); uid = new StringBuffer(20); pwd = new StringBuffer(20); /* we get the information from startConnection con.append("AT+CGDCONT=1,\"IP\",\"A1.net\"\r"); uid.append("ppp@A1plus.at"); pwd.append("ppp"); */ dial.append("ATD*99***1#\r"); String client= "CLIENT"; String ver= "VER"; ok = "OK"; connect = "ECT"; ath = "|+++|ATH\r"; pin = "AT+CPIN=5644\r"; flow = "AT\\Q3\r"; strBuf = new StringBuffer(40); // int [] s7 = { 'A', 'T', '+', 'C', 'G', 'D', 'C', 'O', 'N', 'T', '=', '1', ',', '"', 'I', 'P', '"', ',', '"', 'w', 'e', 'b', '.', 'o', 'n', 'e', '.', 'a', 't', '"', '\r' }; /* A1 */ // int [] s7 = { 'A', 'T', '+', 'C', 'G', 'D', 'C', 'O', 'N', 'T', '=', '1', ',', '"', 'I', 'P', '"', ',', '"', 'A', '1', '.', 'n', 'e', 't', '"', '\r' }; /* OEBB VPN */ /* int [] s7 = { 'A', 'T', '+', 'C', 'G', 'D', 'C', 'O', 'N', 'T', '=', '1', ',', '"', 'I', 'P', '"', ',', '"', 'o', 'e', 'b', 'b', '.', 'A', '1', '.', 'n', 'e', 't', '"', '\r' }; */ /* int [] s8 = { 'A', 'T', 'D', '*', '9', '9', '*', '*', '*', '1', '#', '\r' }; int [] s9 = { 'A', 'T', '\\', 'Q', '3', '\r' }; */ } /** * wait seconds and drop IP packets */ void waitSec(int t) { int timer = Timer.getTimeoutMs(1000); for (int i=0; i<t; ++i) { while (!Timer.timeout(timer)) { rth.waitForNextPeriod(); } timer = Timer.getTimeoutMs(1000); dropIp(); } } /** * send a string to serial line buffer. * '|' has special meaning: wait one second. */ boolean wrString(StringBuffer s) { int i, j, k, val; i = ser.txFreeCnt(); j = s.length(); if (j>i) return false; Dbg.wr('\''); for (i=0; i<j; ++i) { val = s.charAt(i); if (val=='|') { waitSec(2); // for shure if send buffer is full } else { ser.wr(val); Dbg.wr(val); } } Dbg.wr('\''); Dbg.wr('\n'); return true; } boolean sendWait(String snd, String rcv, int timeout) { strBuf.setLength(0); strBuf.append(snd); return sendWait(strBuf, rcv, timeout); } /** * send a string and loop until rcv string arrives or it times out * timeout in seconds for receive string, is also used for send (if handshake lines are not set) * return false means timeout. */ boolean sendWait(StringBuffer snd, String rcv, int timeout) { timeout *= 1000; // // send string // int timer = Timer.getTimeoutMs(timeout); // use same timeout for send while (!wrString(snd)) { rth.waitForNextPeriod(); // wait till send buffer is free dropIp(); if (Timer.timeout(timer)) return false; // timeout on send means problem with handshake lines } if (rcv==null) return true; // no wait string, we're done int ptr = 0; int len = rcv.length(); // // now wait on response string // for (timer = Timer.getTimeoutMs(timeout); !Timer.timeout(timer); ) { rth.waitForNextPeriod(); dropIp(); for (int i = ser.rxCnt(); i>0; --i) { int val = ser.rd(); Dbg.wr(val); if (val == rcv.charAt(ptr)) { ++ptr; if (ptr==len) { Dbg.wr('\n'); waitSec(1); return true; // we're done } } else { ptr = 0; // reset match pointer } } } Dbg.wr('?'); Dbg.wr('\n'); return false; // timeout expired } private static int globTimer; // negotion send and ip-restart timer private static boolean lcpAck; private static boolean ipcpAck; /** * do the modem stuff till CONNECT */ void modemInit() { ++connCount; for (;;++connCount) { System.out.print("Modem init "); System.out.println(connCount); if (sendWait(ath, ok, 3)) { if (sendWait(flow, ok, 3)) { if (!sendWait(con, ok, 2)) { // when ERROR PIN is not set sendWait(pin, ok, 30); if (!sendWait(con, ok, 20)) { continue; // something really strange happend! } } if (sendWait(dial, connect, 10)) { break; } } } waitSec(1); } state = MODEM_OK; globTimer = Timer.getTimeoutMs(NEG_SEND); lcpAck = false; ipcpAck = false; } /** * cancel connection. */ void modemHangUp() { // System.out.print("Modem hangup "); // System.out.println(connCount); setIp(0); // stop sending ip data reconnectRequest = false; disconnectRequest = false; state = INIT; rejCnt = 0; // flush buffer for (int i = ser.rxCnt(); i>0; --i) { ser.rd(); } for (;;) { if (sendWait(ath, ok, 3)) { break; } waitSec(3); } } private static final int INIT = 0; private static final int MODEM_OK = 1; private static final int LCP_SENT = 2; private static final int LCP_OK = 3; private static final int PAP_SENT = 4; private static final int PAP_OK = 5; private static final int IPCP_SENT = 6; private static final int IPCP_SENT2 = 7; private static final int IPCP_OK = 8; private static final int CONNECTED = 9; /** * establish a connetcion. */ void connect() { state = INIT; rejCnt = 0; lcpAck = false; ipcpAck = false; // // wait for startConnection(...) // while (!reconnectRequest) { rth.waitForNextPeriod(); } reconnectRequest = false; // // start the modem // modemInit(); // // now LCP negotiation // for (;;) { rth.waitForNextPeriod(); pppLoop(); if (state==MODEM_OK) { } if (rejCnt > MAX_REJ) { // System.out.print("1"); modemHangUp(); // start over modemInit(); } if (ready && scnt==0) { // one packet is read and send buffer is free dbgCon(); int prot = (rbuf[2]<<8)+rbuf[3]; int code = rbuf[4]; if (prot == LCP) { if (code == REQ) { if (checkOptions(LCP)) { lcpAck = true; } else { ++rejCnt; } } else if (code==ACK && rbuf[5]==lcpId) { state = LCP_OK; } else if (code==TERM) { // System.out.print("2"); modemHangUp(); // start over modemInit(); } } else if (prot == PAP) { if (rbuf[4]==ACK && rbuf[5]==lcpId) { state = PAP_OK; } } else if (prot == IPCP) { if (code == REQ) { if (checkOptions(IPCP)) { ipcpAck = true; } } else if (code == NAK) { // with this NAK we will get our IP address setIp((rbuf[10]<<24) + (rbuf[11]<<16) + (rbuf[12]<<8) + rbuf[13]); dbgIp(super.getIp()); makeIPCP(); state = IPCP_SENT2; } else if (code == ACK) { state = IPCP_OK; // nothing more to do ? state = CONNECTED; Dbg.wr('C'); Dbg.wr('\n'); } // // des is net guat: kommt nur hierher, wenn der Sendbuffer frei ist!!! // } else if (prot == IP) { // we finally got an ip packet :-) readIp(); } cnt = 0; ready = false; } doSend(); } } void doSend() { if (reconnectRequest) { // System.out.print("3"); modemHangUp(); // start over modemInit(); } if (disconnectRequest) { modemHangUp(); // stop the connection } if (state==CONNECTED) { // send waiting ip packets if (scnt==0) { // transmit buffer is free globTimer = Timer.getTimeoutMs(IP_SEND); // use IP timeout // // get a ready to send packet with source from this driver. // Packet p = PacketPool.getTxPacket(single); if (p!=null) { sendIp(p); // send one packet } } else { // check sendTimer; if (Timer.timeout(globTimer)) { // System.out.print("4"); modemHangUp(); // start over modemInit(); } } } else { // do the negotiation stuff dropIp(); if (Timer.timeout(globTimer)) { /* Dbg.intVal(state); if (lcpAck) Dbg.wr('t'); else Dbg.wr('f'); */ if (scnt==0) { // once every three seconds send a REQ if (state == MODEM_OK) { makeLCP(); state = LCP_SENT; } else if (state == LCP_OK && lcpAck) { makePAP(); state = PAP_SENT; // } else if (state == PAP_OK && ipcpAck) { // wait for remote ipcp and ACK first on Linux } else if (state>=PAP_OK && state<CONNECTED) { // ONE makeIPCP(); state = IPCP_SENT; ++rejCnt; // incremenet counter to start over when no respond } globTimer = Timer.getTimeoutMs(NEG_SEND); // use negotiation timeout } } } } /** * drop waiting packet to prevent packet buffer overrun */ void dropIp() { // TODO: what shall we do with CON_RDY packets? Packet p = PacketPool.getTxPacket(single); if (p!=null) { p.free(); // mark packet free } } void dbgCon() { if (state!=CONNECTED) { Dbg.wr('>'); for (int i=0; i<cnt; ++i) { Dbg.byteVal(rbuf[i]); if ((i&0x0f) ==0) rth.waitForNextPeriod(); } Dbg.wr('\n'); } } void dbgIp(int ip) { Dbg.wr('I'); Dbg.wr(' '); Dbg.intVal(ip>>>24); Dbg.intVal((ip>>>16)&0xff); Dbg.intVal((ip>>>8)&0xff); Dbg.intVal(ip&0xff); Dbg.wr('\n'); } /** * generate a PPP (negotiation) request. */ void makeLCP() { lcpId = 0x22; Dbg.wr('L'); Dbg.intVal(lcpId); Dbg.wr('\n'); sbuf[0] = 0xff; sbuf[1] = 0x03; // REQ LCP options 2, 7, 8 sbuf[2] = LCP>>8; sbuf[3] = LCP&0xff; sbuf[4] = REQ; sbuf[5] = lcpId; sbuf[6] = 0; sbuf[7] = 18-4; // length including code, id and length field sbuf[8] = 0x02; // async-map sbuf[9] = 0x06; sbuf[10] = 0x00; sbuf[11] = 0x0a; // sbuf[11] = 0x00; // one does not like this sbuf[12] = 0x00; sbuf[13] = 0x00; sbuf[14] = 0x07; // protocol field compression sbuf[15] = 0x02; sbuf[16] = 0x08; // addr., contr. field compression sbuf[17] = 0x02; checksum(18); } void makePAP() { int i; lcpId = 0x33; Dbg.wr('P'); Dbg.intVal(lcpId); Dbg.wr('\n'); /* compression sbuf[0] = 0xff; sbuf[1] = 0x03; */ sbuf[0] = PAP>>8; sbuf[1] = PAP&0xff; sbuf[2] = REQ; sbuf[3] = lcpId; sbuf[4] = 0; int ulen = uid.length(); int plen = pwd.length(); sbuf[5] = ulen + plen + 6; // length including code, id and length field sbuf[6] = ulen; // length of user id for (i=0; i<ulen; ++i) { sbuf[7+i] = uid.charAt(i); } sbuf[7+ulen] = plen; for (i=0; i<plen; ++i) { sbuf[8+ulen+i] = pwd.charAt(i); } checksum(ulen + plen + 8); } void makeIPCP() { lcpId = 0x44; Dbg.wr('I'); Dbg.intVal(lcpId); Dbg.wr('\n'); /* compression sbuf[0] = 0xff; sbuf[1] = 0x03; */ sbuf[0] = IPCP>>8; sbuf[1] = IPCP&0xff; sbuf[2] = REQ; sbuf[3] = lcpId; sbuf[4] = 0; sbuf[5] = 14-4; // length including code, id and length field sbuf[6] = 0x03; // ip-address 0.0.0.0 sbuf[7] = 0x06; sbuf[8] = super.getIp()>>>24; sbuf[9] = (super.getIp()>>16)&0xff; sbuf[10] = (super.getIp()>>8)&0xff; sbuf[11] = super.getIp()&0xff; // checksum(14); checksum(12); } /** * process a LCP, IPCP request */ boolean checkOptions(int type) { int i; int len = (rbuf[6]<<8) + rbuf[7] - 4; // including code, id and lentgh int ptr = 8; Dbg.wr('R'); Dbg.wr(' '); int resp = ACK; for (i=0; i<cnt; ++i) sbuf[i] = rbuf[i]; // assume ACK int slen = len + 4; while (len > 0) { int opt = rbuf[ptr]; Dbg.intVal(opt); if (type==LCP && opt==3) { // auth. protocol if ((rbuf[ptr+2]<<8) + rbuf[ptr+3] != PAP) { resp = REJ; Dbg.wr('!'); Dbg.wr('P'); Dbg.wr(' '); } } else if (type==IPCP) { if (opt==2) { // IP-Compression resp = REJ; } else if (opt==3) { // IP-address ipRemote = (rbuf[ptr+2]<<24) + (rbuf[ptr+3]<<16) + (rbuf[ptr+4]<<8) + rbuf[ptr+5]; Dbg.hexVal(ipRemote); dbgIp(ipRemote); } } // } else if (opt==xx} if (resp==REJ) { int optlen = rbuf[ptr+1]; slen = 4 + optlen; for (i=0; i<optlen; ++i) { sbuf[8+i] = rbuf[ptr+i]; } break; // end check options } i = rbuf[ptr+1]; // check next option ptr += i; len -= i; } sbuf[4] = resp; sbuf[6] = slen>>>8; sbuf[7] = slen&0xff; Dbg.wr('\n'); checksum(slen+4); return resp == ACK; } /** * get a Packet buffer and copy from receive buffer. */ void readIp() { int i, j, k; Packet p = null;//PacketPool.getFreshRcvPacket(single); if (p==null) { Dbg.wr('!'); return; // try again later } // buf blocks receive buffer :-< int[] pb = p.buf; cnt -= 6; // minus ppp header and checksum rbuf[cnt+4] = 0; rbuf[cnt+4+1] = 0; rbuf[cnt+4+2] = 0; // copy buffer k = 0; for (i=0; i<cnt; i+=4) { for (j=0; j<4; ++j) { k <<= 8; k += rbuf[i+j+4]; // after header } pb[i>>>2] = k; } p.setLen(cnt); Dbg.wr('r'); Dbg.intVal(cnt); /* dbgIp(pb[3]); dbgIp(pb[4]); for (i=0; i<(cnt+4)>>2; ++i) Dbg.hexVal(pb[i]); Dbg.wr('\n'); */ cnt = 0; ready = false; p.setStatus(Packet.RCV); // inform upper layer } /** * copy packet to send buffer. */ void sendIp(Packet p) { int i, k; int[] pb = p.buf; Dbg.wr('s'); Dbg.intVal(p.len()); sbuf[0] = 0xff; sbuf[1] = 0x03; sbuf[2] = IP>>8; sbuf[3] = IP&0xff; int slen = p.len(); sent = 0; for (i=0; i<slen; i+=4) { k = pb[i>>>2]; sbuf[i+4] = k>>>24; sbuf[i+4+1] = (k>>>16)&0xff; sbuf[i+4+2] = (k>>>8)&0xff; sbuf[i+4+3] = k&0xff; } if (p.status()== Packet.CON_RDY) { p.setStatus(Packet.CON_ONFLY); // mark on the fly } else { p.free(); // mark packet free } checksum(slen+4); } /* warum geht das nicht !!!!! private void loop() { */ /** * read from serial buffer and build a ppp packet. * send a packet if one is in our send buffer. */ boolean pppLoop() { int i; boolean ret = false; i = ser.rxCnt(); if (i!=0 && !ready) { ret = true; rcv(i); } if (scnt!=0) { i = ser.txFreeCnt(); if (i>2) { snd(i); } } return ret; } /** * copy from send buffer to serial buffer with flags and escapes. */ void snd(int free) { int i; if (sent==0) { ser.wr('~'); --free; } for (i=sent; free>1 && i<scnt; ++i) { int c = sbuf[i]; /* no hard code if (state >= LCP_OK) { // hard code async map if (c=='~' || c=='}' || c==17 || c==19) { // 0x000a0000 async map ser.wr('}'); ser.wr(c ^ ' '); free -= 2; } else { ser.wr(c); --free; } } else { */ if (c=='~' || c=='}' || c<0x20) { // c<0x20 could be omitted after LCP async map ser.wr('}'); ser.wr(c ^ ' '); free -= 2; } else { ser.wr(c); --free; } /* } */ } sent = i; if (sent==scnt && free!=0) { ser.wr('~'); scnt = 0; sent = 0; } } private static boolean escape; private static int fcs; /** * copy from serial buffer to receive buffer. * calc CRC on the fly. */ void rcv(int len) { int i; if (cnt==0) fcs = 0xffff; // get all bytes from serial buffer for (i=0; i<len && cnt<MAX_BUF; ++i) { int val = ser.rd(); if (cnt==0 && !flag && val!='~') { // wait for a packet start escape = false; // first data byte is not an escape Dbg.wr('d'); continue; // so don't worry about '~' escapes on cnt==0 } if (!escape && val=='~') { flag = true; // remember flag, because end flag and if (cnt!=0) { // starting flag can be the same (see rfc1549) if (fcs==0xf0b8) { // checksum ok? ready = true; } else { Dbg.wr('d'); Dbg.intVal(cnt); Dbg.wr('\n'); cnt = 0; // just drop it } break; } continue; } else { flag = false; } if (!escape && val=='}') { escape = true; continue; } if (escape) { val ^= ' '; escape = false; } // rfc1549 3.2 Address-and-Control-Field-Compression if (cnt==0 && val!=0xff) rbuf[cnt++] = 0xff; if (cnt==1 && val!=0x03) rbuf[cnt++] = 0x03; // rfc1548 6.6 Protocol-Field-Compression if (cnt==2 && (val&1)!=0) rbuf[cnt++] = 0x00; rbuf[cnt++] = val; fcs = check(val^fcs) ^ (fcs>>8); } /* Dbg.wr('r'); Dbg.intVal(cnt); Dbg.wr('\n'); */ } /** * calculate CRC of byte c for checksum */ int check(int c) { c &= 0xff; for (int i=0; i<8; ++i) { if ((c&1) != 0) { c >>= 1; c ^= 0x8408; } else { c >>= 1; } } return c; } /** * calculate CRC for send packet and mark it ready to send. */ void checksum(int len) { int k, j, i; int fcs = 0xffff; for (i=0; i<len; ++i) { j = sbuf[i]; j ^= fcs & 0xff; // only low order byte for (k=0; k<8; ++k) { if ((j&1) != 0) { j >>= 1; j ^= 0x8408; } else { j >>= 1; } } fcs = j ^ (fcs>>8); } fcs = fcs ^ 0xffff; sbuf[len] = fcs & 0xff; // LSB first ! sbuf[len+1] = fcs >> 8; scnt = len+2; if (state!=CONNECTED) { Dbg.wr('<'); for (i=0; i<scnt; ++i) { Dbg.byteVal(sbuf[i]); if ((i&0x0f) ==0) rth.waitForNextPeriod(); } Dbg.wr('\n'); } } public int getConnCount() { return connCount; } }