| /* |
| * Copyright (c) 2019 Intel Corporation. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_DECLARE(net_l2_ppp, CONFIG_NET_L2_PPP_LOG_LEVEL); |
| |
| #include <zephyr/net/net_core.h> |
| #include <zephyr/net/net_l2.h> |
| #include <zephyr/net/net_if.h> |
| #include <zephyr/net/net_pkt.h> |
| #include <zephyr/net/net_mgmt.h> |
| |
| #include <zephyr/net/ppp.h> |
| |
| #include "net_private.h" |
| |
| #include "ppp_stats.h" |
| #include "ppp_internal.h" |
| |
| static enum net_verdict lcp_handle_ext(struct ppp_fsm *fsm, |
| enum ppp_packet_type code, uint8_t id, |
| struct net_pkt *pkt) |
| { |
| enum net_verdict verdict = NET_DROP; |
| |
| switch (code) { |
| case PPP_PROTOCOL_REJ: |
| NET_DBG("PPP Protocol-Rej"); |
| return ppp_fsm_recv_protocol_rej(fsm, id, pkt); |
| |
| case PPP_ECHO_REQ: |
| NET_DBG("PPP Echo-Req"); |
| return ppp_fsm_recv_echo_req(fsm, id, pkt); |
| |
| case PPP_ECHO_REPLY: |
| NET_DBG("PPP Echo-Reply"); |
| return ppp_fsm_recv_echo_reply(fsm, id, pkt); |
| |
| case PPP_DISCARD_REQ: |
| NET_DBG("PPP Discard-Req"); |
| return ppp_fsm_recv_discard_req(fsm, id, pkt); |
| |
| default: |
| break; |
| } |
| |
| return verdict; |
| } |
| |
| static enum net_verdict lcp_handle(struct ppp_context *ctx, |
| struct net_if *iface, |
| struct net_pkt *pkt) |
| { |
| return ppp_fsm_input(&ctx->lcp.fsm, PPP_LCP, pkt); |
| } |
| |
| struct lcp_option_data { |
| bool auth_proto_present; |
| uint16_t auth_proto; |
| }; |
| |
| static const enum ppp_protocol_type lcp_supported_auth_protos[] = { |
| #if defined(CONFIG_NET_L2_PPP_PAP) |
| PPP_PAP, |
| #endif |
| }; |
| |
| static int lcp_auth_proto_parse(struct ppp_fsm *fsm, struct net_pkt *pkt, |
| void *user_data) |
| { |
| struct lcp_option_data *data = user_data; |
| int ret; |
| int i; |
| |
| ret = net_pkt_read_be16(pkt, &data->auth_proto); |
| if (ret < 0) { |
| /* Should not happen, is the pkt corrupt? */ |
| return -EMSGSIZE; |
| } |
| |
| NET_DBG("[LCP] Received auth protocol %x (%s)", |
| (unsigned int) data->auth_proto, |
| ppp_proto2str(data->auth_proto)); |
| |
| for (i = 0; i < ARRAY_SIZE(lcp_supported_auth_protos); i++) { |
| if (data->auth_proto == lcp_supported_auth_protos[i]) { |
| data->auth_proto_present = true; |
| return 0; |
| } |
| } |
| |
| return -EINVAL; |
| } |
| |
| static int lcp_auth_proto_nack(struct ppp_fsm *fsm, struct net_pkt *ret_pkt, |
| void *user_data) |
| { |
| (void)net_pkt_write_u8(ret_pkt, LCP_OPTION_AUTH_PROTO); |
| (void)net_pkt_write_u8(ret_pkt, 4); |
| return net_pkt_write_be16(ret_pkt, PPP_PAP); |
| } |
| |
| static const struct ppp_peer_option_info lcp_peer_options[] = { |
| PPP_PEER_OPTION(LCP_OPTION_AUTH_PROTO, lcp_auth_proto_parse, |
| lcp_auth_proto_nack), |
| }; |
| |
| static int lcp_config_info_req(struct ppp_fsm *fsm, |
| struct net_pkt *pkt, |
| uint16_t length, |
| struct net_pkt *ret_pkt) |
| { |
| struct ppp_context *ctx = CONTAINER_OF(fsm, struct ppp_context, |
| lcp.fsm); |
| struct lcp_option_data data = { |
| .auth_proto_present = false, |
| }; |
| int ret; |
| |
| ret = ppp_config_info_req(fsm, pkt, length, ret_pkt, PPP_LCP, |
| lcp_peer_options, |
| ARRAY_SIZE(lcp_peer_options), |
| &data); |
| if (ret != PPP_CONFIGURE_ACK) { |
| /* There are some issues with configuration still */ |
| return ret; |
| } |
| |
| ctx->lcp.peer_options.auth_proto = data.auth_proto; |
| |
| if (data.auth_proto_present) { |
| NET_DBG("Authentication protocol negotiated: %x (%s)", |
| (unsigned int) data.auth_proto, |
| ppp_proto2str(data.auth_proto)); |
| } |
| |
| return PPP_CONFIGURE_ACK; |
| } |
| |
| static void lcp_lower_down(struct ppp_context *ctx) |
| { |
| ppp_fsm_lower_down(&ctx->lcp.fsm); |
| } |
| |
| static void lcp_lower_up(struct ppp_context *ctx) |
| { |
| #if defined(CONFIG_NET_L2_PPP_OPTION_MRU) |
| /* Get currently set MTU */ |
| ctx->lcp.my_options.mru = net_if_get_mtu(ctx->iface); |
| #endif |
| |
| ppp_fsm_lower_up(&ctx->lcp.fsm); |
| } |
| |
| static void lcp_open(struct ppp_context *ctx) |
| { |
| ppp_fsm_open(&ctx->lcp.fsm); |
| } |
| |
| static void lcp_close(struct ppp_context *ctx, const uint8_t *reason) |
| { |
| if (ctx->phase != PPP_DEAD) { |
| ppp_change_phase(ctx, PPP_TERMINATE); |
| } |
| |
| ppp_fsm_close(&ctx->lcp.fsm, reason); |
| } |
| |
| static void lcp_down(struct ppp_fsm *fsm) |
| { |
| struct ppp_context *ctx = CONTAINER_OF(fsm, struct ppp_context, |
| lcp.fsm); |
| |
| memset(&ctx->lcp.peer_options.auth_proto, 0, |
| sizeof(ctx->lcp.peer_options.auth_proto)); |
| |
| ppp_link_down(ctx); |
| |
| if (net_if_is_carrier_ok(ctx->iface) && ctx->is_enabled) { |
| ppp_change_phase(ctx, PPP_ESTABLISH); |
| } |
| } |
| |
| static void lcp_up(struct ppp_fsm *fsm) |
| { |
| struct ppp_context *ctx = CONTAINER_OF(fsm, struct ppp_context, |
| lcp.fsm); |
| |
| /* TODO: Set MRU/MTU of the network interface here */ |
| |
| ppp_link_established(ctx, fsm); |
| } |
| |
| static void lcp_starting(struct ppp_fsm *fsm) |
| { |
| struct ppp_context *ctx = CONTAINER_OF(fsm, struct ppp_context, |
| lcp.fsm); |
| |
| ppp_link_needed(ctx); |
| } |
| |
| static void lcp_finished(struct ppp_fsm *fsm) |
| { |
| struct ppp_context *ctx = CONTAINER_OF(fsm, struct ppp_context, |
| lcp.fsm); |
| |
| ppp_link_terminated(ctx); |
| } |
| |
| #if defined(CONFIG_NET_L2_PPP_OPTION_MRU) |
| |
| #define MRU_OPTION_LEN 4 |
| |
| static int lcp_add_mru(struct ppp_context *ctx, struct net_pkt *pkt) |
| { |
| net_pkt_write_u8(pkt, MRU_OPTION_LEN); |
| return net_pkt_write_be16(pkt, ctx->lcp.my_options.mru); |
| } |
| |
| static int lcp_ack_mru(struct ppp_context *ctx, struct net_pkt *pkt, |
| uint8_t oplen) |
| { |
| int ret; |
| uint16_t mru; |
| |
| /* Handle ACK : */ |
| if (oplen != sizeof(mru)) { |
| return -EINVAL; |
| } |
| |
| ret = net_pkt_read(pkt, &mru, sizeof(mru)); |
| if (ret) { |
| return ret; |
| } |
| if (mru != ctx->lcp.my_options.mru) { |
| /* Didn't acked our MRU: */ |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int lcp_nak_mru(struct ppp_context *ctx, struct net_pkt *pkt, |
| uint8_t oplen) |
| { |
| int ret; |
| uint16_t mru; |
| |
| /* Handle NAK: accept only smaller/equal than ours */ |
| if (oplen != sizeof(mru)) { |
| return -EINVAL; |
| } |
| |
| ret = net_pkt_read(pkt, &mru, sizeof(mru)); |
| if (ret) { |
| return ret; |
| } |
| |
| if (mru <= ctx->lcp.my_options.mru) { |
| /* OK, reset the MRU also in our side: */ |
| ctx->lcp.my_options.mru = mru; |
| } else { |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static const struct ppp_my_option_info lcp_my_options[] = { |
| PPP_MY_OPTION(LCP_OPTION_MRU, lcp_add_mru, lcp_ack_mru, lcp_nak_mru), |
| }; |
| BUILD_ASSERT(ARRAY_SIZE(lcp_my_options) == LCP_NUM_MY_OPTIONS); |
| |
| static struct net_pkt *lcp_config_info_add(struct ppp_fsm *fsm) |
| { |
| return ppp_my_options_add(fsm, MRU_OPTION_LEN); |
| } |
| |
| static int lcp_config_info_nack(struct ppp_fsm *fsm, struct net_pkt *pkt, |
| uint16_t length, bool rejected) |
| { |
| struct ppp_context *ctx = |
| CONTAINER_OF(fsm, struct ppp_context, lcp.fsm); |
| int ret; |
| |
| ret = ppp_my_options_parse_conf_nak(fsm, pkt, length); |
| if (ret) { |
| return ret; |
| } |
| |
| if (!ctx->lcp.my_options.mru) { |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| #endif |
| |
| static void lcp_init(struct ppp_context *ctx) |
| { |
| NET_DBG("proto %s (0x%04x) fsm %p", ppp_proto2str(PPP_LCP), PPP_LCP, |
| &ctx->lcp.fsm); |
| |
| memset(&ctx->lcp.fsm, 0, sizeof(ctx->lcp.fsm)); |
| |
| ppp_fsm_init(&ctx->lcp.fsm, PPP_LCP); |
| |
| ppp_fsm_name_set(&ctx->lcp.fsm, ppp_proto2str(PPP_LCP)); |
| |
| ctx->lcp.my_options.mru = net_if_get_mtu(ctx->iface); |
| |
| #if defined(CONFIG_NET_L2_PPP_OPTION_MRU) |
| ctx->lcp.fsm.my_options.info = lcp_my_options; |
| ctx->lcp.fsm.my_options.data = ctx->lcp.my_options_data; |
| ctx->lcp.fsm.my_options.count = ARRAY_SIZE(lcp_my_options); |
| |
| ctx->lcp.fsm.cb.config_info_add = lcp_config_info_add; |
| ctx->lcp.fsm.cb.config_info_req = lcp_config_info_req; |
| ctx->lcp.fsm.cb.config_info_nack = lcp_config_info_nack; |
| ctx->lcp.fsm.cb.config_info_rej = ppp_my_options_parse_conf_rej; |
| #endif |
| |
| ctx->lcp.fsm.cb.up = lcp_up; |
| ctx->lcp.fsm.cb.down = lcp_down; |
| ctx->lcp.fsm.cb.starting = lcp_starting; |
| ctx->lcp.fsm.cb.finished = lcp_finished; |
| if (IS_ENABLED(CONFIG_NET_L2_PPP_AUTH_SUPPORT)) { |
| ctx->lcp.fsm.cb.config_info_req = lcp_config_info_req; |
| } |
| ctx->lcp.fsm.cb.proto_extension = lcp_handle_ext; |
| } |
| |
| PPP_PROTOCOL_REGISTER(LCP, PPP_LCP, |
| lcp_init, lcp_handle, |
| lcp_lower_up, lcp_lower_down, |
| lcp_open, lcp_close); |