]> git.proxmox.com Git - mirror_qemu.git/blame - slirp/src/dhcpv6.c
Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging
[mirror_qemu.git] / slirp / src / dhcpv6.c
CommitLineData
7b143999
TH
1/*
2 * SLIRP stateless DHCPv6
3 *
4 * We only support stateless DHCPv6, e.g. for network booting.
5 * See RFC 3315, RFC 3736, RFC 3646 and RFC 5970 for details.
6 *
7 * Copyright 2016 Thomas Huth, Red Hat Inc.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License,
12 * or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, see <http://www.gnu.org/licenses/>.
21 */
22
7b143999
TH
23#include "slirp.h"
24#include "dhcpv6.h"
25
26/* DHCPv6 message types */
27#define MSGTYPE_REPLY 7
28#define MSGTYPE_INFO_REQUEST 11
29
30/* DHCPv6 option types */
31#define OPTION_CLIENTID 1
32#define OPTION_IAADDR 5
33#define OPTION_ORO 6
34#define OPTION_DNS_SERVERS 23
35#define OPTION_BOOTFILE_URL 59
36
37struct requested_infos {
38 uint8_t *client_id;
39 int client_id_len;
40 bool want_dns;
41 bool want_boot_url;
42};
43
44/**
45 * Analyze the info request message sent by the client to see what data it
46 * provided and what it wants to have. The information is gathered in the
47 * "requested_infos" struct. Note that client_id (if provided) points into
48 * the odata region, thus the caller must keep odata valid as long as it
49 * needs to access the requested_infos struct.
50 */
2addc8fb 51static int dhcpv6_parse_info_request(Slirp *slirp, uint8_t *odata, int olen,
7b143999
TH
52 struct requested_infos *ri)
53{
54 int i, req_opt;
55
56 while (olen > 4) {
57 /* Parse one option */
58 int option = odata[0] << 8 | odata[1];
59 int len = odata[2] << 8 | odata[3];
60
61 if (len + 4 > olen) {
3e0fad3a 62 slirp->cb->guest_error("Guest sent bad DHCPv6 packet!", slirp->opaque);
7b143999
TH
63 return -E2BIG;
64 }
65
66 switch (option) {
67 case OPTION_IAADDR:
68 /* According to RFC3315, we must discard requests with IA option */
69 return -EINVAL;
70 case OPTION_CLIENTID:
71 if (len > 256) {
72 /* Avoid very long IDs which could cause problems later */
73 return -E2BIG;
74 }
75 ri->client_id = odata + 4;
76 ri->client_id_len = len;
77 break;
78 case OPTION_ORO: /* Option request option */
79 if (len & 1) {
80 return -EINVAL;
81 }
82 /* Check which options the client wants to have */
83 for (i = 0; i < len; i += 2) {
84 req_opt = odata[4 + i] << 8 | odata[4 + i + 1];
85 switch (req_opt) {
86 case OPTION_DNS_SERVERS:
87 ri->want_dns = true;
88 break;
89 case OPTION_BOOTFILE_URL:
90 ri->want_boot_url = true;
91 break;
92 default:
226ea7a9 93 DEBUG_MISC("dhcpv6: Unsupported option request %d",
2afbb788 94 req_opt);
7b143999
TH
95 }
96 }
97 break;
98 default:
226ea7a9 99 DEBUG_MISC("dhcpv6 info req: Unsupported option %d, len=%d",
2afbb788 100 option, len);
7b143999
TH
101 }
102
103 odata += len + 4;
104 olen -= len + 4;
105 }
106
107 return 0;
108}
109
110
111/**
112 * Handle information request messages
113 */
114static void dhcpv6_info_request(Slirp *slirp, struct sockaddr_in6 *srcsas,
115 uint32_t xid, uint8_t *odata, int olen)
116{
117 struct requested_infos ri = { NULL };
118 struct sockaddr_in6 sa6, da6;
119 struct mbuf *m;
120 uint8_t *resp;
121
2addc8fb 122 if (dhcpv6_parse_info_request(slirp, odata, olen, &ri) < 0) {
7b143999
TH
123 return;
124 }
125
126 m = m_get(slirp);
127 if (!m) {
128 return;
129 }
130 memset(m->m_data, 0, m->m_size);
131 m->m_data += IF_MAXLINKHDR;
132 resp = (uint8_t *)m->m_data + sizeof(struct ip6) + sizeof(struct udphdr);
133
134 /* Fill in response */
135 *resp++ = MSGTYPE_REPLY;
136 *resp++ = (uint8_t)(xid >> 16);
137 *resp++ = (uint8_t)(xid >> 8);
138 *resp++ = (uint8_t)xid;
139
140 if (ri.client_id) {
141 *resp++ = OPTION_CLIENTID >> 8; /* option-code high byte */
142 *resp++ = OPTION_CLIENTID; /* option-code low byte */
143 *resp++ = ri.client_id_len >> 8; /* option-len high byte */
144 *resp++ = ri.client_id_len; /* option-len low byte */
145 memcpy(resp, ri.client_id, ri.client_id_len);
146 resp += ri.client_id_len;
147 }
148 if (ri.want_dns) {
149 *resp++ = OPTION_DNS_SERVERS >> 8; /* option-code high byte */
150 *resp++ = OPTION_DNS_SERVERS; /* option-code low byte */
151 *resp++ = 0; /* option-len high byte */
152 *resp++ = 16; /* option-len low byte */
153 memcpy(resp, &slirp->vnameserver_addr6, 16);
154 resp += 16;
155 }
156 if (ri.want_boot_url) {
157 uint8_t *sa = slirp->vhost_addr6.s6_addr;
158 int slen, smaxlen;
159
160 *resp++ = OPTION_BOOTFILE_URL >> 8; /* option-code high byte */
161 *resp++ = OPTION_BOOTFILE_URL; /* option-code low byte */
162 smaxlen = (uint8_t *)m->m_data + IF_MTU - (resp + 2);
163 slen = snprintf((char *)resp + 2, smaxlen,
164 "tftp://[%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
165 "%02x%02x:%02x%02x:%02x%02x:%02x%02x]/%s",
166 sa[0], sa[1], sa[2], sa[3], sa[4], sa[5], sa[6], sa[7],
167 sa[8], sa[9], sa[10], sa[11], sa[12], sa[13], sa[14],
168 sa[15], slirp->bootp_filename);
893dcdbf 169 slen = MIN(slen, smaxlen);
7b143999
TH
170 *resp++ = slen >> 8; /* option-len high byte */
171 *resp++ = slen; /* option-len low byte */
172 resp += slen;
173 }
174
175 sa6.sin6_addr = slirp->vhost_addr6;
176 sa6.sin6_port = DHCPV6_SERVER_PORT;
177 da6.sin6_addr = srcsas->sin6_addr;
178 da6.sin6_port = srcsas->sin6_port;
179 m->m_data += sizeof(struct ip6) + sizeof(struct udphdr);
180 m->m_len = resp - (uint8_t *)m->m_data;
181 udp6_output(NULL, m, &sa6, &da6);
182}
183
184/**
185 * Handle DHCPv6 messages sent by the client
186 */
187void dhcpv6_input(struct sockaddr_in6 *srcsas, struct mbuf *m)
188{
189 uint8_t *data = (uint8_t *)m->m_data + sizeof(struct udphdr);
190 int data_len = m->m_len - sizeof(struct udphdr);
191 uint32_t xid;
192
193 if (data_len < 4) {
194 return;
195 }
196
197 xid = ntohl(*(uint32_t *)data) & 0xffffff;
198
199 switch (data[0]) {
200 case MSGTYPE_INFO_REQUEST:
201 dhcpv6_info_request(m->slirp, srcsas, xid, &data[4], data_len - 4);
202 break;
203 default:
226ea7a9 204 DEBUG_MISC("dhcpv6_input: Unsupported message type 0x%x", data[0]);
7b143999
TH
205 }
206}