diff --git a/lib/actions.c b/lib/actions.c
index f21be6d..24c9043 100644
--- a/lib/actions.c
+++ b/lib/actions.c
@@ -1982,7 +1982,8 @@ parse_gen_opt(struct action_context *ctx, struct ovnact_gen_option *o,
return;
}
- if (!strcmp(o->option->type, "str")) {
+ if (!strcmp(o->option->type, "str") ||
+ !strcmp(o->option->type, "domains")) {
if (o->value.type != EXPR_C_STRING) {
lexer_error(ctx->lexer, "%s option %s requires string value.",
opts_type, o->option->name);
@@ -2317,6 +2318,89 @@ encode_put_dhcpv4_option(const struct ovnact_gen_option *o,
opt_header[1] = sizeof(ovs_be32);
ofpbuf_put(ofpacts, &c->value.ipv4, sizeof(ovs_be32));
}
+ } else if (!strcmp(o->option->type, "domains")) {
+ /* Please refer to RFC 1035, section 4.1.4 for the format of encoding
+ * domain names. Below is an example for encoding a search list
+ * consisting of the "abc.com" and "xyz.abc.com".
+ *
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * |119|14 | 3 |'a'|'b'|'c'| 3 |'c'|'o'|'m'| 0 |'x'|'y'|'z'|xC0|x00|
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ *
+ * The encoding of "abc.com" ends with 0 to mark the end of the
+ * domain name as required by RFC 1035.
+ *
+ * The encoding of "xyz" (for "xyz.abc.com") ends with the two-octet
+ * compression pointer C000 (hex), which points to offset 0 where
+ * another validly encoded domain name can be found to complete
+ * the name ("abc.com").
+ *
+ * Encoding adds 2 bytes (one for length and one for delimiter) for
+ * every domain name that is unique. If all the domain names are unique
+ * (which probably never happens in real world), then encoded string
+ * could be longer than the original string. Just to be on the safer
+ * side, allocate the (approx.) worst case length here.
+ */
+ uint8_t *dns_encoded = xzalloc(2 * strlen(c->string));
+ uint16_t encode_offset = 0;
+ struct shash label_offset_map;
+ shash_init(&label_offset_map);
+ char *domain_list = xstrdup(c->string), *dom_ptr = NULL;
+ for (char *domain = strtok_r(domain_list, ",", &dom_ptr);
+ domain != NULL;
+ domain = strtok_r(NULL, ",", &dom_ptr)) {
+ if (strlen(domain) > DOMAIN_NAME_MAX_LEN) {
+ VLOG_WARN("Domain names longer than 255 characters are not"
+ "supported");
+ goto out;
+ }
+ char *label_ptr = NULL, *label;
+ for (label = strtok_r(domain, ".", &label_ptr);
+ label != NULL;
+ label = strtok_r(NULL, ".", &label_ptr)) {
+ /* Check if we have already encoded this label. If yes,
+ * fill in the reference and break. */
+ uint16_t *get_offset;
+ get_offset = shash_find_data(&label_offset_map, label);
+ if (get_offset != NULL) {
+ ovs_be16 temp = htons(0xc000) | htons(*get_offset);
+ memcpy(dns_encoded + encode_offset, &temp,
+ sizeof(temp));
+ encode_offset += sizeof(temp);
+ break;
+ } else {
+ /* The label was not encoded before, encode it now and add
+ * the offset to the label_offset_map. */
+ uint16_t *set_offset = xzalloc(sizeof(uint16_t));
+ memcpy(set_offset, &encode_offset, sizeof(uint16_t));
+ shash_add_once(&label_offset_map, label, set_offset);
+
+ uint8_t len = strlen(label);
+ memcpy(dns_encoded + encode_offset, &len, sizeof(uint8_t));
+ encode_offset += sizeof(uint8_t);
+ memcpy(dns_encoded + encode_offset, label, len);
+ encode_offset += len;
+ }
+ }
+ /* Add the end marker (0 byte) to determine the end of the
+ * domain. */
+ if (label == NULL) {
+ uint8_t end = 0;
+ memcpy(dns_encoded + encode_offset, &end, sizeof(uint8_t));
+ encode_offset += sizeof(uint8_t);
+ }
+ }
+ opt_header[1] = encode_offset;
+ ofpbuf_put(ofpacts, dns_encoded, encode_offset);
+
+ out:
+ free(domain_list);
+ free(dns_encoded);
+ struct shash_node *node, *next;
+ SHASH_FOR_EACH_SAFE (node, next, &label_offset_map) {
+ shash_delete(&label_offset_map, node);
+ }
+ shash_destroy(&label_offset_map);
}
}
diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h
index 38555db..f34bc0d 100644
--- a/lib/ovn-l7.h
+++ b/lib/ovn-l7.h
@@ -34,6 +34,7 @@ struct gen_opts_map {
size_t code;
};
+#define DOMAIN_NAME_MAX_LEN 255
#define DHCP_BROADCAST_FLAG 0x8000
#define DHCP_OPTION(NAME, CODE, TYPE) \
@@ -81,6 +82,8 @@ struct gen_opts_map {
#define DHCP_OPT_PATH_PREFIX DHCP_OPTION("path_prefix", 210, "str")
#define DHCP_OPT_TFTP_SERVER_ADDRESS \
DHCP_OPTION("tftp_server_address", 150, "ipv4")
+#define DHCP_OPT_DOMAIN_SEARCH_LIST \
+ DHCP_OPTION("domain_search_list", 119, "domains")
#define DHCP_OPT_ARP_CACHE_TIMEOUT \
DHCP_OPTION("arp_cache_timeout", 35, "uint32")
diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
index ef08414..4ec2034 100644
--- a/northd/ovn-northd.c
+++ b/northd/ovn-northd.c
@@ -11336,6 +11336,7 @@ static struct gen_opts_map supported_dhcp_opts[] = {
DHCP_OPT_DOMAIN_NAME,
DHCP_OPT_ARP_CACHE_TIMEOUT,
DHCP_OPT_TCP_KEEPALIVE_INTERVAL,
+ DHCP_OPT_DOMAIN_SEARCH_LIST,
};
static struct gen_opts_map supported_dhcpv6_opts[] = {
diff --git a/ovn-nb.xml b/ovn-nb.xml
index 54f5301..80e843c 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -2939,7 +2939,7 @@
-
These options accept either an IPv4 address or a string value.
+ These options accept string value which is a comma separated + list of domain names. The domain names are encoded based on RFC 1035. +
+ ++ The DHCPv4 option code for this option is 119. +
+value: domains
+ This indicates that the value of the DHCP option is a domain name + or a comma separated list of domain names. +
+ ++ Example. "name=domain_search_list", "code=119", "type=domains". +
+