041f0e5379
Logging v1 has been removed and log_strdup wrapper function is no longer needed. Removing the function and its use in the tree. Signed-off-by: Krzysztof Chruscinski <krzysztof.chruscinski@nordicsemi.no>
499 lines
12 KiB
C
499 lines
12 KiB
C
/*
|
|
* Copyright (c) 2019 Intel Corporation.
|
|
* Copyright (c) 2020 Endian Technologies AB
|
|
*
|
|
* 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 <zephyr/net/dns_resolve.h>
|
|
|
|
#include "net_private.h"
|
|
|
|
#include "ppp_internal.h"
|
|
|
|
static enum net_verdict ipcp_handle(struct ppp_context *ctx,
|
|
struct net_if *iface,
|
|
struct net_pkt *pkt)
|
|
{
|
|
return ppp_fsm_input(&ctx->ipcp.fsm, PPP_IPCP, pkt);
|
|
}
|
|
|
|
/* Length is (6): code + id + IPv4 address length. RFC 1332 and also
|
|
* DNS in RFC 1877.
|
|
*/
|
|
#define IP_ADDRESS_OPTION_LEN (1 + 1 + 4)
|
|
|
|
static int ipcp_add_address(struct ppp_context *ctx, struct net_pkt *pkt,
|
|
struct in_addr *addr)
|
|
{
|
|
net_pkt_write_u8(pkt, 1 + 1 + sizeof(addr->s_addr));
|
|
return net_pkt_write(pkt, &addr->s_addr, sizeof(addr->s_addr));
|
|
}
|
|
|
|
static int ipcp_add_ip_address(struct ppp_context *ctx, struct net_pkt *pkt)
|
|
{
|
|
return ipcp_add_address(ctx, pkt, &ctx->ipcp.my_options.address);
|
|
}
|
|
|
|
static int ipcp_add_dns1(struct ppp_context *ctx, struct net_pkt *pkt)
|
|
{
|
|
return ipcp_add_address(ctx, pkt, &ctx->ipcp.my_options.dns1_address);
|
|
}
|
|
|
|
static int ipcp_add_dns2(struct ppp_context *ctx, struct net_pkt *pkt)
|
|
{
|
|
return ipcp_add_address(ctx, pkt, &ctx->ipcp.my_options.dns2_address);
|
|
}
|
|
|
|
static int ipcp_ack_check_address(struct net_pkt *pkt, size_t oplen,
|
|
struct in_addr *addr)
|
|
{
|
|
struct in_addr ack_addr;
|
|
int ret;
|
|
|
|
if (oplen != sizeof(ack_addr)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = net_pkt_read(pkt, &ack_addr, sizeof(ack_addr));
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
if (memcmp(&ack_addr, addr, sizeof(ack_addr)) != 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ipcp_ack_ip_address(struct ppp_context *ctx, struct net_pkt *pkt,
|
|
uint8_t oplen)
|
|
{
|
|
return ipcp_ack_check_address(pkt, oplen,
|
|
&ctx->ipcp.my_options.address);
|
|
}
|
|
|
|
static int ipcp_ack_dns1(struct ppp_context *ctx, struct net_pkt *pkt,
|
|
uint8_t oplen)
|
|
{
|
|
return ipcp_ack_check_address(pkt, oplen,
|
|
&ctx->ipcp.my_options.dns1_address);
|
|
}
|
|
|
|
static int ipcp_ack_dns2(struct ppp_context *ctx, struct net_pkt *pkt,
|
|
uint8_t oplen)
|
|
{
|
|
return ipcp_ack_check_address(pkt, oplen,
|
|
&ctx->ipcp.my_options.dns2_address);
|
|
}
|
|
|
|
static int ipcp_nak_override_address(struct net_pkt *pkt, size_t oplen,
|
|
struct in_addr *addr)
|
|
{
|
|
if (oplen != sizeof(*addr)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return net_pkt_read(pkt, addr, sizeof(*addr));
|
|
}
|
|
|
|
static int ipcp_nak_ip_address(struct ppp_context *ctx, struct net_pkt *pkt,
|
|
uint8_t oplen)
|
|
{
|
|
return ipcp_nak_override_address(pkt, oplen,
|
|
&ctx->ipcp.my_options.address);
|
|
}
|
|
|
|
static int ipcp_nak_dns1(struct ppp_context *ctx, struct net_pkt *pkt,
|
|
uint8_t oplen)
|
|
{
|
|
return ipcp_nak_override_address(pkt, oplen,
|
|
&ctx->ipcp.my_options.dns1_address);
|
|
}
|
|
|
|
static int ipcp_nak_dns2(struct ppp_context *ctx, struct net_pkt *pkt,
|
|
uint8_t oplen)
|
|
{
|
|
return ipcp_nak_override_address(pkt, oplen,
|
|
&ctx->ipcp.my_options.dns2_address);
|
|
}
|
|
|
|
static const struct ppp_my_option_info ipcp_my_options[] = {
|
|
PPP_MY_OPTION(IPCP_OPTION_IP_ADDRESS, ipcp_add_ip_address,
|
|
ipcp_ack_ip_address, ipcp_nak_ip_address),
|
|
PPP_MY_OPTION(IPCP_OPTION_DNS1, ipcp_add_dns1,
|
|
ipcp_ack_dns1, ipcp_nak_dns1),
|
|
PPP_MY_OPTION(IPCP_OPTION_DNS2, ipcp_add_dns2,
|
|
ipcp_ack_dns2, ipcp_nak_dns2),
|
|
};
|
|
|
|
BUILD_ASSERT(ARRAY_SIZE(ipcp_my_options) == IPCP_NUM_MY_OPTIONS);
|
|
|
|
static struct net_pkt *ipcp_config_info_add(struct ppp_fsm *fsm)
|
|
{
|
|
return ppp_my_options_add(fsm, 3 * IP_ADDRESS_OPTION_LEN);
|
|
}
|
|
|
|
struct ipcp_peer_option_data {
|
|
bool addr_present;
|
|
struct in_addr addr;
|
|
};
|
|
|
|
#if defined(CONFIG_NET_L2_PPP_OPTION_SERVE_DNS)
|
|
static int ipcp_dns_address_parse(struct ppp_fsm *fsm, struct net_pkt *pkt,
|
|
void *user_data)
|
|
{
|
|
struct ipcp_peer_option_data *data = user_data;
|
|
int ret;
|
|
|
|
ret = net_pkt_read(pkt, &data->addr, sizeof(data->addr));
|
|
if (ret < 0) {
|
|
/* Should not happen, is the pkt corrupt? */
|
|
return -EMSGSIZE;
|
|
}
|
|
|
|
/* Request is zeros? Give our dns address in ConfNak */
|
|
if (data->addr.s_addr == INADDR_ANY) {
|
|
NET_DBG("[IPCP] zeroes rcvd as %s addr, sending NAK with our %s addr",
|
|
"DNS", "DNS");
|
|
return -EINVAL;
|
|
}
|
|
|
|
data->addr_present = true;
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int ipcp_ip_address_parse(struct ppp_fsm *fsm, struct net_pkt *pkt,
|
|
void *user_data)
|
|
{
|
|
struct ipcp_peer_option_data *data = user_data;
|
|
int ret;
|
|
|
|
ret = net_pkt_read(pkt, &data->addr, sizeof(data->addr));
|
|
if (ret < 0) {
|
|
/* Should not happen, is the pkt corrupt? */
|
|
return -EMSGSIZE;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_L2_PPP_OPTION_SERVE_IP)
|
|
/* Request is zeros? Give our IP address in ConfNak */
|
|
if (data->addr.s_addr == INADDR_ANY) {
|
|
NET_DBG("[IPCP] zeroes rcvd as %s addr, sending NAK with our %s addr",
|
|
"IP", "IP");
|
|
return -EINVAL;
|
|
}
|
|
#endif
|
|
if (CONFIG_NET_L2_PPP_LOG_LEVEL >= LOG_LEVEL_DBG) {
|
|
char dst[INET_ADDRSTRLEN];
|
|
char *addr_str;
|
|
|
|
addr_str = net_addr_ntop(AF_INET, &data->addr, dst,
|
|
sizeof(dst));
|
|
|
|
NET_DBG("[IPCP] Received peer address %s",
|
|
addr_str);
|
|
}
|
|
|
|
data->addr_present = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_L2_PPP_OPTION_SERVE_IP)
|
|
static int ipcp_server_nak_ip_address(struct ppp_fsm *fsm,
|
|
struct net_pkt *ret_pkt, void *user_data)
|
|
{
|
|
struct ppp_context *ctx =
|
|
CONTAINER_OF(fsm, struct ppp_context, ipcp.fsm);
|
|
|
|
(void)net_pkt_write_u8(ret_pkt, IPCP_OPTION_IP_ADDRESS);
|
|
ipcp_add_ip_address(ctx, ret_pkt);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_L2_PPP_OPTION_SERVE_DNS)
|
|
static int ipcp_server_nak_dns1_address(struct ppp_fsm *fsm,
|
|
struct net_pkt *ret_pkt,
|
|
void *user_data)
|
|
{
|
|
struct ppp_context *ctx =
|
|
CONTAINER_OF(fsm, struct ppp_context, ipcp.fsm);
|
|
|
|
(void)net_pkt_write_u8(ret_pkt, IPCP_OPTION_DNS1);
|
|
ipcp_add_dns1(ctx, ret_pkt);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ipcp_server_nak_dns2_address(struct ppp_fsm *fsm,
|
|
struct net_pkt *ret_pkt,
|
|
void *user_data)
|
|
{
|
|
struct ppp_context *ctx =
|
|
CONTAINER_OF(fsm, struct ppp_context, ipcp.fsm);
|
|
|
|
(void)net_pkt_write_u8(ret_pkt, IPCP_OPTION_DNS2);
|
|
ipcp_add_dns2(ctx, ret_pkt);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static const struct ppp_peer_option_info ipcp_peer_options[] = {
|
|
#if defined(CONFIG_NET_L2_PPP_OPTION_SERVE_IP)
|
|
PPP_PEER_OPTION(IPCP_OPTION_IP_ADDRESS, ipcp_ip_address_parse,
|
|
ipcp_server_nak_ip_address),
|
|
#else
|
|
PPP_PEER_OPTION(IPCP_OPTION_IP_ADDRESS, ipcp_ip_address_parse, NULL),
|
|
#endif
|
|
#if defined(CONFIG_NET_L2_PPP_OPTION_SERVE_DNS)
|
|
PPP_PEER_OPTION(IPCP_OPTION_DNS1, ipcp_dns_address_parse,
|
|
ipcp_server_nak_dns1_address),
|
|
PPP_PEER_OPTION(IPCP_OPTION_DNS2, ipcp_dns_address_parse,
|
|
ipcp_server_nak_dns2_address),
|
|
#endif
|
|
};
|
|
|
|
static int ipcp_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, ipcp.fsm);
|
|
struct ipcp_peer_option_data data = {
|
|
.addr_present = false,
|
|
};
|
|
int ret;
|
|
|
|
ret = ppp_config_info_req(fsm, pkt, length, ret_pkt, PPP_IPCP,
|
|
ipcp_peer_options,
|
|
ARRAY_SIZE(ipcp_peer_options),
|
|
&data);
|
|
if (ret != PPP_CONFIGURE_ACK) {
|
|
/* There are some issues with configuration still */
|
|
return ret;
|
|
}
|
|
|
|
if (!data.addr_present) {
|
|
NET_DBG("[%s/%p] No %saddress provided",
|
|
fsm->name, fsm, "peer ");
|
|
return PPP_CONFIGURE_ACK;
|
|
}
|
|
|
|
/* The address is the remote address, we then need
|
|
* to figure out what our address should be.
|
|
*
|
|
* TODO:
|
|
* - check that the IP address can be accepted
|
|
*/
|
|
|
|
memcpy(&ctx->ipcp.peer_options.address, &data.addr, sizeof(data.addr));
|
|
|
|
return PPP_CONFIGURE_ACK;
|
|
}
|
|
|
|
static void ipcp_set_dns_servers(struct ppp_fsm *fsm)
|
|
{
|
|
#if defined(CONFIG_NET_L2_PPP_OPTION_DNS_USE)
|
|
struct ppp_context *ctx = CONTAINER_OF(fsm, struct ppp_context,
|
|
ipcp.fsm);
|
|
|
|
struct dns_resolve_context *dnsctx;
|
|
struct sockaddr_in dns1 = {
|
|
.sin_family = AF_INET,
|
|
.sin_port = htons(53),
|
|
.sin_addr = ctx->ipcp.my_options.dns1_address
|
|
};
|
|
struct sockaddr_in dns2 = {
|
|
.sin_family = AF_INET,
|
|
.sin_port = htons(53),
|
|
.sin_addr = ctx->ipcp.my_options.dns2_address
|
|
};
|
|
const struct sockaddr *dns_servers[] = {
|
|
(struct sockaddr *) &dns1,
|
|
(struct sockaddr *) &dns2,
|
|
NULL
|
|
};
|
|
int ret;
|
|
|
|
if (!dns1.sin_addr.s_addr) {
|
|
return;
|
|
}
|
|
|
|
if (!dns2.sin_addr.s_addr) {
|
|
dns_servers[1] = NULL;
|
|
}
|
|
|
|
dnsctx = dns_resolve_get_default();
|
|
ret = dns_resolve_reconfigure(dnsctx, NULL, dns_servers);
|
|
if (ret < 0) {
|
|
NET_ERR("Could not set DNS servers");
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static int ipcp_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,
|
|
ipcp.fsm);
|
|
int ret;
|
|
|
|
ret = ppp_my_options_parse_conf_nak(fsm, pkt, length);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
if (!ctx->ipcp.my_options.address.s_addr) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
ipcp_set_dns_servers(fsm);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ipcp_lower_down(struct ppp_context *ctx)
|
|
{
|
|
ppp_fsm_lower_down(&ctx->ipcp.fsm);
|
|
}
|
|
|
|
static void ipcp_lower_up(struct ppp_context *ctx)
|
|
{
|
|
ppp_fsm_lower_up(&ctx->ipcp.fsm);
|
|
}
|
|
|
|
static void ipcp_open(struct ppp_context *ctx)
|
|
{
|
|
ppp_fsm_open(&ctx->ipcp.fsm);
|
|
}
|
|
|
|
static void ipcp_close(struct ppp_context *ctx, const uint8_t *reason)
|
|
{
|
|
ppp_fsm_close(&ctx->ipcp.fsm, reason);
|
|
}
|
|
|
|
static void ipcp_up(struct ppp_fsm *fsm)
|
|
{
|
|
struct ppp_context *ctx = CONTAINER_OF(fsm, struct ppp_context,
|
|
ipcp.fsm);
|
|
struct net_if_addr *addr;
|
|
char dst[INET_ADDRSTRLEN];
|
|
char *addr_str;
|
|
|
|
if (ctx->is_ipcp_up) {
|
|
return;
|
|
}
|
|
|
|
addr_str = net_addr_ntop(AF_INET, &ctx->ipcp.my_options.address,
|
|
dst, sizeof(dst));
|
|
|
|
addr = net_if_ipv4_addr_add(ctx->iface,
|
|
&ctx->ipcp.my_options.address,
|
|
NET_ADDR_MANUAL,
|
|
0);
|
|
if (addr == NULL) {
|
|
NET_ERR("Could not set IP address %s", addr_str);
|
|
return;
|
|
}
|
|
|
|
NET_DBG("PPP up with address %s", addr_str);
|
|
ppp_network_up(ctx, PPP_IP);
|
|
|
|
ctx->is_ipcp_up = true;
|
|
|
|
NET_DBG("[%s/%p] Current state %s (%d)", fsm->name, fsm,
|
|
ppp_state_str(fsm->state), fsm->state);
|
|
}
|
|
|
|
static void ipcp_down(struct ppp_fsm *fsm)
|
|
{
|
|
struct ppp_context *ctx = CONTAINER_OF(fsm, struct ppp_context,
|
|
ipcp.fsm);
|
|
|
|
/* Ensure address is always removed if it exists */
|
|
if (ctx->ipcp.my_options.address.s_addr) {
|
|
(void)net_if_ipv4_addr_rm(
|
|
ctx->iface, &ctx->ipcp.my_options.address);
|
|
}
|
|
memset(&ctx->ipcp.my_options.address, 0,
|
|
sizeof(ctx->ipcp.my_options.address));
|
|
memset(&ctx->ipcp.my_options.dns1_address, 0,
|
|
sizeof(ctx->ipcp.my_options.dns1_address));
|
|
memset(&ctx->ipcp.my_options.dns2_address, 0,
|
|
sizeof(ctx->ipcp.my_options.dns2_address));
|
|
|
|
if (!ctx->is_ipcp_up) {
|
|
return;
|
|
}
|
|
|
|
ctx->is_ipcp_up = false;
|
|
|
|
ppp_network_down(ctx, PPP_IP);
|
|
}
|
|
|
|
static void ipcp_finished(struct ppp_fsm *fsm)
|
|
{
|
|
struct ppp_context *ctx = CONTAINER_OF(fsm, struct ppp_context,
|
|
ipcp.fsm);
|
|
|
|
if (!ctx->is_ipcp_open) {
|
|
return;
|
|
}
|
|
|
|
ctx->is_ipcp_open = false;
|
|
|
|
ppp_network_done(ctx, PPP_IP);
|
|
}
|
|
|
|
static void ipcp_proto_reject(struct ppp_fsm *fsm)
|
|
{
|
|
ppp_fsm_lower_down(fsm);
|
|
}
|
|
|
|
static void ipcp_init(struct ppp_context *ctx)
|
|
{
|
|
NET_DBG("proto %s (0x%04x) fsm %p", ppp_proto2str(PPP_IPCP), PPP_IPCP,
|
|
&ctx->ipcp.fsm);
|
|
|
|
memset(&ctx->ipcp.fsm, 0, sizeof(ctx->ipcp.fsm));
|
|
|
|
ppp_fsm_init(&ctx->ipcp.fsm, PPP_IPCP);
|
|
|
|
ppp_fsm_name_set(&ctx->ipcp.fsm, ppp_proto2str(PPP_IPCP));
|
|
|
|
ctx->ipcp.fsm.my_options.info = ipcp_my_options;
|
|
ctx->ipcp.fsm.my_options.data = ctx->ipcp.my_options_data;
|
|
ctx->ipcp.fsm.my_options.count = ARRAY_SIZE(ipcp_my_options);
|
|
|
|
ctx->ipcp.fsm.cb.up = ipcp_up;
|
|
ctx->ipcp.fsm.cb.down = ipcp_down;
|
|
ctx->ipcp.fsm.cb.finished = ipcp_finished;
|
|
ctx->ipcp.fsm.cb.proto_reject = ipcp_proto_reject;
|
|
ctx->ipcp.fsm.cb.config_info_add = ipcp_config_info_add;
|
|
ctx->ipcp.fsm.cb.config_info_req = ipcp_config_info_req;
|
|
ctx->ipcp.fsm.cb.config_info_nack = ipcp_config_info_nack;
|
|
ctx->ipcp.fsm.cb.config_info_rej = ppp_my_options_parse_conf_rej;
|
|
}
|
|
|
|
PPP_PROTOCOL_REGISTER(IPCP, PPP_IPCP,
|
|
ipcp_init, ipcp_handle,
|
|
ipcp_lower_up, ipcp_lower_down,
|
|
ipcp_open, ipcp_close);
|