/* * * 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" #define LCP_SUPPORTED_CODES ((1 << PPPCP_CODE_TYPE_CONFIGURE_REQUEST) | \ (1 << PPPCP_CODE_TYPE_CONFIGURE_ACK) | \ (1 << PPPCP_CODE_TYPE_CONFIGURE_NAK) | \ (1 << PPPCP_CODE_TYPE_CONFIGURE_REJECT) | \ (1 << PPPCP_CODE_TYPE_TERMINATE_REQUEST) | \ (1 << PPPCP_CODE_TYPE_TERMINATE_ACK) | \ (1 << PPPCP_CODE_TYPE_CODE_REJECT) | \ (1 << PPPCP_CODE_TYPE_PROTOCOL_REJECT) | \ (1 << PPPCP_CODE_TYPE_ECHO_REQUEST) | \ (1 << PPPCP_CODE_TYPE_ECHO_REPLY) | \ (1 << PPPCP_CODE_TYPE_DISCARD_REQUEST)) enum lcp_options { RESERVED = 0, MRU = 1, ACCM = 2, AUTH_PROTO = 3, QUAL_PROTO = 4, MAGIC_NUMBER = 5, DEPRECATED_QUAL_PROTO = 6, PFC = 7, ACFC = 8, }; /* Maximum size of all options, we only ever request ACCM, MRU, ACFC and PFC */ #define MAX_CONFIG_OPTION_SIZE 14 #define REQ_OPTION_ACCM 0x1 #define REQ_OPTION_MRU 0x2 #define REQ_OPTION_ACFC 0x4 #define REQ_OPTION_PFC 0x8 struct lcp_data { guint8 options[MAX_CONFIG_OPTION_SIZE]; guint16 options_len; guint8 req_options; guint32 accm; /* ACCM value */ guint16 mru; }; static void lcp_generate_config_options(struct lcp_data *lcp) { guint16 len = 0; if (lcp->req_options & REQ_OPTION_ACCM) { guint32 accm; accm = htonl(lcp->accm); lcp->options[len] = ACCM; lcp->options[len + 1] = 6; memcpy(lcp->options + len + 2, &accm, sizeof(accm)); len += 6; } if (lcp->req_options & REQ_OPTION_MRU) { guint16 mru; mru = htons(lcp->mru); lcp->options[len] = MRU; lcp->options[len + 1] = 4; memcpy(lcp->options + len + 2, &mru, sizeof(mru)); len += 4; } if (lcp->req_options & REQ_OPTION_ACFC) { lcp->options[len] = ACFC; lcp->options[len + 1] = 2; len += 2; } if (lcp->req_options & REQ_OPTION_PFC) { lcp->options[len] = PFC; lcp->options[len + 1] = 2; len += 2; } lcp->options_len = len; } static void lcp_reset_config_options(struct lcp_data *lcp) { /* Using the default ACCM */ lcp_generate_config_options(lcp); } /* * signal the Up event to the NCP */ static void lcp_up(struct pppcp_data *pppcp) { ppp_lcp_up_notify(pppcp_get_ppp(pppcp)); } /* * signal the Down event to the NCP */ static void lcp_down(struct pppcp_data *pppcp) { struct lcp_data *lcp = pppcp_get_data(pppcp); lcp_reset_config_options(lcp); pppcp_set_local_options(pppcp, lcp->options, lcp->options_len); ppp_lcp_down_notify(pppcp_get_ppp(pppcp)); } /* * Indicate that the lower layer is not needed * Should trigger Down event */ static void lcp_finished(struct pppcp_data *pppcp) { ppp_lcp_finished_notify(pppcp_get_ppp(pppcp)); } static void lcp_rca(struct pppcp_data *pppcp, const struct pppcp_packet *packet) { struct ppp_option_iter iter; ppp_option_iter_init(&iter, packet); while (ppp_option_iter_next(&iter) == TRUE) { const guint8 *data = ppp_option_iter_get_data(&iter); switch (ppp_option_iter_get_type(&iter)) { case ACCM: /* * RFC1662 Section 7.1 * The Configuration Option is used to inform the peer * which control characters MUST remain mapped when * the peer sends them. */ ppp_set_recv_accm(pppcp_get_ppp(pppcp), get_host_long(data)); break; default: break; } } } static void lcp_rcn_nak(struct pppcp_data *pppcp, const struct pppcp_packet *packet) { struct lcp_data *lcp = pppcp_get_data(pppcp); struct ppp_option_iter iter; ppp_option_iter_init(&iter, packet); while (ppp_option_iter_next(&iter) == TRUE) { const guint8 *data = ppp_option_iter_get_data(&iter); switch (ppp_option_iter_get_type(&iter)) { case MRU: { guint16 mru = get_host_short(data); if (mru < 2048) { lcp->mru = get_host_short(data); lcp->req_options |= REQ_OPTION_MRU; } break; } default: break; } } lcp_generate_config_options(lcp); pppcp_set_local_options(pppcp, lcp->options, lcp->options_len); } static void lcp_rcn_rej(struct pppcp_data *pppcp, const struct pppcp_packet *packet) { } static enum rcr_result lcp_rcr(struct pppcp_data *pppcp, const struct pppcp_packet *packet, guint8 **new_options, guint16 *new_len) { GAtPPP *ppp = pppcp_get_ppp(pppcp); struct ppp_option_iter iter; ppp_option_iter_init(&iter, packet); while (ppp_option_iter_next(&iter) == TRUE) { switch (ppp_option_iter_get_type(&iter)) { case AUTH_PROTO: { const guint8 *option_data = ppp_option_iter_get_data(&iter); guint16 proto = get_host_short(option_data); guint8 method = option_data[2]; guint8 *option; switch (g_at_ppp_get_auth_method(ppp)) { case G_AT_PPP_AUTH_METHOD_CHAP: if (proto == CHAP_PROTOCOL && method == MD5) break; /* * Try to suggest CHAP/MD5. * Just reject if we run out of memory. */ option = g_try_malloc0(5); if (option == NULL) return RCR_REJECT; option[0] = AUTH_PROTO; option[1] = 5; put_network_short(&option[2], CHAP_PROTOCOL); option[4] = MD5; *new_options = option; *new_len = 5; return RCR_NAK; case G_AT_PPP_AUTH_METHOD_PAP: if (proto == PAP_PROTOCOL) break; /* * Try to suggest PAP. * Just reject if we run out of memory. */ option = g_try_malloc0(4); if (option == NULL) return RCR_REJECT; option[0] = AUTH_PROTO; option[1] = 4; put_network_short(&option[2], PAP_PROTOCOL); *new_options = option; *new_len = 4; return RCR_NAK; } break; } case ACCM: case PFC: case ACFC: case MRU: break; case MAGIC_NUMBER: { guint32 magic = get_host_long(ppp_option_iter_get_data(&iter)); if (magic == 0) return RCR_REJECT; break; } default: return RCR_REJECT; } } /* All options were found acceptable, apply them here and return */ ppp_option_iter_init(&iter, packet); while (ppp_option_iter_next(&iter) == TRUE) { switch (ppp_option_iter_get_type(&iter)) { case ACCM: /* * RFC1662 Section 7.1 * The Configuration Option is used to inform the peer * which control characters MUST remain mapped when * the peer sends them. */ ppp_set_xmit_accm(ppp, get_host_long(ppp_option_iter_get_data(&iter))); break; case AUTH_PROTO: ppp_set_auth(ppp, ppp_option_iter_get_data(&iter)); break; case MRU: ppp_set_mtu(ppp, ppp_option_iter_get_data(&iter)); break; case MAGIC_NUMBER: /* don't care */ break; case PFC: { struct lcp_data *lcp = pppcp_get_data(pppcp); if (lcp->req_options & REQ_OPTION_PFC) ppp_set_xmit_pfc(ppp, TRUE); break; } case ACFC: { struct lcp_data *lcp = pppcp_get_data(pppcp); if (lcp->req_options & REQ_OPTION_ACFC) ppp_set_xmit_acfc(ppp, TRUE); break; } } } return RCR_ACCEPT; } struct pppcp_proto lcp_proto = { .proto = LCP_PROTOCOL, .name = "lcp", .supported_codes = LCP_SUPPORTED_CODES, .this_layer_up = lcp_up, .this_layer_down = lcp_down, .this_layer_finished = lcp_finished, .rca = lcp_rca, .rcn_nak = lcp_rcn_nak, .rcn_rej = lcp_rcn_rej, .rcr = lcp_rcr, }; void lcp_free(struct pppcp_data *pppcp) { struct lcp_data *lcp = pppcp_get_data(pppcp); g_free(lcp); pppcp_free(pppcp); } struct pppcp_data *lcp_new(GAtPPP *ppp, gboolean is_server) { struct pppcp_data *pppcp; struct lcp_data *lcp; lcp = g_try_new0(struct lcp_data, 1); if (lcp == NULL) return NULL; pppcp = pppcp_new(ppp, &lcp_proto, is_server, 0); if (pppcp == NULL) { g_free(lcp); return NULL; } pppcp_set_data(pppcp, lcp); lcp_reset_config_options(lcp); pppcp_set_local_options(pppcp, lcp->options, lcp->options_len); return pppcp; } void lcp_set_acfc_enabled(struct pppcp_data *pppcp, gboolean enabled) { struct lcp_data *lcp = pppcp_get_data(pppcp); guint8 old = lcp->req_options; if (enabled == TRUE) lcp->req_options |= REQ_OPTION_ACFC; else lcp->req_options &= ~REQ_OPTION_ACFC; if (lcp->req_options == old) return; lcp_generate_config_options(lcp); pppcp_set_local_options(pppcp, lcp->options, lcp->options_len); } void lcp_set_pfc_enabled(struct pppcp_data *pppcp, gboolean enabled) { struct lcp_data *lcp = pppcp_get_data(pppcp); guint8 old = lcp->req_options; if (enabled == TRUE) lcp->req_options |= REQ_OPTION_PFC; else lcp->req_options &= ~REQ_OPTION_PFC; if (lcp->req_options == old) return; lcp_generate_config_options(lcp); pppcp_set_local_options(pppcp, lcp->options, lcp->options_len); }