Oberon/ETH Oberon/PPPLCP.Mod

(* ETH Oberon, Copyright 1990-2003 Computer Systems Institute, ETH Zurich, CH-8092 Zurich.
Refer to the license.txt file provided with this distribution. *)

MODULE PPPLCP;	(** non-portable *)
(* $VCS   4, Edgar@EdgarSchwarz.de, 9 May :0, 1:20:35 $
    $Log$
$   4, Edgar@EdgarSchwarz.de, 9 May :0, 1:20:35
more debug info with auth stuff
$   3, Edgar@EdgarSchwarz.de, 25 Apr :0, 10:47:30
more debug info, drop Identification code
$   2, Edgar.Schwarz@z.zgs.de, 5 Sep 99, 13:49:32
minimal length for echo request set to 4 bytes
$   1, Edgar.Schwarz@z.zgs.de, 28 Feb 99, 22:19:22
version for PPP 1.0.0
*)
IMPORT 
	FSM := PPPFSM, PAP := PPPPAP, HDLC := PPPHDLC, T := PPPTools, 
	Debug := PPPDebug, SYSTEM;

CONST
	(* Protocol Constants *)
	DefMRU = HDLC.MTU; MinMRU = 128; MaxMRU = HDLC.MTU;
	WarnLoops = 10; 				(* Warn about loop-backs this often *)
	
	LCP*=-03FDFH; (* =0C021H *)

	(* Configure Information Options *)
	Illegal0 = 0;					(*es some PPP implementations send that *)
	MRU = 1;						(* Maximum Receive Unit *)
	AsyncMap = 2;				(* Async Control Character Map *)
	AuthType = 3;				(* Authentication Protocol *)
	Quality = 4;					(* Quality Protocol *)
	MagicNumber = 5;
	(* =  6; *)                       (*es RESERVED*)
	PCompression = 7;		(* Protocol Field Compression *)
	ACCompression = 8;		(* Address And Control Field Compression *)
	(*es extensions from RFC1570 Jan 1994 *)
	(* = 9; *)                      (*es FCSAlternatives *)
	(* = 10; *)                    (*es Self Describing Padding *)
	(* = 13; *)                    (*es Callback *)
	(* = 15; *)                    (*es Compound Frames *)
	LCPConf17 = 17;           (*es* ? *)
	LCPConf19 = 19;           (*es* ? *)

	(* LCP codes *)
	ProtocolRej = 8;	EchoReq = 9;	EchoReply = 10;	DiscardReq = 11;
	(*es*)Identification = 12; TimeRemaining = 13; (**)
	
	(* Options Index *)
	Passive* = FSM.Passive;	(* Don't die if we get no response *)
	Silent* = FSM.Silent;		(* Wait for the other end to start first *)
	NegMRU* = 8; NegAsyncMap* = 9; NegUPap* = 10;	
	(* Negotiate MRU, Async Map, User/Password *)
	NegChap = 11; NegMagicNumber* = 12; NegPComp = 13; 		
	(* Crypt-Auth, MagicNumber, PCompression *)
	NegACComp = 14; NegLqr = 15;													
	(* AC-Compr., Link Quality Reports *)
	
	(* Length of the Configuration Options *)
	VoidLen = 2; ShortLen = 4; ChapLen = 5; LongLen = 6; LqrLen = 8;  	
	(* = Type (1 Byte) + Length (1 Byte) + Data *) 
	
TYPE
	Options*=RECORD
		O*: SET								(* Bits defined by Options Index *);	
		MRU*:INTEGER;					(* Max. Receive Unit *)
		MagicNumb*:LONGINT; NumLoops*:INTEGER;		(* Magic Number Stuff *)
		AsyncMap*:SET;
	END;
	
	LCPfsm*=POINTER TO LCPfsmDesc;
	LCPfsmDesc=RECORD (FSM.FSMDesc)
		wo*, go(*es*)*(*-*),ao*, ho(*es*)*(*-*): Options;
		(* WantOptions, GotOptions, AllowOptions, HisOptions *)
	END;
		
VAR	
	(* Upcalls to PPP *)
	PPPHandleLCPUp*: PROCEDURE (U:HDLC.PPPUnit);
	PPPHandleLCPDown*: PROCEDURE (U:HDLC.PPPUnit); 
	PPPHandleProtRej*: PROCEDURE (U:HDLC.PPPUnit; prot:INTEGER);
	
	(* Open - LCP can come up *)
	PROCEDURE Open* (f: LCPfsm);
	BEGIN	f.Flags:=f.wo.O*{Passive, Silent};
		FSM.Open(f);
	END Open;
	
	(* Close - Take LCP down *)
	PROCEDURE Close* (f: LCPfsm);
	BEGIN
		IF (f.State=FSM.Stopped) & (f.Flags*{Passive, Silent} # {}) THEN	f.State:=FSM.Closed
		ELSE	FSM.Close(f)
		END
	END Close;

	(* LowerUp - The Lower Layer is Up *)
	PROCEDURE LowerUp* (f: LCPfsm);
	BEGIN
		f.HDLCConfig.MTU:=DefMRU; f.HDLCConfig.SendAsyncMap:={0..31}; 
		(* Send Configuration *)
		
		f.HDLCConfig.MRU:=DefMRU; (* Receive Configuration *)
		f.ao.AsyncMap:={}; 
		FSM.LowerUp(f)
	END LowerUp;
	
	(* LowerDown - The Lower Layer is Down *)
	PROCEDURE LowerDown* (f: LCPfsm); 
	BEGIN FSM.LowerDown(f) END LowerDown;
	
	(* Input - New LCP Packet *)
	PROCEDURE Input* (f: LCPfsm; VAR p: ARRAY OF CHAR; pos, len:INTEGER); 
	BEGIN FSM.Input(f, p, pos, len); END Input;
	
	(* SendProtRej - Send a Protocol Reject *)
	PROCEDURE SendProtRej* (f: LCPfsm; VAR p: ARRAY OF CHAR; pos, len:INTEGER);
	BEGIN
		INC(f.ID); FSM.SendData(f, ProtocolRej, f.ID, p, pos+2, len-2); 
	END SendProtRej;

	(* ResetCI - Reset The Configuration Information *)
	PROCEDURE *ResetCI (f: FSM.FSM);
	BEGIN
		IF HDLC.debug THEN Debug.String("reset CI"); Debug.Ln; END;
		WITH f:LCPfsm DO
			f.wo.MagicNumb:=T.Magic(); f.wo.NumLoops:=0;		(* WantOptions *)
			f.go:=f.wo;		(* GotOptions := WantOptions *)
			f.HDLCConfig.MTU:=HDLC.MTU			(* Max. Transmit Unit *)
		END
	END ResetCI;
	
	(* CILen - Returns Length of the Conf. Inf. *)
	PROCEDURE *CILen (f: FSM.FSM): INTEGER;
	VAR i: INTEGER;
	BEGIN i:=0;
		WITH f:LCPfsm DO
			IF (NegMRU IN f.go.O) THEN INC(i, ShortLen); END;
			IF (NegAsyncMap IN f.go.O) THEN INC(i, LongLen); END;
			IF (NegMagicNumber IN f.go.O) THEN INC(i, LongLen); END;	
			(* To update if more Options supported *)
		END;
		RETURN i 
	END CILen;
	
		(* AddCI  - Add our desired Conf. Inf. to a packet (at pos) *)
	PROCEDURE *AddCI (f: FSM.FSM; VAR p: ARRAY OF CHAR; pos: INTEGER; VAR len: INTEGER);
	BEGIN len:=0;
		IF HDLC.debug THEN FSM.OutLog("LCP.AddCI", "", f); END;
		WITH f:LCPfsm DO
			IF (NegMRU IN f.go.O) THEN INC(len, ShortLen); IF HDLC.debug THEN Debug.String("MRU "); END;
				p[pos]:=CHR(MRU); p[pos+1]:=CHR(ShortLen); T.PutInt(f.go.MRU, p, pos+2); INC(pos, ShortLen);
			END;
			IF (NegAsyncMap IN f.go.O) THEN INC(len, LongLen); IF HDLC.debug THEN Debug.String("AsyncMap "); END;
				p[pos]:=CHR(AsyncMap); p[pos+1]:=CHR(LongLen); T.PutSet(f.go.AsyncMap, p, pos+2); INC(pos, LongLen);
			END;
			IF (NegMagicNumber IN f.go.O) THEN INC(len, LongLen); IF HDLC.debug THEN Debug.String("MagicNumber "); END;
				p[pos]:=CHR(MagicNumber); p[pos+1]:=CHR(LongLen);
				T.PutLong(f.go.MagicNumb, p, pos+2); INC(pos, LongLen)
			END
		END;																							(* To update if more Options supported *)
		IF HDLC.debug THEN Debug.Ln; Debug.Ln; END
	END AddCI;
	
	(* AckCI - An Ack Packet has been received as answer to our req *)
	PROCEDURE *AckCI (f: FSM.FSM; VAR p: ARRAY OF CHAR; pos, len: INTEGER): BOOLEAN;
	VAR b:BOOLEAN;
	BEGIN b:=TRUE;
		WITH f:LCPfsm DO				(* No changes to our former req are allowed, otherwise the ack is bad *)
			IF (NegMRU IN f.go.O) THEN
				b:= b & (len>=ShortLen) & (ORD(p[pos])=MRU) & (ORD(p[pos+1])=ShortLen) & (T.GetInt(p, pos+2)=f.go.MRU);
				DEC(len, ShortLen); INC(pos, ShortLen);
			END;
			IF (NegAsyncMap IN f.go.O) THEN
				b:=b & (len>=LongLen) & (ORD(p[pos])=AsyncMap) & (ORD(p[pos+1])=LongLen) 
						& (T.GetSet(p, pos+2)=f.go.AsyncMap);
				DEC(len, LongLen); INC(pos, LongLen);
			END;
			IF (NegMagicNumber IN f.go.O) THEN
				b:=b & (len>=LongLen) & (ORD(p[pos])=MagicNumber) & (ORD(p[pos+1])=LongLen)
					& (T.GetLong(p, pos+2)=f.go.MagicNumb);
				DEC(len, LongLen); INC(pos, LongLen);
			END;
			(* To update if more Options supported *)
			b:=b & (len=0);
			IF HDLC.debug & ~b THEN Debug.String("Received bad Ack!!"); Debug.Ln END;
			RETURN b
		END
	END AckCI; 
	
	(* NakCI - A Nak Packet has been received as answer to our req *)
	PROCEDURE *NakCI (f: FSM.FSM; VAR p: ARRAY OF CHAR; pos, len: INTEGER): BOOLEAN;
	VAR no, try: Options;	(* no: Options with Naks, try: Options we try next time *)
		x, type, size:INTEGER; loopedback, b:BOOLEAN;
	BEGIN
		WITH f:LCPfsm DO				(* same order as we sent it, but only the nak'd ones *)
			no.O:={}; try.O:=f.go.O;
			IF (NegMRU IN f.go.O) & (len>=ShortLen) & (ORD(p[pos])=MRU) & (ORD(p[pos+1])=ShortLen) THEN 
				x:=T.GetInt(p, pos+2); IF (x<DefMRU) THEN no.MRU:=x; try.MRU:=x; END;		(* Accept New Option *)
				INCL(no.O, NegMRU); DEC(len, ShortLen); INC(pos, ShortLen);
			END;
			IF (NegAsyncMap IN f.go.O) & (len>=LongLen) & (ORD(p[pos])=AsyncMap) & (ORD(p[pos+1])=LongLen) THEN
				INCL(no.O, NegAsyncMap); try.AsyncMap:=try.AsyncMap + T.GetSet(p, pos+2);
				DEC(len, LongLen); INC(pos, LongLen); 	(* Include New Chars (our receive side)*)
			END;
			IF (NegMagicNumber IN f.go.O) & (len>=LongLen) & (ORD(p[pos])=MagicNumber) & (ORD(p[pos+1])=LongLen) THEN
				INCL(no.O, NegMagicNumber); try.MagicNumb:=T.Magic(); INC(try.NumLoops); loopedback:=TRUE;
				DEC(len, LongLen); INC(pos, LongLen);
			END;
			b:=TRUE;						(* there may be remaining options , but no changes possible ! (Just checking if Nak is ok) *)
			WHILE b & (len>VoidLen) DO
				type:=ORD(p[pos]); size:=ORD(p[pos+1]);
				CASE type OF
					  MRU: b:=((size=ShortLen) & ~(NegMRU IN f.go.O) & ~(NegMRU IN no.O));
					| AsyncMap: b:=((size=LongLen) & ~(NegAsyncMap IN f.go.O) & ~(NegAsyncMap IN no.O)); 
					| MagicNumber: b:=((size=LongLen) & ~(NegMagicNumber IN f.go.O) & ~(NegMagicNumber IN no.O));
					| PCompression, ACCompression: b:=(size=VoidLen);
					| Quality: b:=(size=LqrLen);
					| AuthType: b:=((size=ChapLen) OR (size=ShortLen));
				ELSE	(* probably an unknown Configuration Option *)
				END;
				INC(pos, size);
			END;
			b:=b & (len=0);
			
			IF b & (f.State#FSM.Opened) THEN f.go:=try;		 (* Update State *)
				IF loopedback & ((try.NumLoops MOD WarnLoops) = 0) THEN 
					Debug.String("This line could be looped back!!"); Debug.Ln; 
				END;
			END;
			RETURN b
		END
	END NakCI;
	
	(* RejCI - A Reject Packet has been received as answer to our req*)
	PROCEDURE *RejCI (f: FSM.FSM; VAR p: ARRAY OF CHAR; pos, len: INTEGER): BOOLEAN;
	VAR try: Options; (* try: Option to request next time *)
	BEGIN
		WITH f:LCPfsm DO	(* Look which options were rejected. Same order and same value! Otherwise a bad packet. *)
			try:=f.go;
			IF (NegMRU IN f.go.O) & (len>=ShortLen) & (ORD(p[pos])=MRU) & (ORD(p[pos+1])=ShortLen)
				& (T.GetInt(p, pos+2)=f.go.MRU) THEN
					DEC(len, ShortLen); INC(pos, ShortLen); EXCL(try.O, NegMRU);
			END;				
			IF (NegAsyncMap IN f.go.O) & (len>=LongLen) & (ORD(p[pos])=AsyncMap) & (ORD(p[pos+1])=LongLen)
				& (T.GetSet(p, pos+2)=f.go.AsyncMap) THEN
					DEC(len, LongLen); INC(pos, LongLen); EXCL(try.O, NegAsyncMap);
			END;
			IF (NegMagicNumber IN f.go.O) & (len>=LongLen) & (ORD(p[pos])=MagicNumber) & (ORD(p[pos+1])=LongLen)
				& (T.GetLong(p, pos+2)=f.go.MagicNumb) THEN
				DEC(len, LongLen); INC(pos, LongLen); EXCL(try.O, NegMagicNumber);
			END;
			
			IF len#0 THEN IF HDLC.debug THEN Debug.String("Received bad Reject !"); END; RETURN FALSE
			ELSE	IF f.State#FSM.Opened THEN	f.go:=try; END;	(* Update the Options *)
				RETURN TRUE
			END
		END
	END RejCI;
	
	(* ReqCI - Conf. Request has arrived: Check the requested CIs and send 
		appropriate response
		Returns:  ConfigureAck, ConfigureNak or ConfigureReject and modified 
		packet (p, pos, length in len)
		If Mode is true, always send Reject, never Nak *)	
	PROCEDURE ReqCI (f: FSM.FSM; VAR p: ARRAY OF CHAR; 
							VAR pos, len: INTEGER; Mode: BOOLEAN): SHORTINT;
	CONST Ack=0; Nak=1; Rej=2; RejAll=3;
	VAR type, size, posp, posw, lenp, x: INTEGER;
		y: LONGINT; eo, s: SET; (* eo: Error-Options: Options to Nak/Rej *)
		status: SHORTINT;  (* Ack: Can Ack, Nak: Should Nak,  Rej: reject of
			some options needed, RejAll: serious reject: rej whole packet *)
	BEGIN eo:={};
		 (* Two steps: 
		 First find out if packet can be accepted, or have to be naked or rejected 
		 Second step: If nak, then modify (better proposals), 
		 	if rej then send back same packet *)
		WITH f:LCPfsm DO
			lenp:=len; posp:=pos; status:=Ack; f.ho.O:={};
			WHILE (status#RejAll) & (lenp>=VoidLen) DO
				size:=ORD(p[posp+1]);
				IF (size<VoidLen) OR (size>lenp) THEN
				ELSE type:=ORD(p[posp]);
					CASE type OF
						  (*es reject 0 if a bad peer sends it *)
						  Illegal0: status:=Rej; INCL(eo, Illegal0);
							IF HDLC.debug THEN Debug.String("RejIllegal0 "); END;
						  | (**) MRU: INCL(f.ho.O, NegMRU);
							IF (NegMRU IN f.ao.O) & (size=ShortLen) THEN 
								x:=T.GetInt(p, posp+2); f.ho.MRU:=x;
								IF (x<MinMRU) THEN
									(* Minimal size needed: we have to change ->NAK *)
									INCL(eo, MRU); 
									IF status=Ack THEN status:=Nak; END;
								END;
							ELSE
								IF HDLC.debug THEN Debug.String("RejMRU "); END;
								status:=Rej; INCL(eo, MRU);
							END;
						| AsyncMap: INCL(f.ho.O, NegAsyncMap);
							IF (NegAsyncMap IN f.ao.O) & (size=LongLen) THEN 
								s:=T.GetSet(p, posp+2); f.ho.AsyncMap:=s;
								IF (f.ao.AsyncMap * (s/{0..31} (* = ~s *)) # {} ) THEN 
									(* at least our options needed *)
									INCL(eo, AsyncMap); 
									IF status=Ack THEN status:=Nak; END;
								END;
							ELSE 
								IF HDLC.debug THEN Debug.String("RejAsyncMap "); END;
								status:=Rej; INCL(eo, AsyncMap); 
							END;
						| MagicNumber: INCL(f.ho.O, NegMagicNumber);
							IF (NegMagicNumber IN f.ao.O) & (size=LongLen) THEN 
								y:=T.GetLong(p, posp+2); f.ho.MagicNumb:=y;
								IF(NegMagicNumber IN f.go.O)&(y=f.go.MagicNumb)THEN 	
									(* MagicNumber MUST be different *)
									INCL(eo, MagicNumber); 
									IF status=Ack THEN status:=Nak; END;
								END;
							ELSE 
								IF HDLC.debug THEN Debug.String("RejMagic "); END;
								status:=Rej; INCL(eo, MagicNumber);
							END;
						| AuthType:
(*es*)				  IF HDLC.debug THEN
								Debug.String("LCP.ReCI: AuthType"); Debug.Ln;
(**)	 				END;
							IF size>=ShortLen THEN x := T.GetInt(p, posp+2);
(*es*)				  	IF HDLC.debug THEN
									Debug.String("x ="); Debug.Int(x, 3); Debug.Ln;
(**)	 					END;
								IF x = PAP.PAPProt THEN (*UPap *)
									IF (NegUPap IN f.ao.O) & (size >= ShortLen) THEN 
										(* dm:1.10.96: size = ShortLen *)
										INCL(f.ho.O, NegUPap);
(*es*)							  IF HDLC.debug THEN
											Debug.String("LCP.ReCI: NegUPap added to his options"); Debug.Ln;
(**)	 							END;
									ELSE
(*es*)							  IF HDLC.debug THEN
											Debug.String("RejNegUPap "); Debug.Int(size,5); Debug.Int(ShortLen,5); Debug.Ln;
(**)	 							END;
										status := Rej; INCL(eo, AuthType);
									END
								ELSE 
(*es*)				  		Debug.String("LCP.ReCI: AuthType CHAP not supported"); Debug.Ln;
									INCL(f.ho.O, NegChap); INCL(eo, AuthType);
									IF status = Ack THEN status := Nak END;
								END
							ELSE
								IF HDLC.debug THEN Debug.String("RejAuth "); END;
								status := Rej; INCL(eo, AuthType);
							END;
							Debug.Ln
						| PCompression: INCL(f.ho.O, NegPComp); status:=Rej; 
							IF HDLC.debug THEN Debug.String("RejPFComp "); END;
							INCL(eo, PCompression);	(* not supported *)
						| ACCompression: INCL(f.ho.O, NegACComp); status:=Rej; 
							IF HDLC.debug THEN Debug.String("RejACComp "); END;
							INCL(eo, ACCompression);	(* not supported *)
						| Quality: INCL(f.ho.O, NegLqr); status:=Rej; 
							IF HDLC.debug THEN Debug.String("RejQuality "); END;
							INCL(eo, Quality);	(* not supported *)
(*es es fehlt weitere Info, einfach mal ablehnen *)
						| LCPConf17: status:=Rej; 
							INCL(eo, LCPConf17);	(* not supported *)
							IF HDLC.debug THEN Debug.String("RejLCPConf17 "); END;
						| LCPConf19: status:=Rej; 
							IF HDLC.debug THEN Debug.String("RejLCPConf19 "); END;
							INCL(eo, LCPConf19);	(* not supported *)
(**)
					ELSE 
						status:=Rej; (* unknown Type ->reject *)
						Debug.String("unknown type"); 
						(*es*)Debug.Int(type, 5); (**) Debug.Ln;
					END;
					DEC(lenp, size); INC(posp, size);
				END;
			END;
			IF lenp # 0 THEN status:=RejAll; END;
			
			IF (status=RejAll) THEN RETURN FSM.ConfRej;(* len, pos are ok *)
			ELSIF (status=Ack) THEN RETURN FSM.ConfAck; (* len, pos are ok *)
			ELSE	IF Mode THEN status:=Rej; END;
				(* little hack *)
				IF status = Rej THEN EXCL(eo, AuthType) END;
				
				lenp:=len; posp:=pos; posw:=pos;
				WHILE lenp#0 DO
					type:=ORD(p[posp]); size:=ORD(p[posp+1]);
					IF (type IN eo) THEN
						IF status=Nak THEN 	(* make better proposals *)
							CASE type OF
								   MRU: T.PutInt(MinMRU, p, posp+2); 
								   (* write over old values *)
								| AsyncMap: 
									T.PutSet(f.ao.AsyncMap+ f.ho.AsyncMap, p, posp+2);
								| MagicNumber: T.PutLong(T.Magic(), p, posp+2);
								|AuthType: T.PutInt(PAP.PAPProt, p, posp+2);
							ELSE
							END
						END;
						IF posw # posp THEN T.CopyString(p, posp, posw, size); END;
						INC(posw,size); (* 'shift' it together *)
					END;
					INC(posp, size); DEC(lenp, size);
				END;
				len:=posw-pos;
				IF status=Rej 
				THEN RETURN FSM.ConfRej; 
				ELSE RETURN FSM.ConfNak; END;
			END
		END;
	END ReqCI;
		
		
	(* Up - LCP is ready *)
	PROCEDURE *Up (f: FSM.FSM);
	VAR x:INTEGER; s:SET;
	BEGIN
		WITH f: LCPfsm DO
					
			(* Configure our HDLC: Receive: MTU: Take WantOption, but not bigger than our Allow Option! *)
			IF (NegMRU IN f.ho.O) THEN x:=f.ho.MRU; ELSE x:=HDLC.MTU; END;
			IF x>f.ao.MRU THEN x:=f.ao.MRU; END;
			f.HDLCConfig.MTU:=x;
									
			IF (NegAsyncMap IN f.ho.O) THEN s:=f.ho.AsyncMap; ELSE s:={0..31}; END;
			f.HDLCConfig.SendAsyncMap:=s;
									
			(* Receive Config *)
			IF (NegMRU IN f.wo.O) THEN x:=f.go.MRU ELSE x:=HDLC.MTU; END;
			f.HDLCConfig.MRU:=x;
			PPPHandleLCPUp(f.HDLCConfig); 	(* PPP will inform other protocols *)
		END;
	END Up;
	
	(* Down - LCP has to close *)
	PROCEDURE *Down (f: FSM.FSM);
	BEGIN
		PPPHandleLCPDown(f.HDLCConfig); 	(* PPP will inform other protocols *)		
		
		f.HDLCConfig.MTU:=HDLC.MTU; f.HDLCConfig.SendAsyncMap:={0..31};
		f.HDLCConfig.MRU:=HDLC.MTU;

	END Down;
	
	(*  RecProtRej - Protocol Reject received *)
	PROCEDURE *RecProtRej(f: FSM.FSM; VAR p: ARRAY OF CHAR; pos, len:INTEGER);
	VAR prot:INTEGER;
	BEGIN
		IF len>1 THEN
			prot:=T.GetInt(p, pos); Debug.String(" Protocol rejected: "); Debug.Int(prot, 10); Debug.Ln;
			IF f.State#FSM.Opened THEN Debug.String(" Protocol reject discarded "); Debug.Ln;
			ELSE	PPPHandleProtRej(f.HDLCConfig, prot); 	(* PPP will inform protocols *)
			END
		ELSE Debug.String("Short ProtReject received"); Debug.Ln
		END		
	END RecProtRej;

	(*		To implement if we intend to send Echo-Requests
	PROCEDURE RecEchoReply(f:FSM.FSM; id:SHORTINT; VAR p:ARRAY OF CHAR; pos, len:LONGINT); BEGIN END RecEchoReply;
	*)

	(* ExtCode - Handle LCP-Codes *)
	PROCEDURE *ExtCode(f: FSM.FSM; code, id: SHORTINT; VAR p: ARRAY OF CHAR; pos, len:INTEGER): BOOLEAN;
	BEGIN
		WITH f:LCPfsm DO
			CASE code OF
				  ProtocolRej: RecProtRej(f, p, pos, len); RETURN TRUE
(** es, 4 Bytes should be enough for a echo request without data (magic only) *)
				| EchoReq: IF len<4 THEN len:=4; END; T.PutLong(f.go.MagicNumb, p, pos);
(**				| EchoReq: IF len<6 THEN len:=6; END; T.PutLong(f.go.MagicNumb, p, pos); **)
					FSM.SendData(f, EchoReply, id, p, pos, len); RETURN TRUE
				| EchoReply: (* RecEchoReply(f, id, p, pos, len); To implement if we intend to send Echo-Requests *) RETURN TRUE
				| DiscardReq: RETURN TRUE
				(* test code for a problem of Jose Francisco <jfglez@teleline.es>, 05.10.1999, es *)
				| Identification: (* a peer seems to choke on CodeRej, so just try to drop it. *) RETURN TRUE
			ELSE RETURN FALSE
			END
		END
	END ExtCode;
	
	(* Initialisation LCP*)
	PROCEDURE Init* (VAR f:LCPfsm; wo, ao:Options; C:HDLC.PPPUnit);
	BEGIN
		NEW(f); f.Protocol:=LCP; f.ProtoName:="LCP";	(* LCP Protocol *)
		f.ResetCI:=ResetCI; f.CILen:=CILen; f.AddCI:=AddCI; f.AckCI:=AckCI; f.NakCI:=NakCI; f.RejCI:=RejCI;
		f.ReqCI:=ReqCI; f.Up:=Up; f.Down:=Down; f.ExtCode:=ExtCode; f.HDLCConfig:=C;
		FSM.Init(f);
		f.wo:=wo; f.ao:=ao
	END Init;
		
END PPPLCP.