diff options
Diffstat (limited to 'kernel/sysnet.c')
-rw-r--r-- | kernel/sysnet.c | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/kernel/sysnet.c b/kernel/sysnet.c new file mode 100644 index 0000000..1c48cb3 --- /dev/null +++ b/kernel/sysnet.c @@ -0,0 +1,185 @@ +// +// network system calls. +// + +#include "types.h" +#include "param.h" +#include "memlayout.h" +#include "riscv.h" +#include "spinlock.h" +#include "proc.h" +#include "defs.h" +#include "fs.h" +#include "sleeplock.h" +#include "file.h" +#include "net.h" + +struct sock { + struct sock *next; // the next socket in the list + uint32 raddr; // the remote IPv4 address + uint16 lport; // the local UDP port number + uint16 rport; // the remote UDP port number + struct spinlock lock; // protects the rxq + struct mbufq rxq; // a queue of packets waiting to be received +}; + +static struct spinlock lock; +static struct sock *sockets; + +void +sockinit(void) +{ + initlock(&lock, "socktbl"); +} + +int +sockalloc(struct file **f, uint32 raddr, uint16 lport, uint16 rport) +{ + struct sock *si, *pos; + + si = 0; + *f = 0; + if ((*f = filealloc()) == 0) + goto bad; + if ((si = (struct sock*)kalloc()) == 0) + goto bad; + + // initialize objects + si->raddr = raddr; + si->lport = lport; + si->rport = rport; + initlock(&si->lock, "sock"); + mbufq_init(&si->rxq); + (*f)->type = FD_SOCK; + (*f)->readable = 1; + (*f)->writable = 1; + (*f)->sock = si; + + // add to list of sockets + acquire(&lock); + pos = sockets; + while (pos) { + if (pos->raddr == raddr && + pos->lport == lport && + pos->rport == rport) { + release(&lock); + goto bad; + } + pos = pos->next; + } + si->next = sockets; + sockets = si; + release(&lock); + return 0; + +bad: + if (si) + kfree((char*)si); + if (*f) + fileclose(*f); + return -1; +} + +void +sockclose(struct sock *si) +{ + struct sock **pos; + struct mbuf *m; + + // remove from list of sockets + acquire(&lock); + pos = &sockets; + while (*pos) { + if (*pos == si){ + *pos = si->next; + break; + } + pos = &(*pos)->next; + } + release(&lock); + + // free any pending mbufs + while (!mbufq_empty(&si->rxq)) { + m = mbufq_pophead(&si->rxq); + mbuffree(m); + } + + kfree((char*)si); +} + +int +sockread(struct sock *si, uint64 addr, int n) +{ + struct proc *pr = myproc(); + struct mbuf *m; + int len; + + acquire(&si->lock); + while (mbufq_empty(&si->rxq) && !pr->killed) { + sleep(&si->rxq, &si->lock); + } + if (pr->killed) { + release(&si->lock); + return -1; + } + m = mbufq_pophead(&si->rxq); + release(&si->lock); + + len = m->len; + if (len > n) + len = n; + if (copyout(pr->pagetable, addr, m->head, len) == -1) { + mbuffree(m); + return -1; + } + mbuffree(m); + return len; +} + +int +sockwrite(struct sock *si, uint64 addr, int n) +{ + struct proc *pr = myproc(); + struct mbuf *m; + + m = mbufalloc(MBUF_DEFAULT_HEADROOM); + if (!m) + return -1; + + if (copyin(pr->pagetable, mbufput(m, n), addr, n) == -1) { + mbuffree(m); + return -1; + } + net_tx_udp(m, si->raddr, si->lport, si->rport); + return n; +} + +// called by protocol handler layer to deliver UDP packets +void +sockrecvudp(struct mbuf *m, uint32 raddr, uint16 lport, uint16 rport) +{ + // + // Find the socket that handles this mbuf and deliver it, waking + // any sleeping reader. Free the mbuf if there are no sockets + // registered to handle it. + // + struct sock *si; + + acquire(&lock); + si = sockets; + while (si) { + if (si->raddr == raddr && si->lport == lport && si->rport == rport) + goto found; + si = si->next; + } + release(&lock); + mbuffree(m); + return; + +found: + acquire(&si->lock); + mbufq_pushtail(&si->rxq, m); + wakeup(&si->rxq); + release(&si->lock); + release(&lock); +} |