Skip to content

Commit

Permalink
northd, controller: Add support for DHCPv6 FQDN option
Browse files Browse the repository at this point in the history
Add support for FQDN option (39), if specified the server
can overwrite FQDN for the client. It behaves similarly
to DHCP hostname option (12).

Reported-at: https://bugzilla.redhat.com/2211890
Signed-off-by: Ales Musil <amusil@redhat.com>
Acked-by: Mark Michelson <mmichels@redhat.com>
Signed-off-by: Mark Michelson <mmichels@redhat.com>
  • Loading branch information
almusil authored and putnopvut committed Jun 12, 2023
1 parent 38644f0 commit b3ae86a
Show file tree
Hide file tree
Showing 11 changed files with 181 additions and 113 deletions.
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ Post v23.06.0
- Added FDB aging mechanism, that is disabled by default.
It can be enabled per logical switch with other_config
"fdb_age_threshold".
- Add DHCPv6 "fqdn" (39) option, that works similarly to
DHCPv4 "hostname" (12) option.

OVN v23.06.0 - 01 Jun 2023
--------------------------
Expand Down
69 changes: 30 additions & 39 deletions controller/pinctrl.c
Original file line number Diff line number Diff line change
Expand Up @@ -2289,7 +2289,7 @@ pinctrl_handle_put_dhcp_opts(
static bool
compose_out_dhcpv6_opts(struct ofpbuf *userdata,
struct ofpbuf *out_dhcpv6_opts,
ovs_be32 iaid, bool ipxe_req)
ovs_be32 iaid, bool ipxe_req, uint8_t fqdn_flags)
{
while (userdata->size) {
struct dhcpv6_opt_header *userdata_opt = ofpbuf_try_pull(
Expand Down Expand Up @@ -2413,6 +2413,24 @@ compose_out_dhcpv6_opts(struct ofpbuf *userdata,
break;
}

case DHCPV6_OPT_FQDN_CODE: {
if (fqdn_flags != DHCPV6_FQDN_FLAGS_UNDEFINED) {
struct dhcpv6_opt_header *header =
ofpbuf_put_zeros(out_dhcpv6_opts, sizeof *header);
header->code = htons(DHCPV6_OPT_FQDN_CODE);
header->len = htons(size + 1);
uint8_t *flags = ofpbuf_put_zeros(out_dhcpv6_opts, 1);
/* Always set N to 1, if client requested S inform him that it
* was overwritten by the server. */
*flags |= DHCPV6_FQDN_FLAGS_N;
if (fqdn_flags & DHCPV6_FQDN_FLAGS_S) {
*flags |= DHCPV6_FQDN_FLAGS_O;
}
ofpbuf_put(out_dhcpv6_opts, userdata_opt_data, size);
}
break;
}

default:
return false;
}
Expand Down Expand Up @@ -2501,6 +2519,7 @@ pinctrl_handle_put_dhcpv6_opts(
size_t l4_len = dp_packet_l4_size(pkt_in);
uint8_t *end = (uint8_t *)in_udp + MIN(udp_len, l4_len);
bool ipxe_req = false;
uint8_t fqdn_flags = DHCPV6_FQDN_FLAGS_UNDEFINED;
while (in_dhcpv6_data < end) {
struct dhcpv6_opt_header const *in_opt =
(struct dhcpv6_opt_header *)in_dhcpv6_data;
Expand All @@ -2525,6 +2544,10 @@ pinctrl_handle_put_dhcpv6_opts(
break;
}

case DHCPV6_OPT_FQDN_CODE:
fqdn_flags = *(in_dhcpv6_data + sizeof *in_opt);
break;

default:
break;
}
Expand All @@ -2548,7 +2571,7 @@ pinctrl_handle_put_dhcpv6_opts(
OFPBUF_STUB_INITIALIZER(out_ofpacts_dhcpv6_opts_stub);

if (!compose_out_dhcpv6_opts(userdata, &out_dhcpv6_opts,
iaid, ipxe_req)) {
iaid, ipxe_req, fqdn_flags)) {
VLOG_WARN_RL(&rl, "Invalid userdata");
goto exit;
}
Expand Down Expand Up @@ -2763,48 +2786,16 @@ dns_build_ptr_answer(
struct ofpbuf *dns_answer, const uint8_t *in_queryname,
uint16_t query_length, const char *answer_data)
{
char *encoded_answer;
uint16_t encoded_answer_length;

dns_build_base_answer(dns_answer, in_queryname, query_length,
DNS_QUERY_TYPE_PTR);

/* Initialize string 2 chars longer than real answer:
* first label length and terminating zero-length label.
* If the answer_data is - vm1tst.ovn.org, it will be encoded as
* - 0010 (Total length which is 16)
* - 06766d31747374 (vm1tst)
* - 036f766e (ovn)
* - 036f7267 (org
* - 00 (zero length field) */
encoded_answer_length = strlen(answer_data) + 2;
encoded_answer = (char *)xzalloc(encoded_answer_length);

put_be16(dns_answer, htons(encoded_answer_length));
uint8_t label_len_index = 0;
uint16_t label_len = 0;
char *encoded_answer_ptr = (char *)encoded_answer + 1;
while (*answer_data) {
if (*answer_data == '.') {
/* Label has ended. Update the length of the label. */
encoded_answer[label_len_index] = label_len;
label_len_index += (label_len + 1);
label_len = 0; /* Init to 0 for the next label. */
} else {
*encoded_answer_ptr = *answer_data;
label_len++;
}
encoded_answer_ptr++;
answer_data++;
}
size_t encoded_len = 0;
char *encoded = encode_fqdn_string(answer_data, &encoded_len);

/* This is required for the last label if it doesn't end with '.' */
if (label_len) {
encoded_answer[label_len_index] = label_len;
}
put_be16(dns_answer, htons(encoded_len));
ofpbuf_put(dns_answer, encoded, encoded_len);

ofpbuf_put(dns_answer, encoded_answer, encoded_answer_length);
free(encoded_answer);
free(encoded);
}

/* Called with in the pinctrl_handler thread context. */
Expand Down
9 changes: 8 additions & 1 deletion lib/actions.c
Original file line number Diff line number Diff line change
Expand Up @@ -2470,7 +2470,8 @@ parse_gen_opt(struct action_context *ctx, struct ovnact_gen_option *o,
}

if (!strcmp(o->option->type, "str") ||
!strcmp(o->option->type, "domains")) {
!strcmp(o->option->type, "domains") ||
!strcmp(o->option->type, "domain")) {
if (o->value.type != EXPR_C_STRING) {
lexer_error(ctx->lexer, "%s option %s requires string value.",
opts_type, o->option->name);
Expand Down Expand Up @@ -2903,6 +2904,12 @@ encode_put_dhcpv6_option(const struct ovnact_gen_option *o,
size = strlen(c->string);
opt->len = htons(size);
ofpbuf_put(ofpacts, c->string, size);
} else if (!strcmp(o->option->type, "domain")) {
char *encoded = encode_fqdn_string(c->string, &size);
opt->len = htons(size);
ofpbuf_put(ofpacts, encoded, size);

free(encoded);
}
}

Expand Down
10 changes: 10 additions & 0 deletions lib/ovn-l7.h
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ BUILD_ASSERT_DECL(DHCP_OPT_HEADER_LEN == sizeof(struct dhcp_opt_header));
#define DHCPV6_OPT_DOMAIN_SEARCH_CODE 24
#define DHCPV6_OPT_IA_PD 25
#define DHCPV6_OPT_IA_PREFIX 26
#define DHCPV6_OPT_FQDN_CODE 39
#define DHCPV6_OPT_BOOT_FILE_URL 59
#define DHCPV6_OPT_BOOT_FILE_URL_ALT 254

Expand All @@ -291,6 +292,15 @@ BUILD_ASSERT_DECL(DHCP_OPT_HEADER_LEN == sizeof(struct dhcp_opt_header));
#define DHCPV6_OPT_BOOTFILE_NAME_ALT \
DHCP_OPTION("bootfile_name_alt", DHCPV6_OPT_BOOT_FILE_URL_ALT, "str")

#define DHCPV6_OPT_FQDN \
DHCP_OPTION("fqdn", DHCPV6_OPT_FQDN_CODE, "domain")

/* DHCPv6 FQDN flags. RFC 4704 */
#define DHCPV6_FQDN_FLAGS_UNDEFINED 0xff
#define DHCPV6_FQDN_FLAGS_S 1 << 0
#define DHCPV6_FQDN_FLAGS_O 1 << 1
#define DHCPV6_FQDN_FLAGS_N 1 << 2

OVS_PACKED(
struct dhcpv6_opt_header {
ovs_be16 code;
Expand Down
27 changes: 27 additions & 0 deletions lib/ovn-util.c
Original file line number Diff line number Diff line change
Expand Up @@ -1141,3 +1141,30 @@ void flow_collector_ids_clear(struct flow_collector_ids *ids)
flow_collector_ids_destroy(ids);
flow_collector_ids_init(ids);
}

char *
encode_fqdn_string(const char *fqdn, size_t *len)
{

size_t domain_len = strlen(fqdn);
*len = domain_len + 2;
char *encoded = xzalloc(*len);

int8_t label_len = 0;
for (size_t i = 0; i < domain_len; i++) {
if (fqdn[i] == '.') {
encoded[i - label_len] = label_len;
label_len = 0;
} else {
encoded[i + 1] = fqdn[i];
label_len++;
}
}

/* This is required for the last label if it doesn't end with '.' */
if (label_len) {
encoded[domain_len - label_len] = label_len;
}

return encoded;
}
5 changes: 5 additions & 0 deletions lib/ovn-util.h
Original file line number Diff line number Diff line change
Expand Up @@ -404,4 +404,9 @@ bool flow_collector_ids_lookup(const struct flow_collector_ids *, uint32_t);
void flow_collector_ids_destroy(struct flow_collector_ids *);
void flow_collector_ids_clear(struct flow_collector_ids *);

/* The DNS format is 2 bytes longer than the "domain".
* It replaces every '.' with len of the next name.
* The returned pointer has to be freed by caller. */
char *encode_fqdn_string(const char *fqdn, size_t *len);

#endif /* OVN_UTIL_H */
3 changes: 2 additions & 1 deletion northd/ovn-northd.c
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,8 @@ static struct gen_opts_map supported_dhcpv6_opts[] = {
DHCPV6_OPT_DOMAIN_SEARCH,
DHCPV6_OPT_DNS_SERVER,
DHCPV6_OPT_BOOTFILE_NAME,
DHCPV6_OPT_BOOTFILE_NAME_ALT
DHCPV6_OPT_BOOTFILE_NAME_ALT,
DHCPV6_OPT_FQDN,
};

static bool
Expand Down
7 changes: 7 additions & 0 deletions ovn-nb.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4206,6 +4206,13 @@ or
way. Default value for this option is false.
</p>
</column>

<column name="options" key="fqdn">
<p>
The DHCPv6 option code for this option is 39.
If set, indicates the DHCPv6 option "FQDN".
</p>
</column>
</group>
</group>

Expand Down
6 changes: 3 additions & 3 deletions ovn-sb.ovsschema
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "OVN_Southbound",
"version": "20.27.3",
"cksum": "800899756 30521",
"version": "20.27.4",
"cksum": "3194181501 30531",
"tables": {
"SB_Global": {
"columns": {
Expand Down Expand Up @@ -311,7 +311,7 @@
"type": {
"type": {"key": {
"type": "string",
"enum": ["set", ["ipv6", "str", "mac"]]}}}},
"enum": ["set", ["ipv6", "str", "mac", "domain"]]}}}},
"isRoot": true},
"Connection": {
"columns": {
Expand Down

0 comments on commit b3ae86a

Please sign in to comment.