From 774e2dc0820b43f8236e76c3bc77cc751915667c Mon Sep 17 00:00:00 2001 From: Seppo Takalo Date: Wed, 16 Jul 2025 11:21:47 +0300 Subject: [PATCH 1/2] net: ppp: ipcp: Don't request DNS if not needed When Kconfig option CONFIG_NET_L2_PPP_OPTION_DNS_USE is enabled, Zephyr should request two DNS addresses in IPCP negotiation by sending IPCP ConfReq for DNS otions using 0.0.0.0 as an address. Remote peer may offer DNS by sending IPCP ConfNak with proper address. This is explained in RFC 1332 and RFC 1877 (DNS extension). When no DNS is required, we should only send IPCP ConfReq for IP address, without having DNS fields in the same request. However, when PPP is configured to serve a DNS using Kconfig option CONFIG_NET_L2_PPP_OPTION_SERVE_DNS it should serve the DNS address in the IPCP ConfNak message and from the ipcp.peer_options structure, not from the ipcp.my_options. This might break backward compatibility outside this repository as DNS addresses used to be served from ipcp.my_options. Signed-off-by: Seppo Takalo --- include/zephyr/net/ppp.h | 4 +++ subsys/net/l2/ppp/ipcp.c | 58 +++++++++++++++++++++------------------- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/include/zephyr/net/ppp.h b/include/zephyr/net/ppp.h index a2f58825c358b..82e026483806d 100644 --- a/include/zephyr/net/ppp.h +++ b/include/zephyr/net/ppp.h @@ -353,7 +353,11 @@ struct ppp_my_option_data { uint32_t flags; }; +#if defined(CONFIG_NET_L2_PPP_OPTION_DNS_USE) #define IPCP_NUM_MY_OPTIONS 3 +#else +#define IPCP_NUM_MY_OPTIONS 1 +#endif #define IPV6CP_NUM_MY_OPTIONS 1 enum ppp_flags { diff --git a/subsys/net/l2/ppp/ipcp.c b/subsys/net/l2/ppp/ipcp.c index 35b7bac018bc9..14c3091ed8dc6 100644 --- a/subsys/net/l2/ppp/ipcp.c +++ b/subsys/net/l2/ppp/ipcp.c @@ -42,16 +42,6 @@ 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) { @@ -81,20 +71,6 @@ static int ipcp_ack_ip_address(struct ppp_context *ctx, struct net_pkt *pkt, &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) { @@ -112,6 +88,31 @@ static int ipcp_nak_ip_address(struct ppp_context *ctx, struct net_pkt *pkt, &ctx->ipcp.my_options.address); } +#if defined(CONFIG_NET_L2_PPP_OPTION_DNS_USE) +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_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_dns1(struct ppp_context *ctx, struct net_pkt *pkt, uint8_t oplen) { @@ -125,21 +126,24 @@ static int ipcp_nak_dns2(struct ppp_context *ctx, struct net_pkt *pkt, return ipcp_nak_override_address(pkt, oplen, &ctx->ipcp.my_options.dns2_address); } +#endif /* CONFIG_NET_L2_PPP_OPTION_DNS_USE */ 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), +#if defined(CONFIG_NET_L2_PPP_OPTION_DNS_USE) 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), +#endif }; 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); + return ppp_my_options_add(fsm, IPCP_NUM_MY_OPTIONS * IP_ADDRESS_OPTION_LEN); } struct ipcp_peer_option_data { @@ -232,7 +236,7 @@ static int ipcp_server_nak_dns1_address(struct ppp_fsm *fsm, CONTAINER_OF(fsm, struct ppp_context, ipcp.fsm); (void)net_pkt_write_u8(ret_pkt, IPCP_OPTION_DNS1); - ipcp_add_dns1(ctx, ret_pkt); + (void)ipcp_add_address(ctx, ret_pkt, &ctx->ipcp.peer_options.dns1_address); return 0; } @@ -245,7 +249,7 @@ static int ipcp_server_nak_dns2_address(struct ppp_fsm *fsm, CONTAINER_OF(fsm, struct ppp_context, ipcp.fsm); (void)net_pkt_write_u8(ret_pkt, IPCP_OPTION_DNS2); - ipcp_add_dns2(ctx, ret_pkt); + (void)ipcp_add_address(ctx, ret_pkt, &ctx->ipcp.peer_options.dns2_address); return 0; } From d7f78e506e34f23d5b66e559ac82c3042e9cbe2c Mon Sep 17 00:00:00 2001 From: Seppo Takalo Date: Wed, 16 Jul 2025 16:38:57 +0300 Subject: [PATCH 2/2] net: ppp: Allow peer-options to be rejected There are cases, for example when peer is requesting two DNS addresses, but we can only give one, we should reject the secondary DNS request first, before sending NAK to acceptable options. Option parser can reject a parameter by returning -ENOTSUP and when it wants to NAK the parameter, it returns -EINVAL. On RFC 1661: Configure-Reject If some Configuration Options received in a Configure-Request are not recognizable or are not acceptable for negotiation (as configured by a network administrator), then the implementation MUST transmit a Configure-Reject. Configure-Nak If every instance of the received Configuration Options is recognizable, but some values are not acceptable, then the implementation MUST transmit a Configure-Nak. So as stated by RFC, we should start the negotiation by rejecting all parameters that we cannot configure. I added an example of rejecting DNS requests, if we don't have those. Signed-off-by: Seppo Takalo --- subsys/net/l2/ppp/ipcp.c | 35 +++++++++++++++++++++++++++++++++-- subsys/net/l2/ppp/options.c | 30 +++++++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/subsys/net/l2/ppp/ipcp.c b/subsys/net/l2/ppp/ipcp.c index 14c3091ed8dc6..320b78a0c0c31 100644 --- a/subsys/net/l2/ppp/ipcp.c +++ b/subsys/net/l2/ppp/ipcp.c @@ -228,6 +228,37 @@ static int ipcp_server_nak_ip_address(struct ppp_fsm *fsm, #endif #if defined(CONFIG_NET_L2_PPP_OPTION_SERVE_DNS) + +static int ipcp_dns1_address_parse(struct ppp_fsm *fsm, struct net_pkt *pkt, + void *user_data) +{ + struct ppp_context *ctx = + CONTAINER_OF(fsm, struct ppp_context, ipcp.fsm); + int ret; + + ret = ipcp_dns_address_parse(fsm, pkt, user_data); + + if (ret == -EINVAL && ctx->ipcp.peer_options.dns1_address.s_addr == INADDR_ANY) { + return -ENOTSUP; + } + return ret; +} + +static int ipcp_dns2_address_parse(struct ppp_fsm *fsm, struct net_pkt *pkt, + void *user_data) +{ + struct ppp_context *ctx = + CONTAINER_OF(fsm, struct ppp_context, ipcp.fsm); + int ret; + + ret = ipcp_dns_address_parse(fsm, pkt, user_data); + + if (ret == -EINVAL && ctx->ipcp.peer_options.dns2_address.s_addr == INADDR_ANY) { + return -ENOTSUP; + } + return ret; +} + static int ipcp_server_nak_dns1_address(struct ppp_fsm *fsm, struct net_pkt *ret_pkt, void *user_data) @@ -263,9 +294,9 @@ static const struct ppp_peer_option_info ipcp_peer_options[] = { 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, + PPP_PEER_OPTION(IPCP_OPTION_DNS1, ipcp_dns1_address_parse, ipcp_server_nak_dns1_address), - PPP_PEER_OPTION(IPCP_OPTION_DNS2, ipcp_dns_address_parse, + PPP_PEER_OPTION(IPCP_OPTION_DNS2, ipcp_dns2_address_parse, ipcp_server_nak_dns2_address), #endif }; diff --git a/subsys/net/l2/ppp/options.c b/subsys/net/l2/ppp/options.c index 6fd8c15de6e0f..694d338c9b767 100644 --- a/subsys/net/l2/ppp/options.c +++ b/subsys/net/l2/ppp/options.c @@ -144,14 +144,38 @@ static int ppp_parse_option_conf_req_supported(struct net_pkt *pkt, { 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; + struct net_pkt_cursor cursor; 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; + net_pkt_cursor_backup(pkt, &cursor); ret = option_info->parse(fsm, pkt, parse_data->user_data); - if (ret == -EINVAL) { + if (ret == -ENOTSUP) { + net_pkt_cursor_restore(pkt, &cursor); + parse_data->rej_count++; + if (parse_data->nack_count != 0) { + /* Remove any NACKed data, if we need to reject something first */ + net_pkt_update_length(ret_pkt, 0); + net_pkt_cursor_init(ret_pkt); + parse_data->nack_count = 0; + } + 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; + } else if (ret == -EINVAL) { + if (parse_data->rej_count != 0) { + /* If we have already rejected some options, we + * cannot NACK anything in the same packet. + */ + return 0; + } parse_data->nack_count++; ret = option_info->nack(fsm, parse_data->ret_pkt, parse_data->user_data); @@ -202,6 +226,10 @@ int ppp_config_info_req(struct ppp_fsm *fsm, return -EINVAL; } + if (parse_data.rej_count) { + return PPP_CONFIGURE_REJ; + } + if (parse_data.nack_count) { return PPP_CONFIGURE_NACK; }