mirror of
https://github.com/Mathewnd/Astral.git
synced 2026-10-27 12:48:22 +00:00
netd: initial implementation (dhcp client)
This commit is contained in:
@@ -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 $@ $<
|
||||
@@ -0,0 +1,474 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/socket.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <netinet/in.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <net/route.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
Reference in New Issue
Block a user