1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
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; }
|