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