Add UDP reception support

This commit is contained in:
Mark
2019-11-02 22:48:38 +02:00
parent 6082e68bc0
commit 05f2838df4
11 changed files with 413 additions and 47 deletions
+8 -1
View File
@@ -89,9 +89,16 @@ QEMU_DEV_HDA=-drive file=$(QEMU_HDA),format=raw,id=disk_hda
QEMU_DEVS+=$(QEMU_DEV_HDA)
endif
ifdef QEMU_NET_TAP
QEMU_NETDEV=-netdev type=tap,id=net0
else
QEMU_NETDEV=-netdev type=user,hostfwd=tcp::5555-:22,hostfwd=udp::5555-:22,id=net0
endif
ifdef QEMU_NET
QEMU_DEV_NET=-device $(QEMU_NET),netdev=net0,mac=11:22:33:44:55:66 \
-netdev type=user,hostfwd=tcp::5555-:22,hostfwd=udp::5555-:22,id=net0
$(QEMU_NETDEV) \
-object filter-dump,id=f1,netdev=net0,file=eth_dump.dat
QEMU_DEVS+=$(QEMU_DEV_NET)
endif
+3 -1
View File
@@ -61,7 +61,9 @@ OBJS+=$(O)/sys/debug.o \
$(O)/sys/vfs/tar.o \
$(O)/sys/blk/ram.o \
$(O)/sys/ring.o \
$(O)/sys/net/eth.o
$(O)/sys/net/eth.o \
$(O)/sys/net/arp.o \
$(O)/sys/net/in.o
DIRS+=$(O)/sys/vfs/ext2 \
$(O)/sys/net \
$(O)/sys/blk
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include "sys/net/netdev.h"
struct arp_frame {
uint16_t htype;
uint16_t ptype;
uint8_t hlen;
uint8_t plen;
uint16_t oper;
uint8_t sha[6];
uint32_t spa;
uint8_t tha[6];
uint32_t tpa;
} __attribute__((packed));
struct arp_route {
struct netdev *dev;
uint32_t addr_in;
uint8_t hwaddr[6];
struct arp_route *next;
};
struct arp_route *arp_find_route_in(struct netdev *dev, uint32_t inaddr);
void arp_handle_frame(struct netdev *dev, struct arp_frame *arp_frame);
+15 -1
View File
@@ -1,5 +1,9 @@
#pragma once
#include "sys/types.h"
#include "sys/net/netdev.h"
#define ETHERTYPE_ARP 0x0806
#define ETHERTYPE_IPV4 0x0800
struct eth_frame {
uint8_t dst_mac[6];
@@ -8,5 +12,15 @@ struct eth_frame {
char payload[];
} __attribute__((packed));
// Packet queue entry
struct ethq {
struct ethq *next;
struct netdev *dev;
char data[];
};
// Ethernet queue stuff
void ethq_handle(void);
// TODO: replace (void *) with (struct netdev *)
void eth_handle_frame(void *dev, struct eth_frame *frame, size_t packet_size);
void eth_handle_frame(struct netdev *dev, struct eth_frame *frame, size_t packet_size);
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include "sys/net/netdev.h"
#define INADDR_FMT "%u.%u.%u.%u"
#define INADDR_VA(x) ((x) & 0xFF), (((x) >> 8) & 0xFF), \
(((x) >> 16) & 0xFF), (((x) >> 24) & 0xFF)
#define IN_PROTO_UDP 0x11
struct in_frame {
uint8_t version;
uint8_t dscp;
uint16_t length;
uint16_t id;
uint16_t flag_frag_offset;
uint8_t ttl;
uint8_t protocol;
uint16_t header_chksum;
uint32_t src_inaddr;
uint32_t dst_inaddr;
char payload[];
} __attribute__((packed));
void in_handle_frame(struct netdev *dev, struct in_frame *frame);
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include "sys/types.h"
struct netdev;
typedef int (*netdev_tx_func_t) (struct netdev *, const void *, size_t);
struct netdev {
uint8_t hwaddr[6];
char name[32];
netdev_tx_func_t tx;
};
+115 -9
View File
@@ -3,8 +3,10 @@
#include "sys/amd64/hw/irq.h"
#include "sys/amd64/hw/io.h"
#include "sys/net/eth.h"
#include "sys/net/netdev.h"
#include "sys/net/net.h"
#include "sys/assert.h"
#include "sys/string.h"
#include "sys/debug.h"
#include "sys/heap.h"
#include "sys/attr.h"
@@ -30,6 +32,8 @@
#define RTL8139_CONFIG1 0x52
#define RTL8139_MSR 0x58
#define RTL8139_TSD_OWN (1 << 13)
#define RTL8139_CR_RST (1 << 4)
#define RTL8139_CR_RE (1 << 3)
#define RTL8139_CR_TE (1 << 2)
@@ -52,13 +56,23 @@
#define RTL8139_IMR_ROK (1 << 0)
struct rtl8139 {
struct netdev net;
uint32_t bar0;
// Receive buffer: 3 pages
uint32_t page0;
uint32_t tx_pages[4];
size_t tx_sizes[4];
// Bits:
// 0..3: TX_BUSY Set by _tx(), cleared by _handle_tx(), means that
// the descriptor cannot be allocated
uint32_t tx_flag;
uint32_t cbr_prev;
uint32_t tx_number;
};
static void rtl8139_handle_recv(struct rtl8139 *rtl);
static void rtl8139_handle_rx(struct rtl8139 *rtl);
static void rtl8139_handle_tx(struct rtl8139 *rtl, int succ);
static uint32_t rtl8139_irq(void *ctx) {
struct rtl8139 *rtl = ctx;
@@ -67,7 +81,10 @@ static uint32_t rtl8139_irq(void *ctx) {
if (isr) {
if (isr & RTL8139_ISR_ROK) {
// Got a packet
rtl8139_handle_recv(rtl);
rtl8139_handle_rx(rtl);
}
if (isr & (RTL8139_ISR_TOK | RTL8139_ISR_TER)) {
rtl8139_handle_tx(rtl, isr & RTL8139_ISR_TOK);
}
// Implementation of RTL8139 in qemu differs
@@ -81,7 +98,7 @@ static uint32_t rtl8139_irq(void *ctx) {
return IRQ_UNHANDLED;
}
static void rtl8139_handle_recv(struct rtl8139 *rtl) {
static void rtl8139_handle_rx(struct rtl8139 *rtl) {
kdebug("Received a packet\n");
uint16_t cbr = inw(rtl->bar0 + RTL8139_CBR);
size_t packet_size;
@@ -103,7 +120,81 @@ static void rtl8139_handle_recv(struct rtl8139 *rtl) {
uint16_t recv_status = ((uint16_t *) pack)[0];
uint16_t recv_length = ((uint16_t *) pack)[1];
eth_handle_frame(rtl, eth_frame, packet_size);
kdebug("Recv status: %u\n", recv_status);
//kdebug("Recv length = %u\n", recv_length);
//kdebug("Packet size = %u\n", packet_size);
//_assert(recv_length == (packet_size - 4));
eth_handle_frame(&rtl->net, eth_frame, recv_length);
}
static void rtl8139_cmd_tx(struct rtl8139 *rtl) {
kdebug("HW TX %d\n", rtl->tx_number);
uint8_t n = rtl->tx_number;
++rtl->tx_number;
if (rtl->tx_number == 4) {
rtl->tx_number = 0;
}
outl(rtl->bar0 + RTL8139_TSD(n), rtl->tx_sizes[n]);
}
static void rtl8139_handle_tx(struct rtl8139 *rtl, int s) {
uint8_t prev = 3;
if (rtl->tx_number != 0) {
prev = rtl->tx_number - 1;
}
kdebug("%s Tx on %d\n", s ? "Successful" : "Failed", prev);
kdebug("Clearing TxD%d\n", prev);
rtl->tx_flag &= ~(1 << prev);
rtl->tx_sizes[prev] = 0;
kdebug("TX Done!\n");
// If after transmission we reached a new entry and it's
// awaiting transmission - emit a Tx command
while (rtl->tx_flag & (1 << rtl->tx_number)) {
rtl8139_cmd_tx(rtl);
}
}
static uint8_t rtl8139_alloc_tx(struct rtl8139 *rtl) {
for (uint8_t i = rtl->tx_number; i < 4; ++i) {
if (!(rtl->tx_flag & (1 << i))) {
return i;
}
}
for (uint8_t i = 0; i < rtl->tx_number; ++i) {
if (!(rtl->tx_flag & (1 << i))) {
return i;
}
}
return 0xFF;
}
static int rtl8139_tx(struct netdev *net, const void *packet, size_t size) {
kdebug("Requested Tx\n");
struct rtl8139 *rtl = (struct rtl8139 *) net;
uint8_t index = rtl8139_alloc_tx(rtl);
// TODO: transmit queue for this?
assert(index != 0xFF, "No free TSADx\n");
void *page = (void *) MM_VIRTUALIZE(rtl->tx_pages[index]);
memcpy(page, packet, size);
rtl->tx_sizes[index] = size;
if (index == rtl->tx_number) {
// Can tx right now
rtl8139_cmd_tx(rtl);
}
return 0;
}
void pci_rtl8139_init(pci_addr_t addr) {
@@ -138,6 +229,16 @@ void pci_rtl8139_init(pci_addr_t addr) {
assert(rtl->page0 != MM_NADDR, "Failed to allocate RxBuf\n");
outl(rtl->bar0 + RTL8139_RBSTART, rtl->page0);
// Initialize tx buffers
for (size_t i = 0; i < 4; ++i) {
rtl->tx_pages[i] = amd64_phys_alloc_page();
rtl->tx_sizes[i] = 0;
assert(rtl->tx_pages[i] != MM_NADDR, "Failed to allocate TxBuf\n");
outl(rtl->bar0 + RTL8139_TSAD(i), rtl->tx_pages[i]);
}
rtl->tx_flag = 0;
rtl->tx_number = 0;
// Accept all
outl(rtl->bar0 + RTL8139_RCR, RTL8139_RCR_APM | RTL8139_RCR_AB | RTL8139_RCR_WRAP);
@@ -151,16 +252,21 @@ void pci_rtl8139_init(pci_addr_t addr) {
}
// Configure interrupt mask
outw(rtl->bar0 + RTL8139_IMR, RTL8139_IMR_ROK | RTL8139_IMR_RER);
outw(rtl->bar0 + RTL8139_IMR, RTL8139_IMR_ROK | RTL8139_IMR_RER |
RTL8139_IMR_TOK | RTL8139_IMR_TER);
// Clear ISR
outw(rtl->bar0 + RTL8139_ISR, 0);
// Dump contents of MAC register
uint8_t mac[6];
// After the reset, controller uses Tx pair 0
rtl->tx_number = 0;
// Setup a system network device
for (size_t i = 0; i < 6; ++i) {
mac[i] = inb(rtl->bar0 + RTL8139_IDR0 + i);
rtl->net.hwaddr[i] = inb(rtl->bar0 + RTL8139_IDR0 + i);
}
kdebug("RTL8139 MAC: " MAC_FMT "\n", MAC_VA(mac));
kdebug("RTL8139 MAC: " MAC_FMT "\n", MAC_VA(rtl->net.hwaddr));
rtl->net.tx = rtl8139_tx;
// TODO: call some kind of netdev_add to bind a name to the device
}
static __init void pci_rtl8139_register(void) {
+5
View File
@@ -1,6 +1,7 @@
#include "sys/amd64/mm/mm.h"
#include "sys/amd64/mm/pool.h"
#include "sys/amd64/cpu.h"
#include "sys/net/eth.h"
#include "sys/thread.h"
#include "sys/debug.h"
#include "sys/heap.h"
@@ -126,6 +127,10 @@ void init_func(void *arg) {
while (1) {
asm volatile ("sti; hlt");
// I've decided not to create a separate thread for network handling yet,
// so the code will be here
ethq_handle();
}
}
+88
View File
@@ -0,0 +1,88 @@
#include "sys/net/arp.h"
#include "sys/net/eth.h"
#include "sys/net/net.h"
#include "sys/net/in.h"
#include "sys/string.h"
#include "sys/debug.h"
#include "sys/assert.h"
#include "sys/heap.h"
static struct arp_route *arp_routes_head = NULL;
static struct arp_route *arp_routes_tail = NULL;
void arp_add_route_in(struct netdev *dev, uint32_t inaddr, uint8_t *hwaddr) {
struct arp_route *rt = (struct arp_route *) kmalloc(sizeof(struct arp_route));
_assert(rt);
kdebug("Added ARP route: " INADDR_FMT " is " MAC_FMT "\n", INADDR_VA(inaddr), MAC_VA(hwaddr));
memcpy(rt->hwaddr, hwaddr, 6);
rt->dev = dev;
rt->addr_in = inaddr;
rt->next = NULL;
if (arp_routes_tail) {
arp_routes_tail->next = rt;
} else {
arp_routes_head = rt;
}
arp_routes_tail = rt;
}
struct arp_route *arp_find_route_in(struct netdev *dev, uint32_t inaddr) {
for (struct arp_route *rt = arp_routes_head; rt; rt = rt->next) {
if (dev == rt->dev && rt->addr_in == inaddr) {
return rt;
}
}
return NULL;
}
void arp_handle_frame(struct netdev *dev, struct arp_frame *arp_frame) {
uint16_t oper = htons(arp_frame->oper);
kdebug("ARP frame:\n");
kdebug("\tOperation: %s\n", (oper == 1) ? "Request" : "Reply");
kdebug("\tSHA: " MAC_FMT "\n", MAC_VA(arp_frame->sha));
kdebug("\tTHA: " MAC_FMT "\n", MAC_VA(arp_frame->tha));
kdebug("\tSPA: " INADDR_FMT "\n", INADDR_VA(arp_frame->spa));
kdebug("\tTPA: " INADDR_FMT "\n", INADDR_VA(arp_frame->tpa));
struct arp_route *rt;
if ((rt = arp_find_route_in(dev, arp_frame->spa))) {
memcpy(rt->hwaddr, arp_frame->sha, 6);
}
// TODO: check that TPA actually belongs to a netdev
if (arp_frame->tpa == 0x0F02000A /* 10.0.2.15 */) {
if (!rt) {
arp_add_route_in(dev, arp_frame->spa, arp_frame->sha);
}
if (oper == 1) {
// Someone requested a MAC for IPv4
char packet[sizeof(struct eth_frame) + sizeof(struct arp_frame)] = {0};
struct eth_frame *r_eth_frame = (struct eth_frame *) packet;
struct arp_frame *r_arp_frame = (struct arp_frame *) r_eth_frame->payload;
// ETH
r_eth_frame->ethertype = htons(ETHERTYPE_ARP);
memcpy(r_eth_frame->dst_mac, arp_frame->sha, 6);
memcpy(r_eth_frame->src_mac, dev->hwaddr, 6);
// ARP
r_arp_frame->htype = arp_frame->htype;
r_arp_frame->ptype = arp_frame->ptype;
r_arp_frame->hlen = arp_frame->hlen;
r_arp_frame->plen = arp_frame->plen;
r_arp_frame->oper = htons(2);
memcpy(r_arp_frame->sha, dev->hwaddr, 6);
r_arp_frame->spa = arp_frame->tpa;
memcpy(r_arp_frame->tha, arp_frame->sha, 6);
r_arp_frame->tpa = arp_frame->spa;
if (dev->tx) {
dev->tx(dev, packet, sizeof(struct eth_frame) + sizeof(struct arp_frame));
}
}
}
}
+63 -35
View File
@@ -1,52 +1,80 @@
#include "sys/net/eth.h"
#include "sys/net/net.h"
#include "sys/string.h"
#include "sys/debug.h"
#include "sys/heap.h"
#include "sys/spin.h"
#define ETHERTYPE_ARP 0x0806
#define ETHERTYPE_IPV4 0x0800
#include "sys/net/arp.h"
#include "sys/net/in.h"
#define INADDR_FMT "%u.%u.%u.%u"
#define INADDR_VA(x) ((x) & 0xFF), (((x) >> 8) & 0xFF), \
(((x) >> 16) & 0xFF), (((x) >> 24) & 0xFF)
static spin_t ethq_lock = 0;
static int ethq_count = 0;
static struct ethq *ethq_head = NULL, *ethq_tail = NULL;
struct arp_frame {
uint16_t htype;
uint16_t ptype;
uint8_t hlen;
uint8_t plen;
uint16_t oper;
uint8_t sha[6];
uint32_t spa;
uint8_t tha[6];
uint32_t tpa;
} __attribute__((packed));
// Just for testing
static void arp_handle_frame(void *dev, struct arp_frame *arp_frame) {
kdebug("ARP frame:\n");
kdebug("\tOperation: %s\n", (htons(arp_frame->oper) == 1) ? "Request" : "Reply");
kdebug("\tSHA: " MAC_FMT "\n", MAC_VA(arp_frame->sha));
kdebug("\tTHA: " MAC_FMT "\n", MAC_VA(arp_frame->tha));
kdebug("\tSPA: " INADDR_FMT "\n", INADDR_VA(arp_frame->spa));
kdebug("\tTPA: " INADDR_FMT "\n", INADDR_VA(arp_frame->tpa));
static struct ethq *ethq_pop(void) {
if (ethq_head) {
struct ethq *head = ethq_head;
if (!head->next) {
ethq_tail = NULL;
}
ethq_head = head->next;
return head;
}
return NULL;
}
void eth_handle_frame(void *dev, struct eth_frame *eth_frame, size_t packet_size) {
debug_dump(DEBUG_DEFAULT, eth_frame, packet_size - 4);
static void ethq_add(struct netdev *dev, struct eth_frame *eth_frame, size_t packet_size) {
struct ethq *ent = (struct ethq *) kmalloc(packet_size + sizeof(struct ethq));
ent->dev = dev;
ent->next = NULL;
memcpy(ent->data, eth_frame, packet_size);
if (eth_frame->ethertype >= 1536) {
kdebug("DST: " MAC_FMT ", SRC: " MAC_FMT "\n",
MAC_VA(eth_frame->dst_mac),
MAC_VA(eth_frame->src_mac));
spin_lock(&ethq_lock);
if (ethq_tail) {
ethq_tail->next = ent;
} else {
ethq_head = ent;
}
ethq_tail = ent;
++ethq_count;
spin_release(&ethq_lock);
kdebug("Added\n");
}
// The function takes a single packet from the queue
// and processes it
void ethq_handle(void) {
spin_lock(&ethq_lock);
struct ethq *ethq = ethq_pop();
spin_release(&ethq_lock);
if (ethq) {
struct eth_frame *eth_frame = (struct eth_frame *) ethq->data;
// Ethernet II frame
switch (eth_frame->ethertype) {
case htons(ETHERTYPE_ARP):
arp_handle_frame(dev, (struct arp_frame *) eth_frame->payload);
arp_handle_frame(ethq->dev, (struct arp_frame *) eth_frame->payload);
break;
case htons(ETHERTYPE_IPV4):
kdebug("IPv4 frame\n");
case htons(ETHERTYPE_IPV4):
in_handle_frame(ethq->dev, (struct in_frame *) eth_frame->payload);
break;
default:
kdebug("Unsupported frame type: %04x\n", htons(eth_frame->ethertype));
break;
}
kfree(ethq);
}
}
// Called from interrupt handler
void eth_handle_frame(struct netdev *dev, struct eth_frame *eth_frame, size_t packet_size) {
debug_dump(DEBUG_DEFAULT, eth_frame, packet_size);
if (htons(eth_frame->ethertype) >= 1536) {
ethq_add(dev, eth_frame, packet_size);
}
// Ignore all other packets
}
+56
View File
@@ -0,0 +1,56 @@
#include "sys/net/in.h"
#include "sys/net/net.h"
#include "sys/net/arp.h"
#include "sys/assert.h"
#include "sys/debug.h"
struct udp_frame {
uint16_t src_port;
uint16_t dst_port;
uint16_t length;
uint16_t checksum;
char payload[];
};
static void udp_handle_frame(struct netdev *dev, int in6, void *frame, struct udp_frame *udp) {
// in6 is required to be 0 now, I haven't yet implemented IPv6 support
_assert(!in6);
struct in_frame *in_frame = frame;
// Ignore UDP checksums
kdebug("UDP packet\n");
kdebug("\tsrcport: %u\n", htons(udp->src_port));
kdebug("\tdstport: %u\n", htons(udp->dst_port));
kdebug("\tLength: %u\n", htons(udp->length));
debug_dump(DEBUG_DEFAULT, udp->payload, htons(udp->length) - 8);
}
void in_handle_frame(struct netdev *dev, struct in_frame *frame) {
kdebug("INET frame\n");
kdebug("\tTotal length: %u\n", htons(frame->length));
kdebug("\tsrcaddr: " INADDR_FMT "\n", INADDR_VA(frame->src_inaddr));
kdebug("\tdstaddr: " INADDR_FMT "\n", INADDR_VA(frame->dst_inaddr));
uint16_t checksum = 0;
for (size_t i = 0; i < sizeof(struct in_frame) / 2; ++i) {
checksum += ((uint16_t *) frame)[i];
}
kdebug("IP checksum = %04x\n", checksum);
//if (checksum != 0xFFFF) {
// kwarn("Packet IP checksum mismatch\n");
// // Drop packet
// return;
//}
switch (frame->protocol) {
case IN_PROTO_UDP:
udp_handle_frame(dev, 0, frame, (struct udp_frame *) frame->payload);
break;
default:
kwarn("Unhandled IP protocol: %02x\n", frame->protocol);
break;
}
}