diff --git a/.gitignore b/.gitignore index a2ff54b6..ffb930b1 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,5 @@ tools/init/init tools/init/main.o tools/mount/mount tools/mount/main.o +tools/netd/main.o +tools/netd/netd diff --git a/Makefile b/Makefile index 27996c4d..613aa16d 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ full: cd sysroot; tar --format=ustar -cf $(INITRD) * minimal: - ./jinx install minimalsysroot mlibc bash coreutils init distro-files vim nano mount + ./jinx install minimalsysroot mlibc bash coreutils init distro-files vim nano mount netd cd minimalsysroot; tar --format=ustar -cf $(INITRD) * kernel: diff --git a/recipes/netd b/recipes/netd new file mode 100644 index 00000000..fa29a441 --- /dev/null +++ b/recipes/netd @@ -0,0 +1,18 @@ +name=netd +revision=1 +from_source=netd +imagedeps="base-devel" +hostdeps="xbinutils xgcc" +deps="mlibc" + +configure() { + true +} + +build() { + make -C ${source_dir} -j ${parallelism} +} + +install() { + make -C ${source_dir} install PREFIX=${prefix} DESTDIR=${dest_dir} +} diff --git a/source-recipes/netd b/source-recipes/netd new file mode 100644 index 00000000..6457ff75 --- /dev/null +++ b/source-recipes/netd @@ -0,0 +1,7 @@ +name=mount +version=0 +source_dir="tools/netd" + +regenerate() { + true +} diff --git a/tools/netd/Makefile b/tools/netd/Makefile new file mode 100644 index 00000000..d3d2061e --- /dev/null +++ b/tools/netd/Makefile @@ -0,0 +1,14 @@ +.phony: all install +CC=x86_64-astral-gcc +LD=x86_64-astral-gcc +all: netd + +install: + mkdir -p "$(DESTDIR)/$(PREFIX)/sbin/" + cp netd "$(DESTDIR)/$(PREFIX)/sbin/netd" + +netd: main.o + $(LD) $(LDFLAGS) -g -o $@ $^ + +main.o: main.c + $(CC) -c $(CFLAGS) -g -o $@ $< diff --git a/tools/netd/main.c b/tools/netd/main.c new file mode 100644 index 00000000..87d117b3 --- /dev/null +++ b/tools/netd/main.c @@ -0,0 +1,474 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef SIOCADDRT + #define SIOCADDRT 0x890B +#endif + +#define DHCPHDR_TYPE_CLIENT 1 +#define DHCPHDR_TYPE_SERVER 2 +#define DHCPHDR_HTYPE_ETH 1 +#define DHCPHDR_HLEN_ETH 6 +#define DHCPHDR_COOKIE 0x63825363 +#define DHCPHDR_XID 0x12345678 + +#define DHCP_CLIENT_PORT 68 +#define DHCP_SERVER_PORT 67 + +typedef struct { + uint8_t op; + uint8_t htype; + uint8_t hlen; + uint8_t hops; + uint32_t xid; + uint16_t secs; + uint16_t flags; + uint32_t clientip; + uint32_t responseip; + uint32_t serverip; + uint32_t relayip; + uint8_t clienthw[16]; + uint8_t sname[64]; + uint8_t file[128]; + uint32_t cookie; +} __attribute__((packed)) dhcphdr_t; + +#define OPTION_TYPE 53 +#define OPTION_TYPE_LENGTH 1 +#define OPTION_TYPE_DHCPDISCOVER 1 +#define OPTION_TYPE_DHCPOFFER 2 +#define OPTION_TYPE_DHCPREQUEST 3 +#define OPTION_TYPE_DHCPACK 5 + +#define OPTION_REQUEST 55 +#define OPTION_REQUEST_LENGTH 2 + +#define OPTION_MASK 1 +#define OPTION_ROUTER 3 +#define OPTION_LEASETIME 51 +#define OPTION_IPREQ 50 +#define OPTION_SERVER 54 +#define OPTION_END 0xff + +static uint8_t discoveropts[] = { + OPTION_TYPE, OPTION_TYPE_LENGTH, OPTION_TYPE_DHCPDISCOVER, + OPTION_REQUEST, OPTION_REQUEST_LENGTH, OPTION_MASK, OPTION_ROUTER, + 0xff +}; + +typedef struct { + dhcphdr_t hdr; + uint8_t opts[sizeof(discoveropts)]; +} __attribute__((packed)) dhcpdiscover_t; + +typedef struct { + dhcphdr_t hdr; + uint8_t opts[]; +} __attribute__((packed)) dhcpoffer_t; + +typedef struct { + uint8_t type[3]; + uint8_t ipreqhdr[2]; + uint32_t ipreq; + uint8_t dhcpserverhdr[2]; + uint32_t dhcpserver; + uint8_t end; +} __attribute__((packed)) requestopts_t; + +typedef struct { + dhcphdr_t hdr; + requestopts_t opts; +} __attribute__((packed)) dhcprequest_t; + +typedef struct { + dhcphdr_t hdr; + uint8_t opts[]; +} __attribute__((packed)) dhcpack_t; + +static char *name; +static int sockfd; +static uint8_t hwaddr[DHCPHDR_HLEN_ETH]; + +static void usage() { + fprintf(stderr, "%s: usage: %s device\n", name, name); + fprintf(stderr, "%s: example: %s vionet0\n", name, name); + exit(EXIT_FAILURE); +} + +static void logstrerror(char *msg) { + fprintf(stderr, "%s: %s: %s\n", name, msg, strerror(errno)); +} + +static void logmsg(char *msg) { + printf("%s: %s\n", name, msg); +} + +static void logerr(char *msg) { + fprintf(stderr, "%s: %s\n", name, msg); +} + +int createsocket(char *device) { + // initialize the DHCP socket and bind it to the client port + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) { + logstrerror("failed to create socket"); + return 1; + } + + struct sockaddr_in addr; + addr.sin_port = htons(DHCP_CLIENT_PORT); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + if (bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in))) { + logstrerror("failed to bind socket"); + return 1; + } + + // make the socket able to broadcast datagrams and bind it to the chosen device + int broadcast = 1; + if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast))) { + logstrerror("failed to make socket accept broadcasts"); + return 1; + } + + if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device) + 1)) { + logstrerror("failed to bind to device"); + return 1; + } + + return 0; +} + +int getdevicehwaddr(char *device) { + // get hardware address of device + struct ifreq buffer = {0}; + strcpy(buffer.ifr_name, device); + if (ioctl(sockfd, SIOCGIFHWADDR, &buffer)) { + logstrerror("failed to get device hardware address"); + return 1; + } + + memcpy(hwaddr, buffer.ifr_hwaddr.sa_data, DHCPHDR_HLEN_ETH); + + char printbuff[50]; + snprintf(printbuff, 50, "hardware address: %02x:%02x:%02x:%02x:%02x:%02x", hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]); + logmsg(printbuff); + return 0; +} + +static int discover() { + // send out the DHCPDISCOVER packet + dhcpdiscover_t packet; + memset(&packet, 0, sizeof(packet)); + + packet.hdr.op = DHCPHDR_TYPE_CLIENT; + packet.hdr.htype = DHCPHDR_HTYPE_ETH; + packet.hdr.hlen = DHCPHDR_HLEN_ETH; + packet.hdr.xid = htonl(DHCPHDR_XID); + memcpy(packet.hdr.clienthw, hwaddr, DHCPHDR_HLEN_ETH); + packet.hdr.cookie = htonl(DHCPHDR_COOKIE); + + memcpy(packet.opts, discoveropts, sizeof(packet.opts)); + + // broadcast address + struct sockaddr_in address = { + .sin_family = AF_INET, + .sin_port = htons(DHCP_SERVER_PORT), + .sin_addr.s_addr = htonl(INADDR_BROADCAST) + }; + + if (sendto(sockfd, &packet, sizeof(packet), 0, (struct sockaddr *)&address, sizeof(address)) < 0) { + logstrerror("sending DHCPDISCOVER failed"); + return 1; + } + + return 0; +} + +static int waitforserver(int timeout) { + struct pollfd pollfd = { + .fd = sockfd, + .events = POLLIN + }; + + int pollr = poll(&pollfd, 1, timeout); + return pollr <= 0 ? 1 : 0; +} + +static inline uint32_t optget32(uint8_t *opt, int i) { + return ((uint32_t)opt[i] << 24) | ((uint32_t)opt[i + 1] << 16) | ((uint32_t)opt[i + 2] << 8) | ((uint32_t)opt[i + 3]); +} + + +typedef struct { + uint32_t clientip; + uint32_t serverip; + uint32_t netmask; + uint32_t router; + uint32_t leasetime; +} offer_t; + +static int processoffer(offer_t *offer, uint32_t *xid) { + uint8_t buffer[1024]; + dhcpoffer_t *dhcpoffer = (dhcpoffer_t *)buffer; + memset(buffer, 0, sizeof(buffer)); + + size_t nbytes = recv(sockfd, buffer, sizeof(buffer), 0); + if (nbytes < 0) { + logstrerror("failed to recv while processing offer"); + return 1; + } + + // not from the server, ignore + if (dhcpoffer->hdr.op != DHCPHDR_TYPE_SERVER) { + *xid = 0; + return 0; + } + + size_t optsize = nbytes - sizeof(dhcphdr_t); + // get the offered stuff in the options + int i = 0; + uint8_t *opts = dhcpoffer->opts; + while (i < optsize) { + switch (opts[i]) { + case OPTION_TYPE: + if (opts[i + 2] != OPTION_TYPE_DHCPOFFER) { + // not a DHCPOFFER, ignore + *xid = 0; + return 0; + } + break; + case OPTION_MASK: + uint32_t mask = optget32(opts, i + 2); + offer->netmask = mask; + break; + case OPTION_ROUTER : + uint32_t router = optget32(opts, i + 2); + offer->router = router; + break; + case OPTION_SERVER: + uint32_t server = optget32(opts, i + 2); + offer->serverip = server; + break; + case OPTION_LEASETIME: + uint32_t leasetime = optget32(opts, i + 2); + offer->leasetime = leasetime; + break; + case OPTION_END: + goto leave; + } + + uint8_t optlen = opts[i + 1]; + i += optlen + 2; + } + leave: + + offer->clientip = ntohl(dhcpoffer->hdr.responseip); + + *xid = ntohl(dhcpoffer->hdr.xid); + return 0; +} + +static int request(offer_t *offer) { + // send out the DHCPREQUEST packet + dhcprequest_t packet; + memset(&packet, 0, sizeof(packet)); + + packet.hdr.op = DHCPHDR_TYPE_CLIENT; + packet.hdr.htype = DHCPHDR_HTYPE_ETH; + packet.hdr.hlen = DHCPHDR_HLEN_ETH; + packet.hdr.xid = htonl(DHCPHDR_XID); + packet.hdr.clientip = htonl(offer->clientip); + packet.hdr.serverip = htonl(offer->serverip); + memcpy(packet.hdr.clienthw, hwaddr, DHCPHDR_HLEN_ETH); + packet.hdr.cookie = htonl(DHCPHDR_COOKIE); + + packet.opts.type[0] = OPTION_TYPE; + packet.opts.type[1] = OPTION_TYPE_LENGTH; + packet.opts.type[2] = OPTION_TYPE_DHCPREQUEST; + packet.opts.ipreqhdr[0] = OPTION_IPREQ; + packet.opts.ipreqhdr[1] = 4; + packet.opts.ipreq = htonl(offer->clientip); + packet.opts.dhcpserverhdr[0] = OPTION_SERVER; + packet.opts.dhcpserverhdr[1] = 4; + packet.opts.dhcpserver = htonl(offer->serverip); + packet.opts.end = 0xff; + + // broadcast address + struct sockaddr_in address = { + .sin_family = AF_INET, + .sin_port = htons(DHCP_SERVER_PORT), + .sin_addr.s_addr = htonl(INADDR_BROADCAST) + }; + + if (sendto(sockfd, &packet, sizeof(packet), 0, (struct sockaddr *)&address, sizeof(address)) < 0) { + logstrerror("sending DHCPREQUEST failed"); + return 1; + } + + return 0; +} + +static int getack(uint32_t *xid) { + uint8_t buffer[1024]; + dhcpoffer_t *dhcpack = (dhcpoffer_t *)buffer; + memset(buffer, 0, sizeof(buffer)); + + size_t nbytes = recv(sockfd, buffer, sizeof(buffer), 0); + if (nbytes < 0) { + logstrerror("failed to recv while processing ack"); + return 1; + } + + // not from the server, ignore + if (dhcpack->hdr.op != DHCPHDR_TYPE_SERVER) { + *xid = 0; + return 0; + } + + size_t optsize = nbytes - sizeof(dhcphdr_t); + // get the offered stuff in the options + int i = 0; + uint8_t *opts = dhcpack->opts; + while (i < optsize) { + switch (opts[i]) { + case OPTION_TYPE: + if (opts[i + 2] == OPTION_TYPE_DHCPACK) { + *xid = ntohl(dhcpack->hdr.xid); + return 0; + } + break; + case OPTION_END: + goto leave; + } + + uint8_t optlen = opts[i + 1]; + i += optlen + 2; + } + leave: + + // if we reach this, its not an ack packet. + *xid = 0; + return 0; +} + +static void printip(char *ipname, uint32_t ip) { + struct in_addr addr; + addr.s_addr = htonl(ip); + char *ipstr = inet_ntoa(addr); + printf("%s: %s: %s\n", name, ipname, ipstr); +} + +static void printoffer(offer_t *offer) { + printip("client ip", offer->clientip); + printip("server ip", offer->serverip); + printip("netmask", offer->netmask); + printip("router", offer->router); + printf("%s: lease time: %d seconds\n", name, offer->leasetime); +} + +static void addroute(char *device, uint32_t addr, uint32_t gateway, uint32_t mask, int weight) { + struct rtentry route; + memset(&route, 0, sizeof(route)); + + struct sockaddr_in sockaddr; + struct sockaddr_in sockgateway; + struct sockaddr_in sockmask; + + sockaddr.sin_family = AF_INET; + sockaddr.sin_addr.s_addr = htonl(addr); + sockgateway.sin_family = AF_INET; + sockgateway.sin_addr.s_addr = htonl(gateway); + sockmask.sin_family = AF_INET; + sockmask.sin_addr.s_addr = htonl(mask); + + memcpy(&route.rt_dst, &sockaddr, sizeof(struct sockaddr_in)); + memcpy(&route.rt_gateway, &sockgateway, sizeof(struct sockaddr_in)); + memcpy(&route.rt_genmask, &sockmask, sizeof(struct sockaddr_in)); + route.rt_metric = weight; + route.rt_dev = device; + + if (ioctl(sockfd, SIOCADDRT, &route) < 0) + logstrerror("failed to add route"); +} + +int main(int argc, char *argv[]) { + name = argv[0]; + + if (argc != 2) + usage(); + + if (createsocket(argv[1])) + return EXIT_FAILURE; + + if (getdevicehwaddr(argv[1])) + return EXIT_FAILURE; + + offer_t offer; + + bool gotoffer = false; + + while (gotoffer == false) { + if (discover()) + return EXIT_FAILURE; + + while (gotoffer == false) { + if (waitforserver(10000)) + break; + + uint32_t xid; + + if (processoffer(&offer, &xid)) + return EXIT_FAILURE; + + // packet wasn't for this machine + if (xid != DHCPHDR_XID) + continue; + + gotoffer = true; + } + } + + bool gotack = false; + while (gotack == false) { + if (request(&offer)) + return EXIT_FAILURE; + + while (gotack == false) { + if (waitforserver(10000)) + break; + + uint32_t xid; + + if (getack(&xid)) + return EXIT_FAILURE; + + if (xid != DHCPHDR_XID) + continue; + + gotack = true; + } + } + + // TODO routing table information + printoffer(&offer); + + addroute(argv[1], 0, offer.router, 0, 1); + addroute(argv[1], offer.clientip, 0, offer.netmask, 10); + + return EXIT_SUCCESS; +}