diff options
| author | Peter Maydell <peter.maydell@linaro.org> | 2019-03-08 11:55:53 +0000 |
|---|---|---|
| committer | Peter Maydell <peter.maydell@linaro.org> | 2019-03-08 11:55:53 +0000 |
| commit | 6bbbe16a02ecf631fffb4a339e1c662d60a1e529 (patch) | |
| tree | 5e847e5af195f8399a8d18ba88bc91e2d06aac57 /slirp/src/dhcpv6.c | |
| parent | e56d931a9ddee7230f647a25ada33ee19d26aa6d (diff) | |
| parent | be1911ff7504be95d5cf2c18bc99ce07246a91e5 (diff) | |
| download | focaccia-qemu-6bbbe16a02ecf631fffb4a339e1c662d60a1e529.tar.gz focaccia-qemu-6bbbe16a02ecf631fffb4a339e1c662d60a1e529.zip | |
Merge remote-tracking branch 'remotes/thibault/tags/samuel-thibault' into staging
Slirp updates Greg Kurz (1): slirp: Fix build with gcc 9 Marc-André Lureau (7): slirp: adapt a subset of QEMU vmstate code slirp: use libslirp migration code slirp: use "slirp_" prefix for inet_aton() win32 implementation slirp: move sources to src/ subdirectory slirp: add a standalone Makefile build-sys: link with slirp as an external project slirp: remove QEMU Makefile.objs Samuel Thibault (2): slirp: fix big/little endian conversion in ident protocol slirp: Mark pieces missing IPv6 support Vic Lee (1): slirp: check for ioctlsocket error and 0-length udp payload. William Bowling (1): slirp: check sscanf result when emulating ident # gpg: Signature made Thu 07 Mar 2019 11:51:20 GMT # gpg: using RSA key E61DBB15D4172BDEC97E92D9DB550E89F0FA54F3 # gpg: Good signature from "Samuel Thibault <samuel.thibault@aquilenet.fr>" [unknown] # gpg: aka "Samuel Thibault <sthibault@debian.org>" [marginal] # gpg: aka "Samuel Thibault <samuel.thibault@gnu.org>" [unknown] # gpg: aka "Samuel Thibault <samuel.thibault@inria.fr>" [marginal] # gpg: aka "Samuel Thibault <samuel.thibault@labri.fr>" [marginal] # gpg: aka "Samuel Thibault <samuel.thibault@ens-lyon.org>" [marginal] # gpg: aka "Samuel Thibault <samuel.thibault@u-bordeaux.fr>" [unknown] # gpg: WARNING: This key is not certified with sufficiently trusted signatures! # gpg: It is not certain that the signature belongs to the owner. # Primary key fingerprint: 900C B024 B679 31D4 0F82 304B D017 8C76 7D06 9EE6 # Subkey fingerprint: E61D BB15 D417 2BDE C97E 92D9 DB55 0E89 F0FA 54F3 * remotes/thibault/tags/samuel-thibault: slirp: remove QEMU Makefile.objs build-sys: link with slirp as an external project slirp: add a standalone Makefile slirp: move sources to src/ subdirectory slirp: use "slirp_" prefix for inet_aton() win32 implementation slirp: use libslirp migration code slirp: adapt a subset of QEMU vmstate code slirp: Mark pieces missing IPv6 support slirp: fix big/little endian conversion in ident protocol slirp: check sscanf result when emulating ident slirp: check for ioctlsocket error and 0-length udp payload. slirp: Fix build with gcc 9 Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'slirp/src/dhcpv6.c')
| -rw-r--r-- | slirp/src/dhcpv6.c | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/slirp/src/dhcpv6.c b/slirp/src/dhcpv6.c new file mode 100644 index 0000000000..e655c7d5b1 --- /dev/null +++ b/slirp/src/dhcpv6.c @@ -0,0 +1,206 @@ +/* + * SLIRP stateless DHCPv6 + * + * We only support stateless DHCPv6, e.g. for network booting. + * See RFC 3315, RFC 3736, RFC 3646 and RFC 5970 for details. + * + * Copyright 2016 Thomas Huth, Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "slirp.h" +#include "dhcpv6.h" + +/* DHCPv6 message types */ +#define MSGTYPE_REPLY 7 +#define MSGTYPE_INFO_REQUEST 11 + +/* DHCPv6 option types */ +#define OPTION_CLIENTID 1 +#define OPTION_IAADDR 5 +#define OPTION_ORO 6 +#define OPTION_DNS_SERVERS 23 +#define OPTION_BOOTFILE_URL 59 + +struct requested_infos { + uint8_t *client_id; + int client_id_len; + bool want_dns; + bool want_boot_url; +}; + +/** + * Analyze the info request message sent by the client to see what data it + * provided and what it wants to have. The information is gathered in the + * "requested_infos" struct. Note that client_id (if provided) points into + * the odata region, thus the caller must keep odata valid as long as it + * needs to access the requested_infos struct. + */ +static int dhcpv6_parse_info_request(Slirp *slirp, uint8_t *odata, int olen, + struct requested_infos *ri) +{ + int i, req_opt; + + while (olen > 4) { + /* Parse one option */ + int option = odata[0] << 8 | odata[1]; + int len = odata[2] << 8 | odata[3]; + + if (len + 4 > olen) { + slirp->cb->guest_error("Guest sent bad DHCPv6 packet!", slirp->opaque); + return -E2BIG; + } + + switch (option) { + case OPTION_IAADDR: + /* According to RFC3315, we must discard requests with IA option */ + return -EINVAL; + case OPTION_CLIENTID: + if (len > 256) { + /* Avoid very long IDs which could cause problems later */ + return -E2BIG; + } + ri->client_id = odata + 4; + ri->client_id_len = len; + break; + case OPTION_ORO: /* Option request option */ + if (len & 1) { + return -EINVAL; + } + /* Check which options the client wants to have */ + for (i = 0; i < len; i += 2) { + req_opt = odata[4 + i] << 8 | odata[4 + i + 1]; + switch (req_opt) { + case OPTION_DNS_SERVERS: + ri->want_dns = true; + break; + case OPTION_BOOTFILE_URL: + ri->want_boot_url = true; + break; + default: + DEBUG_MISC("dhcpv6: Unsupported option request %d", + req_opt); + } + } + break; + default: + DEBUG_MISC("dhcpv6 info req: Unsupported option %d, len=%d", + option, len); + } + + odata += len + 4; + olen -= len + 4; + } + + return 0; +} + + +/** + * Handle information request messages + */ +static void dhcpv6_info_request(Slirp *slirp, struct sockaddr_in6 *srcsas, + uint32_t xid, uint8_t *odata, int olen) +{ + struct requested_infos ri = { NULL }; + struct sockaddr_in6 sa6, da6; + struct mbuf *m; + uint8_t *resp; + + if (dhcpv6_parse_info_request(slirp, odata, olen, &ri) < 0) { + return; + } + + m = m_get(slirp); + if (!m) { + return; + } + memset(m->m_data, 0, m->m_size); + m->m_data += IF_MAXLINKHDR; + resp = (uint8_t *)m->m_data + sizeof(struct ip6) + sizeof(struct udphdr); + + /* Fill in response */ + *resp++ = MSGTYPE_REPLY; + *resp++ = (uint8_t)(xid >> 16); + *resp++ = (uint8_t)(xid >> 8); + *resp++ = (uint8_t)xid; + + if (ri.client_id) { + *resp++ = OPTION_CLIENTID >> 8; /* option-code high byte */ + *resp++ = OPTION_CLIENTID; /* option-code low byte */ + *resp++ = ri.client_id_len >> 8; /* option-len high byte */ + *resp++ = ri.client_id_len; /* option-len low byte */ + memcpy(resp, ri.client_id, ri.client_id_len); + resp += ri.client_id_len; + } + if (ri.want_dns) { + *resp++ = OPTION_DNS_SERVERS >> 8; /* option-code high byte */ + *resp++ = OPTION_DNS_SERVERS; /* option-code low byte */ + *resp++ = 0; /* option-len high byte */ + *resp++ = 16; /* option-len low byte */ + memcpy(resp, &slirp->vnameserver_addr6, 16); + resp += 16; + } + if (ri.want_boot_url) { + uint8_t *sa = slirp->vhost_addr6.s6_addr; + int slen, smaxlen; + + *resp++ = OPTION_BOOTFILE_URL >> 8; /* option-code high byte */ + *resp++ = OPTION_BOOTFILE_URL; /* option-code low byte */ + smaxlen = (uint8_t *)m->m_data + IF_MTU - (resp + 2); + slen = snprintf((char *)resp + 2, smaxlen, + "tftp://[%02x%02x:%02x%02x:%02x%02x:%02x%02x:" + "%02x%02x:%02x%02x:%02x%02x:%02x%02x]/%s", + sa[0], sa[1], sa[2], sa[3], sa[4], sa[5], sa[6], sa[7], + sa[8], sa[9], sa[10], sa[11], sa[12], sa[13], sa[14], + sa[15], slirp->bootp_filename); + slen = MIN(slen, smaxlen); + *resp++ = slen >> 8; /* option-len high byte */ + *resp++ = slen; /* option-len low byte */ + resp += slen; + } + + sa6.sin6_addr = slirp->vhost_addr6; + sa6.sin6_port = DHCPV6_SERVER_PORT; + da6.sin6_addr = srcsas->sin6_addr; + da6.sin6_port = srcsas->sin6_port; + m->m_data += sizeof(struct ip6) + sizeof(struct udphdr); + m->m_len = resp - (uint8_t *)m->m_data; + udp6_output(NULL, m, &sa6, &da6); +} + +/** + * Handle DHCPv6 messages sent by the client + */ +void dhcpv6_input(struct sockaddr_in6 *srcsas, struct mbuf *m) +{ + uint8_t *data = (uint8_t *)m->m_data + sizeof(struct udphdr); + int data_len = m->m_len - sizeof(struct udphdr); + uint32_t xid; + + if (data_len < 4) { + return; + } + + xid = ntohl(*(uint32_t *)data) & 0xffffff; + + switch (data[0]) { + case MSGTYPE_INFO_REQUEST: + dhcpv6_info_request(m->slirp, srcsas, xid, &data[4], data_len - 4); + break; + default: + DEBUG_MISC("dhcpv6_input: Unsupported message type 0x%x", data[0]); + } +} |