summaryrefslogtreecommitdiff
path: root/src/cmd/netstack/udp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/netstack/udp.c')
-rw-r--r--src/cmd/netstack/udp.c124
1 files changed, 124 insertions, 0 deletions
diff --git a/src/cmd/netstack/udp.c b/src/cmd/netstack/udp.c
new file mode 100644
index 0000000..3d560ae
--- /dev/null
+++ b/src/cmd/netstack/udp.c
@@ -0,0 +1,124 @@
+#include "proto.h"
+#include "util.h"
+
+enum {
+ SrcPort = 0,
+ DstPort = 2,
+ Length = 4,
+ Checksum = 6,
+ Payload = 8,
+};
+
+
+struct udp_conn {
+ uint32_t lip, rip;
+ uint16_t lport, rport;
+ mac_t rmac;
+ void (*on_conn)(struct udp_conn *, void *); /* if non-NULL - listening, if NULL - estabilished */
+ void (*on_recv)(const void *, size_t, void *);
+ void *carg;
+ struct udp_conn *next, **link;
+};
+static struct udp_conn *conns;
+static void conns_append(struct udp_conn *c) {
+ c->next = conns;
+ if (c->next)
+ c->next->link = &c->next;
+ c->link = &conns;
+ *c->link = c;
+}
+void udp_listen(
+ uint16_t port,
+ void (*on_conn)(struct udp_conn *, void *carg),
+ void (*on_recv)(const void *, size_t, void *carg),
+ void *carg)
+{
+ if (!on_conn) return;
+ struct udp_conn *c = malloc(sizeof *c);
+ c->lport = port;
+ c->lip = state.ip;
+ c->on_conn = on_conn;
+ c->on_recv = on_recv;
+ c->carg = carg;
+ conns_append(c);
+}
+struct udp_conn *udpc_new(
+ struct udp u,
+ void (*on_recv)(const void *, size_t, void *carg),
+ void *carg)
+{
+ struct udp_conn *c = malloc(sizeof *c);
+ memset(c, 0, sizeof *c);
+ c->lip = u.ip.src;
+ c->rip = u.ip.dst;
+ c->lport = u.src ? u.src : 50000; // TODO randomize source ports
+ c->rport = u.dst;
+ if (arpcache_get(c->rip, &c->rmac) < 0) {
+ // TODO make arp request, wait for reply
+ eprintf("not in ARP cache, unimplemented");
+ free(c);
+ return NULL;
+ }
+ c->on_recv = on_recv;
+ c->carg = carg;
+ conns_append(c);
+ return c;
+}
+void udpc_send(struct udp_conn *c, const void *buf, size_t len) {
+ uint8_t *pkt = malloc(Payload + len);
+ nput16(pkt + SrcPort, c->lport);
+ nput16(pkt + DstPort, c->rport);
+ nput16(pkt + Length, Payload + len);
+ nput16(pkt + Checksum, 0);
+ memcpy(pkt + Payload, buf, len);
+ ipv4_send(pkt, Payload + len, (struct ipv4){
+ .proto = 0x11,
+ .src = c->lip,
+ .dst = c->rip,
+ .e.dst = &c->rmac,
+ });
+ free(pkt);
+}
+void udpc_close(struct udp_conn *c) {
+ if (c->next) c->next->link = c->link;
+ *(c->link) = c->next;
+ free(c);
+}
+
+
+void udp_parse(const uint8_t *buf, size_t len, struct ipv4 ip) {
+ uint16_t srcport = nget16(buf + SrcPort);
+ uint16_t dstport = nget16(buf + DstPort);
+ bool active = false;
+
+ for (struct udp_conn *iter = conns; iter; iter = iter->next) {
+ if (iter->on_conn && dstport == iter->lport) {
+ iter->on_conn(iter, iter->carg);
+ iter->on_conn = NULL;
+ iter->rport = srcport;
+ memcpy(&iter->rmac, ip.e.src, sizeof(mac_t));
+ iter->rip = ip.src;
+ }
+ if (iter->rip == ip.src &&
+ iter->rport == srcport &&
+ iter->lport == dstport &&
+ iter->on_recv)
+ {
+ active = true;
+ iter->on_recv(buf + Payload, len - Payload, iter->carg);
+ }
+ }
+
+ if (!active) {
+ uint8_t *pkt = malloc(4 + ip.hlen + 8);
+ nput32(pkt, 0);
+ memcpy(pkt + 4, ip.header, ip.hlen + 8);
+ icmp_send(pkt, 4 + ip.hlen + 8, (struct icmp){
+ .type = 3, /* destination unreachable */
+ .code = 3, /* port unreachable */
+ .ip.dst = ip.src,
+ .ip.e.dst = ip.e.src,
+ });
+ free(pkt);
+ }
+}