Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/core/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@
#include "lwip/mld6.h"
#include "lwip/api.h"

#if LWIP_DHCP_CLASSLESS_STATIC_ROUTES
#include "lwip/ip4_route_table.h"
#endif /* LWIP_DHCP_CLASSLESS_STATIC_ROUTES */

#include "netif/ppp/ppp_opts.h"
#include "netif/ppp/ppp_impl.h"

Expand Down Expand Up @@ -94,6 +98,9 @@ PACK_STRUCT_END
#if (!LWIP_UDP && LWIP_DHCP)
#error "If you want to use DHCP, you have to define LWIP_UDP=1 in your lwipopts.h"
#endif
#if (LWIP_DHCP_CLASSLESS_STATIC_ROUTES && !LWIP_DHCP)
#error "If you want to use LWIP_DHCP_CLASSLESS_STATIC_ROUTES, you have to define LWIP_DHCP=1 in your lwipopts.h"
#endif
#if (!LWIP_UDP && !LWIP_RAW && LWIP_MULTICAST_TX_OPTIONS)
#error "If you want to use LWIP_MULTICAST_TX_OPTIONS, you have to define LWIP_UDP=1 and/or LWIP_RAW=1 in your lwipopts.h"
#endif
Expand Down Expand Up @@ -370,6 +377,9 @@ lwip_init(void)
#if LWIP_DNS
dns_init();
#endif /* LWIP_DNS */
#if LWIP_DHCP_CLASSLESS_STATIC_ROUTES
ip4_route_table_init();
#endif /* LWIP_DHCP_CLASSLESS_STATIC_ROUTES */
#if PPP_SUPPORT
ppp_init();
#endif
Expand Down
172 changes: 163 additions & 9 deletions src/core/ipv4/dhcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@

#include <string.h>

#if LWIP_DHCP_CLASSLESS_STATIC_ROUTES
#include "lwip/ip4_route_table.h"
#endif

#ifdef LWIP_HOOK_FILENAME
#include LWIP_HOOK_FILENAME
#endif
Expand Down Expand Up @@ -170,6 +174,10 @@ static u8_t dhcp_discover_request_options[] = {
#if LWIP_DHCP_GET_NTP_SRV
, DHCP_OPTION_NTP
#endif /* LWIP_DHCP_GET_NTP_SRV */
#if LWIP_DHCP_CLASSLESS_STATIC_ROUTES
/* request Option 121 (Classless Static Routes) per RFC 3442 */
, DHCP_OPTION_CLASSLESS_STATIC_ROUTE
#endif /* LWIP_DHCP_CLASSLESS_STATIC_ROUTES */
};

#ifdef DHCP_GLOBAL_XID
Expand All @@ -189,6 +197,108 @@ static u8_t dhcp_pcb_refcount;

static sys_lock_t dhcp_mutex;

#if LWIP_DHCP_CLASSLESS_STATIC_ROUTES
/* Calculate significant octets for a prefix length: ceil(prefix_len / 8) */
static u8_t
prefix_to_octets(u8_t prefix_len)
{
return (prefix_len + 7) / 8;
}

/**
* Parse DHCP Option 121 data and add routes to the static route table.
* RFC 3442 variable-length encoding:
* 1 byte: prefix length (0-32)
* N bytes: significant octets of destination (N = ceil(prefix_len/8))
* 4 bytes: gateway IP address
*/
static int
dhcp_parse_classless_routes(struct netif *netif, struct pbuf *opts_pbuf,
u16_t offset, u8_t len)
{
int routes_parsed = 0;
u16_t pos = offset;
u16_t end;

/* overflow check */
if (offset > 0xFFFF - len)
return -1;
end = offset + len;

/* empty option is valid but contains no routes */
if (len == 0)
return 0;

/* Clear existing DHCP routes for this interface first (lease renewal) */
ip4_route_remove_netif(netif, IP4_ROUTE_FLAG_DHCP);

while (pos < end) {
u8_t prefix_len;
u8_t significant_octets;
ip4_addr_t dest;
ip4_addr_t gateway;
u8_t dest_bytes[4] = {0, 0, 0, 0};

/* read prefix length */
if (pbuf_copy_partial(opts_pbuf, &prefix_len, 1, pos) != 1)
break;
pos++;

if (prefix_len > IP4_MAX_PREFIX_LEN)
break;

significant_octets = prefix_to_octets(prefix_len);

/* check we have enough data remaining */
if (pos + significant_octets + sizeof(gateway.addr) > end)
break;

/* read destination network (significant octets only) */
if (significant_octets > 0) {
if (pbuf_copy_partial(opts_pbuf, dest_bytes, significant_octets, pos)
!= significant_octets)
break;
}
pos += significant_octets;

/*
* Reconstruct destination address.
* The significant octets are the high-order bytes of the address.
* dest_bytes is already zero-initialized for non-significant octets.
*/
dest.addr = (dest_bytes[0] << 24) | (dest_bytes[1] << 16) |
(dest_bytes[2] << 8) | dest_bytes[3];
dest.addr = lwip_htonl(dest.addr);

/* apply mask to ensure destination is properly masked (RFC 3442) */
dest.addr &= ip4_prefix_to_mask(prefix_len);

/* read gateway address - already in network byte order */
if (pbuf_copy_partial(opts_pbuf, &gateway.addr, sizeof(gateway.addr), pos)
!= sizeof(gateway.addr))
break;
pos += sizeof(gateway.addr);

/*
* Validate gateway address:
* - Reject multicast/broadcast (invalid as next-hop)
* - 0.0.0.0 is valid per RFC 3442: means destination is on-link
*/
if (ip4_addr_ismulticast(&gateway) ||
ip4_addr_isbroadcast(&gateway, netif))
continue;

/* add route to table */
if (ip4_route_add(&dest, prefix_len, &gateway, netif,
IP4_ROUTE_FLAG_DHCP) == ERR_OK) {
routes_parsed++;
}
}

return routes_parsed;
}
#endif /* LWIP_DHCP_CLASSLESS_STATIC_ROUTES */

/* DHCP client state machine functions */
static err_t dhcp_discover(struct netif *netif);
static err_t dhcp_select(struct netif *netif);
Expand Down Expand Up @@ -287,6 +397,9 @@ dhcp_handle_nak(struct netif *netif)
dhcp_set_state(dhcp, DHCP_STATE_BACKING_OFF);
/* remove IP address from interface (must no longer be used, as per RFC2131) */
netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
#if LWIP_DHCP_CLASSLESS_STATIC_ROUTES
ip4_route_remove_netif(netif, IP4_ROUTE_FLAG_DHCP);
#endif /* LWIP_DHCP_CLASSLESS_STATIC_ROUTES */
/* We can immediately restart discovery */
dhcp_discover(netif);
}
Expand Down Expand Up @@ -733,7 +846,12 @@ dhcp_handle_ack(struct netif *netif, struct dhcp_msg *msg_in)
}

/* gateway router */
if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_ROUTER)) {
if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_ROUTER)
#if LWIP_DHCP_CLASSLESS_STATIC_ROUTES
/* RFC 3442: If Option 121 is present, MUST ignore Option 3 (Router) */
&& !ip4_route_exists(netif, IP4_ROUTE_FLAG_DHCP)
#endif /* LWIP_DHCP_CLASSLESS_STATIC_ROUTES */
) {
ip4_addr_set_u32(&dhcp->offered_gw_addr, lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_ROUTER)));
}

Expand Down Expand Up @@ -791,6 +909,10 @@ void dhcp_cleanup(struct netif *netif)
LWIP_ASSERT_CORE_LOCKED();
LWIP_ASSERT("netif != NULL", netif != NULL);

#if LWIP_DHCP_CLASSLESS_STATIC_ROUTES
ip4_route_remove_netif(netif, IP4_ROUTE_FLAG_DHCP);
#endif /* LWIP_DHCP_CLASSLESS_STATIC_ROUTES */

SYS_ARCH_LOCK(&dhcp_mutex);
if (netif_dhcp_data(netif) != NULL) {
mem_free(netif_dhcp_data(netif));
Expand Down Expand Up @@ -1149,10 +1271,24 @@ dhcp_bind(struct netif *netif)
ip4_addr_copy(gw_addr, dhcp->offered_gw_addr);
/* gateway address not given? */
if (ip4_addr_isany_val(gw_addr)) {
/* copy network address */
ip4_addr_get_network(&gw_addr, &dhcp->offered_ip_addr, &sn_mask);
/* use first host address on network as gateway */
ip4_addr_set_u32(&gw_addr, ip4_addr_get_u32(&gw_addr) | PP_HTONL(0x00000001UL));
#if LWIP_DHCP_CLASSLESS_STATIC_ROUTES
/*
* RFC 3442: If Option 121 is present, use the gateway from the default
* route (0/0) if one was provided. Do not synthesize a gateway — if no
* default route was included in Option 121, there must be none.
*/
if (ip4_route_exists(netif, IP4_ROUTE_FLAG_DHCP)) {
ip4_addr_t default_dest;
ip4_addr_set_zero(&default_dest);
ip4_get_gateway(netif, &default_dest, &gw_addr);
} else
#endif /* LWIP_DHCP_CLASSLESS_STATIC_ROUTES */
{
/* copy network address */
ip4_addr_get_network(&gw_addr, &dhcp->offered_ip_addr, &sn_mask);
/* use first host address on network as gateway */
ip4_addr_set_u32(&gw_addr, ip4_addr_get_u32(&gw_addr) | PP_HTONL(0x00000001UL));
}
}

#if LWIP_DHCP_AUTOIP_COOP
Expand Down Expand Up @@ -1363,6 +1499,10 @@ dhcp_release_and_stop(struct netif *netif)
return;
}

#if LWIP_DHCP_CLASSLESS_STATIC_ROUTES
ip4_route_remove_netif(netif, IP4_ROUTE_FLAG_DHCP);
#endif /* LWIP_DHCP_CLASSLESS_STATIC_ROUTES */

ip_addr_copy(server_ip_addr, dhcp->server_ip_addr);

/* clean old DHCP offer */
Expand Down Expand Up @@ -1536,7 +1676,7 @@ dhcp_option_hostname(u16_t options_out_len, u8_t *options, struct netif *netif)
*
*/
static err_t
dhcp_parse_reply(struct pbuf *p, struct dhcp *dhcp)
dhcp_parse_reply(struct pbuf *p, struct dhcp *dhcp, struct netif *netif)
{
u8_t *options;
u16_t offset;
Expand Down Expand Up @@ -1665,10 +1805,24 @@ dhcp_parse_reply(struct pbuf *p, struct dhcp *dhcp)
LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
decode_idx = DHCP_OPTION_IDX_T2;
break;
#if LWIP_DHCP_CLASSLESS_STATIC_ROUTES
case (DHCP_OPTION_CLASSLESS_STATIC_ROUTE):
/* Parse classless static routes (RFC 3442), only when expecting an ACK.
* Check DHCP state rather than MSG_TYPE to avoid depending on
* option ordering within the packet. */
if (dhcp->state == DHCP_STATE_REQUESTING ||
dhcp->state == DHCP_STATE_RENEWING ||
dhcp->state == DHCP_STATE_REBINDING ||
dhcp->state == DHCP_STATE_REBOOTING) {
dhcp_parse_classless_routes(netif, q, val_offset, len);
}
decode_len = 0;
break;
#endif /* LWIP_DHCP_CLASSLESS_STATIC_ROUTES */
default:
decode_len = 0;
LWIP_DEBUGF(DHCP_DEBUG, ("skipping option %"U16_F" in options\n", (u16_t)op));
LWIP_HOOK_DHCP_PARSE_OPTION(ip_current_netif(), dhcp, dhcp->state, msg_in,
LWIP_HOOK_DHCP_PARSE_OPTION(netif, dhcp, dhcp->state, msg_in,
dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE) ? (u8_t)dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE) : 0,
op, len, q, val_offset);
break;
Expand Down Expand Up @@ -1775,7 +1929,7 @@ dhcp_parse_reply(struct pbuf *p, struct dhcp *dhcp)
/* make sure the string is really NULL-terminated */
dhcp->boot_file_name[DHCP_FILE_LEN-1] = 0;
}
#endif /* LWIP_DHCP_BOOTP_FILE */
#endif /* LWIP_DHCP_BOOTP_FILE */
return ERR_OK;
}

Expand Down Expand Up @@ -1836,7 +1990,7 @@ dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_globals *ip_
goto free_pbuf_and_return;
}
/* option fields could be unfold? */
if (dhcp_parse_reply(p, dhcp) != ERR_OK) {
if (dhcp_parse_reply(p, dhcp, netif) != ERR_OK) {
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
("problem unfolding DHCP message - too short on memory?\n"));
goto free_pbuf_and_return;
Expand Down
13 changes: 13 additions & 0 deletions src/core/ipv4/etharp.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@
#include "lwip/prot/iana.h"
#include "netif/ethernet.h"

#if LWIP_DHCP_CLASSLESS_STATIC_ROUTES
#include "lwip/ip4_route_table.h"
#endif

#include <string.h>

#ifdef LWIP_HOOK_FILENAME
Expand Down Expand Up @@ -845,6 +849,9 @@ etharp_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr)
/* unicast destination IP address? */
} else {
netif_addr_idx_t i;
#if LWIP_DHCP_CLASSLESS_STATIC_ROUTES
ip4_addr_t static_gw;
#endif /* LWIP_DHCP_CLASSLESS_STATIC_ROUTES */
/* outside local network? if so, this can neither be a global broadcast nor
a subnet broadcast. */
if (!ip4_addr_netcmp(ipaddr, netif_ip4_addr(netif), netif_ip4_netmask(netif)) &&
Expand All @@ -858,6 +865,12 @@ etharp_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr)
if (!ip4_addr_islinklocal(&iphdr->src))
#endif /* LWIP_AUTOIP */
{
#if LWIP_DHCP_CLASSLESS_STATIC_ROUTES
/* check static route table for a per-route gateway */
if (ip4_get_gateway(netif, ipaddr, &static_gw)) {
dst_addr = &static_gw;
} else
#endif /* LWIP_DHCP_CLASSLESS_STATIC_ROUTES */
#ifdef LWIP_HOOK_ETHARP_GET_GW
/* For advanced routing, a single default gateway might not be enough, so get
the IP address of the gateway to handle the current destination address. */
Expand Down
25 changes: 22 additions & 3 deletions src/core/ipv4/ip4.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@
#include "lwip/stats.h"
#include "lwip/prot/iana.h"

#if LWIP_DHCP_CLASSLESS_STATIC_ROUTES
#include "lwip/ip4_route_table.h"
#endif

#include <string.h>

#ifdef LWIP_HOOK_FILENAME
Expand Down Expand Up @@ -130,7 +134,7 @@ ip4_set_default_multicast_netif(struct netif *default_multicast_netif)
}
#endif /* LWIP_MULTICAST_TX_OPTIONS */

#ifdef LWIP_HOOK_IP4_ROUTE_SRC
#if defined(LWIP_HOOK_IP4_ROUTE_SRC) || LWIP_DHCP_CLASSLESS_STATIC_ROUTES
/**
* Source based IPv4 routing must be fully implemented in
* LWIP_HOOK_IP4_ROUTE_SRC(). This function only provides the parameters.
Expand All @@ -139,15 +143,24 @@ struct netif *
ip4_route_src(const ip4_addr_t *src, const ip4_addr_t *dest)
{
if (src != NULL) {
struct netif *netif;
/* when src==NULL, the hook is called from ip4_route(dest) */
struct netif *netif = LWIP_HOOK_IP4_ROUTE_SRC(src, dest);
#if LWIP_DHCP_CLASSLESS_STATIC_ROUTES
netif = ip4_static_route(src, dest);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to add static route handling to this function, adding it to ip4_route() is enough. This function calls ip4_route() if no hooks are defined.

if (netif != NULL) {
return netif;
}
#endif /* LWIP_DHCP_CLASSLESS_STATIC_ROUTES */
#ifdef LWIP_HOOK_IP4_ROUTE_SRC
netif = LWIP_HOOK_IP4_ROUTE_SRC(src, dest);
if (netif != NULL) {
return netif;
}
#endif /* LWIP_HOOK_IP4_ROUTE_SRC */
}
return ip4_route(dest);
}
#endif /* LWIP_HOOK_IP4_ROUTE_SRC */
#endif /* LWIP_HOOK_IP4_ROUTE_SRC || LWIP_DHCP_CLASSLESS_STATIC_ROUTES */

static u8_t ip4_route_netif(struct netif *n, void *priv)
{
Expand Down Expand Up @@ -240,6 +253,12 @@ ip4_route(const ip4_addr_t *dest)
}
#endif /* LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF */

#if LWIP_DHCP_CLASSLESS_STATIC_ROUTES
netif = ip4_static_route(NULL, dest);
if (netif != NULL) {
return netif;
}
#endif /* LWIP_DHCP_CLASSLESS_STATIC_ROUTES */
#ifdef LWIP_HOOK_IP4_ROUTE_SRC
netif = LWIP_HOOK_IP4_ROUTE_SRC(NULL, dest);
if (netif != NULL) {
Expand Down
Loading