Skip to content

Commit

Permalink
Fix DHCPv6 options serialization to prevent extra bytes in replies
Browse files Browse the repository at this point in the history
In the `generate_reply_options` function, fixed an issue where DHCPv6 reply packets contained extra unintended bytes at the end. The problem was due to the `options` pointer not being incremented after copying the `opt_rapid` and `boot_file_url` options into the packet buffer.

Changes made:

- After copying `opt_rapid`, the `options` pointer is now incremented by `reply_options.opt_rapid_len`.
- After copying `boot_file_url`, the `options` pointer is now incremented by `reply_options.opt_boot_file_len`.

These changes ensure that all DHCPv6 options are correctly serialized in the packet buffer without overlaps or gaps. By properly advancing the `options` pointer, we prevent unintended data from being included at the end of the packet, eliminating the extra bytes that were being sent.

This fix addresses the issue where clients were receiving malformed DHCPv6 packets due to the extra bytes, which could lead to communication errors or unexpected behavior.
  • Loading branch information
afritzler committed Dec 4, 2024
1 parent a6d0032 commit 79bd842
Showing 1 changed file with 56 additions and 52 deletions.
108 changes: 56 additions & 52 deletions src/nodes/dhcpv6_node.c
Original file line number Diff line number Diff line change
Expand Up @@ -210,58 +210,62 @@ static __rte_always_inline int strip_options(struct rte_mbuf *m, int options_len
/** @return size of generated options or error */
static int generate_reply_options(struct rte_mbuf *m, uint8_t *options, int options_len)
{
int reply_options_len;
struct dhcpv6_opt_server_id_ll opt_sid;
struct dhcpv6_opt_dns_servers dns_opt;
struct dp_dhcpv6_reply_options reply_options = {0}; // this makes *_len fields 0, needed later
const struct dp_conf_dhcp_dns *dhcpv6_dns = dp_conf_get_dhcpv6_dns();

if (DP_FAILED(parse_options(m, options, options_len, &reply_options))) {
DPS_LOG_WARNING("Invalid DHCPv6 options received");
return DP_ERROR;
}

dns_opt.opt_code = htons(DHCPV6_OPT_DNS);
dns_opt.opt_len = htons(dhcpv6_dns->len);

opt_sid = opt_sid_template;
rte_ether_addr_copy(&rte_pktmbuf_mtod(m, struct rte_ether_hdr *)->dst_addr, &opt_sid.id.mac);

reply_options_len = (int)sizeof(opt_sid) + reply_options.opt_cid_len + reply_options.opt_iana_len + reply_options.opt_rapid_len;
reply_options_len += (int)(sizeof(dns_opt.opt_code) + sizeof(dns_opt.opt_len) + ntohs(dns_opt.opt_len));

if (reply_options.pxe_mode != DP_PXE_MODE_NONE)
reply_options_len += reply_options.opt_boot_file_len;

if (DP_FAILED(resize_packet(m, reply_options_len - options_len)))
return DP_ERROR;

// had to use memcpy() here, because GCC's array-bounds check fails for rte_memcpy (using XMM optimization)
memcpy(options, &opt_sid, sizeof(opt_sid));
options += sizeof(opt_sid);
if (reply_options.opt_cid_len) {
memcpy(options, (void *)&reply_options.opt_cid, reply_options.opt_cid_len);
options += reply_options.opt_cid_len;
}
if (reply_options.opt_iana_len) {
memcpy(options, (void *)&reply_options.opt_iana, reply_options.opt_iana_len);
options += reply_options.opt_iana_len;
}
if (reply_options.opt_rapid_len)
memcpy(options, &reply_options.opt_rapid, reply_options.opt_rapid_len);

// Add DNS server option
memcpy(options, &dns_opt.opt_code, sizeof(dns_opt.opt_code));
options += sizeof(dns_opt.opt_code);
memcpy(options, &dns_opt.opt_len, sizeof(dns_opt.opt_len));
options += sizeof(dns_opt.opt_len);
memcpy(options, dhcpv6_dns->array, dhcpv6_dns->len);
options += dhcpv6_dns->len;

if (reply_options.pxe_mode != DP_PXE_MODE_NONE)
memcpy(options, &reply_options.boot_file_url, reply_options.opt_boot_file_len);

return reply_options_len;
int reply_options_len;
struct dhcpv6_opt_server_id_ll opt_sid;
struct dhcpv6_opt_dns_servers dns_opt;
struct dp_dhcpv6_reply_options reply_options = {0}; // this makes *_len fields 0, needed later
const struct dp_conf_dhcp_dns *dhcpv6_dns = dp_conf_get_dhcpv6_dns();

if (DP_FAILED(parse_options(m, options, options_len, &reply_options))) {
DPS_LOG_WARNING("Invalid DHCPv6 options received");
return DP_ERROR;
}

dns_opt.opt_code = htons(DHCPV6_OPT_DNS);
dns_opt.opt_len = htons(dhcpv6_dns->len);

opt_sid = opt_sid_template;
rte_ether_addr_copy(&rte_pktmbuf_mtod(m, struct rte_ether_hdr *)->dst_addr, &opt_sid.id.mac);

reply_options_len = (int)sizeof(opt_sid) + reply_options.opt_cid_len + reply_options.opt_iana_len + reply_options.opt_rapid_len;
reply_options_len += (int)(sizeof(dns_opt.opt_code) + sizeof(dns_opt.opt_len) + ntohs(dns_opt.opt_len));

if (reply_options.pxe_mode != DP_PXE_MODE_NONE)
reply_options_len += reply_options.opt_boot_file_len;

if (DP_FAILED(resize_packet(m, reply_options_len - options_len)))
return DP_ERROR;

// had to use memcpy() here, because GCC's array-bounds check fails for rte_memcpy (using XMM optimization)
memcpy(options, &opt_sid, sizeof(opt_sid));
options += sizeof(opt_sid);
if (reply_options.opt_cid_len) {
memcpy(options, (void *)&reply_options.opt_cid, reply_options.opt_cid_len);
options += reply_options.opt_cid_len;
}
if (reply_options.opt_iana_len) {
memcpy(options, (void *)&reply_options.opt_iana, reply_options.opt_iana_len);
options += reply_options.opt_iana_len;
}
if (reply_options.opt_rapid_len) {
memcpy(options, &reply_options.opt_rapid, reply_options.opt_rapid_len);
options += reply_options.opt_rapid_len; // **Added this line**
}

// Add DNS server option
memcpy(options, &dns_opt.opt_code, sizeof(dns_opt.opt_code));
options += sizeof(dns_opt.opt_code);
memcpy(options, &dns_opt.opt_len, sizeof(dns_opt.opt_len));
options += sizeof(dns_opt.opt_len);
memcpy(options, dhcpv6_dns->array, dhcpv6_dns->len);
options += dhcpv6_dns->len;

if (reply_options.pxe_mode != DP_PXE_MODE_NONE) {
memcpy(options, &reply_options.boot_file_url, reply_options.opt_boot_file_len);
options += reply_options.opt_boot_file_len; // **Added this line**
}

return reply_options_len;
}

static __rte_always_inline rte_edge_t get_next_index(struct rte_node *node, struct rte_mbuf *m)
Expand Down

0 comments on commit 79bd842

Please sign in to comment.