* Copyright 2003-2007, Waldemar Kornewald <wkornew@gmx.net>
* Distributed under the terms of the MIT License.
*/
\brief The LCP protocol.
Every PPP interface \e must have an LCP protocol. It is used for establishing
and terminating connections. \n
When establishing a connecition the LCP protocol determines connection-specific
settings like the packet MRU. These settings are handled by the KPPPOptionHandler
class. Additional LCP codes like the PPP Multilink-Protocol uses them should
be implemented through the KPPPLCPExtension class. \n
*/
#include <KPPPInterface.h>
#include <KPPPDevice.h>
#include <KPPPLCPExtension.h>
#include <KPPPOptionHandler.h>
#include <netinet/in.h>
#include <net_buffer.h>
#include <sys/socket.h>
extern net_buffer_module_info *gBufferModule;
KPPPLCP::KPPPLCP(KPPPInterface& interface)
:
KPPPProtocol("LCP", PPP_ESTABLISHMENT_PHASE, PPP_LCP_PROTOCOL,
PPP_PROTOCOL_LEVEL, AF_UNSPEC, 0, interface, NULL, PPP_ALWAYS_ALLOWED),
fStateMachine(interface.StateMachine()),
fTarget(NULL)
{
SetUpRequested(false);
}
KPPPLCP::~KPPPLCP()
{
while (CountOptionHandlers())
delete OptionHandlerAt(0);
while (CountLCPExtensions())
delete LCPExtensionAt(0);
}
NOTE: You can only add option handlers in \c PPP_DOWN_PHASE. \n
There may only be one handler per option type!
*/
bool
KPPPLCP::AddOptionHandler(KPPPOptionHandler *optionHandler)
{
if (!optionHandler || &optionHandler->Interface() != &Interface())
return false;
if (Interface().Phase() != PPP_DOWN_PHASE
|| OptionHandlerFor(optionHandler->Type()))
return false;
return fOptionHandlers.AddItem(optionHandler);
}
NOTE: You can only remove option handlers in \c PPP_DOWN_PHASE.
*/
bool
KPPPLCP::RemoveOptionHandler(KPPPOptionHandler *optionHandler)
{
if (Interface().Phase() != PPP_DOWN_PHASE)
return false;
return fOptionHandlers.RemoveItem(optionHandler);
}
KPPPOptionHandler*
KPPPLCP::OptionHandlerAt(int32 index) const
{
dprintf("total optionhandler count %" B_PRId32 "\n", CountOptionHandlers());
KPPPOptionHandler *optionHandler = fOptionHandlers.ItemAt(index);
if (optionHandler == fOptionHandlers.GetDefaultItem())
return NULL;
return optionHandler;
}
KPPPOptionHandler*
KPPPLCP::OptionHandlerFor(uint8 type, int32 *start) const
{
int32 index = start ? *start : 0;
if (index < 0)
return NULL;
KPPPOptionHandler *current = OptionHandlerAt(index);
for (; current; current = OptionHandlerAt(++index)) {
if (current->Type() == type) {
if (start)
*start = index;
return current;
}
}
return NULL;
}
NOTE: You can only add LCP extensions in \c PPP_DOWN_PHASE.
*/
bool
KPPPLCP::AddLCPExtension(KPPPLCPExtension *lcpExtension)
{
if (!lcpExtension || &lcpExtension->Interface() != &Interface())
return false;
if (Interface().Phase() != PPP_DOWN_PHASE)
return false;
return fLCPExtensions.AddItem(lcpExtension);
}
NOTE: You can only remove LCP extensions in \c PPP_DOWN_PHASE.
*/
bool
KPPPLCP::RemoveLCPExtension(KPPPLCPExtension *lcpExtension)
{
if (Interface().Phase() != PPP_DOWN_PHASE)
return false;
return fLCPExtensions.RemoveItem(lcpExtension);
}
KPPPLCPExtension*
KPPPLCP::LCPExtensionAt(int32 index) const
{
dprintf("LCPExtension count %" B_PRId32 "\n", CountLCPExtensions());
KPPPLCPExtension *lcpExtension = fLCPExtensions.ItemAt(index);
if (lcpExtension == fLCPExtensions.GetDefaultItem())
return NULL;
return lcpExtension;
}
KPPPLCPExtension*
KPPPLCP::LCPExtensionFor(uint8 code, int32 *start) const
{
int32 index = start ? *start : 0;
if (index < 0)
return NULL;
KPPPLCPExtension *current = LCPExtensionAt(index);
for (; current; current = LCPExtensionAt(++index)) {
if (current->Code() == code) {
if (start)
*start = index;
return current;
}
}
return NULL;
}
bool
KPPPLCP::Up()
{
return true;
}
bool
KPPPLCP::Down()
{
return true;
}
status_t
KPPPLCP::Send(net_buffer *packet, uint16 protocolNumber)
{
if (Target())
return Target()->Send(packet, PPP_LCP_PROTOCOL);
else
return Interface().Send(packet, PPP_LCP_PROTOCOL);
}
status_t
KPPPLCP::Receive(net_buffer *packet, uint16 protocolNumber)
{
if (!packet)
return B_ERROR;
if (protocolNumber != PPP_LCP_PROTOCOL) {
ERROR("KPPPLCP::Receive(): wrong protocol number!\n");
return PPP_UNHANDLED;
}
NetBufferHeaderReader<ppp_lcp_packet> bufferHeader(packet);
if (bufferHeader.Status() < B_OK)
return bufferHeader.Status();
ppp_lcp_packet &data = bufferHeader.Data();
net_buffer *copy = gBufferModule->duplicate(packet);
if (ntohs(data.length) < 4)
return B_ERROR;
bool handled = true;
switch (data.code) {
case PPP_CONFIGURE_REQUEST:
StateMachine().RCREvent(packet);
break;
case PPP_CONFIGURE_ACK:
StateMachine().RCAEvent(packet);
break;
case PPP_CONFIGURE_NAK:
case PPP_CONFIGURE_REJECT:
StateMachine().RCNEvent(packet);
break;
case PPP_TERMINATE_REQUEST:
StateMachine().RTREvent(packet);
break;
case PPP_TERMINATE_ACK:
StateMachine().RTAEvent(packet);
break;
case PPP_CODE_REJECT:
StateMachine().RXJEvent(packet);
break;
case PPP_PROTOCOL_REJECT:
StateMachine().RXJEvent(packet);
break;
case PPP_ECHO_REQUEST:
case PPP_ECHO_REPLY:
case PPP_DISCARD_REQUEST:
StateMachine().RXREvent(packet);
break;
default:
handled = false;
}
packet = copy;
if (!packet)
return handled ? B_OK : B_ERROR;
status_t result = B_OK;
int32 index = 0;
KPPPLCPExtension *lcpExtension = LCPExtensionFor(data.code, &index);
for (; lcpExtension; lcpExtension = LCPExtensionFor(data.code, &(++index))) {
if (!lcpExtension->IsEnabled())
continue;
result = lcpExtension->Receive(packet, data.code);
if (result == B_OK)
handled = true;
else if (result != PPP_UNHANDLED) {
gBufferModule->free(packet);
return result;
}
}
if (!handled) {
StateMachine().RUCEvent(packet, PPP_LCP_PROTOCOL, PPP_CODE_REJECT);
return PPP_REJECTED;
}
gBufferModule->free(packet);
return result;
}
void
KPPPLCP::Pulse()
{
StateMachine().TimerEvent();
for (int32 index = 0; index < CountLCPExtensions(); index++)
LCPExtensionAt(index)->Pulse();
}