commit 4024bcf42fac1e5c2fad4058f281a72c88b872ae Author: Zack Buhman Date: Tue Aug 18 23:53:39 2026 -0500 initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d866725 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +*.out +.gdb_history \ No newline at end of file diff --git a/bpf.asm b/bpf.asm new file mode 100644 index 0000000..38d9e9d --- /dev/null +++ b/bpf.asm @@ -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 */ diff --git a/client.cpp b/client.cpp new file mode 100644 index 0000000..b47151d --- /dev/null +++ b/client.cpp @@ -0,0 +1,233 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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(&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(&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(&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(&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); +} diff --git a/dhcp4.cpp b/dhcp4.cpp new file mode 100644 index 0000000..5359230 --- /dev/null +++ b/dhcp4.cpp @@ -0,0 +1,67 @@ +#include + +#include + +#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(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; + } +} diff --git a/dhcp4.h b/dhcp4.h new file mode 100644 index 0000000..fd4ef45 --- /dev/null +++ b/dhcp4.h @@ -0,0 +1,71 @@ +#pragma once + +#include +#include + +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); +} diff --git a/eth.h b/eth.h new file mode 100644 index 0000000..5fc2a40 --- /dev/null +++ b/eth.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace eth { + struct header { + uint8_t destination_address[6]; + uint8_t source_address[6]; + uint16_t ether_type; + }; +} diff --git a/ipv4.cpp b/ipv4.cpp new file mode 100644 index 0000000..7b7bc02 --- /dev/null +++ b/ipv4.cpp @@ -0,0 +1,70 @@ +#include +#include +#include + +#include + +#include "ipv4.h" + +namespace ipv4 { + + uint32_t checksum_partial(uint32_t sum, void const * src, int length) + { + uint8_t const * addr = reinterpret_cast(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(~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
(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; + } +} diff --git a/ipv4.h b/ipv4.h new file mode 100644 index 0000000..bf52273 --- /dev/null +++ b/ipv4.h @@ -0,0 +1,37 @@ +#pragma once + +#include + +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, + }; + }; +} diff --git a/test.cpp b/test.cpp new file mode 100644 index 0000000..b6c5184 --- /dev/null +++ b/test.cpp @@ -0,0 +1,64 @@ +#include +#include +#include + +#include + +#include "udp4.h" +#include "ipv4.h" +#include "dhcp4.h" + +void dump_hex(void const * src, int length) +{ + uint8_t const * u8 = reinterpret_cast(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(&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(&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(&buf[offset]), + dhcp4::op::bootrequest, + transaction_id, + dhcp4::message_type::dhcprequest); + dump_hex(&buf[offset], dhcp4_length); + offset += dhcp4_length; +} diff --git a/udp4.cpp b/udp4.cpp new file mode 100644 index 0000000..8a38d03 --- /dev/null +++ b/udp4.cpp @@ -0,0 +1,41 @@ +#include +#include + +#include + +#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
(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(&phdr), (sizeof (ipv4_psuedo_header))); + sum = ipv4::checksum_partial(sum, reinterpret_cast(hdr), (sizeof (header))); + sum = ipv4::checksum_partial(sum, reinterpret_cast(payload), payload_length); + + hdr->checksum = htons(ipv4::checksum_finish(sum)); + + return hdr; + } +} diff --git a/udp4.h b/udp4.h new file mode 100644 index 0000000..628ee9f --- /dev/null +++ b/udp4.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#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); +}