Oberon/ETH Oberon/2003-01-05/PPPIPCP.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 PPPIPCP;	(** non-portable *)
(* $VCS   2, Edgar@EdgarSchwarz.de, 25 Apr :0, 10:42:52 $
    $Log$
$   2, Edgar@EdgarSchwarz.de, 25 Apr :0, 10:42:52
no more PT.Equal (pm), ask remote IP if nor available by a NAK
$   1, Edgar.Schwarz@z.zgs.de, 28 Feb 99, 22:17:0
version for PPP 1.0.0
*)
IMPORT 
	FSM:=PPPFSM, HDLC:=PPPHDLC, 
	PT := (*es*)NetBase, NetIP, (*PacketTools,*)
	T:=PPPTools, Debug := PPPDebug, SYSTEM;

CONST
	(* Protocol Constants *)	
	IPCP*=-07FDFH;	(* =08021H *)
	MaxStates=16;		(* Slots for VJ Compression *)

	(* Configure Information Options *)
	Addrs = 1;								(* IP-Addresses, old *)
	CompressType = 2;					(* Compression Type *)
	Addr = 3;								(* IP Address, new *)
	
	(* IPCP codes *)
	VJCompression = 002DH;	
		
	(* Options Index *)
	NegAddrs = 0;	(* Accept old IP-Addresses *)
	NegAddr = 2;		(* Accept Address *)
	NegVJ = 8; 			(* Accept Van Jacobsen Header Compression *)
		
	(* Length of the Configuration Options *)
	VoidLen = 2; CompressLen = 4; VJLen = 6; AddrLen = 6; AddrsLen = 10;  	(* = Type (1 Byte) + Length (1 Byte) + Data *) 
	
TYPE
	Options*=RECORD
		O*: SET								(* Bits defined by Options Index *);
		OurAddress*, HisAddress*: (*es*)NetIP.Adr(*PT.IPAdr*);
		CompressProt*: INTEGER;	MaxSlot*, CFlag*: SHORTINT;		(* VJ Stuff *)		
	END;
	
	IPCPfsm*=POINTER TO IPCPfsmDesc;
	IPCPfsmDesc=RECORD (FSM.FSMDesc)
		wo*, go(*es*)*(*-*), 
		ao*, ho(*es*)*(*-*): Options;		
		(* WantOptions, GotOptions, AllowOptions, HisOptions *)
		AcceptLocal, AcceptRemote: BOOLEAN; 	
		(* Accept his proposal for our IP_address / for his IP_address  *)	
	END;
		
VAR	
	(* Upcalls to PPP *)
	PPPHandleIPCPUp*: PROCEDURE (U:HDLC.PPPUnit);
	PPPHandleIPCPDown*: PROCEDURE (U:HDLC.PPPUnit);
	
	ZeroIP*: (*es*)NetIP.Adr(*PT.IPAdr*);
	i: INTEGER;

	
	(* Open - IPCP can come up *)
	PROCEDURE Open* (f: IPCPfsm); BEGIN f.Flags:={}; FSM.Open(f) END Open;
	
	(* Close - Take IPCP down *)
	PROCEDURE Close* (f: IPCPfsm); BEGIN FSM.Close(f) END Close;

	(* LowerUp - The Lower Layer is Up *)
	PROCEDURE LowerUp* (f: IPCPfsm); BEGIN FSM.LowerUp(f) END LowerUp;
	
	(* LowerDown - The Lower Layer is Down *)
	PROCEDURE LowerDown* (f: IPCPfsm); BEGIN FSM.LowerDown(f) END LowerDown;
	
	(* Input - New IPCP Packet *)
	PROCEDURE Input* (f: IPCPfsm; VAR p: ARRAY OF CHAR; pos, len:INTEGER); BEGIN FSM.Input(f, p, pos, len); END Input;

	(*  RecProtRej - Protocol Reject received, pretend his lower layer went down, so we do the same *)
	PROCEDURE *RecProtRej(f: FSM.FSM; VAR p: ARRAY OF CHAR; pos, len:INTEGER);
	BEGIN FSM.LowerUp(f)
	END RecProtRej;	

	(* ResetCI - Reset The Configuration Information *)
	PROCEDURE *ResetCI (f: FSM.FSM);
	BEGIN
		WITH f:IPCPfsm DO
			f.AcceptLocal:=SYSTEM.VAL(LONGINT, f.wo.OurAddress) = SYSTEM.VAL(LONGINT, ZeroIP);
			f.AcceptRemote:=SYSTEM.VAL(LONGINT, f.wo.HisAddress) = SYSTEM.VAL(LONGINT, ZeroIP);
			f.go:=f.wo;		(* GotOptions := WantOptions *)
		END
	END ResetCI;
	
	(* CIlen - Returns Length of the Conf. Inf. *)
	PROCEDURE *CILen (f: FSM.FSM): INTEGER;
	VAR i:INTEGER;
	BEGIN i:=0;
		WITH f:IPCPfsm DO
			IF (NegAddrs IN f.go.O) THEN INC(i, AddrsLen); END;
			IF (NegAddr IN f.go.O) THEN INC(i, AddrLen); END;
			IF (NegVJ IN f.go.O) THEN INC(i, VJLen); 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;
		WITH f:IPCPfsm DO
			IF (NegAddrs IN f.go.O) THEN INC(len, AddrsLen);
				p[pos]:=CHR(Addrs); p[pos+1]:=CHR(AddrsLen);
				T.PutIP(f.go.OurAddress, p, pos+2); 
				T.PutIP(f.go.HisAddress, p, pos+6); INC(pos, AddrsLen);
			END;
			IF (NegAddr IN f.go.O) THEN INC(len, AddrLen);
				p[pos]:=CHR(Addr); p[pos+1]:=CHR(AddrLen); 
				T.PutIP(f.go.OurAddress, p, pos+2); INC(pos, AddrLen);
			END;
			IF (NegVJ IN f.go.O) THEN INC(len, VJLen);
				p[pos]:=CHR(CompressType); p[pos+1]:=CHR(VJLen); T.PutInt(f.go.CompressProt, p, pos+2);
				p[pos+4]:=CHR(f.go.MaxSlot); p[pos+5]:=CHR(f.go.CFlag); INC(pos, VJLen);
			END
		END																								(* To update if more Options supported *)
	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:IPCPfsm DO				(* No changes to our former req are allowed, otherwise the ack is bad *)
			IF (NegAddrs IN f.go.O) THEN
				b:= b & (len>=AddrsLen) & (ORD(p[pos])=Addrs) & 
				(ORD(p[pos+1])=AddrsLen)
				& T.EqualIP(p, pos+2, f.go.OurAddress) & 
				T.EqualIP(p, pos+6, f.go.HisAddress);
				DEC(len, AddrsLen); INC(pos, AddrsLen);
			END;
			IF (NegAddr IN f.go.O) THEN
				b:= b & (len>=AddrLen) & (ORD(p[pos])=Addr)
				& (ORD(p[pos+1])=AddrLen) & T.EqualIP(p, pos+2, f.go.OurAddress);
				DEC(len, AddrLen); INC(pos, AddrLen);
			END;
			IF (NegVJ IN f.go.O) THEN
				b:=b & (len>=VJLen) & (ORD(p[pos])=CompressType) 
						& (ORD(p[pos+1])=VJLen) 
						& (T.GetInt(p, pos+2)=f.go.CompressProt) 
						& (ORD(p[pos+4])=f.go.MaxSlot) 
						& (ORD(p[pos+5])=f.go.CFlag);
				DEC(len, VJLen); INC(pos, VJLen);
			END;																							(* To update if more Options supported *)
			b:=b & (len=0);
			IF ~b THEN Debug.String("Received bad Ack!!"); 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 *)
		type, size:INTEGER; b, nakd:BOOLEAN;
	BEGIN b:=TRUE;
 		WITH f:IPCPfsm DO (* same order as we sent it, but only the nak'd ones *)
			no.O:={}; try.O:=f.go.O;
			IF (NegAddrs IN f.go.O) & (len>=AddrsLen) 
			& (ORD(p[pos])=Addrs) & (ORD(p[pos+1])=AddrsLen) THEN 
				nakd:=FALSE;
				IF ~T.EqualIP(p, pos+2, f.go.OurAddress) 
				& ~T.EqualIP(p, pos+2, ZeroIP) & f.AcceptLocal THEN
					T.GetIP(p, pos+2, try.OurAddress); 
					T.GetIP(p, pos+2, f.ho.OurAddress); 
					nakd:=TRUE;
				ELSE 
					try.OurAddress:=f.go.OurAddress;
				END;
				IF ~T.EqualIP(p, pos+6, f.go.HisAddress) 
				& ~T.EqualIP(p, pos+6, ZeroIP) & f.AcceptRemote THEN
					T.GetIP(p, pos+6, try.HisAddress); 
					T.GetIP(p, pos+6, f.ho.HisAddress); 
					nakd:=TRUE;
				ELSE
					try.HisAddress:=f.go.HisAddress;
				END;
				IF ~nakd THEN Debug.String(" Addresses-NAK wrong (no changes)"); Debug.Ln; b:=FALSE; END;
				INCL(no.O, NegAddrs); DEC(len, AddrsLen); INC(pos, AddrsLen);
			END;
			IF (NegAddr IN f.go.O) & (len>=AddrLen) & (ORD(p[pos])=Addr) & (ORD(p[pos+1])=AddrLen) THEN 
				IF (f.AcceptLocal & ~T.EqualIP(p, pos+2, ZeroIP)) THEN T.GetIP(p, pos+2, try.OurAddress)  (* Accept New Option *)
				ELSE Debug.String(" Our Address is not accepted or ZeroIP-proposal"); Debug.Ln;
				END;
				INCL(no.O, NegAddr); DEC(len, AddrLen); INC(pos, AddrLen);
			END;
			IF (NegVJ IN f.go.O) & (len>=VJLen) & (ORD(p[pos])=CompressType) THEN size:=ORD(p[pos+1]);
				IF (len=VJLen) & (T.GetInt(p, pos+2)=f.go.CompressProt) THEN 
					IF ((ORD(p[pos+4])#f.go.MaxSlot) OR (ORD(p[pos+5])#f.go.CFlag)) THEN	INCL(no.O, NegVJ);
						IF (ORD(p[pos+4])<=f.go.MaxSlot) THEN
							try.MaxSlot:=SHORT(ORD(p[pos+4])); try.CFlag:=SHORT(ORD(p[pos+5])); END;
						DEC(len, VJLen); INC(pos, VJLen);
					ELSE b:=FALSE;	Debug.String(" Compr-NAK wrong (no changes)"); Debug.Ln;	(* No changes made! *)
					END;
				ELSE Debug.String ("An other Compression Type is wanted! Type: "); Debug.Int(T.GetInt(p,pos+2),6); Debug.Ln;
					INCL(no.O, NegVJ); EXCL(try.O,NegVJ);	(* we switch off VJ-Comp. *)
					DEC(len, size); INC(pos, size);
				END;
			END;
												(* 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
					   Addr: b:=((size=AddrLen) & ~(NegAddr IN f.go.O) & ~(NegAddr IN no.O)); 
					| CompressType: b:=((size>=CompressLen) & ~(NegVJ IN f.go.O) & ~(NegVJ IN no.O)); 
					| Addrs: b:=(size=AddrsLen)
				ELSE
				END;
				INC(pos, size);
			END;
			b:=b & (len=0); IF len#0 THEN Debug.String ("Len not zero"); Debug.Int(len,6); Debug.Ln; END;
			
			IF b & (f.State#FSM.Opened) THEN f.go:=try;		 (* Update State *)	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:IPCPfsm DO	(* Look which options were rejected. Same order and same value! Otherwise a bad packet. *)
			try:=f.go;
			IF (NegAddrs IN f.go.O) & (len>=AddrsLen) & (ORD(p[pos])=Addrs) 
			& (ORD(p[pos+1])=AddrsLen) & T.EqualIP(p, pos+2, f.go.OurAddress) 
			& T.EqualIP(p, pos+6, f.go.HisAddress) THEN
				DEC(len, AddrsLen); INC(pos, AddrsLen); EXCL(try.O, NegAddr);
			END;
			IF (NegAddr IN f.go.O) & (len>=AddrLen) & (ORD(p[pos])=Addr) 
			& (ORD(p[pos+1])=AddrLen) & T.EqualIP(p, pos+2, f.go.OurAddress) THEN
				DEC(len, AddrLen); INC(pos, AddrLen); EXCL(try.O, NegAddr); INCL(try.O, NegAddrs);
			END;
			IF (NegVJ IN f.go.O) & (len>VJLen) & (ORD(p[pos])=CompressType) 
			& (ORD(p[pos+1])=VJLen) & (T.GetInt(p, pos+2)=f.go.CompressProt) 
			& (ORD(p[pos+4])=f.go.MaxSlot) & (ORD(p[pos+5])=f.go.CFlag) THEN
					DEC(len, VJLen); INC(pos, VJLen); EXCL(try.O, NegVJ);
			END;			
			IF len#0 THEN
				Debug.String("Received bad Reject ! len:"); 
				Debug.Int(len, 5); Debug.Ln; 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 respose
		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; (* Ack: Can Ack, Nak: Should Nak, Rej: reject of some options needed, RejAll: serious reject: rej whole packet *)
	VAR type, size, posp, posw, lenp, x:INTEGER;
		eo:SET; (* eo: Options to Nak/Rej *)
		status, MaxSlot, CFlag:SHORTINT;  
	BEGIN eo:={};		 
		WITH f:IPCPfsm 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 
					Debug.String("Bad CI Length!"); Debug.Ln; status:=RejAll;
				ELSE type:=ORD(p[posp]);
					CASE type OF
						  Addr: INCL(f.ho.O, NegAddr);
							IF (NegAddr IN f.ao.O) & (size=AddrLen) THEN 
								T.GetIP(p, posp+2, f.ho.HisAddress);
								IF ~f.AcceptRemote OR T.EqualIP(p, posp+2, ZeroIP) THEN
									INCL(eo, Addr); IF status=Ack THEN status:=Nak; END;
								END;
							ELSE status:=Rej; INCL(eo, Addr);
							END;
						| CompressType: INCL(f.ho.O, NegVJ);
							IF (NegVJ IN f.ao.O) & (size>=CompressLen) THEN 
								x:=T.GetInt(p, posp+2); f.ho.CompressProt:=x;
								IF (x=VJCompression) &(size=VJLen) THEN
									MaxSlot:=SHORT(ORD(p[posp+4])); 
									CFlag:=SHORT(ORD(p[posp+5]));
									f.ho.MaxSlot:=MaxSlot; f.ho.CFlag:=CFlag;
									IF (MaxSlot>f.ao.MaxSlot) OR (CFlag>f.ao.CFlag) THEN
										INCL(eo, CompressType); 
										IF status=Ack THEN status:=Nak; END;
									END;
								ELSE status:=Rej; INCL(eo, CompressType);
								END;
							ELSE status:=Rej; INCL(eo, CompressType)
							END;
						| Addrs: INCL(f.ho.O, NegAddrs);
							IF (NegAddrs IN f.ao.O) & (size=AddrsLen) THEN
								T.GetIP(p, posp+2, f.ho.HisAddress); 
								T.GetIP(p, posp+6, f.ho.OurAddress);
								IF (~f.AcceptRemote 
								& ~T.EqualIP(p, posp+2, f.go.HisAddress)) 
								OR T.EqualIP(p, posp+2, ZeroIP) THEN
									INCL(eo, Addrs); IF status=Ack THEN status:=Nak; END;
								END;
								IF (~f.AcceptLocal 
								& ~T.EqualIP(p, posp+6, f.go.OurAddress)) 
								OR T.EqualIP(p, posp+6, ZeroIP) THEN
									INCL(eo, Addrs); IF status=Ack THEN status:=Nak; END;
								END;
							ELSE status:=Rej; INCL(eo, Addrs);
							END;
					ELSE status:=Rej;				(* unknown Type ->reject *)
					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
				(* es, in special case try to get remote address *)
				IF (len = 0) & (SYSTEM.VAL(LONGINT, f.ho.HisAddress) = SYSTEM.VAL(LONGINT, ZeroIP)) THEN
					Debug.String("try to get remote address!"); Debug.Ln;
					p[posp] := CHR(Addr); p[posp+1] := 6X;
					p[posp+2] := 0X; p[posp+3] := 0X;p[posp+4] := 0X; p[posp+5] := 0X;
					len := 6;
					RETURN FSM.ConfNak;
				ELSE
					RETURN FSM.ConfAck; (* len, pos are ok *)
				END;
			ELSE	IF Mode THEN status:=Rej; 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			(* This type needs to be corrected *)
						IF status=Nak THEN 	(* make better proposals *)
							CASE type OF
								   Addr: T.PutIP(f.wo.HisAddress, p, posp+2);
								   (* write over old values *)
								| CompressType:
									IF (f.ho.MaxSlot>f.ao.MaxSlot) THEN 
										p[posp+4]:=CHR(f.wo.MaxSlot); 
									END;
									IF (f.ho.CFlag>f.ao.CFlag) THEN p[posp+5]:=0X; END;
								| Addrs: T.PutIP(f.wo.HisAddress, p, posp+2); 
									T.PutIP(f.wo.OurAddress, p, posp+6); 
									(* write over old values *)
							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 - IPCP is ready *)
	PROCEDURE *Up (f: FSM.FSM);
		VAR
			ip:(*es*)NetIP.Adr(*PT.IPAdr*); i:INTEGER;
			(*es*)p: ARRAY 16 OF CHAR;
	BEGIN
		WITH f: IPCPfsm DO
			IF SYSTEM.VAL(LONGINT, f.ho.HisAddress) = SYSTEM.VAL(LONGINT,  ZeroIP) THEN
				f.ho.HisAddress := f.wo.HisAddress;
			END;
			
			IF SYSTEM.VAL(LONGINT, f.ho.HisAddress) = SYSTEM.VAL(LONGINT, ZeroIP) THEN	
				Debug.String("Could not determine remote IP address!"); Debug.Ln;
				Close(f); RETURN
			ELSIF
				SYSTEM.VAL(LONGINT, f.go.OurAddress) = SYSTEM.VAL(LONGINT, ZeroIP) THEN	
					Debug.String("Could not determine local IP address!"); Debug.Ln;
				Close(f); RETURN
			ELSE	(* success ! *)
				PPPHandleIPCPUp(f.HDLCConfig); 	(* PPP will inform other protocols *)
			END;
		END;
	END Up;
	
	(* Down - IPCP has to close *)
	PROCEDURE *Down (f: FSM.FSM);
	BEGIN
		PPPHandleIPCPDown(f.HDLCConfig); 	(* PPP will inform other protocols *)		
	END Down;
	
	(* Initialisation IPCP*)
	PROCEDURE Init* (VAR f:IPCPfsm; C:HDLC.PPPUnit; OurIP, HisIP:(*es*)NetIP.Adr(*PT.IPAdr*));
	BEGIN
		NEW(f); f.Protocol:=IPCP; f.ProtoName:="IPCP";	(* IPCP 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:=NIL; f.HDLCConfig:=C;
		FSM.Init(f); 
		f.AcceptLocal:=SYSTEM.VAL(LONGINT, OurIP) = SYSTEM.VAL(LONGINT, ZeroIP); f.wo.OurAddress:=OurIP;
		f.AcceptRemote:=SYSTEM.VAL(LONGINT, HisIP) = SYSTEM.VAL(LONGINT, ZeroIP); f.wo.HisAddress:=HisIP;
		f.wo.O:={NegAddr};
		f.wo.CompressProt:=VJCompression; f.wo.MaxSlot:=MaxStates-1; f.wo.CFlag:=1;				(* Want Option *)
		f.ao.O:={NegAddr, NegAddrs};
		f.ao.CompressProt:=VJCompression; f.ao.MaxSlot:=MaxStates-1; f.ao.CFlag:=1;				(* Allow Option *)
		
	END Init;
	
BEGIN
	FOR i:=0 TO (*es*)NetIP.AdrLen(*PT.IPAdrLen*)-1 DO ZeroIP[i]:=0X; END

END PPPIPCP.