Line data Source code
1 :
2 : #include "AP_Networking_Config.h"
3 :
4 : #if AP_NETWORKING_ENABLED
5 :
6 : #include "AP_Networking.h"
7 : #include "AP_Networking_Backend.h"
8 : #include <GCS_MAVLink/GCS.h>
9 : #include <AP_Math/crc.h>
10 : #include <AP_InternalError/AP_InternalError.h>
11 : #include <AP_Filesystem/AP_Filesystem.h>
12 : #include <AP_HAL/CANIface.h>
13 :
14 : extern const AP_HAL::HAL& hal;
15 :
16 : #if AP_NETWORKING_BACKEND_CHIBIOS
17 : #include "AP_Networking_ChibiOS.h"
18 : #include <hal_mii.h>
19 : #endif
20 :
21 : #include <lwipopts.h>
22 : #include <errno.h>
23 :
24 :
25 : #include <AP_HAL/utility/Socket.h>
26 :
27 : #if AP_NETWORKING_BACKEND_PPP
28 : #include "AP_Networking_PPP.h"
29 : #endif
30 :
31 : #if AP_NETWORKING_BACKEND_SITL
32 : #include "AP_Networking_SITL.h"
33 : #endif
34 :
35 : #if AP_NETWORKING_BACKEND_SITL_TUN
36 : #include "AP_Networking_SITL_TUN.h"
37 : #endif
38 :
39 : const AP_Param::GroupInfo AP_Networking::var_info[] = {
40 : // @Param: ENABLE
41 : // @DisplayName: Networking Enable
42 : // @Description: Networking Enable
43 : // @Values: 0:Disable,1:Enable
44 : // @RebootRequired: True
45 : // @User: Advanced
46 : AP_GROUPINFO_FLAGS("ENABLE", 1, AP_Networking, param.enabled, 0, AP_PARAM_FLAG_ENABLE),
47 :
48 : #if AP_NETWORKING_CONTROLS_HOST_IP_SETTINGS_ENABLED
49 : // @Group: IPADDR
50 : // @Path: AP_Networking_address.cpp
51 : AP_SUBGROUPINFO(param.ipaddr, "IPADDR", 2, AP_Networking, AP_Networking_IPV4),
52 :
53 : // @Param: NETMASK
54 : // @DisplayName: IP Subnet mask
55 : // @Description: Allows setting static subnet mask. The value is a count of consecutive bits. Examples: 24 = 255.255.255.0, 16 = 255.255.0.0
56 : // @Range: 0 32
57 : // @RebootRequired: True
58 : // @User: Advanced
59 : AP_GROUPINFO("NETMASK", 3, AP_Networking, param.netmask, AP_NETWORKING_DEFAULT_NETMASK),
60 :
61 : #if AP_NETWORKING_DHCP_AVAILABLE
62 : // @Param: DHCP
63 : // @DisplayName: DHCP client
64 : // @Description: Enable/Disable DHCP client
65 : // @Values: 0:Disable, 1:Enable
66 : // @RebootRequired: True
67 : // @User: Advanced
68 : AP_GROUPINFO("DHCP", 4, AP_Networking, param.dhcp, AP_NETWORKING_DEFAULT_DHCP_ENABLE),
69 : #endif
70 :
71 : // @Group: GWADDR
72 : // @Path: AP_Networking_address.cpp
73 : AP_SUBGROUPINFO(param.gwaddr, "GWADDR", 5, AP_Networking, AP_Networking_IPV4),
74 :
75 : // @Group: MACADDR
76 : // @Path: AP_Networking_macaddr.cpp
77 : AP_SUBGROUPINFO(param.macaddr, "MACADDR", 6, AP_Networking, AP_Networking_MAC),
78 : #endif // AP_NETWORKING_CONTROLS_HOST_IP_SETTINGS_ENABLED
79 :
80 : #if AP_NETWORKING_TESTS_ENABLED
81 : // @Param: TESTS
82 : // @DisplayName: Test enable flags
83 : // @Description: Enable/Disable networking tests
84 : // @Bitmask: 0:UDP echo test,1:TCP echo test, 2:TCP discard test, 3:TCP reflect test
85 : // @RebootRequired: True
86 : // @User: Advanced
87 : AP_GROUPINFO("TESTS", 7, AP_Networking, param.tests, 0),
88 :
89 : // @Group: TEST_IP
90 : // @Path: AP_Networking_address.cpp
91 : AP_SUBGROUPINFO(param.test_ipaddr, "TEST_IP", 8, AP_Networking, AP_Networking_IPV4),
92 : #endif
93 :
94 : // @Param: OPTIONS
95 : // @DisplayName: Networking options
96 : // @Description: Networking options
97 : // @Bitmask: 0:EnablePPP Ethernet gateway, 1:Enable CAN1 multicast endpoint, 2:Enable CAN2 multicast endpoint, 3:Enable CAN1 multicast bridged, 4:Enable CAN2 multicast bridged, 5:DisablePPPTimeout, 6:DisablePPPEchoLimit, 7:Capture to file
98 : // @RebootRequired: True
99 : // @User: Advanced
100 : AP_GROUPINFO("OPTIONS", 9, AP_Networking, param.options, 0),
101 :
102 : #if AP_NETWORKING_PPP_GATEWAY_ENABLED
103 : // @Group: REMPPP_IP
104 : // @Path: AP_Networking_address.cpp
105 : AP_SUBGROUPINFO(param.remote_ppp_ip, "REMPPP_IP", 10, AP_Networking, AP_Networking_IPV4),
106 : #endif
107 :
108 : AP_GROUPEND
109 : };
110 :
111 : /*
112 : constructor
113 : */
114 10490 : AP_Networking::AP_Networking(void)
115 : {
116 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
117 2096 : if (singleton != nullptr) {
118 0 : AP_HAL::panic("AP_Networking must be singleton");
119 : }
120 : #endif
121 2098 : singleton = this;
122 2098 : AP_Param::setup_object_defaults(this, var_info);
123 2098 : }
124 :
125 : /*
126 : initialise networking subsystem
127 : */
128 2094 : void AP_Networking::init()
129 : {
130 2094 : if (!param.enabled || backend != nullptr) {
131 2086 : return;
132 : }
133 :
134 : #if AP_NETWORKING_CONTROLS_HOST_IP_SETTINGS_ENABLED
135 : // set default MAC Address as lower 3 bytes of the CRC of the UID
136 : uint8_t uid[50];
137 : uint8_t uid_len = sizeof(uid);
138 : if (hal.util->get_system_id_unformatted(uid, uid_len)) {
139 : union {
140 : uint8_t bytes[4];
141 : uint32_t value32;
142 : } crc;
143 : crc.value32 = crc_crc32(0, uid, uid_len);
144 :
145 : param.macaddr.set_default_address_byte(3, crc.bytes[0]);
146 : param.macaddr.set_default_address_byte(4, crc.bytes[1]);
147 : param.macaddr.set_default_address_byte(5, crc.bytes[2]);
148 : }
149 : #endif
150 :
151 : #if AP_NETWORKING_BACKEND_CHIBIOS && AP_NETWORKING_PPP_GATEWAY_ENABLED
152 : if (option_is_set(OPTION::PPP_ETHERNET_GATEWAY)) {
153 : /*
154 : when we are a PPP/Ethernet gateway we bring up the ethernet first
155 : */
156 : backend = NEW_NOTHROW AP_Networking_ChibiOS(*this);
157 : backend_PPP = NEW_NOTHROW AP_Networking_PPP(*this);
158 : }
159 : #endif
160 :
161 : #if AP_NETWORKING_BACKEND_SITL_TUN && AP_NETWORKING_PPP_GATEWAY_ENABLED
162 : // SITL TAP backend takes the same "ethernet" slot the ChibiOS backend
163 : // would on real hardware. Lets a sitl_periph_PPP build act as a PPPGW
164 : // whose ethernet side is a host TAP device, so developer tools on the
165 : // host can talk to the lwIP-served sockets (e.g. the PPPGW web UI).
166 : if (backend == nullptr && option_is_set(OPTION::PPP_ETHERNET_GATEWAY)) {
167 : backend = NEW_NOTHROW AP_Networking_SITL_TUN(*this);
168 : backend_PPP = NEW_NOTHROW AP_Networking_PPP(*this);
169 : }
170 : #endif
171 :
172 :
173 : #if AP_NETWORKING_BACKEND_PPP
174 : if (backend == nullptr && AP::serialmanager().have_serial(AP_SerialManager::SerialProtocol_PPP, 0)) {
175 : backend = NEW_NOTHROW AP_Networking_PPP(*this);
176 : }
177 : #endif
178 :
179 : #if AP_NETWORKING_BACKEND_CHIBIOS
180 : if (backend == nullptr) {
181 : backend = NEW_NOTHROW AP_Networking_ChibiOS(*this);
182 : }
183 : #endif
184 : #if AP_NETWORKING_BACKEND_SITL
185 8 : if (backend == nullptr) {
186 8 : backend = NEW_NOTHROW AP_Networking_SITL(*this);
187 : }
188 : #endif
189 :
190 8 : if (backend == nullptr) {
191 0 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "NET: backend failed");
192 0 : return;
193 : }
194 :
195 8 : if (!backend->init()) {
196 0 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "NET: backend init failed");
197 : // the backend init function creates a thread which references the backend pointer; that thread may be running so don't remove the backend allocation.
198 0 : backend = nullptr;
199 0 : return;
200 : }
201 :
202 : #if AP_NETWORKING_PPP_GATEWAY_ENABLED
203 : if (backend_PPP != nullptr && !backend_PPP->init()) {
204 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "NET: backend_PPP init failed");
205 : backend_PPP = nullptr;
206 : }
207 : #endif
208 :
209 8 : announce_address_changes();
210 :
211 8 : GCS_SEND_TEXT(MAV_SEVERITY_INFO,"NET: Initialized");
212 :
213 : #if AP_NETWORKING_TESTS_ENABLED
214 : start_tests();
215 : #endif
216 :
217 : #if AP_NETWORKING_CAN_MCAST_BRIDGING_ENABLED
218 : if (option_is_set(OPTION::CAN1_MCAST_ENDPOINT) ||
219 : option_is_set(OPTION::CAN2_MCAST_ENDPOINT)) {
220 : // get mask of enabled buses
221 : uint8_t bus_mask = 0;
222 : if (option_is_set(OPTION::CAN1_MCAST_ENDPOINT)) {
223 : bus_mask |= (1U<<0);
224 : }
225 : if (option_is_set(OPTION::CAN2_MCAST_ENDPOINT)) {
226 : bus_mask |= (1U<<1);
227 : }
228 : mcast_server.start(bus_mask);
229 : }
230 : #endif
231 :
232 : #if AP_NETWORKING_REGISTER_PORT_ENABLED
233 : // init network mapped serialmanager ports
234 8 : ports_init();
235 : #endif
236 : }
237 :
238 : /*
239 : check if we should announce changes to IP addresses
240 : */
241 5597 : void AP_Networking::announce_address_changes()
242 : {
243 5597 : const auto &as = backend->activeSettings;
244 :
245 5597 : if (as.last_change_ms == 0 || as.last_change_ms == announce_ms) {
246 : // nothing changed and we've already printed it at least once. Nothing to do.
247 5597 : return;
248 : }
249 :
250 : #if AP_HAVE_GCS_SEND_TEXT
251 : char ipstr[16];
252 0 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "NET: IP %s", SocketAPM::inet_addr_to_str(get_ip_active(), ipstr, sizeof(ipstr)));
253 0 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "NET: Mask %s", SocketAPM::inet_addr_to_str(get_netmask_active(), ipstr, sizeof(ipstr)));
254 0 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "NET: Gateway %s", SocketAPM::inet_addr_to_str(get_gateway_active(), ipstr, sizeof(ipstr)));
255 : #endif
256 :
257 0 : announce_ms = as.last_change_ms;
258 : }
259 :
260 : /*
261 : update called at 10Hz
262 : */
263 1504089 : void AP_Networking::update()
264 : {
265 1504089 : if (!is_healthy()) {
266 1498500 : return;
267 : }
268 5589 : backend->update();
269 5589 : announce_address_changes();
270 : }
271 :
272 0 : uint32_t AP_Networking::convert_netmask_bitcount_to_ip(const uint32_t netmask_bitcount)
273 : {
274 0 : if (netmask_bitcount >= 32) {
275 0 : return 0xFFFFFFFFU;
276 : }
277 0 : return ~((1U<<(32U-netmask_bitcount))-1U);
278 : }
279 :
280 0 : uint8_t AP_Networking::convert_netmask_ip_to_bitcount(const uint32_t netmask_ip)
281 : {
282 0 : uint32_t netmask_bitcount = 0;
283 0 : for (uint32_t i=0; i<32; i++) {
284 : // note, netmask LSB is IP MSB
285 0 : if ((netmask_ip & (1UL<<i)) == 0) {
286 0 : break;
287 : }
288 0 : netmask_bitcount++;
289 : }
290 0 : return netmask_bitcount;
291 : }
292 :
293 : /*
294 : convert a string to an ethernet MAC address
295 : */
296 0 : bool AP_Networking::convert_str_to_macaddr(const char *mac_str, uint8_t addr[6])
297 : {
298 0 : if (strlen(mac_str) != 17) {
299 0 : return false;
300 : }
301 : char s2[18];
302 0 : strncpy(s2, mac_str, sizeof(s2)-1);
303 0 : s2[17] = 0;
304 0 : char *ptr = nullptr;
305 0 : const char *s = strtok_r(s2, ":", &ptr);
306 0 : for (uint8_t i=0; i<6; i++) {
307 0 : if (s == nullptr) {
308 0 : return false;
309 : }
310 0 : auto v = strtoul(s, nullptr, 16);
311 0 : if (v > 255) {
312 0 : return false;
313 : }
314 0 : addr[i] = v;
315 0 : s = strtok_r(nullptr, ":", &ptr);
316 : }
317 0 : return true;
318 : }
319 :
320 : // returns the 32bit value of the active IP address that is currently in use
321 0 : uint32_t AP_Networking::get_ip_active() const
322 : {
323 0 : return backend?backend->activeSettings.ip:0;
324 : }
325 :
326 : // returns the 32bit value of the active Netmask that is currently in use
327 0 : uint32_t AP_Networking::get_netmask_active() const
328 : {
329 0 : return backend?backend->activeSettings.nm:0;
330 : }
331 :
332 0 : uint32_t AP_Networking::get_gateway_active() const
333 : {
334 0 : return backend?backend->activeSettings.gw:0;
335 : }
336 :
337 : /*
338 : wait for networking to be active
339 : */
340 11 : void AP_Networking::startup_wait(void) const
341 : {
342 11 : if (hal.scheduler->in_main_thread()) {
343 0 : INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
344 0 : return;
345 : }
346 235 : while (!hal.scheduler->is_system_initialized()) {
347 224 : hal.scheduler->delay(100);
348 : }
349 : #if AP_NETWORKING_BACKEND_CHIBIOS
350 : do {
351 : hal.scheduler->delay(250);
352 : } while (get_ip_active() == 0);
353 : #endif
354 : }
355 :
356 : /*
357 : send the rest of a file to a socket
358 : */
359 0 : bool AP_Networking::sendfile(SocketAPM *sock, int fd)
360 : {
361 0 : WITH_SEMAPHORE(sem);
362 0 : if (sendfile_buf == nullptr) {
363 0 : uint32_t bufsize = AP_NETWORKING_SENDFILE_BUFSIZE;
364 : do {
365 0 : sendfile_buf = (uint8_t *)hal.util->malloc_type(bufsize, AP_HAL::Util::MEM_FILESYSTEM);
366 0 : if (sendfile_buf != nullptr) {
367 0 : sendfile_bufsize = bufsize;
368 0 : break;
369 : }
370 0 : bufsize /= 2;
371 0 : } while (bufsize >= 4096);
372 0 : if (sendfile_buf == nullptr) {
373 0 : return false;
374 : }
375 : }
376 0 : if (!sendfile_thread_started) {
377 0 : if (!hal.scheduler->thread_create(FUNCTOR_BIND_MEMBER(&AP_Networking::sendfile_check, void),
378 : "sendfile",
379 : 2048, AP_HAL::Scheduler::PRIORITY_UART, 0)) {
380 0 : return false;
381 : }
382 0 : sendfile_thread_started = true;
383 : }
384 0 : for (auto &s : sendfiles) {
385 0 : if (s.sock == nullptr) {
386 0 : s.sock = sock->duplicate();
387 0 : if (s.sock == nullptr) {
388 0 : return false;
389 : }
390 0 : s.fd = fd;
391 0 : return true;
392 : }
393 : }
394 0 : return false;
395 0 : }
396 :
397 0 : void AP_Networking::SendFile::close(void)
398 : {
399 0 : AP::FS().close(fd);
400 0 : delete sock;
401 0 : sock = nullptr;
402 0 : }
403 :
404 : #include <stdio.h>
405 : /*
406 : check for sendfile updates
407 : */
408 0 : void AP_Networking::sendfile_check(void)
409 : {
410 : while (true) {
411 0 : hal.scheduler->delay(1);
412 0 : WITH_SEMAPHORE(sem);
413 0 : bool none_active = true;
414 0 : for (auto &s : sendfiles) {
415 0 : if (s.sock == nullptr) {
416 0 : continue;
417 : }
418 0 : none_active = false;
419 0 : if (!s.sock->pollout(0)) {
420 0 : continue;
421 : }
422 0 : const auto nread = AP::FS().read(s.fd, sendfile_buf, sendfile_bufsize);
423 0 : if (nread <= 0) {
424 0 : s.close();
425 0 : continue;
426 : }
427 0 : const auto nsent = s.sock->send(sendfile_buf, nread);
428 0 : if (nsent <= 0) {
429 0 : s.close();
430 0 : continue;
431 : }
432 0 : if (nsent < nread) {
433 0 : AP::FS().lseek(s.fd, nsent - nread, SEEK_CUR);
434 : }
435 : }
436 0 : if (none_active) {
437 0 : free(sendfile_buf);
438 0 : sendfile_buf = nullptr;
439 : }
440 0 : }
441 : }
442 :
443 : AP_Networking *AP_Networking::singleton;
444 :
445 : namespace AP
446 : {
447 11 : AP_Networking &network()
448 : {
449 11 : return *AP_Networking::get_singleton();
450 : }
451 : }
452 :
453 : /*
454 : debug printfs from LWIP
455 : */
456 0 : int ap_networking_printf(const char *fmt, ...)
457 : {
458 0 : WITH_SEMAPHORE(AP::network().get_semaphore());
459 : #ifdef AP_NETWORKING_LWIP_DEBUG_FILE
460 : static int fd = -1;
461 : if (fd == -1) {
462 : fd = AP::FS().open(AP_NETWORKING_LWIP_DEBUG_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0644);
463 : if (fd == -1) {
464 : return -1;
465 : }
466 : }
467 : va_list ap;
468 : va_start(ap, fmt);
469 : vdprintf(fd, fmt, ap);
470 : va_end(ap);
471 : #else
472 : va_list ap;
473 0 : va_start(ap, fmt);
474 0 : hal.console->vprintf(fmt, ap);
475 0 : va_end(ap);
476 : #endif
477 0 : return 0;
478 0 : }
479 :
480 : // address to string using a static return buffer
481 0 : const char *AP_Networking::address_to_str(uint32_t addr)
482 : {
483 : static char buf[16]; // 16 for aaa.bbb.ccc.ddd
484 0 : return SocketAPM::inet_addr_to_str(addr, buf, sizeof(buf));
485 : }
486 :
487 : #ifdef LWIP_PLATFORM_ASSERT
488 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL || defined(HAL_DEBUG_BUILD)
489 0 : void ap_networking_platform_assert(const char *msg, int line)
490 : {
491 0 : AP_HAL::panic("LWIP:%u %s", line, msg);
492 : }
493 : #else
494 : void ap_networking_platform_assert(int line)
495 : {
496 : AP_HAL::panic("LWIP:%u", line);
497 : }
498 : #endif
499 : #endif
500 :
501 : #ifdef LWIP_HOOK_IP4_ROUTE
502 : #include <lwip/ip4_addr.h>
503 0 : struct netif *ap_networking_routing_hook(const struct ip4_addr *dest_ip)
504 : {
505 0 : if (dest_ip == nullptr) {
506 0 : return nullptr;
507 : }
508 0 : return AP::network().routing_hook(ntohl(dest_ip->addr));
509 : }
510 : #endif
511 :
512 : /*
513 : check for custom routes
514 : */
515 0 : struct netif *AP_Networking::routing_hook(uint32_t dest)
516 : {
517 0 : if (backend) {
518 0 : auto *iface = backend->routing_hook(dest);
519 0 : if (iface != nullptr) {
520 0 : return iface;
521 : }
522 : }
523 : #if AP_NETWORKING_PPP_GATEWAY_ENABLED
524 : if (backend_PPP) {
525 : auto *iface = backend_PPP->routing_hook(dest);
526 : if (iface != nullptr) {
527 : return iface;
528 : }
529 : }
530 : #endif
531 0 : return nullptr;
532 : }
533 :
534 : // add new routes for an interface.
535 : // Returns true if the route is added or the route already exists
536 0 : bool AP_Networking::add_route(uint8_t backend_idx, uint8_t iface_idx, uint32_t dest_ip, uint8_t mask_len)
537 : {
538 0 : if (backend_idx == 0 &&
539 0 : backend != nullptr &&
540 0 : backend->add_route(iface_idx, dest_ip, mask_len)) {
541 0 : return true;
542 : }
543 : #if AP_NETWORKING_PPP_GATEWAY_ENABLED
544 : if (backend_idx == 1 &&
545 : backend_PPP != nullptr &&
546 : backend_PPP->add_route(iface_idx, dest_ip, mask_len)) {
547 : return true;
548 : }
549 : #endif
550 0 : return false;
551 : }
552 :
553 :
554 : #endif // AP_NETWORKING_ENABLED
|