diff --git a/CHANGELOG.md b/CHANGELOG.md index d1d530d..fc1755b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## 0.4.3 - 2019-03-19 +### Added +- DHCP option names are printed along with their option codes. + +### Changed +- Debug output is more detailed and aligned. + +### Fixed +- Alignment and explicit data type conversions are used to compile without + errors on 32-bit architectures. +- Do not fail on strict-overflow warnings, as some may be ignored. +- Do not use non-ASCII characters in debug output. They were not strictly + needed. + ## 0.4.2 - 2019-01-17 ### Changed - Change usage string to reflect formatting used by man page. diff --git a/CMakeLists.txt b/CMakeLists.txt index 9fb3cd2..81f3e4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,36 @@ set(HEADERS ) add_executable(dhcpoptinj ${SOURCES} ${HEADERS}) set_property(TARGET dhcpoptinj PROPERTY C_STANDARD 99) -target_compile_options(dhcpoptinj PRIVATE -Wall -Wextra -pedantic -Wcast-align -Wcast-qual -Wdisabled-optimization -Wformat=2 -Winit-self -Wlogical-op -Wmissing-declarations -Wmissing-include-dirs -Wredundant-decls -Wshadow -Wsign-conversion -Wstrict-overflow=5 -Wswitch-default -Wundef -Werror -Wno-unused -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -fstack-protector -Wwrite-strings -D_POSIX_SOURCE -D_DEFAULT_SOURCE -D_FORTIFY_SOURCE=2) +target_compile_options(dhcpoptinj PRIVATE + -Wall + -Wextra + -pedantic + -Wcast-align + -Wcast-qual + -Wdisabled-optimization + -Wformat=2 + -Winit-self + -Wlogical-op + -Wmissing-declarations + -Wmissing-include-dirs + -Wredundant-decls + -Wshadow + -Wsign-conversion + -Wstrict-overflow=5 + -Wno-error=strict-overflow + -Wswitch-default + -Wundef + -Werror + -Wno-unused + -Wmissing-prototypes + -Wstrict-prototypes + -Wold-style-definition + -fstack-protector + -Wwrite-strings + -D_POSIX_SOURCE + -D_DEFAULT_SOURCE + -D_FORTIFY_SOURCE=2 + ) find_library(NFQ_LIB netfilter_queue REQUIRED) target_link_libraries(dhcpoptinj ${NFQ_LIB}) diff --git a/docs/dhcpoptinj.8 b/docs/dhcpoptinj.8 deleted file mode 100644 index a07de3b..0000000 --- a/docs/dhcpoptinj.8 +++ /dev/null @@ -1,222 +0,0 @@ -'\" t -.\" -.\" Copyright © 2019 Andreas Misje -.\" -.\" This file is part of dhcpoptinj. -.\" -.\" dhcpoptinj is free software: you can redistribute it and/or modify it under -.\" the terms of the GNU General Public License as published by the Free -.\" Software Foundation, either version 3 of the License, or (at your option) -.\" any later version. -.\" -.\" dhcpoptinj is distributed in the hope that it will be useful, but WITHOUT -.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -.\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -.\" more details. -.\" -.\" You should have received a copy of the GNU General Public License along -.\" with dhcpoptinj. If not, see . -.\" -. -.Dd February 10, 2019 -.Dt DHCPOPTINJ 8 -.Os -.Sh NAME -.Nm dhcpoptinj -.Nd DHCP option injector, a tool to manipulate DHCP packet options -.Sh SYNOPSIS -.Nm -.Bk -.Op Fl df -.Ek -.Bk -.Op Fl Fl forward-on-fail -.Ek -.Bk -.Op Fl i Ns | Ns Fl r -.Ek -.Bk -.Op Fl p Op Ar pid_file -.Ek -.Bk -.Fl q Ar queue_num -.Ek -.Bk -.Fl o Ar dhcp_option -.Ek -.Bk -.Op Po Fl o Ar dhcp_option Pc ... -.Ek -.Bk -.Nm -.Fl h Ns | Ns Fl v -.Ek -.Sh DESCRIPTION -.Nm -is a fairly simple tool for manipulating DHCP options in a BOOTP/DHCP packet. -It relies on netfilter queue and nftables/iptables rules, in which you specify what packets to send to -.Nm . -.Nm -waits for packets to arrive in a netfilter queue. -It will ensure that a packet is in fact a BOOTP/DHCP packet, and if so proceed to inject or replace options. -It will recalculate the IPv4 header checksum, disable the UDP checksum (for a simpler implementation) and then give the packet back to netfilter. -.Pp -Based on the options -.Fl Fl ignore-existing-opt -and -.Fl Fl remove-existing-opt -.Nm -may be configured to replace matching options or leave them in place. -.Fl Fl forward-on-fail -can be used to accept the packet (leaving it unaltered) if the package mangling should fail for any reason. -.Ss Invocation -.Nm -incorrectly requires flags to run. -The netfilter queue number -.Pq Fl Fl queue -is necessary, and also at least one DHCP option -.Pq Fl Fl option . -See -.Sx EXAMPLES . -.Ss Argument list processing -.Nm -is invoked only with options and no arguments: -.Bl -tag -width ".Fl r, .Fl .Fl remove-existing-opt" -offset indent -.It Fl d , Fl Fl debug -Make -.Nm -tell you as much as possible about what it does and tries to do. -.It Fl f , Fl Fl foreground -Prevent -.Nm -from running in the background. -.It "\ \ \ " Fl Fl forward-on-fail -If the process of injecting options should fail, let the unaltered DHCP packet pass through. -The default behaviour is to drop the packet if options could not be injected. -.It Fl h , Fl Fl help -Print a help text. -.It Fl i , Fl Fl ignore-existing-opt -Proceed if an injected option already exists in the original packet. -Unless -.Fl Fl remove-existing-opt -is provided, the default behaviour is to drop the packet. -.It Fl o , Fl Fl option Ar dhcp_option -DHCP option to inject as a hex string, where the first byte indicates the option code. -The option length field is automatically calculated and must be omitted. -Several options may be injected. -.Pp -The hex string may be delimited by non-hexadecimal characters for readability. -The following hex strings all produce the same result: '01 02 03', '01:02:03', '010203', '01 <-> 02 <-> 03'. -Remember to quote these arguments if they contains spaces or other characters that have special meaning to your shell. -.It Fl p , Fl Fl pid-file Op Pa pid_file -Write PID to file, using specified path or a default sensible location. -.It Fl q , Fl Fl queue Ar queue_num -The netfilter queue number to use -.It Fl r , Fl Fl remove-existing-opt -Remove existing DHCP options of the same kind as those to be injected. -.It Fl v , Fl Fl version -Display version. -.El -.Pp -.Ss DHCP options -All the DHCP options specified with the -.Fl o Ns / Ns Fl Fl option -flag will be added before the terminating option (end option, 255). -The packet is padded if necessary and sent back to netfilter. -The IPv4 header checksum is recalculated, but the UDP checksum is set to 0 (disabled). -None of the added options are checked for whether they are valid, or whether the option codes are valid. -Options are currently not (automatically) padded individually, but they can be manually padded by adding options with code 0 (one pad byte per option). -This special option is the only option that does not have any payload (the end option, 255, is inserted automatically and cannot be manually added). -Padding individual options should not be necessary. -.Pp -The option hex string is written as a series of two-digit pairs, -optionally delimited by one or more non-hexadecimal characters: '466A6173','46 6A 61 73', '46:6A:61:73' etc. -There is a maximum limit of 256 bytes per option, excluding the option code (the first byte) and the automatically inserted length byte. -At least one option must be provided. -.Pp -If the packet already contains a DHCP option that is to be injected (matched by code), the behaviour depends on the command line options -.Fl Fl ignore-existing-opt -and -.Fl Fl remove-existing-opt : -.Bl -tag -width ".Pq none" -offset indent -.It Pq none -The packet will be dropped. -.It Fl i -The existing options are ignored and the injected options are added. -.It Fl r -Any existing options are removed and the injected options are added. -.El -.Pp -.Em Note -that injected options will not be injected in the same place as those that may have been removed if using -.Fl r . -However, this should not matter. -.Sh EXIT STATUS -.Ex -std dhcpoptinj -.Sh EXAMPLES -Let us say you have two interfaces bridged together, eth0 and eth1. -Let us say you want to intercept all BOOTP requests coming from eth0 and inject the relay agent information option (82/0x52). -Let us make up a silly payload: An agent circuit ID sub-option with the value "Fjas". -.Pp -Add a rule to the iptables mangle table: -.Pp -.Dl sudo iptables -t mangle -A PREROUTING -m physdev --physdev-in eth0 -p udp --dport 67 -j NFQUEUE --queue-num 42 -.Pp -Then run -.Nm -(let us run it in the foreground with extra debug output): -.Pp -.Dl sudo dhcpoptinj -d -f -q 42 -o'52 01 04 46 6A 61 73' -.Pp -.Em Note -that -.Nm -must be run by a user with the CAP_NET_ADMIN capability. -You do not need to, and you really should not run -.Nm -as root. -Instead, you can for instance grant the CAP_NET_ADMIN capability to the binary (using -.Em setcap ) -and limit execution rights to only a specific user or group. -This is a method used for running Wireshark as non-root, so you will find several guides helping you accomplish this. -.Pp -Now send a DHCP packet to the eth0 interface and watch it (using a tool like Wireshark -.Pq Lk https://\:www.wireshark.org/ -having been modified when it reaches the bridged interface. -It should have the injected option at the end of the option list. -If you capture the incoming DHCP packet with Wireshark, it will appear unmodified although it will in fact be mangled. -.Pp -Note the format of the argument to the -.Fl o -option: It should be a hexadecimal string starting with the DHCP option code followed by the option payload. -The option length (the byte that normally follows the option code) is automatically calculated and must not be specified. -The hex string can be delimited by non-hexadecimal characters for readability. -All options must have a payload, except for the special pad option -.Pq Lk https://\:tools.ietf.org/\:html/\:rfc2132#section-2 -(code 0). -.Pp -The layout of the nonsensical option used in this example, 52 01 04 46 6A 61 73, (first the DHCP option layout -.Pq Lk https://\:tools.ietf.org/\:html/\:rfc2132#section-2 , -then the specific relay agent information option sub-option layout -.Pq Lk https://tools.ietf.org/html/rfc3046#section-2.0 ) -is as follows: -.Pp -.TS -allbox tab(;); -c c c -c c c. -Code;Length;Data -52;(auto);01 04 46 6A 61 73 ("Fjas") -.TE -.Pp -.TS -allbox tab(;); -c c c -c c c. -Sub-opt.;Lenth;Data -01;4;46 6A 61 73 ("Fjas") -.TE -.Sh SEE ALSO -iptables(8), nftables(8), dhcp-options(5) -.Sh AUTHORS -.An "Andreas Misje" Aq amisje@gmail.com diff --git a/src/config.c b/src/config.c index c4c1419..23af516 100644 --- a/src/config.c +++ b/src/config.c @@ -252,11 +252,11 @@ static struct Config *createDefaultConfig(void) static void printUsage(const char *programName) { - int progNameLen = strlen(programName); + int progNameLen = (int)strlen(programName); printf( "%s – DHCP option injector\n" "Usage: %s [-df] [--forward-on-fail] [-i|-r] [-p [pid_file]] \n" - " %*s -q queue_num -o dhcp_option [(-o dhcp_option) …]\n" + " %*s -q queue_num -o dhcp_option [(-o dhcp_option) ...]\n" " %s -h|-v\n" , programName, diff --git a/src/dhcp.c b/src/dhcp.c index 4d4b04a..0635e8c 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -43,3 +43,184 @@ const char *dhcp_msgTypeString(uint8_t msgType) return "??"; } } + +const char *dhcp_optionString(uint8_t option) +{ + // From https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml + static const char * const names[] = + { + [0] = "Pad", + [1] = "Subnet Mask", + [2] = "Time Offset", + [3] = "Router", + [4] = "Time Server", + [5] = "Name Server", + [6] = "Domain Server", + [7] = "Log Server", + [8] = "Quotes Server", + [9] = "LPR Server", + [10] = "Impress Server", + [11] = "RLP Server", + [12] = "Hostname", + [13] = "Boot File Size", + [14] = "Merit Dump File", + [15] = "Domain Name", + [16] = "Swap Server", + [17] = "Root Path", + [18] = "Extension File", + [19] = "Forward On/Off", + [20] = "SrcRte On/Off", + [21] = "Policy Filter", + [22] = "Max DG Assembly", + [23] = "Default IP TTL", + [24] = "MTU Timeout", + [25] = "MTU Plateau", + [26] = "MTU Interface", + [27] = "MTU Subnet", + [28] = "Broadcast Address", + [29] = "Mask Discovery", + [30] = "Mask Supplier", + [31] = "Router Discovery", + [32] = "Router Request", + [33] = "Static Route", + [34] = "Trailers", + [35] = "ARP Timeout", + [36] = "Ethernet", + [37] = "Default TCP TTL", + [38] = "Keepalive Time", + [39] = "Keepalive Data", + [40] = "NIS Domain", + [41] = "NIS Servers", + [42] = "NTP Servers", + [43] = "Vendor Specific", + [44] = "NETBIOS Name Srv", + [45] = "NETBIOS Dist Srv", + [46] = "NETBIOS Node Type", + [47] = "NETBIOS Scope", + [48] = "X Window Font", + [49] = "X Window Manager", + [50] = "Address Request", + [51] = "Address Time", + [52] = "Overload", + [53] = "DHCP Msg Type", + [54] = "DHCP Server Id", + [55] = "Parameter List", + [56] = "DHCP Message", + [57] = "DHCP Max Msg Size", + [58] = "Renewal Time", + [59] = "Rebinding Time", + [60] = "Class Id", + [61] = "Client Id", + [62] = "NetWare/IP Domain", + [63] = "NetWare/IP Option", + [64] = "NIS-Domain-Name", + [65] = "NIS-Server-Addr", + [66] = "Server-Name", + [67] = "Bootfile-Name", + [68] = "Home-Agent-Addrs", + [69] = "SMTP-Server", + [70] = "POP3-Server", + [71] = "NNTP-Server", + [72] = "WWW-Server", + [73] = "Finger-Server", + [74] = "IRC-Server", + [75] = "StreetTalk-Server", + [76] = "STDA-Server", + [77] = "User-Class", + [78] = "Directory Agent", + [79] = "Service Scope", + [80] = "Rapid Commit", + [81] = "Client FQDN", + [82] = "Relay Agent Information", + [83] = "iSNS", + // 84 removed/unassigned + [85] = "NDS Servers", + [86] = "NDS Tree Name", + [87] = "NDS Context", + [88] = "BCMCS Controller Domain Name list", + [89] = "BCMCS Controller IPv4 address option", + [90] = "Authentication", + [91] = "client-last-transaction-time option", + [92] = "associated-ip option", + [93] = "Client System", + [94] = "Client NDI", + [95] = "LDAP", + // 96 removed/unassigned + [97] = "UUID/GUID", + [98] = "User-Auth", + [99] = "GEOCONF_CIVIC", + [100] = "PCode", + [101] = "TCode", + // 102–108 removed/unassigned + [109] = "OPTION_DHCP4O6_S46_SADDR", + // 110 removed/unassigned + // 111 removed/unassigned + [112] = "Netinfo Address", + [113] = "Netinfo Tag", + [114] = "URL", + // 115 removed/unassigned + [116] = "Auto-Config", + [117] = "Name Service Search", + [118] = "Subnet Selection Option", + [119] = "Domain Search", + [120] = "SIP Servers DHCP Option", + [121] = "Classless Static Route Option", + [122] = "CCC", + [123] = "GeoConf Option", + [124] = "V-I Vendor Class", + [125] = "V-I Vendor-Specific Information", + // 126 removed/unassigned + // 127 removed/unassigned + [128] = "PXE / Etherboot signature", + [129] = "PXE / Kernel options / Call Server IP address", + [130] = "PXE / Ethernet interface / Discrimination string", + [131] = "PXE / Remote statistics server IP address", + [132] = "PXE", + [133] = "PXE", + [134] = "PXE", + [135] = "PXE / HTTP Proxy for phone-specific applications", + [136] = "OPTION_PANA_AGENT", + [137] = "OPTION_V4_LOST", + [138] = "OPTION_CAPWAP_AC_V4", + [139] = "OPTION-IPv4_Address-MoS", + [140] = "OPTION-IPv4_FQDN-MoS", + [141] = "SIP UA Configuration Service Domains", + [142] = "OPTION-IPv4_Address-ANDSF", + [143] = "OPTION_V4_SZTP_REDIRECT", + [144] = "GeoLoc", + [145] = "FORCERENEW_NONCE_CAPABLE", + [146] = "RDNSS Selection", + // 147–149 unassigned + [150] = "TFTP server address / Etherboot / GRUB configuration path name", + [151] = "status-code", + [152] = "base-time", + [153] = "start-time-of-state", + [154] = "query-start-time", + [155] = "query-end-time", + [156] = "dhcp-state", + [157] = "data-source", + [158] = "OPTION_V4_PCP_SERVER", + [159] = "OPTION_V4_PORTPARAMS", + [160] = "DHCP Captive-Portal", + [161] = "OPTION_MUD_URL_V4", + // 162–174 unassigned + [175] = "Etherboot", + [176] = "IP Telephone", + [177] = "Etherboot / PacketCable and CableHome", + // 178–207 unassigned + [208] = "PXELINUX Magic", + [209] = "Configuration File", + [210] = "Path Prefix", + [211] = "Reboot Time", + [212] = "OPTION_6RD", + [213] = "OPTION_V4_ACCESS_DOMAIN", + // 214–219 unassigned + [220] = "Subnet Allocation Option", + [221] = "Virtual Subnet Selection (VSS) Option", + // 222–223 unassigned + // 224–254 reserved + [255] = "End", + }; + + return names[option] ? names[option] : "(unassigned/reserved)"; +} diff --git a/src/dhcp.h b/src/dhcp.h index 12772fc..7e5923b 100644 --- a/src/dhcp.h +++ b/src/dhcp.h @@ -27,6 +27,7 @@ #define DHCPOPT_END 0xff #define DHCPOPT_TYPE 0x35 +#pragma pack(4) struct BootP { uint8_t op; @@ -45,15 +46,17 @@ struct BootP uint8_t file[128]; uint32_t cookie; // options … -} __attribute__((packed)); +}; struct DHCPOption { uint8_t code; uint8_t length; uint8_t data[]; -} __attribute__((packed)); +}; +#pragma pack() const char *dhcp_msgTypeString(uint8_t msgType); +const char *dhcp_optionString(uint8_t option); #endif // DHCPOPTINJ_DHCP_H diff --git a/src/dhcpoptinj.c b/src/dhcpoptinj.c index fcb4703..48cd5c3 100644 --- a/src/dhcpoptinj.c +++ b/src/dhcpoptinj.c @@ -38,6 +38,7 @@ #include "ipv4.h" #include "udp.h" #include "dhcp.h" +#include #define MIN_BOOTP_SIZE 300 @@ -56,10 +57,10 @@ enum MangleResult }; /* Somewhat arbitrary, feel free to change */ -static const int maxPacketSize = 2048; +static const uint32_t maxPacketSize = 2048; /* The netfilter queue length 20 is also arbitrary. Hopefully it is * sufficient. */ -static const int maxQueueLen = 20; +static const uint32_t maxQueueLen = 20; static struct Config *config; static bool daemonised; static sig_atomic_t escapeMainLoop; @@ -92,7 +93,9 @@ static void inspectOptions(void); /* Debug-print packet header */ static void debugLogPacketHeader(const uint8_t *data, size_t size); /* Debug-print packet's existing DHCP options */ -static void debugLogOption(const struct DHCPOption *option); +static void debugLogOptionFound(const struct DHCPOption *option); +static void debugLogOption(const char *action, const struct DHCPOption *option); +static void debugLogInjectedOptions(void); int main(int argc, char *argv[]) { @@ -103,7 +106,7 @@ int main(int argc, char *argv[]) debugLogOptions(); inspectOptions(); - logMessage(LOG_DEBUG, "Initialising netfilter queue …\n"); + logMessage(LOG_DEBUG, "Initialising netfilter queue\n"); struct nfq_handle *nfq = nfq_open(); if (!nfq) @@ -148,7 +151,7 @@ int main(int argc, char *argv[]) if (!config->foreground) { - logMessage(LOG_DEBUG, "Daemonising …\n"); + logMessage(LOG_DEBUG, "Daemonising\n"); if (daemon(false, false)) { logMessage(LOG_ERR, "Failed to daemonise: daemon() failed: %s\n", @@ -163,7 +166,8 @@ int main(int argc, char *argv[]) initSignalHandler(); if (config->debug) - logMessage(LOG_DEBUG, "Initialisation completed. Waiting for packets to mangle …\n"); + logMessage(LOG_DEBUG, "Initialisation completed. Waiting for packets to " + "mangle on queue %" PRIu16 "\n", config->queue); else logMessage(LOG_INFO, "Started\n"); @@ -197,14 +201,14 @@ int main(int argc, char *argv[]) logMessage(LOG_NOTICE, "Caught signal %s\n", signalName); } - logMessage(LOG_DEBUG, "Destroying netfilter queue …\n"); + logMessage(LOG_DEBUG, "Destroying netfilter queue\n"); nfq_destroy_queue(queue); /* According to libnetfilter_queue's nfqnl_test.c example, nfq_unbind_pf(…) * should NOT be called during clean up. */ nfq_close(nfq); - logMessage(LOG_NOTICE, "Exiting …\n"); + logMessage(LOG_NOTICE, "Exiting\n"); removePIDFile(); destroyConfig(); @@ -277,7 +281,7 @@ static int inspectPacket(struct nfq_q_handle *queue, struct nfgenmsg *pktInfo, } if (config->debug) - logMessage(LOG_DEBUG, "Sending mangled packet …\n"); + logMessage(LOG_DEBUG, "Sending mangled packet\n"); int res = nfq_set_verdict(queue, ntohl(metaHeader->packet_id), NF_ACCEPT, mangledDataSize, mangledData); @@ -369,7 +373,7 @@ static enum MangleResult manglePacket(const uint8_t *origData, size_t origDataSi if (padding && config->debug) logMessage(LOG_DEBUG, "Padding with %zu byte(s) to meet minimal BOOTP payload " - "size …\n", padding); + "size\n", padding); /* Pad to (at least) MIN_BOOTP_SIZE bytes: */ for (size_t i = *newDataSize - padding; i < *newDataSize; ++i) @@ -401,7 +405,7 @@ static enum MangleResult mangleOptions(const uint8_t *origData, size_t origDataS if (padCount) logMessage(LOG_DEBUG, "Found %zu PAD options (removing)\n", padCount); - debugLogOption(option); + debugLogOptionFound(option); padCount = 0; } } @@ -457,7 +461,7 @@ static enum MangleResult mangleOptions(const uint8_t *origData, size_t origDataS } if (config->debug) - logMessage(LOG_DEBUG, "Injecting %zu option(s) …\n", config->dhcpOptCodeCount); + debugLogInjectedOptions(); /* Inject DHCP options: */ for (size_t i = 0; i < config->dhcpOptsSize; ++i) @@ -466,7 +470,7 @@ static enum MangleResult mangleOptions(const uint8_t *origData, size_t origDataS newOffset += config->dhcpOptsSize; if (config->debug) - logMessage(LOG_DEBUG, "Inserting END option …\n"); + logMessage(LOG_DEBUG, "Inserting END option\n"); /* Finally insert the END option: */ newData[newOffset++] = DHCPOPT_END; @@ -514,7 +518,7 @@ static void writePID(void) return; pid_t pid = getpid(); - logMessage(LOG_DEBUG, "Writing PID %ld to %s …\n", (long)pid, config->pidFile); + logMessage(LOG_DEBUG, "Writing PID %ld to %s\n", (long)pid, config->pidFile); FILE *f = fopen(config->pidFile, "w"); if (!f) @@ -531,7 +535,7 @@ static void removePIDFile(void) { if (config->pidFile) { - logMessage(LOG_DEBUG, "Removing PID file %s …\n", config->pidFile); + logMessage(LOG_DEBUG, "Removing PID file %s\n", config->pidFile); unlink(config->pidFile); } } @@ -543,7 +547,7 @@ static void destroyConfig(void) static void initSignalHandler(void) { - logMessage(LOG_DEBUG, "Initialising signal handler …\n"); + logMessage(LOG_DEBUG, "Initialising signal handler\n"); struct sigaction sigAction = { .sa_handler = &setEscapeMainLoopFlag }; @@ -584,7 +588,8 @@ static void debugLogOptions(void) uint8_t code = config->dhcpOptCodes[i]; bool atEnd = i == config->dhcpOptCodeCount - 1; const char *delim = atEnd ? "\n" : ", "; - logMessage(LOG_DEBUG, "0x%02X (%u)%s", code, code, delim); + logMessage(LOG_DEBUG, "%u (0x%02X) (%s)%s", code, code, dhcp_optionString( + code), delim); } } @@ -617,14 +622,15 @@ static void debugLogPacketHeader(const uint8_t *data, size_t size) const struct IPAddr *destIP = (const struct IPAddr *)&packet->ipHeader.destAddr; logMessage(LOG_DEBUG, "Inspecting %zu-byte DHCP packet from " - "%02X:%02X:%02X:%02X:%02X:%02X to %d.%d.%d.%d …\n", + "%02X:%02X:%02X:%02X:%02X:%02X to %d.%d.%d.%d:%d\n", size, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], - destIP->o1, destIP->o2, destIP->o3, destIP->o4 + destIP->o1, destIP->o2, destIP->o3, destIP->o4, + ntohs(packet->udpHeader.destPort) ); } -static void debugLogOption(const struct DHCPOption *option) +static void debugLogOptionFound(const struct DHCPOption *option) { if (option->code == DHCPOPT_PAD) return; @@ -632,22 +638,47 @@ static void debugLogOption(const struct DHCPOption *option) logMessage(LOG_DEBUG,"Found END option %s\n", config->dhcpOptCodeCount ? "(removing)" : "(copying)"); else if (option->code == DHCPOPT_TYPE && option->length == 1) - logMessage(LOG_DEBUG, "Found option %hhu (0x%02hhX) (DHCP message type): %s", + logMessage(LOG_DEBUG, "Found option %' '3hhu (0x%02hhX) (DHCP message type) %s", option->code, option->code, dhcp_msgTypeString(option->data[0])); else + debugLogOption("Found", option); +} + +static void debugLogOption(const char *action, const struct DHCPOption *option) +{ + /* String buffer for hex string (maximum DHCP option length (256) times + * three characters (two digits and a space)) */ + char optPayload[256 * 3]; + size_t i = 0; + for (; i < option->length; ++i) + sprintf(optPayload + 3*i, "%02X ", option->data[i]); + + /* Remove last space: */ + if (i) + optPayload[3*i - 1] = '\0'; + + const char *optName = dhcp_optionString(option->code); + size_t optNameLen = strlen(optName); + const size_t alignedWidth = 24; + logMessage(LOG_DEBUG, "%s option %' '3hhu (0x%02hhX) (%s)%*s with %' '3u-byte payload %s", + action, + option->code, + option->code, + optName, + optNameLen > alignedWidth ? 0 : alignedWidth - optNameLen, + "", + option->length, + optPayload); +} + +static void debugLogInjectedOptions(void) +{ + for (size_t offset = 0; offset < config->dhcpOptsSize;) { - /* String buffer for hex string (maximum DHCP option length (256) times - * three characters (two digits and a space)) */ - char optPayload[256 * 3]; - size_t i = 0; - for (; i < option->length; ++i) - sprintf(optPayload + 3*i, "%02X ", option->data[i]); - - /* Remove last space: */ - if (i) - optPayload[3*i - 1] = '\0'; - - logMessage(LOG_DEBUG, "Found option %hhu (0x%02hhX) with %' '3u-byte payload %s", - option->code, option->code, option->length, optPayload); + const struct DHCPOption *option = (const struct DHCPOption *)(&config->dhcpOpts[offset]); + debugLogOption("Injecting", option); + logMessage(LOG_DEBUG, "%s", "\n"); + offset += option->code == DHCPOPT_PAD || option->code == DHCPOPT_END ? 1 + : sizeof(struct DHCPOption) + option->length; } } diff --git a/src/ipv4.h b/src/ipv4.h index 8b3ee07..00141b4 100644 --- a/src/ipv4.h +++ b/src/ipv4.h @@ -24,6 +24,7 @@ #include #include +#pragma pack(2) struct IPv4Header { uint8_t verIHL; @@ -36,7 +37,8 @@ struct IPv4Header uint16_t checksum; uint32_t sourceAddr; uint32_t destAddr; -} __attribute__((packed)); +}; +#pragma pack() uint16_t ipv4_checksum(const struct IPv4Header *ipv4Header); size_t ipv4_headerLen(const struct IPv4Header *ipv4Header); diff --git a/src/udp.h b/src/udp.h index 879c483..d3e98c2 100644 --- a/src/udp.h +++ b/src/udp.h @@ -22,12 +22,14 @@ #include +#pragma pack(2) struct UDPHeader { uint16_t sourcePort; uint16_t destPort; uint16_t length; uint16_t checksum; -} __attribute__((packed)); +}; +#pragma pack() #endif // DHCPOPTINJ_UDP_H