dhcp/client.cpp
2026-08-18 23:53:39 -05:00

234 lines
7.6 KiB
C++

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/if.h>
#include <linux/sockios.h>
#include <linux/filter.h>
#include "ipv4.h"
#include "udp4.h"
#include "dhcp4.h"
#include "eth.h"
int make_packet(uint8_t * buf,
uint8_t const * eth_source_address,
uint8_t const * eth_destination_address,
uint32_t const ipv4_source_address,
uint32_t const ipv4_destination_address,
uint32_t const dhcp_transaction_id,
uint32_t const dhcp_message_type)
{
//////////////////////////////////////////////////////////////////////
// dhcp4
//////////////////////////////////////////////////////////////////////
int dhcp4_offset = (sizeof (eth::header)) + (sizeof (ipv4::header)) + (sizeof (udp4::header));
int dhcp4_length = dhcp4::make_message(reinterpret_cast<void *>(&buf[dhcp4_offset]),
dhcp4::op::bootrequest,
dhcp_transaction_id,
eth_source_address,
dhcp_message_type);
//////////////////////////////////////////////////////////////////////
// eth
//////////////////////////////////////////////////////////////////////
int eth_offset = 0;
eth::header * eth_header = reinterpret_cast<eth::header *>(&buf[eth_offset]);
memcpy(eth_header->destination_address, eth_destination_address, 6);
memcpy(eth_header->source_address, eth_source_address, 6);
eth_header->ether_type = htons(0x0800);
//////////////////////////////////////////////////////////////////////
// ipv4
//////////////////////////////////////////////////////////////////////
int ipv4_offset = (sizeof (eth::header));
int ipv4_payload_length = dhcp4_length + (sizeof (udp4::header));
int ipv4_identification = 55179;
ipv4::make_header(reinterpret_cast<void *>(&buf[ipv4_offset]),
ipv4_payload_length,
ipv4_identification,
ipv4_source_address,
ipv4_destination_address);
//////////////////////////////////////////////////////////////////////
// udp4
//////////////////////////////////////////////////////////////////////
int udp4_offset = (sizeof (eth::header)) + (sizeof (ipv4::header));
udp4::make_header(reinterpret_cast<void *>(&buf[udp4_offset]),
htons(68),
htons(67),
ipv4_source_address,
ipv4_destination_address,
&buf[dhcp4_offset],
dhcp4_length);
return dhcp4_offset + dhcp4_length;
}
void interface_send(int sock,
int interface_index,
void * buf,
int length,
uint8_t const * eth_destination_address)
{
struct sockaddr_ll socket_address;
socket_address.sll_ifindex = interface_index;
socket_address.sll_halen = ETH_ALEN;
memcpy(socket_address.sll_addr, eth_destination_address, 6);
int flags = 0;
int sret = sendto(sock, buf, length, flags,
(struct sockaddr *)&socket_address, sizeof(struct sockaddr_ll));
if (sret < 0) {
perror("sendto");
exit(EXIT_FAILURE);
}
}
void attach_dhcp_filter(int sock)
{
struct sock_filter filter[] = {
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 8, 0x00000800 },
{ 0x30, 0, 0, 0x00000017 },
{ 0x15, 0, 6, 0x00000011 },
{ 0x28, 0, 0, 0x00000014 },
{ 0x45, 4, 0, 0x00001fff },
{ 0xb1, 0, 0, 0x0000000e },
{ 0x48, 0, 0, 0x00000010 },
{ 0x15, 0, 1, 0x00000044 },
{ 0x06, 0, 0, 0x00040000 },
{ 0x06, 0, 0, 0000000000 },
};
struct sock_fprog fprog = {
.len = sizeof(filter) / sizeof(filter[0]),
.filter = filter,
};
int ret = setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, (sizeof (fprog)));
if (ret < 0) {
perror("setsockopt(SO_ATTACH_FILTER)");
exit(EXIT_FAILURE);
}
}
int main()
{
srand(time(NULL));
uint8_t buf[16384];
//////////////////////////////////////////////////////////////////////
// socket
//////////////////////////////////////////////////////////////////////
int s = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
if (s < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
{
int enable = 1;
int ret = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &enable, (sizeof (int)));
if (ret < 0) {
perror("setsockopt(SO_BROADCAST)");
exit(EXIT_FAILURE);
}
}
//////////////////////////////////////////////////////////////////////
// interface index
//////////////////////////////////////////////////////////////////////
char const * interface_name = "wlp174s0";
struct ifreq if_index;
memset(&if_index, 0, (sizeof (struct ifreq)));
strncpy(if_index.ifr_name, interface_name, IFNAMSIZ - 1);
int iret = ioctl(s, SIOCGIFINDEX, &if_index);
if (iret < 0) {
perror("ioctl(SIOCGIFINDEX)");
exit(EXIT_FAILURE);
}
printf("interface index %d\n", if_index.ifr_ifindex);
//////////////////////////////////////////////////////////////////////
// interface address
//////////////////////////////////////////////////////////////////////
struct ifreq if_hwaddr;
memset(&if_hwaddr, 0, sizeof(struct ifreq));
strncpy(if_hwaddr.ifr_name, interface_name, IFNAMSIZ - 1);
int hret = ioctl(s, SIOCGIFHWADDR, &if_hwaddr);
if (hret < 0) {
perror("ioctl(SIOCGIFHWADDR)");
exit(EXIT_FAILURE);
}
printf("hardware address %02x:%02x:%02x:%02x:%02x:%02x\n",
(uint8_t)if_hwaddr.ifr_hwaddr.sa_data[0], (uint8_t)if_hwaddr.ifr_hwaddr.sa_data[1],
(uint8_t)if_hwaddr.ifr_hwaddr.sa_data[2], (uint8_t)if_hwaddr.ifr_hwaddr.sa_data[3],
(uint8_t)if_hwaddr.ifr_hwaddr.sa_data[4], (uint8_t)if_hwaddr.ifr_hwaddr.sa_data[5]);
//////////////////////////////////////////////////////////////////////
// bind
//////////////////////////////////////////////////////////////////////
struct sockaddr_ll bind_addr = {
.sll_family = AF_PACKET,
.sll_protocol = htons(ETH_P_ALL),
.sll_ifindex = if_index.ifr_ifindex,
};
int bret = bind(s, (struct sockaddr *)&bind_addr, (sizeof (struct sockaddr_ll)));
if (bret < 0) {
perror("bind");
exit(EXIT_FAILURE);
}
attach_dhcp_filter(s);
//////////////////////////////////////////////////////////////////////
// send
//////////////////////////////////////////////////////////////////////
uint32_t dhcp_transaction_id = rand();
uint8_t eth_source_address[6] = {};
memcpy(eth_source_address, if_hwaddr.ifr_hwaddr.sa_data, 6);
uint8_t eth_destination_address[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
uint32_t ipv4_source_address = 0x00000000;
uint32_t ipv4_destination_address = 0xffffffff;
int length = make_packet(buf,
eth_source_address,
eth_destination_address,
ipv4_source_address,
ipv4_destination_address,
dhcp_transaction_id,
dhcp4::message_type::dhcpdiscover);
interface_send(s, if_index.ifr_ifindex, buf, length, eth_destination_address);
uint8_t recvbuf[65536];
struct sockaddr_ll recv_address;
int flags = 0;
socklen_t socklen = (sizeof (struct sockaddr_ll));
ssize_t ret = recvfrom(s, recvbuf, 16384, flags,
(struct sockaddr *)&recv_address, &socklen);
printf("recv ret %ld\n", ret);
close(s);
exit(EXIT_SUCCESS);
}