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