dhcp/client.cpp

195 lines
6.3 KiB
C++

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stddef.h>
#include <time.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include "ip4.h"
#include "udp4.h"
#include "dhcp4.h"
#include "eth.h"
#include "packet.h"
#include "interface.h"
#include "sock.h"
void print_message(packet::packet const& p, dhcp4::options const& options)
{
printf("dhcp4 op: %d\n", p.dhcp4->op);
printf("dhcp4 yiaddr: %s\n", ip4::tostr(p.dhcp4->yiaddr));
printf("message type: %s\n", dhcp4::message_type_tostr(options.message_type));
printf("subnet mask: %s\n", ip4::tostr(options.subnet_mask));
printf("router: %s\n", ip4::tostr(options.router));
printf("domain name server: %s\n", ip4::tostr(options.domain_name_server));
}
int main()
{
srand(time(NULL));
uint32_t dhcp_transaction_id = rand();
int sock = sock::create(68);
char const * interface_name = "wlp174s0";
int ifa_index = sock::interface_index(sock, interface_name);
sock::bind_interface(sock, ifa_index);
uint8_t eth_source_address[6];
sock::interface_address(sock, interface_name, eth_source_address);
uint8_t eth_destination_address[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
uint32_t ip4_source_address = 0x00000000;
uint32_t ip4_destination_address = 0xffffffff;
packet::packet p{};
dhcp4::options options{};
static uint8_t recvbuf[65536];
static uint8_t sendbuf[16384];
//////////////////////////////////////////////////////////////////////
// dhcp send discover
//////////////////////////////////////////////////////////////////////
{
dhcp4::message_parameters params = {
};
int length = packet::make(sendbuf,
dhcp4::op::bootrequest,
eth_source_address,
eth_destination_address,
eth_source_address, // eth_client_address
ip4_source_address,
ip4_destination_address,
68, // udp4_source_port
67, // udp4_destination_port
dhcp_transaction_id,
dhcp4::message_type::dhcpdiscover,
&params);
sock::send(sock, ifa_index, sendbuf, length, eth_destination_address);
}
//////////////////////////////////////////////////////////////////////
// dhcp recv offer
//////////////////////////////////////////////////////////////////////
{
struct sockaddr_ll recv_address;
int flags = 0;
socklen_t socklen = (sizeof (struct sockaddr_ll));
ssize_t rret = recvfrom(sock, recvbuf, (sizeof (recvbuf)), flags,
(struct sockaddr *)&recv_address, &socklen);
if (rret < 0) {
perror("recvfrom");
exit(EXIT_FAILURE);
}
packet::parse(recvbuf, rret, &p);
int dhcp4_options_length = rret - (packet::dhcp4_offset + (offsetof (dhcp4::message, options)));
dhcp4::parse_options(p.dhcp4, dhcp4_options_length, &options);
print_message(p, options);
assert(p.dhcp4->op == dhcp4::op::bootreply);
assert(options.message_type == dhcp4::message_type::dhcpoffer);
}
//////////////////////////////////////////////////////////////////////
// dhcp send request
//////////////////////////////////////////////////////////////////////
{
dhcp4::message_parameters params = {
.requested_ip_address = p.dhcp4->yiaddr,
};
int length = packet::make(sendbuf,
dhcp4::op::bootrequest,
eth_source_address,
eth_destination_address,
eth_source_address, // eth_client_address
ip4_source_address,
ip4_destination_address,
68, // udp4_source_port
67, // udp4_destination_port
dhcp_transaction_id,
dhcp4::message_type::dhcprequest,
&params);
sock::send(sock, ifa_index, sendbuf, length, eth_destination_address);
}
//////////////////////////////////////////////////////////////////////
// dhcp recv ack
//////////////////////////////////////////////////////////////////////
{
struct sockaddr_ll recv_address;
int flags = 0;
socklen_t socklen = (sizeof (struct sockaddr_ll));
ssize_t rret = recvfrom(sock, recvbuf, (sizeof (recvbuf)), flags,
(struct sockaddr *)&recv_address, &socklen);
if (rret < 0) {
perror("recvfrom");
exit(EXIT_FAILURE);
}
packet::parse(recvbuf, rret, &p);
int dhcp4_options_length = rret - (packet::dhcp4_offset + (offsetof (dhcp4::message, options)));
dhcp4::parse_options(p.dhcp4, dhcp4_options_length, &options);
print_message(p, options);
assert(p.dhcp4->op == dhcp4::op::bootreply);
assert(options.message_type == dhcp4::message_type::dhcpack);
}
//////////////////////////////////////////////////////////////////////
// interface configure
//////////////////////////////////////////////////////////////////////
uint32_t mask = ntohl(options.subnet_mask);
uint8_t prefix_length = 32 - __builtin_ctz(mask);
printf("prefix_length %d\n", prefix_length);
interface::configure(ifa_index,
prefix_length,
p.dhcp4->yiaddr,
options.router);
//////////////////////////////////////////////////////////////////////
// dns configure
//////////////////////////////////////////////////////////////////////
int dfd = open("/etc/resolv.conf", O_CREAT | O_WRONLY | O_TRUNC);
if (dfd < 0) {
perror("open");
exit(EXIT_FAILURE);
}
char dbuf[1024];
int dbuflen = snprintf(dbuf, (sizeof (dbuf)), "nameserver %s\n",
ip4::tostr(options.domain_name_server));
if (dbuflen < 0) {
perror("snprintf");
exit(EXIT_FAILURE);
}
ssize_t dret = write(dfd, dbuf, dbuflen);
if (dret < 0) {
perror("write");
exit(EXIT_FAILURE);
}
//////////////////////////////////////////////////////////////////////
// exit
//////////////////////////////////////////////////////////////////////
close(dfd);
close(sock);
exit(EXIT_SUCCESS);
}