diff --git a/lib/actions.c b/lib/actions.c index 9baa90f..ad7ae78 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,93 @@ 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; + char *suffix = xzalloc(strlen(domain_list)); + 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; + } + strcpy(suffix, domain); + char *label; + for (label = strtok_r(domain, ".", &domain); + label != NULL; + label = strtok_r(NULL, ".", &domain)) { + /* Check if we have already encoded this suffix. + * If yes, fill in the reference and break. */ + uint16_t *get_offset; + get_offset = shash_find_data(&label_offset_map, suffix); + 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 suffix was not encoded before, encode it now + * and add the offset to the label_offset_map. */ + uint16_t *set_offset = xzalloc(sizeof(uint16_t)); + *set_offset = encode_offset; + shash_add_once(&label_offset_map, suffix, 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; + } + strcpy(suffix, domain); + } + /* 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(suffix); + 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 22a2153..cea97b9 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 6a9b097..22891c1 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -11345,6 +11345,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 8368d51..80e843c 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -2950,6 +2950,19 @@
+ ++ 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". +
+