* Copyright 2003-2006, Waldemar Kornewald <wkornew@gmx.net>
* Distributed under the terms of the MIT License.
*/
#if DEBUG
#include <cstdio>
#endif
#include "Protocol.h"
#include "IPCP.h"
#include <KPPPConfigurePacket.h>
#include <KPPPInterface.h>
#include <settings_tools.h>
#include <PPPoEDevice.h>
#include <cstring>
#include <arpa/inet.h>
#include <net_buffer.h>
#include <net_stack.h>
#include <net/route.h>
#include <sys/sockio.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define RESOLV_CONF_FILE "/boot/system/settings/network/resolv.conf"
extern net_buffer_module_info *gBufferModule;
static struct net_datalink_module_info *sDatalinkModule;
static struct net_stack_module_info *sStackModule;
#if DEBUG
#include <unistd.h>
static int sFD;
static char sDigits[] = "0123456789ABCDEF";
void
dump_packet(net_buffer *packet, const char *direction)
{
if (!packet)
return;
uint8 *data = mtod(packet, uint8*);
uint8 buffer[128];
uint8 bufferIndex = 0;
sprintf((char*) buffer, "Dumping %s packet;len=%ld;pkthdr.len=%d\n", direction,
packet->m_len, packet->m_flags & M_PKTHDR ? packet->m_pkthdr.len : -1);
write(sFD, buffer, strlen((char*) buffer));
for (uint32 index = 0; index < packet->m_len; index++) {
buffer[bufferIndex++] = sDigits[data[index] >> 4];
buffer[bufferIndex++] = sDigits[data[index] & 0x0F];
if (bufferIndex == 32 || index == packet->m_len - 1) {
buffer[bufferIndex++] = '\n';
buffer[bufferIndex] = 0;
write(sFD, buffer, strlen((char*) buffer));
bufferIndex = 0;
}
}
}
#endif
static const bigtime_t kIPCPStateMachineTimeout = 3000000;
IPCP::IPCP(KPPPInterface& interface, driver_parameter *settings)
: KPPPProtocol("IPCP", PPP_NCP_PHASE, IPCP_PROTOCOL, PPP_PROTOCOL_LEVEL,
AF_INET, 0, interface, settings, PPP_INCLUDES_NCP),
fDefaultRoute(NULL),
fRequestPrimaryDNS(false),
fRequestSecondaryDNS(false),
fState(PPP_INITIAL_STATE),
fID(system_time() & 0xFF),
fMaxRequest(10),
fMaxTerminate(2),
fMaxNak(5),
fRequestID(0),
fTerminateID(0),
fNextTimeout(0)
{
memset(&fLocalConfiguration, 0, sizeof(ipcp_configuration));
memset(&fPeerConfiguration, 0, sizeof(ipcp_configuration));
memset(&fLocalRequests, 0, sizeof(ipcp_requests));
memset(&fPeerRequests, 0, sizeof(ipcp_requests));
ParseSideRequests(get_parameter_with_name(IPCP_LOCAL_SIDE_KEY, Settings()),
PPP_LOCAL_SIDE);
ParseSideRequests(get_parameter_with_name(IPCP_PEER_SIDE_KEY, Settings()),
PPP_PEER_SIDE);
get_module(NET_STACK_MODULE_NAME, (module_info **)&sStackModule);
get_module(NET_DATALINK_MODULE_NAME, (module_info **)&sDatalinkModule);
#if DEBUG
sFD = open("/boot/home/ipcpdebug", O_WRONLY | O_CREAT | O_TRUNC);
#endif
}
IPCP::~IPCP()
{
put_module(NET_DATALINK_MODULE_NAME);
put_module(NET_STACK_MODULE_NAME);
#if DEBUG
close(sFD);
#endif
}
void
IPCP::Uninit()
{
RemoveRoutes();
}
status_t
IPCP::StackControl(uint32 op, void *data)
{
TRACE("IPCP: StackControl(op=%ld)\n", op);
switch (op) {
case SIOCSIFADDR:
break;
case SIOCSIFFLAGS:
break;
case SIOCSIFDSTADDR:
break;
case SIOCSIFNETMASK:
break;
default:
ERROR("IPCP: Unknown ioctl: %" B_PRIu32 "\n", op);
return KPPPProtocol::StackControl(op, data);
}
return B_OK;
}
bool
IPCP::Up()
{
TRACE("IPCP: Up() state=%d\n", State());
if (Interface().Mode() == PPP_SERVER_MODE)
return true;
switch (State()) {
case PPP_INITIAL_STATE:
NewState(PPP_REQ_SENT_STATE);
InitializeRestartCount();
SendConfigureRequest();
break;
default:
;
}
return true;
}
bool
IPCP::Down()
{
TRACE("IPCP: Down() state=%d\n", State());
switch (Interface().Phase()) {
case PPP_DOWN_PHASE:
NewState(PPP_INITIAL_STATE);
ReportDownEvent();
break;
// interface is terminating
break;
case PPP_ESTABLISHMENT_PHASE:
// interface is reconfiguring
break;
*/
case PPP_ESTABLISHED_PHASE:
if (State() != PPP_INITIAL_STATE && State() != PPP_CLOSING_STATE) {
NewState(PPP_CLOSING_STATE);
InitializeRestartCount();
SendTerminateRequest();
}
while (State() == PPP_CLOSING_STATE)
snooze(50000);
break;
default:
;
}
return true;
}
status_t
IPCP::Send(net_buffer *packet, uint16 protocolNumber)
{
TRACE("IPCP: Send(0x%X)\n", protocolNumber);
if ((protocolNumber == IP_PROTOCOL && State() == PPP_OPENED_STATE)
|| protocolNumber == IPCP_PROTOCOL) {
#if DEBUG
dump_packet(packet, "outgoing");
#endif
Interface().UpdateIdleSince();
return SendToNext(packet, protocolNumber);
}
ERROR("IPCP: Send() failed because of wrong state or protocol number!\n");
gBufferModule->free(packet);
return B_ERROR;
}
status_t
IPCP::Receive(net_buffer *packet, uint16 protocolNumber)
{
TRACE("IPCP: Receive(0x%X)\n", protocolNumber);
if (!packet)
return B_ERROR;
if (protocolNumber == IP_PROTOCOL)
return ReceiveIPPacket(packet, protocolNumber);
if (protocolNumber != IPCP_PROTOCOL)
return PPP_UNHANDLED;
NetBufferHeaderReader<ppp_lcp_packet> bufferheader(packet);
if (bufferheader.Status() != B_OK)
return B_ERROR;
ppp_lcp_packet &data = bufferheader.Data();
if (ntohs(data.length) < 4)
return B_ERROR;
switch (data.code) {
case PPP_CONFIGURE_REQUEST:
RCREvent(packet);
break;
case PPP_CONFIGURE_ACK:
RCAEvent(packet);
break;
case PPP_CONFIGURE_NAK:
case PPP_CONFIGURE_REJECT:
RCNEvent(packet);
break;
case PPP_TERMINATE_REQUEST:
RTREvent(packet);
break;
case PPP_TERMINATE_ACK:
RTAEvent(packet);
break;
case PPP_CODE_REJECT:
RXJBadEvent(packet);
break;
default:
RUCEvent(packet);
return PPP_REJECTED;
}
return B_OK;
}
status_t
IPCP::ReceiveIPPacket(net_buffer *packet, uint16 protocolNumber)
{
if (protocolNumber != IP_PROTOCOL || State() != PPP_OPENED_STATE)
return PPP_UNHANDLED;
if (packet) {
#if DEBUG
dump_packet(packet, "incoming");
#endif
Interface().UpdateIdleSince();
TRACE("We got 1 IP packet from %s::%s\n", __FILE__, __func__);
gBufferModule->free(packet);
return B_OK;
} else {
ERROR("IPCP: Error: Could not find input function for IP!\n");
gBufferModule->free(packet);
return B_ERROR;
}
}
void
IPCP::Pulse()
{
if (fNextTimeout == 0 || fNextTimeout > system_time())
return;
fNextTimeout = 0;
switch (State()) {
case PPP_CLOSING_STATE:
if (fTerminateCounter <= 0)
TOBadEvent();
else
TOGoodEvent();
break;
case PPP_REQ_SENT_STATE:
case PPP_ACK_RCVD_STATE:
case PPP_ACK_SENT_STATE:
if (fRequestCounter <= 0)
TOBadEvent();
else
TOGoodEvent();
break;
default:
;
}
}
bool
IPCP::ParseSideRequests(const driver_parameter *requests, ppp_side side)
{
if (!requests)
return false;
ipcp_requests *selectedRequests;
if (side == PPP_LOCAL_SIDE) {
selectedRequests = &fLocalRequests;
fRequestPrimaryDNS = fRequestSecondaryDNS = false;
} else
selectedRequests = &fPeerRequests;
memset(selectedRequests, 0, sizeof(ipcp_requests));
in_addr_t address = INADDR_ANY;
for (int32 index = 0; index < requests->parameter_count; index++) {
if (requests->parameters[index].value_count == 0)
continue;
if (strcasecmp(requests->parameters[index].values[0], "auto")) {
address = inet_addr(requests->parameters[index].values[0]);
if (address == INADDR_NONE)
continue;
}
if (!strcasecmp(requests->parameters[index].name, IPCP_IP_ADDRESS_KEY))
selectedRequests->address = address;
else if (!strcasecmp(requests->parameters[index].name, IPCP_NETMASK_KEY))
selectedRequests->netmask = address;
else if (!strcasecmp(requests->parameters[index].name, IPCP_PRIMARY_DNS_KEY)) {
selectedRequests->primaryDNS = address;
if (side == PPP_LOCAL_SIDE)
fRequestPrimaryDNS = true;
} else if (!strcasecmp(requests->parameters[index].name,
IPCP_SECONDARY_DNS_KEY)) {
selectedRequests->secondaryDNS = address;
if (side == PPP_LOCAL_SIDE)
fRequestSecondaryDNS = true;
}
}
return true;
}
net_interface *
get_interface_by_name(net_domain *domain, const char *name)
{
ifreq request;
memset(&request, 0, sizeof(request));
size_t size = sizeof(request);
strlcpy(request.ifr_name, name, IF_NAMESIZE);
if (sDatalinkModule->control(domain, SIOCGIFINDEX, &request, &size) != B_OK) {
TRACE("sDatalinkModule->control failure\n");
return NULL;
}
return sDatalinkModule->get_interface(domain, request.ifr_index);
}
status_t
set_interface_address(net_domain* domain, struct ifaliasreq* inreq)
{
size_t size = sizeof(struct ifaliasreq);
return sDatalinkModule->control(domain, B_SOCKET_SET_ALIAS, inreq, &size);
}
void
IPCP::UpdateAddresses()
{
TRACE("%s::%s: entering UpdateAddresses\n", __FILE__, __func__);
RemoveRoutes();
if (State() != PPP_OPENED_STATE && !Interface().DoesConnectOnDemand())
return;
TRACE("%s::%s: entering ChangeAddress\n", __FILE__, __func__);
if (sDatalinkModule == NULL) {
TRACE("%s::%s: some module not found!\n", __FILE__, __func__);
return;
}
struct sockaddr newAddr = {6, AF_INET, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
struct sockaddr netmask = {6, AF_INET, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
struct sockaddr broadaddr = {6, AF_INET, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
if (fLocalRequests.address != INADDR_ANY)
memcpy(newAddr.sa_data + 2, &fLocalRequests.address, sizeof(in_addr_t));
else if (fLocalConfiguration.address == INADDR_ANY) {
in_addr_t inaddrBroadcast = 0x010F0F0F;
memcpy(newAddr.sa_data + 2, &inaddrBroadcast, sizeof(in_addr_t));
} else
memcpy(newAddr.sa_data + 2, &fLocalConfiguration.address, sizeof(in_addr_t));
struct ifaliasreq inreq;
memset(&inreq, 0, sizeof(struct ifaliasreq));
memcpy(inreq.ifra_name, Interface().Name(), IF_NAMESIZE);
memcpy(&inreq.ifra_addr, &newAddr, sizeof(struct sockaddr));
memcpy(&inreq.ifra_mask, &netmask, sizeof(struct sockaddr));
memcpy(&inreq.ifra_broadaddr, &broadaddr, sizeof(struct sockaddr));
inreq.ifra_index = -1;
net_domain* domain = sStackModule->get_domain(AF_INET);
status_t status = set_interface_address(domain, &inreq);
if (status != B_OK) {
TRACE("%s:%s: set_interface_address Fail!!!!\n", __FILE__, __func__);
return;
}
TRACE("%s:%s: set_interface_address fine\n", __FILE__, __func__);
net_interface* pppInterface = get_interface_by_name(domain, Interface().Name());
if (pppInterface == NULL) {
TRACE("%s::%s: pppInterface not found!\n", __FILE__, __func__);
return;
}
net_interface_address* pppInterfaceAddress = NULL;
while (sDatalinkModule->get_next_interface_address(pppInterface,
&pppInterfaceAddress)) {
if (pppInterfaceAddress->domain->family != AF_INET)
continue;
break;
}
if (Side() == PPP_LOCAL_SIDE && pppInterfaceAddress != NULL) {
struct sockaddr addrGateway = {8, AF_INET, {0x00, 0x00, 0x00, 0x00,
0x00, 0x00} };
if (fPeerRequests.address != INADDR_ANY)
memcpy(addrGateway.sa_data + 2, &fPeerRequests.address,
sizeof(in_addr_t));
else if (fPeerConfiguration.address == INADDR_ANY) {
in_addr_t gateway = 0x020F0F0F;
memcpy(addrGateway.sa_data + 2, &gateway, sizeof(in_addr_t));
} else
memcpy(addrGateway.sa_data + 2, &fPeerConfiguration.address,
sizeof(in_addr_t));
net_route defaultRoute;
defaultRoute.destination = NULL;
defaultRoute.mask = NULL;
defaultRoute.gateway = &addrGateway;
defaultRoute.flags = RTF_DEFAULT | RTF_GATEWAY;
defaultRoute.interface_address = pppInterfaceAddress;
status_t status = sDatalinkModule->add_route(domain, &defaultRoute);
if (status == B_OK)
dprintf("%s::%s: add route default OK!\n", __FILE__, __func__);
else
dprintf("%s::%s: add route default Fail!\n", __FILE__, __func__);
sDatalinkModule->put_interface_address(pppInterfaceAddress);
}
if (Side() == PPP_LOCAL_SIDE) {
int file;
int primary_dns, secondary_dns;
char buf[256];
file = open(RESOLV_CONF_FILE, O_RDWR);
primary_dns = ntohl(fLocalConfiguration.primaryDNS);
secondary_dns = ntohl(fLocalConfiguration.secondaryDNS);
sprintf(buf, "%s\t%d.%d.%d.%d\n%s\t%d.%d.%d.%d\n",
"nameserver",
(primary_dns & 0xff000000) >> 24,
(primary_dns & 0x00ff0000) >> 16,
(primary_dns & 0x0000ff00) >> 8,
(primary_dns & 0x000000ff),
"nameserver",
(secondary_dns & 0xff000000) >> 24,
(secondary_dns & 0x00ff0000) >> 16,
(secondary_dns & 0x0000ff00) >> 8,
(secondary_dns & 0x000000ff));
write(file, buf, strlen(buf));
close(file);
}
}
void
IPCP::RemoveRoutes()
{
TRACE("%s::%s: entering RemoveRoutes!\n", __FILE__, __func__);
char *ethernetName = NULL;
PPPoEDevice* pppoeDevice = (PPPoEDevice *)Interface().Device();
if (pppoeDevice == NULL)
return;
ethernetName = pppoeDevice->EthernetIfnet()->name;
if (ethernetName == NULL)
return;
net_domain* domain = sStackModule->get_domain(AF_INET);
net_interface* pppInterface = get_interface_by_name(domain, ethernetName);
if (pppInterface == NULL) {
TRACE("%s::%s: pppInterface not found!\n", __FILE__, __func__);
return;
}
net_interface_address* pppInterfaceAddress = NULL;
while (sDatalinkModule->get_next_interface_address(pppInterface,
&pppInterfaceAddress)) {
if (pppInterfaceAddress->domain->family != AF_INET)
continue;
net_route oldDefaultRoute;
oldDefaultRoute.destination = NULL;
oldDefaultRoute.mask = NULL;
oldDefaultRoute.gateway = NULL;
oldDefaultRoute.flags= RTF_DEFAULT;
oldDefaultRoute.interface_address = pppInterfaceAddress;
status_t status = sDatalinkModule->remove_route(domain, &oldDefaultRoute);
sDatalinkModule->put_interface_address(pppInterfaceAddress);
if (status == B_OK)
dprintf("IPCP::RemoveRoutes: remove old default route OK!\n");
else
dprintf("IPCP::RemoveRoutes: remove old default route Fail!\n");
break;
}
if (fDefaultRoute) {
struct sockaddr_in netmask;
memset(&netmask, 0, sizeof(struct sockaddr_in));
netmask.sin_family = AF_INET;
netmask.sin_addr.s_addr = fLocalRequests.netmask;
netmask.sin_len = sizeof(struct sockaddr_in);
ERROR("IPCP: RemoveRoutes(): could not remove default/subnet route!\n");
fDefaultRoute = NULL;
}
}
uint8
IPCP::NextID()
{
return (uint8) atomic_add(&fID, 1);
}
void
IPCP::NewState(ppp_state next)
{
TRACE("IPCP: NewState(%d) state=%d\n", next, State());
if (State() == PPP_INITIAL_STATE && next != State())
UpStarted();
else if (State() == PPP_OPENED_STATE && next != State())
DownStarted();
if (next < PPP_CLOSING_STATE || next == PPP_OPENED_STATE)
fNextTimeout = 0;
fState = next;
}
void
IPCP::TOGoodEvent()
{
#if DEBUG
printf("IPCP: TOGoodEvent() state=%d\n", State());
#endif
switch (State()) {
case PPP_CLOSING_STATE:
SendTerminateRequest();
break;
case PPP_ACK_RCVD_STATE:
NewState(PPP_REQ_SENT_STATE);
case PPP_REQ_SENT_STATE:
case PPP_ACK_SENT_STATE:
SendConfigureRequest();
break;
default:
IllegalEvent(PPP_TO_GOOD_EVENT);
}
}
void
IPCP::TOBadEvent()
{
TRACE("IPCP: TOBadEvent() state=%d\n", State());
switch (State()) {
case PPP_CLOSING_STATE:
NewState(PPP_INITIAL_STATE);
ReportDownEvent();
break;
case PPP_REQ_SENT_STATE:
case PPP_ACK_RCVD_STATE:
case PPP_ACK_SENT_STATE:
NewState(PPP_INITIAL_STATE);
ReportUpFailedEvent();
break;
default:
IllegalEvent(PPP_TO_BAD_EVENT);
}
}
void
IPCP::RCREvent(net_buffer *packet)
{
TRACE("IPCP: RCREvent() state=%d\n", State());
KPPPConfigurePacket request(packet);
KPPPConfigurePacket nak(PPP_CONFIGURE_NAK);
KPPPConfigurePacket reject(PPP_CONFIGURE_REJECT);
NetBufferHeaderReader<ppp_lcp_packet> bufferheader(packet);
if (bufferheader.Status() != B_OK)
return;
ppp_lcp_packet &lcpHeader = bufferheader.Data();
if (fID == lcpHeader.id)
fID -= 128;
nak.SetID(request.ID());
reject.SetID(request.ID());
ppp_configure_item *item;
in_addr_t *requestedAddress, *wishedAddress = NULL;
for (int32 index = 0; index < request.CountItems(); index++) {
item = request.ItemAt(index);
if (!item)
continue;
switch (item->type) {
case IPCP_ADDRESSES:
case IPCP_ADDRESS:
wishedAddress = &fPeerRequests.address;
break;
case IPCP_PRIMARY_DNS:
wishedAddress = &fPeerRequests.primaryDNS;
break;
case IPCP_SECONDARY_DNS:
wishedAddress = &fPeerRequests.secondaryDNS;
break;
}
switch (item->type) {
case IPCP_ADDRESSES:
case IPCP_ADDRESS:
case IPCP_PRIMARY_DNS:
case IPCP_SECONDARY_DNS:
if (item->length != 6) {
gBufferModule->free(packet);
NewState(PPP_INITIAL_STATE);
ReportUpFailedEvent();
return;
}
requestedAddress = (in_addr_t*) item->data;
if (*wishedAddress == INADDR_ANY) {
if (*requestedAddress == INADDR_ANY) {
gBufferModule->free(packet);
NewState(PPP_INITIAL_STATE);
ReportUpFailedEvent();
return;
}
} else if (*requestedAddress != *wishedAddress) {
ip_item ipItem;
ipItem.type = item->type;
ipItem.length = 6;
ipItem.address = *wishedAddress;
nak.AddItem((ppp_configure_item*) &ipItem);
}
break;
default:
reject.AddItem(item);
}
}
if (!request.ItemWithType(IPCP_ADDRESS) && fPeerRequests.address == INADDR_ANY) {
ip_item ipItem;
ipItem.type = IPCP_ADDRESS;
ipItem.length = 6;
ipItem.address = INADDR_ANY;
nak.AddItem((ppp_configure_item*) &ipItem);
}
if (nak.CountItems() > 0) {
RCRBadEvent(nak.ToNetBuffer(Interface().MRU()), NULL);
gBufferModule->free(packet);
} else if (reject.CountItems() > 0) {
RCRBadEvent(NULL, reject.ToNetBuffer(Interface().MRU()));
gBufferModule->free(packet);
} else
RCRGoodEvent(packet);
}
void
IPCP::RCRGoodEvent(net_buffer *packet)
{
TRACE("IPCP: RCRGoodEvent() state=%d\n", State());
switch (State()) {
case PPP_INITIAL_STATE:
NewState(PPP_ACK_SENT_STATE);
InitializeRestartCount();
SendConfigureRequest();
SendConfigureAck(packet);
break;
case PPP_REQ_SENT_STATE:
NewState(PPP_ACK_SENT_STATE);
case PPP_ACK_SENT_STATE:
SendConfigureAck(packet);
break;
case PPP_ACK_RCVD_STATE:
NewState(PPP_OPENED_STATE);
SendConfigureAck(packet);
ReportUpEvent();
break;
case PPP_OPENED_STATE:
NewState(PPP_ACK_SENT_STATE);
SendConfigureRequest();
SendConfigureAck(packet);
break;
default:
gBufferModule->free(packet);
}
}
void
IPCP::RCRBadEvent(net_buffer *nak, net_buffer *reject)
{
TRACE("IPCP: RCRBadEvent() state=%d\n", State());
uint16 lcpHdrRejectLength = 0;
uint16 lcpHdrNakLength = 0;
if (nak) {
NetBufferHeaderReader<ppp_lcp_packet> nakBufferHeaderReader(nak);
if (nakBufferHeaderReader.Status() != B_OK)
return;
ppp_lcp_packet &lcpNakPacket = nakBufferHeaderReader.Data();
lcpHdrNakLength = lcpNakPacket.length;
}
if (reject) {
NetBufferHeaderReader<ppp_lcp_packet> rejectBufferHeaderReader(reject);
if (rejectBufferHeaderReader.Status() != B_OK)
return;
ppp_lcp_packet &lcpRejectPacket = rejectBufferHeaderReader.Data();
lcpHdrRejectLength = lcpRejectPacket.length;
}
switch (State()) {
case PPP_OPENED_STATE:
NewState(PPP_REQ_SENT_STATE);
SendConfigureRequest();
case PPP_ACK_SENT_STATE:
if (State() == PPP_ACK_SENT_STATE)
NewState(PPP_REQ_SENT_STATE);
case PPP_INITIAL_STATE:
case PPP_REQ_SENT_STATE:
case PPP_ACK_RCVD_STATE:
if (nak && ntohs(lcpHdrNakLength) > 3)
SendConfigureNak(nak);
else if (reject && ntohs(lcpHdrRejectLength) > 3)
SendConfigureNak(reject);
return;
default:
;
}
if (nak)
gBufferModule->free(nak);
if (reject)
gBufferModule->free(reject);
}
void
IPCP::RCAEvent(net_buffer *packet)
{
ERROR("IPCP: RCAEvent() state=%d\n", State());
NetBufferHeaderReader<ppp_lcp_packet> bufferheader(packet);
if (bufferheader.Status() != B_OK)
return;
ppp_lcp_packet &lcpHeader = bufferheader.Data();
if (fRequestID != lcpHeader.id) {
gBufferModule->free(packet);
return;
}
KPPPConfigurePacket ack(packet);
ppp_configure_item *item;
in_addr_t *requestedAddress, *wishedAddress = NULL, *configuredAddress = NULL;
for (int32 index = 0; index < ack.CountItems(); index++) {
item = ack.ItemAt(index);
if (!item)
continue;
switch (item->type) {
case IPCP_ADDRESSES:
case IPCP_ADDRESS:
wishedAddress = &fLocalRequests.address;
configuredAddress = &fLocalConfiguration.address;
break;
case IPCP_PRIMARY_DNS:
wishedAddress = &fLocalRequests.primaryDNS;
configuredAddress = &fLocalConfiguration.primaryDNS;
break;
case IPCP_SECONDARY_DNS:
wishedAddress = &fLocalRequests.secondaryDNS;
configuredAddress = &fLocalConfiguration.secondaryDNS;
break;
}
switch (item->type) {
case IPCP_ADDRESSES:
case IPCP_ADDRESS:
case IPCP_PRIMARY_DNS:
case IPCP_SECONDARY_DNS:
requestedAddress = (in_addr_t*) item->data;
if ((*wishedAddress == INADDR_ANY && *requestedAddress != INADDR_ANY)
|| *wishedAddress == *requestedAddress)
*configuredAddress = *requestedAddress;
break;
default:
;
}
}
if (!ack.ItemWithType(IPCP_ADDRESS))
fLocalConfiguration.address = fLocalRequests.address;
switch (State()) {
case PPP_INITIAL_STATE:
IllegalEvent(PPP_RCA_EVENT);
break;
case PPP_REQ_SENT_STATE:
NewState(PPP_ACK_RCVD_STATE);
InitializeRestartCount();
break;
case PPP_ACK_RCVD_STATE:
NewState(PPP_REQ_SENT_STATE);
SendConfigureRequest();
break;
case PPP_ACK_SENT_STATE:
NewState(PPP_OPENED_STATE);
InitializeRestartCount();
ReportUpEvent();
break;
case PPP_OPENED_STATE:
NewState(PPP_REQ_SENT_STATE);
SendConfigureRequest();
break;
default:
;
}
gBufferModule->free(packet);
}
void
IPCP::RCNEvent(net_buffer *packet)
{
TRACE("IPCP: RCNEvent() state=%d\n", State());
NetBufferHeaderReader<ppp_lcp_packet> bufferheader(packet);
if (bufferheader.Status() != B_OK)
return;
ppp_lcp_packet &lcpHeader = bufferheader.Data();
if (fRequestID != lcpHeader.id) {
gBufferModule->free(packet);
return;
}
KPPPConfigurePacket nak_reject(packet);
ppp_configure_item *item;
in_addr_t *requestedAddress;
if (nak_reject.Code() == PPP_CONFIGURE_NAK)
for (int32 index = 0; index < nak_reject.CountItems(); index++) {
item = nak_reject.ItemAt(index);
if (!item)
continue;
switch (item->type) {
case IPCP_ADDRESSES:
case IPCP_ADDRESS:
if (item->length != 6)
continue;
requestedAddress = (in_addr_t*) item->data;
if (fLocalRequests.address == INADDR_ANY
&& *requestedAddress != INADDR_ANY)
fLocalConfiguration.address = *requestedAddress;
break;
case IPCP_PRIMARY_DNS:
if (item->length != 6)
continue;
requestedAddress = (in_addr_t*) item->data;
if (fRequestPrimaryDNS
&& fLocalRequests.primaryDNS == INADDR_ANY
&& *requestedAddress != INADDR_ANY)
fLocalConfiguration.primaryDNS = *requestedAddress;
break;
case IPCP_SECONDARY_DNS:
if (item->length != 6)
continue;
requestedAddress = (in_addr_t*) item->data;
if (fRequestSecondaryDNS
&& fLocalRequests.secondaryDNS == INADDR_ANY
&& *requestedAddress != INADDR_ANY)
fLocalConfiguration.secondaryDNS = *requestedAddress;
break;
default:
;
}
}
else if (nak_reject.Code() == PPP_CONFIGURE_REJECT)
for (int32 index = 0; index < nak_reject.CountItems(); index++) {
item = nak_reject.ItemAt(index);
if (!item)
continue;
switch (item->type) {
default:
gBufferModule->free(packet);
NewState(PPP_INITIAL_STATE);
ReportUpFailedEvent();
return;
}
}
switch (State()) {
case PPP_INITIAL_STATE:
IllegalEvent(PPP_RCN_EVENT);
break;
case PPP_REQ_SENT_STATE:
case PPP_ACK_SENT_STATE:
InitializeRestartCount();
case PPP_ACK_RCVD_STATE:
case PPP_OPENED_STATE:
if (State() == PPP_ACK_RCVD_STATE || State() == PPP_OPENED_STATE)
NewState(PPP_REQ_SENT_STATE);
SendConfigureRequest();
break;
default:
;
}
gBufferModule->free(packet);
}
void
IPCP::RTREvent(net_buffer *packet)
{
TRACE("IPCP: RTREvent() state=%d\n", State());
NetBufferHeaderReader<ppp_lcp_packet> bufferheader(packet);
if (bufferheader.Status() != B_OK)
return;
ppp_lcp_packet &lcpHeader = bufferheader.Data();
if (fID == lcpHeader.id)
fID -= 128;
switch (State()) {
case PPP_INITIAL_STATE:
IllegalEvent(PPP_RTR_EVENT);
break;
case PPP_ACK_RCVD_STATE:
case PPP_ACK_SENT_STATE:
NewState(PPP_REQ_SENT_STATE);
case PPP_CLOSING_STATE:
case PPP_REQ_SENT_STATE:
SendTerminateAck(packet);
return;
case PPP_OPENED_STATE:
NewState(PPP_CLOSING_STATE);
ResetRestartCount();
SendTerminateAck(packet);
return;
default:
;
}
gBufferModule->free(packet);
}
void
IPCP::RTAEvent(net_buffer *packet)
{
TRACE("IPCP: RTAEvent() state=%d\n", State());
NetBufferHeaderReader<ppp_lcp_packet> bufferheader(packet);
if (bufferheader.Status() != B_OK)
return;
ppp_lcp_packet &lcpHeader = bufferheader.Data();
if (fTerminateID != lcpHeader.id) {
gBufferModule->free(packet);
return;
}
switch (State()) {
case PPP_INITIAL_STATE:
IllegalEvent(PPP_RTA_EVENT);
break;
case PPP_CLOSING_STATE:
NewState(PPP_INITIAL_STATE);
ReportDownEvent();
break;
case PPP_ACK_RCVD_STATE:
NewState(PPP_REQ_SENT_STATE);
break;
case PPP_OPENED_STATE:
NewState(PPP_REQ_SENT_STATE);
SendConfigureRequest();
break;
default:
;
}
gBufferModule->free(packet);
}
void
IPCP::RUCEvent(net_buffer *packet)
{
TRACE("IPCP: RUCEvent() state=%d\n", State());
SendCodeReject(packet);
}
void
IPCP::RXJBadEvent(net_buffer *packet)
{
TRACE("IPCP: RXJBadEvent() state=%d\n", State());
switch (State()) {
case PPP_INITIAL_STATE:
IllegalEvent(PPP_RXJ_BAD_EVENT);
break;
case PPP_CLOSING_STATE:
NewState(PPP_INITIAL_STATE);
ReportDownEvent();
break;
case PPP_REQ_SENT_STATE:
case PPP_ACK_RCVD_STATE:
case PPP_ACK_SENT_STATE:
NewState(PPP_INITIAL_STATE);
ReportUpFailedEvent();
break;
case PPP_OPENED_STATE:
NewState(PPP_CLOSING_STATE);
InitializeRestartCount();
SendTerminateRequest();
break;
default:
;
}
gBufferModule->free(packet);
}
void
IPCP::IllegalEvent(ppp_event event)
{
ERROR("IPCP: IllegalEvent(event=%d) state=%d\n", event, State());
}
void
IPCP::ReportUpFailedEvent()
{
memset(&fLocalConfiguration, 0, sizeof(ipcp_configuration));
memset(&fPeerConfiguration, 0, sizeof(ipcp_configuration));
UpdateAddresses();
UpFailedEvent();
}
void
IPCP::ReportUpEvent()
{
UpdateAddresses();
UpEvent();
}
void
IPCP::ReportDownEvent()
{
memset(&fLocalConfiguration, 0, sizeof(ipcp_configuration));
memset(&fPeerConfiguration, 0, sizeof(ipcp_configuration));
dprintf("ppp down, and leaving old address and rotues\n");
DownEvent();
}
void
IPCP::InitializeRestartCount()
{
fRequestCounter = fMaxRequest;
fTerminateCounter = fMaxTerminate;
fNakCounter = fMaxNak;
}
void
IPCP::ResetRestartCount()
{
fRequestCounter = 0;
fTerminateCounter = 0;
fNakCounter = 0;
}
bool
IPCP::SendConfigureRequest()
{
TRACE("IPCP: SendConfigureRequest() state=%d\n", State());
--fRequestCounter;
fNextTimeout = system_time() + kIPCPStateMachineTimeout;
KPPPConfigurePacket request(PPP_CONFIGURE_REQUEST);
request.SetID(NextID());
fRequestID = request.ID();
ip_item ipItem;
ipItem.length = 6;
ipItem.type = IPCP_ADDRESS;
if (fLocalRequests.address == INADDR_ANY)
ipItem.address = (fLocalConfiguration.address);
else
ipItem.address =(fLocalRequests.address);
request.AddItem((ppp_configure_item*) &ipItem);
TRACE("IPCP: SCR: confaddr=%X; reqaddr=%X; addr=%X\n",
fLocalConfiguration.address, fLocalRequests.address,
((ip_item*)request.ItemAt(0))->address);
if (fRequestPrimaryDNS && fLocalRequests.primaryDNS == INADDR_ANY) {
ipItem.type = IPCP_PRIMARY_DNS;
ipItem.address = fLocalConfiguration.primaryDNS;
request.AddItem((ppp_configure_item*) &ipItem);
}
if (fRequestSecondaryDNS && fLocalRequests.primaryDNS == INADDR_ANY) {
ipItem.type = IPCP_SECONDARY_DNS;
ipItem.address = fLocalConfiguration.secondaryDNS;
request.AddItem((ppp_configure_item*) &ipItem);
}
return Send(request.ToNetBuffer(Interface().MRU())) == B_OK;
}
bool
IPCP::SendConfigureAck(net_buffer *packet)
{
TRACE("IPCP: SendConfigureAck() state=%d\n", State());
if (!packet)
return false;
NetBufferHeaderReader<ppp_lcp_packet> bufferheader(packet);
if (bufferheader.Status() != B_OK)
return false;
ppp_lcp_packet &lcpheader = bufferheader.Data();
lcpheader.code = PPP_CONFIGURE_ACK;
bufferheader.Sync();
KPPPConfigurePacket ack(packet);
ppp_configure_item *item;
in_addr_t *requestedAddress, *wishedAddress = NULL, *configuredAddress = NULL;
for (int32 index = 0; index < ack.CountItems(); index++) {
item = ack.ItemAt(index);
if (!item)
continue;
switch (item->type) {
case IPCP_ADDRESSES:
case IPCP_ADDRESS:
wishedAddress = &fPeerRequests.address;
configuredAddress = &fPeerConfiguration.address;
break;
case IPCP_PRIMARY_DNS:
wishedAddress = &fPeerRequests.primaryDNS;
configuredAddress = &fPeerConfiguration.primaryDNS;
break;
case IPCP_SECONDARY_DNS:
wishedAddress = &fPeerRequests.secondaryDNS;
configuredAddress = &fPeerConfiguration.secondaryDNS;
break;
}
switch (item->type) {
case IPCP_ADDRESSES:
case IPCP_ADDRESS:
case IPCP_PRIMARY_DNS:
case IPCP_SECONDARY_DNS:
requestedAddress = (in_addr_t*) item->data;
if ((*wishedAddress == INADDR_ANY && *requestedAddress != INADDR_ANY)
|| *wishedAddress == *requestedAddress)
*configuredAddress = *requestedAddress;
break;
default:
;
}
}
if (!ack.ItemWithType(IPCP_ADDRESS))
fPeerConfiguration.address = fPeerRequests.address;
return Send(packet) == B_OK;
}
bool
IPCP::SendConfigureNak(net_buffer *packet)
{
TRACE("IPCP: SendConfigureNak() state=%d\n", State());
if (!packet)
return false;
NetBufferHeaderReader<ppp_lcp_packet> bufferheader(packet);
if (bufferheader.Status() != B_OK)
return false;
ppp_lcp_packet &nak = bufferheader.Data();
if (nak.code == PPP_CONFIGURE_NAK) {
if (fNakCounter == 0) {
nak.code = PPP_CONFIGURE_REJECT;
} else
--fNakCounter;
}
bufferheader.Sync();
return Send(packet) == B_OK;
}
bool
IPCP::SendTerminateRequest()
{
TRACE("IPCP: SendTerminateRequest() state=%d\n", State());
--fTerminateCounter;
fNextTimeout = system_time() + kIPCPStateMachineTimeout;
net_buffer *packet = gBufferModule->create(256);
if (!packet)
return false;
ppp_lcp_packet *request;
status_t status = gBufferModule->append_size(packet, 1492, (void **)(&request));
if (status != B_OK)
return false;
request->code = PPP_TERMINATE_REQUEST;
request->id = fTerminateID = NextID();
request->length = htons(4);
status = gBufferModule->trim(packet, 4);
if (status != B_OK)
return false;
return Send(packet) == B_OK;
}
bool
IPCP::SendTerminateAck(net_buffer *request)
{
TRACE("IPCP: SendTerminateAck() state=%d\n", State());
net_buffer *reply = request;
if (!reply) {
reply = gBufferModule->create(256);
ppp_lcp_packet *ack;
status_t status = gBufferModule->append_size(reply, 1492, (void **)(&ack));
if (status != B_OK) {
gBufferModule->free(reply);
return false;
}
ack->id = NextID();
ack->code = PPP_TERMINATE_ACK;
ack->length = htons(4);
gBufferModule->trim(reply, 4);
} else {
NetBufferHeaderReader<ppp_lcp_packet> bufferHeader(reply);
if (bufferHeader.Status() < B_OK)
return false;
ppp_lcp_packet &ack = bufferHeader.Data();
ack.code = PPP_TERMINATE_ACK;
ack.length = htons(4);
}
return Send(reply) == B_OK;
}
bool
IPCP::SendCodeReject(net_buffer *packet)
{
TRACE("IPCP: SendCodeReject() state=%d\n", State());
if (!packet)
return false;
NetBufferPrepend<ppp_lcp_packet> bufferHeader(packet);
if (bufferHeader.Status() != B_OK)
return false;
ppp_lcp_packet &reject = bufferHeader.Data();
reject.code = PPP_CODE_REJECT;
reject.id = NextID();
reject.length = htons(packet->size);
bufferHeader.Sync();
return Send(packet) == B_OK;
}