xref: /kvmtool/net/uip/dhcp.c (revision 0796825e08da408fba6614d8a135a264d37ef9fe)
10c4bfcacSAsias He #include "kvm/uip.h"
20c4bfcacSAsias He 
3bdcc9e40SAsias He #include <arpa/inet.h>
4bdcc9e40SAsias He 
5575a9e3fSSasha Levin #define EMPTY_ADDR "0.0.0.0"
6575a9e3fSSasha Levin 
uip_dhcp_is_discovery(struct uip_dhcp * dhcp)78f5f2f95SAsias He static inline bool uip_dhcp_is_discovery(struct uip_dhcp *dhcp)
88f5f2f95SAsias He {
98f5f2f95SAsias He 	return (dhcp->option[2] == UIP_DHCP_DISCOVER &&
108f5f2f95SAsias He 		dhcp->option[1] == UIP_DHCP_TAG_MSG_TYPE_LEN &&
118f5f2f95SAsias He 		dhcp->option[0] == UIP_DHCP_TAG_MSG_TYPE);
128f5f2f95SAsias He }
138f5f2f95SAsias He 
uip_dhcp_is_request(struct uip_dhcp * dhcp)148f5f2f95SAsias He static inline bool uip_dhcp_is_request(struct uip_dhcp *dhcp)
158f5f2f95SAsias He {
168f5f2f95SAsias He 	return (dhcp->option[2] == UIP_DHCP_REQUEST &&
178f5f2f95SAsias He 		dhcp->option[1] == UIP_DHCP_TAG_MSG_TYPE_LEN &&
188f5f2f95SAsias He 		dhcp->option[0] == UIP_DHCP_TAG_MSG_TYPE);
198f5f2f95SAsias He }
208f5f2f95SAsias He 
uip_udp_is_dhcp(struct uip_udp * udp)210c4bfcacSAsias He bool uip_udp_is_dhcp(struct uip_udp *udp)
220c4bfcacSAsias He {
230c4bfcacSAsias He 	struct uip_dhcp *dhcp;
240c4bfcacSAsias He 
250c4bfcacSAsias He 	if (ntohs(udp->sport) != UIP_DHCP_PORT_CLIENT ||
260c4bfcacSAsias He 	    ntohs(udp->dport) != UIP_DHCP_PORT_SERVER)
270c4bfcacSAsias He 		return false;
280c4bfcacSAsias He 
290c4bfcacSAsias He 	dhcp = (struct uip_dhcp *)udp;
300c4bfcacSAsias He 
310c4bfcacSAsias He 	if (ntohl(dhcp->magic_cookie) != UIP_DHCP_MAGIC_COOKIE)
320c4bfcacSAsias He 		return false;
330c4bfcacSAsias He 
340c4bfcacSAsias He 	return true;
350c4bfcacSAsias He }
36bdcc9e40SAsias He 
uip_dhcp_get_dns(struct uip_info * info)37bdcc9e40SAsias He int uip_dhcp_get_dns(struct uip_info *info)
38bdcc9e40SAsias He {
39bdcc9e40SAsias He 	char key[256], val[256];
40bdcc9e40SAsias He 	struct in_addr addr;
41bdcc9e40SAsias He 	int ret = -1;
42bdcc9e40SAsias He 	int n = 0;
43bdcc9e40SAsias He 	FILE *fp;
44bdcc9e40SAsias He 	u32 ip;
45bdcc9e40SAsias He 
46bdcc9e40SAsias He 	fp = fopen("/etc/resolv.conf", "r");
47bdcc9e40SAsias He 	if (!fp)
48a7b946b9SMichael Ellerman 		return ret;
49bdcc9e40SAsias He 
50bdcc9e40SAsias He 	while (!feof(fp)) {
51bdcc9e40SAsias He 		if (fscanf(fp, "%s %s\n", key, val) != 2)
52bdcc9e40SAsias He 			continue;
53bdcc9e40SAsias He 		if (strncmp("domain", key, 6) == 0)
54bdcc9e40SAsias He 			info->domain_name = strndup(val, UIP_DHCP_MAX_DOMAIN_NAME_LEN);
55bdcc9e40SAsias He 		else if (strncmp("nameserver", key, 10) == 0) {
56bdcc9e40SAsias He 			if (!inet_aton(val, &addr))
57bdcc9e40SAsias He 				continue;
58bdcc9e40SAsias He 			ip = ntohl(addr.s_addr);
59bdcc9e40SAsias He 			if (n < UIP_DHCP_MAX_DNS_SERVER_NR)
60bdcc9e40SAsias He 				info->dns_ip[n++] = ip;
61bdcc9e40SAsias He 			ret = 0;
62bdcc9e40SAsias He 		}
63bdcc9e40SAsias He 	}
64bdcc9e40SAsias He 
65bdcc9e40SAsias He 	fclose(fp);
66bdcc9e40SAsias He 	return ret;
67bdcc9e40SAsias He }
6832a44aafSAsias He 
uip_dhcp_fill_option_name_and_server(struct uip_info * info,u8 * opt,int i)6932a44aafSAsias He static int uip_dhcp_fill_option_name_and_server(struct uip_info *info, u8 *opt, int i)
7032a44aafSAsias He {
7132a44aafSAsias He 	u8 domain_name_len;
7232a44aafSAsias He 	u32 *addr;
7332a44aafSAsias He 	int n;
7432a44aafSAsias He 
7532a44aafSAsias He 	if (info->domain_name) {
7632a44aafSAsias He 		domain_name_len	= strlen(info->domain_name);
7732a44aafSAsias He 		opt[i++]	= UIP_DHCP_TAG_DOMAIN_NAME;
7832a44aafSAsias He 		opt[i++]	= domain_name_len;
7932a44aafSAsias He 		memcpy(&opt[i], info->domain_name, domain_name_len);
8032a44aafSAsias He 		i		+= domain_name_len;
8132a44aafSAsias He 	}
8232a44aafSAsias He 
8332a44aafSAsias He 	for (n = 0; n < UIP_DHCP_MAX_DNS_SERVER_NR; n++) {
8432a44aafSAsias He 		if (info->dns_ip[n] == 0)
8532a44aafSAsias He 			continue;
8632a44aafSAsias He 		opt[i++]	= UIP_DHCP_TAG_DNS_SERVER;
8732a44aafSAsias He 		opt[i++]	= UIP_DHCP_TAG_DNS_SERVER_LEN;
8832a44aafSAsias He 		addr		= (u32 *)&opt[i];
8932a44aafSAsias He 		*addr		= htonl(info->dns_ip[n]);
9032a44aafSAsias He 		i		+= UIP_DHCP_TAG_DNS_SERVER_LEN;
9132a44aafSAsias He 	}
9232a44aafSAsias He 
9332a44aafSAsias He 	return i;
9432a44aafSAsias He }
uip_dhcp_fill_option(struct uip_info * info,struct uip_dhcp * dhcp,int reply_msg_type)95ab8d80a6SAsias He static int uip_dhcp_fill_option(struct uip_info *info, struct uip_dhcp *dhcp, int reply_msg_type)
96ab8d80a6SAsias He {
97ab8d80a6SAsias He 	int i = 0;
98ab8d80a6SAsias He 	u32 *addr;
99ab8d80a6SAsias He 	u8 *opt;
100ab8d80a6SAsias He 
101ab8d80a6SAsias He 	opt		= dhcp->option;
102ab8d80a6SAsias He 
103ab8d80a6SAsias He 	opt[i++]	= UIP_DHCP_TAG_MSG_TYPE;
104ab8d80a6SAsias He 	opt[i++]	= UIP_DHCP_TAG_MSG_TYPE_LEN;
105ab8d80a6SAsias He 	opt[i++]	= reply_msg_type;
106ab8d80a6SAsias He 
107ab8d80a6SAsias He 	opt[i++]	= UIP_DHCP_TAG_SERVER_ID;
108ab8d80a6SAsias He 	opt[i++]	= UIP_DHCP_TAG_SERVER_ID_LEN;
109ab8d80a6SAsias He 	addr		= (u32 *)&opt[i];
110ab8d80a6SAsias He 	*addr		= htonl(info->host_ip);
111ab8d80a6SAsias He 	i		+= UIP_DHCP_TAG_SERVER_ID_LEN;
112ab8d80a6SAsias He 
113ab8d80a6SAsias He 	opt[i++]	= UIP_DHCP_TAG_LEASE_TIME;
114ab8d80a6SAsias He 	opt[i++]	= UIP_DHCP_TAG_LEASE_TIME_LEN;
115ab8d80a6SAsias He 	addr		= (u32 *)&opt[i];
116ab8d80a6SAsias He 	*addr		= htonl(UIP_DHCP_LEASE_TIME);
117ab8d80a6SAsias He 	i		+= UIP_DHCP_TAG_LEASE_TIME_LEN;
118ab8d80a6SAsias He 
119ab8d80a6SAsias He 	opt[i++]	= UIP_DHCP_TAG_SUBMASK;
120ab8d80a6SAsias He 	opt[i++]	= UIP_DHCP_TAG_SUBMASK_LEN;
121ab8d80a6SAsias He 	addr		= (u32 *)&opt[i];
122ab8d80a6SAsias He 	*addr		= htonl(info->guest_netmask);
123ab8d80a6SAsias He 	i		+= UIP_DHCP_TAG_SUBMASK_LEN;
124ab8d80a6SAsias He 
125ab8d80a6SAsias He 	opt[i++]	= UIP_DHCP_TAG_ROUTER;
126ab8d80a6SAsias He 	opt[i++]	= UIP_DHCP_TAG_ROUTER_LEN;
127ab8d80a6SAsias He 	addr		= (u32 *)&opt[i];
128ab8d80a6SAsias He 	*addr		= htonl(info->host_ip);
129ab8d80a6SAsias He 	i		+= UIP_DHCP_TAG_ROUTER_LEN;
130ab8d80a6SAsias He 
131575a9e3fSSasha Levin 	opt[i++]	= UIP_DHCP_TAG_ROOT;
132575a9e3fSSasha Levin 	opt[i++]	= strlen(EMPTY_ADDR);
133575a9e3fSSasha Levin 	addr		= (u32 *)&opt[i];
134*0796825eSAndre Przywara 	strcpy((void *) addr, EMPTY_ADDR);
135575a9e3fSSasha Levin 	i		+= strlen(EMPTY_ADDR);
136575a9e3fSSasha Levin 
137ab8d80a6SAsias He 	i 		= uip_dhcp_fill_option_name_and_server(info, opt, i);
138ab8d80a6SAsias He 
139ab8d80a6SAsias He 	opt[i++]	= UIP_DHCP_TAG_END;
140ab8d80a6SAsias He 
141ab8d80a6SAsias He 	return 0;
142ab8d80a6SAsias He }
143155c7f33SAsias He 
uip_dhcp_make_pkg(struct uip_info * info,struct uip_udp_socket * sk,struct uip_buf * buf,u8 reply_msg_type)144155c7f33SAsias He static int uip_dhcp_make_pkg(struct uip_info *info, struct uip_udp_socket *sk, struct uip_buf *buf, u8 reply_msg_type)
145155c7f33SAsias He {
146155c7f33SAsias He 	struct uip_dhcp *dhcp;
147155c7f33SAsias He 
148155c7f33SAsias He 	dhcp		= (struct uip_dhcp *)buf->eth;
149155c7f33SAsias He 
150155c7f33SAsias He 	dhcp->msg_type	= 2;
151155c7f33SAsias He 	dhcp->client_ip	= 0;
152155c7f33SAsias He 	dhcp->your_ip	= htonl(info->guest_ip);
153155c7f33SAsias He 	dhcp->server_ip	= htonl(info->host_ip);
154155c7f33SAsias He 	dhcp->agent_ip	= 0;
155155c7f33SAsias He 
156155c7f33SAsias He 	uip_dhcp_fill_option(info, dhcp, reply_msg_type);
157155c7f33SAsias He 
158155c7f33SAsias He 	sk->sip		= htonl(info->guest_ip);
159155c7f33SAsias He 	sk->dip		= htonl(info->host_ip);
160155c7f33SAsias He 	sk->sport	= htons(UIP_DHCP_PORT_CLIENT);
161155c7f33SAsias He 	sk->dport	= htons(UIP_DHCP_PORT_SERVER);
162155c7f33SAsias He 
163155c7f33SAsias He 	return 0;
164155c7f33SAsias He }
165bcd315dbSAsias He 
uip_tx_do_ipv4_udp_dhcp(struct uip_tx_arg * arg)166bcd315dbSAsias He int uip_tx_do_ipv4_udp_dhcp(struct uip_tx_arg *arg)
167bcd315dbSAsias He {
168bcd315dbSAsias He 	struct uip_udp_socket sk;
169bcd315dbSAsias He 	struct uip_dhcp *dhcp;
170bcd315dbSAsias He 	struct uip_info *info;
171bcd315dbSAsias He 	struct uip_buf *buf;
172bcd315dbSAsias He 	u8 reply_msg_type;
173bcd315dbSAsias He 
174bcd315dbSAsias He 	dhcp = (struct uip_dhcp *)arg->eth;
175bcd315dbSAsias He 
176bcd315dbSAsias He 	if (uip_dhcp_is_discovery(dhcp))
177bcd315dbSAsias He 		reply_msg_type = UIP_DHCP_OFFER;
178bcd315dbSAsias He 	else if (uip_dhcp_is_request(dhcp))
179bcd315dbSAsias He 		reply_msg_type = UIP_DHCP_ACK;
180bcd315dbSAsias He 	else
181bcd315dbSAsias He 		return -1;
182bcd315dbSAsias He 
183bcd315dbSAsias He 	buf = uip_buf_clone(arg);
184bcd315dbSAsias He 	info = arg->info;
185bcd315dbSAsias He 
186bcd315dbSAsias He 	/*
187bcd315dbSAsias He 	 * Cook DHCP pkg
188bcd315dbSAsias He 	 */
189bcd315dbSAsias He 	uip_dhcp_make_pkg(info, &sk, buf, reply_msg_type);
190bcd315dbSAsias He 
191bcd315dbSAsias He 	/*
192bcd315dbSAsias He 	 * Cook UDP pkg
193bcd315dbSAsias He 	 */
194bcd315dbSAsias He 	uip_udp_make_pkg(info, &sk, buf, NULL, UIP_DHCP_MAX_PAYLOAD_LEN);
195bcd315dbSAsias He 
196bcd315dbSAsias He 	/*
197bcd315dbSAsias He 	 * Send data received from socket to guest
198bcd315dbSAsias He 	 */
199bcd315dbSAsias He 	uip_buf_set_used(info, buf);
200bcd315dbSAsias He 
201bcd315dbSAsias He 	return 0;
202bcd315dbSAsias He }
203d87b503fSJean-Philippe Brucker 
uip_dhcp_exit(struct uip_info * info)204d87b503fSJean-Philippe Brucker void uip_dhcp_exit(struct uip_info *info)
205d87b503fSJean-Philippe Brucker {
206d87b503fSJean-Philippe Brucker 	free(info->domain_name);
207d87b503fSJean-Philippe Brucker 	info->domain_name = NULL;
208d87b503fSJean-Philippe Brucker }
209