384 lines
8.4 KiB
C
384 lines
8.4 KiB
C
/*
|
|
* 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_pkt.h>
|
|
|
|
#include <zephyr/net/ppp.h>
|
|
|
|
#include "net_private.h"
|
|
|
|
#include "ppp_internal.h"
|
|
|
|
#define ALLOC_TIMEOUT K_MSEC(100)
|
|
|
|
int ppp_parse_options(struct ppp_fsm *fsm, struct net_pkt *pkt,
|
|
uint16_t length,
|
|
int (*parse)(struct net_pkt *pkt, uint8_t code,
|
|
uint8_t len, void *user_data),
|
|
void *user_data)
|
|
{
|
|
int remaining = length, pkt_remaining;
|
|
uint8_t opt_type, opt_len, opt_val_len;
|
|
int ret;
|
|
struct net_pkt_cursor cursor;
|
|
|
|
pkt_remaining = net_pkt_remaining_data(pkt);
|
|
if (remaining != pkt_remaining) {
|
|
NET_DBG("Expecting %d but pkt data length is %d bytes",
|
|
remaining, pkt_remaining);
|
|
return -EMSGSIZE;
|
|
}
|
|
|
|
while (remaining > 0) {
|
|
ret = net_pkt_read_u8(pkt, &opt_type);
|
|
if (ret < 0) {
|
|
NET_DBG("Cannot read %s (%d) (remaining len %d)",
|
|
"opt_type", ret, pkt_remaining);
|
|
return -EBADMSG;
|
|
}
|
|
|
|
ret = net_pkt_read_u8(pkt, &opt_len);
|
|
if (ret < 0) {
|
|
NET_DBG("Cannot read %s (%d) (remaining len %d)",
|
|
"opt_len", ret, remaining);
|
|
return -EBADMSG;
|
|
}
|
|
|
|
opt_val_len = opt_len - sizeof(opt_type) - sizeof(opt_len);
|
|
|
|
net_pkt_cursor_backup(pkt, &cursor);
|
|
|
|
NET_DBG("[%s/%p] option %s (%d) len %d", fsm->name, fsm,
|
|
ppp_option2str(fsm->protocol, opt_type), opt_type,
|
|
opt_len);
|
|
|
|
ret = parse(pkt, opt_type, opt_val_len, user_data);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
net_pkt_cursor_restore(pkt, &cursor);
|
|
|
|
net_pkt_skip(pkt, opt_val_len);
|
|
remaining -= opt_len;
|
|
}
|
|
|
|
if (remaining < 0) {
|
|
return -EBADMSG;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct ppp_peer_option_info *
|
|
ppp_peer_option_info_get(const struct ppp_peer_option_info *options,
|
|
size_t num_options,
|
|
uint8_t code)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < num_options; i++) {
|
|
if (options[i].code == code) {
|
|
return &options[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct ppp_parse_option_conf_req_data {
|
|
struct ppp_fsm *fsm;
|
|
struct net_pkt *ret_pkt;
|
|
enum ppp_protocol_type protocol;
|
|
const struct ppp_peer_option_info *options_info;
|
|
size_t num_options_info;
|
|
void *user_data;
|
|
|
|
int nack_count;
|
|
int rej_count;
|
|
};
|
|
|
|
static int ppp_parse_option_conf_req_unsupported(struct net_pkt *pkt,
|
|
uint8_t code, uint8_t len,
|
|
void *user_data)
|
|
{
|
|
struct ppp_parse_option_conf_req_data *parse_data = user_data;
|
|
struct ppp_fsm *fsm = parse_data->fsm;
|
|
struct net_pkt *ret_pkt = parse_data->ret_pkt;
|
|
const struct ppp_peer_option_info *option_info =
|
|
ppp_peer_option_info_get(parse_data->options_info,
|
|
parse_data->num_options_info,
|
|
code);
|
|
|
|
NET_DBG("[%s/%p] %s option %s (%d) len %d",
|
|
fsm->name, fsm, "Check",
|
|
ppp_option2str(parse_data->protocol, code),
|
|
code, len);
|
|
|
|
if (option_info) {
|
|
return 0;
|
|
}
|
|
|
|
parse_data->rej_count++;
|
|
|
|
net_pkt_write_u8(ret_pkt, code);
|
|
net_pkt_write_u8(ret_pkt, len + sizeof(code) + sizeof(len));
|
|
|
|
if (len > 0) {
|
|
net_pkt_copy(ret_pkt, pkt, len);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ppp_parse_option_conf_req_supported(struct net_pkt *pkt,
|
|
uint8_t code, uint8_t len,
|
|
void *user_data)
|
|
{
|
|
struct ppp_parse_option_conf_req_data *parse_data = user_data;
|
|
struct ppp_fsm *fsm = parse_data->fsm;
|
|
const struct ppp_peer_option_info *option_info =
|
|
ppp_peer_option_info_get(parse_data->options_info,
|
|
parse_data->num_options_info,
|
|
code);
|
|
int ret;
|
|
|
|
ret = option_info->parse(fsm, pkt, parse_data->user_data);
|
|
if (ret == -EINVAL) {
|
|
parse_data->nack_count++;
|
|
ret = option_info->nack(fsm, parse_data->ret_pkt,
|
|
parse_data->user_data);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ppp_config_info_req(struct ppp_fsm *fsm,
|
|
struct net_pkt *pkt,
|
|
uint16_t length,
|
|
struct net_pkt *ret_pkt,
|
|
enum ppp_protocol_type protocol,
|
|
const struct ppp_peer_option_info *options_info,
|
|
size_t num_options_info,
|
|
void *user_data)
|
|
{
|
|
struct ppp_parse_option_conf_req_data parse_data = {
|
|
.fsm = fsm,
|
|
.ret_pkt = ret_pkt,
|
|
.protocol = protocol,
|
|
.options_info = options_info,
|
|
.num_options_info = num_options_info,
|
|
.user_data = user_data,
|
|
};
|
|
struct net_pkt_cursor cursor;
|
|
int ret;
|
|
|
|
net_pkt_cursor_backup(pkt, &cursor);
|
|
|
|
ret = ppp_parse_options(fsm, pkt, length,
|
|
ppp_parse_option_conf_req_unsupported,
|
|
&parse_data);
|
|
if (ret < 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (parse_data.rej_count) {
|
|
return PPP_CONFIGURE_REJ;
|
|
}
|
|
|
|
net_pkt_cursor_restore(pkt, &cursor);
|
|
|
|
ret = ppp_parse_options(fsm, pkt, length,
|
|
ppp_parse_option_conf_req_supported,
|
|
&parse_data);
|
|
if (ret < 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (parse_data.nack_count) {
|
|
return PPP_CONFIGURE_NACK;
|
|
}
|
|
|
|
net_pkt_cursor_restore(pkt, &cursor);
|
|
net_pkt_copy(ret_pkt, pkt, length);
|
|
|
|
return PPP_CONFIGURE_ACK;
|
|
}
|
|
|
|
struct net_pkt *ppp_my_options_add(struct ppp_fsm *fsm, size_t packet_len)
|
|
{
|
|
struct ppp_context *ctx = ppp_fsm_ctx(fsm);
|
|
const struct ppp_my_option_info *info;
|
|
struct ppp_my_option_data *data;
|
|
struct net_pkt *pkt;
|
|
size_t i;
|
|
int err;
|
|
|
|
pkt = net_pkt_alloc_with_buffer(ppp_fsm_iface(fsm), packet_len,
|
|
AF_UNSPEC, 0, PPP_BUF_ALLOC_TIMEOUT);
|
|
if (!pkt) {
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < fsm->my_options.count; i++) {
|
|
info = &fsm->my_options.info[i];
|
|
data = &fsm->my_options.data[i];
|
|
|
|
if (data->flags & PPP_MY_OPTION_REJECTED) {
|
|
continue;
|
|
}
|
|
|
|
err = net_pkt_write_u8(pkt, info->code);
|
|
if (err) {
|
|
goto unref_pkt;
|
|
}
|
|
|
|
err = info->conf_req_add(ctx, pkt);
|
|
if (err) {
|
|
goto unref_pkt;
|
|
}
|
|
}
|
|
|
|
return pkt;
|
|
|
|
unref_pkt:
|
|
net_pkt_unref(pkt);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
typedef int (*ppp_my_option_handle_t)(struct ppp_context *ctx,
|
|
struct net_pkt *pkt, uint8_t len,
|
|
const struct ppp_my_option_info *info,
|
|
struct ppp_my_option_data *data);
|
|
|
|
struct ppp_my_option_handle_data {
|
|
struct ppp_fsm *fsm;
|
|
ppp_my_option_handle_t handle;
|
|
};
|
|
|
|
static int ppp_my_option_get(struct ppp_fsm *fsm, uint8_t code,
|
|
const struct ppp_my_option_info **info,
|
|
struct ppp_my_option_data **data)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < fsm->my_options.count; i++) {
|
|
if (fsm->my_options.info[i].code == code) {
|
|
*info = &fsm->my_options.info[i];
|
|
*data = &fsm->my_options.data[i];
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -ENOENT;
|
|
}
|
|
|
|
static int ppp_my_option_parse(struct net_pkt *pkt, uint8_t code,
|
|
uint8_t len, void *user_data)
|
|
{
|
|
struct ppp_my_option_handle_data *d = user_data;
|
|
struct ppp_context *ctx = ppp_fsm_ctx(d->fsm);
|
|
const struct ppp_my_option_info *info;
|
|
struct ppp_my_option_data *data;
|
|
int ret;
|
|
|
|
ret = ppp_my_option_get(d->fsm, code, &info, &data);
|
|
if (ret < 0) {
|
|
return 0;
|
|
}
|
|
|
|
return d->handle(ctx, pkt, len, info, data);
|
|
}
|
|
|
|
static int ppp_my_options_parse(struct ppp_fsm *fsm,
|
|
struct net_pkt *pkt,
|
|
uint16_t length,
|
|
ppp_my_option_handle_t handle)
|
|
{
|
|
struct ppp_my_option_handle_data parse_data = {
|
|
.fsm = fsm,
|
|
.handle = handle,
|
|
};
|
|
|
|
return ppp_parse_options(fsm, pkt, length, ppp_my_option_parse,
|
|
&parse_data);
|
|
}
|
|
|
|
static int ppp_my_option_parse_conf_ack(struct ppp_context *ctx,
|
|
struct net_pkt *pkt, uint8_t len,
|
|
const struct ppp_my_option_info *info,
|
|
struct ppp_my_option_data *data)
|
|
{
|
|
data->flags |= PPP_MY_OPTION_ACKED;
|
|
|
|
if (info->conf_ack_handle) {
|
|
return info->conf_ack_handle(ctx, pkt, len);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ppp_my_options_parse_conf_ack(struct ppp_fsm *fsm,
|
|
struct net_pkt *pkt,
|
|
uint16_t length)
|
|
{
|
|
return ppp_my_options_parse(fsm, pkt, length,
|
|
ppp_my_option_parse_conf_ack);
|
|
}
|
|
|
|
static int ppp_my_option_parse_conf_nak(struct ppp_context *ctx,
|
|
struct net_pkt *pkt, uint8_t len,
|
|
const struct ppp_my_option_info *info,
|
|
struct ppp_my_option_data *data)
|
|
{
|
|
return info->conf_nak_handle(ctx, pkt, len);
|
|
}
|
|
|
|
int ppp_my_options_parse_conf_nak(struct ppp_fsm *fsm,
|
|
struct net_pkt *pkt,
|
|
uint16_t length)
|
|
{
|
|
return ppp_my_options_parse(fsm, pkt, length,
|
|
ppp_my_option_parse_conf_nak);
|
|
}
|
|
|
|
static int ppp_my_option_parse_conf_rej(struct ppp_context *ctx,
|
|
struct net_pkt *pkt, uint8_t len,
|
|
const struct ppp_my_option_info *info,
|
|
struct ppp_my_option_data *data)
|
|
{
|
|
data->flags |= PPP_MY_OPTION_REJECTED;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ppp_my_options_parse_conf_rej(struct ppp_fsm *fsm,
|
|
struct net_pkt *pkt,
|
|
uint16_t length)
|
|
{
|
|
return ppp_my_options_parse(fsm, pkt, length,
|
|
ppp_my_option_parse_conf_rej);
|
|
}
|
|
|
|
uint32_t ppp_my_option_flags(struct ppp_fsm *fsm, uint8_t code)
|
|
{
|
|
const struct ppp_my_option_info *info;
|
|
struct ppp_my_option_data *data;
|
|
int ret;
|
|
|
|
ret = ppp_my_option_get(fsm, code, &info, &data);
|
|
if (ret) {
|
|
return false;
|
|
}
|
|
|
|
return data->flags;
|
|
}
|