flycast/core/deps/picotcp/modules/pico_udp.c

228 lines
6.5 KiB
C
Raw Normal View History

/*********************************************************************
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
Authors: Daniele Lacamera
*********************************************************************/
#include "pico_udp.h"
#include "pico_config.h"
#include "pico_eth.h"
#include "pico_socket.h"
#include "pico_stack.h"
#ifdef DEBUG_UDP
#define udp_dbg dbg
#else
#define udp_dbg(...) do {} while(0)
#endif
#define UDP_FRAME_OVERHEAD (sizeof(struct pico_frame))
/* Queues */
static struct pico_queue udp_in = {
0
};
static struct pico_queue udp_out = {
0
};
/* Functions */
uint16_t pico_udp_checksum_ipv4(struct pico_frame *f)
{
struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
struct pico_udp_hdr *udp_hdr = (struct pico_udp_hdr *) f->transport_hdr;
struct pico_socket *s = f->sock;
struct pico_ipv4_pseudo_hdr pseudo;
if (s) {
/* Case of outgoing frame */
udp_dbg("UDP CRC: on outgoing frame\n");
pseudo.src.addr = s->local_addr.ip4.addr;
pseudo.dst.addr = s->remote_addr.ip4.addr;
} else {
/* Case of incomming frame */
udp_dbg("UDP CRC: on incomming frame\n");
pseudo.src.addr = hdr->src.addr;
pseudo.dst.addr = hdr->dst.addr;
}
pseudo.zeros = 0;
pseudo.proto = PICO_PROTO_UDP;
pseudo.len = short_be(f->transport_len);
return pico_dualbuffer_checksum(&pseudo, sizeof(struct pico_ipv4_pseudo_hdr), udp_hdr, f->transport_len);
}
#ifdef PICO_SUPPORT_IPV6
uint16_t pico_udp_checksum_ipv6(struct pico_frame *f)
{
struct pico_ipv6_hdr *ipv6_hdr = (struct pico_ipv6_hdr *)f->net_hdr;
struct pico_udp_hdr *udp_hdr = (struct pico_udp_hdr *)f->transport_hdr;
struct pico_ipv6_pseudo_hdr pseudo = {
.src = {{0}}, .dst = {{0}}, .len = 0, .zero = {0}, .nxthdr = 0
};
struct pico_socket *s = f->sock;
struct pico_remote_endpoint *remote_endpoint = (struct pico_remote_endpoint *)f->info;
/* XXX If the IPv6 packet contains a Routing header, the Destination
* Address used in the pseudo-header is that of the final destination */
if (s) {
/* Case of outgoing frame */
pseudo.src = s->local_addr.ip6;
if (remote_endpoint)
pseudo.dst = remote_endpoint->remote_addr.ip6;
else
pseudo.dst = s->remote_addr.ip6;
} else {
/* Case of incomming frame */
pseudo.src = ipv6_hdr->src;
pseudo.dst = ipv6_hdr->dst;
}
pseudo.len = long_be(f->transport_len);
pseudo.nxthdr = PICO_PROTO_UDP;
return pico_dualbuffer_checksum(&pseudo, sizeof(struct pico_ipv6_pseudo_hdr), udp_hdr, f->transport_len);
}
#endif
static int pico_udp_process_out(struct pico_protocol *self, struct pico_frame *f)
{
IGNORE_PARAMETER(self);
return (int)pico_network_send(f);
}
static int pico_udp_push(struct pico_protocol *self, struct pico_frame *f)
{
struct pico_udp_hdr *hdr = (struct pico_udp_hdr *) f->transport_hdr;
struct pico_remote_endpoint *remote_endpoint = (struct pico_remote_endpoint *) f->info;
/* this (fragmented) frame should contain a transport header */
if (f->transport_hdr != f->payload) {
if (f->local_port)
hdr->trans.sport = f->local_port; // Masqueraded
else
hdr->trans.sport = f->sock->local_port;
if (remote_endpoint) {
hdr->trans.dport = remote_endpoint->remote_port;
} else {
hdr->trans.dport = f->sock->remote_port;
}
hdr->len = short_be(f->transport_len);
/* do not perform CRC validation. If you want to, a system needs to be
implemented to calculate the CRC over the total payload of a
fragmented payload
*/
hdr->crc = 0;
}
if (pico_enqueue(self->q_out, f) > 0) {
return f->payload_len;
} else {
return 0;
}
}
/* Interface: protocol definition */
struct pico_protocol pico_proto_udp = {
.name = "udp",
.proto_number = PICO_PROTO_UDP,
.layer = PICO_LAYER_TRANSPORT,
.process_in = pico_transport_process_in,
.process_out = pico_udp_process_out,
.push = pico_udp_push,
.q_in = &udp_in,
.q_out = &udp_out,
};
struct pico_socket *pico_udp_open(void)
{
struct pico_socket_udp *u = PICO_ZALLOC(sizeof(struct pico_socket_udp));
if (!u)
return NULL;
u->mode = PICO_UDP_MODE_UNICAST;
#ifdef PICO_SUPPORT_MCAST
u->mc_ttl = PICO_IP_DEFAULT_MULTICAST_TTL;
/* enable multicast loopback by default */
u->sock.opt_flags |= (1 << PICO_SOCKET_OPT_MULTICAST_LOOP);
#endif
return &u->sock;
}
static void pico_udp_get_msginfo(struct pico_frame *f, struct pico_msginfo *msginfo)
{
if (!msginfo || !f->net_hdr)
return;
msginfo->dev = f->dev;
if (IS_IPV4(f)) { /* IPV4 */
#ifdef PICO_SUPPORT_IPV4
struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *)(f->net_hdr);
msginfo->ttl = hdr->ttl;
msginfo->tos = hdr->tos;
msginfo->local_addr.ip4 = hdr->dst;
msginfo->local_port = ((struct pico_udp_hdr *)f->transport_hdr)->trans.dport;
#endif
} else {
#ifdef PICO_SUPPORT_IPV6
struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *)(f->net_hdr);
msginfo->ttl = hdr->hop;
msginfo->tos = (hdr->vtf >> 20) & 0xFF; /* IPv6 traffic class */
#endif
}
}
uint16_t pico_udp_recv(struct pico_socket *s, void *buf, uint16_t len, void *src, uint16_t *port, struct pico_msginfo *msginfo)
{
struct pico_frame *f = pico_queue_peek(&s->q_in);
if (f) {
if(!f->payload_len) {
f->payload = f->transport_hdr + sizeof(struct pico_udp_hdr);
f->payload_len = (uint16_t)(f->transport_len - sizeof(struct pico_udp_hdr));
}
udp_dbg("expected: %d, got: %d\n", len, f->payload_len);
if (src)
pico_store_network_origin(src, f);
if (port) {
struct pico_trans *hdr = (struct pico_trans *)f->transport_hdr;
*port = hdr->sport;
}
if (msginfo) {
pico_udp_get_msginfo(f, msginfo);
}
if (f->payload_len > len) {
memcpy(buf, f->payload, len);
f->payload += len;
f->payload_len = (uint16_t)(f->payload_len - len);
return len;
} else {
uint16_t ret = f->payload_len;
memcpy(buf, f->payload, f->payload_len);
f = pico_dequeue(&s->q_in);
pico_frame_discard(f);
return ret;
}
} else return 0;
}