diff --git a/kernel-src/include/kernel/net.h b/kernel-src/include/kernel/net.h index 72e33c67..6d3c560d 100644 --- a/kernel-src/include/kernel/net.h +++ b/kernel-src/include/kernel/net.h @@ -22,6 +22,7 @@ typedef struct { typedef struct netdev_t { mac_t mac; size_t mtu; + uint32_t ip; hashtable_t arpcache; int (*allocdesc)(size_t requestedsize, netdesc_t *desc); int (*sendpacket)(struct netdev_t *_internal, netdesc_t desc, mac_t targetmac, int proto); @@ -57,13 +58,14 @@ typedef struct { #define MAC_EQUAL(m1,m2) (memcmp(m1, m2, sizeof(mac_t)) == 0) #define IPV4_PROTO_UDP 0x11 +#define IPV4_BROADCAST_ADDRESS 0xffffffff void arp_init(); +void ipv4_init(); void udp_init(); void arp_process(netdev_t *netdev, void *buffer); -int udp_allocdesc(netdev_t *netdev, size_t requestedsize, netdesc_t *desc); +int arp_lookup(netdev_t *netdev, uint32_t ip, mac_t *mac); int udp_sendpacket(netdev_t *netdev, netdesc_t desc, uint32_t ip, uint16_t srcport, uint16_t dstport); -int ipv4_allocdesc(netdev_t *netdev, size_t requestedsize, netdesc_t *desc); -int ipv4_sendpacket(netdev_t *netdev, netdesc_t desc, uint32_t ip, int proto); +int ipv4_sendpacket(void *buffer, size_t packetsize, uint32_t ip, int proto, netdev_t *broadcastdev); #endif diff --git a/kernel-src/io/net/ipv4.c b/kernel-src/io/net/ipv4.c index bababf90..70d30c82 100644 --- a/kernel-src/io/net/ipv4.c +++ b/kernel-src/io/net/ipv4.c @@ -2,11 +2,76 @@ #include #include #include +#include +#include -int ipv4_allocdesc(netdev_t *netdev, size_t requestedsize, netdesc_t *desc) { - int e = netdev->allocdesc(requestedsize + sizeof(ipv4frame_t), desc); - desc->curroffset += sizeof(ipv4frame_t); - return e; +typedef struct { + netdev_t *netdev; + uint32_t addr; + uint32_t gateway; + uint32_t mask; + int weight; +} routingentry_t; + +static routingentry_t *routingtable; +static mutex_t routingtablelock; +static size_t routingtablesize; + +static routingentry_t getroute(uint32_t ip) { + routingentry_t selected = {0}; + + MUTEX_ACQUIRE(&routingtablelock, false); + + for (int i = 0; i < routingtablesize; ++i) { + if (routingtable[i].netdev == NULL || (routingtable[i].mask & ip) != (routingtable[i].addr & routingtable[i].mask)) + continue; + + selected = routingtable[i].weight > selected.weight ? routingtable[i] : selected; + } + + MUTEX_RELEASE(&routingtablelock); + + return selected; +} + +int ipv4_addroute(netdev_t *netdev, uint32_t addr, uint32_t gateway, uint32_t mask, int weight) { + routingentry_t entry = { + .netdev = netdev, + .addr = addr, + .gateway = gateway, + .mask = mask, + .weight = weight + }; + + MUTEX_ACQUIRE(&routingtablelock, false); + + int error = 0; + int i; + + for (i = 0; i < routingtablesize; ++i) { + if (routingtable[i].netdev != NULL) + continue; + + routingtable[i] = entry; + break; + } + + if (i == routingtablesize) { + // insert to the end + size_t newsize = routingtablesize + 1; + void *tmp = realloc(routingtable, newsize * sizeof(routingentry_t)); + if (tmp) { + routingtable = tmp; + routingtablesize = newsize; + routingtable[i] = entry; + } else { + error = ENOMEM; + } + } + + MUTEX_RELEASE(&routingtablelock); + + return error; } #define VERSION_LENGTH(v, l) (((v) << 4) | l) @@ -15,43 +80,73 @@ int ipv4_allocdesc(netdev_t *netdev, size_t requestedsize, netdesc_t *desc) { // offset has to be a multiple of 8 #define FLAGS_FRAGOFFSET(flg, offset) (((flg) << 13) | ((offset) >> 3)) -static int dispatch_fragment(netdev_t *netdev, netdesc_t originaldesc, netdesc_t fragdesc, - uintmax_t fragmentoffset, size_t fragmentlen, int id, uint32_t ip, int proto) { +static int dispatch_fragment(netdev_t *netdev, netdesc_t fragdesc, + uintmax_t fragmentoffset, size_t fragmentlen, int id, uint32_t ip, mac_t mac, int proto, bool last) { ipv4frame_t frame = { .version_length = VERSION_LENGTH(4, 5), .servicetype_ecn = 0, .packetlen = cpu_to_be_w(fragmentlen + sizeof(ipv4frame_t)), .id = cpu_to_be_w(0x1234), // TODO proper id allocation per netdev - .flags_fragoffset = cpu_to_be_w(FLAGS_FRAGOFFSET((fragmentoffset + fragmentlen < originaldesc.size - originaldesc.curroffset - sizeof(ipv4frame_t)) ? FLAG_MF : 0, fragmentoffset)), + .flags_fragoffset = cpu_to_be_w(FLAGS_FRAGOFFSET(!last ? FLAG_MF : 0, fragmentoffset)), .timetolive = 0xff, .protocol = proto, .framechecksum = 0xfefe, // TODO checksum - .srcaddr = 0xffffffff, // TODO src addr + .srcaddr = netdev->ip, // TODO src addr .dstaddr = cpu_to_be_d(ip) }; memcpy((void *)((uintptr_t)fragdesc.address + fragdesc.curroffset), &frame, sizeof(ipv4frame_t)); - memcpy((void *)((uintptr_t)fragdesc.address + fragdesc.curroffset + sizeof(ipv4frame_t)), originaldesc.address + originaldesc.curroffset + sizeof(ipv4frame_t) + fragmentoffset, fragmentlen); - return netdev->sendpacket(netdev, fragdesc, NET_BROADCAST_MAC, ETH_PROTO_IP); + return netdev->sendpacket(netdev, fragdesc, mac, ETH_PROTO_IP); } -int ipv4_sendpacket(netdev_t *netdev, netdesc_t desc, uint32_t ip, int proto) { - size_t datasize = desc.size - desc.curroffset; - desc.curroffset -= sizeof(ipv4frame_t); - __assert(desc.size - desc.curroffset <= 65535); - - size_t mtuheader = netdev->mtu - sizeof(ipv4frame_t); - size_t devfragmentsize = mtuheader - (mtuheader % 8); - size_t fragmentcount = datasize / devfragmentsize + 1; +int ipv4_sendpacket(void *buffer, size_t packetsize, uint32_t ip, int proto, netdev_t *broadcastdev) { + if ((packetsize + sizeof(ipv4frame_t)) > 65535) + return EMSGSIZE; - for (int i = 0; i < fragmentcount; ++i) { - size_t fragmentlen = (i == fragmentcount - 1) ? datasize - devfragmentsize * (fragmentcount - 1) : devfragmentsize; - netdesc_t fragdesc; - __assert(ipv4_allocdesc(netdev, fragmentlen + sizeof(ipv4frame_t), &fragdesc) == 0); - fragdesc.curroffset -= sizeof(ipv4frame_t); - dispatch_fragment(netdev, desc, fragdesc, i * devfragmentsize, fragmentlen, 0x1234, ip, proto); - // TODO unallocate desc + mac_t mac; + netdev_t *netdev; + + if (ip != IPV4_BROADCAST_ADDRESS) { + routingentry_t entry = getroute(ip); + if (entry.netdev == NULL) + return 0; // if a packet can't be routed to anywhere just pretend it was sent somewhere + + int error = arp_lookup(entry.netdev, entry.gateway ? entry.gateway : ip, &mac); + if (error) + return error; + + netdev = entry.netdev; + } else { + __assert(broadcastdev); + mac = NET_BROADCAST_MAC; + netdev = broadcastdev; } - return 0; // TODO error handling + + size_t mtuheader = netdev->mtu - sizeof(ipv4frame_t); + size_t devfragmentsize = mtuheader - (mtuheader % 8); + size_t fragmentcount = packetsize / devfragmentsize + 1; + + for (int i = 0; i < fragmentcount; ++i) { + size_t fragmentlen = (i == fragmentcount - 1) ? packetsize - devfragmentsize * (fragmentcount - 1) : devfragmentsize; + netdesc_t fragdesc; + int e = netdev->allocdesc(fragmentlen + sizeof(ipv4frame_t), &fragdesc); + if (e) + return e; + + memcpy((void *)((uintptr_t)fragdesc.address + sizeof(ipv4frame_t)), (void *)((uintptr_t)buffer + i * devfragmentsize), fragmentlen); + e = dispatch_fragment(netdev, fragdesc, i * devfragmentsize, fragmentlen, 0x1234, ip, mac, proto, i == fragmentcount - 1); + // TODO unallocate desc + if (e) + return e; + } + + return 0; +} + +void ipv4_init() { + routingtablesize = 1; + MUTEX_INIT(&routingtablelock); + routingtable = alloc(sizeof(routingentry_t)); + __assert(routingtable); }