1 /*
2 * Copyright (c) 2014 Brian Swetland
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8
9 #include "minip-internal.h"
10
11 #include <lk/err.h>
12 #include <platform.h>
13 #include <stdio.h>
14 #include <lk/debug.h>
15 #include <malloc.h>
16
17 #include <kernel/thread.h>
18 #include <sys/types.h>
19 #include <endian.h>
20 #include <string.h>
21
22 #define TRACE_DHCP 0
23
24 typedef struct dhcp_msg {
25 u8 opcode;
26 u8 hwtype; // hw addr type
27 u8 hwalen; // hw addr length
28 u8 hops;
29 u32 xid; // txn id
30 u16 secs; // seconds since dhcp process start
31 u16 flags;
32 u32 ciaddr; // Client IP Address
33 u32 yiaddr; // Your IP Address
34 u32 siaddr; // Server IP Address
35 u32 giaddr; // Gateway IP Address
36 u8 chaddr[16]; // Client HW Address
37 u8 sname[64]; // Server Hostname, AsciiZ
38 u8 file[128]; // Boot File Name, AsciiZ
39 u32 cookie;
40 u8 options[0];
41 } dhcp_msg_t;
42
43 udp_socket_t *dhcp_udp_handle;
44
45 #define DHCP_FLAG_BROADCAST 0x8000
46
47 #define DHCP_REQUEST 1
48 #define DHCP_REPLY 2
49
50 #define OP_DHCPDISCOVER 1 // Client: Broadcast to find Server
51 #define OP_DHCPOFFER 2 // Server response to Discover
52 #define OP_DHCPREQUEST 3 // Client accepts offer
53 #define OP_DHCPDECLINE 4 // Client notifies address already in use
54 #define OP_DHCPACK 5 // Server confirms accept
55 #define OP_DHCPNAK 6 // Server disconfirms or lease expires
56 #define OP_DHCPRELEASE 7 // Client releases address
57
58 #define OPT_NET_MASK 1 // len 4, mask
59 #define OPT_ROUTERS 3 // len 4n, gateway0, ...
60 #define OPT_DNS 6 // len 4n, nameserver0, ...
61 #define OPT_HOSTNAME 12
62 #define OPT_REQUEST_IP 50 // len 4
63 #define OPT_MSG_TYPE 53 // len 1, type same as op
64 #define OPT_SERVER_ID 54 // len 4, server ident ipaddr
65 #define OPT_DONE 255
66
67 #define DHCP_CLIENT_PORT 68
68 #define DHCP_SERVER_PORT 67
69
70 #define HW_ETHERNET 1
71
72 static u8 mac[6];
73
printip(const char * name,u32 x)74 static void printip(const char *name, u32 x) {
75 union {
76 u32 u;
77 u8 b[4];
78 } ip;
79 ip.u = x;
80 printf("%s %d.%d.%d.%d\n", name, ip.b[0], ip.b[1], ip.b[2], ip.b[3]);
81 }
82
83 static volatile int configured = 0;
84 static int cfgstate = 0;
85
dhcp_discover(u32 xid)86 static void dhcp_discover(u32 xid) {
87 struct {
88 dhcp_msg_t msg;
89 u8 opt[128];
90 } s;
91 u8 *opt = s.opt;
92 const char *hostname = minip_get_hostname();
93 memset(&s, 0, sizeof(s));
94 s.msg.opcode = DHCP_REQUEST;
95 s.msg.hwtype = HW_ETHERNET;
96 s.msg.hwalen = 6;
97 s.msg.xid = xid;
98 s.msg.cookie = 0x63538263;
99 minip_get_macaddr(s.msg.chaddr);
100
101 *opt++ = OPT_MSG_TYPE;
102 *opt++ = 1;
103 *opt++ = OP_DHCPDISCOVER;
104
105 if (hostname && hostname[0]) {
106 size_t len = strlen(hostname);
107 *opt++ = OPT_HOSTNAME;
108 *opt++ = len;
109 memcpy(opt, hostname, len);
110 opt += len;
111 }
112
113 *opt++ = OPT_DONE;
114
115 udp_send(&s.msg, sizeof(dhcp_msg_t) + (opt - s.opt), dhcp_udp_handle);
116 status_t ret = udp_send(&s.msg, sizeof(dhcp_msg_t) + (opt - s.opt), dhcp_udp_handle);
117 if (ret != NO_ERROR) {
118 printf("DHCP_DISCOVER failed: %d\n", ret);
119 }
120 }
121
dhcp_request(u32 xid,u32 server,u32 reqip)122 static void dhcp_request(u32 xid, u32 server, u32 reqip) {
123 struct {
124 dhcp_msg_t msg;
125 u8 opt[128];
126 } s;
127 u8 *opt = s.opt;
128 const char *hostname = minip_get_hostname();
129 memset(&s, 0, sizeof(s));
130 s.msg.opcode = DHCP_REQUEST;
131 s.msg.hwtype = HW_ETHERNET;
132 s.msg.hwalen = 6;
133 s.msg.xid = xid;
134 s.msg.cookie = 0x63538263;
135 minip_get_macaddr(s.msg.chaddr);
136
137 *opt++ = OPT_MSG_TYPE;
138 *opt++ = 1;
139 *opt++ = OP_DHCPREQUEST;
140
141 *opt++ = OPT_SERVER_ID;
142 *opt++ = 4;
143 memcpy(opt, &server, 4);
144 opt += 4;
145
146 *opt++ = OPT_REQUEST_IP;
147 *opt++ = 4;
148 memcpy(opt, &reqip, 4);
149 opt += 4;
150
151 if (hostname && hostname[0]) {
152 size_t len = strlen(hostname);
153 *opt++ = OPT_HOSTNAME;
154 *opt++ = len;
155 memcpy(opt, hostname, len);
156 opt += len;
157 }
158
159 *opt++ = OPT_DONE;
160
161 status_t ret = udp_send(&s.msg, sizeof(dhcp_msg_t) + (opt - s.opt), dhcp_udp_handle);
162 if (ret != NO_ERROR) {
163 printf("DHCP_REQUEST failed: %d\n", ret);
164 }
165 }
166
dhcp_cb(void * data,size_t sz,uint32_t srcip,uint16_t srcport,void * arg)167 static void dhcp_cb(void *data, size_t sz, uint32_t srcip, uint16_t srcport, void *arg) {
168 dhcp_msg_t *msg = data;
169 u8 *opt;
170 u32 netmask = 0;
171 u32 gateway = 0;
172 u32 dns = 0;
173 u32 server = 0;
174 int op = -1;
175
176 if (sz < sizeof(dhcp_msg_t)) return;
177
178 if (memcmp(msg->chaddr, mac, 6)) return;
179
180 #if TRACE_DHCP
181 printf("dhcp op=%d len=%d from p=%d ip=", msg->opcode, sz, srcport);
182 printip("", srcip);
183 #endif
184
185 if (configured) {
186 printf("already configured\n");
187 return;
188 }
189 #if TRACE_DHCP
190 printip("ciaddr", msg->ciaddr);
191 printip("yiaddr", msg->yiaddr);
192 printip("siaddr", msg->siaddr);
193 printip("giaddr", msg->giaddr);
194 printf("chaddr %02x:%02x:%02x:%02x:%02x:%02x\n",
195 msg->chaddr[0], msg->chaddr[1], msg->chaddr[2],
196 msg->chaddr[3], msg->chaddr[4], msg->chaddr[5]);
197 #endif
198 sz -= sizeof(dhcp_msg_t);
199 opt = msg->options;
200 while (sz >= 2) {
201 sz -= 2;
202 if (opt[1] > sz) {
203 break;
204 }
205 #if TRACE_DHCP
206 printf("#%d (%d), ", opt[0], opt[1]);
207 #endif
208 switch (opt[0]) {
209 case OPT_MSG_TYPE:
210 if (opt[1] == 1) op = opt[2];
211 break;
212 case OPT_NET_MASK:
213 if (opt[1] == 4) memcpy(&netmask, opt + 2, 4);
214 break;
215 case OPT_ROUTERS:
216 if (opt[1] >= 4) memcpy(&gateway, opt + 2, 4);
217 break;
218 case OPT_DNS:
219 if (opt[1] >= 4) memcpy(&dns, opt + 2, 4);
220 break;
221 case OPT_SERVER_ID:
222 if (opt[1] == 4) memcpy(&server, opt + 2, 4);
223 break;
224 case OPT_DONE:
225 goto done;
226 }
227 opt += opt[1] + 2;
228 sz -= opt[1];
229 }
230 done:
231 #if TRACE_DHCP
232 printf("\n");
233 if (server) printip("server", server);
234 if (netmask) printip("netmask", netmask);
235 if (gateway) printip("gateway", gateway);
236 if (dns) printip("dns", dns);
237 #endif
238 if (cfgstate == 0) {
239 if (op == OP_DHCPOFFER) {
240 printip("dhcp: offer:", msg->yiaddr);
241 if (server) {
242 dhcp_request(0xaabbccdd, server, msg->yiaddr);
243 cfgstate = 1;
244 }
245 }
246 } else if (cfgstate == 1) {
247 if (op == OP_DHCPACK) {
248 printip("dhcp: ack:", msg->yiaddr);
249 minip_set_ipaddr(msg->yiaddr);
250 configured = 1;
251 }
252 }
253 }
254
dhcp_thread(void * arg)255 static int dhcp_thread(void *arg) {
256 for (;;) {
257 if (configured) break;
258 thread_sleep(500);
259 if (configured) break;
260 dhcp_discover(0xaabbccdd);
261 }
262 return 0;
263 }
264
265 static thread_t *dhcp_thr;
266
minip_init_dhcp(tx_func_t tx_func,void * tx_arg)267 void minip_init_dhcp(tx_func_t tx_func, void *tx_arg) {
268 minip_get_macaddr(mac);
269
270 minip_init(tx_func, tx_arg, IPV4_NONE, IPV4_NONE, IPV4_NONE);
271
272 int ret = udp_open(IPV4_BCAST, DHCP_CLIENT_PORT, DHCP_SERVER_PORT, &dhcp_udp_handle);
273 printf("dhcp opened udp: %d\n", ret);
274
275 udp_listen(DHCP_CLIENT_PORT, dhcp_cb, NULL);
276
277 dhcp_thr = thread_create("dhcp", dhcp_thread, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
278 thread_detach_and_resume(dhcp_thr);
279 }
280