Skip to content

Commit 2d6edbb

Browse files
committed
Add a martian + strict-RPF source filter to ip_recv's WOLFIP_ENABLE_FORWARDING
relay path so packets sourced from 127.0.0.0/8 on a non-loopback ingress, 169.254.0.0/16 link-local, or any locally-configured subnet on the wrong interface are dropped before wolfIP_forward_interface, with test_regression_forwarding_rpf_drops_spoofed_source pinning the contract.
1 parent b203126 commit 2d6edbb

3 files changed

Lines changed: 86 additions & 0 deletions

File tree

src/test/unit/unit.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,7 @@ Suite *wolf_suite(void)
789789
tcase_add_test(tc_proto, test_forward_packet_ip_filter_drop);
790790
tcase_add_test(tc_proto, test_forward_packet_eth_filter_drop);
791791
tcase_add_test(tc_proto, test_loopback_dest_not_forwarded);
792+
tcase_add_test(tc_proto, test_regression_forwarding_rpf_drops_spoofed_source);
792793
tcase_add_test(tc_proto, test_tcp_listen_rejects_wrong_interface);
793794
tcase_add_test(tc_proto, test_tcp_listen_accepts_bound_interface);
794795
tcase_add_test(tc_proto, test_tcp_listen_accepts_any_interface);

src/test/unit/unit_tests_proto.c

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3762,6 +3762,57 @@ START_TEST(test_loopback_dest_not_forwarded)
37623762
}
37633763
END_TEST
37643764

3765+
START_TEST(test_regression_forwarding_rpf_drops_spoofed_source)
3766+
{
3767+
static const ip4 spoofed_sources[] = {
3768+
0x7F000001U, /* 127.0.0.1; loopback */
3769+
0xA9FE0001U, /* 169.254.0.1; link-local */
3770+
0xC0A80132U /* 192.168.1.50; in TEST_SECOND_IF's subnet, wrong ingress */
3771+
};
3772+
unsigned int i;
3773+
uint8_t iface1_mac[6] = {0x02, 0x00, 0x00, 0x00, 0x00, 0x02};
3774+
uint8_t next_hop_mac[6] = {0x02, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE};
3775+
uint8_t src_mac[6] = {0x52, 0x54, 0x00, 0x12, 0x34, 0x56};
3776+
uint32_t dest_ip = 0xC0A80164U; /* 192.168.1.100; on TEST_SECOND_IF */
3777+
3778+
for (i = 0; i < sizeof(spoofed_sources) / sizeof(spoofed_sources[0]); i++) {
3779+
struct wolfIP s;
3780+
uint8_t frame_buf[64];
3781+
struct wolfIP_ip_packet *frame = (struct wolfIP_ip_packet *)frame_buf;
3782+
3783+
wolfIP_init(&s);
3784+
mock_link_init(&s);
3785+
mock_link_init_idx(&s, TEST_SECOND_IF, iface1_mac);
3786+
wolfIP_ipconfig_set(&s, 0xC0A80001U, 0xFFFFFF00U, 0);
3787+
wolfIP_ipconfig_set_ex(&s, TEST_SECOND_IF, 0xC0A80101U, 0xFFFFFF00U, 0);
3788+
s.arp.neighbors[0].ip = dest_ip;
3789+
s.arp.neighbors[0].if_idx = TEST_SECOND_IF;
3790+
memcpy(s.arp.neighbors[0].mac, next_hop_mac, 6);
3791+
3792+
memset(frame_buf, 0, sizeof(frame_buf));
3793+
memcpy(frame->eth.dst, s.ll_dev[TEST_PRIMARY_IF].mac, 6);
3794+
memcpy(frame->eth.src, src_mac, 6);
3795+
frame->eth.type = ee16(ETH_TYPE_IP);
3796+
frame->ver_ihl = 0x45;
3797+
frame->ttl = 64;
3798+
frame->proto = WI_IPPROTO_UDP;
3799+
frame->len = ee16(IP_HEADER_LEN);
3800+
frame->src = ee32(spoofed_sources[i]);
3801+
frame->dst = ee32(dest_ip);
3802+
frame->csum = 0;
3803+
iphdr_set_checksum(frame);
3804+
3805+
memset(last_frame_sent, 0, sizeof(last_frame_sent));
3806+
last_frame_sent_size = 0;
3807+
3808+
wolfIP_recv_ex(&s, TEST_PRIMARY_IF, frame,
3809+
ETH_HEADER_LEN + IP_HEADER_LEN);
3810+
3811+
ck_assert_uint_eq(last_frame_sent_size, 0);
3812+
}
3813+
}
3814+
END_TEST
3815+
37653816
/* wolfSSL IO glue tests */
37663817
START_TEST(test_wolfssl_io_ctx_registers_callbacks)
37673818
{

src/wolfip.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8359,6 +8359,39 @@ static inline void ip_recv(struct wolfIP *s, unsigned int if_idx,
83598359
}
83608360
}
83618361
if (!is_local) {
8362+
ip4 src = ee32(ip->src);
8363+
int rpf_drop = 0;
8364+
8365+
/* Martian source: 127.0.0.0/8 must not arrive on a non-loopback
8366+
* interface (and must never be forwarded). */
8367+
if ((src & WOLFIP_LOOPBACK_MASK) ==
8368+
(WOLFIP_LOOPBACK_IP & WOLFIP_LOOPBACK_MASK) &&
8369+
!wolfIP_is_loopback_if(if_idx)) {
8370+
rpf_drop = 1;
8371+
}
8372+
/* Martian source: 169.254.0.0/16 link-local is not routable. */
8373+
if (!rpf_drop && (src & 0xFFFF0000U) == 0xA9FE0000U) {
8374+
rpf_drop = 1;
8375+
}
8376+
/* Strict RPF: a source that belongs to some other configured
8377+
* interface's local subnet must not arrive on this one. */
8378+
if (!rpf_drop) {
8379+
for (i = 0; i < s->if_count; i++) {
8380+
struct ipconf *conf = &s->ipconf[i];
8381+
if (i == if_idx)
8382+
continue;
8383+
if (!conf || conf->ip == IPADDR_ANY)
8384+
continue;
8385+
if (ip_is_local_conf(conf, src)) {
8386+
rpf_drop = 1;
8387+
break;
8388+
}
8389+
}
8390+
}
8391+
if (rpf_drop)
8392+
return;
8393+
8394+
{
83628395
int out_if = wolfIP_forward_interface(s, if_idx, dest);
83638396
if (out_if >= 0) {
83648397
uint8_t mac[6];
@@ -8381,6 +8414,7 @@ static inline void ip_recv(struct wolfIP *s, unsigned int if_idx,
83818414
wolfIP_forward_packet(s, out_if, ip, len, broadcast ? NULL : mac, broadcast);
83828415
return;
83838416
}
8417+
}
83848418
}
83858419
}
83868420
#endif /* WOLFIP_ENABLE_FORWARDING */

0 commit comments

Comments
 (0)