/* * Copyright (c) 2019 Intel Corporation. * * SPDX-License-Identifier: Apache-2.0 */ #include LOG_MODULE_DECLARE(net_l2_ppp, CONFIG_NET_L2_PPP_LOG_LEVEL); #include #include #include #include #include #include "net_private.h" #include "ppp_internal.h" /* This timeout is in milliseconds */ #define FSM_TIMEOUT K_MSEC(CONFIG_NET_L2_PPP_TIMEOUT) #define MAX_NACK_LOOPS CONFIG_NET_L2_PPP_MAX_NACK_LOOPS struct ppp_context *ppp_fsm_ctx(struct ppp_fsm *fsm) { if (fsm->protocol == PPP_LCP) { return CONTAINER_OF(fsm, struct ppp_context, lcp.fsm); #if defined(CONFIG_NET_IPV4) } else if (fsm->protocol == PPP_IPCP) { return CONTAINER_OF(fsm, struct ppp_context, ipcp.fsm); #endif #if defined(CONFIG_NET_IPV6) } else if (fsm->protocol == PPP_IPV6CP) { return CONTAINER_OF(fsm, struct ppp_context, ipv6cp.fsm); #endif #if defined(CONFIG_NET_L2_PPP_PAP) } else if (fsm->protocol == PPP_PAP) { return CONTAINER_OF(fsm, struct ppp_context, pap.fsm); #endif } return NULL; } struct net_if *ppp_fsm_iface(struct ppp_fsm *fsm) { struct ppp_context *ctx = ppp_fsm_ctx(fsm); NET_ASSERT(ctx->iface); return ctx->iface; } static void fsm_send_configure_req(struct ppp_fsm *fsm, bool retransmit) { struct net_pkt *pkt = NULL; if (fsm->state != PPP_ACK_RECEIVED && fsm->state != PPP_ACK_SENT && fsm->state != PPP_REQUEST_SENT) { /* If we are not negotiating options, then reset them */ if (fsm->cb.config_info_reset) { fsm->cb.config_info_reset(fsm); } fsm->recv_nack_loops = 0; fsm->nack_loops = 0; } if (!retransmit) { fsm->retransmits = MAX_CONFIGURE_REQ; fsm->req_id = ++fsm->id; } fsm->ack_received = false; if (fsm->cb.config_info_add) { pkt = fsm->cb.config_info_add(fsm); } NET_DBG("[%s/%p] Sending %s (%d) id %d to peer while in %s (%d)", fsm->name, fsm, ppp_pkt_type2str(PPP_CONFIGURE_REQ), PPP_CONFIGURE_REQ, fsm->req_id, ppp_state_str(fsm->state), fsm->state); (void)ppp_send_pkt(fsm, NULL, PPP_CONFIGURE_REQ, fsm->req_id, pkt, pkt ? net_pkt_get_len(pkt) : 0); fsm->retransmits--; (void)k_work_reschedule(&fsm->timer, FSM_TIMEOUT); } static void ppp_fsm_timeout(struct k_work *work) { struct k_work_delayable *dwork = k_work_delayable_from_work(work); struct ppp_fsm *fsm = CONTAINER_OF(dwork, struct ppp_fsm, timer); NET_DBG("[%s/%p] Current state %s (%d)", fsm->name, fsm, ppp_state_str(fsm->state), fsm->state); switch (fsm->state) { case PPP_ACK_RECEIVED: case PPP_ACK_SENT: case PPP_REQUEST_SENT: if (fsm->retransmits <= 0) { NET_DBG("[%s/%p] %s retransmit limit %d reached", fsm->name, fsm, ppp_pkt_type2str(PPP_CONFIGURE_REQ), fsm->retransmits); ppp_change_state(fsm, PPP_STOPPED); if (fsm->cb.finished) { fsm->cb.finished(fsm); } } else { if (fsm->cb.retransmit) { fsm->cb.retransmit(fsm); } fsm_send_configure_req(fsm, true); if (fsm->state == PPP_ACK_RECEIVED) { ppp_change_state(fsm, PPP_REQUEST_SENT); } } break; case PPP_CLOSING: case PPP_STOPPING: if (fsm->retransmits <= 0) { ppp_change_state(fsm, fsm->state == PPP_CLOSING ? PPP_CLOSED : PPP_STOPPED); if (fsm->cb.finished) { fsm->cb.finished(fsm); } } else { fsm->req_id = ++fsm->id; ppp_send_pkt(fsm, NULL, PPP_TERMINATE_REQ, fsm->req_id, fsm->terminate_reason, strlen(fsm->terminate_reason)); fsm->retransmits--; (void)k_work_reschedule(&fsm->timer, FSM_TIMEOUT); } break; default: NET_DBG("[%s/%p] %s state %s (%d)", fsm->name, fsm, "Invalid", ppp_state_str(fsm->state), fsm->state); break; } } void ppp_fsm_init(struct ppp_fsm *fsm, uint16_t protocol) { fsm->protocol = protocol; fsm->state = PPP_INITIAL; fsm->flags = 0U; k_work_init_delayable(&fsm->timer, ppp_fsm_timeout); } static void fsm_down(struct ppp_fsm *fsm) { size_t i; for (i = 0; i < fsm->my_options.count; i++) { fsm->my_options.data[i].flags = 0; } if (fsm->cb.down) { fsm->cb.down(fsm); } } static void terminate(struct ppp_fsm *fsm, enum ppp_state next_state) { if (fsm->state != PPP_OPENED) { k_work_cancel_delayable(&fsm->timer); } else { fsm_down(fsm); } fsm->retransmits = MAX_TERMINATE_REQ; fsm->req_id = ++fsm->id; (void)ppp_send_pkt(fsm, NULL, PPP_TERMINATE_REQ, fsm->req_id, fsm->terminate_reason, strlen(fsm->terminate_reason)); if (fsm->retransmits == 0) { ppp_change_state(fsm, next_state); if (fsm->cb.finished) { fsm->cb.finished(fsm); } return; } (void)k_work_reschedule(&fsm->timer, FSM_TIMEOUT); fsm->retransmits--; ppp_change_state(fsm, next_state); } void ppp_fsm_close(struct ppp_fsm *fsm, const uint8_t *reason) { NET_DBG("[%s/%p] Current state %s (%d)", fsm->name, fsm, ppp_state_str(fsm->state), fsm->state); switch (fsm->state) { case PPP_ACK_RECEIVED: case PPP_ACK_SENT: case PPP_OPENED: case PPP_REQUEST_SENT: if (reason) { int limit_len = sizeof(fsm->terminate_reason) - 1; strncpy(fsm->terminate_reason, reason, limit_len); fsm->terminate_reason[limit_len] = '\0'; } terminate(fsm, PPP_CLOSING); break; case PPP_INITIAL: case PPP_STARTING: ppp_change_state(fsm, PPP_INITIAL); break; case PPP_STOPPED: ppp_change_state(fsm, PPP_CLOSED); break; case PPP_STOPPING: ppp_change_state(fsm, PPP_CLOSING); break; default: NET_DBG("[%s/%p] %s state %s (%d)", fsm->name, fsm, "Invalid", ppp_state_str(fsm->state), fsm->state); break; } } void ppp_fsm_lower_down(struct ppp_fsm *fsm) { NET_DBG("[%s/%p] Current state %s (%d)", fsm->name, fsm, ppp_state_str(fsm->state), fsm->state); switch (fsm->state) { case PPP_ACK_RECEIVED: case PPP_ACK_SENT: case PPP_REQUEST_SENT: case PPP_STOPPING: ppp_change_state(fsm, PPP_STARTING); k_work_cancel_delayable(&fsm->timer); break; case PPP_CLOSED: ppp_change_state(fsm, PPP_INITIAL); break; case PPP_CLOSING: ppp_change_state(fsm, PPP_INITIAL); k_work_cancel_delayable(&fsm->timer); break; case PPP_OPENED: ppp_change_state(fsm, PPP_STARTING); fsm_down(fsm); break; case PPP_STOPPED: ppp_change_state(fsm, PPP_STARTING); if (fsm->cb.starting) { fsm->cb.starting(fsm); } break; default: NET_DBG("[%s/%p] %s state %s (%d)", fsm->name, fsm, "Invalid", ppp_state_str(fsm->state), fsm->state); break; } } void ppp_fsm_lower_up(struct ppp_fsm *fsm) { NET_DBG("[%s/%p] Current state %s (%d)", fsm->name, fsm, ppp_state_str(fsm->state), fsm->state); switch (fsm->state) { case PPP_CLOSED: break; case PPP_INITIAL: ppp_change_state(fsm, PPP_CLOSED); break; case PPP_STARTING: fsm_send_configure_req(fsm, false); ppp_change_state(fsm, PPP_REQUEST_SENT); break; default: NET_DBG("[%s/%p] %s state %s (%d)", fsm->name, fsm, "Invalid", ppp_state_str(fsm->state), fsm->state); break; } } void ppp_fsm_open(struct ppp_fsm *fsm) { NET_DBG("[%s/%p] Current state %s (%d)", fsm->name, fsm, ppp_state_str(fsm->state), fsm->state); switch (fsm->state) { case PPP_CLOSED: ppp_change_state(fsm, PPP_REQUEST_SENT); fsm_send_configure_req(fsm, false); break; case PPP_CLOSING: ppp_change_state(fsm, PPP_STOPPING); if (fsm->flags & FSM_RESTART) { ppp_fsm_lower_down(fsm); ppp_fsm_lower_up(fsm); } break; case PPP_INITIAL: ppp_change_state(fsm, PPP_STARTING); if (fsm->cb.starting) { fsm->cb.starting(fsm); } break; case PPP_OPENED: case PPP_STOPPED: if (fsm->flags & FSM_RESTART) { ppp_fsm_lower_down(fsm); ppp_fsm_lower_up(fsm); } break; default: NET_DBG("[%s/%p] %s state %s (%d)", fsm->name, fsm, "Invalid", ppp_state_str(fsm->state), fsm->state); break; } } int ppp_send_pkt(struct ppp_fsm *fsm, struct net_if *iface, enum ppp_packet_type type, uint8_t id, void *data, uint32_t data_len) { /* Note that the data parameter is the received PPP packet if * we want to send PROTOCOL or CODE reject packet. */ struct net_pkt *req_pkt = data; uint16_t protocol = 0; size_t len = 0; struct ppp_packet ppp; struct net_pkt *pkt = NULL; int ret; if (!iface) { if (!fsm) { return -ENOENT; } iface = ppp_fsm_iface(fsm); } if (!net_if_is_carrier_ok(iface)) { return -ENETDOWN; } if (fsm) { protocol = fsm->protocol; } switch (type) { case PPP_CODE_REJ: { struct ppp_context *ctx = ppp_fsm_ctx(fsm); len = net_pkt_get_len(req_pkt); len = MIN(len, ctx->lcp.my_options.mru); break; } case PPP_CONFIGURE_ACK: case PPP_CONFIGURE_NACK: case PPP_CONFIGURE_REJ: case PPP_CONFIGURE_REQ: pkt = data; /* 2 + 1 + 1 (configure-[req|ack|nack|rej]) + * data_len (options) */ len = sizeof(ppp) + data_len; break; case PPP_DISCARD_REQ: break; case PPP_ECHO_REQ: len = sizeof(ppp) + sizeof(uint32_t) + data_len; break; case PPP_ECHO_REPLY: len = sizeof(ppp) + net_pkt_remaining_data(req_pkt); break; case PPP_PROTOCOL_REJ: len = sizeof(ppp) + sizeof(uint16_t) + net_pkt_remaining_data(req_pkt); protocol = PPP_LCP; break; case PPP_TERMINATE_REQ: case PPP_TERMINATE_ACK: len = sizeof(ppp); break; default: break; } if (len < sizeof(ppp)) { return -EINVAL; } ppp.code = type; ppp.id = id; ppp.length = htons(len); if (!pkt) { pkt = net_pkt_alloc_with_buffer(iface, sizeof(uint16_t) + len, AF_UNSPEC, 0, PPP_BUF_ALLOC_TIMEOUT); if (!pkt) { goto out_of_mem; } } else { struct net_buf *buf; buf = net_pkt_get_reserve_tx_data(sizeof(uint16_t) + len, PPP_BUF_ALLOC_TIMEOUT); if (!buf) { LOG_ERR("failed to allocate buffer"); goto out_of_mem; } net_pkt_frag_insert(pkt, buf); net_pkt_cursor_init(pkt); } ret = net_pkt_write_be16(pkt, protocol); if (ret < 0) { goto out_of_mem; } ret = net_pkt_write(pkt, &ppp, sizeof(ppp)); if (ret < 0) { goto out_of_mem; } if (type == PPP_CODE_REJ) { if (!req_pkt) { goto out_of_mem; } net_pkt_cursor_init(req_pkt); net_pkt_copy(pkt, req_pkt, len); } else if (type == PPP_ECHO_REQ) { struct ppp_context *ctx = CONTAINER_OF(fsm, struct ppp_context, lcp.fsm); if (ctx->lcp.magic) { ctx->lcp.magic = sys_rand32_get(); } ret = net_pkt_write_be32(pkt, ctx->lcp.magic); if (ret < 0) { goto out_of_mem; } data_len = MIN(data_len, ctx->lcp.my_options.mru); if (data_len > 0) { if (data_len == sizeof(uint32_t)) { ret = net_pkt_write_be32(pkt, POINTER_TO_UINT(data)); } else { ret = net_pkt_write(pkt, data, data_len); } if (ret < 0) { goto out_of_mem; } } } else if (type == PPP_ECHO_REPLY) { net_pkt_copy(pkt, req_pkt, len); } else if (type == PPP_PROTOCOL_REJ) { net_pkt_cursor_init(req_pkt); net_pkt_copy(pkt, req_pkt, len); } NET_DBG("[%s/%p] Sending %zd bytes pkt %p (options len %d)", fsm ? fsm->name : "?", fsm, net_pkt_get_len(pkt), pkt, data_len); net_pkt_set_ppp(pkt, true); if (fsm) { /* Do not call net_send_data() directly in order to make this * thread run before the sending happens. If we call the * net_send_data() from this thread, then in fast link (like * when running inside QEMU) the reply might arrive before we * have returned from this function. That is bad because the * fsm would be in wrong state and the received pkt is dropped. */ ppp_queue_pkt(pkt); } else { ret = net_send_data(pkt); if (ret < 0) { net_pkt_unref(pkt); } } return 0; out_of_mem: if (pkt) { net_pkt_unref(pkt); } return -ENOMEM; } static enum net_verdict fsm_recv_configure_req(struct ppp_fsm *fsm, uint8_t id, struct net_pkt *pkt, uint16_t remaining_len) { struct net_pkt *out = NULL; int len = 0; enum ppp_packet_type code; NET_DBG("[%s/%p] Current state %s (%d)", fsm->name, fsm, ppp_state_str(fsm->state), fsm->state); switch (fsm->state) { case PPP_ACK_SENT: case PPP_ACK_RECEIVED: break; case PPP_CLOSED: (void)ppp_send_pkt(fsm, net_pkt_iface(pkt), PPP_TERMINATE_ACK, id, NULL, 0); return NET_OK; case PPP_CLOSING: case PPP_STOPPING: return NET_OK; case PPP_OPENED: fsm_down(fsm); fsm_send_configure_req(fsm, false); ppp_change_state(fsm, PPP_REQUEST_SENT); break; case PPP_REQUEST_SENT: /* Received request while waiting ACK */ break; case PPP_STOPPED: fsm_send_configure_req(fsm, false); ppp_change_state(fsm, PPP_REQUEST_SENT); break; default: NET_DBG("[%s/%p] %s state %s (%d)", fsm->name, fsm, "Invalid", ppp_state_str(fsm->state), fsm->state); return NET_DROP; } out = net_pkt_alloc_with_buffer(net_pkt_iface(pkt), sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint8_t) + sizeof(uint8_t) + remaining_len, AF_UNSPEC, 0, PPP_BUF_ALLOC_TIMEOUT); if (!out) { return NET_DROP; } net_pkt_cursor_init(out); if (fsm->cb.config_info_req) { int ret; ret = fsm->cb.config_info_req(fsm, pkt, remaining_len, out); if (ret < 0) { goto unref_out_pkt; } if (fsm->nack_loops >= MAX_NACK_LOOPS && ret == PPP_CONFIGURE_NACK) { ret = PPP_CONFIGURE_REJ; } code = ret; len = net_pkt_get_len(out); } else if (remaining_len) { code = PPP_CONFIGURE_REJ; net_pkt_copy(out, pkt, remaining_len); len = remaining_len; } else { code = PPP_CONFIGURE_ACK; } NET_DBG("[%s/%p] Sending %s (%d) id %d to peer while in %s (%d)", fsm->name, fsm, ppp_pkt_type2str(code), code, id, ppp_state_str(fsm->state), fsm->state); (void)ppp_send_pkt(fsm, NULL, code, id, out, len); if (code == PPP_CONFIGURE_ACK) { if (fsm->state == PPP_ACK_RECEIVED) { k_work_cancel_delayable(&fsm->timer); ppp_change_state(fsm, PPP_OPENED); if (fsm->cb.up) { fsm->cb.up(fsm); } } else { ppp_change_state(fsm, PPP_ACK_SENT); } fsm->nack_loops = 0; } else { if (fsm->state != PPP_ACK_RECEIVED) { ppp_change_state(fsm, PPP_REQUEST_SENT); } if (code == PPP_CONFIGURE_NACK) { fsm->nack_loops++; } } return NET_OK; unref_out_pkt: net_pkt_unref(out); return NET_DROP; } static enum net_verdict fsm_recv_configure_ack(struct ppp_fsm *fsm, uint8_t id, struct net_pkt *pkt, uint16_t remaining_len) { NET_DBG("[%s/%p] Current state %s (%d)", fsm->name, fsm, ppp_state_str(fsm->state), fsm->state); if (id != fsm->req_id || fsm->ack_received) { return NET_DROP; } if (fsm->cb.config_info_ack) { if (fsm->cb.config_info_ack(fsm, pkt, remaining_len) < 0) { NET_DBG("[%s/%p] %s %s received", fsm->name, fsm, "Invalid", ppp_pkt_type2str(PPP_CONFIGURE_ACK)); return NET_DROP; } } fsm->ack_received = true; fsm->recv_nack_loops = 0; switch (fsm->state) { case PPP_ACK_RECEIVED: k_work_cancel_delayable(&fsm->timer); fsm_send_configure_req(fsm, false); ppp_change_state(fsm, PPP_REQUEST_SENT); break; case PPP_ACK_SENT: k_work_cancel_delayable(&fsm->timer); ppp_change_state(fsm, PPP_OPENED); fsm->retransmits = MAX_CONFIGURE_REQ; if (fsm->cb.up) { fsm->cb.up(fsm); } break; case PPP_CLOSED: case PPP_STOPPED: (void)ppp_send_pkt(fsm, net_pkt_iface(pkt), PPP_TERMINATE_ACK, id, NULL, 0); break; case PPP_OPENED: fsm_down(fsm); fsm_send_configure_req(fsm, false); ppp_change_state(fsm, PPP_REQUEST_SENT); break; case PPP_REQUEST_SENT: ppp_change_state(fsm, PPP_ACK_RECEIVED); fsm->retransmits = MAX_CONFIGURE_REQ; break; default: NET_DBG("[%s/%p] %s state %s (%d)", fsm->name, fsm, "Invalid", ppp_state_str(fsm->state), fsm->state); return NET_DROP; } return NET_OK; } static enum net_verdict fsm_recv_configure_nack_rej(struct ppp_fsm *fsm, enum ppp_packet_type code, uint8_t id, struct net_pkt *pkt, uint16_t length) { bool ret = false; NET_DBG("[%s/%p] Current state %s (%d)", fsm->name, fsm, ppp_state_str(fsm->state), fsm->state); if (id != fsm->req_id || fsm->ack_received) { return NET_DROP; } if (code == PPP_CONFIGURE_NACK) { bool rejected = false; fsm->recv_nack_loops++; if (fsm->recv_nack_loops >= MAX_NACK_LOOPS) { rejected = true; } if (fsm->cb.config_info_nack) { int err; err = fsm->cb.config_info_nack(fsm, pkt, length, rejected); if (err < 0) { NET_DBG("[%s/%p] %s failed (%d)", fsm->name, fsm, "Nack", err); } else { ret = true; } } if (!ret) { NET_DBG("[%s/%p] %s %s (id %d)", fsm->name, fsm, "Invalid", ppp_pkt_type2str(code), id); return NET_DROP; } } else { fsm->recv_nack_loops = 0; if (fsm->cb.config_info_rej) { int err; err = fsm->cb.config_info_rej(fsm, pkt, length); if (err < 0) { NET_DBG("[%s/%p] %s failed (%d)", fsm->name, fsm, "Reject", err); } else { ret = true; } } if (!ret) { NET_DBG("[%s/%p] %s %s (id %d)", fsm->name, fsm, "Invalid", ppp_pkt_type2str(code), id); return NET_DROP; } } fsm->ack_received = true; switch (fsm->state) { case PPP_ACK_RECEIVED: k_work_cancel_delayable(&fsm->timer); fsm_send_configure_req(fsm, false); ppp_change_state(fsm, PPP_REQUEST_SENT); break; case PPP_ACK_SENT: case PPP_REQUEST_SENT: k_work_cancel_delayable(&fsm->timer); fsm_send_configure_req(fsm, false); break; case PPP_CLOSED: case PPP_STOPPED: (void)ppp_send_pkt(fsm, net_pkt_iface(pkt), PPP_TERMINATE_ACK, id, NULL, 0); break; case PPP_OPENED: fsm_down(fsm); fsm_send_configure_req(fsm, false); ppp_change_state(fsm, PPP_REQUEST_SENT); break; default: NET_DBG("[%s/%p] %s state %s (%d)", fsm->name, fsm, "Invalid", ppp_state_str(fsm->state), fsm->state); return NET_DROP; } return NET_OK; } static enum net_verdict fsm_recv_terminate_req(struct ppp_fsm *fsm, uint8_t id, struct net_pkt *pkt, uint16_t length) { NET_DBG("[%s/%p] Current state %s (%d)", fsm->name, fsm, ppp_state_str(fsm->state), fsm->state); switch (fsm->state) { case PPP_ACK_RECEIVED: case PPP_ACK_SENT: ppp_change_state(fsm, PPP_REQUEST_SENT); break; case PPP_OPENED: if (length > 0) { net_pkt_read(pkt, fsm->terminate_reason, MIN(length, sizeof(fsm->terminate_reason) - 1)); NET_DBG("[%s/%p] %s (%s)", fsm->name, fsm, "Terminated by peer", fsm->terminate_reason); } else { NET_DBG("[%s/%p] Terminated by peer", fsm->name, fsm); } fsm->retransmits = 0; ppp_change_state(fsm, PPP_STOPPING); fsm_down(fsm); (void)k_work_reschedule(&fsm->timer, FSM_TIMEOUT); break; default: NET_DBG("[%s/%p] %s state %s (%d)", fsm->name, fsm, "Invalid", ppp_state_str(fsm->state), fsm->state); return NET_DROP; } (void)ppp_send_pkt(fsm, net_pkt_iface(pkt), PPP_TERMINATE_ACK, id, NULL, 0); return NET_OK; } static enum net_verdict fsm_recv_terminate_ack(struct ppp_fsm *fsm, uint8_t id, struct net_pkt *pkt, uint16_t length) { enum ppp_state new_state; NET_DBG("[%s/%p] Current state %s (%d)", fsm->name, fsm, ppp_state_str(fsm->state), fsm->state); switch (fsm->state) { case PPP_CLOSING: new_state = PPP_CLOSED; goto stopped; case PPP_OPENED: fsm_down(fsm); fsm_send_configure_req(fsm, false); ppp_change_state(fsm, PPP_REQUEST_SENT); break; case PPP_STOPPING: new_state = PPP_STOPPED; goto stopped; case PPP_ACK_RECEIVED: ppp_change_state(fsm, PPP_REQUEST_SENT); break; default: NET_DBG("[%s/%p] %s state %s (%d)", fsm->name, fsm, "Invalid", ppp_state_str(fsm->state), fsm->state); return NET_DROP; } return NET_OK; stopped: k_work_cancel_delayable(&fsm->timer); ppp_change_state(fsm, new_state); if (fsm->cb.finished) { fsm->cb.finished(fsm); } return NET_OK; } static enum net_verdict fsm_recv_code_rej(struct ppp_fsm *fsm, struct net_pkt *pkt) { uint8_t code, id; int ret; NET_DBG("[%s/%p] Current state %s (%d)", fsm->name, fsm, ppp_state_str(fsm->state), fsm->state); ret = net_pkt_read_u8(pkt, &code); if (ret < 0) { return NET_DROP; } ret = net_pkt_read_u8(pkt, &id); if (ret < 0) { return NET_DROP; } NET_DBG("[%s/%p] Received Code-Rej code %d id %d", fsm->name, fsm, code, id); if (fsm->state == PPP_ACK_RECEIVED) { ppp_change_state(fsm, PPP_REQUEST_SENT); } return NET_OK; } void ppp_fsm_proto_reject(struct ppp_fsm *fsm) { NET_DBG("[%s/%p] Current state %s (%d)", fsm->name, fsm, ppp_state_str(fsm->state), fsm->state); switch (fsm->state) { case PPP_ACK_RECEIVED: case PPP_ACK_SENT: case PPP_STOPPING: case PPP_REQUEST_SENT: k_work_cancel_delayable(&fsm->timer); ppp_change_state(fsm, PPP_STOPPED); if (fsm->cb.finished) { fsm->cb.finished(fsm); } break; case PPP_CLOSED: ppp_change_state(fsm, PPP_CLOSED); if (fsm->cb.finished) { fsm->cb.finished(fsm); } break; case PPP_CLOSING: k_work_cancel_delayable(&fsm->timer); ppp_change_state(fsm, PPP_CLOSED); if (fsm->cb.finished) { fsm->cb.finished(fsm); } break; case PPP_OPENED: terminate(fsm, PPP_STOPPING); break; case PPP_STOPPED: ppp_change_state(fsm, PPP_STOPPED); if (fsm->cb.finished) { fsm->cb.finished(fsm); } break; default: NET_DBG("[%s/%p] %s state %s (%d)", fsm->name, fsm, "Invalid", ppp_state_str(fsm->state), fsm->state); break; } } enum net_verdict ppp_fsm_input(struct ppp_fsm *fsm, uint16_t proto, struct net_pkt *pkt) { uint8_t code, id; uint16_t length; int ret; struct ppp_context *ctx = ppp_fsm_ctx(fsm); ret = net_pkt_read_u8(pkt, &code); if (ret < 0) { NET_DBG("[%s/%p] Cannot read %s (pkt len %zd)", fsm->name, fsm, "code", net_pkt_get_len(pkt)); return NET_DROP; } ret = net_pkt_read_u8(pkt, &id); if (ret < 0) { NET_DBG("[%s/%p] Cannot read %s (pkt len %zd)", fsm->name, fsm, "id", net_pkt_get_len(pkt)); return NET_DROP; } ret = net_pkt_read_be16(pkt, &length); if (ret < 0) { NET_DBG("[%s/%p] Cannot read %s (pkt len %zd)", fsm->name, fsm, "length", net_pkt_get_len(pkt)); return NET_DROP; } if (length > ctx->lcp.my_options.mru) { NET_DBG("[%s/%p] Too long msg %d", fsm->name, fsm, length); return NET_DROP; } if (fsm->state == PPP_INITIAL || fsm->state == PPP_STARTING) { NET_DBG("[%s/%p] Received %s packet in wrong state %s (%d)", fsm->name, fsm, ppp_proto2str(proto), ppp_state_str(fsm->state), fsm->state); return NET_DROP; } /* Length will only contain payload/data length */ length -= sizeof(code) + sizeof(id) + sizeof(length); NET_DBG("[%s/%p] %s %s (%d) id %d payload len %d", fsm->name, fsm, ppp_proto2str(proto), ppp_pkt_type2str(code), code, id, length); switch (code) { case PPP_CODE_REJ: return fsm_recv_code_rej(fsm, pkt); case PPP_CONFIGURE_ACK: return fsm_recv_configure_ack(fsm, id, pkt, length); case PPP_CONFIGURE_NACK: return fsm_recv_configure_nack_rej(fsm, code, id, pkt, length); case PPP_CONFIGURE_REQ: return fsm_recv_configure_req(fsm, id, pkt, length); case PPP_CONFIGURE_REJ: return fsm_recv_configure_nack_rej(fsm, code, id, pkt, length); case PPP_TERMINATE_ACK: return fsm_recv_terminate_ack(fsm, id, pkt, length); case PPP_TERMINATE_REQ: return fsm_recv_terminate_req(fsm, id, pkt, length); default: if (fsm->cb.proto_extension) { enum net_verdict verdict; verdict = fsm->cb.proto_extension(fsm, code, id, pkt); if (verdict != NET_DROP) { return verdict; } } (void)ppp_send_pkt(fsm, net_pkt_iface(pkt), PPP_CODE_REJ, id, pkt, 0); } return NET_DROP; } enum net_verdict ppp_fsm_recv_protocol_rej(struct ppp_fsm *fsm, uint8_t id, struct net_pkt *pkt) { NET_DBG("[%s/%p] Current state %s (%d)", fsm->name, fsm, ppp_state_str(fsm->state), fsm->state); return NET_DROP; } enum net_verdict ppp_fsm_recv_echo_req(struct ppp_fsm *fsm, uint8_t id, struct net_pkt *pkt) { NET_DBG("[%s/%p] Current state %s (%d)", fsm->name, fsm, ppp_state_str(fsm->state), fsm->state); (void)ppp_send_pkt(fsm, net_pkt_iface(pkt), PPP_ECHO_REPLY, id, pkt, 0); return NET_OK; } enum net_verdict ppp_fsm_recv_echo_reply(struct ppp_fsm *fsm, uint8_t id, struct net_pkt *pkt) { NET_DBG("[%s/%p] Current state %s (%d)", fsm->name, fsm, ppp_state_str(fsm->state), fsm->state); #if defined(CONFIG_NET_SHELL) struct ppp_context *ctx = CONTAINER_OF(fsm, struct ppp_context, lcp.fsm); if (ctx->shell.echo_reply.cb) { ctx->shell.echo_reply.cb(ctx->shell.echo_reply.user_data, ctx->shell.echo_reply.user_data_len); } #endif /* CONFIG_NET_SHELL */ return NET_OK; } enum net_verdict ppp_fsm_recv_discard_req(struct ppp_fsm *fsm, uint8_t id, struct net_pkt *pkt) { NET_DBG("[%s/%p] Current state %s (%d)", fsm->name, fsm, ppp_state_str(fsm->state), fsm->state); return NET_OK; } void ppp_send_proto_rej(struct net_if *iface, struct net_pkt *pkt, uint16_t protocol) { uint8_t code, id; int ret; ret = net_pkt_read_u8(pkt, &code); if (ret < 0) { goto quit; } ret = net_pkt_read_u8(pkt, &id); if (ret < 0) { goto quit; } (void)ppp_send_pkt(NULL, iface, PPP_PROTOCOL_REJ, id, pkt, 0); quit: return; }