diff network/PPP.c @ 110:4eb5a746d7af avr-http

Import avrusbmodem code minus the USB bits. Not built yet.
author Matt Johnston <matt@ucc.asn.au>
date Sat, 15 Sep 2012 21:49:05 +0800
parents 56d09a0969b5
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/network/PPP.c	Sat Sep 15 21:49:05 2012 +0800
@@ -0,0 +1,1426 @@
+/*
+    LUFA Powered Wireless 3G Modem Host
+	
+    Copyright (C) Mike Alexander, 2010.
+     Copyright (C) Dean Camera, 2010.
+*/
+
+/*
+  Copyright 2010  Mike Alexander (mike [at] mikealex [dot] com)
+  Copyright 2010  Dean Camera (dean [at] fourwalledcubicle [dot] com)
+
+  Permission to use, copy, modify, distribute, and sell this 
+  software and its documentation for any purpose is hereby granted
+  without fee, provided that the above copyright notice appear in 
+  all copies and that both that the copyright notice and this
+  permission notice and warranty disclaimer appear in supporting 
+  documentation, and that the name of the author not be used in 
+  advertising or publicity pertaining to distribution of the 
+  software without specific, written prior permission.
+
+  The author disclaim all warranties with regard to this
+  software, including all implied warranties of merchantability
+  and fitness.  In no event shall the author be liable for any
+  special, indirect or consequential damages or any damages
+  whatsoever resulting from loss of use, data or profits, whether
+  in an action of contract, negligence or other tortious action,
+  arising out of or in connection with the use or performance of
+  this software.
+*/
+
+#define  INCLUDE_FROM_PPP_C
+#include "PPP.h"
+
+static uint8_t  			OutgoingPacketID;								// Unique packet ID
+static PPP_Phases_t			PPP_Phase;										// PPP negotiation phases
+static PPP_States_t			LCP_State;										// Each phase has a number of states
+static PPP_States_t			PAP_State;
+static PPP_States_t			IPCP_State;
+static PPP_Packet_t*  		OutgoingPacket;									// Pointer to the outgoing packet
+static PPP_Packet_t*  		IncomingPacket;									// Pointer to the incoming packet
+static uint16_t     		CurrentProtocol;								// Type of the last received packet
+static uint8_t 				RestartCount;
+static uint16_t				LinkTimer;
+static bool					TimerOn;
+static bool 				MarkForNAK;
+static bool					MarkForREJ;
+static uint8_t				OutgoingPacketBuffer[OUTGOING_PACKET_BUFFER_SIZE];	// Buffer to store the outgoing packet in
+
+PPP_Packet_t* dummy;	
+
+
+void PPP_InitPPP(void)
+{
+	PPP_Phase = PPP_PHASE_Dead;
+	TimerOn = false;
+}
+
+void PPP_StartLink(void)
+{
+	OutgoingPacket = NULL;
+	CurrentProtocol = NONE;
+	TimerOn = false;
+	LinkTimer = 0;
+	uip_len = 0;
+	RestartCount = MAX_RESTARTS;
+	MarkForNAK = MarkForREJ = false;
+	OutgoingPacketID = -1;
+	OutgoingPacket = IncomingPacket = NULL;	
+	LCP_State  = PPP_STATE_Initial;
+	PAP_State  = PPP_STATE_Initial;
+	IPCP_State = PPP_STATE_Initial;
+	PPP_Phase = PPP_PHASE_Establish;
+	PPP_ManageState(PPP_EVENT_Open, &LCP_State, PPP_LAYER_Physical);
+	PPP_ManageState(PPP_EVENT_Up, &LCP_State, PPP_LAYER_Physical);
+}
+
+// Called every 10ms. Send events to the state machine every 3 seconds if the timer is currently running
+void PPP_LinkTimer(void)
+{
+	if (!TimerOn || LinkTimer++ < 300)
+		return;
+
+	if (RestartCount > 0)
+	{
+		Debug_Print("Timer+\r\n");
+
+		switch(PPP_Phase)
+		{
+			case PPP_PHASE_Establish:
+				PPP_ManageState(PPP_EVENT_TOPlus, &LCP_State, PPP_LAYER_Physical);
+			break;
+
+			case PPP_PHASE_Authenticate:
+				PPP_ManageState(PPP_EVENT_TOPlus, &PAP_State, PPP_LAYER_Authentication);
+			break;
+
+			case PPP_PHASE_Network:
+				PPP_ManageState(PPP_EVENT_TOPlus, &IPCP_State, PPP_LAYER_Network);
+			break;
+
+			default:
+			break;
+		}
+	}
+	else
+	{
+		Debug_Print("Timer-\r\n");
+		
+		switch(PPP_Phase)
+		{
+			case PPP_PHASE_Establish:
+				PPP_ManageState(PPP_EVENT_TOMinus, &LCP_State, PPP_LAYER_Physical);
+			break;
+
+			case PPP_PHASE_Authenticate:
+				PPP_ManageState(PPP_EVENT_TOMinus, &PAP_State, PPP_LAYER_Authentication);
+			break;
+
+			case PPP_PHASE_Network:
+				PPP_ManageState(PPP_EVENT_TOMinus, &IPCP_State, PPP_LAYER_Network);
+			break;
+
+			default:
+			break;
+		}
+	}
+
+	LinkTimer = 0;																										// Reset Timer
+}
+
+void PPP_ManageLink(void)
+{	
+	if (PPP_Phase == PPP_PHASE_Dead)
+		return;
+	
+	uint16_t ReadProtocol = network_read();																				// Don't mess with CurrentProtocol in case we get 0 (No data) back
+
+	if (ReadProtocol == 0)
+		return;
+	
+	CurrentProtocol = ReadProtocol;
+	IncomingPacket = (PPP_Packet_t*)uip_buf;																			// Map the incoming data to a packet
+	
+	Debug_Print("Got ");
+
+	switch (CurrentProtocol)
+	{
+		case LCP:
+			Debug_Print("LCP ");
+
+			switch(IncomingPacket->Code)
+			{
+				case REQ:
+					Debug_Print("REQ\r\n");
+
+					MarkForNAK = MarkForREJ = false;
+
+					// List of options that we can support. If any other options come in, we have to REJ them
+					const uint8_t SupportedOptions[] = {LCP_OPTION_Maximum_Receive_Unit,
+														LCP_OPTION_Magic_Number,
+														LCP_OPTION_Async_Control_Character_Map,
+					                                    LCP_OPTION_Authentication_Protocol,
+					                                    LCP_OPTION_Protocol_Field_Compression,
+					                                    LCP_OPTION_Address_and_Control_Field_Compression};
+
+					if ((MarkForREJ = PPP_TestForREJ(SupportedOptions, sizeof(SupportedOptions))))						// Check that we can support all the options the other end wants to use
+					{
+						PPP_ManageState(PPP_EVENT_RCRMinus, &LCP_State, PPP_LAYER_Physical);
+						break;
+					}
+					
+					static PPP_Option_t Option3 = {.Type  = 0x03, .Length = 4, .Data = {0xc0, 0x23}};				
+
+					if ((MarkForNAK = PPP_TestForNAK(&Option3)))														// Check that the authentication protocol = PAP (0xc023)
+					{
+						PPP_ManageState(PPP_EVENT_RCRMinus, &LCP_State, PPP_LAYER_Physical);
+						break;
+					}
+
+					PPP_ManageState(PPP_EVENT_RCRPlus, &LCP_State, PPP_LAYER_Physical);
+				break;
+
+				case ACK:
+					Debug_Print("ACK\r\n");
+					
+					if (IncomingPacket->PacketID != OutgoingPacketID)
+					{
+						Debug_Print("Out of sync\r\n");
+						break;
+					}
+
+					PPP_ManageState(PPP_EVENT_RCA, &LCP_State, PPP_LAYER_Physical);
+				break;
+
+				case NAK:
+					Debug_Print("NAK\r\n");
+
+					if (IncomingPacket->PacketID != OutgoingPacketID)
+					{
+						Debug_Print("Out of sync\r\n");
+						break;
+					}
+
+					PPP_ProcessNAK();
+					PPP_ManageState(PPP_EVENT_RCN, &LCP_State, PPP_LAYER_Physical);
+				break;
+
+				case REJ:
+					Debug_Print("REJ\r\n");
+
+					if (IncomingPacket->PacketID != OutgoingPacketID)
+					{
+						Debug_Print("Out of sync\r\n");
+						break;
+					}
+
+					PPP_ProcessREJ();
+					PPP_ManageState(PPP_EVENT_RCN, &LCP_State, PPP_LAYER_Physical);
+				break;
+
+				case DISC:
+				case ECHOREQ:
+				case ECHOREPLY:
+					Debug_Print("DISC\r\n");
+					PPP_ManageState(PPP_EVENT_RXR, &LCP_State, PPP_LAYER_Physical);
+				break;
+
+				case TERMREQ:
+					Debug_Print("TERM\r\n");
+					PPP_ManageState(PPP_EVENT_RTR, &LCP_State, PPP_LAYER_Physical);
+				break;
+
+				case CODEREJ:
+				case PROTREJ:
+					Debug_Print("CODE/PROTREJ\r\n");
+					PPP_ManageState(PPP_EVENT_RXJMinus, &LCP_State, PPP_LAYER_Physical);
+				break;
+				
+				default:
+					Debug_Print("unknown\r\n");
+					PPP_ManageState(PPP_EVENT_RUC, &LCP_State, PPP_LAYER_Physical);
+				break;
+			}
+		break;
+
+		case PAP:
+			Debug_Print("PAP ");
+			
+			switch(IncomingPacket->Code)
+			{
+				case REQ:
+					Debug_Print("REQ\r\n");
+					PPP_ManageState(PPP_EVENT_RCRPlus, &PAP_State, PPP_LAYER_Authentication);
+				break;
+
+				case ACK:
+					Debug_Print("ACK\r\n");
+					PPP_ManageState(PPP_EVENT_RCRPlus, &PAP_State, PPP_LAYER_Authentication);						// The host never sends a Configure request for PAP, but we need it to move the state machine to completion. So, fake it.
+					PPP_ManageState(PPP_EVENT_RCA, &PAP_State, PPP_LAYER_Authentication);
+				break;
+			}
+		break;
+
+		case IPCP:
+			Debug_Print("IPCP ");
+
+			switch(IncomingPacket->Code)
+			{
+				case REQ:
+					Debug_Print("REQ\r\n");
+	
+					MarkForNAK = MarkForREJ = false;
+					
+					// List of options that we can support. If any other options come in, we have to REJ them
+					uint8_t SupportedOptions[] = {IPCP_OPTION_IP_address, IPCP_OPTION_Primary_DNS, IPCP_OPTION_Secondary_DNS};
+					
+					if ((MarkForREJ = PPP_TestForREJ(SupportedOptions, sizeof(SupportedOptions))))
+					{
+						PPP_ManageState(PPP_EVENT_RCRMinus, &IPCP_State, PPP_LAYER_Network);
+						break;
+					}
+
+					PPP_ManageState(PPP_EVENT_RCRPlus, &IPCP_State, PPP_LAYER_Network);
+				break;
+
+				case ACK:
+					Debug_Print("ACK\r\n");
+
+					if (IncomingPacket->PacketID != OutgoingPacketID)
+					{
+						Debug_Print("Out of sync\r\n");
+						break;
+					}
+
+					IPAddr1 = IncomingPacket->Options[0].Data[0]; 						// Store address for use in IP packets.
+					IPAddr2 = IncomingPacket->Options[0].Data[1];    				 	// Assumption is that Option 3 is the first option, which it
+					IPAddr3 = IncomingPacket->Options[0].Data[2];						// should be as the PPP spec states that implementations should
+					IPAddr4 = IncomingPacket->Options[0].Data[3];						// not reorder packets, and we sent out a REQ with option 3 first
+
+					PPP_ManageState(PPP_EVENT_RCA, &IPCP_State, PPP_LAYER_Network);
+				break;
+
+				case NAK:
+					Debug_Print("NAK\r\n");
+					if (IncomingPacket->PacketID != OutgoingPacketID)
+					{
+						Debug_Print("Out of sync\r\n");
+						break;
+					}
+
+					PPP_ProcessNAK();
+					PPP_ManageState(PPP_EVENT_RCN, &IPCP_State, PPP_LAYER_Network);
+				break;
+
+				case REJ:
+					Debug_Print("REJ\r\n");
+
+					if (IncomingPacket->PacketID != OutgoingPacketID)
+					{
+						Debug_Print("Out of sync\r\n");
+						break;
+					}
+
+					PPP_ProcessREJ();
+					PPP_ManageState(PPP_EVENT_RCN, &IPCP_State, PPP_LAYER_Network);
+				break;
+			}
+		break;
+
+		case IP:
+			TCPIP_GotNewPacket();
+		break;
+
+		default:
+			Debug_Print("unknown protocol: 0x");
+			Debug_PrintHex(CurrentProtocol / 256); Debug_PrintHex(CurrentProtocol & 255);
+			Debug_Print("\r\n");
+		break;
+	}
+}
+
+
+// Either create a new OutgoingPacket, or if we've just received a NAK or REJ we will have already changed the OutgoingPacket so just send that
+static void Send_Configure_Request(void)
+{
+	Debug_Print("Send Configure Request\r\n");
+	
+	switch(PPP_Phase)
+	{
+		case PPP_PHASE_Establish:
+			if (OutgoingPacket == NULL)																			// Create a new packet
+			{
+				// When we send a REQ, we want to make sure that the other end supports these options
+//				static PPP_Option_t Option1 = {.Type = LCP_OPTION_Maximum_Receive_Unit,					 .Length = 4,	.Data = {0x5, 0xa0}};
+				static PPP_Option_t Option2 = {.Type = LCP_OPTION_Async_Control_Character_Map,			 .Length = 6,	.Data = {0x0, 0x0, 0x0, 0x0}};
+				static PPP_Option_t Option5 = {.Type = LCP_OPTION_Magic_Number,							 .Length = 6,	.Data = {0x27, 0xf5, 0x46, 0xa1}};
+				static PPP_Option_t Option7 = {.Type = LCP_OPTION_Protocol_Field_Compression,			 .Length = 2};
+				static PPP_Option_t Option8 = {.Type = LCP_OPTION_Address_and_Control_Field_Compression, .Length = 2};
+				static PPP_Option_t OptionD = {.Type = LCP_OPTION_Callback,								 .Length = 3,	.Data = {0x6}};
+				
+				//PPP_AddOption(OutgoingPacket, &Option1);
+				PPP_AddOption(&Option2);
+				PPP_AddOption(&Option5);
+				PPP_AddOption(&Option7);
+				PPP_AddOption(&Option8);
+				PPP_AddOption(&OptionD);
+				OutgoingPacket->Code = REQ;
+				CurrentProtocol = LCP;
+			}
+		break;
+
+		case PPP_PHASE_Authenticate:
+			if (OutgoingPacket == NULL)																			// Create a new packet
+			{
+				static PPP_Option_t Option0 = {.Type = 0x00, .Length = 2};
+			
+				PPP_AddOption(&Option0);
+				OutgoingPacket->Options[0].Type = 0x00;															// No User Name 
+                OutgoingPacket->Options[0].Length = 0x00;														// No Password
+				OutgoingPacket->Code = REQ;
+				CurrentProtocol = PAP;
+			}
+		break;
+
+		case PPP_PHASE_Network:
+			if (OutgoingPacket == NULL)																			// Create a new packet
+			{
+				// When we send a REQ, we want to make sure that the other end supports these options
+				static PPP_Option_t Option3  = {.Type = IPCP_OPTION_IP_address,    .Length = 6, .Data = {0, 0, 0, 0}};		
+				static PPP_Option_t Option81 = {.Type = IPCP_OPTION_Primary_DNS,   .Length = 6, .Data = {0, 0, 0, 0}};
+				static PPP_Option_t Option83 = {.Type = IPCP_OPTION_Secondary_DNS, .Length = 6, .Data = {0, 0, 0, 0}};
+				
+				PPP_AddOption(&Option3);																		// Make sure Option3 is first
+				PPP_AddOption(&Option81);
+				PPP_AddOption(&Option83);
+				OutgoingPacket->Code = REQ;
+				CurrentProtocol = IPCP;
+			}
+		break;
+
+		default:
+		break;
+	}
+
+	OutgoingPacket->PacketID = ++OutgoingPacketID;																// Every new REQ Packet going out gets a new ID
+	RestartCount--;																								// Decrement the count before we restart the layer
+	uip_len = uip_ntohs(OutgoingPacket->Length);
+	memcpy(uip_buf, OutgoingPacket, uip_len);																	// Copy the outgoing packet to the buffer for sending
+
+	network_send(CurrentProtocol);																				// Send either the new packet or the modified packet
+}
+
+
+// We change the incoming packet code to send an ACK to the remote end, and re-use all the data from the incoming packet
+static void Send_Configure_Ack(void)
+{
+	Debug_Print("Send Configure ACK\r\n");
+
+	IncomingPacket->Code = ACK;
+	network_send(CurrentProtocol);
+}
+
+// We change the incoming packet code to send a NAK or REJ to the remote end. The incoming packet has already been altered to show which options to NAK/REJ
+static void Send_Configure_Nak_Rej(void)
+{
+	if (MarkForNAK)
+	{
+		Debug_Print("Send Configure NAK\r\n");
+		IncomingPacket->Code = NAK;
+	}
+	else if (MarkForREJ)
+	{
+		Debug_Print("Send Configure REJ\r\n");
+		IncomingPacket->Code = REJ;
+	}
+	else
+	{
+		return;
+	}
+
+	uip_len = uip_ntohs(IncomingPacket->Length);
+	network_send(CurrentProtocol);
+}
+
+// Send a TERM to the remote end.
+static void Send_Terminate_Request(void)
+{
+	Debug_Print("Send Terminate Request\r\n");
+	
+	OutgoingPacket = (PPP_Packet_t*)uip_buf;																	// Build the outgoing packet in uip_buf
+	CurrentProtocol = LCP;
+	OutgoingPacket->Code = TERMREQ;
+	OutgoingPacket->Length = UIP_HTONS(sizeof(PPP_Packet_t));
+	OutgoingPacket->PacketID = ++OutgoingPacketID;																// Every new REQ Packet going out gets a new ID
+
+	RestartCount--;
+
+	uip_len = uip_ntohs(OutgoingPacket->Length);
+	network_send(CurrentProtocol);																				// Send the packet
+}
+
+// Send a TERM ACK to the remote end.
+static void Send_Terminate_Ack(void)
+{
+	Debug_Print("Send Terminate ACK\r\n");
+
+	IncomingPacket->Code = TERMREPLY;
+	network_send(CurrentProtocol);
+}
+
+// Send a REJ to the remote end.
+static void Send_Code_Reject(void)
+{
+	Debug_Print("Send Code Reject\r\n");
+
+	IncomingPacket->Code = CODEREJ;
+	network_send(CurrentProtocol);
+}
+
+// Send an ECHO to the remote end.
+static void Send_Echo_Reply(void)
+{
+	Debug_Print("Send Echo Reply\r\n");
+
+	IncomingPacket->Code = ECHOREPLY;
+	network_send(CurrentProtocol);
+}
+
+// Called by the state machine when the current layer comes up. Use this to start the next layer up.
+static void This_Layer_Up(PPP_Layers_t Layer)
+{
+	CurrentProtocol = NONE;
+	OutgoingPacket = NULL;																						// Clear the outgoing packet
+
+	switch(Layer)
+	{
+		case PPP_LAYER_Physical:
+			Debug_Print("**LCP Up**\r\n");
+			PPP_Phase = PPP_PHASE_Authenticate;
+			OutgoingPacketID = -1;
+			PPP_ManageState(PPP_EVENT_Open, &PAP_State, PPP_LAYER_Authentication);
+			PPP_ManageState(PPP_EVENT_Up, &PAP_State, PPP_LAYER_Authentication);
+		break;
+
+		case PPP_LAYER_Authentication:
+			Debug_Print("**PAP Up**\r\n");
+			PPP_Phase = PPP_PHASE_Network;
+			OutgoingPacketID = -1;
+			PPP_ManageState(PPP_EVENT_Open, &IPCP_State, PPP_LAYER_Network);
+			PPP_ManageState(PPP_EVENT_Up, &IPCP_State, PPP_LAYER_Network);
+		break;
+
+		case PPP_LAYER_Network:
+			Debug_Print("**IPCP Up**\r\n");
+			ConnectedState = LINKMANAGEMENT_STATE_InitializeTCPStack;
+		break;
+
+		default:
+		break;
+	}
+}
+
+// Called by the state machine when a layer goes down. Use this to stop the next layer up
+static void This_Layer_Down(PPP_Layers_t Layer)
+{
+	OutgoingPacket = NULL;																						// Clear the outgoing packet
+
+	switch(Layer)
+	{
+		case PPP_LAYER_Physical:
+			Debug_Print("**LCP Down**\r\n");
+			PPP_ManageState(PPP_EVENT_Down, &PAP_State, PPP_LAYER_Authentication);
+			PPP_Phase = PPP_PHASE_Establish;
+		break;
+
+		case PPP_LAYER_Authentication:
+			Debug_Print("**PAP Down**\r\n");
+			PPP_ManageState(PPP_EVENT_Down, &IPCP_State, PPP_LAYER_Network);
+		break;
+
+		case PPP_LAYER_Network:
+			Debug_Print("**IPCP Down**\r\n");
+		break;
+
+		default:
+		break;
+	}
+}
+
+// Called by the state machine when the current layer starts
+static void This_Layer_Started(PPP_Layers_t Layer)
+{
+	switch(Layer)
+	{
+		case PPP_LAYER_Physical:
+			Debug_Print("**LCP Started**\r\n");
+		break;
+
+		case PPP_LAYER_Authentication:
+			Debug_Print("**PAP Started**\r\n");
+		break;
+
+		case PPP_LAYER_Network:
+			Debug_Print("**IPCP Started**\r\n");
+		break;
+
+		default:
+		break;
+	}
+}
+
+// Called by the state machine when the current layer finishes
+static void This_Layer_Finished(PPP_Layers_t Layer)
+{
+	switch(Layer)
+	{
+		case PPP_LAYER_Physical:
+			Debug_Print("**LCP Finished**\r\n");
+		break;
+
+		case PPP_LAYER_Authentication:
+			Debug_Print("**PAP Finished**\r\n");
+		break;
+
+		case PPP_LAYER_Network:
+			Debug_Print("**IPCP Finished**\r\n");
+		break;
+
+		default:
+		break;
+	}
+
+	Debug_Print("**Rebooting**\r\n");
+	Reboot();
+}
+
+
+// We get a NAK if our outbound Configure Request contains valid options, but the values are wrong. So we adjust our values for when the next Configure Request is sent
+static void PPP_ProcessNAK(void)
+{
+	PPP_Option_t* CurrentOption = NULL;
+
+	while ((CurrentOption = PPP_GetNextOption(IncomingPacket, CurrentOption)) != NULL)		// Scan options in NAK packet
+	{
+		if (PPP_CheckForOption(CurrentOption))
+			PPP_ChangeOption(OutgoingPacket, CurrentOption);								// If the NAK'd option is already in the outgoing packet then change the existing option
+		else
+			PPP_AddOption(CurrentOption);													// Otherwise add it
+	}
+}
+
+// We get a REJ if our outbound Configure Request contains any options not acceptable to the remote end. So we remove those options.
+static void PPP_ProcessREJ(void)
+{
+	PPP_Option_t* CurrentOption = NULL;
+
+	while ((CurrentOption = PPP_GetNextOption(IncomingPacket, CurrentOption)) != NULL)		// Scan options in REJ packet
+	  PPP_RemoveOption(OutgoingPacket, CurrentOption->Type);								// Remove the options(s) we don't want
+}
+
+// Test to see if the incoming packet contains any options we can't support If so, take out all good options and leave the bad ones to be sent out in the REJ
+static bool PPP_TestForREJ(const uint8_t Options[],
+                           const uint8_t NumOptions)
+{
+	PPP_Option_t* CurrentOption = NULL;
+	bool FoundBadOption = false;
+	bool ThisOptionOK;
+
+	while ((CurrentOption = PPP_GetNextOption(IncomingPacket, CurrentOption)) != NULL)		// Scan incoming options
+	{
+		ThisOptionOK = false;
+
+		for (uint8_t i = 0; i < NumOptions; i++)
+		{
+			if (CurrentOption->Type == Options[i])
+			{
+				ThisOptionOK = true;
+				break;
+			}
+		}
+
+		if (!ThisOptionOK)
+		  FoundBadOption = true;
+	}
+	
+	if (!FoundBadOption)																	// No bad options. Return, leaving the packet untouched
+	  return false;
+
+	// We found some bad options, so now we need to go through the packet and remove all others, leaving the bad options to be sent out in the REJ
+	while ((CurrentOption = PPP_GetNextOption(IncomingPacket, CurrentOption)) != NULL)
+	{
+		for (uint8_t i = 0; i < NumOptions; i++)
+		{
+			if (CurrentOption->Type == Options[i])
+			{
+				PPP_RemoveOption(IncomingPacket, CurrentOption->Type);
+				CurrentOption = NULL;														// Start again. Easier than moving back (as next option has now moved into place of current option)
+				break;
+			}
+		}
+	}
+
+	return true;
+}
+
+// Test to see if the incoming packet contains an option with values that we can't accept
+static bool PPP_TestForNAK(const PPP_Option_t* const Option)
+{
+	PPP_Option_t* CurrentOption = NULL;
+	bool FoundBadOption = false;
+	
+	while ((CurrentOption = PPP_GetNextOption(IncomingPacket, CurrentOption)) != NULL)		// Scan options in receiver buffer
+	{
+		if (CurrentOption->Type == Option->Type)
+		{	
+			for (uint8_t i = 0; i < Option->Length - 2; i++)
+			{
+				if (CurrentOption->Data[i] != Option->Data[i])
+				  FoundBadOption = true;
+			}
+		}
+	}
+	
+	if (!FoundBadOption)																	// No bad option. Return, leaving the packet untouched
+	  return false;
+
+	// We found a bad option, so now we need to go through the packet and remove all others, leaving the bad option to be sent out in the NAK
+	// and change the bad option to have a value that we can support
+	while ((CurrentOption = PPP_GetNextOption(IncomingPacket, CurrentOption)) != NULL)
+	{
+		if (CurrentOption->Type == Option->Type)
+		{
+			PPP_ChangeOption(IncomingPacket, Option);
+		}
+		else
+		{
+			PPP_RemoveOption(IncomingPacket, CurrentOption->Type);
+			CurrentOption = NULL;															// Start again. Easier than moving back (as next option has now moved into place of current option)
+		}
+	}
+
+	return true;
+}
+
+
+/////////////////////////////
+// Packet Helper functions //
+/////////////////////////////
+
+// Check if the given option is in the outgoing packet
+static bool PPP_CheckForOption(const PPP_Option_t* const Option)
+{
+	PPP_Option_t* CurrentOption = NULL;
+
+	while ((CurrentOption = PPP_GetNextOption(OutgoingPacket, CurrentOption)) != NULL)		// Scan options in the packet
+	{
+		if (CurrentOption->Type == Option->Type)
+		  return true;
+	}
+
+	return false;
+}
+
+// Add the given option to the end of the outgoing packet and adjust the size of the packet
+static void PPP_AddOption(const PPP_Option_t* const Option)
+{
+	uint16_t OldPacketLength, NewPacketLength;
+	
+	if (OutgoingPacket == NULL)
+	{
+		OutgoingPacket = (PPP_Packet_t*)&OutgoingPacketBuffer;
+		OldPacketLength = sizeof(PPP_Packet_t);												// If we're creating a new empty packet
+	}
+	else
+		OldPacketLength = uip_ntohs(OutgoingPacket->Length);									// If the packet already exists
+	
+	NewPacketLength = OldPacketLength + Option->Length;
+	
+	if (NewPacketLength > OUTGOING_PACKET_BUFFER_SIZE)
+	{
+		Debug_Print("*** Packet overflow ***\r\n");
+		return;
+	}
+	
+	memcpy((void*)OutgoingPacket + OldPacketLength, Option, Option->Length);				// Add the new option
+	
+	OutgoingPacket->Length = uip_htons(NewPacketLength);
+}
+
+// Try and find the option in the packet, and if it exists, change its value to that in the passed-in option (assumes the option lengths are the same)
+static void PPP_ChangeOption(PPP_Packet_t* const ThisPacket,
+                             const PPP_Option_t* const Option)
+{
+	PPP_Option_t* CurrentOption = NULL;
+
+	while ((CurrentOption = PPP_GetNextOption(ThisPacket, CurrentOption)) != NULL)			// Scan options in the packet
+	{
+		if (CurrentOption->Type == Option->Type)
+		  memcpy(CurrentOption->Data, Option->Data, Option->Length - 2);					// Copy the data portion of the option (not Type or Length)
+	}
+}
+
+// Try and find the option in the packet, and if it exists remove it and adjust the size of the packet
+static void PPP_RemoveOption(PPP_Packet_t* const ThisPacket,
+                             const uint8_t Type)
+{
+	PPP_Option_t* CurrentOption = NULL;
+	PPP_Option_t* NextOption = NULL;
+
+	while ((CurrentOption = PPP_GetNextOption(ThisPacket, CurrentOption)) != NULL)					// Scan the options in the packet
+	{
+		if (CurrentOption->Type == Type)															// Is it the packet we want to remove?
+		{
+			NextOption = PPP_GetNextOption(ThisPacket, CurrentOption);								// Find the next option in the packet
+			uint8_t OptionLength = CurrentOption->Length;											// Save the Option Length as the memcpy will change CurrentOption->Length
+
+			if (NextOption != NULL)																	// If it's not the last option in the packet ...
+			{
+				uint16_t LenToCopy = uip_ntohs(ThisPacket->Length) - ((void*)CurrentOption - (void*)ThisPacket) - OptionLength;
+			    memcpy(CurrentOption, NextOption, LenToCopy);										// ... move all further options forward
+			}
+
+			ThisPacket->Length = uip_htons(uip_ntohs(ThisPacket->Length) - OptionLength);					// Adjust the length
+		}
+	}
+}
+
+// Get the next option in the packet from the option that is passed in. Return NULL if last packet
+static PPP_Option_t* PPP_GetNextOption(const PPP_Packet_t* const ThisPacket,
+                                       const PPP_Option_t* const CurrentOption)
+{
+	PPP_Option_t* NextOption;
+	
+	if (CurrentOption == NULL)
+		NextOption = (PPP_Option_t*)ThisPacket->Options;											// First option
+	else
+		NextOption = (PPP_Option_t*)((uint8_t*)CurrentOption + CurrentOption->Length);
+
+	// Check that we haven't overrun the end of the packet
+	if (((void*)NextOption - (void*)ThisPacket->Options) < (uip_ntohs(ThisPacket->Length) - 4))
+		return NextOption;
+	else 
+		return NULL;
+}
+
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// The main PPP state machine - following RFC1661 - http://tools.ietf.org/html/rfc1661 //
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static void PPP_ManageState(const PPP_Events_t Event,
+                     PPP_States_t* const State,
+					 PPP_Layers_t const Layer)
+{
+	switch (*State)
+	{
+		case PPP_STATE_Initial:
+
+			switch (Event)
+			{
+				case PPP_EVENT_Up:
+					*State = PPP_STATE_Closed;
+				break;
+
+				case PPP_EVENT_Open:
+					*State = PPP_STATE_Starting;
+					This_Layer_Started(Layer);
+				break;
+
+				case PPP_EVENT_Close:
+					*State = PPP_STATE_Initial;
+				break;
+
+				case PPP_EVENT_TOPlus:
+				case PPP_EVENT_TOMinus:
+				break;
+
+				default:
+					Debug_Print("Illegal Event\r\n");
+				break;
+			}
+		break;
+
+		case PPP_STATE_Starting:
+
+			switch (Event)
+			{
+				case PPP_EVENT_Up:
+					*State = PPP_STATE_Req_Sent;
+					RestartCount = MAX_RESTARTS;
+					LinkTimer = 0;
+					TimerOn = true;
+					Send_Configure_Request();
+				break;
+
+				case PPP_EVENT_Open:
+					*State = PPP_STATE_Starting;
+				break;
+
+				case PPP_EVENT_Close:
+					*State = PPP_STATE_Initial;
+					This_Layer_Finished(Layer);
+				break;
+
+				case PPP_EVENT_TOPlus:
+				case PPP_EVENT_TOMinus:
+				break;
+
+				default:
+					Debug_Print("Illegal Event\r\n");
+				break;
+			}
+		break;
+
+		case PPP_STATE_Closed:
+
+			switch (Event)
+			{
+				case PPP_EVENT_Down:
+					*State = PPP_STATE_Initial;
+				break;
+
+				case PPP_EVENT_Open:
+					*State = PPP_STATE_Req_Sent;
+					RestartCount = MAX_RESTARTS;
+					LinkTimer = 0;
+					TimerOn = true;
+					Send_Configure_Request();
+				break;
+
+				case PPP_EVENT_Close:
+				case PPP_EVENT_RTA:
+				case PPP_EVENT_RXJPlus:
+				case PPP_EVENT_RXR:
+					*State = PPP_STATE_Closed;
+				break;
+
+				case PPP_EVENT_RCRPlus:
+				case PPP_EVENT_RCRMinus:
+				case PPP_EVENT_RCA:
+				case PPP_EVENT_RCN:
+					*State = PPP_STATE_Closed;
+					Send_Terminate_Ack();
+				break;
+
+				case PPP_EVENT_TOPlus:
+				case PPP_EVENT_TOMinus:
+				break;
+
+				default:
+					Debug_Print("Illegal Event\r\n");
+				break;
+			}
+		break;
+
+		case PPP_STATE_Stopped:
+			
+			switch (Event)
+			{
+				case PPP_EVENT_Down:
+					*State = PPP_STATE_Starting;
+					This_Layer_Started(Layer);
+				break;
+
+				case PPP_EVENT_Open:
+					*State = PPP_STATE_Stopped;
+				break;
+
+				case PPP_EVENT_Close:
+					*State = PPP_STATE_Closed;
+				break;
+
+				case PPP_EVENT_RCRPlus:
+					*State = PPP_STATE_Ack_Sent;
+					RestartCount = MAX_RESTARTS;
+					LinkTimer = 0;
+					TimerOn = true;
+					Send_Configure_Request();
+					Send_Configure_Ack();
+				break;
+
+				case PPP_EVENT_RCRMinus:
+					*State = PPP_STATE_Req_Sent;
+					RestartCount = MAX_RESTARTS;
+					LinkTimer = 0;
+					TimerOn = true;
+					Send_Configure_Request();
+					Send_Configure_Nak_Rej();
+				break;
+
+				case PPP_EVENT_RCA:
+				case PPP_EVENT_RCN:
+				case PPP_EVENT_RTR:
+					*State = PPP_STATE_Stopped;
+					Send_Terminate_Ack();
+				break;
+
+				case PPP_EVENT_RTA:
+				case PPP_EVENT_RXJPlus:
+				case PPP_EVENT_RXR:
+					*State = PPP_STATE_Stopped;
+				break;
+
+				case PPP_EVENT_RUC:
+					*State = PPP_STATE_Stopped;
+					Send_Code_Reject();
+				break;
+				
+				case PPP_EVENT_RXJMinus:
+					*State = PPP_STATE_Stopped;
+					This_Layer_Finished(Layer);
+				break;
+
+				case PPP_EVENT_TOPlus:
+				case PPP_EVENT_TOMinus:
+				break;
+
+				default:
+					Debug_Print("Illegal Event\r\n");
+				break;
+			}
+		break;
+
+		case PPP_STATE_Closing:
+
+			switch (Event)
+			{
+				case PPP_EVENT_Down:
+					TimerOn = false;
+					*State = PPP_STATE_Initial;
+				break;
+
+				case PPP_EVENT_Open:
+					*State = PPP_STATE_Stopping;
+				break;
+
+				case PPP_EVENT_Close:
+					*State = PPP_STATE_Closing;
+				break;
+
+				case PPP_EVENT_TOPlus:
+					Send_Terminate_Request();
+					*State = PPP_STATE_Closing;
+				break;
+				
+				case PPP_EVENT_TOMinus:
+					*State = PPP_STATE_Closed;
+					TimerOn = false;
+					This_Layer_Finished(Layer);
+				break;
+
+				case PPP_EVENT_RCRPlus:
+				case PPP_EVENT_RCRMinus:
+				case PPP_EVENT_RCA:
+				case PPP_EVENT_RCN:
+				case PPP_EVENT_RXJPlus:
+				case PPP_EVENT_RXR:
+					*State = PPP_STATE_Closing;
+				break;
+
+				case PPP_EVENT_RTR:
+					*State = PPP_STATE_Closing;
+					Send_Terminate_Ack();
+				break;
+
+				case PPP_EVENT_RTA:
+				case PPP_EVENT_RXJMinus:
+					*State = PPP_STATE_Closed;
+					TimerOn = false;
+					This_Layer_Finished(Layer);
+				break;
+
+				case PPP_EVENT_RUC:
+					*State = PPP_STATE_Closing;
+					Send_Code_Reject();
+				break;
+
+				default:
+					Debug_Print("Illegal Event\r\n");
+				break;
+			}
+		break;
+
+		case PPP_STATE_Stopping:
+			
+			switch (Event)
+			{
+				case PPP_EVENT_Down:
+					*State = PPP_STATE_Starting;
+					TimerOn = false;
+				break;
+
+				case PPP_EVENT_Open:
+					*State = PPP_STATE_Stopping;
+				break;
+
+				case PPP_EVENT_Close:
+					*State = PPP_STATE_Closing;
+				break;
+
+				case PPP_EVENT_TOPlus:
+					Send_Terminate_Request();
+					*State = PPP_STATE_Stopping;
+				break;
+				
+				case PPP_EVENT_TOMinus:
+					This_Layer_Finished(Layer);
+					TimerOn = false;
+					*State = PPP_STATE_Stopped;
+				break;
+
+				case PPP_EVENT_RCRPlus:
+				case PPP_EVENT_RCRMinus:
+				case PPP_EVENT_RCA:
+				case PPP_EVENT_RCN:
+				case PPP_EVENT_RXJPlus:
+				case PPP_EVENT_RXR:
+					*State = PPP_STATE_Stopping;
+				break;
+				
+				case PPP_EVENT_RTR:
+					Send_Terminate_Ack();
+					*State = PPP_STATE_Stopping;
+				break;
+
+				case PPP_EVENT_RTA:
+				case PPP_EVENT_RXJMinus:
+					This_Layer_Finished(Layer);
+					TimerOn = false;
+					*State = PPP_STATE_Stopped;
+				break;
+
+				case PPP_EVENT_RUC:
+					Send_Code_Reject();
+					*State = PPP_STATE_Stopping;
+				break;
+
+				default:
+					Debug_Print("Illegal Event\r\n");
+				break;
+			}
+		break;
+
+		case PPP_STATE_Req_Sent:
+			
+			switch (Event)
+			{
+				case PPP_EVENT_Down:
+					*State = PPP_STATE_Starting;
+					TimerOn = false;
+				break;
+
+				case PPP_EVENT_Open:
+				case PPP_EVENT_RTA:
+				case PPP_EVENT_RXJPlus:
+				case PPP_EVENT_RXR:
+					*State = PPP_STATE_Req_Sent;
+				break;
+
+				case PPP_EVENT_Close:
+					RestartCount = MAX_RESTARTS;
+					LinkTimer = 0;
+					Send_Terminate_Request();
+					*State = PPP_STATE_Closing;
+				break;
+
+				case PPP_EVENT_TOPlus:
+					Send_Configure_Request();
+					*State = PPP_STATE_Req_Sent;
+				break;
+
+				case PPP_EVENT_TOMinus:
+					This_Layer_Finished(Layer);
+					TimerOn = false;
+					*State = PPP_STATE_Stopped;
+				break;
+
+				case PPP_EVENT_RCRPlus:
+					if (Layer != PPP_LAYER_Authentication)											// We faked a Configure request for PAP. So no need to answer it if we're in the authentication phase
+						Send_Configure_Ack();															
+
+					*State = PPP_STATE_Ack_Sent;
+				break;
+
+				case PPP_EVENT_RCRMinus:
+					Send_Configure_Nak_Rej();
+					*State = PPP_STATE_Req_Sent;
+				break;
+
+				case PPP_EVENT_RCA:
+					RestartCount = MAX_RESTARTS;
+					LinkTimer = 0;
+					*State = PPP_STATE_Ack_Rcvd;
+				break;
+
+				case PPP_EVENT_RCN:
+					RestartCount = MAX_RESTARTS;
+					LinkTimer = 0;
+					Send_Configure_Request();
+					*State = PPP_STATE_Req_Sent;
+				break;
+
+				case PPP_EVENT_RTR:
+					Send_Terminate_Ack();
+					*State = PPP_STATE_Req_Sent;
+				break;
+
+				case PPP_EVENT_RUC:
+					Send_Code_Reject();
+					*State = PPP_STATE_Req_Sent;
+				break;
+
+				case PPP_EVENT_RXJMinus:
+					This_Layer_Finished(Layer);
+					TimerOn = false;
+					*State = PPP_STATE_Stopped;
+				break;
+				
+				default:
+					Debug_Print("Illegal Event\r\n");
+				break;
+			}
+		break;
+
+		case PPP_STATE_Ack_Rcvd:
+
+			switch (Event)
+			{
+				case PPP_EVENT_Down:
+					*State = PPP_STATE_Starting;
+					TimerOn = false;
+				break;
+
+				case PPP_EVENT_Open:
+				case PPP_EVENT_RXR:
+					*State = PPP_STATE_Ack_Rcvd;
+				break;
+
+				case PPP_EVENT_Close:
+					RestartCount = MAX_RESTARTS;
+					LinkTimer = 0;
+					Send_Terminate_Request();
+					*State = PPP_STATE_Closing;
+				break;
+
+				case PPP_EVENT_TOPlus:
+				case PPP_EVENT_RCA:
+				case PPP_EVENT_RCN:
+					Send_Configure_Request();
+					*State = PPP_STATE_Req_Sent;
+				break;
+
+				case PPP_EVENT_TOMinus:
+					This_Layer_Finished(Layer);
+					TimerOn = false;
+					*State = PPP_STATE_Stopped;
+				break;
+
+				case PPP_EVENT_RCRPlus:
+					Send_Configure_Ack();
+					This_Layer_Up(Layer);
+					TimerOn = false;
+					*State = PPP_STATE_Opened;
+				break;
+
+				case PPP_EVENT_RCRMinus:
+					Send_Configure_Nak_Rej();
+					*State = PPP_STATE_Ack_Rcvd;
+				break;
+
+				case PPP_EVENT_RTR:
+					Send_Terminate_Ack();
+					*State = PPP_STATE_Req_Sent;
+				break;
+
+				case PPP_EVENT_RTA:
+				case PPP_EVENT_RXJPlus:
+					Send_Terminate_Ack();
+					*State = PPP_STATE_Req_Sent;
+				break;
+
+				case PPP_EVENT_RUC:
+					Send_Code_Reject();
+					*State = PPP_STATE_Ack_Rcvd;
+				break;
+
+				case PPP_EVENT_RXJMinus:
+					This_Layer_Finished(Layer);
+					TimerOn = false;
+					*State = PPP_STATE_Stopped;
+				break;
+				
+				default:
+					Debug_Print("Illegal Event\r\n");
+				break;
+			}
+		break;
+
+		case PPP_STATE_Ack_Sent:
+			
+			switch (Event)
+			{
+				case PPP_EVENT_Down:
+					*State = PPP_STATE_Starting;
+					TimerOn = false;
+				break;
+
+				case PPP_EVENT_Open:
+				case PPP_EVENT_RTA:
+				case PPP_EVENT_RXJPlus:
+				case PPP_EVENT_RXR:
+					*State = PPP_STATE_Ack_Sent;
+				break;
+
+				case PPP_EVENT_Close:
+					RestartCount = MAX_RESTARTS;
+					LinkTimer = 0;
+					Send_Terminate_Request();
+					*State = PPP_STATE_Closing;
+				break;
+
+				case PPP_EVENT_TOPlus:
+					Send_Configure_Request();
+					*State = PPP_STATE_Ack_Sent;
+				break;
+
+				case PPP_EVENT_TOMinus:
+					This_Layer_Finished(Layer);
+					TimerOn = false;
+					*State = PPP_STATE_Stopped;
+				break;
+
+				case PPP_EVENT_RCRPlus:
+					Send_Configure_Ack();
+					*State = PPP_STATE_Ack_Sent;
+				break;
+
+				case PPP_EVENT_RCRMinus:
+					Send_Configure_Nak_Rej();
+					*State = PPP_STATE_Req_Sent;
+				break;
+
+				case PPP_EVENT_RCA:
+					RestartCount = MAX_RESTARTS;
+					LinkTimer = 0;
+					TimerOn = false;
+					This_Layer_Up(Layer);
+					*State = PPP_STATE_Opened;
+				break;
+
+				case PPP_EVENT_RCN:
+					RestartCount = MAX_RESTARTS;
+					LinkTimer = 0;
+					Send_Configure_Request();
+					*State = PPP_STATE_Ack_Sent;
+				break;
+
+				case PPP_EVENT_RTR:
+					Send_Terminate_Ack();
+					*State = PPP_STATE_Req_Sent;
+				break;
+
+				case PPP_EVENT_RUC:
+					Send_Code_Reject();
+					*State = PPP_STATE_Ack_Sent;
+				break;
+
+				case PPP_EVENT_RXJMinus:
+					This_Layer_Finished(Layer);
+					TimerOn = false;
+					*State = PPP_STATE_Stopped;
+				break;
+				
+				default:
+					Debug_Print("Illegal Event\r\n");
+				break;
+			}
+		break;
+
+		case PPP_STATE_Opened:
+
+			switch (Event)
+			{
+				case PPP_EVENT_Down:
+					This_Layer_Down(Layer);
+					*State = PPP_STATE_Starting;
+				break;
+
+				case PPP_EVENT_Open:
+				case PPP_EVENT_RXJPlus:
+					*State = PPP_STATE_Opened;
+				break;
+
+				case PPP_EVENT_Close:
+					This_Layer_Down(Layer);
+					RestartCount = MAX_RESTARTS;
+					LinkTimer = 0;
+					TimerOn = true;
+					Send_Terminate_Request();
+					*State = PPP_STATE_Closing;
+				break;
+
+				case PPP_EVENT_RCRPlus:
+					This_Layer_Down(Layer);
+					TimerOn = true;
+					Send_Configure_Request();
+					Send_Configure_Ack();
+					*State = PPP_STATE_Ack_Sent;
+				break;
+
+				case PPP_EVENT_RCRMinus:
+					This_Layer_Down(Layer);
+					TimerOn = true;
+					Send_Configure_Request();
+					Send_Configure_Nak_Rej();
+					*State = PPP_STATE_Req_Sent;
+				break;
+
+				case PPP_EVENT_RCA:
+				case PPP_EVENT_RCN:
+				case PPP_EVENT_RTA:
+					This_Layer_Down(Layer);
+					TimerOn = true;
+					Send_Configure_Request();
+					*State = PPP_STATE_Req_Sent;
+				break;
+
+				case PPP_EVENT_RTR:
+					This_Layer_Down(Layer);
+					RestartCount = 0;
+					LinkTimer = 0;
+					TimerOn = true;
+					Send_Terminate_Ack();
+					*State = PPP_STATE_Stopping;
+				break;
+
+				case PPP_EVENT_RUC:
+					Send_Code_Reject();
+					*State = PPP_STATE_Opened;
+				break;
+
+				case PPP_EVENT_RXJMinus:
+					This_Layer_Down(Layer);
+					RestartCount = MAX_RESTARTS;
+					LinkTimer = 0;
+					TimerOn = true;
+					Send_Terminate_Request();
+					*State = PPP_STATE_Stopping;
+				break;
+				
+				case PPP_EVENT_RXR:
+					Send_Echo_Reply();
+					*State = PPP_STATE_Opened;
+				break;
+				
+				default:
+					Debug_Print("Illegal Event\r\n");
+				break;
+			}
+		break;
+
+		default:
+		break;
+	}
+}