dhcp4 response/option parsing

This commit is contained in:
Zack Buhman 2026-08-19 10:14:38 -05:00
parent 4024bcf42f
commit 364d03f15c
15 changed files with 274 additions and 56 deletions

4
.gitignore vendored
View File

@ -1,3 +1,7 @@
*
!*.*
!*/
*.o
*.out
.gdb_history

39
Makefile Normal file
View File

@ -0,0 +1,39 @@
OPT = -O0 -fno-stack-protector
CFLAGS = -g -gdwarf-4 -Wall -Wextra -Wno-error -Wfatal-errors
#CFLAGS += -Wno-error=unused-parameter
#CFLAGS += -Wno-error=unused-variable
#CFLAGS += -Wno-error=unused-but-set-variable
CFLAGS += -Wno-missing-field-initializers
CFLAGS += -I../../include
CFLAGS += $(OPT)
LDFLAGS += $(OPT)
CXXFLAGS = -std=c++23
OBJS = \
dhcp4.o \
ip4.o \
packet.o \
udp4.o \
eth.o
all: client test2
%.o: %.cpp
$(CXX) $(CFLAGS) $(CXXFLAGS) $(FREETYPE_CFLAGS) -c $< -o $@
%: %.o $(OBJS)
$(CXX) $(LDFLAGS) $^ -o $@
.SUFFIXES:
.INTERMEDIATE:
.SECONDARY:
.PHONY: all clean phony
%: RCS/%,v
%: RCS/%
%: %,v
%: s.%
%: SCCS/s.%

View File

@ -13,7 +13,7 @@
#include <linux/sockios.h>
#include <linux/filter.h>
#include "ipv4.h"
#include "ip4.h"
#include "udp4.h"
#include "dhcp4.h"
#include "eth.h"
@ -21,8 +21,8 @@
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 ip4_source_address,
uint32_t const ip4_destination_address,
uint32_t const dhcp_transaction_id,
uint32_t const dhcp_message_type)
{
@ -30,7 +30,7 @@ int make_packet(uint8_t * buf,
// dhcp4
//////////////////////////////////////////////////////////////////////
int dhcp4_offset = (sizeof (eth::header)) + (sizeof (ipv4::header)) + (sizeof (udp4::header));
int dhcp4_offset = (sizeof (eth::header)) + (sizeof (ip4::header)) + (sizeof (udp4::header));
int dhcp4_length = dhcp4::make_message(reinterpret_cast<void *>(&buf[dhcp4_offset]),
dhcp4::op::bootrequest,
dhcp_transaction_id,
@ -48,28 +48,28 @@ int make_packet(uint8_t * buf,
eth_header->ether_type = htons(0x0800);
//////////////////////////////////////////////////////////////////////
// ipv4
// ip4
//////////////////////////////////////////////////////////////////////
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);
int ip4_offset = (sizeof (eth::header));
int ip4_payload_length = dhcp4_length + (sizeof (udp4::header));
int ip4_identification = 55179;
ip4::make_header(reinterpret_cast<void *>(&buf[ip4_offset]),
ip4_payload_length,
ip4_identification,
ip4_source_address,
ip4_destination_address);
//////////////////////////////////////////////////////////////////////
// udp4
//////////////////////////////////////////////////////////////////////
int udp4_offset = (sizeof (eth::header)) + (sizeof (ipv4::header));
int udp4_offset = (sizeof (eth::header)) + (sizeof (ip4::header));
udp4::make_header(reinterpret_cast<void *>(&buf[udp4_offset]),
htons(68),
htons(67),
ipv4_source_address,
ipv4_destination_address,
ip4_source_address,
ip4_destination_address,
&buf[dhcp4_offset],
dhcp4_length);
@ -207,14 +207,14 @@ int main()
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;
uint32_t ip4_source_address = 0x00000000;
uint32_t ip4_destination_address = 0xffffffff;
int length = make_packet(buf,
eth_source_address,
eth_destination_address,
ipv4_source_address,
ipv4_destination_address,
ip4_source_address,
ip4_destination_address,
dhcp_transaction_id,
dhcp4::message_type::dhcpdiscover);

View File

@ -38,24 +38,13 @@ namespace dhcp4 {
{
uint8_t * prl = &msg->options[index];
int length = 4;
int length = 3;
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;
prl[2] = option::subnet_mask;
prl[3] = option::router;
prl[4] = option::domain_name_server;
}
uint8_t * end = &msg->options[index];
@ -64,4 +53,37 @@ namespace dhcp4 {
return (sizeof (message)) + index;
}
void parse_options(message const * m, int options_length, options * o)
{
uint8_t const * options = m->options;
int index = 4;
while (index + 2 <= options_length) {
int option = options[index + 0];
int option_length = options[index + 1];
//printf("option %d length %d\n", option, option_length);
switch (option) {
case option::message_type:
o->message_type = options[index + 2];
break;
case option::subnet_mask:
o->subnet_mask = *reinterpret_cast<uint32_t const *>(&options[index + 2]);
break;
case option::router:
o->router = *reinterpret_cast<uint32_t const *>(&options[index + 2]);
break;
case option::domain_name_server:
o->domain_name_server = *reinterpret_cast<uint32_t const *>(&options[index + 2]);
break;
default:
break;
}
index += 2 + option_length;
}
assert(options[index] == 255);
}
}

13
dhcp4.h
View File

@ -23,6 +23,13 @@ namespace dhcp4 {
uint8_t options[];
};
struct options {
int message_type; // 53
uint32_t subnet_mask; // 1
uint32_t router; // 3
uint32_t domain_name_server; // 6
};
struct op {
enum {
bootrequest = 1,
@ -40,6 +47,10 @@ namespace dhcp4 {
struct option {
enum {
subnet_mask = 1,
router = 3,
domain_name_server = 6,
broadcast_address = 28,
ip_address_lease_time = 51,
option_overload = 52,
message_type = 53,
@ -68,4 +79,6 @@ namespace dhcp4 {
uint32_t transaction_id,
uint8_t const * chaddr,
uint32_t dhcp_message_type);
void parse_options(message const * m, int options_length, options * o);
}

15
eth.cpp Normal file
View File

@ -0,0 +1,15 @@
#include <assert.h>
#include <stdio.h>
#include "eth.h"
namespace eth {
char const * tostr(uint8_t const * buf)
{
static char tmp[18];
int ret = snprintf(tmp, 18,
"%02x:%02x:%02x:%02x:%02x:%02x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
assert(ret > 0);
return tmp;
}
}

2
eth.h
View File

@ -8,4 +8,6 @@ namespace eth {
uint8_t source_address[6];
uint16_t ether_type;
};
char const * tostr(uint8_t const * buf);
}

View File

@ -4,9 +4,15 @@
#include <arpa/inet.h>
#include "ipv4.h"
#include "ip4.h"
namespace ipv4 {
namespace ip4 {
char const * tostr(uint32_t a)
{
static char tmp[16];
return inet_ntop(AF_INET, reinterpret_cast<void const *>(&a), tmp, 16);
}
uint32_t checksum_partial(uint32_t sum, void const * src, int length)
{

View File

@ -2,7 +2,7 @@
#include <stdint.h>
namespace ipv4 {
namespace ip4 {
struct header {
uint8_t version_ihl;
@ -19,6 +19,8 @@ namespace ipv4 {
static_assert((sizeof (header)) == 20);
char const * tostr(uint32_t a);
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);

62
packet.cpp Normal file
View File

@ -0,0 +1,62 @@
#include <stdio.h>
#include <arpa/inet.h>
#include "packet.h"
namespace packet {
void parse(uint8_t const * buf, int length, packet * p)
{
//////////////////////////////////////////////////////////////////////
// eth
//////////////////////////////////////////////////////////////////////
eth::header const * eth_header = reinterpret_cast<eth::header const *>(&buf[eth_offset]);
//printf("eth source address ");
//print_eth_address(eth_header->source_address);
//printf("eth destination address ");
//print_eth_address(eth_header->destination_address);
//////////////////////////////////////////////////////////////////////
// ip4
//////////////////////////////////////////////////////////////////////
ip4::header const * ip4_header = reinterpret_cast<ip4::header const *>(&buf[ip4_offset]);
//printf("ip4 total length: %d\n", ntohs(ip4_header->total_length));
assert(ip4_offset + ntohs(ip4_header->total_length) == length);
printf("ip4 source address: %s\n", ip4::tostr(ip4_header->source_address));
printf("ip4 destination address: %s\n", ip4::tostr(ip4_header->destination_address));
//////////////////////////////////////////////////////////////////////
// udp4
//////////////////////////////////////////////////////////////////////
udp4::header const * udp4_header = reinterpret_cast<udp4::header const *>(&buf[udp4_offset]);
//printf("udp4 length: %d\n", ntohs(udp4_header->length));
assert(udp4_offset + ntohs(udp4_header->length) == length);
//////////////////////////////////////////////////////////////////////
// dhcp4
//////////////////////////////////////////////////////////////////////
dhcp4::message const * dhcp4_message = reinterpret_cast<dhcp4::message const *>(&buf[dhcp4_offset]);
uint8_t const * dhcp4_options = dhcp4_message->options;
//printf("dhcp4 cookie %02x %02x %02x %02x\n", dhcp4_options[0], dhcp4_options[1], dhcp4_options[2], dhcp4_options[3]);
assert(dhcp4_options[0] == 0x63 &&
dhcp4_options[1] == 0x82 &&
dhcp4_options[2] == 0x53 &&
dhcp4_options[3] == 0x63);
//////////////////////////////////////////////////////////////////////
// packet
//////////////////////////////////////////////////////////////////////
p->eth = eth_header;
p->ip4 = ip4_header;
p->udp4 = udp4_header;
p->dhcp4 = dhcp4_message;
}
}

22
packet.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include "eth.h"
#include "ip4.h"
#include "udp4.h"
#include "dhcp4.h"
namespace packet {
struct packet {
eth::header const * eth;
ip4::header const * ip4;
udp4::header const * udp4;
dhcp4::message const * dhcp4;
};
void parse(uint8_t const * buf, int length, packet * p);
const int eth_offset = 0;
const int ip4_offset = (sizeof (eth::header));
const int udp4_offset = (sizeof (eth::header)) + (sizeof (ip4::header));
const int dhcp4_offset = (sizeof (eth::header)) + (sizeof (ip4::header)) + (sizeof (udp4::header));
};

View File

@ -5,7 +5,7 @@
#include <stdio.h>
#include "udp4.h"
#include "ipv4.h"
#include "ip4.h"
#include "dhcp4.h"
void dump_hex(void const * src, int length)
@ -32,16 +32,16 @@ int main()
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 ip4_payload_length = (sizeof (payload)) + (sizeof (udp4::header));
int ip4_identification = 55179;
int offset = 0;
ipv4::header * ipv4_header = ipv4::make_header(reinterpret_cast<void *>(&buf[offset]),
ipv4_payload_length,
ipv4_identification,
ip4::header * ip4_header = ip4::make_header(reinterpret_cast<void *>(&buf[offset]),
ip4_payload_length,
ip4_identification,
srcA.s_addr,
dstA.s_addr);
offset += (sizeof (ipv4::header));
dump_hex(ipv4_header, (sizeof (ipv4::header)));
offset += (sizeof (ip4::header));
dump_hex(ip4_header, (sizeof (ip4::header)));
udp4::header * udp4_header = udp4::make_header(reinterpret_cast<void *>(&buf[offset]),
htons(67),

31
test2.cpp Normal file
View File

@ -0,0 +1,31 @@
#include <stdio.h>
#include <assert.h>
#include <stddef.h>
#include <arpa/inet.h>
#include "packet.h"
unsigned char bytes[] = {0xf4, 0x7b, 0x9, 0x99, 0x3e, 0x28, 0x0, 0xbc, 0x2f, 0x8c, 0x58, 0x20, 0x8, 0x0, 0x45, 0xb8, 0x1, 0x6f, 0x48, 0x9d, 0x0, 0x0, 0x40, 0x11, 0xab, 0xe7, 0xc0, 0xa8, 0x1, 0x1, 0xc0, 0xa8, 0x1, 0xf0, 0x0, 0x43, 0x0, 0x44, 0x1, 0x5b, 0xe6, 0xe6, 0x2, 0x1, 0x6, 0x0, 0x99, 0x87, 0x50, 0x1a, 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, 0x2, 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};
void parse_packet(uint8_t const * buf, int length)
{
packet::packet p;
packet::parse(buf, length, &p);
printf("dhcp4 op: %d\n", p.dhcp4->op);
printf("dhcp4 yiaddr: %s\n", ip4::tostr(p.dhcp4->yiaddr));
int dhcp4_options_length = length - (packet::dhcp4_offset + (offsetof (dhcp4::message, options)));
dhcp4::options options = {};
dhcp4::parse_options(p.dhcp4, dhcp4_options_length, &options);
printf("message type: %d\n", 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()
{
parse_packet(bytes, (sizeof (bytes)));
}

View File

@ -3,7 +3,7 @@
#include <arpa/inet.h>
#include "ipv4.h"
#include "ip4.h"
#include "udp4.h"
namespace udp4 {
@ -22,19 +22,19 @@ namespace udp4 {
hdr->destination_port = destination_port;
hdr->length = htons(payload_length + (sizeof (header)));
ipv4_psuedo_header phdr;
ip4_psuedo_header phdr;
phdr.source_address = source_address;
phdr.destination_address = destination_address;
phdr.zeros = 0;
phdr.protocol = ipv4::protocol::udp;
phdr.protocol = ip4::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);
sum = ip4::checksum_partial(sum, reinterpret_cast<void const *>(&phdr), (sizeof (ip4_psuedo_header)));
sum = ip4::checksum_partial(sum, reinterpret_cast<void const *>(hdr), (sizeof (header)));
sum = ip4::checksum_partial(sum, reinterpret_cast<void const *>(payload), payload_length);
hdr->checksum = htons(ipv4::checksum_finish(sum));
hdr->checksum = htons(ip4::checksum_finish(sum));
return hdr;
}

4
udp4.h
View File

@ -2,7 +2,7 @@
#include <stdint.h>
#include "ipv4.h"
#include "ip4.h"
namespace udp4 {
struct header {
@ -14,7 +14,7 @@ namespace udp4 {
static_assert((sizeof (header)) == 8);
struct ipv4_psuedo_header {
struct ip4_psuedo_header {
uint32_t source_address;
uint32_t destination_address;
uint8_t zeros;