This commit is contained in:
Zack Buhman 2026-08-18 23:53:39 -05:00
commit 4024bcf42f
11 changed files with 640 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.o
*.out
.gdb_history

11
bpf.asm Normal file
View File

@ -0,0 +1,11 @@
l0: ldh [12] /* eth.type */
l1: jeq #0x800, l2, l10 /* if ipv4, l3 */
l2: ldb [23] /* ip.proto */
l3: jeq #0x11, l4, l10 /* if UDP, l5 */
l4: ldh [20] /* ip.frag_offset */
l5: jset #0x1fff, l10, l6 /* if not fragmented, l7 */
l6: ldxb 4*([14]&0xf) /* ip.hdr_len * 4 -> X */
l7: ldh [x + 16] /* 36: UDP destination port */
l8: jeq #0x44, l9, l10 /* if equal to 68, l10 */
l9: ret #0x40000 /* keep 262144 bytes */
l10: ret #0 /* discard the packet */

233
client.cpp Normal file
View File

@ -0,0 +1,233 @@
#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);
}

67
dhcp4.cpp Normal file
View File

@ -0,0 +1,67 @@
#include <string.h>
#include <arpa/inet.h>
#include "dhcp4.h"
namespace dhcp4 {
int make_message(void * buf,
uint32_t bootp_message_type,
uint32_t transaction_id,
uint8_t const * chaddr,
uint32_t dhcp_message_type)
{
memset(buf, 0, (sizeof (message)));
message * msg = reinterpret_cast<message *>(buf);
msg->op = bootp_message_type;
msg->htype = htype::ethernet;
msg->hlen = 6;
msg->xid = htonl(transaction_id);
memcpy(msg->chaddr, chaddr, 6);
int index = 0;
uint8_t * cookie = &msg->options[index];
index += 4;
cookie[0] = 0x63;
cookie[1] = 0x82;
cookie[2] = 0x53;
cookie[3] = 0x63;
uint8_t * message_type = &msg->options[index];
index += 2 + 1;
message_type[0] = option::message_type;
message_type[1] = 1; // length
message_type[2] = dhcp_message_type;
{
uint8_t * prl = &msg->options[index];
int length = 4;
index += 2 + length;
prl[0] = option::parameter_request_list;
prl[1] = length;
prl[2] = 1; // subnet mask
prl[3] = 3; // router
prl[4] = 6; // domain name server
prl[5] = 28; // broadcast address
}
if (0) {
uint8_t * message_size = &msg->options[index];
int length = 2;
index += 2 + length;
message_size[0] = option::maximum_message_size;
message_size[1] = length;
message_size[2] = 0x05;
message_size[3] = 0xc0;
}
uint8_t * end = &msg->options[index];
index += 1;
end[0] = 255;
return (sizeof (message)) + index;
}
}

71
dhcp4.h Normal file
View File

@ -0,0 +1,71 @@
#pragma once
#include <stdint.h>
#include <assert.h>
namespace dhcp4 {
struct __attribute__((packed)) message {
uint8_t op; // message type
uint8_t htype; // hardware type
uint8_t hlen; // hardware address length
uint8_t hops;
uint32_t xid; // transaction id
uint16_t secs; // seconds elapsed
uint16_t flags;
uint32_t ciaddr; // client IP address
uint32_t yiaddr; // your (client) IP address
uint32_t siaddr; // next server IP address
uint32_t giaddr; // relay agent IP address
uint8_t chaddr[16]; // client hardware address
uint8_t sname[64];
uint8_t file[128];
uint8_t options[];
};
struct op {
enum {
bootrequest = 1,
bootreply = 2,
};
};
struct htype {
enum {
ethernet = 1,
};
};
static_assert((sizeof (message)) == 236);
struct option {
enum {
ip_address_lease_time = 51,
option_overload = 52,
message_type = 53,
server_identifier = 54,
parameter_request_list = 55,
maximum_message_size = 57,
client_identifier = 61,
};
};
struct message_type {
enum {
dhcpdiscover = 1,
dhcpoffer = 2,
dhcprequest = 3,
dhcpdecline = 4,
dhcpack = 5,
dhcpnak = 6,
dhcprelease = 7,
dhcpinform = 8,
};
};
int make_message(void * buf,
uint32_t bootp_message_type,
uint32_t transaction_id,
uint8_t const * chaddr,
uint32_t dhcp_message_type);
}

11
eth.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include <stdint.h>
namespace eth {
struct header {
uint8_t destination_address[6];
uint8_t source_address[6];
uint16_t ether_type;
};
}

70
ipv4.cpp Normal file
View File

@ -0,0 +1,70 @@
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <arpa/inet.h>
#include "ipv4.h"
namespace ipv4 {
uint32_t checksum_partial(uint32_t sum, void const * src, int length)
{
uint8_t const * addr = reinterpret_cast<uint8_t const *>(src);
ptrdiff_t i = 0;
while (length >= 2) {
sum += (addr[i+0] << 8) | (addr[i+1] << 0);
i += 2;
length -= 2;
}
if (length > 0) {
sum += (addr[i+0] << 8);
}
return sum;
}
uint32_t checksum_finish(uint32_t sum)
{
while (sum >> 16) {
sum = (sum & 0xffff) + (sum >> 16);
}
return static_cast<uint16_t>(~sum);
}
uint32_t checksum(void const * src, int length)
{
uint32_t sum = checksum_partial(0, src, length);
return checksum_finish(sum);
}
header * make_header(void * buf,
uint16_t payload_length,
uint16_t identification,
uint32_t source_address, // big endian
uint32_t destination_address) // big endian
{
memset(buf, 0, (sizeof (header)));
header * hdr = reinterpret_cast<header *>(buf);
const int version = 4;
const int ihl = (sizeof (header)) / 4;
const int total_length = payload_length + (sizeof (header));
hdr->version_ihl = (version << 4) | (ihl << 0);
//hdr->type_of_service = 0xb8;
hdr->total_length = htons(total_length);
hdr->identification = htons(identification);
hdr->time_to_live = 64;
hdr->protocol = protocol::udp;
hdr->source_address = source_address;
hdr->destination_address = destination_address;
hdr->header_checksum = htons(checksum(buf, (sizeof (header))));
return hdr;
}
}

37
ipv4.h Normal file
View File

@ -0,0 +1,37 @@
#pragma once
#include <stdint.h>
namespace ipv4 {
struct header {
uint8_t version_ihl;
uint8_t type_of_service;
uint16_t total_length;
uint16_t identification;
uint16_t fragment_offset;
uint8_t time_to_live;
uint8_t protocol;
uint16_t header_checksum;
uint32_t source_address;
uint32_t destination_address;
};
static_assert((sizeof (header)) == 20);
uint32_t checksum_partial(uint32_t sum, void const * src, int length);
uint32_t checksum_finish(uint32_t sum);
uint32_t checksum(void const * src, int length);
header * make_header(void * buf,
uint16_t payload_length,
uint16_t identification,
uint32_t source_address, // big endian
uint32_t destination_address); // big endian
struct protocol {
enum {
udp = 17,
};
};
}

64
test.cpp Normal file
View File

@ -0,0 +1,64 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include "udp4.h"
#include "ipv4.h"
#include "dhcp4.h"
void dump_hex(void const * src, int length)
{
uint8_t const * u8 = reinterpret_cast<uint8_t const *>(src);
for (int i = 0; i < length; i++) {
printf("%02x ", u8[i]);
if (i % 16 == 15)
printf("\n");
}
printf("\n");
}
int main()
{
int udp_header = 8 + 339;
unsigned char payload[] = {0x2, 0x1, 0x6, 0x0, 0x12, 0x27, 0x67, 0xa8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0, 0xa8, 0x1, 0xf0, 0xc0, 0xa8, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0xf4, 0x7b, 0x9, 0x99, 0x3e, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x63, 0x82, 0x53, 0x63, 0x35, 0x1, 0x5, 0x36, 0x4, 0xc0, 0xa8, 0x1, 0x1, 0x33, 0x4, 0x0, 0x1, 0x51, 0x80, 0x3a, 0x4, 0x0, 0x0, 0xa8, 0xc0, 0x3b, 0x4, 0x0, 0x1, 0x27, 0x50, 0x1c, 0x4, 0xc0, 0xa8, 0x1, 0xff, 0x3, 0x4, 0xc0, 0xa8, 0x1, 0x1, 0xf, 0x3, 0x6c, 0x61, 0x6e, 0x1, 0x4, 0xff, 0xff, 0xff, 0x0, 0x6, 0x4, 0xc0, 0xa8, 0x1, 0x1, 0x7d, 0x28, 0x0, 0x0, 0xd, 0xe9, 0x23, 0x6, 0x8, 0x47, 0x52, 0x42, 0x45, 0x33, 0x33, 0x31, 0x43, 0x5, 0xf, 0x52, 0x30, 0x31, 0x43, 0x31, 0x4d, 0x32, 0x35, 0x34, 0x36, 0x30, 0x30, 0x32, 0x48, 0x45, 0x4, 0x6, 0x30, 0x30, 0x30, 0x46, 0x42, 0x33, 0xff};
uint8_t buf[16384];
struct in_addr srcA;
struct in_addr dstA;
inet_aton("192.168.1.1", &srcA);
inet_aton("192.168.1.240", &dstA);
int ipv4_payload_length = (sizeof (payload)) + (sizeof (udp4::header));
int ipv4_identification = 55179;
int offset = 0;
ipv4::header * ipv4_header = ipv4::make_header(reinterpret_cast<void *>(&buf[offset]),
ipv4_payload_length,
ipv4_identification,
srcA.s_addr,
dstA.s_addr);
offset += (sizeof (ipv4::header));
dump_hex(ipv4_header, (sizeof (ipv4::header)));
udp4::header * udp4_header = udp4::make_header(reinterpret_cast<void *>(&buf[offset]),
htons(67),
htons(68),
srcA.s_addr,
dstA.s_addr,
payload,
(sizeof (payload)));
offset += (sizeof (udp4::header));
dump_hex(udp4_header, (sizeof (udp4::header)));
uint32_t transaction_id = 0x122767a8;
int dhcp4_length = dhcp4::make_message(reinterpret_cast<void *>(&buf[offset]),
dhcp4::op::bootrequest,
transaction_id,
dhcp4::message_type::dhcprequest);
dump_hex(&buf[offset], dhcp4_length);
offset += dhcp4_length;
}

41
udp4.cpp Normal file
View File

@ -0,0 +1,41 @@
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include "ipv4.h"
#include "udp4.h"
namespace udp4 {
header * make_header(void * buf,
uint32_t source_port,
uint32_t destination_port,
uint32_t source_address,
uint32_t destination_address,
void const * payload,
int payload_length)
{
memset(buf, 0, (sizeof (header)));
header * hdr = reinterpret_cast<header *>(buf);
hdr->source_port = source_port;
hdr->destination_port = destination_port;
hdr->length = htons(payload_length + (sizeof (header)));
ipv4_psuedo_header phdr;
phdr.source_address = source_address;
phdr.destination_address = destination_address;
phdr.zeros = 0;
phdr.protocol = ipv4::protocol::udp;
phdr.udp_length = hdr->length; // already big endian
uint32_t sum = 0;
sum = ipv4::checksum_partial(sum, reinterpret_cast<void const *>(&phdr), (sizeof (ipv4_psuedo_header)));
sum = ipv4::checksum_partial(sum, reinterpret_cast<void const *>(hdr), (sizeof (header)));
sum = ipv4::checksum_partial(sum, reinterpret_cast<void const *>(payload), payload_length);
hdr->checksum = htons(ipv4::checksum_finish(sum));
return hdr;
}
}

32
udp4.h Normal file
View File

@ -0,0 +1,32 @@
#pragma once
#include <stdint.h>
#include "ipv4.h"
namespace udp4 {
struct header {
uint16_t source_port;
uint16_t destination_port;
uint16_t length;
uint16_t checksum;
};
static_assert((sizeof (header)) == 8);
struct ipv4_psuedo_header {
uint32_t source_address;
uint32_t destination_address;
uint8_t zeros;
uint8_t protocol;
uint16_t udp_length;
};
header * make_header(void * buf,
uint32_t source_port,
uint32_t destination_port,
uint32_t source_address,
uint32_t destination_address,
void const * payload,
int payload_length);
}