GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/vmd/dhcp.c Lines: 0 72 0.0 %
Date: 2017-11-07 Branches: 0 40 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: dhcp.c,v 1.3 2017/04/24 07:14:27 reyk Exp $	*/
2
3
/*
4
 * Copyright (c) 2017 Reyk Floeter <reyk@openbsd.org>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/types.h>
20
#include <sys/socket.h>
21
22
#include <net/if.h>
23
#include <netinet/in.h>
24
#include <netinet/if_ether.h>
25
26
#include <stdlib.h>
27
#include <string.h>
28
#include <stddef.h>
29
30
#include "proc.h"
31
#include "vmd.h"
32
#include "dhcp.h"
33
#include "virtio.h"
34
35
static const uint8_t broadcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
36
extern struct vmd *env;
37
38
ssize_t
39
dhcp_request(struct vionet_dev *dev, char *buf, size_t buflen, char **obuf)
40
{
41
	unsigned char		*respbuf = NULL;
42
	ssize_t			 offset, respbuflen = 0;
43
	struct packet_ctx	 pc;
44
	struct dhcp_packet	 req, resp;
45
	struct in_addr		 in, mask;
46
	size_t			 resplen, o;
47
48
	if (buflen < (ssize_t)(BOOTP_MIN_LEN + sizeof(struct ether_header)))
49
		return (-1);
50
51
	memset(&pc, 0, sizeof(pc));
52
	if ((offset = decode_hw_header(buf, buflen, 0, &pc, HTYPE_ETHER)) < 0)
53
		return (-1);
54
55
	if (memcmp(pc.pc_smac, dev->mac, ETHER_ADDR_LEN) != 0 ||
56
	    memcmp(pc.pc_dmac, broadcast, ETHER_ADDR_LEN) != 0)
57
		return (-1);
58
59
	if ((offset = decode_udp_ip_header(buf, buflen, offset, &pc)) < 0)
60
		return (-1);
61
62
	if (ntohs(ss2sin(&pc.pc_src)->sin_port) != CLIENT_PORT ||
63
	    ntohs(ss2sin(&pc.pc_dst)->sin_port) != SERVER_PORT)
64
		return (-1);
65
66
	memset(&req, 0, sizeof(req));
67
	memcpy(&req, buf + offset, buflen - offset);
68
69
	if (req.op != BOOTREQUEST ||
70
	    req.htype != pc.pc_htype ||
71
	    req.hlen != ETHER_ADDR_LEN ||
72
	    memcmp(dev->mac, req.chaddr, req.hlen) != 0)
73
		return (-1);
74
75
	/* Ignore unsupported requests for now */
76
	if (req.ciaddr.s_addr != 0 || req.file[0] != '\0' || req.hops != 0)
77
		return (-1);
78
79
	memset(&resp, 0, sizeof(resp));
80
	resp.op = BOOTREPLY;
81
	resp.htype = req.htype;
82
	resp.hlen = req.hlen;
83
	resp.xid = req.xid;
84
85
	if ((in.s_addr = vm_priv_addr(&env->vmd_cfg.cfg_localprefix,
86
	    dev->vm_vmid, dev->idx, 1)) == 0)
87
		return (-1);
88
	memcpy(&resp.yiaddr, &in, sizeof(in));
89
	memcpy(&ss2sin(&pc.pc_dst)->sin_addr, &in, sizeof(in));
90
	ss2sin(&pc.pc_dst)->sin_port = htons(CLIENT_PORT);
91
92
	if ((in.s_addr = vm_priv_addr(&env->vmd_cfg.cfg_localprefix,
93
	    dev->vm_vmid, dev->idx, 0)) == 0)
94
		return (-1);
95
	memcpy(&resp.siaddr, &in, sizeof(in));
96
	memcpy(&ss2sin(&pc.pc_src)->sin_addr, &in, sizeof(in));
97
	ss2sin(&pc.pc_src)->sin_port = htons(SERVER_PORT);
98
99
	/* Packet is already allocated */
100
	if (*obuf != NULL)
101
		goto fail;
102
103
	buflen = 0;
104
	respbuflen = DHCP_MTU_MAX;
105
	if ((respbuf = calloc(1, respbuflen)) == NULL)
106
		goto fail;
107
108
	memcpy(&pc.pc_dmac, dev->mac, sizeof(pc.pc_dmac));
109
	memcpy(&resp.chaddr, dev->mac, resp.hlen);
110
	memcpy(&pc.pc_smac, dev->mac, sizeof(pc.pc_smac));
111
	pc.pc_smac[5]++;
112
	if ((offset = assemble_hw_header(respbuf, respbuflen, 0,
113
	    &pc, HTYPE_ETHER)) < 0) {
114
		log_debug("%s: assemble_hw_header failed", __func__);
115
		goto fail;
116
	}
117
118
	/* BOOTP uses a 64byte vendor field instead of the DHCP options */
119
	resplen = BOOTP_MIN_LEN;
120
121
	/* Add BOOTP Vendor Extensions (DHCP options) */
122
	o = 0;
123
	memcpy(&resp.options,
124
	    DHCP_OPTIONS_COOKIE, DHCP_OPTIONS_COOKIE_LEN);
125
	o+= DHCP_OPTIONS_COOKIE_LEN;
126
127
	resp.options[o++] = DHO_SUBNET_MASK;
128
	resp.options[o++] = sizeof(mask);
129
	mask.s_addr = htonl(0xfffffffe);
130
	memcpy(&resp.options[o], &mask, sizeof(mask));
131
	o += sizeof(mask);
132
133
	resp.options[o++] = DHO_ROUTERS;
134
	resp.options[o++] = sizeof(in);
135
	memcpy(&resp.options[o], &in, sizeof(in));
136
	o += sizeof(in);
137
138
	resp.options[o++] = DHO_DOMAIN_NAME_SERVERS;
139
	resp.options[o++] = sizeof(in);
140
	memcpy(&resp.options[o], &in, sizeof(in));
141
	o += sizeof(in);
142
143
	resp.options[o++] = DHO_END;
144
145
	resplen = offsetof(struct dhcp_packet, options) + o;
146
147
	/* Minimum packet size */
148
	if (resplen < BOOTP_MIN_LEN)
149
		resplen = BOOTP_MIN_LEN;
150
151
	if ((offset = assemble_udp_ip_header(respbuf, respbuflen, offset, &pc,
152
	    (unsigned char *)&resp, resplen)) < 0) {
153
		log_debug("%s: assemble_udp_ip_header failed", __func__);
154
		goto fail;
155
	}
156
157
	memcpy(respbuf + offset, &resp, resplen);
158
	respbuflen = offset + resplen;
159
160
	*obuf = respbuf;
161
	return (respbuflen);
162
 fail:
163
	free(respbuf);
164
	return (0);
165
}
166