365 Days of Code - Day 037

Project Status

ProjectLanguageStatusDue DateLatest Update
Personal WebsiteHugoOngoingNoneThe site is live. There are some TODOs. Need to work on categorization, tagging, and layout improvements.
Laravel From ScratchLaravel (PHP)In-Progress2026-03-31Episode 8
PRMLaravel (PHP)In-Progress2026-03-31Working alongside other Laravel projects.
Client Website (J.L.)Laravel (PHP)In-Progress2026-03-31Working alongside other Laravel projects.
Project EulerCOngoingNoneWorking on P25. BigInt (AI gen) was a waste of time, need to rewrite
Practice JavaJavaPausedNoneInstalled, need to find a good project.
Practice PythonPythonPausedNoneInstalled, need to find a good project.
Learn GoGoPausedNoneInstalled, work on LDAP Injector from ippsec.
Learn RustRustHaven’t StartedNoneInstalled, will try network protocols after finishing in C and Zig.
Learn ElixirElixirHaven’t StartedNoneInstalled, need a good tutorial project.
Learn HaskellHaskellHaven’t StartedNoneInstalled, need a good tutorial project.
Learn ZigZigHaven’t StartedNoneInstalled, will try network protocols after finishing in C.
Linux+N/AIn-Progress2026-03-31Reading Chapter 4.
Cyber Quest 2026N/AIn-Progress2026-02-28Finished quiz 1 with 75%. Need to work on ARP poisoning and timestamp adjustments in WireShark.
Operating SystemsN/AIn-Progress2026-03-31Reading Chapter 4: Abstraction
Grey-Hat HackingVariousIn-Progress2026-03-31Reading Chapter 8: Threat Hunting Lab
PHP Time TrackerPHPBeta FinishedNoneWorking on a basic level. Could use a couple more updates to make it fully functional.
HTTP Status Code ReaderCComplete2026-02-18Complete. Could potentially upgrade for more advanced functions or follow redirects.
ZSH Configurationbash/zshCompleteNoneSort of an ongoing process, but complete for now. Works good.
Network ProtocolsCIn-ProgressNoneIPv4 Datagram Header built. Need to work on payload and ICMP.

Bug Fix - zshrc

I ran into an issue with my zshrc file today. Once again, related to the ssh agent. I use Linux Mint Debian Edition (opens in a new tab) with the Cinnamon DE. In an effort to be “helpful,” Cinnamon launches an SSH Agent for you when you boot up. This conflicted with my script, as my script was overriding the $SSH_AUTH_SOCK with a stale agent. The fix was relatively simple, just check if the $SSH_AUTH_SOCK has already been set.

zsh
###############################################################################
# Configure SSH Agent
###############################################################################
case "$OSTYPE" in
    darwin*)
        # macOS native launchd handles ssh-agent automatically.
        # Just ensure you have `AddKeysToAgent yes` and `UseKeychain yes` in your ~/.ssh/config
        ;;

    linux*)
        # 1. If a valid socket already exists (e.g., provided by Cinnamon), do nothing!
        if [ ! -S "$SSH_AUTH_SOCK" ]; then
            # Setup a persistent SSH agent to prevent zombie processes
            SSH_ENV="$HOME/.ssh/agent-environment"

            function start_agent {
                # Start ssh-agent and write the environment variables to a file
                ssh-agent | sed 's/^echo/#echo/' > "$SSH_ENV"
                chmod 600 "$SSH_ENV"
                . "$SSH_ENV" > /dev/null
            }

            # If the environment file exists, load it
            if [ -f "$SSH_ENV" ]; then
                . "$SSH_ENV" > /dev/null
                # Check if the PID is alive AND if the socket file actually exists
                if ! kill -0 "$SSH_AGENT_PID" 2>/dev/null || ! [ -S "$SSH_AUTH_SOCK" ]; then
                    start_agent
                fi
            else
                start_agent
            fi
        fi
        ;;
esac
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
###############################################################################
# Configure SSH Agent
###############################################################################
case "$OSTYPE" in
    darwin*)
        # macOS native launchd handles ssh-agent automatically.
        # Just ensure you have `AddKeysToAgent yes` and `UseKeychain yes` in your ~/.ssh/config
        ;;

    linux*)
        # 1. If a valid socket already exists (e.g., provided by Cinnamon), do nothing!
        if [ ! -S "$SSH_AUTH_SOCK" ]; then
            # Setup a persistent SSH agent to prevent zombie processes
            SSH_ENV="$HOME/.ssh/agent-environment"

            function start_agent {
                # Start ssh-agent and write the environment variables to a file
                ssh-agent | sed 's/^echo/#echo/' > "$SSH_ENV"
                chmod 600 "$SSH_ENV"
                . "$SSH_ENV" > /dev/null
            }

            # If the environment file exists, load it
            if [ -f "$SSH_ENV" ]; then
                . "$SSH_ENV" > /dev/null
                # Check if the PID is alive AND if the socket file actually exists
                if ! kill -0 "$SSH_AGENT_PID" 2>/dev/null || ! [ -S "$SSH_AUTH_SOCK" ]; then
                    start_agent
                fi
            else
                start_agent
            fi
        fi
        ;;
esac

Network Protocols - ICMP in C

The ICMP messaging protocol is extremely simplistic. It still blows my mind how these structures and paradigms defined decades ago still underpin so much technology that we take for granted. I use pings all the time. It is a critical tool in the toolbelt for network troubleshooting.

Many new pieces were added to my program today.

First, an enum for message types:

c
// ICMP Message Identifiers
enum icmp_message_type
{
  ECHO_REPLY = 0,
  ECHO = 8
};
1
2
3
4
5
6
// ICMP Message Identifiers
enum icmp_message_type
{
  ECHO_REPLY = 0,
  ECHO = 8
};

Second, the ICMP header and ICMP Echo payload structs:

c
// The ICMP Header, modeled from RFC 792
// https://datatracker.ietf.org/doc/html/rfc792
// Every ICMP packet, irregardless of message type, must contain this 4 byte header first
struct icmp_header
{
  uint8_t type;     /* The type of ICMP message, such as `8` for echo, or `0` for echo reply */
  uint8_t code;     /* The code relayed by the message type, varies according to message type */
  uint16_t checksum; /* A ones's complement checksum for the ICMP message */
} __attribute__((packed));

// The ICMP Echo specific data, appended after the baseline ICMP header
struct icmp_echo
{
  uint16_t identifier;
  uint16_t sequence;
} __attribute__((packed));
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// The ICMP Header, modeled from RFC 792
// https://datatracker.ietf.org/doc/html/rfc792
// Every ICMP packet, irregardless of message type, must contain this 4 byte header first
struct icmp_header
{
  uint8_t type;     /* The type of ICMP message, such as `8` for echo, or `0` for echo reply */
  uint8_t code;     /* The code relayed by the message type, varies according to message type */
  uint16_t checksum; /* A ones's complement checksum for the ICMP message */
} __attribute__((packed));

// The ICMP Echo specific data, appended after the baseline ICMP header
struct icmp_echo
{
  uint16_t identifier;
  uint16_t sequence;
} __attribute__((packed));

Third, the checksum was modified to support odd bytes:

c
  // If there is 1 byte remaining, logically pad it with a zero and add it
  if (length == 1)
  {
    uint16_t padded_word = 0;
    // Safely copy the final byte into the first physical byte of our 16-bit word
    ((uint8_t *)&padded_word)[0] = *(const uint8_t *)word_ptr;
    sum += padded_word;
  }
1
2
3
4
5
6
7
8
  // If there is 1 byte remaining, logically pad it with a zero and add it
  if (length == 1)
  {
    uint16_t padded_word = 0;
    // Safely copy the final byte into the first physical byte of our 16-bit word
    ((uint8_t *)&padded_word)[0] = *(const uint8_t *)word_ptr;
    sum += padded_word;
  }

Lastly, the ICMP packet was built, and the IPv4 header modified as needed:

c
  // IPv4 Header Testing is complete
  // ICMP Testing begins

  // The previous packet for testing the IP header is replaced with a packet buffer, as an array
  // Pointer casting will be used to overlay the structs in the correct places
  uint8_t packet_buffer[1024] = {0};

  // Map the IPv4 Header to the start of the buffer
  struct ipv4_header *ip = (struct ipv4_header *)packet_buffer;

  // Map the ICMP Header to the end of the ipv4 header
  struct icmp_header *icmp = (struct icmp_header *)(packet_buffer + sizeof(struct ipv4_header));

  // Build up the IPv4 header, using the predefined values from the IPv4 header testing
  version = 4;
  ihl = 5;
  ip->version_ihl = (version << 4) | ihl;
  assert(ip->version_ihl == 0x45);

  ip->type_of_service = 0x00;
  ip->total_length = htons(20);
  assert(ip->type_of_service == 0x00);
  assert(ntohs(ip->total_length) == 20);

  populated_bytes = (uint8_t *)&ip->identification - (uint8_t *)ip;
  assert(populated_bytes == 4);

  ip->identification = htons(0xBEEF);
  ip->flags_frag_offset = htons(0x0000);
  assert(ntohs(ip->identification) == 0xBEEF);
  assert(ntohs(ip->flags_frag_offset) == 0x0000);

  populated_bytes = (uint8_t *)&ip->time_to_live - (uint8_t *)ip;
  assert(populated_bytes == 8);

  ip->time_to_live = 64;
  ip->protocol = IP_PROTO_ICMP;
  ip->header_checksum = htons(0x0000);
  assert(ip->time_to_live == 64);
  assert(ip->protocol == IP_PROTO_ICMP);
  assert(ntohs(ip->header_checksum) == 0x0000);

  populated_bytes = (uint8_t *)&ip->src_addr - (uint8_t *)ip;
  assert(populated_bytes == 12);

  inet_pton(AF_INET, "127.0.0.1", &ip->src_addr);
  inet_pton(AF_INET, "127.0.0.1", &ip->dst_addr);
  assert(ntohl(ip->src_addr) == 0x7F000001);
  assert(ntohl(ip->dst_addr) == 0x7F000001);

  populated_bytes = (uint8_t *)(ip + 1) - (uint8_t *)ip;
  assert(populated_bytes == 20);

  ip->header_checksum = compute_checksum(ip, sizeof(struct ipv4_header));
  assert(ip->header_checksum == htons(0xBDF7));

  raw_bytes = (const uint8_t *)ip;
  printf("Raw IPv4 Header (Wire Format) | Packet 2:\n");
  for (size_t i = 0; i < sizeof(struct ipv4_header); ++i)
  {
    printf("%02X ", raw_bytes[i]);
    if ((i + 1) % 4 == 0)
      printf("\n");
  }
  printf("\n");

  // New IPv4 Header complete
  // Build the ICMP Header

  icmp->type = ECHO;
  icmp->code = 0;          /* Echo and Echo Reply always use 0 */
  icmp->checksum = htons(0x0000); /* Always use a 0 checksum value for calculation */

  assert(icmp->type == ECHO);
  assert(icmp->code == 0);
  assert(ntohs(icmp->checksum) == 0x0000);

  // The ICMP header should be placed immediately after the IPv4 Header
  size_t icmp_offset = (uint8_t *)icmp - packet_buffer;
  assert(icmp_offset == 20);

  printf("Test 14 Passed: ICMP header set and offset is exactly 20 bytes.\n");

  // Our constructed packet now includes an IPv4 header, and ICMP header
  // This means that our total length has now changed, and must be updated
  // And, since the header will change, so will the checksum
  // The checksum must be set to 0 before calculation
  ip->total_length = htons(sizeof(struct ipv4_header) + sizeof(struct icmp_header));
  ip->header_checksum = 0;
  ip->header_checksum = compute_checksum(ip, sizeof(struct ipv4_header));

  printf("New IPv4 Header Checksum: %04X\n", ip->header_checksum);

  assert(ntohs(ip->total_length) == 24);
  assert(ip->header_checksum != htons(0xBDF7));
  printf("Test 15 Passed: Total length updated and checksum recalculated.\n");

  // Map the Echo struct immediately after the ICMP baseline header
  struct icmp_echo *echo = (struct icmp_echo *)(packet_buffer + sizeof(struct ipv4_header) + sizeof(struct icmp_header));

  // Populate the Echo specific fields
  // Using arbitrary values for our ping session
  echo->identifier = htons(0x1234);
  echo->sequence = htons(0x0001);

  // Test 16: Verify the Echo struct mapping
  assert(ntohs(echo->identifier) == 0x1234);
  assert(ntohs(echo->sequence) == 0x0001);

  // Verify the exact memory offset (20 bytes IP + 4 bytes ICMP base = 24)
  size_t echo_offset = (uint8_t *)echo - packet_buffer;
  assert(echo_offset == 24);
  printf("Test 16 Passed: ICMP Echo specific fields mapped precisely at offset 24.\n");

  // 4 more bytes were appended, so the IPv4 header must be updated
  ip->total_length = htons(sizeof(struct ipv4_header) + sizeof(struct icmp_header) + sizeof(struct icmp_echo));
  ip->header_checksum = 0;
  ip->header_checksum = compute_checksum(ip, sizeof(struct ipv4_header));

  printf("New IPv4 Header Checksum: %04X\n", ip->header_checksum);

  assert(ntohs(ip->total_length) == 28);
  assert(ip->header_checksum != htons(0xF3BD)); /* F3BD is the checksum calculated with the ICMP header attached */
  printf("Test 17 Passed: Total length updated and checksum recalculated.\n");

  // Calculate the ICMP checksum over the entire ICMP message (NOT including the IPv4 Header)
  // Currently, this is just the baseline header + the echo struct (8 bytes total)
  size_t icmp_length = sizeof(struct icmp_header) + sizeof(struct icmp_echo);
  icmp->checksum = compute_checksum(icmp, icmp_length);

  // Test 18: Verify the ICMP checksum is calculated
  assert(icmp->checksum != htons(0x0000));
  printf("Test 18 Passed: ICMP Checksum calculated as %04X\n", ntohs(icmp->checksum));

  // Test 19: Odd-byte Checksum Validation
  // 5 bytes total. Logically padded to 6 bytes: [FF, 00, 01, FF, 48, 00]
  uint8_t odd_dummy[] = {0xFF, 0x00, 0x01, 0xFF, 0x48};
  uint16_t odd_checksum = compute_checksum(odd_dummy, sizeof(odd_dummy));
  assert(odd_checksum == htons(0xB6FF));
  printf("Test 19 Passed: Checksum algorithm now handles odd-byte payloads.\n");

  // Now we must construct an arbitrary payload for the ICMP packet
  // This payload will be returned in the Echo Reply

  // Define the payload
  const char *payload_data = "HELLO";
  size_t payload_len = 5; /* Exactly 5 bytes (odd-length), no null-terminator needed for the wire */

  // Map a pointer to exactly where the payload should start
  uint8_t *payload_ptr = packet_buffer + sizeof(struct ipv4_header) + sizeof(struct icmp_header) + sizeof(struct icmp_echo);

  // Copy the payload into the packet buffer
  memcpy(payload_ptr, payload_data, payload_len);

  // 1. Update the IPv4 Header (Total Length & Checksum)
  ip->total_length = htons(sizeof(struct ipv4_header) + sizeof(struct icmp_header) + sizeof(struct icmp_echo) + payload_len);
  ip->header_checksum = 0;
  ip->header_checksum = compute_checksum(ip, sizeof(struct ipv4_header));

  // 2. Update the ICMP Checksum (Baseline + Echo Struct + Payload)
  icmp->checksum = 0; /* MUST zero out the old checksum before recalculating! */
  size_t final_icmp_len = sizeof(struct icmp_header) + sizeof(struct icmp_echo) + payload_len;
  icmp->checksum = compute_checksum(icmp, final_icmp_len);

  // Test 20: Final packet validations
  assert(ntohs(ip->total_length) == 33); // 20 + 4 + 4 + 5
  assert(ip->header_checksum != 0);
  assert(icmp->checksum != 0);
  printf("Test 20 Passed: Payload attached. IP Total Length: 33. Checksums updated.\n");

  // Final Validation: Print the completed, wire-ready 33-byte datagram
  printf("\nRaw IPv4 + ICMP Datagram (Wire Format):\n");
  for (size_t i = 0; i < ntohs(ip->total_length); ++i)
  {
    printf("%02X ", packet_buffer[i]);
    if ((i + 1) % 4 == 0)
      printf("\n");
  }
  printf("\n");
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
  // IPv4 Header Testing is complete
  // ICMP Testing begins

  // The previous packet for testing the IP header is replaced with a packet buffer, as an array
  // Pointer casting will be used to overlay the structs in the correct places
  uint8_t packet_buffer[1024] = {0};

  // Map the IPv4 Header to the start of the buffer
  struct ipv4_header *ip = (struct ipv4_header *)packet_buffer;

  // Map the ICMP Header to the end of the ipv4 header
  struct icmp_header *icmp = (struct icmp_header *)(packet_buffer + sizeof(struct ipv4_header));

  // Build up the IPv4 header, using the predefined values from the IPv4 header testing
  version = 4;
  ihl = 5;
  ip->version_ihl = (version << 4) | ihl;
  assert(ip->version_ihl == 0x45);

  ip->type_of_service = 0x00;
  ip->total_length = htons(20);
  assert(ip->type_of_service == 0x00);
  assert(ntohs(ip->total_length) == 20);

  populated_bytes = (uint8_t *)&ip->identification - (uint8_t *)ip;
  assert(populated_bytes == 4);

  ip->identification = htons(0xBEEF);
  ip->flags_frag_offset = htons(0x0000);
  assert(ntohs(ip->identification) == 0xBEEF);
  assert(ntohs(ip->flags_frag_offset) == 0x0000);

  populated_bytes = (uint8_t *)&ip->time_to_live - (uint8_t *)ip;
  assert(populated_bytes == 8);

  ip->time_to_live = 64;
  ip->protocol = IP_PROTO_ICMP;
  ip->header_checksum = htons(0x0000);
  assert(ip->time_to_live == 64);
  assert(ip->protocol == IP_PROTO_ICMP);
  assert(ntohs(ip->header_checksum) == 0x0000);

  populated_bytes = (uint8_t *)&ip->src_addr - (uint8_t *)ip;
  assert(populated_bytes == 12);

  inet_pton(AF_INET, "127.0.0.1", &ip->src_addr);
  inet_pton(AF_INET, "127.0.0.1", &ip->dst_addr);
  assert(ntohl(ip->src_addr) == 0x7F000001);
  assert(ntohl(ip->dst_addr) == 0x7F000001);

  populated_bytes = (uint8_t *)(ip + 1) - (uint8_t *)ip;
  assert(populated_bytes == 20);

  ip->header_checksum = compute_checksum(ip, sizeof(struct ipv4_header));
  assert(ip->header_checksum == htons(0xBDF7));

  raw_bytes = (const uint8_t *)ip;
  printf("Raw IPv4 Header (Wire Format) | Packet 2:\n");
  for (size_t i = 0; i < sizeof(struct ipv4_header); ++i)
  {
    printf("%02X ", raw_bytes[i]);
    if ((i + 1) % 4 == 0)
      printf("\n");
  }
  printf("\n");

  // New IPv4 Header complete
  // Build the ICMP Header

  icmp->type = ECHO;
  icmp->code = 0;          /* Echo and Echo Reply always use 0 */
  icmp->checksum = htons(0x0000); /* Always use a 0 checksum value for calculation */

  assert(icmp->type == ECHO);
  assert(icmp->code == 0);
  assert(ntohs(icmp->checksum) == 0x0000);

  // The ICMP header should be placed immediately after the IPv4 Header
  size_t icmp_offset = (uint8_t *)icmp - packet_buffer;
  assert(icmp_offset == 20);

  printf("Test 14 Passed: ICMP header set and offset is exactly 20 bytes.\n");

  // Our constructed packet now includes an IPv4 header, and ICMP header
  // This means that our total length has now changed, and must be updated
  // And, since the header will change, so will the checksum
  // The checksum must be set to 0 before calculation
  ip->total_length = htons(sizeof(struct ipv4_header) + sizeof(struct icmp_header));
  ip->header_checksum = 0;
  ip->header_checksum = compute_checksum(ip, sizeof(struct ipv4_header));

  printf("New IPv4 Header Checksum: %04X\n", ip->header_checksum);

  assert(ntohs(ip->total_length) == 24);
  assert(ip->header_checksum != htons(0xBDF7));
  printf("Test 15 Passed: Total length updated and checksum recalculated.\n");

  // Map the Echo struct immediately after the ICMP baseline header
  struct icmp_echo *echo = (struct icmp_echo *)(packet_buffer + sizeof(struct ipv4_header) + sizeof(struct icmp_header));

  // Populate the Echo specific fields
  // Using arbitrary values for our ping session
  echo->identifier = htons(0x1234);
  echo->sequence = htons(0x0001);

  // Test 16: Verify the Echo struct mapping
  assert(ntohs(echo->identifier) == 0x1234);
  assert(ntohs(echo->sequence) == 0x0001);

  // Verify the exact memory offset (20 bytes IP + 4 bytes ICMP base = 24)
  size_t echo_offset = (uint8_t *)echo - packet_buffer;
  assert(echo_offset == 24);
  printf("Test 16 Passed: ICMP Echo specific fields mapped precisely at offset 24.\n");

  // 4 more bytes were appended, so the IPv4 header must be updated
  ip->total_length = htons(sizeof(struct ipv4_header) + sizeof(struct icmp_header) + sizeof(struct icmp_echo));
  ip->header_checksum = 0;
  ip->header_checksum = compute_checksum(ip, sizeof(struct ipv4_header));

  printf("New IPv4 Header Checksum: %04X\n", ip->header_checksum);

  assert(ntohs(ip->total_length) == 28);
  assert(ip->header_checksum != htons(0xF3BD)); /* F3BD is the checksum calculated with the ICMP header attached */
  printf("Test 17 Passed: Total length updated and checksum recalculated.\n");

  // Calculate the ICMP checksum over the entire ICMP message (NOT including the IPv4 Header)
  // Currently, this is just the baseline header + the echo struct (8 bytes total)
  size_t icmp_length = sizeof(struct icmp_header) + sizeof(struct icmp_echo);
  icmp->checksum = compute_checksum(icmp, icmp_length);

  // Test 18: Verify the ICMP checksum is calculated
  assert(icmp->checksum != htons(0x0000));
  printf("Test 18 Passed: ICMP Checksum calculated as %04X\n", ntohs(icmp->checksum));

  // Test 19: Odd-byte Checksum Validation
  // 5 bytes total. Logically padded to 6 bytes: [FF, 00, 01, FF, 48, 00]
  uint8_t odd_dummy[] = {0xFF, 0x00, 0x01, 0xFF, 0x48};
  uint16_t odd_checksum = compute_checksum(odd_dummy, sizeof(odd_dummy));
  assert(odd_checksum == htons(0xB6FF));
  printf("Test 19 Passed: Checksum algorithm now handles odd-byte payloads.\n");

  // Now we must construct an arbitrary payload for the ICMP packet
  // This payload will be returned in the Echo Reply

  // Define the payload
  const char *payload_data = "HELLO";
  size_t payload_len = 5; /* Exactly 5 bytes (odd-length), no null-terminator needed for the wire */

  // Map a pointer to exactly where the payload should start
  uint8_t *payload_ptr = packet_buffer + sizeof(struct ipv4_header) + sizeof(struct icmp_header) + sizeof(struct icmp_echo);

  // Copy the payload into the packet buffer
  memcpy(payload_ptr, payload_data, payload_len);

  // 1. Update the IPv4 Header (Total Length & Checksum)
  ip->total_length = htons(sizeof(struct ipv4_header) + sizeof(struct icmp_header) + sizeof(struct icmp_echo) + payload_len);
  ip->header_checksum = 0;
  ip->header_checksum = compute_checksum(ip, sizeof(struct ipv4_header));

  // 2. Update the ICMP Checksum (Baseline + Echo Struct + Payload)
  icmp->checksum = 0; /* MUST zero out the old checksum before recalculating! */
  size_t final_icmp_len = sizeof(struct icmp_header) + sizeof(struct icmp_echo) + payload_len;
  icmp->checksum = compute_checksum(icmp, final_icmp_len);

  // Test 20: Final packet validations
  assert(ntohs(ip->total_length) == 33); // 20 + 4 + 4 + 5
  assert(ip->header_checksum != 0);
  assert(icmp->checksum != 0);
  printf("Test 20 Passed: Payload attached. IP Total Length: 33. Checksums updated.\n");

  // Final Validation: Print the completed, wire-ready 33-byte datagram
  printf("\nRaw IPv4 + ICMP Datagram (Wire Format):\n");
  for (size_t i = 0; i < ntohs(ip->total_length); ++i)
  {
    printf("%02X ", packet_buffer[i]);
    if ((i + 1) % 4 == 0)
      printf("\n");
  }
  printf("\n");

Complete source code can be found on the repo (opens in a new tab).