summaryrefslogtreecommitdiff
path: root/kernel/net.c
diff options
context:
space:
mode:
authorSanjit Bhat <[email protected]>2023-10-26 06:44:48 -0400
committerSanjit Bhat <[email protected]>2023-10-26 06:44:48 -0400
commitcfae93475dfb4cb5cfe264f4c029136e1447c262 (patch)
tree699903e093e3a23caf7ce3899e7c80e48511f900 /kernel/net.c
parent1ed40716eb54e371df9d1814b9129666b3fe4f09 (diff)
downloadxv6-labs-cfae93475dfb4cb5cfe264f4c029136e1447c262.tar.gz
xv6-labs-cfae93475dfb4cb5cfe264f4c029136e1447c262.tar.bz2
xv6-labs-cfae93475dfb4cb5cfe264f4c029136e1447c262.zip
net add missing files
Diffstat (limited to 'kernel/net.c')
-rw-r--r--kernel/net.c374
1 files changed, 374 insertions, 0 deletions
diff --git a/kernel/net.c b/kernel/net.c
new file mode 100644
index 0000000..137ea2b
--- /dev/null
+++ b/kernel/net.c
@@ -0,0 +1,374 @@
+//
+// networking protocol support (IP, UDP, ARP, etc.).
+//
+
+#include "types.h"
+#include "param.h"
+#include "memlayout.h"
+#include "riscv.h"
+#include "spinlock.h"
+#include "proc.h"
+#include "net.h"
+#include "defs.h"
+
+static uint32 local_ip = MAKE_IP_ADDR(10, 0, 2, 15); // qemu's idea of the guest IP
+static uint8 local_mac[ETHADDR_LEN] = { 0x52, 0x54, 0x00, 0x12, 0x34, 0x56 };
+static uint8 broadcast_mac[ETHADDR_LEN] = { 0xFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF };
+
+// Strips data from the start of the buffer and returns a pointer to it.
+// Returns 0 if less than the full requested length is available.
+char *
+mbufpull(struct mbuf *m, unsigned int len)
+{
+ char *tmp = m->head;
+ if (m->len < len)
+ return 0;
+ m->len -= len;
+ m->head += len;
+ return tmp;
+}
+
+// Prepends data to the beginning of the buffer and returns a pointer to it.
+char *
+mbufpush(struct mbuf *m, unsigned int len)
+{
+ m->head -= len;
+ if (m->head < m->buf)
+ panic("mbufpush");
+ m->len += len;
+ return m->head;
+}
+
+// Appends data to the end of the buffer and returns a pointer to it.
+char *
+mbufput(struct mbuf *m, unsigned int len)
+{
+ char *tmp = m->head + m->len;
+ m->len += len;
+ if (m->len > MBUF_SIZE)
+ panic("mbufput");
+ return tmp;
+}
+
+// Strips data from the end of the buffer and returns a pointer to it.
+// Returns 0 if less than the full requested length is available.
+char *
+mbuftrim(struct mbuf *m, unsigned int len)
+{
+ if (len > m->len)
+ return 0;
+ m->len -= len;
+ return m->head + m->len;
+}
+
+// Allocates a packet buffer.
+struct mbuf *
+mbufalloc(unsigned int headroom)
+{
+ struct mbuf *m;
+
+ if (headroom > MBUF_SIZE)
+ return 0;
+ m = kalloc();
+ if (m == 0)
+ return 0;
+ m->next = 0;
+ m->head = (char *)m->buf + headroom;
+ m->len = 0;
+ memset(m->buf, 0, sizeof(m->buf));
+ return m;
+}
+
+// Frees a packet buffer.
+void
+mbuffree(struct mbuf *m)
+{
+ kfree(m);
+}
+
+// Pushes an mbuf to the end of the queue.
+void
+mbufq_pushtail(struct mbufq *q, struct mbuf *m)
+{
+ m->next = 0;
+ if (!q->head){
+ q->head = q->tail = m;
+ return;
+ }
+ q->tail->next = m;
+ q->tail = m;
+}
+
+// Pops an mbuf from the start of the queue.
+struct mbuf *
+mbufq_pophead(struct mbufq *q)
+{
+ struct mbuf *head = q->head;
+ if (!head)
+ return 0;
+ q->head = head->next;
+ return head;
+}
+
+// Returns one (nonzero) if the queue is empty.
+int
+mbufq_empty(struct mbufq *q)
+{
+ return q->head == 0;
+}
+
+// Intializes a queue of mbufs.
+void
+mbufq_init(struct mbufq *q)
+{
+ q->head = 0;
+}
+
+// This code is lifted from FreeBSD's ping.c, and is copyright by the Regents
+// of the University of California.
+static unsigned short
+in_cksum(const unsigned char *addr, int len)
+{
+ int nleft = len;
+ const unsigned short *w = (const unsigned short *)addr;
+ unsigned int sum = 0;
+ unsigned short answer = 0;
+
+ /*
+ * Our algorithm is simple, using a 32 bit accumulator (sum), we add
+ * sequential 16 bit words to it, and at the end, fold back all the
+ * carry bits from the top 16 bits into the lower 16 bits.
+ */
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ /* mop up an odd byte, if necessary */
+ if (nleft == 1) {
+ *(unsigned char *)(&answer) = *(const unsigned char *)w;
+ sum += answer;
+ }
+
+ /* add back carry outs from top 16 bits to low 16 bits */
+ sum = (sum & 0xffff) + (sum >> 16);
+ sum += (sum >> 16);
+ /* guaranteed now that the lower 16 bits of sum are correct */
+
+ answer = ~sum; /* truncate to 16 bits */
+ return answer;
+}
+
+// sends an ethernet packet
+static void
+net_tx_eth(struct mbuf *m, uint16 ethtype)
+{
+ struct eth *ethhdr;
+
+ ethhdr = mbufpushhdr(m, *ethhdr);
+ memmove(ethhdr->shost, local_mac, ETHADDR_LEN);
+ // In a real networking stack, dhost would be set to the address discovered
+ // through ARP. Because we don't support enough of the ARP protocol, set it
+ // to broadcast instead.
+ memmove(ethhdr->dhost, broadcast_mac, ETHADDR_LEN);
+ ethhdr->type = htons(ethtype);
+ if (e1000_transmit(m)) {
+ mbuffree(m);
+ }
+}
+
+// sends an IP packet
+static void
+net_tx_ip(struct mbuf *m, uint8 proto, uint32 dip)
+{
+ struct ip *iphdr;
+
+ // push the IP header
+ iphdr = mbufpushhdr(m, *iphdr);
+ memset(iphdr, 0, sizeof(*iphdr));
+ iphdr->ip_vhl = (4 << 4) | (20 >> 2);
+ iphdr->ip_p = proto;
+ iphdr->ip_src = htonl(local_ip);
+ iphdr->ip_dst = htonl(dip);
+ iphdr->ip_len = htons(m->len);
+ iphdr->ip_ttl = 100;
+ iphdr->ip_sum = in_cksum((unsigned char *)iphdr, sizeof(*iphdr));
+
+ // now on to the ethernet layer
+ net_tx_eth(m, ETHTYPE_IP);
+}
+
+// sends a UDP packet
+void
+net_tx_udp(struct mbuf *m, uint32 dip,
+ uint16 sport, uint16 dport)
+{
+ struct udp *udphdr;
+
+ // put the UDP header
+ udphdr = mbufpushhdr(m, *udphdr);
+ udphdr->sport = htons(sport);
+ udphdr->dport = htons(dport);
+ udphdr->ulen = htons(m->len);
+ udphdr->sum = 0; // zero means no checksum is provided
+
+ // now on to the IP layer
+ net_tx_ip(m, IPPROTO_UDP, dip);
+}
+
+// sends an ARP packet
+static int
+net_tx_arp(uint16 op, uint8 dmac[ETHADDR_LEN], uint32 dip)
+{
+ struct mbuf *m;
+ struct arp *arphdr;
+
+ m = mbufalloc(MBUF_DEFAULT_HEADROOM);
+ if (!m)
+ return -1;
+
+ // generic part of ARP header
+ arphdr = mbufputhdr(m, *arphdr);
+ arphdr->hrd = htons(ARP_HRD_ETHER);
+ arphdr->pro = htons(ETHTYPE_IP);
+ arphdr->hln = ETHADDR_LEN;
+ arphdr->pln = sizeof(uint32);
+ arphdr->op = htons(op);
+
+ // ethernet + IP part of ARP header
+ memmove(arphdr->sha, local_mac, ETHADDR_LEN);
+ arphdr->sip = htonl(local_ip);
+ memmove(arphdr->tha, dmac, ETHADDR_LEN);
+ arphdr->tip = htonl(dip);
+
+ // header is ready, send the packet
+ net_tx_eth(m, ETHTYPE_ARP);
+ return 0;
+}
+
+// receives an ARP packet
+static void
+net_rx_arp(struct mbuf *m)
+{
+ struct arp *arphdr;
+ uint8 smac[ETHADDR_LEN];
+ uint32 sip, tip;
+
+ arphdr = mbufpullhdr(m, *arphdr);
+ if (!arphdr)
+ goto done;
+
+ // validate the ARP header
+ if (ntohs(arphdr->hrd) != ARP_HRD_ETHER ||
+ ntohs(arphdr->pro) != ETHTYPE_IP ||
+ arphdr->hln != ETHADDR_LEN ||
+ arphdr->pln != sizeof(uint32)) {
+ goto done;
+ }
+
+ // only requests are supported so far
+ // check if our IP was solicited
+ tip = ntohl(arphdr->tip); // target IP address
+ if (ntohs(arphdr->op) != ARP_OP_REQUEST || tip != local_ip)
+ goto done;
+
+ // handle the ARP request
+ memmove(smac, arphdr->sha, ETHADDR_LEN); // sender's ethernet address
+ sip = ntohl(arphdr->sip); // sender's IP address (qemu's slirp)
+ net_tx_arp(ARP_OP_REPLY, smac, sip);
+
+done:
+ mbuffree(m);
+}
+
+// receives a UDP packet
+static void
+net_rx_udp(struct mbuf *m, uint16 len, struct ip *iphdr)
+{
+ struct udp *udphdr;
+ uint32 sip;
+ uint16 sport, dport;
+
+
+ udphdr = mbufpullhdr(m, *udphdr);
+ if (!udphdr)
+ goto fail;
+
+ // TODO: validate UDP checksum
+
+ // validate lengths reported in headers
+ if (ntohs(udphdr->ulen) != len)
+ goto fail;
+ len -= sizeof(*udphdr);
+ if (len > m->len)
+ goto fail;
+ // minimum packet size could be larger than the payload
+ mbuftrim(m, m->len - len);
+
+ // parse the necessary fields
+ sip = ntohl(iphdr->ip_src);
+ sport = ntohs(udphdr->sport);
+ dport = ntohs(udphdr->dport);
+ sockrecvudp(m, sip, dport, sport);
+ return;
+
+fail:
+ mbuffree(m);
+}
+
+// receives an IP packet
+static void
+net_rx_ip(struct mbuf *m)
+{
+ struct ip *iphdr;
+ uint16 len;
+
+ iphdr = mbufpullhdr(m, *iphdr);
+ if (!iphdr)
+ goto fail;
+
+ // check IP version and header len
+ if (iphdr->ip_vhl != ((4 << 4) | (20 >> 2)))
+ goto fail;
+ // validate IP checksum
+ if (in_cksum((unsigned char *)iphdr, sizeof(*iphdr)))
+ goto fail;
+ // can't support fragmented IP packets
+ if (htons(iphdr->ip_off) != 0)
+ goto fail;
+ // is the packet addressed to us?
+ if (htonl(iphdr->ip_dst) != local_ip)
+ goto fail;
+ // can only support UDP
+ if (iphdr->ip_p != IPPROTO_UDP)
+ goto fail;
+
+ len = ntohs(iphdr->ip_len) - sizeof(*iphdr);
+ net_rx_udp(m, len, iphdr);
+ return;
+
+fail:
+ mbuffree(m);
+}
+
+// called by e1000 driver's interrupt handler to deliver a packet to the
+// networking stack
+void net_rx(struct mbuf *m)
+{
+ struct eth *ethhdr;
+ uint16 type;
+
+ ethhdr = mbufpullhdr(m, *ethhdr);
+ if (!ethhdr) {
+ mbuffree(m);
+ return;
+ }
+
+ type = ntohs(ethhdr->type);
+ if (type == ETHTYPE_IP)
+ net_rx_ip(m);
+ else if (type == ETHTYPE_ARP)
+ net_rx_arp(m);
+ else
+ mbuffree(m);
+}