From e35d6a4fde9a0671bc7d2527ff6b55b0ce1b4b1e Mon Sep 17 00:00:00 2001 From: dzwdz Date: Sun, 21 Aug 2022 22:33:09 +0200 Subject: user: rename ethdump to netstack --- src/user/app/ethdump/arp.c | 48 --------- src/user/app/ethdump/ethdump.c | 41 -------- src/user/app/ethdump/ether.c | 59 ----------- src/user/app/ethdump/fs.c | 154 ---------------------------- src/user/app/ethdump/icmp.c | 34 ------- src/user/app/ethdump/ipv4.c | 215 --------------------------------------- src/user/app/ethdump/proto.h | 70 ------------- src/user/app/ethdump/udp.c | 98 ------------------ src/user/app/ethdump/util.c | 31 ------ src/user/app/ethdump/util.h | 38 ------- src/user/app/init/init.c | 2 +- src/user/app/netstack/arp.c | 48 +++++++++ src/user/app/netstack/ether.c | 59 +++++++++++ src/user/app/netstack/fs.c | 154 ++++++++++++++++++++++++++++ src/user/app/netstack/icmp.c | 34 +++++++ src/user/app/netstack/ipv4.c | 215 +++++++++++++++++++++++++++++++++++++++ src/user/app/netstack/netstack.c | 43 ++++++++ src/user/app/netstack/proto.h | 70 +++++++++++++ src/user/app/netstack/udp.c | 98 ++++++++++++++++++ src/user/app/netstack/util.c | 31 ++++++ src/user/app/netstack/util.h | 38 +++++++ 21 files changed, 791 insertions(+), 789 deletions(-) delete mode 100644 src/user/app/ethdump/arp.c delete mode 100644 src/user/app/ethdump/ethdump.c delete mode 100644 src/user/app/ethdump/ether.c delete mode 100644 src/user/app/ethdump/fs.c delete mode 100644 src/user/app/ethdump/icmp.c delete mode 100644 src/user/app/ethdump/ipv4.c delete mode 100644 src/user/app/ethdump/proto.h delete mode 100644 src/user/app/ethdump/udp.c delete mode 100644 src/user/app/ethdump/util.c delete mode 100644 src/user/app/ethdump/util.h create mode 100644 src/user/app/netstack/arp.c create mode 100644 src/user/app/netstack/ether.c create mode 100644 src/user/app/netstack/fs.c create mode 100644 src/user/app/netstack/icmp.c create mode 100644 src/user/app/netstack/ipv4.c create mode 100644 src/user/app/netstack/netstack.c create mode 100644 src/user/app/netstack/proto.h create mode 100644 src/user/app/netstack/udp.c create mode 100644 src/user/app/netstack/util.c create mode 100644 src/user/app/netstack/util.h (limited to 'src') diff --git a/src/user/app/ethdump/arp.c b/src/user/app/ethdump/arp.c deleted file mode 100644 index cfb1e04..0000000 --- a/src/user/app/ethdump/arp.c +++ /dev/null @@ -1,48 +0,0 @@ -#include "proto.h" -#include "util.h" -#include - -enum { - HdrType = 0, - HdrTypeEther = 1, - ProtoType = 2, - HdrALen = 4, - ProtoALen = 5, - Operation = 6, - OpReq = 1, - OpReply = 2, -}; - -void arp_parse(const uint8_t *buf, size_t len) { - // TODO no bound checks - uint16_t htype = nget16(buf + HdrType); - uint16_t ptype = nget16(buf + ProtoType); - uint16_t op = nget16(buf + Operation); - - if (!(htype == HdrTypeEther && ptype == ET_IPv4)) return; - enum { /* only valid for this combination of header + proto */ - SrcMAC = 8, - SrcIP = 14, - DstMAC = 18, - DstIP = 24, - }; - - if (op == OpReq) { - uint32_t daddr = nget32(buf + DstIP); - if (daddr == state.ip) { - uint8_t *pkt = ether_start(30, (struct ethernet){ - .dst = buf + SrcMAC, - .type = ET_ARP, - }); - nput16(pkt + HdrType, 1); - nput16(pkt + ProtoType, ET_IPv4); - pkt[HdrALen] = 6; - pkt[ProtoALen] = 4; - nput16(pkt + Operation, OpReply); - memcpy(pkt + SrcMAC, state.mac, 6); - nput32(pkt + SrcIP, state.ip); - memcpy(pkt + DstMAC, buf + SrcMAC, 10); /* sender's MAC and IP */ - ether_finish(pkt); - } - } -} diff --git a/src/user/app/ethdump/ethdump.c b/src/user/app/ethdump/ethdump.c deleted file mode 100644 index 17592a1..0000000 --- a/src/user/app/ethdump/ethdump.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "proto.h" -#include "util.h" -#include -#include -#include -#include -#include -#include - -struct net_state state = { - // TODO dynamically get mac - .mac = {0x52, 0x54, 0x00, 0xCA, 0x77, 0x1A}, - .ip = (192 << 24) + (168 << 16) + 11, -}; - -void network_thread(void *arg) { (void)arg; - const size_t buflen = 4096; - char *buf = malloc(buflen); - for (;;) { - long ret = _syscall_read(state.raw_h, buf, buflen, -1); - if (ret < 0) break; - ether_parse((void*)buf, ret); - } - free(buf); -} - -void fs_thread(void *arg); - -int main(void) { - const char *path = "/kdev/eth"; - state.raw_h = _syscall_open(path, strlen(path), 0); - if (state.raw_h < 0) { - eprintf("couldn't open %s", path); - return 1; - } - - thread_create(0, network_thread, NULL); - thread_create(0, fs_thread, NULL); - _syscall_await(); - return 0; -} diff --git a/src/user/app/ethdump/ether.c b/src/user/app/ethdump/ether.c deleted file mode 100644 index 5893632..0000000 --- a/src/user/app/ethdump/ether.c +++ /dev/null @@ -1,59 +0,0 @@ -#include -#include "proto.h" -#include "util.h" - -enum { - DstMAC = 0, - SrcMAC = 6, - EtherType = 12, - Payload = 14, -}; -struct ethq *ether_queue; - -void ether_parse(const uint8_t *buf, size_t len) { - struct ethernet ether = (struct ethernet){ - .src = buf + SrcMAC, - .dst = buf + DstMAC, - .type = nget16(buf + EtherType), - }; - - for (struct ethq **iter = ðer_queue; iter && *iter; ) { - struct ethq *qe = *iter; - _syscall_fs_respond(qe->h, buf, len, 0); - /* remove entry */ - /* yes, doing it this way here doesn't make sense. i'm preparing for filtering */ - *iter = qe->next; - free(qe); - } - - switch (ether.type) { - case ET_IPv4: - ipv4_parse(buf + Payload, len - Payload, ether); - break; - case ET_ARP: - arp_parse(buf + Payload, len - Payload); - break; - } -} - -static const size_t fhoff = sizeof(size_t); -uint8_t *ether_start(size_t len, struct ethernet ether) { - if (len < 60 - Payload) len = 60 - Payload; - - if (!ether.dst) eprintf("NULL ether.dst!"); // TODO arp? i guess? - if (!ether.src) ether.src = &state.mac; - - uint8_t *buf = malloc(fhoff + Payload + len); - memset(buf, 0, fhoff + Payload + len); - *(size_t*)buf = len + Payload; - memcpy(buf + fhoff + DstMAC, ether.dst, 6); - memcpy(buf + fhoff + SrcMAC, ether.src, 6); - nput16(buf + fhoff + EtherType, ether.type); - return buf + fhoff + Payload; -} -void ether_finish(uint8_t *pkt) { - uint8_t *buf = pkt - Payload - fhoff; - size_t len = *(size_t*)buf; - _syscall_write(state.raw_h, buf + fhoff, len, 0, 0); - free(buf); -} diff --git a/src/user/app/ethdump/fs.c b/src/user/app/ethdump/fs.c deleted file mode 100644 index b8c2f7b..0000000 --- a/src/user/app/ethdump/fs.c +++ /dev/null @@ -1,154 +0,0 @@ -#include "proto.h" -#include -#include -#include -#include -#include - -enum handle_type { - H_ROOT, - H_ETHER, - H_UDP, -}; - -struct strqueue { - struct strqueue *next; - size_t len; - char buf[]; -}; - -struct handle { - enum handle_type type; - struct { - struct udp_conn *c; - struct strqueue *rx, *rxlast; - } udp; - handle_t reqh; -}; - - -static void udp_listen_callback(struct udp_conn *c, void *arg) { - struct handle *h = arg; - h->udp.c = c; - h->udp.rx = NULL; - h->udp.rxlast = NULL; - _syscall_fs_respond(h->reqh, h, 0, 0); - h->reqh = -1; -} - -static void udp_recv_callback(const void *buf, size_t len, void *arg) { - struct handle *h = arg; - if (h->reqh >= 0) { - _syscall_fs_respond(h->reqh, buf, len, 0); - h->reqh = -1; - return; - } - // TODO don't malloc on the network thread, dumbass - struct strqueue *sq = malloc(sizeof(*sq) + len); - sq->next = NULL; - sq->len = len; - memcpy(sq->buf, buf, len); - if (h->udp.rx) { - h->udp.rxlast->next = sq; - h->udp.rxlast = sq; - } else { - h->udp.rx = sq; - h->udp.rxlast = sq; - } -} - -static void udp_recv_enqueue(struct handle *h, handle_t reqh) { - if (h->reqh > 0) { - // TODO queue - _syscall_fs_respond(reqh, NULL, -1, 0); - } else if (h->udp.rx) { - _syscall_fs_respond(reqh, h->udp.rx->buf, h->udp.rx->len, 0); - h->udp.rx = h->udp.rx->next; - free(h->udp.rx); - } else { - h->reqh = reqh; - } -} - -void fs_thread(void *arg) { (void)arg; - const size_t buflen = 4096; - char *buf = malloc(buflen); - for (;;) { - struct fs_wait_response res; - handle_t reqh = _syscall_fs_wait(buf, buflen, &res); - if (reqh < 0) break; - struct handle *h = res.id; - long ret; - switch (res.op) { - case VFSOP_OPEN: - if (res.len == 1 && !memcmp("/", buf, 1)) { - h = malloc(sizeof *h); - h->type = H_ROOT; - _syscall_fs_respond(reqh, h, 0, 0); - } else if (res.len == 4 && !memcmp("/raw", buf, 4)) { - h = malloc(sizeof *h); - h->type = H_ETHER; - _syscall_fs_respond(reqh, h, 0, 0); - } else if (res.len > 6 && !memcmp("/udpl/", buf, 6)) { - uint16_t port = strtol(buf + 6, NULL, 0); - h = malloc(sizeof *h); - h->type = H_UDP; - h->udp.c = NULL; - h->reqh = reqh; - udp_listen(port, udp_listen_callback, udp_recv_callback, h); - } else { - _syscall_fs_respond(reqh, NULL, -1, 0); - } - break; - case VFSOP_READ: - switch (h->type) { - case H_ROOT: { - struct dirbuild db; - dir_start(&db, res.offset, buf, sizeof buf); - dir_append(&db, "raw"); - dir_append(&db, "udpl/"); - _syscall_fs_respond(reqh, buf, dir_finish(&db), 0); - break;} - case H_ETHER: { - struct ethq *qe; - qe = malloc(sizeof *qe); - qe->h = reqh; - qe->next = ether_queue; - ether_queue = qe; - break;} - case H_UDP: - udp_recv_enqueue(h, reqh); - break; - default: - _syscall_fs_respond(reqh, NULL, -1, 0); - } - break; - case VFSOP_WRITE: - switch (h->type) { - case H_ETHER: - ret = _syscall_write(state.raw_h, buf, res.len, 0, 0); - _syscall_fs_respond(reqh, NULL, ret, 0); - break; - case H_UDP: - udpc_send(h->udp.c, buf, res.len); - _syscall_fs_respond(reqh, NULL, res.len, 0); - break; - default: - _syscall_fs_respond(reqh, NULL, -1, 0); - } - break; - case VFSOP_CLOSE: - // TODO remove entries in queue - // TODO why does close even have _syscall_fs_respond? - if (h->type == H_UDP) - udpc_close(h->udp.c); - free(h); - _syscall_fs_respond(reqh, NULL, -1, 0); - break; - default: - _syscall_fs_respond(reqh, NULL, -1, 0); - break; - } - } - free(buf); -} diff --git a/src/user/app/ethdump/icmp.c b/src/user/app/ethdump/icmp.c deleted file mode 100644 index 0c6a502..0000000 --- a/src/user/app/ethdump/icmp.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "proto.h" -#include "util.h" - -enum { - Type = 0, - Code = 1, - Checksum = 2, - Payload = 4, -}; - -void icmp_parse(const uint8_t *buf, size_t len, struct ipv4 ip) { - if (len < Payload) return; - uint8_t type = buf[Type]; - if (type == 8 && ip.dst == state.ip) { - /* echo reply */ - icmp_send(buf + Payload, len - Payload, (struct icmp){ - .type = 0, - .ip.dst = ip.src, - .ip.e.dst = ip.e.src, - }); - } -} - -void icmp_send(const void *payload, size_t len, struct icmp i) { - i.ip.proto = 1; - uint8_t *pkt = malloc(Payload + len); - pkt[Type] = i.type; - pkt[Code] = i.code; - memcpy(pkt + Payload, payload, len); - nput16(pkt + Checksum, 0); - nput16(pkt + Checksum, ip_checksum(pkt, Payload + len)); - ipv4_send(pkt, Payload + len, i.ip); - free(pkt); -} diff --git a/src/user/app/ethdump/ipv4.c b/src/user/app/ethdump/ipv4.c deleted file mode 100644 index 3243d3c..0000000 --- a/src/user/app/ethdump/ipv4.c +++ /dev/null @@ -1,215 +0,0 @@ -#include "proto.h" -#include "util.h" -#include -#include - -enum { - Version = 0, - HdrLen = 0, - TotalLen = 2, - Id = 4, - FragInfo = 6, - EvilBit = 0x8000, - DontFrag = 0x4000, - MoreFrags = 0x2000, - FragOff = 0x1FFF, - TTL = 8, - Proto = 9, - Checksum = 10, - SrcIP = 12, - DstIP = 16, -}; -static void ipv4_dispatch(const uint8_t *buf, size_t len, struct ipv4 ip); - -struct fragment { - struct fragment *next; /* sorted */ - size_t len, offset; - bool last; - uint8_t buf[]; -}; -struct fragmented { - /* src, dst, proto, id come from the first arrived packet - * and are used to tell fragmenteds apart. - * the rest comes from the sequentially first packet. - * ip.h.header points to a malloc'd buffer*/ - struct ipv4 h; - - struct fragment *first; - struct fragmented *next, **prev; /* *(inc->prev) == inc */ - // TODO timer -}; -struct fragmented *fragmenteds; -static struct fragmented *fragmented_find(struct ipv4 fraghdr); -static void fragmented_tryinsert(const uint8_t *payload, size_t plen, struct ipv4 ip); -static void fragmented_tryfinish(struct fragmented *inc); -static void fragmented_free(struct fragmented *inc); - -static struct fragmented *fragmented_find(struct ipv4 fraghdr) { - struct fragmented *inc; - for (inc = fragmenteds; inc; inc = inc->next) { - if (inc->h.src == fraghdr.src && - inc->h.dst == fraghdr.dst && - inc->h.proto == fraghdr.proto && - inc->h.id == fraghdr.id) - { - return inc; - } - } - inc = malloc(sizeof *inc); - memset(inc, 0, sizeof *inc); - inc->h.src = fraghdr.src; - inc->h.dst = fraghdr.dst; - inc->h.proto = fraghdr.proto; - inc->h.id = fraghdr.id; - - inc->next = fragmenteds; - if (inc->next) inc->next->prev = &inc->next; - inc->prev = &fragmenteds; - *inc->prev = inc; - return inc; -} - -static void fragmented_tryinsert(const uint8_t *payload, size_t plen, struct ipv4 ip) { - struct fragmented *inc = fragmented_find(ip); - size_t off = (ip.fraginfo & FragOff) * 8; - bool last = !(ip.fraginfo & MoreFrags); - // eprintf("fragmented packet, %u + %u, part of 0x%x", off, plen, inc); - - /* find the first fragment at a bigger offset, and insert before it */ - struct fragment **insert = &inc->first; - for (; *insert; insert = &(*insert)->next) { - if ((*insert)->offset > off) break; - if ((*insert)->offset == off) return; /* duplicate packet */ - } - /* later on: frag->next = *insert; - * if we're the last fragment, frag->next must == NULL */ - if (last && *insert != NULL) return; - - struct fragment *frag = malloc(sizeof(struct fragment) + plen); - frag->next = *insert; - *insert = frag; - frag->len = plen; - frag->offset = off; - frag->last = last; - memcpy(frag->buf, payload, plen); - - if (off == 0) { /* save header */ - assert(!inc->h.header); - void *headercpy = malloc(ip.hlen); - memcpy(headercpy, ip.header, ip.hlen); - inc->h = ip; - inc->h.header = headercpy; - } - - fragmented_tryfinish(inc); -} - -static void fragmented_tryfinish(struct fragmented *inc) { - if (inc->first->offset != 0) return; - for (struct fragment *iter = inc->first; iter; iter = iter->next) { - size_t iterend = iter->offset + iter->len; - struct fragment *next = iter->next; - if (next) { - if (iterend < next->offset) return; /* incomplete */ - if (iterend > next->offset) { - fragmented_free(inc); - return; - } - } else if (iter->last) { - void *buf = malloc(iterend); - for (struct fragment *iter = inc->first; iter; iter = iter->next) { - assert(iter->offset + iter->len <= iterend); - memcpy(buf + iter->offset, iter->buf, iter->len); - } - ipv4_dispatch(buf, iterend, inc->h); - free(buf); - fragmented_free(inc); - } - } -} - -static void fragmented_free(struct fragmented *inc) { - if (inc->next) { - inc->next->prev = inc->prev; - *inc->next->prev = inc->next; - } else { - *inc->prev = NULL; - } - - for (struct fragment *next, *iter = inc->first; iter; iter = next) { - next = iter->next; - free(iter); - } - free((void*)inc->h.header); - free(inc); -} - - -static void ipv4_dispatch(const uint8_t *buf, size_t len, struct ipv4 ip) { - switch (ip.proto) { - case 0x01: icmp_parse(buf, len, ip); break; - case 0x11: udp_parse(buf, len, ip); break; - } -} - -void ipv4_parse(const uint8_t *buf, size_t len, struct ethernet ether) { - uint8_t version, headerlen; - uint16_t totallen; - - version = buf[Version] >> 4; - if (version != 4) return; - headerlen = (buf[HdrLen] & 0xf) * 4; - totallen = nget16(buf + TotalLen); - if (totallen < headerlen) return; - - /* ignores checksum. TODO? */ - - struct ipv4 ip = (struct ipv4){ - .e = ether, - .src = nget32(buf + SrcIP), - .dst = nget32(buf + DstIP), - .id = nget16(buf + Id), - .fraginfo = nget16(buf + FragInfo), - .proto = buf[Proto], - .header = buf, - .hlen = headerlen, - }; - - if (ip.fraginfo & ~(EvilBit | DontFrag)) { - fragmented_tryinsert(buf + headerlen, totallen - headerlen, ip); - } else { - if (totallen > len) return; - ipv4_dispatch(buf + headerlen, totallen - headerlen, ip); - } -} - -static uint16_t next_id = 0; -void ipv4_send(const void *payload, size_t len, struct ipv4 ip) { - const size_t mtu = 1500; - const size_t hdrlen = 20; - - ip.e.type = ET_IPv4; - if (!ip.src) ip.src = state.ip; - if (!ip.e.dst && ip.dst == 0xFFFFFFFF) - ip.e.dst = &MAC_BROADCAST; - - uint16_t id = next_id++; - for (size_t off = 0, fraglen = mtu - hdrlen; off < len; off += fraglen) { - if (fraglen > len - off) - fraglen = len - off; - bool last = off + fraglen >= len; - uint8_t *pkt = ether_start(hdrlen + fraglen, ip.e); - pkt[Version] = 0x40; - pkt[HdrLen] |= hdrlen / 4; - nput16(pkt + TotalLen, hdrlen + fraglen); - nput16(pkt + Id, id); - nput16(pkt + FragInfo, (off >> 3) | (last ? 0 : MoreFrags)); - pkt[TTL] = 0xFF; - pkt[Proto] = ip.proto; - nput32(pkt + SrcIP, ip.src); - nput32(pkt + DstIP, ip.dst); - nput16(pkt + Checksum, ip_checksum(pkt, hdrlen)); - memcpy(pkt + hdrlen, payload + off, fraglen); - ether_finish(pkt); - } -} diff --git a/src/user/app/ethdump/proto.h b/src/user/app/ethdump/proto.h deleted file mode 100644 index 106f286..0000000 --- a/src/user/app/ethdump/proto.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once -#include -#include -#include - -typedef uint8_t mac_t[6]; -static const mac_t MAC_BROADCAST = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - -extern struct net_state { - mac_t mac; - uint32_t ip; - - handle_t raw_h; -} state; - -enum { /* ethertype */ - ET_IPv4 = 0x800, - ET_ARP = 0x806, -}; - -struct ethernet { - const mac_t *src, *dst; - uint16_t type; -}; - -struct ipv4 { - struct ethernet e; - uint32_t src, dst; - uint16_t id, fraginfo; - uint8_t proto; - const uint8_t *header; size_t hlen; -}; - -struct icmp { - struct ipv4 ip; - uint8_t type, code; -}; - - -/* NOT THREADSAFE, YET USED FROM CONCURRENT THREADS - * will break if i implement a scheduler*/ -struct ethq { - struct ethq *next; - handle_t h; -}; -extern struct ethq *ether_queue; - -void arp_parse(const uint8_t *buf, size_t len); - -void icmp_parse(const uint8_t *buf, size_t len, struct ipv4 ip); -void icmp_send(const void *payload, size_t len, struct icmp i); - -void ipv4_parse(const uint8_t *buf, size_t len, struct ethernet ether); -void ipv4_send(const void *payload, size_t len, struct ipv4 ip); - -void ether_parse(const uint8_t *buf, size_t len); -uint8_t *ether_start(size_t len, struct ethernet ether); -void ether_finish(uint8_t *pkt); - -struct udp_conn; -void udp_parse(const uint8_t *buf, size_t len, struct ipv4 ip); -/* calls callback once, after a client connects. */ -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); -// TODO udp_onclosed -void udpc_send(struct udp_conn *, const void *buf, size_t len); -/* frees */ -void udpc_close(struct udp_conn *); diff --git a/src/user/app/ethdump/udp.c b/src/user/app/ethdump/udp.c deleted file mode 100644 index 8129a48..0000000 --- a/src/user/app/ethdump/udp.c +++ /dev/null @@ -1,98 +0,0 @@ -#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, **prev; -}; -struct udp_conn *conns; -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; - - c->next = conns; - if (c->next) *(c->next->prev) = c; - c->prev = &conns; - *c->prev = c; -} -void udpc_close(struct udp_conn *c) { - if (c->next) c->next->prev = c->prev; - *(c->prev) = c->next; - free(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 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); - } -} diff --git a/src/user/app/ethdump/util.c b/src/user/app/ethdump/util.c deleted file mode 100644 index 7c484f3..0000000 --- a/src/user/app/ethdump/util.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "util.h" - -/* https://www.w3.org/TR/PNG/#D-CRCAppendix */ -static uint32_t crc_table[256]; -uint32_t crc32(const uint8_t *buf, size_t len) { - if (!crc_table[1]) { - for (int i = 0; i < 256; i++) { - uint32_t c = i; - for (int j = 0; j < 8; j++) - c = ((c&1) ? 0xedb88320 : 0) ^ (c >> 1); - crc_table[i] = c; - } - } - - uint32_t c = 0xFFFFFFFF; - for (size_t i = 0; i < len; i++) - c = crc_table[(c ^ buf[i]) & 0xff] ^ (c >> 8); - return ~c; -} - -uint16_t ip_checksum(const uint8_t *buf, size_t len) { - uint32_t c = 0; - while (len >= 2) { - c += nget16(buf); - buf += 2; len -= 2; - } - if (len) c += (*buf) << 8; - while (c >= 0xFFFF) - c = (c & 0xFFFF) + (c >> 16); - return ~c; -} diff --git a/src/user/app/ethdump/util.h b/src/user/app/ethdump/util.h deleted file mode 100644 index bfff6e3..0000000 --- a/src/user/app/ethdump/util.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once -#include -#include -#include -#include - -#define eprintf(fmt, ...) fprintf(stderr, "ethdump: "fmt"\n" __VA_OPT__(,) __VA_ARGS__) - -uint32_t crc32(const uint8_t *buf, size_t len); -uint16_t ip_checksum(const uint8_t *buf, size_t len); - -static inline void nput16(void *vbuf, uint16_t n) { - uint8_t *b = vbuf; - b[0] = n >> 8; - b[1] = n >> 0; -} - -static inline void nput32(void *vbuf, uint32_t n) { - uint8_t *b = vbuf; - b[0] = n >> 24; - b[1] = n >> 16; - b[2] = n >> 8; - b[3] = n >> 0; -} - -static inline uint16_t nget16(const void *vbuf) { - const uint8_t *b = vbuf; - return (b[0] << 8) - | (b[1] << 0); -} - -static inline uint32_t nget32(const void *vbuf) { - const uint8_t *b = vbuf; - return (b[0] << 24) - | (b[1] << 16) - | (b[2] << 8) - | (b[3] << 0); -} diff --git a/src/user/app/init/init.c b/src/user/app/init/init.c index acf9b03..7b7afc4 100644 --- a/src/user/app/init/init.c +++ b/src/user/app/init/init.c @@ -68,7 +68,7 @@ int main(void) { execv(argv[0], (void*)argv); } MOUNT_AT("/net/") { - const char *argv[] = {"/bin/ethdump", NULL}; + const char *argv[] = {"/bin/netstack", "/kdev/eth", NULL}; execv(argv[0], (void*)argv); } diff --git a/src/user/app/netstack/arp.c b/src/user/app/netstack/arp.c new file mode 100644 index 0000000..cfb1e04 --- /dev/null +++ b/src/user/app/netstack/arp.c @@ -0,0 +1,48 @@ +#include "proto.h" +#include "util.h" +#include + +enum { + HdrType = 0, + HdrTypeEther = 1, + ProtoType = 2, + HdrALen = 4, + ProtoALen = 5, + Operation = 6, + OpReq = 1, + OpReply = 2, +}; + +void arp_parse(const uint8_t *buf, size_t len) { + // TODO no bound checks + uint16_t htype = nget16(buf + HdrType); + uint16_t ptype = nget16(buf + ProtoType); + uint16_t op = nget16(buf + Operation); + + if (!(htype == HdrTypeEther && ptype == ET_IPv4)) return; + enum { /* only valid for this combination of header + proto */ + SrcMAC = 8, + SrcIP = 14, + DstMAC = 18, + DstIP = 24, + }; + + if (op == OpReq) { + uint32_t daddr = nget32(buf + DstIP); + if (daddr == state.ip) { + uint8_t *pkt = ether_start(30, (struct ethernet){ + .dst = buf + SrcMAC, + .type = ET_ARP, + }); + nput16(pkt + HdrType, 1); + nput16(pkt + ProtoType, ET_IPv4); + pkt[HdrALen] = 6; + pkt[ProtoALen] = 4; + nput16(pkt + Operation, OpReply); + memcpy(pkt + SrcMAC, state.mac, 6); + nput32(pkt + SrcIP, state.ip); + memcpy(pkt + DstMAC, buf + SrcMAC, 10); /* sender's MAC and IP */ + ether_finish(pkt); + } + } +} diff --git a/src/user/app/netstack/ether.c b/src/user/app/netstack/ether.c new file mode 100644 index 0000000..5893632 --- /dev/null +++ b/src/user/app/netstack/ether.c @@ -0,0 +1,59 @@ +#include +#include "proto.h" +#include "util.h" + +enum { + DstMAC = 0, + SrcMAC = 6, + EtherType = 12, + Payload = 14, +}; +struct ethq *ether_queue; + +void ether_parse(const uint8_t *buf, size_t len) { + struct ethernet ether = (struct ethernet){ + .src = buf + SrcMAC, + .dst = buf + DstMAC, + .type = nget16(buf + EtherType), + }; + + for (struct ethq **iter = ðer_queue; iter && *iter; ) { + struct ethq *qe = *iter; + _syscall_fs_respond(qe->h, buf, len, 0); + /* remove entry */ + /* yes, doing it this way here doesn't make sense. i'm preparing for filtering */ + *iter = qe->next; + free(qe); + } + + switch (ether.type) { + case ET_IPv4: + ipv4_parse(buf + Payload, len - Payload, ether); + break; + case ET_ARP: + arp_parse(buf + Payload, len - Payload); + break; + } +} + +static const size_t fhoff = sizeof(size_t); +uint8_t *ether_start(size_t len, struct ethernet ether) { + if (len < 60 - Payload) len = 60 - Payload; + + if (!ether.dst) eprintf("NULL ether.dst!"); // TODO arp? i guess? + if (!ether.src) ether.src = &state.mac; + + uint8_t *buf = malloc(fhoff + Payload + len); + memset(buf, 0, fhoff + Payload + len); + *(size_t*)buf = len + Payload; + memcpy(buf + fhoff + DstMAC, ether.dst, 6); + memcpy(buf + fhoff + SrcMAC, ether.src, 6); + nput16(buf + fhoff + EtherType, ether.type); + return buf + fhoff + Payload; +} +void ether_finish(uint8_t *pkt) { + uint8_t *buf = pkt - Payload - fhoff; + size_t len = *(size_t*)buf; + _syscall_write(state.raw_h, buf + fhoff, len, 0, 0); + free(buf); +} diff --git a/src/user/app/netstack/fs.c b/src/user/app/netstack/fs.c new file mode 100644 index 0000000..b8c2f7b --- /dev/null +++ b/src/user/app/netstack/fs.c @@ -0,0 +1,154 @@ +#include "proto.h" +#include +#include +#include +#include +#include + +enum handle_type { + H_ROOT, + H_ETHER, + H_UDP, +}; + +struct strqueue { + struct strqueue *next; + size_t len; + char buf[]; +}; + +struct handle { + enum handle_type type; + struct { + struct udp_conn *c; + struct strqueue *rx, *rxlast; + } udp; + handle_t reqh; +}; + + +static void udp_listen_callback(struct udp_conn *c, void *arg) { + struct handle *h = arg; + h->udp.c = c; + h->udp.rx = NULL; + h->udp.rxlast = NULL; + _syscall_fs_respond(h->reqh, h, 0, 0); + h->reqh = -1; +} + +static void udp_recv_callback(const void *buf, size_t len, void *arg) { + struct handle *h = arg; + if (h->reqh >= 0) { + _syscall_fs_respond(h->reqh, buf, len, 0); + h->reqh = -1; + return; + } + // TODO don't malloc on the network thread, dumbass + struct strqueue *sq = malloc(sizeof(*sq) + len); + sq->next = NULL; + sq->len = len; + memcpy(sq->buf, buf, len); + if (h->udp.rx) { + h->udp.rxlast->next = sq; + h->udp.rxlast = sq; + } else { + h->udp.rx = sq; + h->udp.rxlast = sq; + } +} + +static void udp_recv_enqueue(struct handle *h, handle_t reqh) { + if (h->reqh > 0) { + // TODO queue + _syscall_fs_respond(reqh, NULL, -1, 0); + } else if (h->udp.rx) { + _syscall_fs_respond(reqh, h->udp.rx->buf, h->udp.rx->len, 0); + h->udp.rx = h->udp.rx->next; + free(h->udp.rx); + } else { + h->reqh = reqh; + } +} + +void fs_thread(void *arg) { (void)arg; + const size_t buflen = 4096; + char *buf = malloc(buflen); + for (;;) { + struct fs_wait_response res; + handle_t reqh = _syscall_fs_wait(buf, buflen, &res); + if (reqh < 0) break; + struct handle *h = res.id; + long ret; + switch (res.op) { + case VFSOP_OPEN: + if (res.len == 1 && !memcmp("/", buf, 1)) { + h = malloc(sizeof *h); + h->type = H_ROOT; + _syscall_fs_respond(reqh, h, 0, 0); + } else if (res.len == 4 && !memcmp("/raw", buf, 4)) { + h = malloc(sizeof *h); + h->type = H_ETHER; + _syscall_fs_respond(reqh, h, 0, 0); + } else if (res.len > 6 && !memcmp("/udpl/", buf, 6)) { + uint16_t port = strtol(buf + 6, NULL, 0); + h = malloc(sizeof *h); + h->type = H_UDP; + h->udp.c = NULL; + h->reqh = reqh; + udp_listen(port, udp_listen_callback, udp_recv_callback, h); + } else { + _syscall_fs_respond(reqh, NULL, -1, 0); + } + break; + case VFSOP_READ: + switch (h->type) { + case H_ROOT: { + struct dirbuild db; + dir_start(&db, res.offset, buf, sizeof buf); + dir_append(&db, "raw"); + dir_append(&db, "udpl/"); + _syscall_fs_respond(reqh, buf, dir_finish(&db), 0); + break;} + case H_ETHER: { + struct ethq *qe; + qe = malloc(sizeof *qe); + qe->h = reqh; + qe->next = ether_queue; + ether_queue = qe; + break;} + case H_UDP: + udp_recv_enqueue(h, reqh); + break; + default: + _syscall_fs_respond(reqh, NULL, -1, 0); + } + break; + case VFSOP_WRITE: + switch (h->type) { + case H_ETHER: + ret = _syscall_write(state.raw_h, buf, res.len, 0, 0); + _syscall_fs_respond(reqh, NULL, ret, 0); + break; + case H_UDP: + udpc_send(h->udp.c, buf, res.len); + _syscall_fs_respond(reqh, NULL, res.len, 0); + break; + default: + _syscall_fs_respond(reqh, NULL, -1, 0); + } + break; + case VFSOP_CLOSE: + // TODO remove entries in queue + // TODO why does close even have _syscall_fs_respond? + if (h->type == H_UDP) + udpc_close(h->udp.c); + free(h); + _syscall_fs_respond(reqh, NULL, -1, 0); + break; + default: + _syscall_fs_respond(reqh, NULL, -1, 0); + break; + } + } + free(buf); +} diff --git a/src/user/app/netstack/icmp.c b/src/user/app/netstack/icmp.c new file mode 100644 index 0000000..0c6a502 --- /dev/null +++ b/src/user/app/netstack/icmp.c @@ -0,0 +1,34 @@ +#include "proto.h" +#include "util.h" + +enum { + Type = 0, + Code = 1, + Checksum = 2, + Payload = 4, +}; + +void icmp_parse(const uint8_t *buf, size_t len, struct ipv4 ip) { + if (len < Payload) return; + uint8_t type = buf[Type]; + if (type == 8 && ip.dst == state.ip) { + /* echo reply */ + icmp_send(buf + Payload, len - Payload, (struct icmp){ + .type = 0, + .ip.dst = ip.src, + .ip.e.dst = ip.e.src, + }); + } +} + +void icmp_send(const void *payload, size_t len, struct icmp i) { + i.ip.proto = 1; + uint8_t *pkt = malloc(Payload + len); + pkt[Type] = i.type; + pkt[Code] = i.code; + memcpy(pkt + Payload, payload, len); + nput16(pkt + Checksum, 0); + nput16(pkt + Checksum, ip_checksum(pkt, Payload + len)); + ipv4_send(pkt, Payload + len, i.ip); + free(pkt); +} diff --git a/src/user/app/netstack/ipv4.c b/src/user/app/netstack/ipv4.c new file mode 100644 index 0000000..3243d3c --- /dev/null +++ b/src/user/app/netstack/ipv4.c @@ -0,0 +1,215 @@ +#include "proto.h" +#include "util.h" +#include +#include + +enum { + Version = 0, + HdrLen = 0, + TotalLen = 2, + Id = 4, + FragInfo = 6, + EvilBit = 0x8000, + DontFrag = 0x4000, + MoreFrags = 0x2000, + FragOff = 0x1FFF, + TTL = 8, + Proto = 9, + Checksum = 10, + SrcIP = 12, + DstIP = 16, +}; +static void ipv4_dispatch(const uint8_t *buf, size_t len, struct ipv4 ip); + +struct fragment { + struct fragment *next; /* sorted */ + size_t len, offset; + bool last; + uint8_t buf[]; +}; +struct fragmented { + /* src, dst, proto, id come from the first arrived packet + * and are used to tell fragmenteds apart. + * the rest comes from the sequentially first packet. + * ip.h.header points to a malloc'd buffer*/ + struct ipv4 h; + + struct fragment *first; + struct fragmented *next, **prev; /* *(inc->prev) == inc */ + // TODO timer +}; +struct fragmented *fragmenteds; +static struct fragmented *fragmented_find(struct ipv4 fraghdr); +static void fragmented_tryinsert(const uint8_t *payload, size_t plen, struct ipv4 ip); +static void fragmented_tryfinish(struct fragmented *inc); +static void fragmented_free(struct fragmented *inc); + +static struct fragmented *fragmented_find(struct ipv4 fraghdr) { + struct fragmented *inc; + for (inc = fragmenteds; inc; inc = inc->next) { + if (inc->h.src == fraghdr.src && + inc->h.dst == fraghdr.dst && + inc->h.proto == fraghdr.proto && + inc->h.id == fraghdr.id) + { + return inc; + } + } + inc = malloc(sizeof *inc); + memset(inc, 0, sizeof *inc); + inc->h.src = fraghdr.src; + inc->h.dst = fraghdr.dst; + inc->h.proto = fraghdr.proto; + inc->h.id = fraghdr.id; + + inc->next = fragmenteds; + if (inc->next) inc->next->prev = &inc->next; + inc->prev = &fragmenteds; + *inc->prev = inc; + return inc; +} + +static void fragmented_tryinsert(const uint8_t *payload, size_t plen, struct ipv4 ip) { + struct fragmented *inc = fragmented_find(ip); + size_t off = (ip.fraginfo & FragOff) * 8; + bool last = !(ip.fraginfo & MoreFrags); + // eprintf("fragmented packet, %u + %u, part of 0x%x", off, plen, inc); + + /* find the first fragment at a bigger offset, and insert before it */ + struct fragment **insert = &inc->first; + for (; *insert; insert = &(*insert)->next) { + if ((*insert)->offset > off) break; + if ((*insert)->offset == off) return; /* duplicate packet */ + } + /* later on: frag->next = *insert; + * if we're the last fragment, frag->next must == NULL */ + if (last && *insert != NULL) return; + + struct fragment *frag = malloc(sizeof(struct fragment) + plen); + frag->next = *insert; + *insert = frag; + frag->len = plen; + frag->offset = off; + frag->last = last; + memcpy(frag->buf, payload, plen); + + if (off == 0) { /* save header */ + assert(!inc->h.header); + void *headercpy = malloc(ip.hlen); + memcpy(headercpy, ip.header, ip.hlen); + inc->h = ip; + inc->h.header = headercpy; + } + + fragmented_tryfinish(inc); +} + +static void fragmented_tryfinish(struct fragmented *inc) { + if (inc->first->offset != 0) return; + for (struct fragment *iter = inc->first; iter; iter = iter->next) { + size_t iterend = iter->offset + iter->len; + struct fragment *next = iter->next; + if (next) { + if (iterend < next->offset) return; /* incomplete */ + if (iterend > next->offset) { + fragmented_free(inc); + return; + } + } else if (iter->last) { + void *buf = malloc(iterend); + for (struct fragment *iter = inc->first; iter; iter = iter->next) { + assert(iter->offset + iter->len <= iterend); + memcpy(buf + iter->offset, iter->buf, iter->len); + } + ipv4_dispatch(buf, iterend, inc->h); + free(buf); + fragmented_free(inc); + } + } +} + +static void fragmented_free(struct fragmented *inc) { + if (inc->next) { + inc->next->prev = inc->prev; + *inc->next->prev = inc->next; + } else { + *inc->prev = NULL; + } + + for (struct fragment *next, *iter = inc->first; iter; iter = next) { + next = iter->next; + free(iter); + } + free((void*)inc->h.header); + free(inc); +} + + +static void ipv4_dispatch(const uint8_t *buf, size_t len, struct ipv4 ip) { + switch (ip.proto) { + case 0x01: icmp_parse(buf, len, ip); break; + case 0x11: udp_parse(buf, len, ip); break; + } +} + +void ipv4_parse(const uint8_t *buf, size_t len, struct ethernet ether) { + uint8_t version, headerlen; + uint16_t totallen; + + version = buf[Version] >> 4; + if (version != 4) return; + headerlen = (buf[HdrLen] & 0xf) * 4; + totallen = nget16(buf + TotalLen); + if (totallen < headerlen) return; + + /* ignores checksum. TODO? */ + + struct ipv4 ip = (struct ipv4){ + .e = ether, + .src = nget32(buf + SrcIP), + .dst = nget32(buf + DstIP), + .id = nget16(buf + Id), + .fraginfo = nget16(buf + FragInfo), + .proto = buf[Proto], + .header = buf, + .hlen = headerlen, + }; + + if (ip.fraginfo & ~(EvilBit | DontFrag)) { + fragmented_tryinsert(buf + headerlen, totallen - headerlen, ip); + } else { + if (totallen > len) return; + ipv4_dispatch(buf + headerlen, totallen - headerlen, ip); + } +} + +static uint16_t next_id = 0; +void ipv4_send(const void *payload, size_t len, struct ipv4 ip) { + const size_t mtu = 1500; + const size_t hdrlen = 20; + + ip.e.type = ET_IPv4; + if (!ip.src) ip.src = state.ip; + if (!ip.e.dst && ip.dst == 0xFFFFFFFF) + ip.e.dst = &MAC_BROADCAST; + + uint16_t id = next_id++; + for (size_t off = 0, fraglen = mtu - hdrlen; off < len; off += fraglen) { + if (fraglen > len - off) + fraglen = len - off; + bool last = off + fraglen >= len; + uint8_t *pkt = ether_start(hdrlen + fraglen, ip.e); + pkt[Version] = 0x40; + pkt[HdrLen] |= hdrlen / 4; + nput16(pkt + TotalLen, hdrlen + fraglen); + nput16(pkt + Id, id); + nput16(pkt + FragInfo, (off >> 3) | (last ? 0 : MoreFrags)); + pkt[TTL] = 0xFF; + pkt[Proto] = ip.proto; + nput32(pkt + SrcIP, ip.src); + nput32(pkt + DstIP, ip.dst); + nput16(pkt + Checksum, ip_checksum(pkt, hdrlen)); + memcpy(pkt + hdrlen, payload + off, fraglen); + ether_finish(pkt); + } +} diff --git a/src/user/app/netstack/netstack.c b/src/user/app/netstack/netstack.c new file mode 100644 index 0000000..0451511 --- /dev/null +++ b/src/user/app/netstack/netstack.c @@ -0,0 +1,43 @@ +#include "proto.h" +#include "util.h" +#include +#include +#include +#include +#include +#include + +struct net_state state = { + // TODO dynamically get mac + .mac = {0x52, 0x54, 0x00, 0xCA, 0x77, 0x1A}, + .ip = (192 << 24) + (168 << 16) + 11, +}; + +void network_thread(void *arg) { (void)arg; + const size_t buflen = 4096; + char *buf = malloc(buflen); + for (;;) { + long ret = _syscall_read(state.raw_h, buf, buflen, -1); + if (ret < 0) break; + ether_parse((void*)buf, ret); + } + free(buf); +} + +void fs_thread(void *arg); + +int main(int argc, char **argv) { + if (argc < 2) { + eprintf("no argument"); + return 1; + } + state.raw_h = _syscall_open(argv[1], strlen(argv[1]), 0); + if (state.raw_h < 0) { + eprintf("couldn't open %s", argv[1]); + return 1; + } + thread_create(0, network_thread, NULL); + thread_create(0, fs_thread, NULL); + _syscall_await(); + return 0; +} diff --git a/src/user/app/netstack/proto.h b/src/user/app/netstack/proto.h new file mode 100644 index 0000000..106f286 --- /dev/null +++ b/src/user/app/netstack/proto.h @@ -0,0 +1,70 @@ +#pragma once +#include +#include +#include + +typedef uint8_t mac_t[6]; +static const mac_t MAC_BROADCAST = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + +extern struct net_state { + mac_t mac; + uint32_t ip; + + handle_t raw_h; +} state; + +enum { /* ethertype */ + ET_IPv4 = 0x800, + ET_ARP = 0x806, +}; + +struct ethernet { + const mac_t *src, *dst; + uint16_t type; +}; + +struct ipv4 { + struct ethernet e; + uint32_t src, dst; + uint16_t id, fraginfo; + uint8_t proto; + const uint8_t *header; size_t hlen; +}; + +struct icmp { + struct ipv4 ip; + uint8_t type, code; +}; + + +/* NOT THREADSAFE, YET USED FROM CONCURRENT THREADS + * will break if i implement a scheduler*/ +struct ethq { + struct ethq *next; + handle_t h; +}; +extern struct ethq *ether_queue; + +void arp_parse(const uint8_t *buf, size_t len); + +void icmp_parse(const uint8_t *buf, size_t len, struct ipv4 ip); +void icmp_send(const void *payload, size_t len, struct icmp i); + +void ipv4_parse(const uint8_t *buf, size_t len, struct ethernet ether); +void ipv4_send(const void *payload, size_t len, struct ipv4 ip); + +void ether_parse(const uint8_t *buf, size_t len); +uint8_t *ether_start(size_t len, struct ethernet ether); +void ether_finish(uint8_t *pkt); + +struct udp_conn; +void udp_parse(const uint8_t *buf, size_t len, struct ipv4 ip); +/* calls callback once, after a client connects. */ +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); +// TODO udp_onclosed +void udpc_send(struct udp_conn *, const void *buf, size_t len); +/* frees */ +void udpc_close(struct udp_conn *); diff --git a/src/user/app/netstack/udp.c b/src/user/app/netstack/udp.c new file mode 100644 index 0000000..8129a48 --- /dev/null +++ b/src/user/app/netstack/udp.c @@ -0,0 +1,98 @@ +#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, **prev; +}; +struct udp_conn *conns; +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; + + c->next = conns; + if (c->next) *(c->next->prev) = c; + c->prev = &conns; + *c->prev = c; +} +void udpc_close(struct udp_conn *c) { + if (c->next) c->next->prev = c->prev; + *(c->prev) = c->next; + free(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 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); + } +} diff --git a/src/user/app/netstack/util.c b/src/user/app/netstack/util.c new file mode 100644 index 0000000..7c484f3 --- /dev/null +++ b/src/user/app/netstack/util.c @@ -0,0 +1,31 @@ +#include "util.h" + +/* https://www.w3.org/TR/PNG/#D-CRCAppendix */ +static uint32_t crc_table[256]; +uint32_t crc32(const uint8_t *buf, size_t len) { + if (!crc_table[1]) { + for (int i = 0; i < 256; i++) { + uint32_t c = i; + for (int j = 0; j < 8; j++) + c = ((c&1) ? 0xedb88320 : 0) ^ (c >> 1); + crc_table[i] = c; + } + } + + uint32_t c = 0xFFFFFFFF; + for (size_t i = 0; i < len; i++) + c = crc_table[(c ^ buf[i]) & 0xff] ^ (c >> 8); + return ~c; +} + +uint16_t ip_checksum(const uint8_t *buf, size_t len) { + uint32_t c = 0; + while (len >= 2) { + c += nget16(buf); + buf += 2; len -= 2; + } + if (len) c += (*buf) << 8; + while (c >= 0xFFFF) + c = (c & 0xFFFF) + (c >> 16); + return ~c; +} diff --git a/src/user/app/netstack/util.h b/src/user/app/netstack/util.h new file mode 100644 index 0000000..5472b05 --- /dev/null +++ b/src/user/app/netstack/util.h @@ -0,0 +1,38 @@ +#pragma once +#include +#include +#include +#include + +#define eprintf(fmt, ...) fprintf(stderr, "netstack: "fmt"\n" __VA_OPT__(,) __VA_ARGS__) + +uint32_t crc32(const uint8_t *buf, size_t len); +uint16_t ip_checksum(const uint8_t *buf, size_t len); + +static inline void nput16(void *vbuf, uint16_t n) { + uint8_t *b = vbuf; + b[0] = n >> 8; + b[1] = n >> 0; +} + +static inline void nput32(void *vbuf, uint32_t n) { + uint8_t *b = vbuf; + b[0] = n >> 24; + b[1] = n >> 16; + b[2] = n >> 8; + b[3] = n >> 0; +} + +static inline uint16_t nget16(const void *vbuf) { + const uint8_t *b = vbuf; + return (b[0] << 8) + | (b[1] << 0); +} + +static inline uint32_t nget32(const void *vbuf) { + const uint8_t *b = vbuf; + return (b[0] << 24) + | (b[1] << 16) + | (b[2] << 8) + | (b[3] << 0); +} -- cgit v1.2.3