net: l2: ppp: add generic function for handling Conf-Req

Introduce new ppp_config_info_req() function that can be used in order
to handle options received within Conf-Req packet. As an input it takes
array of supported options. If received Conf-Req packet contains unknown
options, then a Conf-Rej packet is automatically generated with all of
those options. If all of received options are supported, then function
continues to parse each provided option individually by calling option
specific callbacks.

Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com>
This commit is contained in:
Marcin Niestroj 2020-07-27 16:32:49 +02:00 committed by Carles Cufí
parent 8e5ea59ed5
commit 35a2519091
4 changed files with 264 additions and 222 deletions

View File

@ -142,133 +142,80 @@ 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;
};
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 (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",
log_strdup(addr_str));
}
data->addr_present = true;
return 0;
}
static const struct ppp_peer_option_info ipcp_peer_options[] = {
PPP_PEER_OPTION(IPCP_OPTION_IP_ADDRESS, ipcp_ip_address_parse),
};
static int ipcp_config_info_req(struct ppp_fsm *fsm,
struct net_pkt *pkt,
uint16_t length,
struct net_pkt *ret_pkt)
{
int nack_idx = 0, address_option_idx = -1;
struct ppp_option_pkt options[MAX_IPCP_OPTIONS];
struct ppp_option_pkt nack_options[MAX_IPCP_OPTIONS];
enum ppp_packet_type code;
struct ppp_context *ctx =
CONTAINER_OF(fsm, struct ppp_context, ipcp.fsm);
struct ipcp_peer_option_data data = {
.addr_present = false,
};
int ret;
int i;
memset(options, 0, sizeof(options));
memset(nack_options, 0, sizeof(nack_options));
ret = ppp_parse_options_array(fsm, pkt, length, options,
ARRAY_SIZE(options));
if (ret < 0) {
return -EINVAL;
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;
}
for (i = 0; i < ARRAY_SIZE(options); i++) {
if (options[i].type.ipcp != IPCP_OPTION_RESERVED) {
NET_DBG("[%s/%p] %s option %s (%d) len %d",
fsm->name, fsm, "Check",
ppp_option2str(PPP_IPCP, options[i].type.ipcp),
options[i].type.ipcp, options[i].len);
}
switch (options[i].type.ipcp) {
case IPCP_OPTION_RESERVED:
continue;
case IPCP_OPTION_IP_ADDRESS:
/* Currently we only accept one option (IP address) */
address_option_idx = i;
break;
default:
nack_options[nack_idx].type.ipcp =
options[i].type.ipcp;
nack_options[nack_idx].len = options[i].len;
if (options[i].len > 2) {
memcpy(&nack_options[nack_idx].value,
&options[i].value,
sizeof(nack_options[nack_idx].value));
}
nack_idx++;
break;
}
if (!data.addr_present) {
NET_DBG("[%s/%p] No %saddress provided",
fsm->name, fsm, "peer ");
return PPP_CONFIGURE_ACK;
}
if (nack_idx > 0) {
code = PPP_CONFIGURE_REJ;
/* 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
*/
/* Fill ret_pkt with options that are not accepted */
for (i = 0; i < MIN(nack_idx, ARRAY_SIZE(nack_options)); i++) {
net_pkt_write_u8(ret_pkt, nack_options[i].type.ipcp);
net_pkt_write_u8(ret_pkt, nack_options[i].len);
memcpy(&ctx->ipcp.peer_options.address, &data.addr, sizeof(data.addr));
/* If there is some data, copy it to result buf */
if (nack_options[i].value.pos) {
net_pkt_cursor_restore(pkt,
&nack_options[i].value);
net_pkt_copy(ret_pkt, pkt,
nack_options[i].len - 1 - 1);
}
}
} else {
struct ppp_context *ctx;
struct in_addr addr;
int ret;
ctx = CONTAINER_OF(fsm, struct ppp_context, ipcp.fsm);
if (address_option_idx < 0) {
/* The address option was not present, but we
* can continue without it.
*/
NET_DBG("[%s/%p] No %saddress provided",
fsm->name, fsm, "peer ");
return PPP_CONFIGURE_ACK;
}
code = PPP_CONFIGURE_ACK;
net_pkt_cursor_restore(pkt,
&options[address_option_idx].value);
ret = net_pkt_read(pkt, (uint32_t *)&addr, sizeof(addr));
if (ret < 0) {
/* Should not happen, is the pkt corrupt? */
return -EMSGSIZE;
}
memcpy(&ctx->ipcp.peer_options.address, &addr, sizeof(addr));
if (CONFIG_NET_L2_PPP_LOG_LEVEL >= LOG_LEVEL_DBG) {
char dst[INET_ADDRSTRLEN];
char *addr_str;
addr_str = net_addr_ntop(AF_INET, &addr, dst,
sizeof(dst));
NET_DBG("[%s/%p] Received %saddress %s",
fsm->name, fsm, "peer ", log_strdup(addr_str));
}
if (addr.s_addr) {
/* 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
*/
net_pkt_write_u8(ret_pkt, IPCP_OPTION_IP_ADDRESS);
net_pkt_write_u8(ret_pkt, IP_ADDRESS_OPTION_LEN);
net_pkt_write(ret_pkt, &addr.s_addr,
sizeof(addr.s_addr));
}
}
return code;
return PPP_CONFIGURE_ACK;
}
static void ipcp_set_dns_servers(struct ppp_fsm *fsm)

View File

@ -82,122 +82,74 @@ static struct net_pkt *ipv6cp_config_info_add(struct ppp_fsm *fsm)
return ppp_my_options_add(fsm, INTERFACE_IDENTIFIER_OPTION_LEN);
}
static int ipv6cp_config_info_req(struct ppp_fsm *fsm,
struct net_pkt *pkt,
uint16_t length,
struct net_pkt *ret_pkt)
struct ipv6cp_peer_option_data {
bool iface_id_present;
uint8_t iface_id[PPP_INTERFACE_IDENTIFIER_LEN];
};
static int ipv6cp_interface_identifier_parse(struct ppp_fsm *fsm,
struct net_pkt *pkt,
void *user_data)
{
int nack_idx = 0, iface_id_option_idx = -1;
struct ppp_option_pkt options[MAX_IPV6CP_OPTIONS];
struct ppp_option_pkt nack_options[MAX_IPV6CP_OPTIONS];
enum ppp_packet_type code;
struct ipv6cp_peer_option_data *data = user_data;
int ret;
int i;
memset(options, 0, sizeof(options));
memset(nack_options, 0, sizeof(nack_options));
ret = ppp_parse_options_array(fsm, pkt, length, options,
ARRAY_SIZE(options));
ret = net_pkt_read(pkt, data->iface_id, sizeof(data->iface_id));
if (ret < 0) {
/* Should not happen, is the pkt corrupt? */
return -EMSGSIZE;
}
if (CONFIG_NET_L2_PPP_LOG_LEVEL >= LOG_LEVEL_DBG) {
uint8_t iid_str[sizeof("xx:xx:xx:xx:xx:xx:xx:xx")];
net_sprint_ll_addr_buf(data->iface_id, sizeof(data->iface_id),
iid_str, sizeof(iid_str));
NET_DBG("[%s/%p] Received %siid %s",
fsm->name, fsm, "peer ", log_strdup(iid_str));
}
data->iface_id_present = true;
return 0;
}
static const struct ppp_peer_option_info ipv6cp_peer_options[] = {
PPP_PEER_OPTION(IPV6CP_OPTION_INTERFACE_IDENTIFIER,
ipv6cp_interface_identifier_parse),
};
static int ipv6cp_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, ipv6cp.fsm);
struct ipv6cp_peer_option_data data = {
.iface_id_present = false,
};
int ret;
ret = ppp_config_info_req(fsm, pkt, length, ret_pkt, PPP_IPV6CP,
ipv6cp_peer_options,
ARRAY_SIZE(ipv6cp_peer_options),
&data);
if (ret != PPP_CONFIGURE_ACK) {
/* There are some issues with configuration still */
return ret;
}
if (!data.iface_id_present) {
/* Interface id option was not present */
return -EINVAL;
}
for (i = 0; i < ARRAY_SIZE(options); i++) {
if (options[i].type.ipv6cp != IPV6CP_OPTION_RESERVED) {
NET_DBG("[%s/%p] %s option %s (%d) len %d",
fsm->name, fsm, "Check",
ppp_option2str(PPP_IPV6CP,
options[i].type.ipv6cp),
options[i].type.ipv6cp, options[i].len);
}
memcpy(ctx->ipv6cp.peer_options.iid, data.iface_id,
sizeof(data.iface_id));
switch (options[i].type.ipv6cp) {
case IPV6CP_OPTION_RESERVED:
continue;
case IPV6CP_OPTION_INTERFACE_IDENTIFIER:
/* Currently we only accept one option (iface id) */
iface_id_option_idx = i;
break;
default:
nack_options[nack_idx].type.ipv6cp =
options[i].type.ipv6cp;
nack_options[nack_idx].len = options[i].len;
if (options[i].len > 2) {
memcpy(&nack_options[nack_idx].value,
&options[i].value,
sizeof(nack_options[nack_idx].value));
}
nack_idx++;
break;
}
}
if (nack_idx > 0) {
code = PPP_CONFIGURE_REJ;
/* Fill ret_pkt with options that are not accepted */
for (i = 0; i < MIN(nack_idx, ARRAY_SIZE(nack_options)); i++) {
net_pkt_write_u8(ret_pkt, nack_options[i].type.ipv6cp);
net_pkt_write_u8(ret_pkt, nack_options[i].len);
/* If there is some data, copy it to result buf */
if (nack_options[i].value.pos) {
net_pkt_cursor_restore(pkt,
&nack_options[i].value);
net_pkt_copy(ret_pkt, pkt,
nack_options[i].len - 1 - 1);
}
}
} else {
uint8_t iface_id[PPP_INTERFACE_IDENTIFIER_LEN];
struct ppp_context *ctx;
int ret;
ctx = CONTAINER_OF(fsm, struct ppp_context, ipv6cp.fsm);
if (iface_id_option_idx < 0) {
/* Interface id option was not present */
return -EINVAL;
}
code = PPP_CONFIGURE_ACK;
net_pkt_cursor_restore(pkt,
&options[iface_id_option_idx].value);
ret = net_pkt_read(pkt, iface_id, sizeof(iface_id));
if (ret < 0) {
/* Should not happen, is the pkt corrupt? */
return -EMSGSIZE;
}
memcpy(ctx->ipv6cp.peer_options.iid, iface_id,
sizeof(iface_id));
if (CONFIG_NET_L2_PPP_LOG_LEVEL >= LOG_LEVEL_DBG) {
uint8_t iid_str[sizeof("xx:xx:xx:xx:xx:xx:xx:xx")];
net_sprint_ll_addr_buf(iface_id, sizeof(iface_id),
iid_str, sizeof(iid_str));
NET_DBG("[%s/%p] Received %siid %s",
fsm->name, fsm, "peer ", log_strdup(iid_str));
}
/* TODO: check whether iid is empty and create one if so */
net_pkt_write_u8(ret_pkt, IPV6CP_OPTION_INTERFACE_IDENTIFIER);
net_pkt_write_u8(ret_pkt, INTERFACE_IDENTIFIER_OPTION_LEN);
net_pkt_write(ret_pkt, iface_id, sizeof(iface_id));
}
return code;
return PPP_CONFIGURE_ACK;
}
static int ipv6cp_config_info_ack(struct ppp_fsm *fsm,

View File

@ -122,6 +122,128 @@ int ppp_parse_options(struct ppp_fsm *fsm, struct net_pkt *pkt,
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 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);
return option_info->parse(fsm, pkt, parse_data->user_data);
}
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;
}
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);

View File

@ -72,6 +72,27 @@ struct ppp_protocol_handler {
uint16_t protocol;
};
struct ppp_peer_option_info {
uint8_t code;
int (*parse)(struct ppp_fsm *fsm, struct net_pkt *pkt,
void *user_data);
};
#define PPP_PEER_OPTION(_code, _parse) \
{ \
.code = _code, \
.parse = _parse, \
}
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);
#define PPP_PROTO_GET_NAME(proto_name) \
(ppp_protocol_handler_##proto_name)