blob: 170691d7d076bb00ca4c4281358536f21ccf5244 [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (c) 2014 Brian Swetland
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24#include "minip-internal.h"
25
26#include <err.h>
27#include <platform.h>
28#include <stdio.h>
29#include <debug.h>
30#include <malloc.h>
31
32#include <kernel/thread.h>
33#include <sys/types.h>
34#include <endian.h>
35#include <string.h>
36
37#define TRACE_DHCP 0
38
39typedef struct dhcp_msg {
40 u8 opcode;
41 u8 hwtype; // hw addr type
42 u8 hwalen; // hw addr length
43 u8 hops;
44 u32 xid; // txn id
45 u16 secs; // seconds since dhcp process start
46 u16 flags;
47 u32 ciaddr; // Client IP Address
48 u32 yiaddr; // Your IP Address
49 u32 siaddr; // Server IP Address
50 u32 giaddr; // Gateway IP Address
51 u8 chaddr[16]; // Client HW Address
52 u8 sname[64]; // Server Hostname, AsciiZ
53 u8 file[128]; // Boot File Name, AsciiZ
54 u32 cookie;
55 u8 options[0];
56} dhcp_msg_t;
57
58udp_socket_t *dhcp_udp_handle;
59
60#define DHCP_FLAG_BROADCAST 0x8000
61
62#define DHCP_REQUEST 1
63#define DHCP_REPLY 2
64
65#define OP_DHCPDISCOVER 1 // Client: Broadcast to find Server
66#define OP_DHCPOFFER 2 // Server response to Discover
67#define OP_DHCPREQUEST 3 // Client accepts offer
68#define OP_DHCPDECLINE 4 // Client notifies address already in use
69#define OP_DHCPACK 5 // Server confirms accept
70#define OP_DHCPNAK 6 // Server disconfirms or lease expires
71#define OP_DHCPRELEASE 7 // Client releases address
72
73#define OPT_NET_MASK 1 // len 4, mask
74#define OPT_ROUTERS 3 // len 4n, gateway0, ...
75#define OPT_DNS 6 // len 4n, nameserver0, ...
76#define OPT_HOSTNAME 12
77#define OPT_REQUEST_IP 50 // len 4
78#define OPT_MSG_TYPE 53 // len 1, type same as op
79#define OPT_SERVER_ID 54 // len 4, server ident ipaddr
80#define OPT_DONE 255
81
82#define DHCP_CLIENT_PORT 68
83#define DHCP_SERVER_PORT 67
84
85#define HW_ETHERNET 1
86
87static u8 mac[6];
88
89static void printip(const char *name, u32 x) {
90 union {
91 u32 u;
92 u8 b[4];
93 } ip;
94 ip.u = x;
95 printf("%s %d.%d.%d.%d\n", name, ip.b[0], ip.b[1], ip.b[2], ip.b[3]);
96}
97
98static volatile int configured = 0;
99static int cfgstate = 0;
100
101static void dhcp_discover(u32 xid) {
102 struct {
103 dhcp_msg_t msg;
104 u8 opt[128];
105 } s;
106 u8 *opt = s.opt;
107 const char *hostname = minip_get_hostname();
108 memset(&s, 0, sizeof(s));
109 s.msg.opcode = DHCP_REQUEST;
110 s.msg.hwtype = HW_ETHERNET;
111 s.msg.hwalen = 6;
112 s.msg.xid = xid;
113 s.msg.cookie = 0x63538263;
114 minip_get_macaddr(s.msg.chaddr);
115
116 *opt++ = OPT_MSG_TYPE;
117 *opt++ = 1;
118 *opt++ = OP_DHCPDISCOVER;
119
120 if (hostname && hostname[0]) {
121 size_t len = strlen(hostname);
122 *opt++ = OPT_HOSTNAME;
123 *opt++ = len;
124 memcpy(opt, hostname, len);
125 opt += len;
126 }
127
128 *opt++ = OPT_DONE;
129
130 udp_send(&s.msg, sizeof(dhcp_msg_t) + (opt - s.opt), dhcp_udp_handle);
131 status_t ret = udp_send(&s.msg, sizeof(dhcp_msg_t) + (opt - s.opt), dhcp_udp_handle);
132 if (ret != NO_ERROR) {
133 printf("DHCP_DISCOVER failed: %d\n", ret);
134 }
135}
136
137static void dhcp_request(u32 xid, u32 server, u32 reqip) {
138 struct {
139 dhcp_msg_t msg;
140 u8 opt[128];
141 } s;
142 u8 *opt = s.opt;
143 const char *hostname = minip_get_hostname();
144 memset(&s, 0, sizeof(s));
145 s.msg.opcode = DHCP_REQUEST;
146 s.msg.hwtype = HW_ETHERNET;
147 s.msg.hwalen = 6;
148 s.msg.xid = xid;
149 s.msg.cookie = 0x63538263;
150 minip_get_macaddr(s.msg.chaddr);
151
152 *opt++ = OPT_MSG_TYPE;
153 *opt++ = 1;
154 *opt++ = OP_DHCPREQUEST;
155
156 *opt++ = OPT_SERVER_ID;
157 *opt++ = 4;
158 memcpy(opt, &server, 4);
159 opt += 4;
160
161 *opt++ = OPT_REQUEST_IP;
162 *opt++ = 4;
163 memcpy(opt, &reqip, 4);
164 opt += 4;
165
166 if (hostname && hostname[0]) {
167 size_t len = strlen(hostname);
168 *opt++ = OPT_HOSTNAME;
169 *opt++ = len;
170 memcpy(opt, hostname, len);
171 opt += len;
172 }
173
174 *opt++ = OPT_DONE;
175
176 status_t ret = udp_send(&s.msg, sizeof(dhcp_msg_t) + (opt - s.opt), dhcp_udp_handle);
177 if (ret != NO_ERROR) {
178 printf("DHCP_REQUEST failed: %d\n", ret);
179 }
180}
181
182static void dhcp_cb(void *data, size_t sz, uint32_t srcip, uint16_t srcport, void *arg) {
183 dhcp_msg_t *msg = data;
184 u8 *opt;
185 u32 netmask = 0;
186 u32 gateway = 0;
187 u32 dns = 0;
188 u32 server = 0;
189 int op = -1;
190
191 if (sz < sizeof(dhcp_msg_t)) return;
192
193 if (memcmp(msg->chaddr, mac, 6)) return;
194
195#if TRACE_DHCP
196 printf("dhcp op=%d len=%d from p=%d ip=", msg->opcode, sz, srcport);
197 printip("", srcip);
198#endif
199
200 if (configured) {
201 printf("already configured\n");
202 return;
203 }
204#if TRACE_DHCP
205 printip("ciaddr", msg->ciaddr);
206 printip("yiaddr", msg->yiaddr);
207 printip("siaddr", msg->siaddr);
208 printip("giaddr", msg->giaddr);
209 printf("chaddr %02x:%02x:%02x:%02x:%02x:%02x\n",
210 msg->chaddr[0], msg->chaddr[1], msg->chaddr[2],
211 msg->chaddr[3], msg->chaddr[4], msg->chaddr[5]);
212#endif
213 sz -= sizeof(dhcp_msg_t);
214 opt = msg->options;
215 while (sz >= 2) {
216 sz -= 2;
217 if (opt[1] > sz) {
218 break;
219 }
220#if TRACE_DHCP
221 printf("#%d (%d), ", opt[0], opt[1]);
222#endif
223 switch (opt[0]) {
224 case OPT_MSG_TYPE:
225 if (opt[1] == 1) op = opt[2];
226 break;
227 case OPT_NET_MASK:
228 if (opt[1] == 4) memcpy(&netmask, opt + 2, 4);
229 break;
230 case OPT_ROUTERS:
231 if (opt[1] >= 4) memcpy(&gateway, opt + 2, 4);
232 break;
233 case OPT_DNS:
234 if (opt[1] >= 4) memcpy(&dns, opt + 2, 4);
235 break;
236 case OPT_SERVER_ID:
237 if (opt[1] == 4) memcpy(&server, opt + 2, 4);
238 break;
239 case OPT_DONE:
240 goto done;
241 }
242 opt += opt[1] + 2;
243 sz -= opt[1];
244 }
245done:
246#if TRACE_DHCP
247 printf("\n");
248 if (server) printip("server", server);
249 if (netmask) printip("netmask", netmask);
250 if (gateway) printip("gateway", gateway);
251 if (dns) printip("dns", dns);
252#endif
253 if (cfgstate == 0) {
254 if (op == OP_DHCPOFFER) {
255 printip("dhcp: offer:", msg->yiaddr);
256 if (server) {
257 dhcp_request(0xaabbccdd, server, msg->yiaddr);
258 cfgstate = 1;
259 }
260 }
261 } else if (cfgstate == 1) {
262 if (op == OP_DHCPACK) {
263 printip("dhcp: ack:", msg->yiaddr);
264 minip_set_ipaddr(msg->yiaddr);
265 configured = 1;
266 }
267 }
268}
269
270static int dhcp_thread(void *arg) {
271 for (;;) {
272 if (configured) break;
273 thread_sleep(500);
274 if (configured) break;
275 dhcp_discover(0xaabbccdd);
276 }
277 return 0;
278}
279
280static thread_t *dhcp_thr;
281
282void minip_init_dhcp(tx_func_t tx_func, void *tx_arg) {
283 minip_get_macaddr(mac);
284
285 minip_init(tx_func, tx_arg, IPV4_NONE, IPV4_NONE, IPV4_NONE);
286
287 int ret = udp_open(IPV4_BCAST, DHCP_CLIENT_PORT, DHCP_SERVER_PORT, &dhcp_udp_handle);
288 printf("dhcp opened udp: %d\n", ret);
289
290 udp_listen(DHCP_CLIENT_PORT, dhcp_cb, NULL);
291
292 dhcp_thr = thread_create("dhcp", dhcp_thread, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
293 thread_detach_and_resume(dhcp_thr);
294}
295
296// vim: set noexpandtab: