summaryrefslogtreecommitdiff
path: root/tests/unit/dhcpv6_safeguards_test.c
diff options
context:
space:
mode:
authorDenys Fedoryshchenko <denys.f@collabora.com>2026-09-28 21:50:27 +0300
committerGitHub <noreply@github.com>2026-09-28 21:50:27 +0300
commit50c54c477624d5a9b668225fde2b9844d362760a (patch)
treed1fe53a3c637f8b963027aa483053ae7afb55b5e /tests/unit/dhcpv6_safeguards_test.c
parentc9d938173838d6955c824d141f24672d7037eb4e (diff)
parent06aa0ef737d6993fd18e7db1bc8fc35601079505 (diff)
downloadaccel-ppp-master.tar.gz
accel-ppp-master.zip
Merge pull request #362 from nuclearcat/fix/ng40-additional-safeguardsHEADmaster
Additional safeguards
Diffstat (limited to 'tests/unit/dhcpv6_safeguards_test.c')
-rw-r--r--tests/unit/dhcpv6_safeguards_test.c154
1 files changed, 154 insertions, 0 deletions
diff --git a/tests/unit/dhcpv6_safeguards_test.c b/tests/unit/dhcpv6_safeguards_test.c
new file mode 100644
index 00000000..63692ee3
--- /dev/null
+++ b/tests/unit/dhcpv6_safeguards_test.c
@@ -0,0 +1,154 @@
+/* Standalone tests of the production parser and reply builders. See run_safeguards.sh. */
+#include <assert.h>
+#include "triton.h"
+#undef DEFINE_INIT
+#define DEFINE_INIT(o, f)
+#include "../../accel-pppd/ipv6/dhcpv6_packet.c"
+#undef BUF_SIZE
+#include "../../accel-pppd/ipv6/dhcpv6.c"
+
+__thread struct ap_net *net;
+static unsigned int sent;
+static unsigned char response[4096];
+static size_t response_len;
+void log_warn(const char *fmt, ...) {}
+void log_emerg(const char *fmt, ...) {}
+void log_ppp_error(const char *fmt, ...) {}
+void log_ppp_info2(const char *fmt, ...) {}
+int ip6addr_add_peer(int i, struct in6_addr *a, struct in6_addr *p) { return 0; }
+int ip6route_add(int i, const struct in6_addr *a, int p, const struct in6_addr *g, int m, uint32_t prio, const char *v) { return 0; }
+struct ipv6db_prefix_t *ipdb_get_ipv6_prefix(struct ap_session *s) { return NULL; }
+
+static ssize_t capture_send(int fd, const void *buf, size_t len, int flags,
+ const struct sockaddr *addr, socklen_t addrlen)
+{
+ assert(len <= sizeof(response));
+ memcpy(response, buf, len);
+ response_len = len;
+ sent++;
+ return len;
+}
+
+static size_t option(unsigned char *p, unsigned int code, const void *data, size_t len)
+{
+ u_write_be16(p, code);
+ u_write_be16(p + 2, len);
+ if (len)
+ memcpy(p + 4, data, len);
+ return len + 4;
+}
+
+static size_t request(unsigned char *buf)
+{
+ static const unsigned char duid[] = {0, 3, 0, 1, 1, 2, 3, 4, 5, 6};
+ size_t len = 4;
+ memset(buf, 0, 4);
+ buf[0] = D6_SOLICIT;
+ len += option(buf + len, D6_OPTION_CLIENTID, duid, sizeof(duid));
+ len += option(buf + len, D6_OPTION_SERVERID, duid, sizeof(duid));
+ return len;
+}
+
+static size_t relay(unsigned char *buf, size_t len)
+{
+ size_t hdrlen = sizeof(struct dhcpv6_relay_hdr);
+ memmove(buf + hdrlen + 4, buf, len);
+ memset(buf, 0, hdrlen);
+ buf[0] = D6_RELAY_FORW;
+ u_write_be16(buf + hdrlen, D6_OPTION_RELAY_MSG);
+ u_write_be16(buf + hdrlen + 2, len);
+ return len + hdrlen + 4;
+}
+
+int main(void)
+{
+ unsigned char buf[16000], oro[4000], name[256];
+ const unsigned char valid_name[] = {2, 'g', 'w', 0};
+ struct dhcpv6_packet *pkt, *reply;
+ struct dhcpv6_option *opt;
+ struct ap_session ses = {0};
+ struct dhcpv6_pd pd = {0};
+ struct ap_net test_net = {.sendto = capture_send};
+ size_t len, base, avail;
+ int i;
+
+ net = &test_net;
+ base = request(buf);
+ len = base + option(buf + base, D6_OPTION_RAPID_COMMIT, NULL, 0);
+ pkt = dhcpv6_packet_parse(buf, len);
+ assert(pkt && pkt->rapid_commit);
+ pkt->ses = &ses;
+ dhcpv6_send_reply(pkt, &pd, D6_REPLY);
+ assert(sent == 1);
+ reply = dhcpv6_packet_parse(response, response_len);
+ assert(reply && reply->rapid_commit);
+ dhcpv6_packet_free(reply);
+ dhcpv6_packet_free(pkt);
+ len += option(buf + len, D6_OPTION_RAPID_COMMIT, NULL, 0);
+ assert(!dhcpv6_packet_parse(buf, len));
+ len = base + option(buf + base, D6_OPTION_RAPID_COMMIT, "x", 1);
+ assert(!dhcpv6_packet_parse(buf, len));
+
+ len = base + option(buf + base, D6_OPTION_AFTR_NAME, valid_name, sizeof(valid_name));
+ pkt = dhcpv6_packet_parse(buf, len);
+ assert(pkt);
+ dhcpv6_packet_free(pkt);
+ len += option(buf + len, D6_OPTION_AFTR_NAME, valid_name, sizeof(valid_name));
+ assert(!dhcpv6_packet_parse(buf, len));
+ memset(name, 'x', sizeof(name));
+ for (i = 0; i < 5; i++) {
+ const unsigned char invalid[][4] = {{0,0,0,0}, {0xc0,1,1,0}, {4,'a','b',0}, {2,'a','b',1}, {1,'a',0,1}};
+ len = base + option(buf + base, D6_OPTION_AFTR_NAME, invalid[i], 4);
+ assert(!dhcpv6_packet_parse(buf, len));
+ }
+ len = base + option(buf + base, D6_OPTION_AFTR_NAME, name, sizeof(name));
+ assert(!dhcpv6_packet_parse(buf, len));
+
+ len = request(buf);
+ for (i = 0; i <= DHCPV6_HOP_COUNT_LIMIT; i++) {
+ pkt = dhcpv6_packet_parse(buf, len);
+ assert(pkt);
+ reply = dhcpv6_packet_alloc_reply(pkt, D6_REPLY);
+ assert(reply);
+ /* The last legal byte is writable; both allocators reject overflow. */
+ avail = (char *)(reply + 1) + 4096 - (char *)reply->endptr;
+ opt = dhcpv6_option_alloc(reply, 65000, avail - 4);
+ assert(opt);
+ memset(opt->hdr->data, 1, avail - 4);
+ assert(!dhcpv6_option_alloc(reply, 65000, 0));
+ assert(!dhcpv6_nested_option_alloc(reply, opt, 65000, 0));
+ assert(!dhcpv6_option_alloc(reply, 65000, -1));
+ dhcpv6_fill_relay_info(reply);
+ assert((char *)reply->endptr == (char *)(reply + 1) + 4096);
+ /* Verify Relay-Message wire length in every enclosing layer. */
+ if (i) {
+ struct dhcpv6_relay_hdr *h = (void *)reply->hdr;
+ struct dhcpv6_opt_hdr *o = (void *)h->data;
+ assert(ntohs(o->len) == (char *)reply->endptr - (char *)o->data);
+ }
+ dhcpv6_packet_free(reply);
+ dhcpv6_packet_free(pkt);
+ len = relay(buf, len);
+ }
+ assert(!dhcpv6_packet_parse(buf, len));
+
+ /* An ORO can amplify a small request into more than 4096 response bytes. */
+ conf_dns_count = 1;
+ for (i = 0; i < sizeof(oro); i += 2)
+ u_write_be16(oro + i, D6_OPTION_DNS_SERVERS);
+ len = request(buf);
+ len += option(buf + len, D6_OPTION_ORO, oro, sizeof(oro));
+ pkt = dhcpv6_packet_parse(buf, len);
+ assert(pkt);
+ pkt->ses = &ses;
+ sent = 0;
+ dhcpv6_send_reply(pkt, &pd, D6_REPLY);
+ assert(!sent);
+ dhcpv6_send_reply2(pkt, &pd, D6_REPLY);
+ assert(!sent);
+ dhcpv6_packet_free(pkt);
+ puts("DHCPv6 safeguards: PASS");
+ return 0;
+}
+void build_ip6_addr(struct ipv6db_addr_t *a, uint64_t id, struct in6_addr *addr) { *addr = a->addr; }
+int ip6addr_add(int i, struct in6_addr *a, int p) { return 0; }