diff options
Diffstat (limited to 'circuitpython/shared/netutils')
| -rw-r--r-- | circuitpython/shared/netutils/dhcpserver.c | 304 | ||||
| -rw-r--r-- | circuitpython/shared/netutils/dhcpserver.h | 49 | ||||
| -rw-r--r-- | circuitpython/shared/netutils/netutils.c | 95 | ||||
| -rw-r--r-- | circuitpython/shared/netutils/netutils.h | 56 | ||||
| -rw-r--r-- | circuitpython/shared/netutils/trace.c | 170 | 
5 files changed, 674 insertions, 0 deletions
| diff --git a/circuitpython/shared/netutils/dhcpserver.c b/circuitpython/shared/netutils/dhcpserver.c new file mode 100644 index 0000000..9db42b3 --- /dev/null +++ b/circuitpython/shared/netutils/dhcpserver.c @@ -0,0 +1,304 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018-2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// For DHCP specs see: +//  https://www.ietf.org/rfc/rfc2131.txt +//  https://tools.ietf.org/html/rfc2132 -- DHCP Options and BOOTP Vendor Extensions + +#include <stdio.h> +#include <string.h> +#include "py/mperrno.h" +#include "py/mphal.h" + +#if MICROPY_PY_LWIP + +#include "shared/netutils/dhcpserver.h" +#include "lwip/udp.h" + +#define DHCPDISCOVER    (1) +#define DHCPOFFER       (2) +#define DHCPREQUEST     (3) +#define DHCPDECLINE     (4) +#define DHCPACK         (5) +#define DHCPNACK        (6) +#define DHCPRELEASE     (7) +#define DHCPINFORM      (8) + +#define DHCP_OPT_PAD                (0) +#define DHCP_OPT_SUBNET_MASK        (1) +#define DHCP_OPT_ROUTER             (3) +#define DHCP_OPT_DNS                (6) +#define DHCP_OPT_HOST_NAME          (12) +#define DHCP_OPT_REQUESTED_IP       (50) +#define DHCP_OPT_IP_LEASE_TIME      (51) +#define DHCP_OPT_MSG_TYPE           (53) +#define DHCP_OPT_SERVER_ID          (54) +#define DHCP_OPT_PARAM_REQUEST_LIST (55) +#define DHCP_OPT_MAX_MSG_SIZE       (57) +#define DHCP_OPT_VENDOR_CLASS_ID    (60) +#define DHCP_OPT_CLIENT_ID          (61) +#define DHCP_OPT_END                (255) + +#define PORT_DHCP_SERVER (67) +#define PORT_DHCP_CLIENT (68) + +#define DEFAULT_DNS MAKE_IP4(8, 8, 8, 8) +#define DEFAULT_LEASE_TIME_S (24 * 60 * 60) // in seconds + +#define MAC_LEN (6) +#define MAKE_IP4(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d)) + +typedef struct { +    uint8_t op; // message opcode +    uint8_t htype; // hardware address type +    uint8_t hlen; // hardware address length +    uint8_t hops; +    uint32_t xid; // transaction id, chosen by client +    uint16_t secs; // client seconds elapsed +    uint16_t flags; +    uint8_t ciaddr[4]; // client IP address +    uint8_t yiaddr[4]; // your IP address +    uint8_t siaddr[4]; // next server IP address +    uint8_t giaddr[4]; // relay agent IP address +    uint8_t chaddr[16]; // client hardware address +    uint8_t sname[64]; // server host name +    uint8_t file[128]; // boot file name +    uint8_t options[312]; // optional parameters, variable, starts with magic +} dhcp_msg_t; + +static int dhcp_socket_new_dgram(struct udp_pcb **udp, void *cb_data, udp_recv_fn cb_udp_recv) { +    // family is AF_INET +    // type is SOCK_DGRAM + +    *udp = udp_new(); +    if (*udp == NULL) { +        return -MP_ENOMEM; +    } + +    // Register callback +    udp_recv(*udp, cb_udp_recv, (void *)cb_data); + +    return 0; // success +} + +static void dhcp_socket_free(struct udp_pcb **udp) { +    if (*udp != NULL) { +        udp_remove(*udp); +        *udp = NULL; +    } +} + +static int dhcp_socket_bind(struct udp_pcb **udp, uint32_t ip, uint16_t port) { +    ip_addr_t addr; +    IP4_ADDR(&addr, ip >> 24 & 0xff, ip >> 16 & 0xff, ip >> 8 & 0xff, ip & 0xff); +    // TODO convert lwIP errors to errno +    return udp_bind(*udp, &addr, port); +} + +static int dhcp_socket_sendto(struct udp_pcb **udp, const void *buf, size_t len, uint32_t ip, uint16_t port) { +    if (len > 0xffff) { +        len = 0xffff; +    } + +    struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); +    if (p == NULL) { +        return -MP_ENOMEM; +    } + +    memcpy(p->payload, buf, len); + +    ip_addr_t dest; +    IP4_ADDR(&dest, ip >> 24 & 0xff, ip >> 16 & 0xff, ip >> 8 & 0xff, ip & 0xff); +    err_t err = udp_sendto(*udp, p, &dest, port); + +    pbuf_free(p); + +    if (err != ERR_OK) { +        return err; +    } + +    return len; +} + +static uint8_t *opt_find(uint8_t *opt, uint8_t cmd) { +    for (int i = 0; i < 308 && opt[i] != DHCP_OPT_END;) { +        if (opt[i] == cmd) { +            return &opt[i]; +        } +        i += 2 + opt[i + 1]; +    } +    return NULL; +} + +static void opt_write_n(uint8_t **opt, uint8_t cmd, size_t n, void *data) { +    uint8_t *o = *opt; +    *o++ = cmd; +    *o++ = n; +    memcpy(o, data, n); +    *opt = o + n; +} + +static void opt_write_u8(uint8_t **opt, uint8_t cmd, uint8_t val) { +    uint8_t *o = *opt; +    *o++ = cmd; +    *o++ = 1; +    *o++ = val; +    *opt = o; +} + +static void opt_write_u32(uint8_t **opt, uint8_t cmd, uint32_t val) { +    uint8_t *o = *opt; +    *o++ = cmd; +    *o++ = 4; +    *o++ = val >> 24; +    *o++ = val >> 16; +    *o++ = val >> 8; +    *o++ = val; +    *opt = o; +} + +static void dhcp_server_process(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *src_addr, u16_t src_port) { +    dhcp_server_t *d = arg; +    (void)upcb; +    (void)src_addr; +    (void)src_port; + +    // This is around 548 bytes +    dhcp_msg_t dhcp_msg; + +    #define DHCP_MIN_SIZE (240 + 3) +    if (p->tot_len < DHCP_MIN_SIZE) { +        goto ignore_request; +    } + +    size_t len = pbuf_copy_partial(p, &dhcp_msg, sizeof(dhcp_msg), 0); +    if (len < DHCP_MIN_SIZE) { +        goto ignore_request; +    } + +    dhcp_msg.op = DHCPOFFER; +    memcpy(&dhcp_msg.yiaddr, &d->ip.addr, 4); + +    uint8_t *opt = (uint8_t *)&dhcp_msg.options; +    opt += 4; // assume magic cookie: 99, 130, 83, 99 + +    switch (opt[2]) { +        case DHCPDISCOVER: { +            int yi = DHCPS_MAX_IP; +            for (int i = 0; i < DHCPS_MAX_IP; ++i) { +                if (memcmp(d->lease[i].mac, dhcp_msg.chaddr, MAC_LEN) == 0) { +                    // MAC match, use this IP address +                    yi = i; +                    break; +                } +                if (yi == DHCPS_MAX_IP) { +                    // Look for a free IP address +                    if (memcmp(d->lease[i].mac, "\x00\x00\x00\x00\x00\x00", MAC_LEN) == 0) { +                        // IP available +                        yi = i; +                    } +                    uint32_t expiry = d->lease[i].expiry << 16 | 0xffff; +                    if ((int32_t)(expiry - mp_hal_ticks_ms()) < 0) { +                        // IP expired, reuse it +                        memset(d->lease[i].mac, 0, MAC_LEN); +                        yi = i; +                    } +                } +            } +            if (yi == DHCPS_MAX_IP) { +                // No more IP addresses left +                goto ignore_request; +            } +            dhcp_msg.yiaddr[3] = DHCPS_BASE_IP + yi; +            opt_write_u8(&opt, DHCP_OPT_MSG_TYPE, DHCPOFFER); +            break; +        } + +        case DHCPREQUEST: { +            uint8_t *o = opt_find(opt, DHCP_OPT_REQUESTED_IP); +            if (o == NULL) { +                // Should be NACK +                goto ignore_request; +            } +            if (memcmp(o + 2, &d->ip.addr, 3) != 0) { +                // Should be NACK +                goto ignore_request; +            } +            uint8_t yi = o[5] - DHCPS_BASE_IP; +            if (yi >= DHCPS_MAX_IP) { +                // Should be NACK +                goto ignore_request; +            } +            if (memcmp(d->lease[yi].mac, dhcp_msg.chaddr, MAC_LEN) == 0) { +                // MAC match, ok to use this IP address +            } else if (memcmp(d->lease[yi].mac, "\x00\x00\x00\x00\x00\x00", MAC_LEN) == 0) { +                // IP unused, ok to use this IP address +                memcpy(d->lease[yi].mac, dhcp_msg.chaddr, MAC_LEN); +            } else { +                // IP already in use +                // Should be NACK +                goto ignore_request; +            } +            d->lease[yi].expiry = (mp_hal_ticks_ms() + DEFAULT_LEASE_TIME_S * 1000) >> 16; +            dhcp_msg.yiaddr[3] = DHCPS_BASE_IP + yi; +            opt_write_u8(&opt, DHCP_OPT_MSG_TYPE, DHCPACK); +            printf("DHCPS: client connected: MAC=%02x:%02x:%02x:%02x:%02x:%02x IP=%u.%u.%u.%u\n", +                dhcp_msg.chaddr[0], dhcp_msg.chaddr[1], dhcp_msg.chaddr[2], dhcp_msg.chaddr[3], dhcp_msg.chaddr[4], dhcp_msg.chaddr[5], +                dhcp_msg.yiaddr[0], dhcp_msg.yiaddr[1], dhcp_msg.yiaddr[2], dhcp_msg.yiaddr[3]); +            break; +        } + +        default: +            goto ignore_request; +    } + +    opt_write_n(&opt, DHCP_OPT_SERVER_ID, 4, &d->ip.addr); +    opt_write_n(&opt, DHCP_OPT_SUBNET_MASK, 4, &d->nm.addr); +    opt_write_n(&opt, DHCP_OPT_ROUTER, 4, &d->ip.addr); // aka gateway; can have mulitple addresses +    opt_write_u32(&opt, DHCP_OPT_DNS, DEFAULT_DNS); // can have mulitple addresses +    opt_write_u32(&opt, DHCP_OPT_IP_LEASE_TIME, DEFAULT_LEASE_TIME_S); +    *opt++ = DHCP_OPT_END; +    dhcp_socket_sendto(&d->udp, &dhcp_msg, opt - (uint8_t *)&dhcp_msg, 0xffffffff, PORT_DHCP_CLIENT); + +ignore_request: +    pbuf_free(p); +} + +void dhcp_server_init(dhcp_server_t *d, ip_addr_t *ip, ip_addr_t *nm) { +    ip_addr_copy(d->ip, *ip); +    ip_addr_copy(d->nm, *nm); +    memset(d->lease, 0, sizeof(d->lease)); +    if (dhcp_socket_new_dgram(&d->udp, d, dhcp_server_process) != 0) { +        return; +    } +    dhcp_socket_bind(&d->udp, 0, PORT_DHCP_SERVER); +} + +void dhcp_server_deinit(dhcp_server_t *d) { +    dhcp_socket_free(&d->udp); +} + +#endif // MICROPY_PY_LWIP diff --git a/circuitpython/shared/netutils/dhcpserver.h b/circuitpython/shared/netutils/dhcpserver.h new file mode 100644 index 0000000..2349d2e --- /dev/null +++ b/circuitpython/shared/netutils/dhcpserver.h @@ -0,0 +1,49 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018-2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H +#define MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H + +#include "lwip/ip_addr.h" + +#define DHCPS_BASE_IP (16) +#define DHCPS_MAX_IP (8) + +typedef struct _dhcp_server_lease_t { +    uint8_t mac[6]; +    uint16_t expiry; +} dhcp_server_lease_t; + +typedef struct _dhcp_server_t { +    ip_addr_t ip; +    ip_addr_t nm; +    dhcp_server_lease_t lease[DHCPS_MAX_IP]; +    struct udp_pcb *udp; +} dhcp_server_t; + +void dhcp_server_init(dhcp_server_t *d, ip_addr_t *ip, ip_addr_t *nm); +void dhcp_server_deinit(dhcp_server_t *d); + +#endif // MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H diff --git a/circuitpython/shared/netutils/netutils.c b/circuitpython/shared/netutils/netutils.c new file mode 100644 index 0000000..fe92e8b --- /dev/null +++ b/circuitpython/shared/netutils/netutils.c @@ -0,0 +1,95 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include "py/runtime.h" +#include "shared/netutils/netutils.h" +#include "supervisor/shared/translate.h" + +// Takes an array with a raw IPv4 address and returns something like '192.168.0.1'. +mp_obj_t netutils_format_ipv4_addr(uint8_t *ip, netutils_endian_t endian) { +    char ip_str[16]; +    mp_uint_t ip_len; +    if (endian == NETUTILS_LITTLE) { +        ip_len = snprintf(ip_str, 16, "%u.%u.%u.%u", ip[3], ip[2], ip[1], ip[0]); +    } else { +        ip_len = snprintf(ip_str, 16, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); +    } +    return mp_obj_new_str(ip_str, ip_len); +} + +// Takes an array with a raw IP address, and a port, and returns a net-address +// tuple such as ('192.168.0.1', 8080). +mp_obj_t netutils_format_inet_addr(uint8_t *ip, mp_uint_t port, netutils_endian_t endian) { +    mp_obj_t tuple[2] = { +        tuple[0] = netutils_format_ipv4_addr(ip, endian), +        tuple[1] = mp_obj_new_int(port), +    }; +    return mp_obj_new_tuple(2, tuple); +} + +void netutils_parse_ipv4_addr(mp_obj_t addr_in, uint8_t *out_ip, netutils_endian_t endian) { +    size_t addr_len; +    const char *addr_str = mp_obj_str_get_data(addr_in, &addr_len); +    if (addr_len == 0) { +        // special case of no address given +        memset(out_ip, 0, NETUTILS_IPV4ADDR_BUFSIZE); +        return; +    } +    const char *s = addr_str; +    const char *s_top = addr_str + addr_len; +    for (mp_uint_t i = 3; ; i--) { +        mp_uint_t val = 0; +        for (; s < s_top && *s != '.'; s++) { +            val = val * 10 + *s - '0'; +        } +        if (endian == NETUTILS_LITTLE) { +            out_ip[i] = val; +        } else { +            out_ip[NETUTILS_IPV4ADDR_BUFSIZE - 1 - i] = val; +        } +        if (i == 0 && s == s_top) { +            return; +        } else if (i > 0 && s < s_top && *s == '.') { +            s++; +        } else { +            mp_raise_ValueError(MP_ERROR_TEXT("invalid arguments")); +        } +    } +} + +// Takes an address of the form ('192.168.0.1', 8080), returns the port and +// puts IP in out_ip (which must take at least IPADDR_BUF_SIZE bytes). +mp_uint_t netutils_parse_inet_addr(mp_obj_t addr_in, uint8_t *out_ip, netutils_endian_t endian) { +    mp_obj_t *addr_items; +    mp_obj_get_array_fixed_n(addr_in, 2, &addr_items); +    netutils_parse_ipv4_addr(addr_items[0], out_ip, endian); +    return mp_obj_get_int(addr_items[1]); +} diff --git a/circuitpython/shared/netutils/netutils.h b/circuitpython/shared/netutils/netutils.h new file mode 100644 index 0000000..b23a78e --- /dev/null +++ b/circuitpython/shared/netutils/netutils.h @@ -0,0 +1,56 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_LIB_NETUTILS_NETUTILS_H +#define MICROPY_INCLUDED_LIB_NETUTILS_NETUTILS_H + +#define NETUTILS_IPV4ADDR_BUFSIZE    4 + +#define NETUTILS_TRACE_IS_TX        (0x0001) +#define NETUTILS_TRACE_PAYLOAD      (0x0002) +#define NETUTILS_TRACE_NEWLINE      (0x0004) + +typedef enum _netutils_endian_t { +    NETUTILS_LITTLE, +    NETUTILS_BIG, +} netutils_endian_t; + +// Takes an array with a raw IPv4 address and returns something like '192.168.0.1'. +mp_obj_t netutils_format_ipv4_addr(uint8_t *ip, netutils_endian_t endian); + +// Takes an array with a raw IP address, and a port, and returns a net-address +// tuple such as ('192.168.0.1', 8080). +mp_obj_t netutils_format_inet_addr(uint8_t *ip, mp_uint_t port, netutils_endian_t endian); + +void netutils_parse_ipv4_addr(mp_obj_t addr_in, uint8_t *out_ip, netutils_endian_t endian); + +// Takes an address of the form ('192.168.0.1', 8080), returns the port and +// puts IP in out_ip (which must take at least IPADDR_BUF_SIZE bytes). +mp_uint_t netutils_parse_inet_addr(mp_obj_t addr_in, uint8_t *out_ip, netutils_endian_t endian); + +void netutils_ethernet_trace(const mp_print_t *print, size_t len, const uint8_t *buf, unsigned int flags); + +#endif // MICROPY_INCLUDED_LIB_NETUTILS_NETUTILS_H diff --git a/circuitpython/shared/netutils/trace.c b/circuitpython/shared/netutils/trace.c new file mode 100644 index 0000000..a6dfb42 --- /dev/null +++ b/circuitpython/shared/netutils/trace.c @@ -0,0 +1,170 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/mphal.h" +#include "shared/netutils/netutils.h" + +static uint32_t get_be16(const uint8_t *buf) { +    return buf[0] << 8 | buf[1]; +} + +static uint32_t get_be32(const uint8_t *buf) { +    return buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; +} + +static void dump_hex_bytes(const mp_print_t *print, size_t len, const uint8_t *buf) { +    for (size_t i = 0; i < len; ++i) { +        mp_printf(print, " %02x", buf[i]); +    } +} + +static const char *ethertype_str(uint16_t type) { +    // A value between 0x0000 - 0x05dc (inclusive) indicates a length, not type +    switch (type) { +        case 0x0800: +            return "IPv4"; +        case 0x0806: +            return "ARP"; +        case 0x86dd: +            return "IPv6"; +        default: +            return NULL; +    } +} + +void netutils_ethernet_trace(const mp_print_t *print, size_t len, const uint8_t *buf, unsigned int flags) { +    mp_printf(print, "[% 8d] ETH%cX len=%u", mp_hal_ticks_ms(), flags & NETUTILS_TRACE_IS_TX ? 'T' : 'R', len); +    mp_printf(print, " dst=%02x:%02x:%02x:%02x:%02x:%02x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); +    mp_printf(print, " src=%02x:%02x:%02x:%02x:%02x:%02x", buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]); + +    const char *ethertype = ethertype_str(buf[12] << 8 | buf[13]); +    if (ethertype) { +        mp_printf(print, " type=%s", ethertype); +    } else { +        mp_printf(print, " type=0x%04x", buf[12] << 8 | buf[13]); +    } +    if (len > 14) { +        len -= 14; +        buf += 14; +        if (buf[-2] == 0x08 && buf[-1] == 0x00 && buf[0] == 0x45) { +            // IPv4 packet +            len = get_be16(buf + 2); +            mp_printf(print, " srcip=%u.%u.%u.%u dstip=%u.%u.%u.%u", +                buf[12], buf[13], buf[14], buf[15], +                buf[16], buf[17], buf[18], buf[19]); +            uint8_t prot = buf[9]; +            buf += 20; +            len -= 20; +            if (prot == 6) { +                // TCP packet +                uint16_t srcport = get_be16(buf); +                uint16_t dstport = get_be16(buf + 2); +                uint32_t seqnum = get_be32(buf + 4); +                uint32_t acknum = get_be32(buf + 8); +                uint16_t dataoff_flags = get_be16(buf + 12); +                uint16_t winsz = get_be16(buf + 14); +                mp_printf(print, " TCP srcport=%u dstport=%u seqnum=%u acknum=%u dataoff=%u flags=%x winsz=%u", +                    srcport, dstport, (unsigned)seqnum, (unsigned)acknum, dataoff_flags >> 12, dataoff_flags & 0x1ff, winsz); +                buf += 20; +                len -= 20; +                if (dataoff_flags >> 12 > 5) { +                    mp_printf(print, " opts="); +                    size_t opts_len = ((dataoff_flags >> 12) - 5) * 4; +                    dump_hex_bytes(print, opts_len, buf); +                    buf += opts_len; +                    len -= opts_len; +                } +            } else if (prot == 17) { +                // UDP packet +                uint16_t srcport = get_be16(buf); +                uint16_t dstport = get_be16(buf + 2); +                mp_printf(print, " UDP srcport=%u dstport=%u", srcport, dstport); +                len = get_be16(buf + 4); +                buf += 8; +                if ((srcport == 67 && dstport == 68) || (srcport == 68 && dstport == 67)) { +                    // DHCP +                    if (srcport == 67) { +                        mp_printf(print, " DHCPS"); +                    } else { +                        mp_printf(print, " DHCPC"); +                    } +                    dump_hex_bytes(print, 12 + 16 + 16 + 64, buf); +                    size_t n = 12 + 16 + 16 + 64 + 128; +                    len -= n; +                    buf += n; +                    mp_printf(print, " opts:"); +                    switch (buf[6]) { +                        case 1: +                            mp_printf(print, " DISCOVER"); +                            break; +                        case 2: +                            mp_printf(print, " OFFER"); +                            break; +                        case 3: +                            mp_printf(print, " REQUEST"); +                            break; +                        case 4: +                            mp_printf(print, " DECLINE"); +                            break; +                        case 5: +                            mp_printf(print, " ACK"); +                            break; +                        case 6: +                            mp_printf(print, " NACK"); +                            break; +                        case 7: +                            mp_printf(print, " RELEASE"); +                            break; +                        case 8: +                            mp_printf(print, " INFORM"); +                            break; +                    } +                } +            } else { +                // Non-UDP packet +                mp_printf(print, " prot=%u", prot); +            } +        } else if (buf[-2] == 0x86 && buf[-1] == 0xdd && (buf[0] >> 4) == 6) { +            // IPv6 packet +            uint32_t h = get_be32(buf); +            uint16_t l = get_be16(buf + 4); +            mp_printf(print, " tclass=%u flow=%u len=%u nexthdr=%u hoplimit=%u", (unsigned)((h >> 20) & 0xff), (unsigned)(h & 0xfffff), l, buf[6], buf[7]); +            mp_printf(print, " srcip="); +            dump_hex_bytes(print, 16, buf + 8); +            mp_printf(print, " dstip="); +            dump_hex_bytes(print, 16, buf + 24); +            buf += 40; +            len -= 40; +        } +        if (flags & NETUTILS_TRACE_PAYLOAD) { +            mp_printf(print, " data="); +            dump_hex_bytes(print, len, buf); +        } +    } +    if (flags & NETUTILS_TRACE_NEWLINE) { +        mp_printf(print, "\n"); +    } +} | 
