xref: /illumos-kvm-cmd/net/vnic-dhcp.c (revision 844e23ee)
1 /*
2  * QEMU System Emulator
3  * Solaris VNIC DHCP support
4  *
5  * Copyright 2016 Joyent, Inc.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  */
25 
26 #include <stdio.h>
27 #include <arpa/inet.h>
28 #include <net/if.h>
29 #include <net/if_arp.h>
30 #include <netinet/if_ether.h>
31 
32 #include "net/vnic-dhcp.h"
33 
34 #include <slirp.h>  /* GodDAMN */
35 #include "ip.h"
36 #include "udp.h"
37 #include "qemu-error.h"
38 
39 /* from slirp.c: */
40 #define ETH_ALEN 6
41 #define ETH_HLEN 14
42 #define ETH_P_IP	0x0800		/* Internet Protocol packet	*/
43 #define ETH_P_ARP	0x0806		/* Address Resolution packet	*/
44 /* from bootp.c: */
45 #define LEASE_TIME (24 * 3600)
46 /* from slirp/debug.h: */
47 #define dfd stderr
48 /* from bootp.c and modified: */
49 #ifdef VNIC_DHCP_DEBUG
50 #define DPRINTF(fmt, ...) \
51 do if (VNIC_DHCP_DEBUG) { fprintf(dfd, fmt, ##  __VA_ARGS__); fflush(dfd); } \
52     while (0)
53 #else
54 #define DPRINTF(fmt, ...) do {} while(0)
55 #endif
56 /* from cksum.c: */
57 #define ADDCARRY(x)  (x > 65535 ? x -= 65535 : x)
58 #define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; \
59 	(void)ADDCARRY(sum);}
60 
61 /* from slirp.c: */
62 struct ethhdr
63 {
64 	unsigned char	h_dest[ETH_ALEN];	/* destination eth addr	*/
65 	unsigned char	h_source[ETH_ALEN];	/* source ether addr	*/
66 	unsigned short	h_proto;		/* packet type ID field	*/
67 };
68 
69 /* from bootp.c: */
70 static const uint8_t rfc1533_cookie[] = { RFC1533_COOKIE };
71 /* emulated hosts use the MAC addr 52:55:IP:IP:IP:IP */
72 static const uint8_t special_ethaddr[6] = {
73 	0x52, 0x55, 0x55, 0x55, 0x55, 0x55
74 };
75 
76 static void
print_dhcp_info(const struct bootp_t * bp)77 print_dhcp_info(const struct bootp_t *bp)
78 {
79 	char ip_str[INET_ADDRSTRLEN];
80 	char *macaddr;
81 	DPRINTF("  bp->bp_op=%d\n", bp->bp_op);
82 
83 	inet_ntop(AF_INET, &(bp->ip.ip_src), ip_str, INET_ADDRSTRLEN);
84 	DPRINTF("  bp->ip_src=%s\n", ip_str);
85 	inet_ntop(AF_INET, &(bp->ip.ip_dst), ip_str, INET_ADDRSTRLEN);
86 	DPRINTF("  bp->ip_dst=%s\n", ip_str);
87 	DPRINTF("  bp->udp.uh_sport=%d\n", ntohs(bp->udp.uh_sport));
88 	DPRINTF("  bp->udp.uh_dport=%d\n", ntohs(bp->udp.uh_dport));
89 
90 	macaddr = _link_ntoa(bp->bp_hwaddr, ip_str, ETH_ALEN, 0);
91 	DPRINTF("  bp->bp_hwaddr=%s\n", macaddr);
92 	free(macaddr);
93 
94 	inet_ntop(AF_INET, &(bp->bp_yiaddr), ip_str, INET_ADDRSTRLEN);
95 	DPRINTF("  bp->bp_yiaddr=%s\n", ip_str);
96 	inet_ntop(AF_INET, &(bp->bp_siaddr), ip_str, INET_ADDRSTRLEN);
97 	DPRINTF("  bp->bp_siaddr=%s\n", ip_str);
98 	inet_ntop(AF_INET, &(bp->bp_giaddr), ip_str, INET_ADDRSTRLEN);
99 	DPRINTF("  bp->bp_giaddr=%s\n", ip_str);
100 	inet_ntop(AF_INET, &(bp->bp_ciaddr), ip_str, INET_ADDRSTRLEN);
101 	DPRINTF("  bp->bp_ciaddr=%s\n", ip_str);
102 }
103 
104 /* from bootp.c: */
105 static void
dhcp_decode(const struct bootp_t * bp,int * pmsg_type,struct in_addr * preq_addr)106 dhcp_decode(const struct bootp_t *bp, int *pmsg_type,
107     struct in_addr *preq_addr)
108 {
109 	const uint8_t *p, *p_end;
110 	int len, tag;
111 
112 	*pmsg_type = 0;
113 	preq_addr->s_addr = htonl(0L);
114 
115 	p = bp->bp_vend;
116 	p_end = p + DHCP_OPT_LEN;
117 	if (memcmp(p, rfc1533_cookie, 4) != 0)
118 		return;
119 	p += 4;
120 	while (p < p_end) {
121 		tag = p[0];
122 		if (tag == RFC1533_PAD) {
123 			p++;
124 		} else if (tag == RFC1533_END) {
125 			break;
126 		} else {
127 			p++;
128 			if (p >= p_end)
129 				break;
130 			len = *p++;
131 			DPRINTF("dhcp: tag=%d len=%d\n", tag, len);
132 
133 			switch(tag) {
134 			case RFC2132_MSG_TYPE:
135 				if (len >= 1)
136 					*pmsg_type = p[0];
137 				break;
138 			case RFC2132_REQ_ADDR:
139 				if (len >= 4) {
140 					memcpy(&(preq_addr->s_addr), p, 4);
141 				}
142 				break;
143 			default:
144 				break;
145 			}
146 			p += len;
147 		}
148 	}
149 	if (*pmsg_type == DHCPREQUEST && preq_addr->s_addr == htonl(0L) &&
150 	    bp->bp_ciaddr.s_addr) {
151 		memcpy(&(preq_addr->s_addr), &bp->bp_ciaddr, 4);
152 	}
153 }
154 
155 #if VNIC_DHCP_HEX_DUMP
156 /* from net.c: */
157 static void
hex_dump(FILE * f,const uint8_t * buf,int size)158 hex_dump(FILE *f, const uint8_t *buf, int size)
159 {
160 	int len, i, j, c;
161 
162 	for(i = 0; i < size; i += 16) {
163 		len = size - i;
164 		if (len > 16)
165 			len = 16;
166 		fprintf(f, "%08x ", i);
167 		for(j = 0; j < 16; j++) {
168 			if (j < len)
169 				fprintf(f, " %02x", buf[i + j]);
170 			else
171 				fprintf(f, "   ");
172 		}
173 		fprintf(f, " ");
174 		for(j = 0; j < len; j++) {
175 			c = buf[i + j];
176 			if (c < ' ' || c > '~')
177 				c = '.';
178 			fprintf(f, "%c", c);
179 		}
180 		fprintf(f, "\n");
181 	}
182 }
183 #endif
184 
185 
186 /* from cksum.c, and modified to work on non-mbufs: */
187 static int
cksum2(uint16_t * m,int len)188 cksum2(uint16_t *m, int len)
189 {
190 	register uint16_t *w = m;
191 	register int sum = 0;
192 	register int mlen = len;
193 	int byte_swapped = 0;
194 
195 	union {
196 		uint8_t  c[2];
197 		uint16_t s;
198 	} s_util;
199 	union {
200 		uint16_t s[2];
201 		uint32_t l;
202 	} l_util;
203 
204 #ifdef DEBUG
205 	len -= mlen;
206 #endif
207 	/*
208 	 * Force to even boundary.
209 	 */
210 	if ((1 & (long) w) && (mlen > 0)) {
211 		REDUCE;
212 		sum <<= 8;
213 		s_util.c[0] = *(uint8_t *)w;
214 		w = (uint16_t *)((int8_t *)w + 1);
215 		mlen--;
216 		byte_swapped = 1;
217 	}
218 	/*
219 	 * Unroll the loop to make overhead from
220 	 * branches &c small.
221 	 */
222 	while ((mlen -= 32) >= 0) {
223 		sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
224 		sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
225 		sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
226 		sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
227 		w += 16;
228 	}
229 	mlen += 32;
230 	while ((mlen -= 8) >= 0) {
231 		sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
232 		w += 4;
233 	}
234 	mlen += 8;
235 	if (mlen == 0 && byte_swapped == 0)
236 		goto cont;
237 	REDUCE;
238 	while ((mlen -= 2) >= 0) {
239 		sum += *w++;
240 	}
241 
242 	if (byte_swapped) {
243 		REDUCE;
244 		sum <<= 8;
245 		if (mlen == -1) {
246 			s_util.c[1] = *(uint8_t *)w;
247 			sum += s_util.s;
248 			mlen = 0;
249 		} else
250 
251 			mlen = -1;
252 	} else if (mlen == -1)
253 		s_util.c[0] = *(uint8_t *)w;
254 
255 cont:
256 #ifdef DEBUG
257 	if (len) {
258 		DPRINTF("cksum: out of data\n");
259 		DPRINTF(" len = %d\n", len);
260 	}
261 #endif
262 	if (mlen == -1) {
263 		/* The last mbuf has odd # of bytes. Follow the
264 	           standard (the odd byte may be shifted left by 8 bits
265 	           or not as determined by endian-ness of the machine) */
266 		s_util.c[1] = 0;
267 		sum += s_util.s;
268 	}
269 	REDUCE;
270 	return (~sum & 0xffff);
271 }
272 
273 #ifdef VNIC_DHCP_DEBUG
274 void
debug_eth_frame(const uint8_t * buf_p,size_t size)275 debug_eth_frame(const uint8_t *buf_p, size_t size)
276 {
277 	int proto;
278 	char ip_str[INET_ADDRSTRLEN];
279 	struct ip *ip;
280 	char *macaddr;
281 
282 	DPRINTF("ethernet frame: ");
283 #if VNIC_DHCP_HEX_DUMP
284 	DPRINTF("\n");
285 	hex_dump(dfd, buf_p, size);
286 #endif
287 
288 	if (size < ETH_HLEN) {
289 		DPRINTF("size %d < ETH_HLEN\n", (int)size);
290 	}
291 
292 	macaddr = _link_ntoa(((struct ethhdr *)buf_p)->h_source, ip_str,
293 	    ETH_ALEN, 0);
294 	DPRINTF("  src mac=%s", macaddr);
295 	free(macaddr);
296 
297 	macaddr = _link_ntoa(((struct ethhdr *)buf_p)->h_dest, ip_str,
298 	    ETH_ALEN, 0);
299 	DPRINTF("  dst mac=%s", macaddr);
300 	free(macaddr);
301 
302 	proto = ntohs(*(uint16_t *)(buf_p + 12));
303 	DPRINTF(" proto=%d ", proto);
304 	/* XXX: does this work with VLAN tags? */
305 
306 	switch (proto) {
307 	case ETH_P_ARP:
308 		DPRINTF("(ETH_P_ARP)\n");
309 		break;
310 	case ETH_P_IP:
311 		DPRINTF("(ETH_P_IP)\n");
312 		break;
313 	default:
314 		DPRINTF("(unknown)\n");
315 		break;
316 	}
317 
318 	if (size < sizeof(struct ip)) {
319 		DPRINTF("  (len < sizeof(struct ip))\n");
320 		return;
321 	}
322 
323 	ip = (struct ip *)(buf_p + ETH_HLEN);
324 	DPRINTF("  ip version: %d\n", ip->ip_v);
325 	if (ip->ip_v != IPVERSION)
326 		return;
327 
328 	inet_ntop(AF_INET, &(ip->ip_src), ip_str, INET_ADDRSTRLEN);
329 	DPRINTF("  ip_src=%s\n", ip_str);
330 	inet_ntop(AF_INET, &(ip->ip_dst), ip_str, INET_ADDRSTRLEN);
331 	DPRINTF("  ip_dst=%s\n", ip_str);
332 
333 	DPRINTF("  ip_proto=");
334 	switch (ip->ip_p) {
335 	case IPPROTO_TCP:
336 		DPRINTF("IPPROTO_TCP\n");
337 		break;
338 	case IPPROTO_UDP:
339 		DPRINTF("IPPROTO_UDP\n");
340 		break;
341 	case IPPROTO_ICMP:
342 		DPRINTF("IPPROTO_ICMP\n");
343 		break;
344 	default:
345 		DPRINTF("unknown protocol %d\n", ip->ip_p);
346 	}
347 
348 	if (ip->ip_dst.s_addr == 0xffffffff && ip->ip_p == IPPROTO_UDP) {
349 		DPRINTF("  UDP broadcast\n");
350 	}
351 
352 	if (ip->ip_p == IPPROTO_UDP) {
353 		struct udphdr *uh = (struct udphdr *)(buf_p + ETH_HLEN +
354 		    (ip->ip_hl << 2));
355 		DPRINTF("  uh_sport=%d\n", ntohs(uh->uh_sport));
356 		DPRINTF("  uh_dport=%d\n", ntohs(uh->uh_dport));
357 		DPRINTF("  uh_ulen=%d\n", uh->uh_ulen);
358 
359 		if (ntohs(uh->uh_dport) == BOOTP_SERVER) {
360 			struct bootp_t bp;
361 			DPRINTF("  BOOTP_SERVER\n");
362 			memcpy(&bp, ip, sizeof(struct ip));
363 			memcpy(&(bp.udp), uh, sizeof(struct bootp_t) -
364 			    sizeof(struct ip));
365 			print_dhcp_info(&bp);
366 		}
367 
368 		if (ntohs(uh->uh_dport) == TFTP_SERVER) {
369 			DPRINTF("  TFTP_SERVER\n");
370 		}
371 	}
372 }
373 #endif
374 
375 /*
376  * Join the provided iovec into a single buffer, possibly copying into buf,
377  * whose length is passed as len. The returned pointer points to the joined
378  * buffer, and olen is updated with its length.
379  *
380  * If join_iov can't fit all of the iovec's contents into buf, it returns
381  * NULL.
382  */
383 static uint8_t *
join_iov(uint8_t * buf,const struct iovec * iov,int iovcnt,size_t len,size_t * olen)384 join_iov(uint8_t *buf, const struct iovec *iov, int iovcnt, size_t len,
385     size_t *olen)
386 {
387 	size_t clen, toff = 0, tlen = len;
388 
389 	if (iovcnt == 1) {
390 		*olen = iov->iov_len;
391 		return (iov->iov_base);
392 	}
393 
394 	while (iovcnt > 0) {
395 		clen = iov->iov_len;
396 		if (clen > tlen)
397 			return (NULL);
398 		bcopy(iov->iov_base, buf + toff, clen);
399 		toff += clen;
400 		tlen -= clen;
401 		iov++;
402 		iovcnt--;
403 	}
404 
405 	*olen = toff;
406 	return (buf);
407 }
408 
409 /*
410  * Fetch at least len bytes from the given iov, possibly copying into buf,
411  * which should be at least len in length. The returned pointer points to
412  * the buffer to read from, and olen is updated with its length.
413  *
414  * If copy_iov can't fetch the requested number of bytes, it returns NULL.
415  */
416 static uint8_t *
copy_iov(uint8_t * buf,const struct iovec * iov,int iovcnt,size_t len,size_t * olen)417 copy_iov(uint8_t *buf, const struct iovec *iov, int iovcnt, size_t len,
418     size_t *olen)
419 {
420 	size_t clen, coff, tc, toff, tlen = len;
421 
422 	if (iov->iov_len >= len) {
423 		*olen = iov->iov_len;
424 		return (iov->iov_base);
425 	}
426 
427 	clen = iov->iov_len;
428 	coff = 0;
429 	toff = 0;
430 	while (tlen > 0) {
431 		tc = MIN(tlen, clen);
432 		if (clen == 0) {
433 			if (iovcnt == 1)
434 				return (NULL);
435 			iov++;
436 			iovcnt--;
437 			clen = iov->iov_len;
438 			coff = 0;
439 			continue;
440 		}
441 		bcopy(iov->iov_base + coff, buf + toff, tc);
442 		tlen -= tc;
443 		clen -= tc;
444 		coff += tc;
445 		toff += tc;
446 	}
447 
448 	*olen = len;
449 	return (buf);
450 }
451 
452 static int
populate_dhcp_reply(const struct bootp_t * bp,struct bootp_t * rbp,struct sockaddr_in * saddr,struct sockaddr_in * daddr,VNICDHCPState * vdsp)453 populate_dhcp_reply(const struct bootp_t *bp, struct bootp_t *rbp,
454     struct sockaddr_in *saddr, struct sockaddr_in *daddr, VNICDHCPState *vdsp)
455 {
456 	uint8_t *q;
457 	struct in_addr preq_addr;
458 	int dhcp_msg_type, val, i;
459 
460 	/* extract exact DHCP msg type */
461 	dhcp_decode(bp, &dhcp_msg_type, &preq_addr);
462 	DPRINTF("bootp packet op=%d msgtype=%d", bp->bp_op, dhcp_msg_type);
463 	if (preq_addr.s_addr != htonl(0L))
464 		DPRINTF(" req_addr=%08x\n", ntohl(preq_addr.s_addr));
465 	else
466 		DPRINTF("\n");
467 
468 	if (dhcp_msg_type == 0)
469 		dhcp_msg_type = DHCPREQUEST; /* Force reply for old clients */
470 
471 	if (dhcp_msg_type != DHCPDISCOVER &&
472 	    dhcp_msg_type != DHCPREQUEST)
473 		return (0);
474 
475 	memset(rbp, 0, sizeof(struct bootp_t));
476 
477 	rbp->bp_op = BOOTP_REPLY;
478 	rbp->bp_xid = bp->bp_xid;
479 	rbp->bp_htype = 1;
480 	rbp->bp_hlen = 6;
481 	memcpy(rbp->bp_hwaddr, bp->bp_hwaddr, 6);
482 	rbp->bp_yiaddr = daddr->sin_addr;
483 	rbp->bp_siaddr = saddr->sin_addr;
484 	q = rbp->bp_vend;
485 	memcpy(q, rfc1533_cookie, 4);
486 	q += 4;
487 
488 	if (dhcp_msg_type == DHCPDISCOVER || dhcp_msg_type == DHCPREQUEST) {
489 		DPRINTF("%s addr=%08x\n",
490 		    (dhcp_msg_type == DHCPDISCOVER) ? "offered" : "ack'ed",
491 		    ntohl(daddr->sin_addr.s_addr));
492 
493 		*q++ = RFC2132_MSG_TYPE;
494 		*q++ = 1;
495 		/* DHCPREQUEST */
496 		*q++ = (dhcp_msg_type == DHCPDISCOVER) ? DHCPOFFER : DHCPACK;
497 
498 		*q++ = RFC2132_SRV_ID;
499 		*q++ = 4;
500 		memcpy(q, &saddr->sin_addr, 4);
501 		q += 4;
502 
503 		// netmask
504 		*q++ = RFC1533_NETMASK;
505 		*q++ = 4;
506 		memcpy(q, &vdsp->vnds_netmask_addr, sizeof(struct in_addr));
507 		q += 4;
508 
509 		// default gw
510 		if (vdsp->vnds_gw_addr.s_addr != 0) {
511 			*q++ = RFC1533_GATEWAY;
512 			*q++ = 4;
513 			memcpy(q, &vdsp->vnds_gw_addr, sizeof(struct in_addr));
514 			q += 4;
515 		}
516 
517 		// dns server list
518 		if (vdsp->vnds_num_dns_addrs > 0) {
519 			*q++ = RFC1533_DNS;
520 			*q++ = 4 * vdsp->vnds_num_dns_addrs;
521 			for (i = 0; i < vdsp->vnds_num_dns_addrs; i++) {
522 				memcpy(q, &vdsp->vnds_dns_addrs[i], sizeof(struct in_addr));
523 				q += 4;
524 			}
525 		}
526 
527 		// lease time
528 		*q++ = RFC2132_LEASE_TIME;
529 		*q++ = 4;
530 		memcpy(q, &vdsp->vnds_lease_time, 4);
531 		q += 4;
532 
533 		// hostname
534 		val = strlen(vdsp->vnds_client_hostname);
535 		if (val > 0) {
536 			*q++ = RFC1533_HOSTNAME;
537 			*q++ = val;
538 			memcpy(q, &vdsp->vnds_client_hostname, val);
539 			q += val;
540 		}
541 	} else {
542 		static const char nak_msg[] = "requested address not available";
543 
544 		DPRINTF("nak'ed addr=%08x\n", ntohl(preq_addr.s_addr));
545 
546 		*q++ = RFC2132_MSG_TYPE;
547 		*q++ = 1;
548 		*q++ = DHCPNAK;
549 
550 		*q++ = RFC2132_MESSAGE;
551 		*q++ = sizeof(nak_msg) - 1;
552 		memcpy(q, nak_msg, sizeof(nak_msg) - 1);
553 		q += sizeof(nak_msg) - 1;
554 	}
555 	*q = RFC1533_END;
556 
557 	return (1);
558 }
559 
560 static void
add_udpip_header(struct udpiphdr * ui,struct sockaddr_in * saddr,struct sockaddr_in * daddr)561 add_udpip_header(struct udpiphdr *ui, struct sockaddr_in *saddr,
562     struct sockaddr_in *daddr)
563 {
564 	memset(&ui->ui_i.ih_mbuf, 0 , sizeof(struct mbuf_ptr));
565 	ui->ui_x1 = 0;
566 	ui->ui_pr = IPPROTO_UDP;
567 	ui->ui_len = htons(sizeof(struct bootp_t) - sizeof(struct ip));
568 	ui->ui_src = saddr->sin_addr;
569 	ui->ui_dst = daddr->sin_addr;
570 	ui->ui_sport = saddr->sin_port;
571 	ui->ui_dport = daddr->sin_port;
572 	ui->ui_ulen = htons(sizeof(struct bootp_t) - sizeof(struct ip));
573 
574 	ui->ui_sum = 0;
575 	if ((ui->ui_sum = cksum2((uint16_t *)ui, sizeof(struct bootp_t))) == 0)
576 		ui->ui_sum = 0xffff;
577 }
578 
579 static void
add_ip_header(struct ip * ip,VNICDHCPState * vdsp)580 add_ip_header(struct ip *ip, VNICDHCPState *vdsp)
581 {
582 	register int ip_hlen = sizeof(struct ip);
583 	register int ip_len = sizeof(struct bootp_t);
584 
585 	ip->ip_v = IPVERSION;
586 	ip->ip_off &= IP_DF;
587 	ip->ip_id = htons(vdsp->vnds_ip_id++);
588 	ip->ip_hl = ip_hlen >> 2;
589 
590 	ip->ip_ttl = IPDEFTTL;
591 	ip->ip_tos = IPTOS_LOWDELAY;
592 
593 	ip->ip_len = htons((uint16_t)ip_len);
594 	ip->ip_off = htons((uint16_t)ip->ip_off);
595 	ip->ip_sum = 0;
596 	ip->ip_sum = cksum2((uint16_t *) ip, ip_hlen);
597 }
598 
599 static int
dhcp_reply(const struct bootp_t * bp,const unsigned char * src_mac,VNICDHCPState * vdsp)600 dhcp_reply(const struct bootp_t *bp, const unsigned char *src_mac,
601     VNICDHCPState *vdsp)
602 {
603 	struct sockaddr_in saddr, daddr;
604 	struct ethhdr *eh;
605 	struct bootp_t *rbp = (struct bootp_t *)(vdsp->vnds_buf + ETH_HLEN);
606 
607 	/* Client IP address */
608 	memcpy(&daddr.sin_addr, &vdsp->vnds_client_addr,
609 	    sizeof(struct in_addr));
610 	daddr.sin_port = htons(BOOTP_CLIENT);
611 
612 	/* Server IP address */
613 	memcpy(&saddr.sin_addr, &vdsp->vnds_srv_addr, sizeof(struct in_addr));
614 	saddr.sin_port = htons(BOOTP_SERVER);
615 
616 	/* Now set all of the DHCP options */
617 	if (!populate_dhcp_reply(bp, rbp, &saddr, &daddr, vdsp))
618 		return (0);
619 
620 	if (bp->ip.ip_src.s_addr == 0) {
621 		daddr.sin_addr.s_addr = 0xffffffffu;
622 	} else {
623 		daddr.sin_addr.s_addr = bp->ip.ip_src.s_addr;
624 	}
625 
626 	/* Buffer Layout:
627 	 *
628 	 * | ethhdr | bootp_t                          |
629 	 *          | ip       | udphdr | dhcp payload |
630 	 *          | udpiphdr          |
631 	 *
632 	 */
633 
634 	/* Ethernet header */
635 	eh = (struct ethhdr *)vdsp->vnds_buf;
636 	memcpy(eh->h_dest, src_mac, ETH_ALEN);
637 	memcpy(eh->h_source, special_ethaddr, ETH_ALEN);
638 	eh->h_proto = htons(ETH_P_IP);
639 
640 	/* IP pseudo header (for UDP checksum) */
641 	add_udpip_header((struct udpiphdr *)rbp, &saddr, &daddr);
642 
643 	add_ip_header((struct ip *)rbp, vdsp);
644 
645 #if VNIC_DHCP_DEBUG
646 	DPRINTF("= dhcp reply =\n");
647 	debug_eth_frame((const uint8_t *)vdsp->vnds_buf,
648 	    sizeof(struct bootp_t) + sizeof(struct ethhdr));
649 	DPRINTF("= dhcp reply end =\n");
650 #endif
651 
652 	return (sizeof(struct bootp_t) + sizeof(struct ethhdr));
653 }
654 
655 int
create_arp_responsev(const struct iovec * iov,int iovcnt,VNICDHCPState * vdsp)656 create_arp_responsev(const struct iovec *iov, int iovcnt, VNICDHCPState *vdsp)
657 {
658 	/* We use the maximum length of an IPv4 packet for our buffer length. */
659 	uint8_t buf[65536], *bufp;
660 	size_t len = 0;
661 
662 	if ((bufp = join_iov(buf, iov, iovcnt, sizeof (buf), &len)) == NULL) {
663 		return (0);
664 	}
665 
666 	return (create_arp_response(bufp, len, vdsp));
667 }
668 
669 int
create_arp_response(const uint8_t * buf_p,int pkt_len,VNICDHCPState * vdsp)670 create_arp_response(const uint8_t *buf_p, int pkt_len, VNICDHCPState *vdsp)
671 {
672 	struct ether_arp *inarp = (struct ether_arp *)(buf_p + ETH_HLEN);
673 	struct ethhdr *outeth = (struct ethhdr *)vdsp->vnds_buf;
674 	struct ether_arp *outarp =
675 	    (struct ether_arp *)(vdsp->vnds_buf + ETH_HLEN);
676 
677 	memcpy(outeth->h_dest, inarp->arp_sha, ETH_ALEN);
678 	memcpy(outeth->h_source, special_ethaddr, ETH_ALEN);
679 	outeth->h_proto = htons(ETH_P_ARP);
680 
681 	outarp->arp_hrd = htons(ARPHRD_ETHER);
682 	outarp->arp_pro = htons(ETH_P_IP);
683 	outarp->arp_hln = ETH_ALEN;
684 	outarp->arp_pln = 4;
685 	outarp->arp_op = htons(ARPOP_REPLY);
686 	memcpy(outarp->arp_sha, special_ethaddr, ETH_ALEN);
687 	memcpy(outarp->arp_spa, &vdsp->vnds_srv_addr, sizeof (outarp->arp_spa));
688 	memcpy(outarp->arp_tha, inarp->arp_sha, ETH_ALEN);
689 	memcpy(outarp->arp_tpa, inarp->arp_spa, sizeof (outarp->arp_tpa));
690 
691 	return (sizeof (struct ethhdr) + sizeof (struct ether_arp));
692 }
693 
694 /*
695  * Since this is not the common path, just combine the IOV for the moment, it's
696  * not great.
697  */
698 int
create_dhcp_responsev(const struct iovec * iov,int iovcnt,VNICDHCPState * vdsp)699 create_dhcp_responsev(const struct iovec *iov, int iovcnt, VNICDHCPState *vdsp)
700 {
701 	/* We use the maximum length of an IPv4 packet for our buffer length. */
702 	uint8_t buf[65536], *bufp;
703 	size_t len = 0;
704 
705 	if ((bufp = join_iov(buf, iov, iovcnt, sizeof (buf), &len)) == NULL) {
706 		return (0);
707 	}
708 
709 	return (create_dhcp_response(bufp, len, vdsp));
710 }
711 
712 int
create_dhcp_response(const uint8_t * buf_p,int pkt_len,VNICDHCPState * vdsp)713 create_dhcp_response(const uint8_t *buf_p, int pkt_len, VNICDHCPState *vdsp)
714 {
715 	struct bootp_t bp;
716 	register struct udphdr *uh;
717 	register struct ip *ip = (struct ip *)(buf_p + ETH_HLEN);
718 
719 	DPRINTF("create_dhcp_response()\n");
720 
721 	if (ip->ip_p != IPPROTO_UDP) {
722 		return (0);
723 	}
724 
725 	uh = (struct udphdr *) (buf_p + ETH_HLEN + (ip->ip_hl << 2));
726 	if (ntohs(uh->uh_dport) != BOOTP_SERVER) {
727 		return (0);
728 	}
729 
730 	/* Trim out IP options (if any) */
731 	memcpy(&bp, ip, sizeof(struct ip));
732 	memcpy(&(bp.udp), uh, sizeof(struct bootp_t) - sizeof(struct ip));
733 
734 	if (bp.bp_op != BOOTP_REQUEST) {
735 		return (0);
736 	}
737 
738 	return (dhcp_reply(&bp, ((struct ethhdr *)buf_p)->h_source, vdsp));
739 }
740 
741 int
get_ethertype(const uint8_t * buf_p,size_t size,uint16_t * ethertype)742 get_ethertype(const uint8_t *buf_p, size_t size, uint16_t *ethertype)
743 {
744 	if (size < ETH_HLEN) {
745 		return (0);
746 	}
747 
748 	*ethertype =
749 	    ntohs(*(uint16_t *)(buf_p + offsetof(struct ethhdr, h_proto)));
750 	return (1);
751 }
752 
753 int
get_ethertypev(const struct iovec * iov,int iovcnt,uint16_t * ethertype)754 get_ethertypev(const struct iovec *iov, int iovcnt, uint16_t *ethertype)
755 {
756 	uint8_t buf[1024], *bufp;
757 	size_t len = 0;
758 
759 	if ((bufp = copy_iov(buf, iov, iovcnt, ETH_HLEN, &len)) == NULL) {
760 		return (0);
761 	}
762 
763 	return (get_ethertype(bufp, len, ethertype));
764 }
765 
766 int
is_arp_request(const uint8_t * buf_p,size_t size,VNICDHCPState * vdsp)767 is_arp_request(const uint8_t *buf_p, size_t size, VNICDHCPState *vdsp)
768 {
769 	struct ether_arp *arp;
770 
771 	DPRINTF("is_arp_request(): ");
772 	if (size < (ETH_HLEN + sizeof (struct ether_arp))) {
773 		DPRINTF("ARP packet too small\n");
774 		return (0);
775 	}
776 
777 	arp = (struct ether_arp *)(buf_p + ETH_HLEN);
778 
779 	if (ntohs(arp->arp_hrd) != ARPHRD_ETHER ||
780 	    ntohs(arp->arp_pro) != ETH_P_IP ||
781 	    ntohs(arp->arp_op) != ARPOP_REQUEST) {
782 		DPRINTF("not an IP->Ethernet ARP request\n");
783 		return (0);
784 	}
785 
786 	if (arp->arp_hln != ETH_ALEN || arp->arp_pln != 4) {
787 		DPRINTF("ARP request contains bad lengths\n");
788 		return (0);
789 	}
790 
791 	struct in_addr *lladdr = &vdsp->vnds_srv_addr;
792 	struct in_addr *tpaddr = (struct in_addr *)arp->arp_tpa;
793 
794 	if (memcmp(tpaddr, lladdr, sizeof (struct in_addr)) != 0) {
795 		return (0);
796 	}
797 
798 	return (1);
799 }
800 
801 int
is_arp_requestv(const struct iovec * iov,int iovcnt,VNICDHCPState * vdsp)802 is_arp_requestv(const struct iovec *iov, int iovcnt, VNICDHCPState *vdsp)
803 {
804 	uint8_t buf[1024], *bufp;
805 	size_t sz = ETH_HLEN + sizeof (struct ether_arp);
806 	size_t len = 0;
807 
808 	if ((bufp = copy_iov(buf, iov, iovcnt, sz, &len)) == NULL) {
809 		return (0);
810 	}
811 
812 	return (is_arp_request(bufp, len, vdsp));
813 }
814 
815 int
is_dhcp_request(const uint8_t * buf_p,size_t size)816 is_dhcp_request(const uint8_t *buf_p, size_t size)
817 {
818 	struct ip *ip;
819 	struct udphdr *uh;
820 
821 	DPRINTF("is_dhcp_request(): ");
822 	if (size < (ETH_HLEN + sizeof (struct ip) + sizeof (struct udphdr))) {
823 		DPRINTF("IP packet too small\n");
824 		return (0);
825 	}
826 
827 	ip = (struct ip *)(buf_p + ETH_HLEN);
828 
829 	if (ip->ip_v != IPVERSION) {
830 		DPRINTF("not an IPv4 packet\n");
831 		return (0);
832 	}
833 
834 	if (ip->ip_p != IPPROTO_UDP) {
835 		DPRINTF("not a UDP packet\n");
836 		return (0);
837 	}
838 
839 	uh = (struct udphdr *)(buf_p + ETH_HLEN + (ip->ip_hl << 2));
840 	if (ntohs(uh->uh_dport) == BOOTP_SERVER) {
841 		DPRINTF("is a DHCP request\n");
842 		return (1);
843 	}
844 
845 	DPRINTF("UDP packet, but not a DHCP request\n");
846 	return (0);
847 }
848 
849 int
is_dhcp_requestv(const struct iovec * iov,int iovcnt)850 is_dhcp_requestv(const struct iovec *iov, int iovcnt)
851 {
852 	uint8_t buf[1024], *bufp;
853 	size_t sz = ETH_HLEN + sizeof (struct ip) + sizeof (struct udphdr);
854 	size_t len = 0;
855 
856 	if ((bufp = copy_iov(buf, iov, iovcnt, sz, &len)) == NULL) {
857 		return (0);
858 	}
859 
860 	return (is_dhcp_request(bufp, len));
861 }
862 
863 static int
qemu_ip_opt(QemuOpts * opts,const char * opt_name,struct in_addr * addr,int required)864 qemu_ip_opt(QemuOpts *opts, const char *opt_name, struct in_addr *addr, int required)
865 {
866 	const char *opt;
867 	if ((opt = qemu_opt_get(opts, opt_name)) == NULL) {
868 		if (required)
869 			error_report("missing %s for vnic dhcp\n", opt_name);
870 		return (0);
871 	}
872 
873 	if (inet_pton(AF_INET, opt, addr) != 1) {
874 		error_report("invalid %s '%s' for vnic dhcp\n", opt_name, opt);
875 		return (-1);
876 	}
877 
878 	return (1);
879 }
880 
881 int
vnic_dhcp_init(VNICDHCPState * vdsp,QemuOpts * opts)882 vnic_dhcp_init(VNICDHCPState *vdsp, QemuOpts *opts)
883 {
884 	int ret, i;
885 	uint32_t lease_time;
886 	const char *hostname;
887 	char dns_opt[8];
888 	int num_dns_servers = 0;
889 
890 	/* Use the ip option to determine if dhcp should be enabled */
891 	if (qemu_opt_get(opts, "ip") == NULL) {
892 		error_report("vnic dhcp disabled\n");
893 		vdsp->vnds_enabled = 0;
894 		return (1);
895 	}
896 
897 	if (!qemu_ip_opt(opts, "ip", &(vdsp->vnds_client_addr), 1))
898 		return (0);
899 
900 	if (!qemu_ip_opt(opts, "netmask", &(vdsp->vnds_netmask_addr), 1))
901 		return (0);
902 
903 	if (!(ret = qemu_ip_opt(opts, "server_ip", &(vdsp->vnds_srv_addr), 0))) {
904 		if (ret == 0) {
905 			/* default DHCP server address */
906 			inet_pton(AF_INET, "169.254.169.254",
907 			    &(vdsp->vnds_srv_addr));
908 		} else {
909 			return (0);
910 		}
911 	}
912 
913 	if (!qemu_ip_opt(opts, "gateway_ip", &(vdsp->vnds_gw_addr), 0)) {
914 		vdsp->vnds_gw_addr.s_addr = 0;
915 	}
916 
917 	if ((ret = qemu_ip_opt(opts, "dns_ip", &(vdsp->vnds_dns_addrs[0]), 0)) != 0) {
918 		if (ret == -1)
919 			return (0);
920 		num_dns_servers = 1;
921 	}
922 
923 	for(i = 0; i < VNIC_DHCP_NUM_RESOLVERS; i++) {
924 		sprintf(dns_opt, "dns_ip%d", i);
925 		if (!(ret = qemu_ip_opt(opts, dns_opt, &(vdsp->vnds_dns_addrs[i]), 0))) {
926 			if (ret == 0) {
927 				break;
928 			} else {
929 				return (0);
930 			}
931 		}
932 		num_dns_servers = i + 1;
933 	}
934 	vdsp->vnds_num_dns_addrs = num_dns_servers;
935 
936 	if ((hostname = qemu_opt_get(opts, "hostname")) != NULL) {
937 		ret = strlen(hostname);
938 		if (ret > sizeof(vdsp->vnds_client_hostname)) {
939 			error_report("hostname is too long\n");
940 			return (0);
941 		}
942 		memcpy(&vdsp->vnds_client_hostname, hostname, ret);
943 	} else {
944 		vdsp->vnds_client_hostname[0] = '\0';
945 	}
946 
947 	lease_time = qemu_opt_get_number(opts, "lease_time", LEASE_TIME);
948 	vdsp->vnds_lease_time = htonl(lease_time);
949 
950 	vdsp->vnds_ip_id = 0;
951 	vdsp->vnds_enabled = 1;
952 
953 	return (1);
954 }
955