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#include "slirp.h"
42#include "dhcpv6.h"
43
44
45#define MSGTYPE_REPLY 7
46#define MSGTYPE_INFO_REQUEST 11
47
48
49#define OPTION_CLIENTID 1
50#define OPTION_IAADDR 5
51#define OPTION_ORO 6
52#define OPTION_DNS_SERVERS 23
53#define OPTION_BOOTFILE_URL 59
54
55struct requested_infos {
56 uint8_t *client_id;
57 int client_id_len;
58 bool want_dns;
59 bool want_boot_url;
60};
61
62
63
64
65
66
67
68
69static int dhcpv6_parse_info_request(Slirp *slirp, uint8_t *odata, int olen,
70 struct requested_infos *ri)
71{
72 int i, req_opt;
73
74 while (olen > 4) {
75
76 int option = odata[0] << 8 | odata[1];
77 int len = odata[2] << 8 | odata[3];
78
79 if (len + 4 > olen) {
80 slirp->cb->guest_error("Guest sent bad DHCPv6 packet!", slirp->opaque);
81 return -E2BIG;
82 }
83
84 switch (option) {
85 case OPTION_IAADDR:
86
87 return -EINVAL;
88 case OPTION_CLIENTID:
89 if (len > 256) {
90
91 return -E2BIG;
92 }
93 ri->client_id = odata + 4;
94 ri->client_id_len = len;
95 break;
96 case OPTION_ORO:
97 if (len & 1) {
98 return -EINVAL;
99 }
100
101 for (i = 0; i < len; i += 2) {
102 req_opt = odata[4 + i] << 8 | odata[4 + i + 1];
103 switch (req_opt) {
104 case OPTION_DNS_SERVERS:
105 ri->want_dns = true;
106 break;
107 case OPTION_BOOTFILE_URL:
108 ri->want_boot_url = true;
109 break;
110 default:
111 DEBUG_MISC("dhcpv6: Unsupported option request %d",
112 req_opt);
113 }
114 }
115 break;
116 default:
117 DEBUG_MISC("dhcpv6 info req: Unsupported option %d, len=%d",
118 option, len);
119 }
120
121 odata += len + 4;
122 olen -= len + 4;
123 }
124
125 return 0;
126}
127
128
129
130
131
132static void dhcpv6_info_request(Slirp *slirp, struct sockaddr_in6 *srcsas,
133 uint32_t xid, uint8_t *odata, int olen)
134{
135 struct requested_infos ri = { NULL };
136 struct sockaddr_in6 sa6, da6;
137 struct mbuf *m;
138 uint8_t *resp;
139
140 if (dhcpv6_parse_info_request(slirp, odata, olen, &ri) < 0) {
141 return;
142 }
143
144 m = m_get(slirp);
145 if (!m) {
146 return;
147 }
148 memset(m->m_data, 0, m->m_size);
149 m->m_data += IF_MAXLINKHDR;
150 resp = (uint8_t *)m->m_data + sizeof(struct ip6) + sizeof(struct udphdr);
151
152
153 *resp++ = MSGTYPE_REPLY;
154 *resp++ = (uint8_t)(xid >> 16);
155 *resp++ = (uint8_t)(xid >> 8);
156 *resp++ = (uint8_t)xid;
157
158 if (ri.client_id) {
159 *resp++ = OPTION_CLIENTID >> 8;
160 *resp++ = OPTION_CLIENTID;
161 *resp++ = ri.client_id_len >> 8;
162 *resp++ = ri.client_id_len;
163 memcpy(resp, ri.client_id, ri.client_id_len);
164 resp += ri.client_id_len;
165 }
166 if (ri.want_dns) {
167 *resp++ = OPTION_DNS_SERVERS >> 8;
168 *resp++ = OPTION_DNS_SERVERS;
169 *resp++ = 0;
170 *resp++ = 16;
171 memcpy(resp, &slirp->vnameserver_addr6, 16);
172 resp += 16;
173 }
174 if (ri.want_boot_url) {
175 uint8_t *sa = slirp->vhost_addr6.s6_addr;
176 int slen, smaxlen;
177
178 *resp++ = OPTION_BOOTFILE_URL >> 8;
179 *resp++ = OPTION_BOOTFILE_URL;
180 smaxlen = (uint8_t *)m->m_data + IF_MTU - (resp + 2);
181 slen = snprintf((char *)resp + 2, smaxlen,
182 "tftp://[%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
183 "%02x%02x:%02x%02x:%02x%02x:%02x%02x]/%s",
184 sa[0], sa[1], sa[2], sa[3], sa[4], sa[5], sa[6], sa[7],
185 sa[8], sa[9], sa[10], sa[11], sa[12], sa[13], sa[14],
186 sa[15], slirp->bootp_filename);
187 slen = MIN(slen, smaxlen);
188 *resp++ = slen >> 8;
189 *resp++ = slen;
190 resp += slen;
191 }
192
193 sa6.sin6_addr = slirp->vhost_addr6;
194 sa6.sin6_port = DHCPV6_SERVER_PORT;
195 da6.sin6_addr = srcsas->sin6_addr;
196 da6.sin6_port = srcsas->sin6_port;
197 m->m_data += sizeof(struct ip6) + sizeof(struct udphdr);
198 m->m_len = resp - (uint8_t *)m->m_data;
199 udp6_output(NULL, m, &sa6, &da6);
200}
201
202
203
204
205void dhcpv6_input(struct sockaddr_in6 *srcsas, struct mbuf *m)
206{
207 uint8_t *data = (uint8_t *)m->m_data + sizeof(struct udphdr);
208 int data_len = m->m_len - sizeof(struct udphdr);
209 uint32_t xid;
210
211 if (data_len < 4) {
212 return;
213 }
214
215 xid = ntohl(*(uint32_t *)data) & 0xffffff;
216
217 switch (data[0]) {
218 case MSGTYPE_INFO_REQUEST:
219 dhcpv6_info_request(m->slirp, srcsas, xid, &data[4], data_len - 4);
220 break;
221 default:
222 DEBUG_MISC("dhcpv6_input: Unsupported message type 0x%x", data[0]);
223 }
224}
225