mirror of
				https://github.com/qemu/qemu.git
				synced 2025-10-30 19:15:42 +00:00 
			
		
		
		
	 226ea7a964
			
		
	
	
		226ea7a964
		
	
	
	
	
		
			
			Make slirp use GLib logging, instead of fprintf(), so that applications can filter log, process it etc. With recent versions of glib, G_MESSAGES_DEBUG must be set to "all" or "Slirp" to see slirp debug messages. Reformat DEBUG_MISC & DEBUG_ERROR calls to not need \n ending. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
		
			
				
	
	
		
			209 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			209 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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 "qemu/osdep.h"
 | |
| #include "qemu/log.h"
 | |
| #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!");
 | |
|             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]);
 | |
|     }
 | |
| }
 |