extmod/modlwip: Support IPv6.

With these changes IPv6 works on the rp2 port (and possibly others that use
the lwIP socket implementation).

Things that have been tested and work:
- Neighbour solicitation for v6 link local address.
- Ping of v6 link-local address.
- Receiving a SLAAC address via router advertisement.
- Ping a v6 address allocated via SLAAC.
- Perform an outgoing connection to a routed v6-address (via default
  gateway).
- Create a listening IPv6 wildcard socked bound to ::, and trying to access
  it via link-local, SLAAC, and IPv4 (to ensure the dual-stack binding
  works).

Things that could be improved:
- socket.socket().getaddrinfo only returns the v4 address.  It could also
  return v6 addresses (getaddrinfo is actively programmed to only return a
  single address, and this is the v4-address by default, with fallback to
  the v6 address if both are enabled).

Signed-off-by: Felix Dörre <felix@dogcraft.de>
pull/9108/head
Felix Dörre 2022-08-25 17:25:08 +00:00 zatwierdzone przez Damien George
rodzic 866fc3447c
commit 628abf8f25
3 zmienionych plików z 60 dodań i 33 usunięć

Wyświetl plik

@ -353,6 +353,26 @@ STATIC void lwip_socket_free_incoming(lwip_socket_obj_t *socket) {
}
}
mp_obj_t lwip_format_inet_addr(const ip_addr_t *ip, mp_uint_t port) {
char *ipstr = ipaddr_ntoa(ip);
mp_obj_t tuple[2] = {
tuple[0] = mp_obj_new_str(ipstr, strlen(ipstr)),
tuple[1] = mp_obj_new_int(port),
};
return mp_obj_new_tuple(2, tuple);
}
mp_uint_t lwip_parse_inet_addr(mp_obj_t addr_in, ip_addr_t *out_ip) {
mp_obj_t *addr_items;
mp_obj_get_array_fixed_n(addr_in, 2, &addr_items);
size_t addr_len;
const char *addr_str = mp_obj_str_get_data(addr_items[0], &addr_len);
if (!ipaddr_aton(addr_str, out_ip)) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid arguments"));
}
return mp_obj_get_int(addr_items[1]);
}
/*******************************************************************************/
// Callback functions for the lwIP raw API.
@ -538,7 +558,7 @@ STATIC err_t _lwip_tcp_recv(void *arg, struct tcp_pcb *tcpb, struct pbuf *p, err
// these to do the work.
// Helper function for send/sendto to handle raw/UDP packets.
STATIC mp_uint_t lwip_raw_udp_send(lwip_socket_obj_t *socket, const byte *buf, mp_uint_t len, byte *ip, mp_uint_t port, int *_errno) {
STATIC mp_uint_t lwip_raw_udp_send(lwip_socket_obj_t *socket, const byte *buf, mp_uint_t len, ip_addr_t *ip, mp_uint_t port, int *_errno) {
if (len > 0xffff) {
// Any packet that big is probably going to fail the pbuf_alloc anyway, but may as well try
len = 0xffff;
@ -567,15 +587,13 @@ STATIC mp_uint_t lwip_raw_udp_send(lwip_socket_obj_t *socket, const byte *buf, m
err = udp_send(socket->pcb.udp, p);
}
} else {
ip_addr_t dest;
IP4_ADDR(&dest, ip[0], ip[1], ip[2], ip[3]);
#if MICROPY_PY_LWIP_SOCK_RAW
if (socket->type == MOD_NETWORK_SOCK_RAW) {
err = raw_sendto(socket->pcb.raw, p, &dest);
err = raw_sendto(socket->pcb.raw, p, ip);
} else
#endif
{
err = udp_sendto(socket->pcb.udp, p, &dest, port);
err = udp_sendto(socket->pcb.udp, p, ip, port);
}
}
@ -885,11 +903,8 @@ STATIC mp_obj_t lwip_socket_make_new(const mp_obj_type_t *type, size_t n_args, s
STATIC mp_obj_t lwip_socket_bind(mp_obj_t self_in, mp_obj_t addr_in) {
lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in);
uint8_t ip[NETUTILS_IPV4ADDR_BUFSIZE];
mp_uint_t port = netutils_parse_inet_addr(addr_in, ip, NETUTILS_BIG);
ip_addr_t bind_addr;
IP4_ADDR(&bind_addr, ip[0], ip[1], ip[2], ip[3]);
mp_uint_t port = lwip_parse_inet_addr(addr_in, &bind_addr);
err_t err = ERR_ARG;
switch (socket->type) {
@ -926,6 +941,12 @@ STATIC mp_obj_t lwip_socket_listen(size_t n_args, const mp_obj_t *args) {
if (socket->type != MOD_NETWORK_SOCK_STREAM) {
mp_raise_OSError(MP_EOPNOTSUPP);
}
#if LWIP_IPV6
if (ip_addr_cmp(&socket->pcb.tcp->local_ip, IP6_ADDR_ANY)) {
IP_SET_TYPE_VAL(socket->pcb.tcp->local_ip, IPADDR_TYPE_ANY);
IP_SET_TYPE_VAL(socket->pcb.tcp->remote_ip, IPADDR_TYPE_ANY);
}
#endif
struct tcp_pcb *new_pcb;
#if LWIP_VERSION_MACRO < 0x02000100
@ -1043,12 +1064,10 @@ STATIC mp_obj_t lwip_socket_accept(mp_obj_t self_in) {
MICROPY_PY_LWIP_EXIT
// make the return value
uint8_t ip[NETUTILS_IPV4ADDR_BUFSIZE];
memcpy(ip, &(socket2->pcb.tcp->remote_ip), sizeof(ip));
mp_uint_t port = (mp_uint_t)socket2->pcb.tcp->remote_port;
mp_obj_tuple_t *client = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL));
client->items[0] = MP_OBJ_FROM_PTR(socket2);
client->items[1] = netutils_format_inet_addr(ip, port, NETUTILS_BIG);
client->items[1] = lwip_format_inet_addr(&socket2->pcb.tcp->remote_ip, port);
return MP_OBJ_FROM_PTR(client);
}
@ -1062,11 +1081,8 @@ STATIC mp_obj_t lwip_socket_connect(mp_obj_t self_in, mp_obj_t addr_in) {
}
// get address
uint8_t ip[NETUTILS_IPV4ADDR_BUFSIZE];
mp_uint_t port = netutils_parse_inet_addr(addr_in, ip, NETUTILS_BIG);
ip_addr_t dest;
IP4_ADDR(&dest, ip[0], ip[1], ip[2], ip[3]);
mp_uint_t port = lwip_parse_inet_addr(addr_in, &dest);
err_t err = ERR_ARG;
switch (socket->type) {
@ -1219,8 +1235,8 @@ STATIC mp_obj_t lwip_socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(data_in, &bufinfo, MP_BUFFER_READ);
uint8_t ip[NETUTILS_IPV4ADDR_BUFSIZE];
mp_uint_t port = netutils_parse_inet_addr(addr_in, ip, NETUTILS_BIG);
ip_addr_t ip;
mp_uint_t port = lwip_parse_inet_addr(addr_in, &ip);
mp_uint_t ret = 0;
switch (socket->type) {
@ -1232,7 +1248,7 @@ STATIC mp_obj_t lwip_socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t
#if MICROPY_PY_LWIP_SOCK_RAW
case MOD_NETWORK_SOCK_RAW:
#endif
ret = lwip_raw_udp_send(socket, bufinfo.buf, bufinfo.len, ip, port, &_errno);
ret = lwip_raw_udp_send(socket, bufinfo.buf, bufinfo.len, &ip, port, &_errno);
break;
}
if (ret == -1) {
@ -1357,6 +1373,12 @@ STATIC mp_obj_t lwip_socket_setblocking(mp_obj_t self_in, mp_obj_t flag_in) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_setblocking_obj, lwip_socket_setblocking);
#if LWIP_VERSION_MAJOR < 2
#define MP_IGMP_IP_ADDR_TYPE ip_addr_t
#else
#define MP_IGMP_IP_ADDR_TYPE ip4_addr_t
#endif
STATIC mp_obj_t lwip_socket_setsockopt(size_t n_args, const mp_obj_t *args) {
(void)n_args; // always 4
lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(args[0]);
@ -1397,9 +1419,9 @@ STATIC mp_obj_t lwip_socket_setsockopt(size_t n_args, const mp_obj_t *args) {
// POSIX setsockopt has order: group addr, if addr, lwIP has it vice-versa
err_t err;
if (opt == IP_ADD_MEMBERSHIP) {
err = igmp_joingroup((ip_addr_t *)bufinfo.buf + 1, bufinfo.buf);
err = igmp_joingroup((MP_IGMP_IP_ADDR_TYPE *)bufinfo.buf + 1, bufinfo.buf);
} else {
err = igmp_leavegroup((ip_addr_t *)bufinfo.buf + 1, bufinfo.buf);
err = igmp_leavegroup((MP_IGMP_IP_ADDR_TYPE *)bufinfo.buf + 1, bufinfo.buf);
}
if (err != ERR_OK) {
mp_raise_OSError(error_lookup_table[-err]);
@ -1412,6 +1434,9 @@ STATIC mp_obj_t lwip_socket_setsockopt(size_t n_args, const mp_obj_t *args) {
}
return mp_const_none;
}
#undef MP_IGMP_IP_ADDR_TYPE
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(lwip_socket_setsockopt_obj, 4, 4, lwip_socket_setsockopt);
STATIC mp_obj_t lwip_socket_makefile(size_t n_args, const mp_obj_t *args) {
@ -1742,12 +1767,13 @@ STATIC mp_obj_t lwip_getaddrinfo(size_t n_args, const mp_obj_t *args) {
mp_raise_OSError(state.status);
}
ip_addr_t ipcopy = state.ipaddr;
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(5, NULL));
tuple->items[0] = MP_OBJ_NEW_SMALL_INT(MOD_NETWORK_AF_INET);
tuple->items[1] = MP_OBJ_NEW_SMALL_INT(MOD_NETWORK_SOCK_STREAM);
tuple->items[2] = MP_OBJ_NEW_SMALL_INT(0);
tuple->items[3] = MP_OBJ_NEW_QSTR(MP_QSTR_);
tuple->items[4] = netutils_format_inet_addr((uint8_t *)&state.ipaddr, port, NETUTILS_BIG);
tuple->items[4] = lwip_format_inet_addr(&ipcopy, port);
return mp_obj_new_list(1, (mp_obj_t *)&tuple);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(lwip_getaddrinfo_obj, 2, 6, lwip_getaddrinfo);

Wyświetl plik

@ -78,13 +78,14 @@ STATIC void network_cyw43_print(const mp_print_t *print, mp_obj_t self_in, mp_pr
} else {
status_str = "fail";
}
ip4_addr_t *addr = ip_2_ip4(&netif->ip_addr);
mp_printf(print, "<CYW43 %s %s %u.%u.%u.%u>",
self->itf == CYW43_ITF_STA ? "STA" : "AP",
status_str,
netif->ip_addr.addr & 0xff,
netif->ip_addr.addr >> 8 & 0xff,
netif->ip_addr.addr >> 16 & 0xff,
netif->ip_addr.addr >> 24
addr->addr & 0xff,
addr->addr >> 8 & 0xff,
addr->addr >> 16 & 0xff,
addr->addr >> 24
);
}

Wyświetl plik

@ -113,7 +113,7 @@ static void dhcp_socket_free(struct udp_pcb **udp) {
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);
IP_ADDR4(&addr, ip >> 24 & 0xff, ip >> 16 & 0xff, ip >> 8 & 0xff, ip & 0xff);
// TODO convert lwIP errors to errno
return udp_bind(*udp, &addr, port);
}
@ -131,7 +131,7 @@ static int dhcp_socket_sendto(struct udp_pcb **udp, struct netif *netif, const v
memcpy(p->payload, buf, len);
ip_addr_t dest;
IP4_ADDR(&dest, ip >> 24 & 0xff, ip >> 16 & 0xff, ip >> 8 & 0xff, ip & 0xff);
IP_ADDR4(&dest, ip >> 24 & 0xff, ip >> 16 & 0xff, ip >> 8 & 0xff, ip & 0xff);
err_t err;
if (netif != NULL) {
err = udp_sendto_if(*udp, p, &dest, port, netif);
@ -205,7 +205,7 @@ static void dhcp_server_process(void *arg, struct udp_pcb *upcb, struct pbuf *p,
}
dhcp_msg.op = DHCPOFFER;
memcpy(&dhcp_msg.yiaddr, &d->ip.addr, 4);
memcpy(&dhcp_msg.yiaddr, &ip_2_ip4(&d->ip)->addr, 4);
uint8_t *opt = (uint8_t *)&dhcp_msg.options;
opt += 4; // assume magic cookie: 99, 130, 83, 99
@ -248,7 +248,7 @@ static void dhcp_server_process(void *arg, struct udp_pcb *upcb, struct pbuf *p,
// Should be NACK
goto ignore_request;
}
if (memcmp(o + 2, &d->ip.addr, 3) != 0) {
if (memcmp(o + 2, &ip_2_ip4(&d->ip)->addr, 3) != 0) {
// Should be NACK
goto ignore_request;
}
@ -280,9 +280,9 @@ static void dhcp_server_process(void *arg, struct udp_pcb *upcb, struct pbuf *p,
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 multiple addresses
opt_write_n(&opt, DHCP_OPT_SERVER_ID, 4, &ip_2_ip4(&d->ip)->addr);
opt_write_n(&opt, DHCP_OPT_SUBNET_MASK, 4, &ip_2_ip4(&d->nm)->addr);
opt_write_n(&opt, DHCP_OPT_ROUTER, 4, &ip_2_ip4(&d->ip)->addr); // aka gateway; can have multiple addresses
opt_write_u32(&opt, DHCP_OPT_DNS, DEFAULT_DNS); // can have multiple addresses
opt_write_u32(&opt, DHCP_OPT_IP_LEASE_TIME, DEFAULT_LEASE_TIME_S);
*opt++ = DHCP_OPT_END;