/* * * PPP library with GLib integration * * Copyright (C) 2009-2011 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 #endif #include #include #include #include #include #include #include #include "gatppp.h" #include "ppp.h" static const char *pppcp_state_strings[] = { "INITIAL", "STARTING", "CLOSED", "STOPPED", "CLOSING", "STOPPING", "REQSENT", "ACKRCVD", "ACKSENT", "OPENED" }; static const char *pppcp_event_strings[] = { "Up", "Down", "Open", "Close", "TO+", "TO-", "RCR+", "RCR-", "RCA", "RCN", "RTR", "RTA", "RUC", "RXJ+", "RXJ-", "RXR" }; #define pppcp_trace(p) do { \ char *str = g_strdup_printf("%s: %s: current state %d:%s", \ p->driver->name, __FUNCTION__, \ p->state, pppcp_state_strings[p->state]); \ ppp_debug(p->ppp, str); \ g_free(str); \ } while (0); #define pppcp_trace_event(p, type, actions, state) do { \ char *str = g_strdup_printf("event: %d (%s), " \ "action: %x, new_state: %d (%s)", \ type, pppcp_event_strings[type], \ actions, state, pppcp_state_strings[state]); \ ppp_debug(p->ppp, str); \ g_free(str); \ } while (0); #define pppcp_to_ppp_packet(p) \ (((guint8 *) p) - sizeof(struct ppp_header)) #define INITIAL_RESTART_TIMEOUT 3 /* restart interval in seconds */ #define MAX_TERMINATE 2 #define MAX_CONFIGURE 10 #define MAX_FAILURE 5 #define CP_HEADER_SZ 4 enum pppcp_state { INITIAL = 0, STARTING = 1, CLOSED = 2, STOPPED = 3, CLOSING = 4, STOPPING = 5, REQSENT = 6, ACKRCVD = 7, ACKSENT = 8, OPENED = 9, }; enum actions { INV = 0x10, IRC = 0x20, ZRC = 0x40, TLU = 0x100, TLD = 0x200, TLS = 0x400, TLF = 0x800, SCR = 0x1000, SCA = 0x2000, SCN = 0x4000, STR = 0x8000, STA = 0x10000, SCJ = 0x20000, SER = 0x40000, }; /* * Transition table straight from RFC 1661 Section 4.1 * Y coordinate is the events, while X coordinate is the state * * Magic of bitwise operations allows the table to describe all state * transitions defined in the specification */ static int cp_transitions[16][10] = { /* Up */ { 2, IRC|SCR|6, INV, INV, INV, INV, INV, INV, INV, INV }, /* Down */ { INV, INV, 0, TLS|1, 0, 1, 1, 1, 1, TLD|1 }, /* Open */ { TLS|1, 1, IRC|SCR|6, 3, 5, 5, 6, 7, 8, 9 }, /* Close */ { 0, TLF|0, 2, 2, 4, 4, IRC|STR|4, IRC|STR|4, IRC|STR|4, TLD|IRC|STR|4 }, /* TO+ */ { INV, INV, INV, INV, STR|4, STR|5, SCR|6, SCR|6, SCR|8, INV }, /* TO- */ { INV, INV, INV, INV, TLF|2, TLF|3, TLF|3, TLF|3, TLF|3, INV }, /* RCR+ */ { INV, INV, STA|2, IRC|SCR|SCA|8, 4, 5, SCA|8, SCA|TLU|9, SCA|8, TLD|SCR|SCA|8 }, /* RCR- */ { INV, INV, STA|2, IRC|SCR|SCN|6, 4, 5, SCN|6, SCN|7, SCN|6, TLD|SCR|SCN|6 }, /* RCA */ { INV, INV, STA|2, STA|3, 4, 5, IRC|7, SCR|6, IRC|TLU|9, TLD|SCR|6 }, /* RCN */ { INV, INV, STA|2, STA|3, 4, 5, IRC|SCR|6, SCR|6, IRC|SCR|8, TLD|SCR|6 }, /* RTR */ { INV, INV, STA|2, STA|3, STA|4, STA|5, STA|6, STA|6, STA|6, TLD|ZRC|STA|5 }, /* RTA */ { INV, INV, 2, 3, TLF|2, TLF|3, 6, 6, 8, TLD|SCR|6 }, /* RUC */ { INV, INV, SCJ|2, SCJ|3, SCJ|4, SCJ|5, SCJ|6, SCJ|7, SCJ|8, SCJ|9 }, /* RXJ+ */ { INV, INV, 2, 3, 4, 5, 6, 6, 8, 9 }, /* RXJ- */ { INV, INV, TLF|2, TLF|3, TLF|2, TLF|3, TLF|3, TLF|3, TLF|3, TLD|IRC|STR|5 }, /* RXR */ { INV, INV, 2, 3, 4, 5, 6, 7, 8, SER|9 }, }; enum pppcp_event_type { UP = 0, DOWN = 1, OPEN = 2, CLOSE = 3, TO_PLUS = 4, TO_MINUS = 5, RCR_PLUS = 6, RCR_MINUS = 7, RCA = 8, RCN = 9, RTR = 10, RTA = 11, RUC = 12, RXJ_PLUS = 13, RXJ_MINUS = 14, RXR = 15, }; struct pppcp_timer_data { struct pppcp_data *data; guint restart_counter; guint restart_interval; guint max_counter; guint restart_timer; }; struct pppcp_data { unsigned char state; struct pppcp_timer_data config_timer_data; struct pppcp_timer_data terminate_timer_data; guint max_failure; guint failure_counter; GAtPPP *ppp; guint8 config_identifier; guint8 terminate_identifier; guint8 reject_identifier; const guint8 *local_options; guint16 local_options_len; guint8 *peer_options; guint16 peer_options_len; gboolean send_reject; const struct pppcp_proto *driver; gpointer priv; }; static void pppcp_generate_event(struct pppcp_data *data, enum pppcp_event_type event_type, const guint8 *packet, guint len); static void pppcp_packet_free(struct pppcp_packet *packet) { g_free(pppcp_to_ppp_packet(packet)); } 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 = ppp_packet_new(packet_length, data->driver->proto); if (ppp_packet == NULL) return NULL; /* 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; } void ppp_option_iter_init(struct ppp_option_iter *iter, const struct pppcp_packet *packet) { iter->max = ntohs(packet->length) - CP_HEADER_SZ; iter->pdata = packet->data; iter->pos = 0; iter->type = 0; iter->len = 0; iter->option_data = NULL; } gboolean ppp_option_iter_next(struct ppp_option_iter *iter) { const guint8 *cur = iter->pdata + iter->pos; const guint8 *end = iter->pdata + iter->max; if (cur + 1 > end) return FALSE; if (cur + cur[1] > end) return FALSE; iter->type = cur[0]; iter->len = cur[1] - 2; iter->option_data = cur + 2; iter->pos += cur[1]; return TRUE; } guint8 ppp_option_iter_get_type(struct ppp_option_iter *iter) { return iter->type; } guint8 ppp_option_iter_get_length(struct ppp_option_iter *iter) { return iter->len; } const guint8 *ppp_option_iter_get_data(struct ppp_option_iter *iter) { return iter->option_data; } guint8 pppcp_get_code(const guint8 *data) { struct ppp_header *ppp_packet = (struct ppp_header *) data; struct pppcp_packet *packet = (struct pppcp_packet *) ppp_packet->info; return packet->code; } static gboolean pppcp_timeout(gpointer user_data) { struct pppcp_timer_data *timer_data = user_data; pppcp_trace(timer_data->data); timer_data->restart_timer = 0; if (timer_data->restart_counter > 0) pppcp_generate_event(timer_data->data, TO_PLUS, NULL, 0); else pppcp_generate_event(timer_data->data, TO_MINUS, NULL, 0); return FALSE; } static void pppcp_stop_timer(struct pppcp_timer_data *timer_data) { if (timer_data->restart_timer > 0) { g_source_remove(timer_data->restart_timer); timer_data->restart_timer = 0; } } static void pppcp_start_timer(struct pppcp_timer_data *timer_data) { pppcp_stop_timer(timer_data); timer_data->restart_timer = g_timeout_add_seconds(timer_data->restart_interval, pppcp_timeout, timer_data); } static gboolean is_first_request(struct pppcp_timer_data *timer_data) { return (timer_data->restart_counter == timer_data->max_counter); } /* actions */ /* log an illegal event, but otherwise do nothing */ static void pppcp_illegal_event(GAtPPP *ppp, guint8 state, guint8 type) { DBG(ppp, "Illegal event %d while in state %d", type, state); } static void pppcp_this_layer_up(struct pppcp_data *data) { if (data->driver->this_layer_up) data->driver->this_layer_up(data); } static void pppcp_this_layer_down(struct pppcp_data *data) { if (data->driver->this_layer_down) data->driver->this_layer_down(data); } static void pppcp_this_layer_started(struct pppcp_data *data) { if (data->driver->this_layer_started) data->driver->this_layer_started(data); } static void pppcp_this_layer_finished(struct pppcp_data *data) { pppcp_trace(data); if (data->driver->this_layer_finished) data->driver->this_layer_finished(data); } /* * set the restart counter to either max-terminate * or max-configure. The counter is decremented for * each transmission, including the first. */ static void pppcp_initialize_restart_count(struct pppcp_timer_data *timer_data) { struct pppcp_data *data = timer_data->data; pppcp_trace(data); timer_data->restart_counter = timer_data->max_counter; } /* * set restart counter to zero */ static void pppcp_zero_restart_count(struct pppcp_timer_data *timer_data) { timer_data->restart_counter = 0; } /* * TBD - generate new identifier for packet */ static guint8 new_identity(struct pppcp_data *data, guint prev_identifier) { return prev_identifier + 1; } /* * transmit a Configure-Request packet * start the restart timer * decrement the restart counter */ static void pppcp_send_configure_request(struct pppcp_data *pppcp) { struct pppcp_packet *packet; struct pppcp_timer_data *timer_data = &pppcp->config_timer_data; pppcp_trace(pppcp); packet = pppcp_packet_new(pppcp, PPPCP_CODE_TYPE_CONFIGURE_REQUEST, pppcp->local_options_len); memcpy(packet->data, pppcp->local_options, pppcp->local_options_len); /* * if this is the first request, we need a new identifier. * if this is a retransmission, leave the identifier alone. */ if (is_first_request(timer_data)) pppcp->config_identifier = new_identity(pppcp, pppcp->config_identifier); packet->identifier = pppcp->config_identifier; ppp_transmit(pppcp->ppp, pppcp_to_ppp_packet(packet), ntohs(packet->length)); pppcp_packet_free(packet); /* start timer for retransmission */ timer_data->restart_counter--; pppcp_start_timer(timer_data); } /* * transmit a Configure-Ack packet */ static void pppcp_send_configure_ack(struct pppcp_data *pppcp, const guint8 *request) { struct pppcp_packet *packet; struct pppcp_packet *cr_req = (struct pppcp_packet *) request; guint16 len; pppcp_trace(pppcp); pppcp->failure_counter = 0; /* subtract for header. */ len = ntohs(cr_req->length) - CP_HEADER_SZ; packet = pppcp_packet_new(pppcp, PPPCP_CODE_TYPE_CONFIGURE_ACK, len); memcpy(packet->data, cr_req->data, len); packet->identifier = cr_req->identifier; ppp_transmit(pppcp->ppp, pppcp_to_ppp_packet(packet), ntohs(packet->length)); pppcp_packet_free(packet); } /* * transmit a Configure-Nak or Configure-Reject packet */ static void pppcp_send_configure_nak(struct pppcp_data *pppcp, const guint8 *request) { struct pppcp_packet *packet; struct pppcp_packet *cr_req = (struct pppcp_packet *) request; pppcp_trace(pppcp); /* * if we have exceeded our Max-Failure counter, we simply reject all * the options. */ if (pppcp->failure_counter >= pppcp->max_failure) { guint16 len = ntohs(cr_req->length) - CP_HEADER_SZ; packet = pppcp_packet_new(pppcp, PPPCP_CODE_TYPE_CONFIGURE_REJECT, len); memcpy(packet->data, cr_req->data, len); } else { enum pppcp_code code = PPPCP_CODE_TYPE_CONFIGURE_NAK; if (pppcp->send_reject == TRUE) code = PPPCP_CODE_TYPE_CONFIGURE_REJECT; else pppcp->failure_counter++; packet = pppcp_packet_new(pppcp, code, pppcp->peer_options_len); memcpy(packet->data, pppcp->peer_options, pppcp->peer_options_len); } packet->identifier = cr_req->identifier; ppp_transmit(pppcp->ppp, pppcp_to_ppp_packet(packet), ntohs(packet->length)); pppcp_packet_free(packet); g_free(pppcp->peer_options); pppcp->peer_options = NULL; pppcp->peer_options_len = 0; } /* * 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; struct pppcp_timer_data *timer_data = &data->terminate_timer_data; pppcp_trace(data); /* * the data field can be used by the sender (us). * leave this empty for now. */ packet = pppcp_packet_new(data, PPPCP_CODE_TYPE_TERMINATE_REQUEST, 0); /* * Is this a retransmission? If so, do not change * the identifier. If not, we need a fresh identity. */ if (is_first_request(timer_data)) data->terminate_identifier = new_identity(data, data->terminate_identifier); packet->identifier = data->terminate_identifier; ppp_transmit(data->ppp, pppcp_to_ppp_packet(packet), ntohs(packet->length)); pppcp_packet_free(packet); timer_data->restart_counter--; pppcp_start_timer(timer_data); } /* * transmit a Terminate-Ack packet */ static void pppcp_send_terminate_ack(struct pppcp_data *data, const guint8 *request) { struct pppcp_packet *packet; struct pppcp_packet *pppcp_header = (struct pppcp_packet *) request; struct pppcp_timer_data *timer_data = &data->terminate_timer_data; pppcp_trace(data); packet = pppcp_packet_new(data, PPPCP_CODE_TYPE_TERMINATE_ACK, 0); /* match identifier of the request */ packet->identifier = pppcp_header->identifier; ppp_transmit(data->ppp, pppcp_to_ppp_packet(packet), ntohs(pppcp_header->length)); pppcp_packet_free(packet); pppcp_start_timer(timer_data); } /* * transmit a Code-Reject packet * * XXX this seg faults. */ static void pppcp_send_code_reject(struct pppcp_data *data, const guint8 *rejected_packet) { struct pppcp_packet *packet; const struct pppcp_packet *old_packet = (const struct pppcp_packet *) rejected_packet; pppcp_trace(data); packet = pppcp_packet_new(data, PPPCP_CODE_TYPE_CODE_REJECT, ntohs(old_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); ppp_transmit(data->ppp, pppcp_to_ppp_packet(packet), ntohs(packet->length)); pppcp_packet_free(packet); } /* * transmit an Echo-Reply packet */ static void pppcp_send_echo_reply(struct pppcp_data *data, const 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, PPPCP_CODE_TYPE_ECHO_REPLY, 4); /* * match identifier of request */ packet->identifier = header->identifier; /* magic number will always be zero */ ppp_transmit(data->ppp, pppcp_to_ppp_packet(packet), ntohs(packet->length)); pppcp_packet_free(packet); } 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: pppcp_stop_timer(&data->config_timer_data); pppcp_stop_timer(&data->terminate_timer_data); break; case CLOSING: case STOPPING: case REQSENT: case ACKRCVD: case ACKSENT: break; } data->state = new_state; } /* * send the event handler a new event to process */ static void pppcp_generate_event(struct pppcp_data *data, enum pppcp_event_type event_type, const guint8 *packet, guint len) { int actions; unsigned char new_state; if (event_type > RXR) goto error; pppcp_trace(data); actions = cp_transitions[event_type][data->state]; new_state = actions & 0xf; pppcp_trace_event(data, event_type, actions, new_state); if (actions & INV) goto error; if (actions & IRC) { struct pppcp_timer_data *timer_data; if (new_state == CLOSING || new_state == STOPPING) timer_data = &data->terminate_timer_data; else timer_data = &data->config_timer_data; pppcp_initialize_restart_count(timer_data); } else if (actions & ZRC) pppcp_zero_restart_count(&data->terminate_timer_data); if (actions & SCR) pppcp_send_configure_request(data); if (actions & SCA) pppcp_send_configure_ack(data, packet); else if (actions & SCN) pppcp_send_configure_nak(data, packet); if (actions & STR) pppcp_send_terminate_request(data); else if (actions & STA) pppcp_send_terminate_ack(data, packet); if (actions & SCJ) pppcp_send_code_reject(data, packet); if (actions & SER) pppcp_send_echo_reply(data, packet); pppcp_transition_state(new_state, data); if (actions & TLS) pppcp_this_layer_started(data); else if (actions & TLU) pppcp_this_layer_up(data); else if (actions & TLD) pppcp_this_layer_down(data); else if (actions & TLF) pppcp_this_layer_finished(data); return; error: pppcp_illegal_event(data->ppp, data->state, event_type); } void pppcp_signal_open(struct pppcp_data *data) { pppcp_generate_event(data, OPEN, NULL, 0); } void pppcp_signal_close(struct pppcp_data *data) { pppcp_generate_event(data, CLOSE, NULL, 0); } void pppcp_signal_up(struct pppcp_data *data) { pppcp_generate_event(data, UP, NULL, 0); } void pppcp_signal_down(struct pppcp_data *data) { pppcp_generate_event(data, DOWN, NULL, 0); } static guint8 pppcp_process_configure_request(struct pppcp_data *pppcp, const struct pppcp_packet *packet) { pppcp_trace(pppcp); if (pppcp->failure_counter >= pppcp->max_failure) return RCR_MINUS; if (pppcp->driver->rcr) { enum rcr_result res; res = pppcp->driver->rcr(pppcp, packet, &pppcp->peer_options, &pppcp->peer_options_len); if (res == RCR_REJECT) { pppcp->send_reject = TRUE; return RCR_MINUS; } else if (res == RCR_NAK) { pppcp->send_reject = FALSE; return RCR_MINUS; } } return RCR_PLUS; } static guint8 pppcp_process_configure_ack(struct pppcp_data *pppcp, const struct pppcp_packet *packet) { gint len; pppcp_trace(pppcp); len = ntohs(packet->length) - CP_HEADER_SZ; /* if identifiers don't match, we should silently discard */ if (packet->identifier != pppcp->config_identifier) { return 0; } /* * First we must sanity check that all config options acked are * equal to the config options sent and are in the same order. * If this is not the case, then silently drop the packet */ if (pppcp->local_options_len != len) return 0; if (memcmp(pppcp->local_options, packet->data, len)) return 0; /* Otherwise, apply local options */ if (pppcp->driver->rca) pppcp->driver->rca(pppcp, packet); return RCA; } static guint8 pppcp_process_configure_nak(struct pppcp_data *pppcp, const struct pppcp_packet *packet) { pppcp_trace(pppcp); /* if identifiers don't match, we should silently discard */ if (packet->identifier != pppcp->config_identifier) return 0; if (pppcp->driver->rcn_nak) pppcp->driver->rcn_nak(pppcp, packet); return RCN; } static guint8 pppcp_process_configure_reject(struct pppcp_data *pppcp, const struct pppcp_packet *packet) { pppcp_trace(pppcp); /* * make sure identifier matches that of last sent configure * request */ if (packet->identifier != pppcp->config_identifier) return 0; /* * check to see which options were rejected * Rejected options must be a subset of requested * options and in the same order. * * when a new configure-request is sent, we may * not request any of these options be negotiated */ if (pppcp->driver->rcn_rej) pppcp->driver->rcn_rej(pppcp, packet); return RCN; } static guint8 pppcp_process_terminate_request(struct pppcp_data *data, const struct pppcp_packet *packet) { pppcp_trace(data); return RTR; } static guint8 pppcp_process_terminate_ack(struct pppcp_data *data, const 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 */ pppcp_trace(data); return RTA; } static guint8 pppcp_process_code_reject(struct pppcp_data *data, const 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. * * for now we always return RXJ_MINUS. Any code * reject will be catastrophic, since we only support the * bare minimum number of codes necessary to function. */ return RXJ_MINUS; } static guint8 pppcp_process_protocol_reject(struct pppcp_data *data, const 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. * * for now we always return RXJ_MINUS. Any protocol * reject will be catastrophic, since we only support the * bare minimum number of protocols necessary to function. */ return RXJ_MINUS; } /* * For Echo-Request, Echo-Reply, and Discard-Request, we will not * bother checking the magic number of the packet, because we will * never send an echo or discard request. We can't reliably detect * loop back anyway, since we don't negotiate a magic number. */ static guint8 pppcp_process_echo_request(struct pppcp_data *data, const struct pppcp_packet *packet) { return RXR; } static guint8 pppcp_process_echo_reply(struct pppcp_data *data, const struct pppcp_packet *packet) { return 0; } static guint8 pppcp_process_discard_request(struct pppcp_data *data, const struct pppcp_packet *packet) { return 0; } static guint8 (*packet_ops[11])(struct pppcp_data *data, const struct pppcp_packet *packet) = { pppcp_process_configure_request, pppcp_process_configure_ack, pppcp_process_configure_nak, pppcp_process_configure_reject, pppcp_process_terminate_request, pppcp_process_terminate_ack, pppcp_process_code_reject, pppcp_process_protocol_reject, pppcp_process_echo_request, pppcp_process_echo_reply, pppcp_process_discard_request, }; void pppcp_send_protocol_reject(struct pppcp_data *data, const guint8 *rejected_packet, gsize len) { struct pppcp_packet *packet; pppcp_trace(data); /* * Protocol-Reject can only be sent when we are in * the OPENED state. If in any other state, silently discard. */ if (data->state != OPENED) return; /* * info should contain the old packet info, plus the 16bit * protocol number we are rejecting. */ packet = pppcp_packet_new(data, PPPCP_CODE_TYPE_PROTOCOL_REJECT, len - 2); /* * Identifier must be changed for each Protocol-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 + 2, len - 2); ppp_transmit(data->ppp, pppcp_to_ppp_packet(packet), ntohs(packet->length)); pppcp_packet_free(packet); } /* * parse the packet and determine which event this packet caused */ void pppcp_process_packet(gpointer priv, const guint8 *new_packet, gsize len) { struct pppcp_data *data = priv; const struct pppcp_packet *packet = (const struct pppcp_packet *) new_packet; guint8 event_type; guint data_len = 0; if (len < sizeof(struct pppcp_packet)) return; /* check flags to see if we support this code */ if (!(data->driver->supported_codes & (1 << packet->code))) event_type = RUC; else event_type = packet_ops[packet->code-1](data, packet); if (event_type) { data_len = ntohs(packet->length); pppcp_generate_event(data, event_type, new_packet, data_len); } } void pppcp_free(struct pppcp_data *pppcp) { pppcp_stop_timer(&pppcp->config_timer_data); pppcp_stop_timer(&pppcp->terminate_timer_data); g_free(pppcp->peer_options); g_free(pppcp); } void pppcp_set_data(struct pppcp_data *pppcp, gpointer data) { pppcp->priv = data; } gpointer pppcp_get_data(struct pppcp_data *pppcp) { return pppcp->priv; } GAtPPP *pppcp_get_ppp(struct pppcp_data *pppcp) { return pppcp->ppp; } void pppcp_set_local_options(struct pppcp_data *pppcp, const guint8 *options, guint16 len) { pppcp->local_options = options; pppcp->local_options_len = len; } struct pppcp_data *pppcp_new(GAtPPP *ppp, const struct pppcp_proto *proto, gboolean dormant, guint max_failure) { struct pppcp_data *data; data = g_try_malloc0(sizeof(struct pppcp_data)); if (data == NULL) return NULL; if (dormant) data->state = STOPPED; else data->state = INITIAL; 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; if (max_failure) data->max_failure = max_failure; else data->max_failure = MAX_FAILURE; data->ppp = ppp; data->driver = proto; return data; }