瀏覽代碼

Add WiFi AP mode

This allows to choose, when running cmake, whether the bootloader will connect to an existing AP (default behavior)
or fire up its own (by passing -DPICOWOTA_WIFI_AP=1) by using the same provided credentials.
DHCP requests are handled, with these settings the subnet is 192.168.4.1/24.
Tomi 1 年之前
父節點
當前提交
d39bd485ad
共有 5 個檔案被更改,包括 409 行新增5 行删除
  1. 10
    2
      CMakeLists.txt
  2. 21
    0
      dhcpserver/LICENSE
  3. 300
    0
      dhcpserver/dhcpserver.c
  4. 49
    0
      dhcpserver/dhcpserver.h
  5. 29
    3
      main.c

+ 10
- 2
CMakeLists.txt 查看文件

@@ -36,6 +36,7 @@ pico_sdk_init()
36 36
 add_executable(picowota
37 37
 	main.c
38 38
 	tcp_comm.c
39
+	dhcpserver/dhcpserver.c
39 40
 )
40 41
 
41 42
 function(target_cl_options option)
@@ -51,8 +52,9 @@ target_link_options(picowota PRIVATE "LINKER:--gc-sections")
51 52
 
52 53
 pico_add_extra_outputs(picowota)
53 54
 
54
-# Needed so that lwip can find lwipopts.h
55
-target_include_directories(picowota PRIVATE ${CMAKE_CURRENT_LIST_DIR})
55
+target_include_directories(picowota PRIVATE
56
+	${CMAKE_CURRENT_LIST_DIR} # Needed so that lwip can find lwipopts.h
57
+	${CMAKE_CURRENT_LIST_DIR}/dhcpserver)
56 58
 
57 59
 pico_enable_stdio_usb(picowota 1)
58 60
 
@@ -92,6 +94,12 @@ endif ()
92 94
 target_compile_definitions(picowota PUBLIC PICOWOTA_WIFI_SSID=${PICOWOTA_WIFI_SSID})
93 95
 target_compile_definitions(picowota PUBLIC PICOWOTA_WIFI_PASS=${PICOWOTA_WIFI_PASS})
94 96
 
97
+# Use the WiFi AP mode upon request
98
+if (PICOWOTA_WIFI_AP)
99
+	target_compile_definitions(picowota PUBLIC PICOWOTA_WIFI_AP=1)
100
+	message("Building in WiFi AP mode.")
101
+endif()
102
+
95 103
 # Provide a helper to build a standalone target
96 104
 function(picowota_build_standalone NAME)
97 105
 	get_target_property(PICOWOTA_SRC_DIR picowota SOURCE_DIR)

+ 21
- 0
dhcpserver/LICENSE 查看文件

@@ -0,0 +1,21 @@
1
+The MIT License (MIT)
2
+
3
+Copyright (c) 2013-2022 Damien P. George
4
+
5
+Permission is hereby granted, free of charge, to any person obtaining a copy
6
+of this software and associated documentation files (the "Software"), to deal
7
+in the Software without restriction, including without limitation the rights
8
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+copies of the Software, and to permit persons to whom the Software is
10
+furnished to do so, subject to the following conditions:
11
+
12
+The above copyright notice and this permission notice shall be included in
13
+all copies or substantial portions of the Software.
14
+
15
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+THE SOFTWARE.

+ 300
- 0
dhcpserver/dhcpserver.c 查看文件

@@ -0,0 +1,300 @@
1
+/*
2
+ * This file is part of the MicroPython project, http://micropython.org/
3
+ *
4
+ * The MIT License (MIT)
5
+ *
6
+ * Copyright (c) 2018-2019 Damien P. George
7
+ *
8
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ * of this software and associated documentation files (the "Software"), to deal
10
+ * in the Software without restriction, including without limitation the rights
11
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ * copies of the Software, and to permit persons to whom the Software is
13
+ * furnished to do so, subject to the following conditions:
14
+ *
15
+ * The above copyright notice and this permission notice shall be included in
16
+ * all copies or substantial portions of the Software.
17
+ *
18
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ * THE SOFTWARE.
25
+ */
26
+
27
+// For DHCP specs see:
28
+//  https://www.ietf.org/rfc/rfc2131.txt
29
+//  https://tools.ietf.org/html/rfc2132 -- DHCP Options and BOOTP Vendor Extensions
30
+
31
+#include <stdio.h>
32
+#include <string.h>
33
+#include <errno.h>
34
+
35
+#include "cyw43_config.h"
36
+#include "dhcpserver.h"
37
+#include "lwip/udp.h"
38
+
39
+#define DHCPDISCOVER    (1)
40
+#define DHCPOFFER       (2)
41
+#define DHCPREQUEST     (3)
42
+#define DHCPDECLINE     (4)
43
+#define DHCPACK         (5)
44
+#define DHCPNACK        (6)
45
+#define DHCPRELEASE     (7)
46
+#define DHCPINFORM      (8)
47
+
48
+#define DHCP_OPT_PAD                (0)
49
+#define DHCP_OPT_SUBNET_MASK        (1)
50
+#define DHCP_OPT_ROUTER             (3)
51
+#define DHCP_OPT_DNS                (6)
52
+#define DHCP_OPT_HOST_NAME          (12)
53
+#define DHCP_OPT_REQUESTED_IP       (50)
54
+#define DHCP_OPT_IP_LEASE_TIME      (51)
55
+#define DHCP_OPT_MSG_TYPE           (53)
56
+#define DHCP_OPT_SERVER_ID          (54)
57
+#define DHCP_OPT_PARAM_REQUEST_LIST (55)
58
+#define DHCP_OPT_MAX_MSG_SIZE       (57)
59
+#define DHCP_OPT_VENDOR_CLASS_ID    (60)
60
+#define DHCP_OPT_CLIENT_ID          (61)
61
+#define DHCP_OPT_END                (255)
62
+
63
+#define PORT_DHCP_SERVER (67)
64
+#define PORT_DHCP_CLIENT (68)
65
+
66
+#define DEFAULT_DNS MAKE_IP4(8, 8, 8, 8)
67
+#define DEFAULT_LEASE_TIME_S (24 * 60 * 60) // in seconds
68
+
69
+#define MAC_LEN (6)
70
+#define MAKE_IP4(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d))
71
+
72
+typedef struct {
73
+    uint8_t op; // message opcode
74
+    uint8_t htype; // hardware address type
75
+    uint8_t hlen; // hardware address length
76
+    uint8_t hops;
77
+    uint32_t xid; // transaction id, chosen by client
78
+    uint16_t secs; // client seconds elapsed
79
+    uint16_t flags;
80
+    uint8_t ciaddr[4]; // client IP address
81
+    uint8_t yiaddr[4]; // your IP address
82
+    uint8_t siaddr[4]; // next server IP address
83
+    uint8_t giaddr[4]; // relay agent IP address
84
+    uint8_t chaddr[16]; // client hardware address
85
+    uint8_t sname[64]; // server host name
86
+    uint8_t file[128]; // boot file name
87
+    uint8_t options[312]; // optional parameters, variable, starts with magic
88
+} dhcp_msg_t;
89
+
90
+static int dhcp_socket_new_dgram(struct udp_pcb **udp, void *cb_data, udp_recv_fn cb_udp_recv) {
91
+    // family is AF_INET
92
+    // type is SOCK_DGRAM
93
+
94
+    *udp = udp_new();
95
+    if (*udp == NULL) {
96
+        return -ENOMEM;
97
+    }
98
+
99
+    // Register callback
100
+    udp_recv(*udp, cb_udp_recv, (void *)cb_data);
101
+
102
+    return 0; // success
103
+}
104
+
105
+static void dhcp_socket_free(struct udp_pcb **udp) {
106
+    if (*udp != NULL) {
107
+        udp_remove(*udp);
108
+        *udp = NULL;
109
+    }
110
+}
111
+
112
+static int dhcp_socket_bind(struct udp_pcb **udp, uint32_t ip, uint16_t port) {
113
+    ip_addr_t addr;
114
+    IP4_ADDR(&addr, ip >> 24 & 0xff, ip >> 16 & 0xff, ip >> 8 & 0xff, ip & 0xff);
115
+    // TODO convert lwIP errors to errno
116
+    return udp_bind(*udp, &addr, port);
117
+}
118
+
119
+static int dhcp_socket_sendto(struct udp_pcb **udp, const void *buf, size_t len, uint32_t ip, uint16_t port) {
120
+    if (len > 0xffff) {
121
+        len = 0xffff;
122
+    }
123
+
124
+    struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
125
+    if (p == NULL) {
126
+        return -ENOMEM;
127
+    }
128
+
129
+    memcpy(p->payload, buf, len);
130
+
131
+    ip_addr_t dest;
132
+    IP4_ADDR(&dest, ip >> 24 & 0xff, ip >> 16 & 0xff, ip >> 8 & 0xff, ip & 0xff);
133
+    err_t err = udp_sendto(*udp, p, &dest, port);
134
+
135
+    pbuf_free(p);
136
+
137
+    if (err != ERR_OK) {
138
+        return err;
139
+    }
140
+
141
+    return len;
142
+}
143
+
144
+static uint8_t *opt_find(uint8_t *opt, uint8_t cmd) {
145
+    for (int i = 0; i < 308 && opt[i] != DHCP_OPT_END;) {
146
+        if (opt[i] == cmd) {
147
+            return &opt[i];
148
+        }
149
+        i += 2 + opt[i + 1];
150
+    }
151
+    return NULL;
152
+}
153
+
154
+static void opt_write_n(uint8_t **opt, uint8_t cmd, size_t n, void *data) {
155
+    uint8_t *o = *opt;
156
+    *o++ = cmd;
157
+    *o++ = n;
158
+    memcpy(o, data, n);
159
+    *opt = o + n;
160
+}
161
+
162
+static void opt_write_u8(uint8_t **opt, uint8_t cmd, uint8_t val) {
163
+    uint8_t *o = *opt;
164
+    *o++ = cmd;
165
+    *o++ = 1;
166
+    *o++ = val;
167
+    *opt = o;
168
+}
169
+
170
+static void opt_write_u32(uint8_t **opt, uint8_t cmd, uint32_t val) {
171
+    uint8_t *o = *opt;
172
+    *o++ = cmd;
173
+    *o++ = 4;
174
+    *o++ = val >> 24;
175
+    *o++ = val >> 16;
176
+    *o++ = val >> 8;
177
+    *o++ = val;
178
+    *opt = o;
179
+}
180
+
181
+static void dhcp_server_process(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *src_addr, u16_t src_port) {
182
+    dhcp_server_t *d = arg;
183
+    (void)upcb;
184
+    (void)src_addr;
185
+    (void)src_port;
186
+
187
+    // This is around 548 bytes
188
+    dhcp_msg_t dhcp_msg;
189
+
190
+    #define DHCP_MIN_SIZE (240 + 3)
191
+    if (p->tot_len < DHCP_MIN_SIZE) {
192
+        goto ignore_request;
193
+    }
194
+
195
+    size_t len = pbuf_copy_partial(p, &dhcp_msg, sizeof(dhcp_msg), 0);
196
+    if (len < DHCP_MIN_SIZE) {
197
+        goto ignore_request;
198
+    }
199
+
200
+    dhcp_msg.op = DHCPOFFER;
201
+    memcpy(&dhcp_msg.yiaddr, &d->ip.addr, 4);
202
+
203
+    uint8_t *opt = (uint8_t *)&dhcp_msg.options;
204
+    opt += 4; // assume magic cookie: 99, 130, 83, 99
205
+
206
+    switch (opt[2]) {
207
+        case DHCPDISCOVER: {
208
+            int yi = DHCPS_MAX_IP;
209
+            for (int i = 0; i < DHCPS_MAX_IP; ++i) {
210
+                if (memcmp(d->lease[i].mac, dhcp_msg.chaddr, MAC_LEN) == 0) {
211
+                    // MAC match, use this IP address
212
+                    yi = i;
213
+                    break;
214
+                }
215
+                if (yi == DHCPS_MAX_IP) {
216
+                    // Look for a free IP address
217
+                    if (memcmp(d->lease[i].mac, "\x00\x00\x00\x00\x00\x00", MAC_LEN) == 0) {
218
+                        // IP available
219
+                        yi = i;
220
+                    }
221
+                    uint32_t expiry = d->lease[i].expiry << 16 | 0xffff;
222
+                    if ((int32_t)(expiry - cyw43_hal_ticks_ms()) < 0) {
223
+                        // IP expired, reuse it
224
+                        memset(d->lease[i].mac, 0, MAC_LEN);
225
+                        yi = i;
226
+                    }
227
+                }
228
+            }
229
+            if (yi == DHCPS_MAX_IP) {
230
+                // No more IP addresses left
231
+                goto ignore_request;
232
+            }
233
+            dhcp_msg.yiaddr[3] = DHCPS_BASE_IP + yi;
234
+            opt_write_u8(&opt, DHCP_OPT_MSG_TYPE, DHCPOFFER);
235
+            break;
236
+        }
237
+
238
+        case DHCPREQUEST: {
239
+            uint8_t *o = opt_find(opt, DHCP_OPT_REQUESTED_IP);
240
+            if (o == NULL) {
241
+                // Should be NACK
242
+                goto ignore_request;
243
+            }
244
+            if (memcmp(o + 2, &d->ip.addr, 3) != 0) {
245
+                // Should be NACK
246
+                goto ignore_request;
247
+            }
248
+            uint8_t yi = o[5] - DHCPS_BASE_IP;
249
+            if (yi >= DHCPS_MAX_IP) {
250
+                // Should be NACK
251
+                goto ignore_request;
252
+            }
253
+            if (memcmp(d->lease[yi].mac, dhcp_msg.chaddr, MAC_LEN) == 0) {
254
+                // MAC match, ok to use this IP address
255
+            } else if (memcmp(d->lease[yi].mac, "\x00\x00\x00\x00\x00\x00", MAC_LEN) == 0) {
256
+                // IP unused, ok to use this IP address
257
+                memcpy(d->lease[yi].mac, dhcp_msg.chaddr, MAC_LEN);
258
+            } else {
259
+                // IP already in use
260
+                // Should be NACK
261
+                goto ignore_request;
262
+            }
263
+            d->lease[yi].expiry = (cyw43_hal_ticks_ms() + DEFAULT_LEASE_TIME_S * 1000) >> 16;
264
+            dhcp_msg.yiaddr[3] = DHCPS_BASE_IP + yi;
265
+            opt_write_u8(&opt, DHCP_OPT_MSG_TYPE, DHCPACK);
266
+            printf("DHCPS: client connected: MAC=%02x:%02x:%02x:%02x:%02x:%02x IP=%u.%u.%u.%u\n",
267
+                dhcp_msg.chaddr[0], dhcp_msg.chaddr[1], dhcp_msg.chaddr[2], dhcp_msg.chaddr[3], dhcp_msg.chaddr[4], dhcp_msg.chaddr[5],
268
+                dhcp_msg.yiaddr[0], dhcp_msg.yiaddr[1], dhcp_msg.yiaddr[2], dhcp_msg.yiaddr[3]);
269
+            break;
270
+        }
271
+
272
+        default:
273
+            goto ignore_request;
274
+    }
275
+
276
+    opt_write_n(&opt, DHCP_OPT_SERVER_ID, 4, &d->ip.addr);
277
+    opt_write_n(&opt, DHCP_OPT_SUBNET_MASK, 4, &d->nm.addr);
278
+    opt_write_n(&opt, DHCP_OPT_ROUTER, 4, &d->ip.addr); // aka gateway; can have mulitple addresses
279
+    opt_write_u32(&opt, DHCP_OPT_DNS, DEFAULT_DNS); // can have mulitple addresses
280
+    opt_write_u32(&opt, DHCP_OPT_IP_LEASE_TIME, DEFAULT_LEASE_TIME_S);
281
+    *opt++ = DHCP_OPT_END;
282
+    dhcp_socket_sendto(&d->udp, &dhcp_msg, opt - (uint8_t *)&dhcp_msg, 0xffffffff, PORT_DHCP_CLIENT);
283
+
284
+ignore_request:
285
+    pbuf_free(p);
286
+}
287
+
288
+void dhcp_server_init(dhcp_server_t *d, ip_addr_t *ip, ip_addr_t *nm) {
289
+    ip_addr_copy(d->ip, *ip);
290
+    ip_addr_copy(d->nm, *nm);
291
+    memset(d->lease, 0, sizeof(d->lease));
292
+    if (dhcp_socket_new_dgram(&d->udp, d, dhcp_server_process) != 0) {
293
+        return;
294
+    }
295
+    dhcp_socket_bind(&d->udp, 0, PORT_DHCP_SERVER);
296
+}
297
+
298
+void dhcp_server_deinit(dhcp_server_t *d) {
299
+    dhcp_socket_free(&d->udp);
300
+}

+ 49
- 0
dhcpserver/dhcpserver.h 查看文件

@@ -0,0 +1,49 @@
1
+/*
2
+ * This file is part of the MicroPython project, http://micropython.org/
3
+ *
4
+ * The MIT License (MIT)
5
+ *
6
+ * Copyright (c) 2018-2019 Damien P. George
7
+ *
8
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ * of this software and associated documentation files (the "Software"), to deal
10
+ * in the Software without restriction, including without limitation the rights
11
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ * copies of the Software, and to permit persons to whom the Software is
13
+ * furnished to do so, subject to the following conditions:
14
+ *
15
+ * The above copyright notice and this permission notice shall be included in
16
+ * all copies or substantial portions of the Software.
17
+ *
18
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ * THE SOFTWARE.
25
+ */
26
+#ifndef MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H
27
+#define MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H
28
+
29
+#include "lwip/ip_addr.h"
30
+
31
+#define DHCPS_BASE_IP (16)
32
+#define DHCPS_MAX_IP (8)
33
+
34
+typedef struct _dhcp_server_lease_t {
35
+    uint8_t mac[6];
36
+    uint16_t expiry;
37
+} dhcp_server_lease_t;
38
+
39
+typedef struct _dhcp_server_t {
40
+    ip_addr_t ip;
41
+    ip_addr_t nm;
42
+    dhcp_server_lease_t lease[DHCPS_MAX_IP];
43
+    struct udp_pcb *udp;
44
+} dhcp_server_t;
45
+
46
+void dhcp_server_init(dhcp_server_t *d, ip_addr_t *ip, ip_addr_t *nm);
47
+void dhcp_server_deinit(dhcp_server_t *d);
48
+
49
+#endif // MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H

+ 29
- 3
main.c 查看文件

@@ -40,6 +40,11 @@
40 40
 #define DBG_PRINTF(...) { }
41 41
 #endif
42 42
 
43
+#if PICOWOTA_WIFI_AP == 1
44
+#include "dhcpserver.h"
45
+static dhcp_server_t dhcp_server;
46
+#endif
47
+
43 48
 #define QUOTE(name) #name
44 49
 #define STR(macro) QUOTE(macro)
45 50
 
@@ -557,6 +562,14 @@ static bool should_stay_in_bootloader()
557 562
 	return !gpio_get(BOOTLOADER_ENTRY_PIN) || wd_says_so;
558 563
 }
559 564
 
565
+static void network_deinit()
566
+{
567
+#if PICOWOTA_WIFI_AP == 1
568
+	dhcp_server_deinit(&dhcp_server);
569
+#endif
570
+	cyw43_arch_deinit();
571
+}
572
+
560 573
 int main()
561 574
 {
562 575
 	err_t err;
@@ -585,6 +598,18 @@ int main()
585 598
 		return 1;
586 599
 	}
587 600
 
601
+#if PICOWOTA_WIFI_AP == 1
602
+	cyw43_arch_enable_ap_mode(wifi_ssid, wifi_pass, CYW43_AUTH_WPA2_AES_PSK);
603
+	DBG_PRINTF("Enabled the WiFi AP.\n");
604
+
605
+	ip4_addr_t gw, mask;
606
+	IP4_ADDR(&gw, 192, 168, 4, 1);
607
+	IP4_ADDR(&mask, 255, 255, 255, 0);
608
+
609
+	dhcp_server_t dhcp_server;
610
+	dhcp_server_init(&dhcp_server, &gw, &mask);
611
+	DBG_PRINTF("Started the DHCP server.\n");
612
+#else
588 613
 	cyw43_arch_enable_sta_mode();
589 614
 
590 615
 	DBG_PRINTF("Connecting to WiFi...\n");
@@ -594,6 +619,7 @@ int main()
594 619
 	} else {
595 620
 		DBG_PRINTF("Connected.\n");
596 621
 	}
622
+#endif
597 623
 
598 624
 	critical_section_init(&critical_section);
599 625
 
@@ -629,13 +655,13 @@ int main()
629 655
 				break;
630 656
 			case EVENT_TYPE_REBOOT:
631 657
 				tcp_comm_server_close(tcp);
632
-				cyw43_arch_deinit();
658
+				network_deinit();
633 659
 				picowota_reboot(ev.reboot.to_bootloader);
634 660
 				/* Should never get here */
635 661
 				break;
636 662
 			case EVENT_TYPE_GO:
637 663
 				tcp_comm_server_close(tcp);
638
-				cyw43_arch_deinit();
664
+				network_deinit();
639 665
 				disable_interrupts();
640 666
 				reset_peripherals();
641 667
 				jump_to_vtor(ev.go.vtor);
@@ -648,6 +674,6 @@ int main()
648 674
 		sleep_ms(5);
649 675
 	}
650 676
 
651
-	cyw43_arch_deinit();
677
+	network_deinit();
652 678
 	return 0;
653 679
 }

Loading…
取消
儲存