2010-03-23 00:05:57 +00:00
|
|
|
/*
|
|
|
|
*
|
|
|
|
* PPP library with GLib integration
|
|
|
|
*
|
|
|
|
* Copyright (C) 2009-2010 Intel Corporation. All rights reserved.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include <config.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <termios.h>
|
|
|
|
#include <glib.h>
|
|
|
|
#include <arpa/inet.h>
|
2010-03-23 03:01:28 +00:00
|
|
|
|
2010-03-23 00:05:57 +00:00
|
|
|
#include "gatppp.h"
|
|
|
|
#include "ppp.h"
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
2010-03-23 02:56:53 +00:00
|
|
|
static const char *pppcp_state_strings[] =
|
2010-03-23 00:05:57 +00:00
|
|
|
{"INITIAL", "STARTING", "CLOSED", "STOPPED", "CLOSING", "STOPPING",
|
|
|
|
"REQSENT", "ACKRCVD", "ACKSENT", "OPENED" };
|
|
|
|
|
2010-03-23 02:56:53 +00:00
|
|
|
#define pppcp_trace(p) do { \
|
|
|
|
g_print("%s: current state %d:%s\n", __FUNCTION__, \
|
|
|
|
p->state, pppcp_state_strings[p->state]); \
|
|
|
|
} while (0)
|
2010-03-23 00:05:57 +00:00
|
|
|
#else
|
2010-03-23 02:56:53 +00:00
|
|
|
#define pppcp_trace(p) do { } while (0)
|
2010-03-23 00:05:57 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#define pppcp_to_ppp_packet(p) \
|
2010-03-24 17:13:24 +00:00
|
|
|
(((guint8 *) p) - PPP_HEADROOM)
|
2010-03-23 00:05:57 +00:00
|
|
|
|
|
|
|
struct pppcp_event {
|
|
|
|
enum pppcp_event_type type;
|
|
|
|
gint len;
|
|
|
|
guint8 data[0];
|
|
|
|
};
|
|
|
|
|
2010-03-25 03:26:27 +00:00
|
|
|
#define INITIAL_RESTART_TIMEOUT 3 /* restart interval in seconds */
|
2010-03-23 00:05:57 +00:00
|
|
|
#define MAX_TERMINATE 2
|
|
|
|
#define MAX_CONFIGURE 10
|
|
|
|
#define MAX_FAILURE 5
|
|
|
|
#define CP_HEADER_SZ 4
|
|
|
|
|
2010-03-24 17:13:24 +00:00
|
|
|
static void pppcp_packet_free(struct pppcp_packet *packet)
|
|
|
|
{
|
|
|
|
g_free(pppcp_to_ppp_packet(packet));
|
|
|
|
}
|
|
|
|
|
2010-03-23 00:05:57 +00:00
|
|
|
static struct pppcp_packet *pppcp_packet_new(struct pppcp_data *data,
|
|
|
|
guint type, guint bufferlen)
|
|
|
|
{
|
|
|
|
struct pppcp_packet *packet;
|
|
|
|
struct ppp_header *ppp_packet;
|
|
|
|
guint16 packet_length = bufferlen + sizeof(*packet);
|
|
|
|
|
|
|
|
ppp_packet = g_try_malloc0(packet_length + 2);
|
|
|
|
if (!ppp_packet)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* add our protocol information */
|
|
|
|
ppp_packet->proto = htons(data->proto);
|
|
|
|
|
|
|
|
/* advance past protocol to add CP header information */
|
|
|
|
packet = (struct pppcp_packet *) (ppp_packet->info);
|
|
|
|
|
|
|
|
packet->length = htons(packet_length);
|
|
|
|
packet->code = type;
|
|
|
|
return packet;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean pppcp_timeout(gpointer user_data)
|
|
|
|
{
|
2010-03-25 03:26:28 +00:00
|
|
|
struct pppcp_timer_data *timer_data = user_data;
|
2010-03-23 00:05:57 +00:00
|
|
|
|
2010-03-25 03:26:28 +00:00
|
|
|
pppcp_trace(timer_data->data);
|
2010-03-23 00:05:57 +00:00
|
|
|
|
2010-03-25 03:26:28 +00:00
|
|
|
timer_data->restart_timer = 0;
|
2010-03-23 00:05:57 +00:00
|
|
|
|
2010-03-25 03:26:28 +00:00
|
|
|
if (timer_data->restart_counter)
|
|
|
|
pppcp_generate_event(timer_data->data, TO_PLUS, NULL, 0);
|
2010-03-23 00:05:57 +00:00
|
|
|
else
|
2010-03-25 03:26:28 +00:00
|
|
|
pppcp_generate_event(timer_data->data, TO_MINUS, NULL, 0);
|
2010-03-23 03:01:28 +00:00
|
|
|
|
2010-03-23 00:05:57 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2010-03-25 03:26:28 +00:00
|
|
|
static void pppcp_start_timer(struct pppcp_timer_data *timer_data)
|
2010-03-23 00:05:57 +00:00
|
|
|
{
|
2010-03-25 03:26:28 +00:00
|
|
|
if (timer_data->restart_timer)
|
|
|
|
return;
|
|
|
|
|
|
|
|
timer_data->restart_timer =
|
|
|
|
g_timeout_add_seconds(timer_data->restart_interval,
|
|
|
|
pppcp_timeout, timer_data);
|
2010-03-23 00:05:57 +00:00
|
|
|
}
|
|
|
|
|
2010-03-25 03:26:28 +00:00
|
|
|
static void pppcp_stop_timer(struct pppcp_timer_data *timer_data)
|
2010-03-23 00:05:57 +00:00
|
|
|
{
|
2010-03-25 03:26:28 +00:00
|
|
|
if (timer_data->restart_timer) {
|
|
|
|
g_source_remove(timer_data->restart_timer);
|
|
|
|
timer_data->restart_timer = 0;
|
2010-03-23 00:05:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-03-25 03:26:28 +00:00
|
|
|
static gboolean is_first_request(struct pppcp_timer_data *timer_data)
|
2010-03-23 00:05:57 +00:00
|
|
|
{
|
2010-03-25 03:26:28 +00:00
|
|
|
return (timer_data->restart_counter == timer_data->max_counter);
|
2010-03-23 00:05:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct pppcp_event *pppcp_event_new(enum pppcp_event_type type,
|
|
|
|
gpointer event_data, guint len)
|
|
|
|
{
|
|
|
|
struct pppcp_event *event;
|
|
|
|
guint8 *data = event_data;
|
|
|
|
|
|
|
|
event = g_try_malloc0(sizeof(struct pppcp_event) + len);
|
|
|
|
if (!event)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
event->type = type;
|
|
|
|
memcpy(event->data, data, len);
|
|
|
|
event->len = len;
|
|
|
|
return event;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct pppcp_event *pppcp_get_event(struct pppcp_data *data)
|
|
|
|
{
|
|
|
|
return g_queue_pop_head(data->event_queue);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* actions */
|
|
|
|
/* log an illegal event, but otherwise do nothing */
|
|
|
|
static void pppcp_illegal_event(guint8 state, guint8 type)
|
|
|
|
{
|
|
|
|
g_printerr("Illegal event %d while in state %d\n", type, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pppcp_this_layer_up(struct pppcp_data *data)
|
|
|
|
{
|
|
|
|
struct pppcp_action *action = data->action;
|
|
|
|
|
|
|
|
if (action->this_layer_up)
|
|
|
|
action->this_layer_up(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pppcp_this_layer_down(struct pppcp_data *data)
|
|
|
|
{
|
|
|
|
struct pppcp_action *action = data->action;
|
|
|
|
|
|
|
|
if (action->this_layer_up)
|
|
|
|
action->this_layer_down(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pppcp_this_layer_started(struct pppcp_data *data)
|
|
|
|
{
|
|
|
|
struct pppcp_action *action = data->action;
|
|
|
|
|
|
|
|
if (action->this_layer_up)
|
|
|
|
action->this_layer_started(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pppcp_this_layer_finished(struct pppcp_data *data)
|
|
|
|
{
|
|
|
|
struct pppcp_action *action = data->action;
|
|
|
|
|
|
|
|
if (action->this_layer_up)
|
|
|
|
action->this_layer_finished(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pppcp_clear_options(struct pppcp_data *data)
|
|
|
|
{
|
2010-03-24 20:17:57 +00:00
|
|
|
g_list_foreach(data->acceptable_options, (GFunc) g_free, NULL);
|
|
|
|
g_list_foreach(data->unacceptable_options, (GFunc) g_free, NULL);
|
|
|
|
g_list_foreach(data->rejected_options, (GFunc) g_free, NULL);
|
2010-03-23 00:05:57 +00:00
|
|
|
g_list_free(data->acceptable_options);
|
|
|
|
g_list_free(data->unacceptable_options);
|
|
|
|
g_list_free(data->rejected_options);
|
|
|
|
data->acceptable_options = NULL;
|
|
|
|
data->unacceptable_options = NULL;
|
|
|
|
data->rejected_options = NULL;
|
|
|
|
}
|
|
|
|
|
2010-03-24 17:13:23 +00:00
|
|
|
static void pppcp_free_options(struct pppcp_data *data)
|
|
|
|
{
|
|
|
|
/* remove all config options */
|
|
|
|
pppcp_clear_options(data);
|
|
|
|
|
|
|
|
/* remove default option list */
|
2010-03-24 20:17:57 +00:00
|
|
|
g_list_foreach(data->config_options, (GFunc) g_free, NULL);
|
2010-03-24 17:13:23 +00:00
|
|
|
g_list_free(data->config_options);
|
|
|
|
}
|
|
|
|
|
2010-03-23 00:05:57 +00:00
|
|
|
/*
|
|
|
|
* set the restart counter to either max-terminate
|
|
|
|
* or max-configure. The counter is decremented for
|
|
|
|
* each transmission, including the first.
|
|
|
|
*/
|
2010-03-25 03:26:28 +00:00
|
|
|
static void pppcp_initialize_restart_count(struct pppcp_timer_data *timer_data)
|
2010-03-23 00:05:57 +00:00
|
|
|
{
|
2010-03-25 03:26:28 +00:00
|
|
|
struct pppcp_data *data = timer_data->data;
|
|
|
|
|
2010-03-23 00:05:57 +00:00
|
|
|
pppcp_trace(data);
|
|
|
|
pppcp_clear_options(data);
|
2010-03-25 03:26:28 +00:00
|
|
|
timer_data->restart_counter = timer_data->max_counter;
|
2010-03-23 00:05:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* set restart counter to zero
|
|
|
|
*/
|
2010-03-25 03:26:28 +00:00
|
|
|
static void pppcp_zero_restart_count(struct pppcp_timer_data *timer_data)
|
2010-03-23 00:05:57 +00:00
|
|
|
{
|
2010-03-25 03:26:28 +00:00
|
|
|
timer_data->restart_counter = 0;
|
2010-03-23 00:05:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TBD - generate new identifier for packet
|
|
|
|
*/
|
|
|
|
static guint8 new_identity(struct pppcp_data *data, guint prev_identifier)
|
|
|
|
{
|
2010-03-23 03:01:28 +00:00
|
|
|
return prev_identifier + 1;
|
2010-03-23 00:05:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void get_option_length(gpointer data, gpointer user_data)
|
|
|
|
{
|
|
|
|
struct ppp_option *option = data;
|
|
|
|
guint8 *length = user_data;
|
|
|
|
|
|
|
|
*length += option->length;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void copy_option(gpointer data, gpointer user_data)
|
|
|
|
{
|
|
|
|
struct ppp_option *option = data;
|
|
|
|
guint8 **location = user_data;
|
|
|
|
memcpy(*location, (guint8 *) option, option->length);
|
|
|
|
*location += option->length;
|
|
|
|
}
|
|
|
|
|
|
|
|
void pppcp_add_config_option(struct pppcp_data *data, struct ppp_option *option)
|
|
|
|
{
|
|
|
|
data->config_options = g_list_append(data->config_options, option);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* transmit a Configure-Request packet
|
|
|
|
* start the restart timer
|
|
|
|
* decrement the restart counter
|
|
|
|
*/
|
|
|
|
static void pppcp_send_configure_request(struct pppcp_data *data)
|
|
|
|
{
|
|
|
|
struct pppcp_packet *packet;
|
|
|
|
guint8 olength = 0;
|
|
|
|
guint8 *odata;
|
2010-03-25 03:26:28 +00:00
|
|
|
struct pppcp_timer_data *timer_data = &data->config_timer_data;
|
2010-03-23 00:05:57 +00:00
|
|
|
|
|
|
|
pppcp_trace(data);
|
|
|
|
|
|
|
|
/* figure out how much space to allocate for options */
|
|
|
|
g_list_foreach(data->config_options, get_option_length, &olength);
|
|
|
|
|
|
|
|
packet = pppcp_packet_new(data, CONFIGURE_REQUEST, olength);
|
|
|
|
|
|
|
|
/* copy config options into packet data */
|
|
|
|
odata = packet->data;
|
|
|
|
g_list_foreach(data->config_options, copy_option, &odata);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if this is the first request, we need a new identifier.
|
|
|
|
* if this is a retransmission, leave the identifier alone.
|
|
|
|
*/
|
2010-03-25 03:26:28 +00:00
|
|
|
if (is_first_request(timer_data))
|
2010-03-23 00:05:57 +00:00
|
|
|
data->config_identifier =
|
|
|
|
new_identity(data, data->config_identifier);
|
|
|
|
packet->identifier = data->config_identifier;
|
|
|
|
|
2010-03-24 17:13:24 +00:00
|
|
|
ppp_transmit(data->ppp, pppcp_to_ppp_packet(packet),
|
2010-03-23 00:05:57 +00:00
|
|
|
ntohs(packet->length));
|
|
|
|
|
2010-03-24 17:13:24 +00:00
|
|
|
pppcp_packet_free(packet);
|
|
|
|
|
2010-03-25 03:26:28 +00:00
|
|
|
/* start timer for retransmission */
|
|
|
|
timer_data->restart_counter--;
|
|
|
|
pppcp_start_timer(timer_data);
|
2010-03-23 00:05:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* transmit a Configure-Ack packet
|
|
|
|
*/
|
|
|
|
static void pppcp_send_configure_ack(struct pppcp_data *data,
|
|
|
|
guint8 *request)
|
|
|
|
{
|
|
|
|
struct pppcp_packet *packet;
|
|
|
|
struct pppcp_packet *pppcp_header = (struct pppcp_packet *) request;
|
|
|
|
guint len;
|
|
|
|
guint8 *odata;
|
|
|
|
|
|
|
|
pppcp_trace(data);
|
|
|
|
|
|
|
|
/* subtract for header. */
|
|
|
|
len = ntohs(pppcp_header->length) - sizeof(*packet);
|
|
|
|
|
|
|
|
packet = pppcp_packet_new(data, CONFIGURE_ACK, len);
|
|
|
|
|
|
|
|
/* copy the applied options in. */
|
|
|
|
odata = packet->data;
|
|
|
|
|
2010-03-23 04:43:46 +00:00
|
|
|
g_list_foreach(data->acceptable_options, copy_option, &odata);
|
2010-03-23 00:05:57 +00:00
|
|
|
|
|
|
|
/* match identifier of the request */
|
|
|
|
packet->identifier = pppcp_header->identifier;
|
|
|
|
|
2010-03-24 17:13:24 +00:00
|
|
|
ppp_transmit(data->ppp, pppcp_to_ppp_packet(packet),
|
2010-03-23 00:05:57 +00:00
|
|
|
ntohs(packet->length));
|
2010-03-24 17:13:24 +00:00
|
|
|
pppcp_packet_free(packet);
|
2010-03-23 00:05:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* transmit a Configure-Nak or Configure-Reject packet
|
|
|
|
*/
|
|
|
|
static void pppcp_send_configure_nak(struct pppcp_data *data,
|
|
|
|
guint8 *configure_packet)
|
|
|
|
{
|
|
|
|
struct pppcp_packet *packet;
|
|
|
|
struct pppcp_packet *pppcp_header =
|
|
|
|
(struct pppcp_packet *) configure_packet;
|
|
|
|
guint8 olength = 0;
|
|
|
|
guint8 *odata;
|
|
|
|
|
|
|
|
/* if we have any rejected options, send a config-reject */
|
|
|
|
if (g_list_length(data->rejected_options)) {
|
|
|
|
/* figure out how much space to allocate for options */
|
|
|
|
g_list_foreach(data->rejected_options, get_option_length,
|
|
|
|
&olength);
|
|
|
|
|
|
|
|
packet = pppcp_packet_new(data, CONFIGURE_REJECT, olength);
|
|
|
|
|
|
|
|
/* copy the rejected options in. */
|
|
|
|
odata = packet->data;
|
|
|
|
g_list_foreach(data->rejected_options, copy_option,
|
|
|
|
&odata);
|
|
|
|
|
|
|
|
packet->identifier = pppcp_header->identifier;
|
2010-03-24 17:13:24 +00:00
|
|
|
ppp_transmit(data->ppp, pppcp_to_ppp_packet(packet),
|
2010-03-23 00:05:57 +00:00
|
|
|
ntohs(packet->length));
|
|
|
|
|
2010-03-24 17:13:24 +00:00
|
|
|
pppcp_packet_free(packet);
|
2010-03-23 00:05:57 +00:00
|
|
|
}
|
|
|
|
/* if we have any unacceptable options, send a config-nak */
|
|
|
|
if (g_list_length(data->unacceptable_options)) {
|
|
|
|
olength = 0;
|
|
|
|
|
|
|
|
/* figure out how much space to allocate for options */
|
|
|
|
g_list_foreach(data->unacceptable_options, get_option_length,
|
|
|
|
&olength);
|
|
|
|
|
|
|
|
packet = pppcp_packet_new(data, CONFIGURE_NAK, olength);
|
|
|
|
|
|
|
|
/* copy the unacceptable options in. */
|
|
|
|
odata = packet->data;
|
|
|
|
g_list_foreach(data->unacceptable_options, copy_option,
|
|
|
|
&odata);
|
|
|
|
|
|
|
|
packet->identifier = pppcp_header->identifier;
|
2010-03-24 17:13:24 +00:00
|
|
|
ppp_transmit(data->ppp, pppcp_to_ppp_packet(packet),
|
2010-03-23 00:05:57 +00:00
|
|
|
ntohs(packet->length));
|
2010-03-24 17:13:24 +00:00
|
|
|
|
|
|
|
pppcp_packet_free(packet);
|
2010-03-23 00:05:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* transmit a Terminate-Request packet.
|
|
|
|
* start the restart timer.
|
|
|
|
* decrement the restart counter
|
|
|
|
*/
|
|
|
|
static void pppcp_send_terminate_request(struct pppcp_data *data)
|
|
|
|
{
|
|
|
|
struct pppcp_packet *packet;
|
2010-03-25 03:26:28 +00:00
|
|
|
struct pppcp_timer_data *timer_data = &data->terminate_timer_data;
|
2010-03-23 00:05:57 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* the data field can be used by the sender (us).
|
|
|
|
* leave this empty for now.
|
|
|
|
*/
|
|
|
|
packet = pppcp_packet_new(data, TERMINATE_REQUEST, 0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Is this a retransmission? If so, do not change
|
|
|
|
* the identifier. If not, we need a fresh identity.
|
|
|
|
*/
|
2010-03-25 03:26:28 +00:00
|
|
|
if (is_first_request(timer_data))
|
2010-03-23 00:05:57 +00:00
|
|
|
data->terminate_identifier =
|
|
|
|
new_identity(data, data->terminate_identifier);
|
|
|
|
packet->identifier = data->terminate_identifier;
|
2010-03-24 17:13:24 +00:00
|
|
|
ppp_transmit(data->ppp, pppcp_to_ppp_packet(packet),
|
2010-03-23 00:05:57 +00:00
|
|
|
ntohs(packet->length));
|
2010-03-24 17:13:24 +00:00
|
|
|
|
|
|
|
pppcp_packet_free(packet);
|
2010-03-25 03:26:28 +00:00
|
|
|
timer_data->restart_counter--;
|
|
|
|
pppcp_start_timer(timer_data);
|
2010-03-23 00:05:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* transmit a Terminate-Ack packet
|
|
|
|
*/
|
|
|
|
static void pppcp_send_terminate_ack(struct pppcp_data *data,
|
|
|
|
guint8 *request)
|
|
|
|
{
|
|
|
|
struct pppcp_packet *packet;
|
|
|
|
struct pppcp_packet *pppcp_header = (struct pppcp_packet *) request;
|
|
|
|
|
|
|
|
packet = pppcp_packet_new(data, TERMINATE_ACK, 0);
|
|
|
|
|
|
|
|
/* match identifier of the request */
|
|
|
|
packet->identifier = pppcp_header->identifier;
|
|
|
|
|
2010-03-24 17:13:24 +00:00
|
|
|
ppp_transmit(data->ppp, pppcp_to_ppp_packet(packet),
|
2010-03-23 00:05:57 +00:00
|
|
|
ntohs(pppcp_header->length));
|
2010-03-24 17:13:24 +00:00
|
|
|
|
|
|
|
pppcp_packet_free(packet);
|
2010-03-23 00:05:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* transmit a Code-Reject packet
|
|
|
|
*
|
|
|
|
* XXX this seg faults.
|
|
|
|
*/
|
|
|
|
static void pppcp_send_code_reject(struct pppcp_data *data,
|
|
|
|
guint8 *rejected_packet)
|
|
|
|
{
|
|
|
|
struct pppcp_packet *packet;
|
|
|
|
|
|
|
|
packet = pppcp_packet_new(data, CODE_REJECT,
|
|
|
|
ntohs(((struct pppcp_packet *) rejected_packet)->length));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Identifier must be changed for each Code-Reject sent
|
|
|
|
*/
|
|
|
|
packet->identifier = new_identity(data, data->reject_identifier);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* rejected packet should be copied in, but it should be
|
|
|
|
* truncated if it needs to be to comply with mtu requirement
|
|
|
|
*/
|
|
|
|
memcpy(packet->data, rejected_packet,
|
|
|
|
ntohs(packet->length - CP_HEADER_SZ));
|
|
|
|
|
2010-03-24 17:13:24 +00:00
|
|
|
ppp_transmit(data->ppp, pppcp_to_ppp_packet(packet),
|
2010-03-23 00:05:57 +00:00
|
|
|
ntohs(packet->length));
|
2010-03-24 17:13:24 +00:00
|
|
|
|
|
|
|
pppcp_packet_free(packet);
|
2010-03-23 00:05:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* transmit an Echo-Reply packet
|
|
|
|
*/
|
|
|
|
static void pppcp_send_echo_reply(struct pppcp_data *data,
|
|
|
|
guint8 *request)
|
|
|
|
{
|
|
|
|
struct pppcp_packet *packet;
|
|
|
|
struct pppcp_packet *header = (struct pppcp_packet *) request;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 0 bytes for data, 4 bytes for magic number
|
|
|
|
*/
|
|
|
|
packet = pppcp_packet_new(data, ECHO_REPLY, 4);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* match identifier of request
|
|
|
|
*/
|
|
|
|
packet->identifier = header->identifier;
|
|
|
|
|
|
|
|
/* magic number? */
|
2010-03-24 17:13:24 +00:00
|
|
|
ppp_transmit(data->ppp, pppcp_to_ppp_packet(packet),
|
2010-03-23 00:05:57 +00:00
|
|
|
ntohs(packet->length));
|
|
|
|
|
2010-03-24 17:13:24 +00:00
|
|
|
pppcp_packet_free(packet);
|
2010-03-23 00:05:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void pppcp_transition_state(enum pppcp_state new_state,
|
|
|
|
struct pppcp_data *data)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* if switching from a state where
|
|
|
|
* TO events occur, to one where they
|
|
|
|
* may not, shut off the timer
|
|
|
|
*/
|
|
|
|
switch (new_state) {
|
|
|
|
case INITIAL:
|
|
|
|
case STARTING:
|
|
|
|
case CLOSED:
|
|
|
|
case STOPPED:
|
|
|
|
case OPENED:
|
2010-03-25 03:26:28 +00:00
|
|
|
pppcp_stop_timer(&data->config_timer_data);
|
|
|
|
pppcp_stop_timer(&data->terminate_timer_data);
|
2010-03-23 00:05:57 +00:00
|
|
|
break;
|
|
|
|
case CLOSING:
|
|
|
|
case STOPPING:
|
|
|
|
case REQSENT:
|
|
|
|
case ACKRCVD:
|
|
|
|
case ACKSENT:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
data->state = new_state;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pppcp_up_event(struct pppcp_data *data, guint8 *packet, guint len)
|
|
|
|
{
|
|
|
|
pppcp_trace(data);
|
|
|
|
switch (data->state) {
|
|
|
|
case INITIAL:
|
|
|
|
/* switch state to CLOSED */
|
|
|
|
pppcp_transition_state(CLOSED, data);
|
|
|
|
break;
|
|
|
|
case STARTING:
|
|
|
|
/* irc, scr/6 */
|
2010-03-25 03:26:28 +00:00
|
|
|
pppcp_initialize_restart_count(&data->config_timer_data);
|
2010-03-23 00:05:57 +00:00
|
|
|
pppcp_send_configure_request(data);
|
|
|
|
pppcp_transition_state(REQSENT, data);
|
|
|
|
break;
|
|
|
|
case CLOSED:
|
|
|
|
case STOPPED:
|
|
|
|
case OPENED:
|
|
|
|
case CLOSING:
|
|
|
|
case STOPPING:
|
|
|
|
case REQSENT:
|
|
|
|
case ACKRCVD:
|
|
|
|
case ACKSENT:
|
|
|
|
pppcp_illegal_event(data->state, UP);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pppcp_down_event(struct pppcp_data *data, guint8 *packet, guint len)
|
|
|
|
{
|
|
|
|
switch (data->state) {
|
|
|
|
case CLOSED:
|
|
|
|
pppcp_transition_state(INITIAL, data);
|
|
|
|
break;
|
|
|
|
case STOPPED:
|
|
|
|
/* tls/1 */
|
|
|
|
pppcp_transition_state(STARTING, data);
|
|
|
|
pppcp_this_layer_started(data);
|
|
|
|
break;
|
|
|
|
case CLOSING:
|
|
|
|
pppcp_transition_state(INITIAL, data);
|
|
|
|
break;
|
|
|
|
case STOPPING:
|
|
|
|
case REQSENT:
|
|
|
|
case ACKRCVD:
|
|
|
|
case ACKSENT:
|
|
|
|
pppcp_transition_state(STARTING, data);
|
|
|
|
break;
|
|
|
|
case OPENED:
|
|
|
|
pppcp_transition_state(STARTING, data);
|
|
|
|
pppcp_this_layer_down(data);
|
|
|
|
case INITIAL:
|
|
|
|
case STARTING:
|
|
|
|
pppcp_illegal_event(data->state, DOWN);
|
|
|
|
/* illegal */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pppcp_open_event(struct pppcp_data *data, guint8 *packet, guint len)
|
|
|
|
{
|
|
|
|
pppcp_trace(data);
|
|
|
|
switch (data->state) {
|
|
|
|
case INITIAL:
|
|
|
|
/* tls/1 */
|
|
|
|
pppcp_transition_state(STARTING, data);
|
|
|
|
pppcp_this_layer_started(data);
|
|
|
|
break;
|
|
|
|
case STARTING:
|
|
|
|
pppcp_transition_state(STARTING, data);
|
|
|
|
break;
|
|
|
|
case CLOSED:
|
2010-03-25 03:26:28 +00:00
|
|
|
pppcp_initialize_restart_count(&data->config_timer_data);
|
2010-03-23 00:05:57 +00:00
|
|
|
pppcp_send_configure_request(data);
|
|
|
|
pppcp_transition_state(REQSENT, data);
|
|
|
|
break;
|
|
|
|
case STOPPED:
|
|
|
|
/* 3r */
|
|
|
|
pppcp_transition_state(STOPPED, data);
|
|
|
|
break;
|
|
|
|
case CLOSING:
|
|
|
|
case STOPPING:
|
|
|
|
/* 5r */
|
|
|
|
pppcp_transition_state(STOPPING, data);
|
|
|
|
break;
|
|
|
|
case REQSENT:
|
|
|
|
case ACKRCVD:
|
|
|
|
case ACKSENT:
|
|
|
|
pppcp_transition_state(data->state, data);
|
|
|
|
break;
|
|
|
|
case OPENED:
|
|
|
|
/* 9r */
|
|
|
|
pppcp_transition_state(data->state, data);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pppcp_close_event(struct pppcp_data *data, guint8* packet, guint len)
|
|
|
|
{
|
|
|
|
switch (data->state) {
|
|
|
|
case INITIAL:
|
|
|
|
pppcp_transition_state(INITIAL, data);
|
|
|
|
break;
|
|
|
|
case STARTING:
|
|
|
|
pppcp_this_layer_finished(data);
|
|
|
|
pppcp_transition_state(INITIAL, data);
|
|
|
|
break;
|
|
|
|
case CLOSED:
|
|
|
|
case STOPPED:
|
|
|
|
pppcp_transition_state(CLOSED, data);
|
|
|
|
break;
|
|
|
|
case CLOSING:
|
|
|
|
case STOPPING:
|
|
|
|
pppcp_transition_state(CLOSING, data);
|
|
|
|
break;
|
|
|
|
case OPENED:
|
|
|
|
pppcp_this_layer_down(data);
|
|
|
|
case REQSENT:
|
|
|
|
case ACKRCVD:
|
|
|
|
case ACKSENT:
|
2010-03-25 03:26:28 +00:00
|
|
|
pppcp_initialize_restart_count(&data->terminate_timer_data);
|
2010-03-23 00:05:57 +00:00
|
|
|
pppcp_send_terminate_request(data);
|
|
|
|
pppcp_transition_state(CLOSING, data);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pppcp_to_plus_event(struct pppcp_data *data, guint8 *packet, guint len)
|
|
|
|
{
|
|
|
|
pppcp_trace(data);
|
|
|
|
|
|
|
|
switch (data->state) {
|
|
|
|
case CLOSING:
|
|
|
|
pppcp_send_terminate_request(data);
|
|
|
|
pppcp_transition_state(CLOSING, data);
|
|
|
|
break;
|
|
|
|
case STOPPING:
|
|
|
|
pppcp_send_terminate_request(data);
|
|
|
|
pppcp_transition_state(STOPPING, data);
|
|
|
|
break;
|
|
|
|
case REQSENT:
|
|
|
|
case ACKRCVD:
|
|
|
|
pppcp_send_configure_request(data);
|
|
|
|
pppcp_transition_state(REQSENT, data);
|
|
|
|
break;
|
|
|
|
case ACKSENT:
|
|
|
|
pppcp_send_configure_request(data);
|
|
|
|
pppcp_transition_state(ACKSENT, data);
|
|
|
|
break;
|
|
|
|
case INITIAL:
|
|
|
|
case STARTING:
|
|
|
|
case CLOSED:
|
|
|
|
case STOPPED:
|
|
|
|
case OPENED:
|
|
|
|
pppcp_illegal_event(data->state, TO_PLUS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pppcp_to_minus_event(struct pppcp_data *data, guint8 *packet, guint len)
|
|
|
|
{
|
|
|
|
pppcp_trace(data);
|
|
|
|
switch (data->state) {
|
|
|
|
case CLOSING:
|
|
|
|
pppcp_transition_state(CLOSED, data);
|
|
|
|
pppcp_this_layer_finished(data);
|
|
|
|
break;
|
|
|
|
case STOPPING:
|
|
|
|
pppcp_transition_state(STOPPED, data);
|
|
|
|
pppcp_this_layer_finished(data);
|
|
|
|
break;
|
|
|
|
case REQSENT:
|
|
|
|
case ACKRCVD:
|
|
|
|
case ACKSENT:
|
|
|
|
/* tlf/3p */
|
|
|
|
pppcp_transition_state(STOPPED, data);
|
|
|
|
pppcp_this_layer_finished(data);
|
|
|
|
break;
|
|
|
|
case INITIAL:
|
|
|
|
case STARTING:
|
|
|
|
case CLOSED:
|
|
|
|
case STOPPED:
|
|
|
|
case OPENED:
|
|
|
|
pppcp_illegal_event(data->state, TO_MINUS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pppcp_rcr_plus_event(struct pppcp_data *data,
|
|
|
|
guint8 *packet, guint len)
|
|
|
|
{
|
|
|
|
pppcp_trace(data);
|
|
|
|
switch (data->state) {
|
|
|
|
case CLOSED:
|
|
|
|
pppcp_send_terminate_ack(data, packet);
|
|
|
|
pppcp_transition_state(CLOSED, data);
|
|
|
|
break;
|
|
|
|
case STOPPED:
|
2010-03-25 03:26:28 +00:00
|
|
|
pppcp_initialize_restart_count(&data->config_timer_data);
|
2010-03-23 00:05:57 +00:00
|
|
|
pppcp_send_configure_request(data);
|
|
|
|
pppcp_send_configure_ack(data, packet);
|
|
|
|
pppcp_transition_state(ACKSENT, data);
|
|
|
|
break;
|
|
|
|
case CLOSING:
|
|
|
|
case STOPPING:
|
|
|
|
pppcp_transition_state(data->state, data);
|
|
|
|
break;
|
|
|
|
case REQSENT:
|
|
|
|
pppcp_send_configure_ack(data, packet);
|
|
|
|
pppcp_transition_state(ACKSENT, data);
|
|
|
|
break;
|
|
|
|
case ACKRCVD:
|
|
|
|
pppcp_send_configure_ack(data, packet);
|
|
|
|
pppcp_this_layer_up(data);
|
|
|
|
pppcp_transition_state(OPENED, data);
|
|
|
|
break;
|
|
|
|
case ACKSENT:
|
|
|
|
pppcp_send_configure_ack(data, packet);
|
|
|
|
pppcp_transition_state(ACKSENT, data);
|
|
|
|
break;
|
|
|
|
case OPENED:
|
|
|
|
pppcp_this_layer_down(data);
|
|
|
|
pppcp_send_configure_request(data);
|
|
|
|
pppcp_send_configure_ack(data, packet);
|
|
|
|
pppcp_transition_state(ACKSENT, data);
|
|
|
|
break;
|
|
|
|
case INITIAL:
|
|
|
|
case STARTING:
|
|
|
|
pppcp_illegal_event(data->state, RCR_PLUS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pppcp_rcr_minus_event(struct pppcp_data *data,
|
|
|
|
guint8 *packet, guint len)
|
|
|
|
{
|
|
|
|
pppcp_trace(data);
|
|
|
|
|
|
|
|
switch (data->state) {
|
|
|
|
case CLOSED:
|
|
|
|
pppcp_send_terminate_ack(data, packet);
|
|
|
|
pppcp_transition_state(CLOSED, data);
|
|
|
|
break;
|
|
|
|
case STOPPED:
|
2010-03-25 03:26:28 +00:00
|
|
|
pppcp_initialize_restart_count(&data->config_timer_data);
|
2010-03-23 00:05:57 +00:00
|
|
|
pppcp_send_configure_request(data);
|
|
|
|
pppcp_send_configure_nak(data, packet);
|
|
|
|
pppcp_transition_state(REQSENT, data);
|
|
|
|
break;
|
|
|
|
case CLOSING:
|
|
|
|
case STOPPING:
|
|
|
|
pppcp_transition_state(data->state, data);
|
|
|
|
break;
|
|
|
|
case REQSENT:
|
|
|
|
case ACKRCVD:
|
|
|
|
pppcp_send_configure_nak(data, packet);
|
|
|
|
pppcp_transition_state(data->state, data);
|
|
|
|
break;
|
|
|
|
case ACKSENT:
|
|
|
|
pppcp_send_configure_nak(data, packet);
|
|
|
|
pppcp_transition_state(REQSENT, data);
|
|
|
|
break;
|
|
|
|
case OPENED:
|
|
|
|
pppcp_this_layer_down(data);
|
|
|
|
pppcp_send_configure_request(data);
|
|
|
|
pppcp_send_configure_nak(data, packet);
|
|
|
|
pppcp_transition_state(REQSENT, data);
|
|
|
|
break;
|
|
|
|
case INITIAL:
|
|
|
|
case STARTING:
|
|
|
|
pppcp_illegal_event(data->state, RCR_MINUS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pppcp_rca_event(struct pppcp_data *data, guint8 *packet, guint len)
|
|
|
|
{
|
|
|
|
pppcp_trace(data);
|
|
|
|
|
|
|
|
switch (data->state) {
|
|
|
|
case CLOSED:
|
|
|
|
case STOPPED:
|
|
|
|
pppcp_send_terminate_ack(data, packet);
|
|
|
|
case CLOSING:
|
|
|
|
case STOPPING:
|
|
|
|
pppcp_transition_state(data->state, data);
|
|
|
|
break;
|
|
|
|
case REQSENT:
|
2010-03-25 03:26:28 +00:00
|
|
|
pppcp_initialize_restart_count(&data->config_timer_data);
|
2010-03-23 00:05:57 +00:00
|
|
|
pppcp_transition_state(ACKRCVD, data);
|
|
|
|
break;
|
|
|
|
case ACKRCVD:
|
|
|
|
/* scr/6x */
|
|
|
|
pppcp_send_configure_request(data);
|
|
|
|
pppcp_transition_state(REQSENT, data);
|
|
|
|
case ACKSENT:
|
2010-03-25 03:26:28 +00:00
|
|
|
pppcp_initialize_restart_count(&data->config_timer_data);
|
2010-03-23 00:05:57 +00:00
|
|
|
pppcp_this_layer_up(data);
|
|
|
|
pppcp_transition_state(OPENED, data);
|
|
|
|
break;
|
|
|
|
case OPENED:
|
|
|
|
pppcp_this_layer_down(data);
|
|
|
|
pppcp_send_configure_request(data);
|
|
|
|
pppcp_transition_state(REQSENT, data);
|
|
|
|
break;
|
|
|
|
case INITIAL:
|
|
|
|
case STARTING:
|
|
|
|
pppcp_illegal_event(data->state, RCA);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pppcp_rcn_event(struct pppcp_data *data, guint8 *packet, guint len)
|
|
|
|
{
|
|
|
|
pppcp_trace(data);
|
|
|
|
|
|
|
|
switch (data->state) {
|
|
|
|
case CLOSED:
|
|
|
|
case STOPPED:
|
|
|
|
pppcp_send_terminate_ack(data, packet);
|
|
|
|
case CLOSING:
|
|
|
|
case STOPPING:
|
|
|
|
pppcp_transition_state(data->state, data);
|
|
|
|
case REQSENT:
|
2010-03-25 03:26:28 +00:00
|
|
|
pppcp_initialize_restart_count(&data->config_timer_data);
|
2010-03-23 00:05:57 +00:00
|
|
|
pppcp_send_configure_request(data);
|
|
|
|
pppcp_transition_state(REQSENT, data);
|
|
|
|
break;
|
|
|
|
case ACKRCVD:
|
|
|
|
/* scr/6x */
|
|
|
|
pppcp_send_configure_request(data);
|
|
|
|
pppcp_transition_state(REQSENT, data);
|
|
|
|
break;
|
|
|
|
case ACKSENT:
|
2010-03-25 03:26:28 +00:00
|
|
|
pppcp_initialize_restart_count(&data->config_timer_data);
|
2010-03-23 00:05:57 +00:00
|
|
|
pppcp_send_configure_request(data);
|
|
|
|
pppcp_transition_state(ACKSENT, data);
|
|
|
|
break;
|
|
|
|
case OPENED:
|
|
|
|
pppcp_this_layer_down(data);
|
|
|
|
pppcp_send_configure_request(data);
|
|
|
|
pppcp_transition_state(REQSENT, data);
|
|
|
|
break;
|
|
|
|
case INITIAL:
|
|
|
|
case STARTING:
|
|
|
|
pppcp_illegal_event(data->state, RCN);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pppcp_rtr_event(struct pppcp_data *data, guint8 *packet, guint len)
|
|
|
|
{
|
|
|
|
pppcp_trace(data);
|
|
|
|
|
|
|
|
switch (data->state) {
|
|
|
|
case CLOSED:
|
|
|
|
case STOPPED:
|
|
|
|
pppcp_send_terminate_ack(data, packet);
|
|
|
|
case CLOSING:
|
|
|
|
case STOPPING:
|
|
|
|
break;
|
|
|
|
case REQSENT:
|
|
|
|
case ACKRCVD:
|
|
|
|
case ACKSENT:
|
|
|
|
pppcp_send_terminate_ack(data, packet);
|
|
|
|
pppcp_transition_state(REQSENT, data);
|
|
|
|
break;
|
|
|
|
case OPENED:
|
|
|
|
pppcp_this_layer_down(data);
|
2010-03-25 03:26:28 +00:00
|
|
|
pppcp_zero_restart_count(&data->terminate_timer_data);
|
2010-03-23 00:05:57 +00:00
|
|
|
pppcp_send_terminate_ack(data, packet);
|
|
|
|
pppcp_transition_state(STOPPING, data);
|
|
|
|
break;
|
|
|
|
case INITIAL:
|
|
|
|
case STARTING:
|
|
|
|
pppcp_illegal_event(data->state, RTR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pppcp_rta_event(struct pppcp_data *data, guint8 *packet, guint len)
|
|
|
|
{
|
|
|
|
pppcp_trace(data);
|
|
|
|
|
|
|
|
switch (data->state) {
|
|
|
|
case CLOSED:
|
|
|
|
case STOPPED:
|
|
|
|
pppcp_transition_state(data->state, data);
|
|
|
|
break;
|
|
|
|
case CLOSING:
|
|
|
|
pppcp_this_layer_finished(data);
|
|
|
|
pppcp_transition_state(CLOSED, data);
|
|
|
|
break;
|
|
|
|
case STOPPING:
|
|
|
|
pppcp_this_layer_finished(data);
|
|
|
|
pppcp_transition_state(STOPPED, data);
|
|
|
|
break;
|
|
|
|
case REQSENT:
|
|
|
|
case ACKRCVD:
|
|
|
|
pppcp_transition_state(REQSENT, data);
|
|
|
|
break;
|
|
|
|
case ACKSENT:
|
|
|
|
pppcp_transition_state(ACKSENT, data);
|
|
|
|
break;
|
|
|
|
case OPENED:
|
|
|
|
pppcp_this_layer_down(data);
|
|
|
|
pppcp_send_configure_request(data);
|
|
|
|
pppcp_transition_state(REQSENT, data);
|
|
|
|
break;
|
|
|
|
case INITIAL:
|
|
|
|
case STARTING:
|
|
|
|
pppcp_illegal_event(data->state, RTA);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pppcp_ruc_event(struct pppcp_data *data, guint8 *packet, guint len)
|
|
|
|
{
|
|
|
|
pppcp_trace(data);
|
|
|
|
|
|
|
|
switch (data->state) {
|
|
|
|
case CLOSED:
|
|
|
|
case STOPPED:
|
|
|
|
case CLOSING:
|
|
|
|
case STOPPING:
|
|
|
|
case REQSENT:
|
|
|
|
case ACKRCVD:
|
|
|
|
case ACKSENT:
|
|
|
|
case OPENED:
|
|
|
|
pppcp_send_code_reject(data, packet);
|
|
|
|
pppcp_transition_state(data->state, data);
|
|
|
|
break;
|
|
|
|
case INITIAL:
|
|
|
|
case STARTING:
|
|
|
|
pppcp_illegal_event(data->state, RUC);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pppcp_rxj_plus_event(struct pppcp_data *data, guint8 *packet, guint len)
|
|
|
|
{
|
|
|
|
pppcp_trace(data);
|
|
|
|
|
|
|
|
switch (data->state) {
|
|
|
|
case CLOSED:
|
|
|
|
case STOPPED:
|
|
|
|
case CLOSING:
|
|
|
|
case STOPPING:
|
|
|
|
pppcp_transition_state(data->state, data);
|
|
|
|
break;
|
|
|
|
case REQSENT:
|
|
|
|
case ACKRCVD:
|
|
|
|
pppcp_transition_state(REQSENT, data);
|
|
|
|
break;
|
|
|
|
case ACKSENT:
|
|
|
|
case OPENED:
|
|
|
|
pppcp_transition_state(data->state, data);
|
|
|
|
break;
|
|
|
|
case INITIAL:
|
|
|
|
case STARTING:
|
|
|
|
pppcp_illegal_event(data->state, RXJ_PLUS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pppcp_rxj_minus_event(struct pppcp_data *data,
|
|
|
|
guint8 *packet, guint len)
|
|
|
|
{
|
|
|
|
pppcp_trace(data);
|
|
|
|
|
|
|
|
switch (data->state) {
|
|
|
|
case CLOSED:
|
|
|
|
case STOPPED:
|
|
|
|
pppcp_this_layer_finished(data);
|
|
|
|
pppcp_transition_state(data->state, data);
|
|
|
|
break;
|
|
|
|
case CLOSING:
|
|
|
|
pppcp_this_layer_finished(data);
|
|
|
|
pppcp_transition_state(CLOSED, data);
|
|
|
|
break;
|
|
|
|
case STOPPING:
|
|
|
|
pppcp_this_layer_finished(data);
|
|
|
|
pppcp_transition_state(STOPPED, data);
|
|
|
|
break;
|
|
|
|
case REQSENT:
|
|
|
|
case ACKRCVD:
|
|
|
|
case ACKSENT:
|
|
|
|
pppcp_this_layer_finished(data);
|
|
|
|
pppcp_transition_state(STOPPED, data);
|
|
|
|
break;
|
|
|
|
case OPENED:
|
|
|
|
pppcp_this_layer_down(data);
|
2010-03-25 03:26:28 +00:00
|
|
|
pppcp_initialize_restart_count(&data->terminate_timer_data);
|
2010-03-23 00:05:57 +00:00
|
|
|
pppcp_send_terminate_request(data);
|
|
|
|
pppcp_transition_state(STOPPING, data);
|
|
|
|
break;
|
|
|
|
case INITIAL:
|
|
|
|
case STARTING:
|
|
|
|
pppcp_illegal_event(data->state, RXJ_MINUS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pppcp_rxr_event(struct pppcp_data *data, guint8 *packet, guint len)
|
|
|
|
{
|
|
|
|
pppcp_trace(data);
|
|
|
|
|
|
|
|
switch (data->state) {
|
|
|
|
case CLOSED:
|
|
|
|
case STOPPED:
|
|
|
|
case CLOSING:
|
|
|
|
case STOPPING:
|
|
|
|
case REQSENT:
|
|
|
|
case ACKRCVD:
|
|
|
|
case ACKSENT:
|
|
|
|
pppcp_transition_state(data->state, data);
|
|
|
|
break;
|
|
|
|
case OPENED:
|
|
|
|
pppcp_send_echo_reply(data, packet);
|
|
|
|
pppcp_transition_state(OPENED, data);
|
|
|
|
break;
|
|
|
|
case INITIAL:
|
|
|
|
case STARTING:
|
|
|
|
pppcp_illegal_event(data->state, RXR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pppcp_handle_event(gpointer user_data)
|
|
|
|
{
|
|
|
|
struct pppcp_event *event;
|
|
|
|
struct pppcp_data *data = user_data;
|
|
|
|
|
|
|
|
while ((event = pppcp_get_event(data))) {
|
|
|
|
if (event->type > RXR)
|
|
|
|
pppcp_illegal_event(data->state, event->type);
|
|
|
|
else
|
|
|
|
data->event_ops[event->type](data, event->data,
|
|
|
|
event->len);
|
|
|
|
g_free(event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* send the event handler a new event to process
|
|
|
|
*/
|
|
|
|
void pppcp_generate_event(struct pppcp_data *data,
|
|
|
|
enum pppcp_event_type event_type,
|
|
|
|
gpointer event_data, guint data_len)
|
|
|
|
{
|
|
|
|
struct pppcp_event *event;
|
|
|
|
|
|
|
|
event = pppcp_event_new(event_type, event_data, data_len);
|
|
|
|
if (event)
|
|
|
|
g_queue_push_tail(data->event_queue, event);
|
|
|
|
pppcp_handle_event(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static gint is_option(gconstpointer a, gconstpointer b)
|
|
|
|
{
|
|
|
|
const struct ppp_option *o = a;
|
|
|
|
guint8 otype = (guint8) GPOINTER_TO_UINT(b);
|
|
|
|
|
|
|
|
if (o->type == otype)
|
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void verify_config_option(gpointer elem, gpointer user_data)
|
|
|
|
{
|
|
|
|
struct ppp_option *config = elem;
|
|
|
|
struct pppcp_data *data = user_data;
|
2010-03-23 02:32:47 +00:00
|
|
|
guint type = config->type;
|
2010-03-23 03:07:13 +00:00
|
|
|
struct ppp_option *option;
|
2010-03-23 02:32:47 +00:00
|
|
|
GList *list;
|
2010-03-23 00:05:57 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* determine whether this config option is in the
|
|
|
|
* acceptable options list
|
|
|
|
*/
|
2010-03-23 02:32:47 +00:00
|
|
|
list = g_list_find_custom(data->acceptable_options,
|
2010-03-23 03:07:13 +00:00
|
|
|
GUINT_TO_POINTER(type), is_option);
|
|
|
|
if (list)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if the option did not exist, we need to store a copy
|
|
|
|
* of the option in the unacceptable_options list so it
|
|
|
|
* can be nak'ed.
|
|
|
|
*/
|
|
|
|
option = g_try_malloc0(config->length);
|
|
|
|
if (option == NULL)
|
|
|
|
return;
|
2010-03-23 02:32:47 +00:00
|
|
|
|
2010-03-23 03:07:13 +00:00
|
|
|
option->type = config->type;
|
|
|
|
option->length = config->length;
|
|
|
|
data->unacceptable_options =
|
2010-03-23 00:05:57 +00:00
|
|
|
g_list_append(data->unacceptable_options, option);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void remove_config_option(gpointer elem, gpointer user_data)
|
|
|
|
{
|
|
|
|
struct ppp_option *config = elem;
|
|
|
|
struct pppcp_data *data = user_data;
|
2010-03-23 02:32:47 +00:00
|
|
|
guint type = config->type;
|
2010-03-23 00:05:57 +00:00
|
|
|
GList *list;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* determine whether this config option is in the
|
|
|
|
* applied options list
|
|
|
|
*/
|
2010-03-23 02:32:47 +00:00
|
|
|
list = g_list_find_custom(data->config_options,
|
2010-03-23 03:07:13 +00:00
|
|
|
GUINT_TO_POINTER(type), is_option);
|
|
|
|
if (!list)
|
|
|
|
return;
|
|
|
|
|
2010-03-24 17:13:23 +00:00
|
|
|
g_free(list->data);
|
2010-03-23 03:07:13 +00:00
|
|
|
data->config_options = g_list_delete_link(data->config_options, list);
|
2010-03-23 00:05:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static guint8 pppcp_process_configure_request(struct pppcp_data *data,
|
|
|
|
struct pppcp_packet *packet)
|
|
|
|
{
|
|
|
|
gint len;
|
|
|
|
int i = 0;
|
|
|
|
struct ppp_option *option;
|
|
|
|
enum option_rval rval = OPTION_ERR;
|
|
|
|
struct pppcp_action *action = data->action;
|
|
|
|
|
|
|
|
pppcp_trace(data);
|
|
|
|
|
|
|
|
len = ntohs(packet->length) - CP_HEADER_SZ;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* check the options.
|
|
|
|
*/
|
|
|
|
while (i < len) {
|
|
|
|
guint8 otype = packet->data[i];
|
|
|
|
guint8 olen = packet->data[i+1];
|
|
|
|
option = g_try_malloc0(olen);
|
|
|
|
if (option == NULL)
|
|
|
|
break;
|
|
|
|
option->type = otype;
|
|
|
|
option->length = olen;
|
|
|
|
memcpy(option->data, &packet->data[i+2], olen-2);
|
|
|
|
if (action->option_scan)
|
|
|
|
rval = action->option_scan(option, data);
|
|
|
|
switch (rval) {
|
|
|
|
case OPTION_ACCEPT:
|
|
|
|
data->acceptable_options =
|
|
|
|
g_list_append(data->acceptable_options, option);
|
|
|
|
break;
|
|
|
|
case OPTION_REJECT:
|
|
|
|
data->rejected_options =
|
|
|
|
g_list_append(data->rejected_options, option);
|
|
|
|
break;
|
|
|
|
case OPTION_NAK:
|
|
|
|
data->unacceptable_options =
|
|
|
|
g_list_append(data->unacceptable_options,
|
|
|
|
option);
|
|
|
|
break;
|
|
|
|
case OPTION_ERR:
|
|
|
|
g_printerr("unhandled option type %d\n", otype);
|
|
|
|
}
|
|
|
|
/* skip ahead to the next option */
|
|
|
|
i += olen;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* make sure all required config options were included */
|
|
|
|
g_list_foreach(data->config_options, verify_config_option, data);
|
|
|
|
|
|
|
|
if (g_list_length(data->unacceptable_options) ||
|
|
|
|
g_list_length(data->rejected_options))
|
|
|
|
return RCR_MINUS;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* all options were acceptable, so we should apply them before
|
|
|
|
* sending a configure-ack
|
|
|
|
*
|
|
|
|
* Remove all applied options from the config_option list. The
|
|
|
|
* protocol will have to re-add them if they want them renegotiated
|
|
|
|
* when the ppp goes down.
|
|
|
|
*/
|
|
|
|
if (action->option_process) {
|
|
|
|
g_list_foreach(data->acceptable_options,
|
|
|
|
action->option_process, data->priv);
|
|
|
|
g_list_foreach(data->acceptable_options, remove_config_option,
|
|
|
|
data);
|
|
|
|
}
|
|
|
|
|
|
|
|
return RCR_PLUS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static guint8 pppcp_process_configure_ack(struct pppcp_data *data,
|
|
|
|
struct pppcp_packet *packet)
|
|
|
|
{
|
|
|
|
guint len;
|
|
|
|
GList *list;
|
|
|
|
struct ppp_option *acked_option;
|
|
|
|
guint i = 0;
|
|
|
|
struct pppcp_action *action = data->action;
|
|
|
|
|
|
|
|
pppcp_trace(data);
|
|
|
|
|
|
|
|
len = ntohs(packet->length) - CP_HEADER_SZ;
|
|
|
|
|
|
|
|
/* if identifiers don't match, we should silently discard */
|
|
|
|
if (packet->identifier != data->config_identifier) {
|
|
|
|
g_printerr("received an ack id %d, but config id is %d\n",
|
|
|
|
packet->identifier, data->config_identifier);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* check each acked option. If it is what we requested,
|
|
|
|
* then we can apply these option values.
|
|
|
|
*
|
|
|
|
* XXX what if it isn't? Do this correctly -- for now
|
|
|
|
* we are just going to assume that all options matched
|
|
|
|
* and apply them.
|
|
|
|
*/
|
|
|
|
while (i < len) {
|
|
|
|
guint8 otype = packet->data[i];
|
|
|
|
guint8 olen = packet->data[i + 1];
|
|
|
|
acked_option = g_try_malloc0(olen);
|
|
|
|
if (acked_option == NULL)
|
|
|
|
break;
|
|
|
|
acked_option->type = otype;
|
|
|
|
acked_option->length = olen;
|
|
|
|
memcpy(acked_option->data, &packet->data[i + 2], olen - 2);
|
|
|
|
list = g_list_find_custom(data->config_options,
|
2010-03-23 02:29:34 +00:00
|
|
|
GUINT_TO_POINTER((guint) otype), is_option);
|
2010-03-23 00:05:57 +00:00
|
|
|
if (list) {
|
|
|
|
/*
|
|
|
|
* once we've applied the option, delete it from
|
|
|
|
* the config_options list.
|
|
|
|
*/
|
|
|
|
if (action->option_process)
|
|
|
|
action->option_process(acked_option,
|
|
|
|
data->priv);
|
2010-03-24 17:13:23 +00:00
|
|
|
|
|
|
|
g_free(list->data);
|
2010-03-23 00:05:57 +00:00
|
|
|
data->config_options =
|
|
|
|
g_list_delete_link(data->config_options, list);
|
|
|
|
} else
|
|
|
|
g_printerr("oops -- found acked option %d we didn't request\n", acked_option->type);
|
|
|
|
g_free(acked_option);
|
|
|
|
|
|
|
|
/* skip ahead to the next option */
|
|
|
|
i += olen;
|
|
|
|
}
|
|
|
|
return RCA;
|
|
|
|
}
|
|
|
|
|
|
|
|
static guint8 pppcp_process_configure_nak(struct pppcp_data *data,
|
|
|
|
struct pppcp_packet *packet)
|
|
|
|
{
|
|
|
|
guint len;
|
|
|
|
GList *list;
|
|
|
|
struct ppp_option *naked_option;
|
|
|
|
struct ppp_option *config_option;
|
|
|
|
guint i = 0;
|
|
|
|
enum option_rval rval = OPTION_ERR;
|
|
|
|
struct pppcp_action *action = data->action;
|
|
|
|
|
|
|
|
pppcp_trace(data);
|
|
|
|
|
|
|
|
len = ntohs(packet->length) - CP_HEADER_SZ;
|
|
|
|
|
|
|
|
/* if identifiers don't match, we should silently discard */
|
|
|
|
if (packet->identifier != data->config_identifier)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* check each unacceptable option. If it is acceptable, then
|
|
|
|
* we can resend the configure request with this value. we need
|
|
|
|
* to check the current config options to see if we need to
|
|
|
|
* modify a value there, or add a new option.
|
|
|
|
*/
|
|
|
|
while (i < len) {
|
|
|
|
guint8 otype = packet->data[i];
|
|
|
|
guint8 olen = packet->data[i+1];
|
|
|
|
naked_option = g_try_malloc0(olen);
|
|
|
|
if (naked_option == NULL)
|
|
|
|
break;
|
|
|
|
naked_option->type = otype;
|
|
|
|
naked_option->length = olen;
|
|
|
|
memcpy(naked_option->data, &packet->data[i + 2], olen - 2);
|
|
|
|
if (action->option_scan)
|
|
|
|
rval = action->option_scan(naked_option, data);
|
|
|
|
if (rval == OPTION_ACCEPT) {
|
|
|
|
/*
|
|
|
|
* check the current config options to see if they
|
|
|
|
* match.
|
|
|
|
*/
|
|
|
|
list = g_list_find_custom(data->config_options,
|
2010-03-23 02:29:34 +00:00
|
|
|
GUINT_TO_POINTER((guint) otype), is_option);
|
2010-03-23 00:05:57 +00:00
|
|
|
if (list) {
|
|
|
|
/* modify current option value to match */
|
|
|
|
config_option = list->data;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* option values should match, otherwise
|
|
|
|
* we need to reallocate
|
|
|
|
*/
|
|
|
|
if ((config_option->length ==
|
|
|
|
naked_option->length) && (olen - 2)) {
|
|
|
|
memcpy(config_option->data,
|
|
|
|
naked_option->data,
|
|
|
|
olen - 2);
|
|
|
|
} else {
|
|
|
|
/* XXX implement this */
|
|
|
|
g_printerr("uh oh, option value doesn't match\n");
|
|
|
|
}
|
|
|
|
g_free(naked_option);
|
|
|
|
} else {
|
|
|
|
/* add to list of config options */
|
|
|
|
pppcp_add_config_option(data, naked_option);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* XXX handle this correctly */
|
|
|
|
g_printerr("oops, option wasn't acceptable\n");
|
|
|
|
g_free(naked_option);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* skip ahead to the next option */
|
|
|
|
i += olen;
|
|
|
|
}
|
|
|
|
return RCN;
|
|
|
|
}
|
|
|
|
|
|
|
|
static guint8 pppcp_process_configure_reject(struct pppcp_data *data,
|
|
|
|
struct pppcp_packet *packet)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* make sure identifier matches that of last sent configure
|
|
|
|
* request
|
|
|
|
*/
|
|
|
|
if (packet->identifier == data->config_identifier) {
|
|
|
|
/*
|
|
|
|
* check to see which options were rejected
|
|
|
|
* Rejected options must be a subset of requested
|
|
|
|
* options.
|
|
|
|
*
|
|
|
|
* when a new configure-request is sent, we may
|
|
|
|
* not request any of these options be negotiated
|
|
|
|
*/
|
|
|
|
return RCN;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static guint8 pppcp_process_terminate_request(struct pppcp_data *data,
|
|
|
|
struct pppcp_packet *packet)
|
|
|
|
{
|
|
|
|
return RTR;
|
|
|
|
}
|
|
|
|
|
|
|
|
static guint8 pppcp_process_terminate_ack(struct pppcp_data *data,
|
|
|
|
struct pppcp_packet *packet)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* if we wind up using the data field for anything, then
|
|
|
|
* we'd want to check the identifier.
|
|
|
|
* even if the identifiers don't match, we still handle
|
|
|
|
* a terminate ack, as it is allowed to be unelicited
|
|
|
|
*/
|
|
|
|
return RTA;
|
|
|
|
}
|
|
|
|
|
|
|
|
static guint8 pppcp_process_code_reject(struct pppcp_data *data,
|
|
|
|
struct pppcp_packet *packet)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* determine if the code reject is catastrophic or not.
|
|
|
|
* return RXJ_PLUS if this reject is acceptable, RXJ_MINUS if
|
|
|
|
* it is catastrophic.
|
|
|
|
*/
|
|
|
|
return RXJ_MINUS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static guint8 pppcp_process_protocol_reject(struct pppcp_data *data,
|
|
|
|
struct pppcp_packet *packet)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* determine if the protocol reject is catastrophic or not.
|
|
|
|
* return RXJ_PLUS if this reject is acceptable, RXJ_MINUS if
|
|
|
|
* it is catastrophic.
|
|
|
|
*/
|
|
|
|
return RXJ_MINUS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static guint8 pppcp_process_echo_request(struct pppcp_data *data,
|
|
|
|
struct pppcp_packet *packet)
|
|
|
|
{
|
|
|
|
return RXR;
|
|
|
|
}
|
|
|
|
|
|
|
|
static guint8 pppcp_process_echo_reply(struct pppcp_data *data,
|
|
|
|
struct pppcp_packet *packet)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static guint8 pppcp_process_discard_request(struct pppcp_data *data,
|
|
|
|
struct pppcp_packet *packet)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* parse the packet and determine which event this packet caused
|
|
|
|
*/
|
|
|
|
void pppcp_process_packet(gpointer priv, guint8 *new_packet)
|
|
|
|
{
|
|
|
|
struct pppcp_data *data = priv;
|
|
|
|
struct pppcp_packet *packet = (struct pppcp_packet *) new_packet;
|
|
|
|
guint8 event_type;
|
|
|
|
gpointer event_data = NULL;
|
|
|
|
guint data_len = 0;
|
|
|
|
|
|
|
|
if (data == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* check flags to see if we support this code */
|
2010-03-23 03:01:28 +00:00
|
|
|
if (!(data->valid_codes & (1 << packet->code)))
|
2010-03-23 00:05:57 +00:00
|
|
|
event_type = RUC;
|
2010-03-23 03:01:28 +00:00
|
|
|
else
|
2010-03-23 00:05:57 +00:00
|
|
|
event_type = data->packet_ops[packet->code-1](data, packet);
|
2010-03-23 03:01:28 +00:00
|
|
|
|
2010-03-23 00:05:57 +00:00
|
|
|
if (event_type) {
|
|
|
|
data_len = ntohs(packet->length);
|
|
|
|
event_data = packet;
|
|
|
|
pppcp_generate_event(data, event_type, event_data, data_len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void pppcp_set_valid_codes(struct pppcp_data *data, guint16 codes)
|
|
|
|
{
|
|
|
|
if (data == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
data->valid_codes = codes;
|
|
|
|
}
|
|
|
|
|
|
|
|
void pppcp_free(struct pppcp_data *data)
|
|
|
|
{
|
|
|
|
if (data == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* free event queue */
|
|
|
|
if (!g_queue_is_empty(data->event_queue))
|
|
|
|
g_queue_foreach(data->event_queue, (GFunc) g_free, NULL);
|
|
|
|
g_queue_free(data->event_queue);
|
|
|
|
|
|
|
|
/* remove all config options */
|
2010-03-24 17:13:23 +00:00
|
|
|
pppcp_free_options(data);
|
2010-03-23 00:05:57 +00:00
|
|
|
|
|
|
|
/* free self */
|
|
|
|
g_free(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct pppcp_data *pppcp_new(GAtPPP *ppp, guint16 proto,
|
|
|
|
gpointer priv)
|
|
|
|
{
|
|
|
|
struct pppcp_data *data;
|
|
|
|
|
|
|
|
data = g_try_malloc0(sizeof(struct pppcp_data));
|
|
|
|
if (!data)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
data->state = INITIAL;
|
2010-03-25 03:26:28 +00:00
|
|
|
data->config_timer_data.restart_interval = INITIAL_RESTART_TIMEOUT;
|
|
|
|
data->terminate_timer_data.restart_interval = INITIAL_RESTART_TIMEOUT;
|
|
|
|
data->config_timer_data.max_counter = MAX_CONFIGURE;
|
|
|
|
data->terminate_timer_data.max_counter = MAX_TERMINATE;
|
|
|
|
data->config_timer_data.data = data;
|
|
|
|
data->terminate_timer_data.data = data;
|
2010-03-23 00:05:57 +00:00
|
|
|
data->max_failure = MAX_FAILURE;
|
|
|
|
data->event_queue = g_queue_new();
|
|
|
|
data->identifier = 0;
|
|
|
|
data->ppp = ppp;
|
|
|
|
data->proto = proto;
|
|
|
|
data->priv = priv;
|
|
|
|
|
|
|
|
/* setup func ptrs for processing packet by pppcp code */
|
|
|
|
data->packet_ops[CONFIGURE_REQUEST - 1] =
|
|
|
|
pppcp_process_configure_request;
|
|
|
|
data->packet_ops[CONFIGURE_ACK - 1] = pppcp_process_configure_ack;
|
|
|
|
data->packet_ops[CONFIGURE_NAK - 1] = pppcp_process_configure_nak;
|
|
|
|
data->packet_ops[CONFIGURE_REJECT - 1] = pppcp_process_configure_reject;
|
|
|
|
data->packet_ops[TERMINATE_REQUEST - 1] =
|
|
|
|
pppcp_process_terminate_request;
|
|
|
|
data->packet_ops[TERMINATE_ACK - 1] = pppcp_process_terminate_ack;
|
|
|
|
data->packet_ops[CODE_REJECT - 1] = pppcp_process_code_reject;
|
|
|
|
data->packet_ops[PROTOCOL_REJECT - 1] = pppcp_process_protocol_reject;
|
|
|
|
data->packet_ops[ECHO_REQUEST - 1] = pppcp_process_echo_request;
|
|
|
|
data->packet_ops[ECHO_REPLY - 1] = pppcp_process_echo_reply;
|
|
|
|
data->packet_ops[DISCARD_REQUEST - 1] = pppcp_process_discard_request;
|
|
|
|
|
|
|
|
/* setup func ptrs for handling events by event type */
|
|
|
|
data->event_ops[UP] = pppcp_up_event;
|
|
|
|
data->event_ops[DOWN] = pppcp_down_event;
|
|
|
|
data->event_ops[OPEN] = pppcp_open_event;
|
|
|
|
data->event_ops[CLOSE] = pppcp_close_event;
|
|
|
|
data->event_ops[TO_PLUS] = pppcp_to_plus_event;
|
|
|
|
data->event_ops[TO_MINUS] = pppcp_to_minus_event;
|
|
|
|
data->event_ops[RCR_PLUS] = pppcp_rcr_plus_event;
|
|
|
|
data->event_ops[RCR_MINUS] = pppcp_rcr_minus_event;
|
|
|
|
data->event_ops[RCA] = pppcp_rca_event;
|
|
|
|
data->event_ops[RCN] = pppcp_rcn_event;
|
|
|
|
data->event_ops[RTR] = pppcp_rtr_event;
|
|
|
|
data->event_ops[RTA] = pppcp_rta_event;
|
|
|
|
data->event_ops[RUC] = pppcp_ruc_event;
|
|
|
|
data->event_ops[RXJ_PLUS] = pppcp_rxj_plus_event;
|
|
|
|
data->event_ops[RXJ_MINUS] = pppcp_rxj_minus_event;
|
|
|
|
data->event_ops[RXR] = pppcp_rxr_event;
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|