From 0f8043893d15bb0975fcd28b61a34fae313c3c1a Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Fri, 21 Aug 2026 22:53:45 -0500 Subject: [PATCH] factor out client/server-agnostic code --- Makefile | 3 +- client.cpp | 208 ++++++++++------------------------------------------ dhcp4.cpp | 63 +++++++++++----- dhcp4.h | 12 ++- interface.h | 2 + packet.cpp | 6 +- packet.h | 2 +- sock.cpp | 192 ++++++++++++++++++++++++++++++++++++++++++++++++ sock.h | 21 ++++++ 9 files changed, 316 insertions(+), 193 deletions(-) create mode 100644 sock.cpp create mode 100644 sock.h diff --git a/Makefile b/Makefile index 1aa4c67..b0a041b 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,8 @@ OBJS = \ udp4.o \ eth.o \ rtnetlink.o \ - interface.o + interface.o \ + sock.o all: client test2 diff --git a/client.cpp b/client.cpp index 608280a..6f571b7 100644 --- a/client.cpp +++ b/client.cpp @@ -7,13 +7,7 @@ #include #include -#include -#include #include -#include -#include -#include -#include #include "ip4.h" #include "udp4.h" @@ -21,177 +15,46 @@ #include "eth.h" #include "packet.h" #include "interface.h" - -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); - } -} +#include "sock.h" 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); - } - int ifa_index = if_index.ifr_ifindex; - printf("interface index %d\n", ifa_index); - - ////////////////////////////////////////////////////////////////////// - // 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_ll = { - .sll_family = AF_PACKET, - .sll_protocol = htons(ETH_P_IP), - .sll_ifindex = ifa_index, - }; - int bret_ll = bind(s, (struct sockaddr *)&bind_addr_ll, (sizeof (struct sockaddr_ll))); - if (bret_ll < 0) { - perror("bind ll"); - exit(EXIT_FAILURE); - } - attach_dhcp_filter(s); - - ////////////////////////////////////////////////////////////////////// - // fake socket - ////////////////////////////////////////////////////////////////////// - - int sf; - { - sf = socket(AF_INET, SOCK_DGRAM, 0); - if (sf < 0) { - perror("socket"); - exit(EXIT_FAILURE); - } - - // this has the effect of inhibiting linux's generation of icmp - // "destination unreachable" packets - struct sockaddr_in bind_addr_in = { - .sin_family = AF_INET, - .sin_port = htons(68), - .sin_addr = { .s_addr = INADDR_ANY }, - }; - int bret_in = bind(sf, (struct sockaddr *)&bind_addr_in, (sizeof (struct sockaddr_in))); - if (bret_in < 0) { - perror("bind in"); - exit(EXIT_FAILURE); - } - } - - ////////////////////////////////////////////////////////////////////// - // dhcp send discover - ////////////////////////////////////////////////////////////////////// - uint32_t dhcp_transaction_id = rand(); - uint8_t eth_source_address[6] = {}; - memcpy(eth_source_address, if_hwaddr.ifr_hwaddr.sa_data, 6); + static uint8_t buf[16384]; + + 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; - int length = packet::make(buf, - eth_source_address, - eth_destination_address, - ip4_source_address, - ip4_destination_address, - dhcp_transaction_id, - dhcp4::message_type::dhcpdiscover, - 0); + ////////////////////////////////////////////////////////////////////// + // dhcp send discover + ////////////////////////////////////////////////////////////////////// + { + dhcp4::message_parameters params = { + }; - interface_send(s, ifa_index, buf, length, eth_destination_address); + int length = packet::make(buf, + eth_source_address, + eth_destination_address, + ip4_source_address, + ip4_destination_address, + dhcp_transaction_id, + dhcp4::message_type::dhcpdiscover, + ¶ms); + + sock::send(sock, ifa_index, buf, length, eth_destination_address); + } ////////////////////////////////////////////////////////////////////// // dhcp recv offer @@ -205,7 +68,7 @@ int main() struct sockaddr_ll recv_address; int flags = 0; socklen_t socklen = (sizeof (struct sockaddr_ll)); - ssize_t rret = recvfrom(s, recvbuf, (sizeof (recvbuf)), flags, + ssize_t rret = recvfrom(sock, recvbuf, (sizeof (recvbuf)), flags, (struct sockaddr *)&recv_address, &socklen); if (rret < 0) { perror("recvfrom"); @@ -233,6 +96,10 @@ int main() ////////////////////////////////////////////////////////////////////// { + dhcp4::message_parameters params = { + .requested_ip_address = p.dhcp4->yiaddr, + }; + int length = packet::make(buf, eth_source_address, eth_destination_address, @@ -240,9 +107,9 @@ int main() ip4_destination_address, dhcp_transaction_id, dhcp4::message_type::dhcprequest, - p.dhcp4->yiaddr); + ¶ms); - interface_send(s, ifa_index, buf, length, eth_destination_address); + sock::send(sock, ifa_index, buf, length, eth_destination_address); } ////////////////////////////////////////////////////////////////////// @@ -253,7 +120,7 @@ int main() struct sockaddr_ll recv_address; int flags = 0; socklen_t socklen = (sizeof (struct sockaddr_ll)); - ssize_t rret = recvfrom(s, recvbuf, (sizeof (recvbuf)), flags, + ssize_t rret = recvfrom(sock, recvbuf, (sizeof (recvbuf)), flags, (struct sockaddr *)&recv_address, &socklen); if (rret < 0) { perror("recvfrom"); @@ -318,7 +185,6 @@ int main() ////////////////////////////////////////////////////////////////////// close(dfd); - close(s); - close(sf); + close(sock); exit(EXIT_SUCCESS); } diff --git a/dhcp4.cpp b/dhcp4.cpp index b98b954..8a78f4e 100644 --- a/dhcp4.cpp +++ b/dhcp4.cpp @@ -8,12 +8,36 @@ namespace dhcp4 { + int add_parameter_request_list(message * msg, int index) + { + uint8_t * opt = &msg->options[index]; + int length = 3; + index += 2 + length; + opt[0] = option::parameter_request_list; + opt[1] = length; + opt[2] = option::subnet_mask; + opt[3] = option::router; + opt[4] = option::domain_name_server; + return index; + } + + int add_uint32(message * msg, int index, uint8_t option, uint32_t value) + { + uint8_t * opt = &msg->options[index]; + int length = 4; + index += 2 + length; + opt[0] = option; + opt[1] = length; + memcpy(&opt[2], &value, 4); + return index; + } + int make_message(void * buf, uint32_t bootp_message_type, uint32_t transaction_id, uint8_t const * chaddr, uint32_t dhcp_message_type, - uint32_t requested_ip_address) + message_parameters const * params) { memset(buf, 0, (sizeof (message))); message * msg = reinterpret_cast(buf); @@ -22,6 +46,10 @@ namespace dhcp4 { msg->htype = htype::ethernet; msg->hlen = 6; msg->xid = htonl(transaction_id); + + msg->yiaddr = params->your_ip_address; + msg->siaddr = params->next_server_ip_address; + memcpy(msg->chaddr, chaddr, 6); int index = 0; @@ -39,24 +67,25 @@ namespace dhcp4 { message_type[1] = 1; // length message_type[2] = dhcp_message_type; - { - uint8_t * prl = &msg->options[index]; - int length = 3; - index += 2 + length; - prl[0] = option::parameter_request_list; - prl[1] = length; - prl[2] = option::subnet_mask; - prl[3] = option::router; - prl[4] = option::domain_name_server; + if (dhcp_message_type == message_type::dhcpdiscover || + dhcp_message_type == message_type::dhcprequest) { + index = add_parameter_request_list(msg, index); } - if (requested_ip_address != 0) { - uint8_t * addr = &msg->options[index]; - int length = 4; - index += 2 + length; - addr[0] = option::requested_ip_address; - addr[1] = length; - memcpy(&addr[2], &requested_ip_address, 4); + if (params->subnet_mask != 0) { + index = add_uint32(msg, index, option::subnet_mask, params->subnet_mask); + } + + if (params->router != 0) { + index = add_uint32(msg, index, option::router, params->router); + } + + if (params->domain_name_server != 0) { + index = add_uint32(msg, index, option::domain_name_server, params->domain_name_server); + } + + if (params->requested_ip_address != 0) { + index = add_uint32(msg, index, option::requested_ip_address, params->requested_ip_address); } uint8_t * end = &msg->options[index]; diff --git a/dhcp4.h b/dhcp4.h index 6a0da42..74c8c42 100644 --- a/dhcp4.h +++ b/dhcp4.h @@ -75,12 +75,22 @@ namespace dhcp4 { }; }; + struct message_parameters { + uint32_t your_ip_address; + uint32_t next_server_ip_address; + uint32_t dhcp_server_identifier; + uint32_t subnet_mask; + uint32_t router; + uint32_t domain_name_server; + uint32_t requested_ip_address; + }; + int make_message(void * buf, uint32_t bootp_message_type, uint32_t transaction_id, uint8_t const * chaddr, uint32_t dhcp_message_type, - uint32_t requested_ip_address); + message_parameters const * params); char const * message_type_tostr(int message_type); diff --git a/interface.h b/interface.h index 6421857..6f77337 100644 --- a/interface.h +++ b/interface.h @@ -10,3 +10,5 @@ namespace interface { uint32_t gateway); } +{ >(/X k5@/X Xo#",nq +/X 23{ >(R< H`H` H`H`D( >(T`&C.N#> 6 diff --git a/packet.cpp b/packet.cpp index c6634c5..a76a298 100644 --- a/packet.cpp +++ b/packet.cpp @@ -68,18 +68,20 @@ namespace packet { uint32_t const ip4_destination_address, uint32_t const dhcp_transaction_id, uint32_t const dhcp_message_type, - uint32_t const requested_ip_address) + dhcp4::message_parameters const * params) { ////////////////////////////////////////////////////////////////////// // dhcp4 ////////////////////////////////////////////////////////////////////// + + int dhcp4_length = dhcp4::make_message(reinterpret_cast(&buf[dhcp4_offset]), dhcp4::op::bootrequest, dhcp_transaction_id, eth_source_address, dhcp_message_type, - requested_ip_address); + params); ////////////////////////////////////////////////////////////////////// // eth diff --git a/packet.h b/packet.h index faed5ed..f6c4006 100644 --- a/packet.h +++ b/packet.h @@ -27,5 +27,5 @@ namespace packet { uint32_t const ip4_destination_address, uint32_t const dhcp_transaction_id, uint32_t const dhcp_message_type, - uint32_t const requested_ip_address); + dhcp4::message_parameters const * params); }; diff --git a/sock.cpp b/sock.cpp new file mode 100644 index 0000000..1cc60f2 --- /dev/null +++ b/sock.cpp @@ -0,0 +1,192 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "sock.h" + +namespace sock { + + ////////////////////////////////////////////////////////////////////// + // socket filter + ////////////////////////////////////////////////////////////////////// + + void attach_udp_filter(int sock, uint32_t destination_port) + { + 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, destination_port }, + { 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); + } + } + + ////////////////////////////////////////////////////////////////////// + // fake socket + ////////////////////////////////////////////////////////////////////// + + int create_fake_socket(int port) { + int sf = socket(AF_INET, SOCK_DGRAM, 0); + if (sf < 0) { + perror("socket"); + exit(EXIT_FAILURE); + } + + // this has the effect of inhibiting linux's generation of icmp + // "destination unreachable" packets + struct sockaddr_in bind_addr_in = { + .sin_family = AF_INET, + .sin_port = htons(port), + .sin_addr = { .s_addr = INADDR_ANY }, + }; + int bret_in = bind(sf, (struct sockaddr *)&bind_addr_in, (sizeof (struct sockaddr_in))); + if (bret_in < 0) { + perror("bind in"); + exit(EXIT_FAILURE); + } + + return sf; + } + ////////////////////////////////////////////////////////////////////// + // create + ////////////////////////////////////////////////////////////////////// + + int create(int port) + { + int sock = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW); + if (sock < 0) { + perror("socket"); + exit(EXIT_FAILURE); + } + + ////////////////////////////////////////////////////////////////////// + // broadcast + ////////////////////////////////////////////////////////////////////// + int enable = 1; + int ret = setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &enable, (sizeof (int))); + if (ret < 0) { + perror("setsockopt(SO_BROADCAST)"); + exit(EXIT_FAILURE); + } + + ////////////////////////////////////////////////////////////////////// + // dhcp_filter + ////////////////////////////////////////////////////////////////////// + attach_udp_filter(sock, port); + + ////////////////////////////////////////////////////////////////////// + // fake socket + ////////////////////////////////////////////////////////////////////// + create_fake_socket(port); + + return sock; + } + + + ////////////////////////////////////////////////////////////////////// + // bind + ////////////////////////////////////////////////////////////////////// + + void bind_interface(int sock, int ifa_index) + { + struct sockaddr_ll bind_addr_ll = { + .sll_family = AF_PACKET, + .sll_protocol = htons(ETH_P_IP), + .sll_ifindex = ifa_index, + }; + int bret_ll = bind(sock, (struct sockaddr *)&bind_addr_ll, (sizeof (struct sockaddr_ll))); + if (bret_ll < 0) { + perror("bind ll"); + exit(EXIT_FAILURE); + } + } + + ////////////////////////////////////////////////////////////////////// + // send + ////////////////////////////////////////////////////////////////////// + + void 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); + } + } + + ////////////////////////////////////////////////////////////////////// + // interface index + ////////////////////////////////////////////////////////////////////// + + int interface_index(int sock, char const * interface_name) + { + struct ifreq if_index; + memset(&if_index, 0, (sizeof (struct ifreq))); + strncpy(if_index.ifr_name, interface_name, IFNAMSIZ - 1); + int iret = ioctl(sock, SIOCGIFINDEX, &if_index); + if (iret < 0) { + perror("ioctl(SIOCGIFINDEX)"); + exit(EXIT_FAILURE); + } + int ifa_index = if_index.ifr_ifindex; + printf("interface index %d\n", ifa_index); + return ifa_index; + } + + ////////////////////////////////////////////////////////////////////// + // interface address + ////////////////////////////////////////////////////////////////////// + + void interface_address(int sock, char const * interface_name, uint8_t * hardware_address) + { + struct ifreq if_hwaddr; + memset(&if_hwaddr, 0, sizeof(struct ifreq)); + strncpy(if_hwaddr.ifr_name, interface_name, IFNAMSIZ - 1); + int hret = ioctl(sock, 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]); + + memcpy(hardware_address, if_hwaddr.ifr_hwaddr.sa_data, 6); + } +} diff --git a/sock.h b/sock.h new file mode 100644 index 0000000..700769e --- /dev/null +++ b/sock.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +namespace sock { + + int create(int port); + + void bind_interface(int sock, int ifa_index); + + void send(int sock, + int interface_index, + void * buf, + int length, + uint8_t const * eth_destination_address); + + int interface_index(int sock, char const * interface_name); + + void interface_address(int sock, char const * interface_name, uint8_t * hardware_address); + +}