diff options
author | dzwdz | 2022-08-23 17:58:42 +0200 |
---|---|---|
committer | dzwdz | 2022-08-23 17:58:42 +0200 |
commit | 03c5dd9462492e291c6a49b88e1cd9ab34d86b6f (patch) | |
tree | 8c8d8e73c8c07b83f5aede18d83671369d0f7589 /src/user/app/netstack | |
parent | fcdadf5df39e1d72f9ac79fa384fc6b98be0b1aa (diff) |
user/netstack: TCP listen and close
Diffstat (limited to 'src/user/app/netstack')
-rw-r--r-- | src/user/app/netstack/fs.c | 59 | ||||
-rw-r--r-- | src/user/app/netstack/ipv4.c | 3 | ||||
-rw-r--r-- | src/user/app/netstack/proto.h | 14 | ||||
-rw-r--r-- | src/user/app/netstack/tcp.c | 180 | ||||
-rw-r--r-- | src/user/app/netstack/udp.c | 23 | ||||
-rw-r--r-- | src/user/app/netstack/util.c | 16 | ||||
-rw-r--r-- | src/user/app/netstack/util.h | 4 |
7 files changed, 278 insertions, 21 deletions
diff --git a/src/user/app/netstack/fs.c b/src/user/app/netstack/fs.c index 19e5bfd..cc4491d 100644 --- a/src/user/app/netstack/fs.c +++ b/src/user/app/netstack/fs.c @@ -6,19 +6,21 @@ * ARP cache (currently read-only) * /net/0.0.0.0/connect/1.2.3.4/udp/53 * connect from 0.0.0.0 (any ip) to 1.2.3.4 on udp port 53 - * /net/0.0.0.0/listen/udp/53 + * /net/0.0.0.0/listen/{tcp,udp}/53 * waits for a connection to any ip on udp port 53 * open() returns once a connection to ip 0.0.0.0 on udp port 53 is received */ #include "proto.h" #include "util.h" #include <camellia/syscalls.h> +#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> enum handle_type { H_ETHER, + H_TCP, H_UDP, H_ARP, }; @@ -35,10 +37,31 @@ struct handle { struct udp_conn *c; struct strqueue *rx, *rxlast; } udp; + struct { + struct tcp_conn *c; + } tcp; + bool dead; handle_t reqh; }; +static void tcp_listen_callback(struct tcp_conn *c, void *arg) { + struct handle *h = arg; + h->tcp.c = c; + _syscall_fs_respond(h->reqh, h, 0, 0); + h->reqh = -1; +} + +static void tcp_close_callback(void *arg) { + struct handle *h = arg; + h->dead = true; + if (h->reqh >= 0) { + _syscall_fs_respond(h->reqh, NULL, -ECONNRESET, 0); + h->reqh = -1; + return; + } +} + static void udp_listen_callback(struct udp_conn *c, void *arg) { struct handle *h = arg; h->udp.c = c; @@ -66,11 +89,11 @@ static void udp_recv_callback(const void *buf, size_t len, void *arg) { } } -static void udp_recv_enqueue(struct handle *h, handle_t reqh) { +static void 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) { + } else if (h->type == H_UDP && 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); @@ -87,10 +110,12 @@ static void fs_open(handle_t reqh, char *path) { if (strcmp(path, "raw") == 0) { h = malloc(sizeof *h); + memset(h, 0, sizeof *h); h->type = H_ETHER; respond(h, 0); } else if (strcmp(path, "arp") == 0) { h = malloc(sizeof *h); + memset(h, 0, sizeof *h); h->type = H_ARP; respond(h, 0); } @@ -118,12 +143,23 @@ static void fs_open(handle_t reqh, char *path) { h = malloc(sizeof *h); memset(h, 0, sizeof *h); h->type = H_UDP; - h->udp.c = NULL; h->reqh = reqh; udp_listen(port, udp_listen_callback, udp_recv_callback, h); return; } } + if (strcmp(proto, "tcp") == 0) { + port_s = strtok_r(NULL, "/", &save); + if (port_s) { + uint16_t port = strtol(port_s, NULL, 0); + h = malloc(sizeof *h); + memset(h, 0, sizeof *h); + h->type = H_TCP; + h->reqh = reqh; + tcp_listen(port, tcp_listen_callback, tcp_close_callback, h); + return; + } + } } else if (strcmp(verb, "connect") == 0) { if (ip_parse(strtok_r(NULL, "/", &save), &dstip) < 0) respond(NULL, -1); @@ -172,6 +208,10 @@ void fs_thread(void *arg) { (void)arg; } break; case VFSOP_READ: + if (h->dead) { + _syscall_fs_respond(reqh, NULL, -ECONNRESET, 0); + break; + } switch (h->type) { case H_ETHER: { struct ethq *qe; @@ -180,8 +220,9 @@ void fs_thread(void *arg) { (void)arg; qe->next = ether_queue; ether_queue = qe; break;} + case H_TCP: case H_UDP: - udp_recv_enqueue(h, reqh); + recv_enqueue(h, reqh); break; case H_ARP: arp_fsread(reqh, res.offset); @@ -191,6 +232,10 @@ void fs_thread(void *arg) { (void)arg; } break; case VFSOP_WRITE: + if (h->dead) { + _syscall_fs_respond(reqh, NULL, -ECONNRESET, 0); + break; + } switch (h->type) { case H_ETHER: ret = _syscall_write(state.raw_h, buf, res.len, 0, 0); @@ -207,8 +252,8 @@ void fs_thread(void *arg) { (void)arg; 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); + if (h->type == H_TCP) tcpc_close(h->tcp.c); + if (h->type == H_UDP) udpc_close(h->udp.c); free(h); _syscall_fs_respond(reqh, NULL, -1, 0); break; diff --git a/src/user/app/netstack/ipv4.c b/src/user/app/netstack/ipv4.c index 3243d3c..1336dc1 100644 --- a/src/user/app/netstack/ipv4.c +++ b/src/user/app/netstack/ipv4.c @@ -148,7 +148,8 @@ static void fragmented_free(struct fragmented *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; + case 0x06: tcp_parse(buf, len, ip); break; + case 0x11: udp_parse(buf, len, ip); break; } } diff --git a/src/user/app/netstack/proto.h b/src/user/app/netstack/proto.h index 68dccad..0c4338b 100644 --- a/src/user/app/netstack/proto.h +++ b/src/user/app/netstack/proto.h @@ -31,6 +31,11 @@ struct ipv4 { const uint8_t *header; size_t hlen; }; +struct tcp { + struct ipv4 ip; + uint16_t src, dst; +}; + struct udp { struct ipv4 ip; uint16_t src, dst; @@ -81,3 +86,12 @@ struct udp_conn *udpc_new( void udpc_send(struct udp_conn *, const void *buf, size_t len); /* frees */ void udpc_close(struct udp_conn *); + +struct tcp_conn; +void tcp_parse(const uint8_t *buf, size_t len, struct ipv4 ip); +void tcp_listen( + uint16_t port, + void (*on_conn)(struct tcp_conn *, void *carg), + void (*on_close)(void *carg), + void *carg); +void tcpc_close(struct tcp_conn *); diff --git a/src/user/app/netstack/tcp.c b/src/user/app/netstack/tcp.c new file mode 100644 index 0000000..71e82f2 --- /dev/null +++ b/src/user/app/netstack/tcp.c @@ -0,0 +1,180 @@ +/* Welcome to spaghetti land. */ +#include "proto.h" +#include "util.h" + +enum { + SrcPort = 0, + DstPort = 2, + Seq = 4, + AckNum = 8, /* last processed seq + 1 */ + Flags = 12, + FlagSize = 0xF000, /* size of header / 4 bytes */ + /* 3 bits: 0, still unused + * 3 bits: modern stuff not in the original RFC */ + FlagURG = 0x0020, /* urgent */ + FlagACK = 0x0010, + FlagPSH = 0x0008, /* force buffer flush */ + FlagRST = 0x0004, /* reset connection */ + FlagSYN = 0x0002, /* first packet; sync sequence numbers */ + FlagFIN = 0x0001, /* last packet */ + FlagAll = 0x003F, + WinSize = 14, /* amount of data we're willing to receive */ + Checksum = 16, + Urgent = 18, + MinHdr = 20, +}; + +enum tcp_state { + LISTEN, + SYN_RCVD, + LAST_ACK, + CLOSED, +}; + +struct tcp_conn { + uint32_t lip, rip; + mac_t rmac; + uint16_t lport, rport; + struct tcp_conn *next, **link; + + enum tcp_state state; + uint32_t lack, rack; + uint32_t lseq; + bool uclosed; /* did the user close? */ + + void (*on_conn)(struct tcp_conn *, void *carg); + void (*on_close)(void *carg); + void *carg; +}; +static struct tcp_conn *conns; +static void conns_append(struct tcp_conn *c) { + c->next = conns; + if (c->next) + c->next->link = &c->next; + c->link = &conns; + *c->link = c; +} +static void tcpc_send(struct tcp_conn *c, uint16_t flags) { + uint8_t *pkt = malloc(MinHdr); + memset(pkt, 0, MinHdr); + + nput16(pkt + SrcPort, c->lport); + nput16(pkt + DstPort, c->rport); + nput32(pkt + Seq, c->lseq); + nput32(pkt + AckNum, c->lack); + flags |= (MinHdr / 4) << 12; + nput16(pkt + Flags, flags); + nput16(pkt + WinSize, 4096); + nput16(pkt + Checksum, ip_checksumphdr(pkt, MinHdr, c->lip, c->rip, 6)); + + ipv4_send(pkt, MinHdr, (struct ipv4){ + .proto = 6, + .src = c->lip, + .dst = c->rip, + .e.dst = &c->rmac, + }); + free(pkt); +} +void tcp_listen( + uint16_t port, + void (*on_conn)(struct tcp_conn *, void *carg), + void (*on_close)(void *carg), + void *carg) +{ + struct tcp_conn *c = malloc(sizeof *c); + c->lport = port; + c->lip = state.ip; + c->state = LISTEN; + c->on_conn = on_conn; + c->on_close = on_close; + c->carg = carg; + conns_append(c); +} +void tcpc_close(struct tcp_conn *c) { + /* ONLY FOR USE BY THE USER, drops their reference */ + if (!c->uclosed) { + c->uclosed = true; + tcpc_send(c, FlagFIN | FlagACK); + c->state = LAST_ACK; + c->on_conn = NULL; + c->on_close = NULL; + } +} + +void tcp_parse(const uint8_t *buf, size_t len, struct ipv4 ip) { + if (len < 20) return; + uint16_t srcport = nget16(buf + SrcPort); + uint16_t dstport = nget16(buf + DstPort); + uint32_t seq = nget32(buf + Seq); + uint32_t acknum = nget32(buf + AckNum); + uint16_t flags = nget16(buf + Flags); + uint16_t winsize = nget16(buf + WinSize); + uint16_t chksum = nget16(buf + Checksum); + uint16_t hdrlen = ((flags & FlagSize) >> 12) * 4; + if (hdrlen > len) return; + uint16_t payloadlen = len - hdrlen; + + for (struct tcp_conn *iter = conns; iter; iter = iter->next) { + if (iter->state == CLOSED) continue; + if (iter->lport != dstport) continue; + + if (iter->state == LISTEN && (flags & FlagAll) == FlagSYN) { + iter->state = SYN_RCVD; + iter->rip = ip.src; + iter->rport = srcport; + iter->lack = seq + 1; + memcpy(&iter->rmac, ip.e.src, sizeof(mac_t)); + tcpc_send(iter, FlagSYN | FlagACK); + iter->lseq++; + if (iter->on_conn) iter->on_conn(iter, iter->carg); + return; + } + + if (iter->rip == ip.src && iter->rport == srcport) { + // TODO doesn't handle seq/ack overflows + if (flags & FlagACK) { + if (iter->rack < acknum) + iter->rack = acknum; + if (iter->state == LAST_ACK) { + iter->state = CLOSED; // TODO check if ack has correct number + // TODO free (also after a timeout) + return; + } + } + if (iter->lack != seq && iter->lack - 1 != seq) { + eprintf("remote seq jumped by %d", seq - iter->lack); + tcpc_send(iter, FlagACK); + return; + } + iter->lack = seq + 1; + if (flags & FlagFIN) { + // TODO should resend the packet until an ACK is received + // TODO duplicated in tcpc_close + tcpc_send(iter, FlagFIN | FlagACK); + iter->state = LAST_ACK; + if (iter->on_close) iter->on_close(iter->carg); + return; + } + return; + } + } + + uint8_t *pkt = malloc(MinHdr); + memset(pkt, 0, MinHdr); + nput16(pkt + SrcPort, dstport); + nput16(pkt + DstPort, srcport); + nput32(pkt + Seq, acknum); + nput32(pkt + AckNum, seq + 1); + uint16_t pktflags = FlagRST | FlagACK; + pktflags |= (MinHdr / 4) << 12; + nput16(pkt + Flags, pktflags); + nput16(pkt + Checksum, ip_checksumphdr(pkt, MinHdr, ip.src, ip.dst, 6)); + + ipv4_send(pkt, MinHdr, (struct ipv4){ + .proto = 6, + .src = ip.dst, + .dst = ip.src, + .e.dst = ip.e.src, + }); + free(pkt); +} diff --git a/src/user/app/netstack/udp.c b/src/user/app/netstack/udp.c index 46d09de..3d560ae 100644 --- a/src/user/app/netstack/udp.c +++ b/src/user/app/netstack/udp.c @@ -17,14 +17,15 @@ struct udp_conn { 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 *next, **link; }; -struct udp_conn *conns; +static struct udp_conn *conns; static void conns_append(struct udp_conn *c) { c->next = conns; - if (c->next) *(c->next->prev) = c; - c->prev = &conns; - *c->prev = c; + if (c->next) + c->next->link = &c->next; + c->link = &conns; + *c->link = c; } void udp_listen( uint16_t port, @@ -79,8 +80,8 @@ void udpc_send(struct udp_conn *c, const void *buf, size_t len) { free(pkt); } void udpc_close(struct udp_conn *c) { - if (c->next) c->next->prev = c->prev; - *(c->prev) = c->next; + if (c->next) c->next->link = c->link; + *(c->link) = c->next; free(c); } @@ -98,10 +99,10 @@ void udp_parse(const uint8_t *buf, size_t len, struct ipv4 ip) { 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) + 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); diff --git a/src/user/app/netstack/util.c b/src/user/app/netstack/util.c index e9d3118..68092aa 100644 --- a/src/user/app/netstack/util.c +++ b/src/user/app/netstack/util.c @@ -25,8 +25,20 @@ uint16_t ip_checksum(const uint8_t *buf, size_t len) { buf += 2; len -= 2; } if (len) c += (*buf) << 8; - while (c > 0xFFFF) - c = (c & 0xFFFF) + (c >> 16); + while (c > 0xFFFF) c = (c & 0xFFFF) + (c >> 16); + return ~c; +} + +uint16_t ip_checksumphdr( + const uint8_t *buf, size_t len, + uint32_t ip1, uint32_t ip2, + uint16_t proto) +{ + uint32_t c = (uint16_t)~ip_checksum(buf, len); + c += (ip1 & 0xFFFF) + (ip1 >> 16); + c += (ip2 & 0xFFFF) + (ip2 >> 16); + c += proto + len; + 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 index b8fa5c6..0b29560 100644 --- a/src/user/app/netstack/util.h +++ b/src/user/app/netstack/util.h @@ -8,6 +8,10 @@ uint32_t crc32(const uint8_t *buf, size_t len); uint16_t ip_checksum(const uint8_t *buf, size_t len); +uint16_t ip_checksumphdr( + const uint8_t *buf, size_t len, + uint32_t ip1, uint32_t ip2, + uint16_t proto); /* 0 on success, negative failure */ int ip_parse(const char *s, uint32_t *ip); |