diff --git a/include/net/arp.h b/include/net/arp.h index 92d2e44..df42ad9 100644 --- a/include/net/arp.h +++ b/include/net/arp.h @@ -23,4 +23,5 @@ struct arp_frame { struct packet; void arp_handle_frame(struct packet *packet, void *data, size_t len); +int arp_send(struct netdev *dev, uint32_t inaddr, uint16_t etype, void *data, size_t len); const uint8_t *arp_resolve(struct netdev *dev, uint32_t inaddr); diff --git a/include/net/eth.h b/include/net/eth.h index dbbcf24..d6da2aa 100644 --- a/include/net/eth.h +++ b/include/net/eth.h @@ -11,6 +11,8 @@ struct eth_frame { uint16_t ethertype; } __attribute__((packed)); +static const uint8_t ETH_A_BROADCAST[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + struct netdev; struct packet; void eth_handle_frame(struct packet *p); diff --git a/net/arp.c b/net/arp.c index 10954a7..5dad8ca 100644 --- a/net/arp.c +++ b/net/arp.c @@ -1,3 +1,4 @@ +#include "arch/amd64/mm/phys.h" #include "net/packet.h" #include "sys/assert.h" #include "sys/string.h" @@ -7,14 +8,27 @@ #include "net/arp.h" #include "net/eth.h" #include "net/if.h" +#include "sys/mm.h" + +struct arp_hold; struct arp_ent { uint32_t inaddr; uint8_t hwaddr[6]; int resolved; + struct arp_hold *hold; struct arp_ent *prev, *next; }; +struct arp_hold { + uint16_t ethertype; + size_t len; + struct arp_hold *next; + char data[4096 - sizeof(uint16_t) - sizeof(struct arp_hold *) - sizeof(size_t)]; +} __attribute__((packed)); + +static void arp_send_request(struct netdev *dev, uint32_t inaddr); + static struct arp_ent *arp_find_inaddr(struct netdev *dev, uint32_t inaddr) { for (struct arp_ent *ent = dev->arp_ent_head; ent; ent = ent->next) { if (ent->inaddr == inaddr) { @@ -24,29 +38,106 @@ static struct arp_ent *arp_find_inaddr(struct netdev *dev, uint32_t inaddr) { return NULL; } -static void arp_add_entry(struct netdev *dev, uint32_t inaddr, const uint8_t *hw) { +static struct arp_ent *arp_add_entry(struct netdev *dev, uint32_t inaddr, const uint8_t *hw) { struct arp_ent *ent = kmalloc(sizeof(struct arp_ent)); _assert(ent); ent->inaddr = inaddr; - memcpy(ent->hwaddr, hw, 6); - ent->resolved = 1; + if (hw) { + char mac[24]; + strmac(mac, hw); + kdebug("%s: add arp entry: " FMT_INADDR " -> %s\n", dev->name, VA_INADDR(inaddr), mac); + memcpy(ent->hwaddr, hw, 6); + ent->resolved = 1; + } else { + ent->resolved = 0; + kdebug("%s: pending resolution: " FMT_INADDR "\n", dev->name, VA_INADDR(inaddr)); + } + + ent->hold = NULL; ent->next = dev->arp_ent_head; ent->prev = NULL; dev->arp_ent_head = ent; - char mac[24]; - strmac(mac, hw); - kdebug("%s: add arp entry: " FMT_INADDR " -> %s\n", dev->name, VA_INADDR(inaddr), mac); + return ent; } -const uint8_t *arp_resolve(struct netdev *dev, uint32_t inaddr) { - struct arp_ent *ent = arp_find_inaddr(dev, inaddr); - if (!ent || !ent->resolved) { - return NULL; +static struct arp_ent *arp_find_or_create(struct netdev *dev, uint32_t inaddr) { + struct arp_ent *ent; + if ((ent = arp_find_inaddr(dev, inaddr)) != NULL) { + return ent; } - return ent->hwaddr; + + // Send a request for inaddr + arp_send_request(dev, inaddr); + + return arp_add_entry(dev, inaddr, NULL); +} + +static void arp_send_reply(struct netdev *src, struct arp_frame *req) { + char reply[sizeof(struct eth_frame) + sizeof(struct arp_frame)]; + struct arp_frame *arp = (struct arp_frame *) &reply[sizeof(struct eth_frame)]; + + memcpy(arp->tha, req->sha, 6); + memcpy(arp->sha, src->hwaddr, 6); + + arp->spa = req->tpa; + arp->tpa = req->spa; + + arp->hlen = req->hlen; + arp->plen = req->plen; + arp->htype = req->htype; + arp->ptype = req->ptype; + + arp->oper = htons(ARP_OP_REPLY); + + eth_send_wrapped(src, arp->tha, ETH_T_ARP, reply, sizeof(reply)); +} + +int arp_send(struct netdev *src, uint32_t inaddr, uint16_t ethertype, void *data, size_t len) { + struct arp_ent *ent = arp_find_or_create(src, inaddr); + _assert(ent); + + if (ent->resolved) { + return eth_send_wrapped(src, ent->hwaddr, ethertype, data, len); + } else { + // TODO: better way? + uintptr_t page = amd64_phys_alloc_page(); + _assert(page != MM_NADDR); + struct arp_hold *hold = (struct arp_hold *) MM_VIRTUALIZE(page); + + hold->ethertype = ethertype; + hold->len = len; + memcpy(hold->data, data, len); + + hold->next = ent->hold; + ent->hold = hold; + + return 0; + } +} + +static void arp_send_request(struct netdev *dev, uint32_t inaddr) { + _assert(dev->flags & IF_F_HASIP); + char packet[sizeof(struct eth_frame) + sizeof(struct arp_frame)]; + struct arp_frame *arp = (struct arp_frame *) &packet[sizeof(struct eth_frame)]; + + memcpy(arp->sha, dev->hwaddr, 6); + memset(arp->tha, 0, 6); + + arp->spa = htonl(dev->inaddr); + arp->tpa = htonl(inaddr); + + arp->hlen = 6; + arp->plen = 4; + arp->htype = htons(ARP_H_ETH); + arp->ptype = htons(ARP_P_IP); + + arp->oper = htons(ARP_OP_REQUEST); + + kinfo("Send request for " FMT_INADDR "\n", VA_INADDR(inaddr)); + eth_send_wrapped(dev, ETH_A_BROADCAST, ETH_T_ARP, packet, sizeof(packet)); } /* @@ -75,26 +166,6 @@ Yes: (almost definitely) the same hardware on which the request was received. */ -static void arp_send_reply(struct netdev *src, struct arp_frame *req) { - char reply[sizeof(struct eth_frame) + sizeof(struct arp_frame)]; - struct arp_frame *arp = (struct arp_frame *) &reply[sizeof(struct eth_frame)]; - - memcpy(arp->tha, req->sha, 6); - memcpy(arp->sha, src->hwaddr, 6); - - arp->spa = req->tpa; - arp->tpa = req->spa; - - arp->hlen = req->hlen; - arp->plen = req->plen; - arp->htype = req->htype; - arp->ptype = req->ptype; - - arp->oper = htons(ARP_OP_REPLY); - - eth_send_wrapped(src, arp->tha, ETH_T_ARP, reply, sizeof(reply)); -} - void arp_handle_frame(struct packet *p, void *data, size_t len) { if (len < sizeof(struct arp_frame)) { kwarn("%s: dropping undersized frame: %u\n", p->dev->name, len); @@ -117,7 +188,20 @@ void arp_handle_frame(struct packet *p, void *data, size_t len) { if ((ent = arp_find_inaddr(dev, ntohl(arp->spa))) != NULL) { merge = 1; - kinfo("Found source addr\n"); + + memcpy(ent->hwaddr, arp->sha, 6); + + if (!ent->resolved) { + ent->resolved = 1; + struct arp_hold *hold; + while ((hold = ent->hold)) { + ent->hold = hold->next; + + eth_send_wrapped(dev, ent->hwaddr, hold->ethertype, hold->data, hold->len); + + amd64_phys_free(MM_PHYS(hold)); + } + } } if ((dev->flags & IF_F_HASIP) && diff --git a/net/inet.c b/net/inet.c index d4b1a42..cfb871b 100644 --- a/net/inet.c +++ b/net/inet.c @@ -29,10 +29,6 @@ int inet_send_wrapped(struct netdev *src, uint32_t inaddr, uint8_t proto, void * } struct inet_frame *ip = data + sizeof(struct eth_frame); - const uint8_t *hwaddr = arp_resolve(src, inaddr); - if (!hwaddr) { - panic("TODO: handle this\n"); - } ip->dst_inaddr = htonl(inaddr); ip->src_inaddr = htonl(src->inaddr); @@ -47,7 +43,7 @@ int inet_send_wrapped(struct netdev *src, uint32_t inaddr, uint8_t proto, void * ip->checksum = inet_checksum(ip, sizeof(struct inet_frame)); - return eth_send_wrapped(src, hwaddr, ETH_T_IP, data, len); + return arp_send(src, inaddr, ETH_T_IP, data, len); } void inet_handle_frame(struct packet *p, struct eth_frame *eth, void *data, size_t len) {