2 * SLIRP stateless DHCPv6
4 * We only support stateless DHCPv6, e.g. for network booting.
5 * See RFC 3315, RFC 3736, RFC 3646 and RFC 5970 for details.
7 * Copyright 2016 Thomas Huth, Red Hat Inc.
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.
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.
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/>.
23 #include "qemu/osdep.h"
28 /* DHCPv6 message types */
29 #define MSGTYPE_REPLY 7
30 #define MSGTYPE_INFO_REQUEST 11
32 /* DHCPv6 option types */
33 #define OPTION_CLIENTID 1
34 #define OPTION_IAADDR 5
36 #define OPTION_DNS_SERVERS 23
37 #define OPTION_BOOTFILE_URL 59
39 struct requested_infos
{
47 * Analyze the info request message sent by the client to see what data it
48 * provided and what it wants to have. The information is gathered in the
49 * "requested_infos" struct. Note that client_id (if provided) points into
50 * the odata region, thus the caller must keep odata valid as long as it
51 * needs to access the requested_infos struct.
53 static int dhcpv6_parse_info_request(uint8_t *odata
, int olen
,
54 struct requested_infos
*ri
)
59 /* Parse one option */
60 int option
= odata
[0] << 8 | odata
[1];
61 int len
= odata
[2] << 8 | odata
[3];
64 qemu_log_mask(LOG_GUEST_ERROR
, "Guest sent bad DHCPv6 packet!\n");
70 /* According to RFC3315, we must discard requests with IA option */
74 /* Avoid very long IDs which could cause problems later */
77 ri
->client_id
= odata
+ 4;
78 ri
->client_id_len
= len
;
80 case OPTION_ORO
: /* Option request option */
84 /* Check which options the client wants to have */
85 for (i
= 0; i
< len
; i
+= 2) {
86 req_opt
= odata
[4 + i
] << 8 | odata
[4 + i
+ 1];
88 case OPTION_DNS_SERVERS
:
91 case OPTION_BOOTFILE_URL
:
92 ri
->want_boot_url
= true;
95 DEBUG_MISC((dfd
, "dhcpv6: Unsupported option request %d\n",
101 DEBUG_MISC((dfd
, "dhcpv6 info req: Unsupported option %d, len=%d\n",
114 * Handle information request messages
116 static void dhcpv6_info_request(Slirp
*slirp
, struct sockaddr_in6
*srcsas
,
117 uint32_t xid
, uint8_t *odata
, int olen
)
119 struct requested_infos ri
= { NULL
};
120 struct sockaddr_in6 sa6
, da6
;
124 if (dhcpv6_parse_info_request(odata
, olen
, &ri
) < 0) {
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
);
136 /* Fill in response */
137 *resp
++ = MSGTYPE_REPLY
;
138 *resp
++ = (uint8_t)(xid
>> 16);
139 *resp
++ = (uint8_t)(xid
>> 8);
140 *resp
++ = (uint8_t)xid
;
143 *resp
++ = OPTION_CLIENTID
>> 8; /* option-code high byte */
144 *resp
++ = OPTION_CLIENTID
; /* option-code low byte */
145 *resp
++ = ri
.client_id_len
>> 8; /* option-len high byte */
146 *resp
++ = ri
.client_id_len
; /* option-len low byte */
147 memcpy(resp
, ri
.client_id
, ri
.client_id_len
);
148 resp
+= ri
.client_id_len
;
151 *resp
++ = OPTION_DNS_SERVERS
>> 8; /* option-code high byte */
152 *resp
++ = OPTION_DNS_SERVERS
; /* option-code low byte */
153 *resp
++ = 0; /* option-len high byte */
154 *resp
++ = 16; /* option-len low byte */
155 memcpy(resp
, &slirp
->vnameserver_addr6
, 16);
158 if (ri
.want_boot_url
) {
159 uint8_t *sa
= slirp
->vhost_addr6
.s6_addr
;
162 *resp
++ = OPTION_BOOTFILE_URL
>> 8; /* option-code high byte */
163 *resp
++ = OPTION_BOOTFILE_URL
; /* option-code low byte */
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; /* option-len high byte */
173 *resp
++ = slen
; /* option-len low byte */
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
);
187 * Handle DHCPv6 messages sent by the client
189 void dhcpv6_input(struct sockaddr_in6
*srcsas
, struct mbuf
*m
)
191 uint8_t *data
= (uint8_t *)m
->m_data
+ sizeof(struct udphdr
);
192 int data_len
= m
->m_len
- sizeof(struct udphdr
);
199 xid
= ntohl(*(uint32_t *)data
) & 0xffffff;
202 case MSGTYPE_INFO_REQUEST
:
203 dhcpv6_info_request(m
->slirp
, srcsas
, xid
, &data
[4], data_len
- 4);
206 DEBUG_MISC((dfd
, "dhcpv6_input: Unsupported message type 0x%x\n",