Message ID | 20230606065102.51899-1-amusil@redhat.com |
---|---|
State | Accepted |
Headers | show |
Series | [ovs-dev,v2] northd, controller: Add support for DHCPv6 FQDN option | expand |
Context | Check | Description |
---|---|---|
ovsrobot/apply-robot | success | apply and check: success |
ovsrobot/github-robot-_Build_and_Test | success | github build: passed |
ovsrobot/github-robot-_ovn-kubernetes | fail | github build: failed |
Thanks for the changes Ales. Acked-by: Mark Michelson <mmichels@redhat.com> On 6/6/23 02:51, Ales Musil wrote: > 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> > --- > v2: Rebase on top of current main. > Address comments from Mark: > - Properly propagate option flags. > - Extract the fqdn encoding to common function. > --- > NEWS | 2 + > controller/pinctrl.c | 69 +++++++++---------- > lib/actions.c | 9 ++- > lib/ovn-l7.h | 10 +++ > lib/ovn-util.c | 27 ++++++++ > lib/ovn-util.h | 5 ++ > northd/ovn-northd.c | 3 +- > ovn-nb.xml | 7 ++ > ovn-sb.ovsschema | 6 +- > tests/ovn.at | 155 ++++++++++++++++++++++++------------------- > tests/test-ovn.c | 1 + > 11 files changed, 181 insertions(+), 113 deletions(-) > > diff --git a/NEWS b/NEWS > index 645acea1f..e6c87e9ad 100644 > --- a/NEWS > +++ b/NEWS > @@ -1,5 +1,7 @@ > Post v23.06.0 > ------------- > + - Add DHCPv6 "fqdn" (39) option, that works similarly to > + DHCPv4 "hostname" (12) option. > > OVN v23.06.0 - 01 Jun 2023 > -------------------------- > diff --git a/controller/pinctrl.c b/controller/pinctrl.c > index c396ad4c2..d7bd20ed6 100644 > --- a/controller/pinctrl.c > +++ b/controller/pinctrl.c > @@ -2288,7 +2288,7 @@ exit: > 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( > @@ -2412,6 +2412,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; > } > @@ -2500,6 +2518,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; > @@ -2524,6 +2543,10 @@ pinctrl_handle_put_dhcpv6_opts( > break; > } > > + case DHCPV6_OPT_FQDN_CODE: > + fqdn_flags = *(in_dhcpv6_data + sizeof *in_opt); > + break; > + > default: > break; > } > @@ -2547,7 +2570,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; > } > @@ -2762,48 +2785,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. */ > diff --git a/lib/actions.c b/lib/actions.c > index ec27223f9..037172e60 100644 > --- a/lib/actions.c > +++ b/lib/actions.c > @@ -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); > @@ -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); > } > } > > diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h > index d9b103119..9dc331421 100644 > --- a/lib/ovn-l7.h > +++ b/lib/ovn-l7.h > @@ -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 > > @@ -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; > diff --git a/lib/ovn-util.c b/lib/ovn-util.c > index bffb521cf..12ca27319 100644 > --- a/lib/ovn-util.c > +++ b/lib/ovn-util.c > @@ -1126,3 +1126,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; > +} > diff --git a/lib/ovn-util.h b/lib/ovn-util.h > index b17b0e236..0af940918 100644 > --- a/lib/ovn-util.h > +++ b/lib/ovn-util.h > @@ -403,4 +403,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 */ > diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c > index 3515b68a2..2bafbd4af 100644 > --- a/northd/ovn-northd.c > +++ b/northd/ovn-northd.c > @@ -273,7 +273,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 > diff --git a/ovn-nb.xml b/ovn-nb.xml > index 9afe3b584..05e155b89 100644 > --- a/ovn-nb.xml > +++ b/ovn-nb.xml > @@ -4191,6 +4191,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> > > diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema > index f59af8cc5..06e16b403 100644 > --- a/ovn-sb.ovsschema > +++ b/ovn-sb.ovsschema > @@ -1,7 +1,7 @@ > { > "name": "OVN_Southbound", > - "version": "20.27.2", > - "cksum": "1291808617 30462", > + "version": "20.27.3", > + "cksum": "3876528905 30472", > "tables": { > "SB_Global": { > "columns": { > @@ -311,7 +311,7 @@ > "type": { > "type": {"key": { > "type": "string", > - "enum": ["set", ["ipv6", "str", "mac"]]}}}}, > + "enum": ["set", ["ipv6", "str", "mac", "domain"]]}}}}, > "isRoot": true}, > "Connection": { > "columns": { > diff --git a/tests/ovn.at b/tests/ovn.at > index 5e6a8fefa..2b8ed7571 100644 > --- a/tests/ovn.at > +++ b/tests/ovn.at > @@ -1697,6 +1697,9 @@ reg1[0] = put_dhcpv6_opts(bootfile_name="https://127.0.0.1/boot.ipxe"); > reg1[0] = put_dhcpv6_opts(bootfile_name_alt="https://127.0.0.1/boot.ipxe"); > formats as reg1[0] = put_dhcpv6_opts(bootfile_name_alt = "https://127.0.0.1/boot.ipxe"); > encodes as controller(userdata=00.00.00.05.00.00.00.00.00.01.de.10.00.00.00.40.00.fe.00.1b.68.74.74.70.73.3a.2f.2f.31.32.37.2e.30.2e.30.2e.31.2f.62.6f.6f.74.2e.69.70.78.65,pause) > +reg1[0] = put_dhcpv6_opts(fqdn="ovn.org"); > + formats as reg1[0] = put_dhcpv6_opts(fqdn = "ovn.org"); > + encodes as controller(userdata=00.00.00.05.00.00.00.00.00.01.de.10.00.00.00.40.00.27.00.09.03.6f.76.6e.03.6f.72.67.00,pause) > > # lookup_nd > reg2[0] = lookup_nd(inport, ip6.dst, eth.src); > @@ -7083,6 +7086,7 @@ AT_CLEANUP > > OVN_FOR_EACH_NORTHD([ > AT_SETUP([dhcpv6 : 1 HV, 2 LS, 5 LSPs]) > +AT_SKIP_IF([test $HAVE_SCAPY = no]) > ovn_start > > ovn-nbctl ls-add ls1 > @@ -7171,61 +7175,56 @@ trim_zeros() { > # packet should be received twice (one from ovn-controller and the other > # from the "ovs-ofctl monitor br-int resume" > test_dhcpv6() { > - local inport=$1 src_mac=$2 src_lla=$3 msg_code=$4 offer_ip=$5 ipxe=$6 > - if test $ipxe -eq 2; then > - req_len=34 > - elif test $msg_code != 0b; then > - req_len=2a > - else > - req_len=1a > - fi > - local request=ffffffffffff${src_mac}86dd6000000000${req_len}1101${src_lla} > - # dst ip ff02::1:2 > - request=${request}ff020000000000000000000000010002 > - # udp header and dhcpv6 header > - request=${request}0222022300${req_len}ffff${msg_code}010203 > - # Client identifier > - request=${request}0001000a00030001${src_mac} > + local inport=$1 src_mac=$2 src_lla=$3 msg_code=$4 offer_ip=$5 boot_file=$6 > + local fqdn=$7 > + > + local req_scapy="Ether(dst='ff:ff:ff:ff:ff:ff', src='${src_mac}')/ \ > + IPv6(dst='ff02::1:2', src='${src_lla}')/ \ > + UDP(sport=546, dport=547)/ \ > + DHCP6(msgtype=${msg_code}, trid=0x010203)/ \ > + DHCP6OptClientId(duid=DUID_LL(lladdr='${src_mac}'))" > + > # Add IA-NA (Identity Association for Non Temporary Address) if msg_code > # is not 11 (information request packet) > - if test $msg_code != 0b; then > - request=${request}0003000c0102030400000e1000001518 > + if test $msg_code != 11; then > + req_scapy="${req_scapy}/DHCP6OptIA_NA(iaid=0x01020304, T1=3600, T2=5400)" > fi > - if test $ipxe -eq 2; then > - request=${request}000f0006000669505845 > + if test "$boot_file" = "bootfile_name"; then > + local uc_data="USER_CLASS_DATA(data=b'iPXE')" > + req_scapy="${req_scapy}/DHCP6OptUserClass(userclassdata=[[${uc_data}]])" > fi > - shift; shift; shift; shift; shift; shift; > + if test -n "$fqdn"; then > + req_scapy="${req_scapy}/DHCP6OptClientFQDN(flags=0x01, fqdn=b'test')" > + fi > + request=$(fmt_pkt "${req_scapy}") > + shift; shift; shift; shift; shift; shift; shift; > if test $offer_ip != 0; then > - local server_mac=000000100001 > - local server_lla=fe80000000000000020000fffe100001 > - local reply_code=07 > - if test $msg_code = 01; then > - reply_code=02 > - fi > - local msg_len=54 > - if test $ipxe -eq 1; then > - msg_len=69 > - elif test $ipxe -eq 2; then > - msg_len=65 > - elif test $offer_ip = 1; then > - msg_len=28 > + local reply_code=7 > + if test $msg_code = 1; then > + reply_code=2 > fi > - local reply=${src_mac}${server_mac}86dd6000000000${msg_len}1101${server_lla}${src_lla} > - # udp header and dhcpv6 header > - reply=${reply}0223022200${msg_len}ffff${reply_code}010203 > - # Client identifier > - reply=${reply}0001000a00030001${src_mac} > - # IA-NA > + > + local rep_scapy="Ether(dst='${src_mac}', src='00:00:00:10:00:01')/ \ > + IPv6(dst='${src_lla}', src='fe80::0200:00ff:fe10:0001')/ \ > + UDP(sport=547, dport=546)/ \ > + DHCP6(msgtype=${reply_code}, trid=0x010203)/ \ > + DHCP6OptClientId(duid=DUID_LL(lladdr='${src_mac}'))" > + > if test $offer_ip != 1; then > - reply=${reply}0003002801020304ffffffffffffffff00050018${offer_ip}ffffffffffffffff > + local ip_scapy="DHCP6OptIAAddress(addr='${offer_ip}', \ > + preflft=0xffffffff, validlft=0xffffffff)" > + rep_scapy="${rep_scapy}/ \ > + DHCP6OptIA_NA(iaid=0x01020304, T1=0xffffffff, \ > + T2=0xffffffff, ianaopts=[[${ip_scapy}]])" > fi > - if test $ipxe -eq 1; then > - reply=${reply}003b0011626f6f7466696c655f6e616d655f616c74 > - elif test $ipxe -eq 2; then > - reply=${reply}003b000d626f6f7466696c655f6e616d65 > + if test -n "$boot_file"; then > + rep_scapy="${rep_scapy}/DHCP6OptBootFileUrl(optdata=b'${boot_file}')" > fi > - # Server identifier > - reply=${reply}0002000a00030001${server_mac} > + if test -n "$fqdn"; then > + rep_scapy="${rep_scapy}/DHCP6OptClientFQDN(flags=0x06, fqdn=b'${fqdn}')" > + fi > + rep_scapy="${rep_scapy}/DHCP6OptServerId(duid=DUID_LL(lladdr='00:00:00:10:00:01'))" > + reply=$(fmt_pkt "${rep_scapy}") > echo $reply | trim_zeros >> $inport.expected > else > for outport; do > @@ -7249,16 +7248,16 @@ echo "---------------------" > ovn-sbctl list logical_flow > echo "---------------------" > > -ovn-sbctl dump-flows > sbflows > +#ovn-sbctl dump-flows > sbflows > AT_CAPTURE_FILE([sbflows]) > > echo "------ hv1 dump ----------" > as hv1 ovs-ofctl dump-flows br-int > > -src_mac=f00000000001 > -src_lla=fe80000000000000f20000fffe000001 > -offer_ip=ae700000000000000000000000000004 > -test_dhcpv6 1 $src_mac $src_lla 01 $offer_ip 0 > +src_mac="f0:00:00:00:00:01" > +src_lla="fe80::f200:00ff:fe00:0001" > +offer_ip="ae70::4" > +test_dhcpv6 1 $src_mac $src_lla 1 $offer_ip "" "" > > # NXT_RESUMEs should be 1. > OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) > @@ -7281,12 +7280,12 @@ rm 1.expected > reset_pcap_file hv1-vif1 hv1/vif1 > reset_pcap_file hv1-vif2 hv1/vif2 > > -src_mac=f00000000002 > -src_lla=fe80000000000000f20000fffe000002 > -offer_ip=ae700000000000000000000000000005 > +src_mac="f0:00:00:00:00:02" > +src_lla="fe80::f200:00ff:fe00:0002" > +offer_ip="ae70::5" > # Set invalid msg_type > > -test_dhcpv6 2 $src_mac $src_lla 10 0 0 1 1 > +test_dhcpv6 2 $src_mac $src_lla 16 0 "" "" 1 1 > > # NXT_RESUMEs should be 2. > OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) > @@ -7305,9 +7304,9 @@ AT_CHECK([cat 1.packets], [0], [expout]) > # There should be no DHCPv6 reply from ovn-controller and the request packet > # should be received by ls2-lp2. > > -src_mac=f00000000003 > -src_lla=fe80000000000000f20000fffe000003 > -test_dhcpv6 3 $src_mac $src_lla 01 0 0 4 > +src_mac="f0:00:00:00:00:03" > +src_lla="fe80::f200:00ff:fe00:0003" > +test_dhcpv6 3 $src_mac $src_lla 1 0 "" "" 4 > > # NXT_RESUMEs should be 2 only. > OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) > @@ -7323,10 +7322,10 @@ AT_CHECK([cat 4.packets], [0], [expout]) > > # Send DHCPv6 packet on ls1-lp3. native DHCPv6 works as stateless mode for this port. > # The DHCPv6 reply shouldn't contain offer_ip. > -src_mac=f00000000022 > -src_lla=fe80000000000000f20000fffe000022 > +src_mac="f0:00:00:00:00:22" > +src_lla="fe80::f200:00ff:fe00:0022" > reset_pcap_file hv1-vif5 hv1/vif5 > -test_dhcpv6 5 $src_mac $src_lla 01 1 0 5 > +test_dhcpv6 5 $src_mac $src_lla 1 1 "" "" 5 > > # NXT_RESUMEs should be 3. > OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) > @@ -7338,11 +7337,11 @@ AT_CHECK([cat 5.packets | cut -c 1-120,125- ], [0], [expout]) > > # Send DHCPv6 information request (code 11) on ls1-lp3. The DHCPv6 reply > # shouldn't contain offer_ip > -src_mac=f00000000022 > -src_lla=fe80000000000000f20000fffe000022 > +src_mac="f0:00:00:00:00:22" > +src_lla="fe80::f200:00ff:fe00:0022" > reset_pcap_file hv1-vif5 hv1/vif5 > rm -f 5.expected > -test_dhcpv6 5 $src_mac $src_lla 0b 1 0 5 > +test_dhcpv6 5 $src_mac $src_lla 11 1 "" "" 5 > > # NXT_RESUMEs should be 4. > OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) > @@ -7363,11 +7362,11 @@ ovn-nbctl lsp-set-dhcpv6-options ls1-lp2 ${d1} > > reset_pcap_file hv1-vif2 hv1/vif2 > > -src_mac=f00000000002 > -src_lla=fe80000000000000f20000fffe000002 > -offer_ip=ae700000000000000000000000000005 > +src_mac="f0:00:00:00:00:02" > +src_lla="fe80::f200:00ff:fe00:0002" > +offer_ip="ae70::5" > > -test_dhcpv6 2 $src_mac $src_lla 01 $offer_ip 1 > +test_dhcpv6 2 $src_mac $src_lla 1 $offer_ip "bootfile_name_alt" "" > # NXT_RESUMEs should be 5. > OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) > > @@ -7381,7 +7380,7 @@ AT_CHECK([cat 2.packets | cut -c 1-120,125- ], [0], [expout]) > reset_pcap_file hv1-vif2 hv1/vif2 > rm 2.packets 2.expected > > -test_dhcpv6 2 $src_mac $src_lla 01 $offer_ip 2 > +test_dhcpv6 2 $src_mac $src_lla 1 $offer_ip "bootfile_name" "" > # NXT_RESUMEs should be 6. > OVS_WAIT_UNTIL([test 6 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) > > @@ -7389,6 +7388,24 @@ $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap | trim_zeros > 2.pa > cat 2.expected | cut -c 1-120,125- > expout > AT_CHECK([cat 2.packets | cut -c 1-120,125- ], [0], [expout]) > > +ovn-nbctl --all destroy dhcp-option > +d1="$(ovn-nbctl create DHCP_Options cidr="ae70\:\:/64")" > +ovn-nbctl dhcp-options-set-options $d1 \ > + server_id=00:00:00:10:00:01 \ > + fqdn=\"ovn.org\" > +ovn-nbctl --wait=hv lsp-set-dhcpv6-options ls1-lp2 ${d1} > + > +reset_pcap_file hv1-vif2 hv1/vif2 > +rm 2.packets 2.expected > + > +test_dhcpv6 2 $src_mac $src_lla 1 $offer_ip "" "ovn.org" > +# NXT_RESUMEs should be 7. > +OVS_WAIT_UNTIL([test 7 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) > + > +$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap | trim_zeros > 2.packets > +cat 2.expected | cut -c 1-120,125- > expout > +AT_CHECK([cat 2.packets | cut -c 1-120,125- ], [0], [expout]) > + > OVN_CLEANUP([hv1]) > > AT_CLEANUP > diff --git a/tests/test-ovn.c b/tests/test-ovn.c > index ce9213c1d..6c7754eac 100644 > --- a/tests/test-ovn.c > +++ b/tests/test-ovn.c > @@ -207,6 +207,7 @@ create_gen_opts(struct hmap *dhcp_opts, struct hmap *dhcpv6_opts, > dhcp_opt_add(dhcpv6_opts, "domain_search", 24, "str"); > dhcp_opt_add(dhcpv6_opts, "bootfile_name", 59, "str"); > dhcp_opt_add(dhcpv6_opts, "bootfile_name_alt", 254, "str"); > + dhcp_opt_add(dhcpv6_opts, "fqdn", 39, "domain"); > > /* IPv6 ND RA options. */ > hmap_init(nd_ra_opts);
Because the FDB aging series went in, there was a small merge conflict when trying to apply this patch. I updated the ovn-sb.xml version to "20.27.4" instead of "20.27.3". I applied this patch to main. Thanks Ales! On 6/9/23 15:15, Mark Michelson wrote: > Thanks for the changes Ales. > > Acked-by: Mark Michelson <mmichels@redhat.com> > > On 6/6/23 02:51, Ales Musil wrote: >> 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> >> --- >> v2: Rebase on top of current main. >> Address comments from Mark: >> - Properly propagate option flags. >> - Extract the fqdn encoding to common function. >> --- >> NEWS | 2 + >> controller/pinctrl.c | 69 +++++++++---------- >> lib/actions.c | 9 ++- >> lib/ovn-l7.h | 10 +++ >> lib/ovn-util.c | 27 ++++++++ >> lib/ovn-util.h | 5 ++ >> northd/ovn-northd.c | 3 +- >> ovn-nb.xml | 7 ++ >> ovn-sb.ovsschema | 6 +- >> tests/ovn.at | 155 ++++++++++++++++++++++++------------------- >> tests/test-ovn.c | 1 + >> 11 files changed, 181 insertions(+), 113 deletions(-) >> >> diff --git a/NEWS b/NEWS >> index 645acea1f..e6c87e9ad 100644 >> --- a/NEWS >> +++ b/NEWS >> @@ -1,5 +1,7 @@ >> Post v23.06.0 >> ------------- >> + - Add DHCPv6 "fqdn" (39) option, that works similarly to >> + DHCPv4 "hostname" (12) option. >> OVN v23.06.0 - 01 Jun 2023 >> -------------------------- >> diff --git a/controller/pinctrl.c b/controller/pinctrl.c >> index c396ad4c2..d7bd20ed6 100644 >> --- a/controller/pinctrl.c >> +++ b/controller/pinctrl.c >> @@ -2288,7 +2288,7 @@ exit: >> 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( >> @@ -2412,6 +2412,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; >> } >> @@ -2500,6 +2518,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; >> @@ -2524,6 +2543,10 @@ pinctrl_handle_put_dhcpv6_opts( >> break; >> } >> + case DHCPV6_OPT_FQDN_CODE: >> + fqdn_flags = *(in_dhcpv6_data + sizeof *in_opt); >> + break; >> + >> default: >> break; >> } >> @@ -2547,7 +2570,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; >> } >> @@ -2762,48 +2785,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. */ >> diff --git a/lib/actions.c b/lib/actions.c >> index ec27223f9..037172e60 100644 >> --- a/lib/actions.c >> +++ b/lib/actions.c >> @@ -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); >> @@ -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); >> } >> } >> diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h >> index d9b103119..9dc331421 100644 >> --- a/lib/ovn-l7.h >> +++ b/lib/ovn-l7.h >> @@ -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 >> @@ -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; >> diff --git a/lib/ovn-util.c b/lib/ovn-util.c >> index bffb521cf..12ca27319 100644 >> --- a/lib/ovn-util.c >> +++ b/lib/ovn-util.c >> @@ -1126,3 +1126,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; >> +} >> diff --git a/lib/ovn-util.h b/lib/ovn-util.h >> index b17b0e236..0af940918 100644 >> --- a/lib/ovn-util.h >> +++ b/lib/ovn-util.h >> @@ -403,4 +403,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 */ >> diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c >> index 3515b68a2..2bafbd4af 100644 >> --- a/northd/ovn-northd.c >> +++ b/northd/ovn-northd.c >> @@ -273,7 +273,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 >> diff --git a/ovn-nb.xml b/ovn-nb.xml >> index 9afe3b584..05e155b89 100644 >> --- a/ovn-nb.xml >> +++ b/ovn-nb.xml >> @@ -4191,6 +4191,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> >> diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema >> index f59af8cc5..06e16b403 100644 >> --- a/ovn-sb.ovsschema >> +++ b/ovn-sb.ovsschema >> @@ -1,7 +1,7 @@ >> { >> "name": "OVN_Southbound", >> - "version": "20.27.2", >> - "cksum": "1291808617 30462", >> + "version": "20.27.3", >> + "cksum": "3876528905 30472", >> "tables": { >> "SB_Global": { >> "columns": { >> @@ -311,7 +311,7 @@ >> "type": { >> "type": {"key": { >> "type": "string", >> - "enum": ["set", ["ipv6", "str", "mac"]]}}}}, >> + "enum": ["set", ["ipv6", "str", "mac", >> "domain"]]}}}}, >> "isRoot": true}, >> "Connection": { >> "columns": { >> diff --git a/tests/ovn.at b/tests/ovn.at >> index 5e6a8fefa..2b8ed7571 100644 >> --- a/tests/ovn.at >> +++ b/tests/ovn.at >> @@ -1697,6 +1697,9 @@ reg1[0] = >> put_dhcpv6_opts(bootfile_name="https://127.0.0.1/boot.ipxe"); >> reg1[0] = >> put_dhcpv6_opts(bootfile_name_alt="https://127.0.0.1/boot.ipxe"); >> formats as reg1[0] = put_dhcpv6_opts(bootfile_name_alt = >> "https://127.0.0.1/boot.ipxe"); >> encodes as >> controller(userdata=00.00.00.05.00.00.00.00.00.01.de.10.00.00.00.40.00.fe.00.1b.68.74.74.70.73.3a.2f.2f.31.32.37.2e.30.2e.30.2e.31.2f.62.6f.6f.74.2e.69.70.78.65,pause) >> +reg1[0] = put_dhcpv6_opts(fqdn="ovn.org"); >> + formats as reg1[0] = put_dhcpv6_opts(fqdn = "ovn.org"); >> + encodes as >> controller(userdata=00.00.00.05.00.00.00.00.00.01.de.10.00.00.00.40.00.27.00.09.03.6f.76.6e.03.6f.72.67.00,pause) >> # lookup_nd >> reg2[0] = lookup_nd(inport, ip6.dst, eth.src); >> @@ -7083,6 +7086,7 @@ AT_CLEANUP >> OVN_FOR_EACH_NORTHD([ >> AT_SETUP([dhcpv6 : 1 HV, 2 LS, 5 LSPs]) >> +AT_SKIP_IF([test $HAVE_SCAPY = no]) >> ovn_start >> ovn-nbctl ls-add ls1 >> @@ -7171,61 +7175,56 @@ trim_zeros() { >> # packet should be received twice (one from ovn-controller and the >> other >> # from the "ovs-ofctl monitor br-int resume" >> test_dhcpv6() { >> - local inport=$1 src_mac=$2 src_lla=$3 msg_code=$4 offer_ip=$5 >> ipxe=$6 >> - if test $ipxe -eq 2; then >> - req_len=34 >> - elif test $msg_code != 0b; then >> - req_len=2a >> - else >> - req_len=1a >> - fi >> - local >> request=ffffffffffff${src_mac}86dd6000000000${req_len}1101${src_lla} >> - # dst ip ff02::1:2 >> - request=${request}ff020000000000000000000000010002 >> - # udp header and dhcpv6 header >> - request=${request}0222022300${req_len}ffff${msg_code}010203 >> - # Client identifier >> - request=${request}0001000a00030001${src_mac} >> + local inport=$1 src_mac=$2 src_lla=$3 msg_code=$4 offer_ip=$5 >> boot_file=$6 >> + local fqdn=$7 >> + >> + local req_scapy="Ether(dst='ff:ff:ff:ff:ff:ff', src='${src_mac}')/ \ >> + IPv6(dst='ff02::1:2', src='${src_lla}')/ \ >> + UDP(sport=546, dport=547)/ \ >> + DHCP6(msgtype=${msg_code}, trid=0x010203)/ \ >> + >> DHCP6OptClientId(duid=DUID_LL(lladdr='${src_mac}'))" >> + >> # Add IA-NA (Identity Association for Non Temporary Address) if >> msg_code >> # is not 11 (information request packet) >> - if test $msg_code != 0b; then >> - request=${request}0003000c0102030400000e1000001518 >> + if test $msg_code != 11; then >> + req_scapy="${req_scapy}/DHCP6OptIA_NA(iaid=0x01020304, >> T1=3600, T2=5400)" >> fi >> - if test $ipxe -eq 2; then >> - request=${request}000f0006000669505845 >> + if test "$boot_file" = "bootfile_name"; then >> + local uc_data="USER_CLASS_DATA(data=b'iPXE')" >> + >> req_scapy="${req_scapy}/DHCP6OptUserClass(userclassdata=[[${uc_data}]])" >> fi >> - shift; shift; shift; shift; shift; shift; >> + if test -n "$fqdn"; then >> + req_scapy="${req_scapy}/DHCP6OptClientFQDN(flags=0x01, >> fqdn=b'test')" >> + fi >> + request=$(fmt_pkt "${req_scapy}") >> + shift; shift; shift; shift; shift; shift; shift; >> if test $offer_ip != 0; then >> - local server_mac=000000100001 >> - local server_lla=fe80000000000000020000fffe100001 >> - local reply_code=07 >> - if test $msg_code = 01; then >> - reply_code=02 >> - fi >> - local msg_len=54 >> - if test $ipxe -eq 1; then >> - msg_len=69 >> - elif test $ipxe -eq 2; then >> - msg_len=65 >> - elif test $offer_ip = 1; then >> - msg_len=28 >> + local reply_code=7 >> + if test $msg_code = 1; then >> + reply_code=2 >> fi >> - local >> reply=${src_mac}${server_mac}86dd6000000000${msg_len}1101${server_lla}${src_lla} >> - # udp header and dhcpv6 header >> - reply=${reply}0223022200${msg_len}ffff${reply_code}010203 >> - # Client identifier >> - reply=${reply}0001000a00030001${src_mac} >> - # IA-NA >> + >> + local rep_scapy="Ether(dst='${src_mac}', >> src='00:00:00:10:00:01')/ \ >> + IPv6(dst='${src_lla}', >> src='fe80::0200:00ff:fe10:0001')/ \ >> + UDP(sport=547, dport=546)/ \ >> + DHCP6(msgtype=${reply_code}, trid=0x010203)/ \ >> + >> DHCP6OptClientId(duid=DUID_LL(lladdr='${src_mac}'))" >> + >> if test $offer_ip != 1; then >> - >> reply=${reply}0003002801020304ffffffffffffffff00050018${offer_ip}ffffffffffffffff >> + local ip_scapy="DHCP6OptIAAddress(addr='${offer_ip}', \ >> + preflft=0xffffffff, validlft=0xffffffff)" >> + rep_scapy="${rep_scapy}/ \ >> + DHCP6OptIA_NA(iaid=0x01020304, T1=0xffffffff, \ >> + T2=0xffffffff, ianaopts=[[${ip_scapy}]])" >> fi >> - if test $ipxe -eq 1; then >> - reply=${reply}003b0011626f6f7466696c655f6e616d655f616c74 >> - elif test $ipxe -eq 2; then >> - reply=${reply}003b000d626f6f7466696c655f6e616d65 >> + if test -n "$boot_file"; then >> + >> rep_scapy="${rep_scapy}/DHCP6OptBootFileUrl(optdata=b'${boot_file}')" >> fi >> - # Server identifier >> - reply=${reply}0002000a00030001${server_mac} >> + if test -n "$fqdn"; then >> + rep_scapy="${rep_scapy}/DHCP6OptClientFQDN(flags=0x06, >> fqdn=b'${fqdn}')" >> + fi >> + >> rep_scapy="${rep_scapy}/DHCP6OptServerId(duid=DUID_LL(lladdr='00:00:00:10:00:01'))" >> + reply=$(fmt_pkt "${rep_scapy}") >> echo $reply | trim_zeros >> $inport.expected >> else >> for outport; do >> @@ -7249,16 +7248,16 @@ echo "---------------------" >> ovn-sbctl list logical_flow >> echo "---------------------" >> -ovn-sbctl dump-flows > sbflows >> +#ovn-sbctl dump-flows > sbflows >> AT_CAPTURE_FILE([sbflows]) >> echo "------ hv1 dump ----------" >> as hv1 ovs-ofctl dump-flows br-int >> -src_mac=f00000000001 >> -src_lla=fe80000000000000f20000fffe000001 >> -offer_ip=ae700000000000000000000000000004 >> -test_dhcpv6 1 $src_mac $src_lla 01 $offer_ip 0 >> +src_mac="f0:00:00:00:00:01" >> +src_lla="fe80::f200:00ff:fe00:0001" >> +offer_ip="ae70::4" >> +test_dhcpv6 1 $src_mac $src_lla 1 $offer_ip "" "" >> # NXT_RESUMEs should be 1. >> OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor*.log | grep -c >> NXT_RESUME`]) >> @@ -7281,12 +7280,12 @@ rm 1.expected >> reset_pcap_file hv1-vif1 hv1/vif1 >> reset_pcap_file hv1-vif2 hv1/vif2 >> -src_mac=f00000000002 >> -src_lla=fe80000000000000f20000fffe000002 >> -offer_ip=ae700000000000000000000000000005 >> +src_mac="f0:00:00:00:00:02" >> +src_lla="fe80::f200:00ff:fe00:0002" >> +offer_ip="ae70::5" >> # Set invalid msg_type >> -test_dhcpv6 2 $src_mac $src_lla 10 0 0 1 1 >> +test_dhcpv6 2 $src_mac $src_lla 16 0 "" "" 1 1 >> # NXT_RESUMEs should be 2. >> OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c >> NXT_RESUME`]) >> @@ -7305,9 +7304,9 @@ AT_CHECK([cat 1.packets], [0], [expout]) >> # There should be no DHCPv6 reply from ovn-controller and the >> request packet >> # should be received by ls2-lp2. >> -src_mac=f00000000003 >> -src_lla=fe80000000000000f20000fffe000003 >> -test_dhcpv6 3 $src_mac $src_lla 01 0 0 4 >> +src_mac="f0:00:00:00:00:03" >> +src_lla="fe80::f200:00ff:fe00:0003" >> +test_dhcpv6 3 $src_mac $src_lla 1 0 "" "" 4 >> # NXT_RESUMEs should be 2 only. >> OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c >> NXT_RESUME`]) >> @@ -7323,10 +7322,10 @@ AT_CHECK([cat 4.packets], [0], [expout]) >> # Send DHCPv6 packet on ls1-lp3. native DHCPv6 works as stateless >> mode for this port. >> # The DHCPv6 reply shouldn't contain offer_ip. >> -src_mac=f00000000022 >> -src_lla=fe80000000000000f20000fffe000022 >> +src_mac="f0:00:00:00:00:22" >> +src_lla="fe80::f200:00ff:fe00:0022" >> reset_pcap_file hv1-vif5 hv1/vif5 >> -test_dhcpv6 5 $src_mac $src_lla 01 1 0 5 >> +test_dhcpv6 5 $src_mac $src_lla 1 1 "" "" 5 >> # NXT_RESUMEs should be 3. >> OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c >> NXT_RESUME`]) >> @@ -7338,11 +7337,11 @@ AT_CHECK([cat 5.packets | cut -c 1-120,125- ], >> [0], [expout]) >> # Send DHCPv6 information request (code 11) on ls1-lp3. The DHCPv6 >> reply >> # shouldn't contain offer_ip >> -src_mac=f00000000022 >> -src_lla=fe80000000000000f20000fffe000022 >> +src_mac="f0:00:00:00:00:22" >> +src_lla="fe80::f200:00ff:fe00:0022" >> reset_pcap_file hv1-vif5 hv1/vif5 >> rm -f 5.expected >> -test_dhcpv6 5 $src_mac $src_lla 0b 1 0 5 >> +test_dhcpv6 5 $src_mac $src_lla 11 1 "" "" 5 >> # NXT_RESUMEs should be 4. >> OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c >> NXT_RESUME`]) >> @@ -7363,11 +7362,11 @@ ovn-nbctl lsp-set-dhcpv6-options ls1-lp2 ${d1} >> reset_pcap_file hv1-vif2 hv1/vif2 >> -src_mac=f00000000002 >> -src_lla=fe80000000000000f20000fffe000002 >> -offer_ip=ae700000000000000000000000000005 >> +src_mac="f0:00:00:00:00:02" >> +src_lla="fe80::f200:00ff:fe00:0002" >> +offer_ip="ae70::5" >> -test_dhcpv6 2 $src_mac $src_lla 01 $offer_ip 1 >> +test_dhcpv6 2 $src_mac $src_lla 1 $offer_ip "bootfile_name_alt" "" >> # NXT_RESUMEs should be 5. >> OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c >> NXT_RESUME`]) >> @@ -7381,7 +7380,7 @@ AT_CHECK([cat 2.packets | cut -c 1-120,125- ], >> [0], [expout]) >> reset_pcap_file hv1-vif2 hv1/vif2 >> rm 2.packets 2.expected >> -test_dhcpv6 2 $src_mac $src_lla 01 $offer_ip 2 >> +test_dhcpv6 2 $src_mac $src_lla 1 $offer_ip "bootfile_name" "" >> # NXT_RESUMEs should be 6. >> OVS_WAIT_UNTIL([test 6 = `cat ofctl_monitor*.log | grep -c >> NXT_RESUME`]) >> @@ -7389,6 +7388,24 @@ $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" >> hv1/vif2-tx.pcap | trim_zeros > 2.pa >> cat 2.expected | cut -c 1-120,125- > expout >> AT_CHECK([cat 2.packets | cut -c 1-120,125- ], [0], [expout]) >> +ovn-nbctl --all destroy dhcp-option >> +d1="$(ovn-nbctl create DHCP_Options cidr="ae70\:\:/64")" >> +ovn-nbctl dhcp-options-set-options $d1 \ >> + server_id=00:00:00:10:00:01 \ >> + fqdn=\"ovn.org\" >> +ovn-nbctl --wait=hv lsp-set-dhcpv6-options ls1-lp2 ${d1} >> + >> +reset_pcap_file hv1-vif2 hv1/vif2 >> +rm 2.packets 2.expected >> + >> +test_dhcpv6 2 $src_mac $src_lla 1 $offer_ip "" "ovn.org" >> +# NXT_RESUMEs should be 7. >> +OVS_WAIT_UNTIL([test 7 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) >> + >> +$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap | >> trim_zeros > 2.packets >> +cat 2.expected | cut -c 1-120,125- > expout >> +AT_CHECK([cat 2.packets | cut -c 1-120,125- ], [0], [expout]) >> + >> OVN_CLEANUP([hv1]) >> AT_CLEANUP >> diff --git a/tests/test-ovn.c b/tests/test-ovn.c >> index ce9213c1d..6c7754eac 100644 >> --- a/tests/test-ovn.c >> +++ b/tests/test-ovn.c >> @@ -207,6 +207,7 @@ create_gen_opts(struct hmap *dhcp_opts, struct >> hmap *dhcpv6_opts, >> dhcp_opt_add(dhcpv6_opts, "domain_search", 24, "str"); >> dhcp_opt_add(dhcpv6_opts, "bootfile_name", 59, "str"); >> dhcp_opt_add(dhcpv6_opts, "bootfile_name_alt", 254, "str"); >> + dhcp_opt_add(dhcpv6_opts, "fqdn", 39, "domain"); >> /* IPv6 ND RA options. */ >> hmap_init(nd_ra_opts); >
diff --git a/NEWS b/NEWS index 645acea1f..e6c87e9ad 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,7 @@ Post v23.06.0 ------------- + - Add DHCPv6 "fqdn" (39) option, that works similarly to + DHCPv4 "hostname" (12) option. OVN v23.06.0 - 01 Jun 2023 -------------------------- diff --git a/controller/pinctrl.c b/controller/pinctrl.c index c396ad4c2..d7bd20ed6 100644 --- a/controller/pinctrl.c +++ b/controller/pinctrl.c @@ -2288,7 +2288,7 @@ exit: 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( @@ -2412,6 +2412,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; } @@ -2500,6 +2518,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; @@ -2524,6 +2543,10 @@ pinctrl_handle_put_dhcpv6_opts( break; } + case DHCPV6_OPT_FQDN_CODE: + fqdn_flags = *(in_dhcpv6_data + sizeof *in_opt); + break; + default: break; } @@ -2547,7 +2570,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; } @@ -2762,48 +2785,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. */ diff --git a/lib/actions.c b/lib/actions.c index ec27223f9..037172e60 100644 --- a/lib/actions.c +++ b/lib/actions.c @@ -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); @@ -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); } } diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h index d9b103119..9dc331421 100644 --- a/lib/ovn-l7.h +++ b/lib/ovn-l7.h @@ -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 @@ -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; diff --git a/lib/ovn-util.c b/lib/ovn-util.c index bffb521cf..12ca27319 100644 --- a/lib/ovn-util.c +++ b/lib/ovn-util.c @@ -1126,3 +1126,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; +} diff --git a/lib/ovn-util.h b/lib/ovn-util.h index b17b0e236..0af940918 100644 --- a/lib/ovn-util.h +++ b/lib/ovn-util.h @@ -403,4 +403,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 */ diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index 3515b68a2..2bafbd4af 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -273,7 +273,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 diff --git a/ovn-nb.xml b/ovn-nb.xml index 9afe3b584..05e155b89 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -4191,6 +4191,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> diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema index f59af8cc5..06e16b403 100644 --- a/ovn-sb.ovsschema +++ b/ovn-sb.ovsschema @@ -1,7 +1,7 @@ { "name": "OVN_Southbound", - "version": "20.27.2", - "cksum": "1291808617 30462", + "version": "20.27.3", + "cksum": "3876528905 30472", "tables": { "SB_Global": { "columns": { @@ -311,7 +311,7 @@ "type": { "type": {"key": { "type": "string", - "enum": ["set", ["ipv6", "str", "mac"]]}}}}, + "enum": ["set", ["ipv6", "str", "mac", "domain"]]}}}}, "isRoot": true}, "Connection": { "columns": { diff --git a/tests/ovn.at b/tests/ovn.at index 5e6a8fefa..2b8ed7571 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -1697,6 +1697,9 @@ reg1[0] = put_dhcpv6_opts(bootfile_name="https://127.0.0.1/boot.ipxe"); reg1[0] = put_dhcpv6_opts(bootfile_name_alt="https://127.0.0.1/boot.ipxe"); formats as reg1[0] = put_dhcpv6_opts(bootfile_name_alt = "https://127.0.0.1/boot.ipxe"); encodes as controller(userdata=00.00.00.05.00.00.00.00.00.01.de.10.00.00.00.40.00.fe.00.1b.68.74.74.70.73.3a.2f.2f.31.32.37.2e.30.2e.30.2e.31.2f.62.6f.6f.74.2e.69.70.78.65,pause) +reg1[0] = put_dhcpv6_opts(fqdn="ovn.org"); + formats as reg1[0] = put_dhcpv6_opts(fqdn = "ovn.org"); + encodes as controller(userdata=00.00.00.05.00.00.00.00.00.01.de.10.00.00.00.40.00.27.00.09.03.6f.76.6e.03.6f.72.67.00,pause) # lookup_nd reg2[0] = lookup_nd(inport, ip6.dst, eth.src); @@ -7083,6 +7086,7 @@ AT_CLEANUP OVN_FOR_EACH_NORTHD([ AT_SETUP([dhcpv6 : 1 HV, 2 LS, 5 LSPs]) +AT_SKIP_IF([test $HAVE_SCAPY = no]) ovn_start ovn-nbctl ls-add ls1 @@ -7171,61 +7175,56 @@ trim_zeros() { # packet should be received twice (one from ovn-controller and the other # from the "ovs-ofctl monitor br-int resume" test_dhcpv6() { - local inport=$1 src_mac=$2 src_lla=$3 msg_code=$4 offer_ip=$5 ipxe=$6 - if test $ipxe -eq 2; then - req_len=34 - elif test $msg_code != 0b; then - req_len=2a - else - req_len=1a - fi - local request=ffffffffffff${src_mac}86dd6000000000${req_len}1101${src_lla} - # dst ip ff02::1:2 - request=${request}ff020000000000000000000000010002 - # udp header and dhcpv6 header - request=${request}0222022300${req_len}ffff${msg_code}010203 - # Client identifier - request=${request}0001000a00030001${src_mac} + local inport=$1 src_mac=$2 src_lla=$3 msg_code=$4 offer_ip=$5 boot_file=$6 + local fqdn=$7 + + local req_scapy="Ether(dst='ff:ff:ff:ff:ff:ff', src='${src_mac}')/ \ + IPv6(dst='ff02::1:2', src='${src_lla}')/ \ + UDP(sport=546, dport=547)/ \ + DHCP6(msgtype=${msg_code}, trid=0x010203)/ \ + DHCP6OptClientId(duid=DUID_LL(lladdr='${src_mac}'))" + # Add IA-NA (Identity Association for Non Temporary Address) if msg_code # is not 11 (information request packet) - if test $msg_code != 0b; then - request=${request}0003000c0102030400000e1000001518 + if test $msg_code != 11; then + req_scapy="${req_scapy}/DHCP6OptIA_NA(iaid=0x01020304, T1=3600, T2=5400)" fi - if test $ipxe -eq 2; then - request=${request}000f0006000669505845 + if test "$boot_file" = "bootfile_name"; then + local uc_data="USER_CLASS_DATA(data=b'iPXE')" + req_scapy="${req_scapy}/DHCP6OptUserClass(userclassdata=[[${uc_data}]])" fi - shift; shift; shift; shift; shift; shift; + if test -n "$fqdn"; then + req_scapy="${req_scapy}/DHCP6OptClientFQDN(flags=0x01, fqdn=b'test')" + fi + request=$(fmt_pkt "${req_scapy}") + shift; shift; shift; shift; shift; shift; shift; if test $offer_ip != 0; then - local server_mac=000000100001 - local server_lla=fe80000000000000020000fffe100001 - local reply_code=07 - if test $msg_code = 01; then - reply_code=02 - fi - local msg_len=54 - if test $ipxe -eq 1; then - msg_len=69 - elif test $ipxe -eq 2; then - msg_len=65 - elif test $offer_ip = 1; then - msg_len=28 + local reply_code=7 + if test $msg_code = 1; then + reply_code=2 fi - local reply=${src_mac}${server_mac}86dd6000000000${msg_len}1101${server_lla}${src_lla} - # udp header and dhcpv6 header - reply=${reply}0223022200${msg_len}ffff${reply_code}010203 - # Client identifier - reply=${reply}0001000a00030001${src_mac} - # IA-NA + + local rep_scapy="Ether(dst='${src_mac}', src='00:00:00:10:00:01')/ \ + IPv6(dst='${src_lla}', src='fe80::0200:00ff:fe10:0001')/ \ + UDP(sport=547, dport=546)/ \ + DHCP6(msgtype=${reply_code}, trid=0x010203)/ \ + DHCP6OptClientId(duid=DUID_LL(lladdr='${src_mac}'))" + if test $offer_ip != 1; then - reply=${reply}0003002801020304ffffffffffffffff00050018${offer_ip}ffffffffffffffff + local ip_scapy="DHCP6OptIAAddress(addr='${offer_ip}', \ + preflft=0xffffffff, validlft=0xffffffff)" + rep_scapy="${rep_scapy}/ \ + DHCP6OptIA_NA(iaid=0x01020304, T1=0xffffffff, \ + T2=0xffffffff, ianaopts=[[${ip_scapy}]])" fi - if test $ipxe -eq 1; then - reply=${reply}003b0011626f6f7466696c655f6e616d655f616c74 - elif test $ipxe -eq 2; then - reply=${reply}003b000d626f6f7466696c655f6e616d65 + if test -n "$boot_file"; then + rep_scapy="${rep_scapy}/DHCP6OptBootFileUrl(optdata=b'${boot_file}')" fi - # Server identifier - reply=${reply}0002000a00030001${server_mac} + if test -n "$fqdn"; then + rep_scapy="${rep_scapy}/DHCP6OptClientFQDN(flags=0x06, fqdn=b'${fqdn}')" + fi + rep_scapy="${rep_scapy}/DHCP6OptServerId(duid=DUID_LL(lladdr='00:00:00:10:00:01'))" + reply=$(fmt_pkt "${rep_scapy}") echo $reply | trim_zeros >> $inport.expected else for outport; do @@ -7249,16 +7248,16 @@ echo "---------------------" ovn-sbctl list logical_flow echo "---------------------" -ovn-sbctl dump-flows > sbflows +#ovn-sbctl dump-flows > sbflows AT_CAPTURE_FILE([sbflows]) echo "------ hv1 dump ----------" as hv1 ovs-ofctl dump-flows br-int -src_mac=f00000000001 -src_lla=fe80000000000000f20000fffe000001 -offer_ip=ae700000000000000000000000000004 -test_dhcpv6 1 $src_mac $src_lla 01 $offer_ip 0 +src_mac="f0:00:00:00:00:01" +src_lla="fe80::f200:00ff:fe00:0001" +offer_ip="ae70::4" +test_dhcpv6 1 $src_mac $src_lla 1 $offer_ip "" "" # NXT_RESUMEs should be 1. OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) @@ -7281,12 +7280,12 @@ rm 1.expected reset_pcap_file hv1-vif1 hv1/vif1 reset_pcap_file hv1-vif2 hv1/vif2 -src_mac=f00000000002 -src_lla=fe80000000000000f20000fffe000002 -offer_ip=ae700000000000000000000000000005 +src_mac="f0:00:00:00:00:02" +src_lla="fe80::f200:00ff:fe00:0002" +offer_ip="ae70::5" # Set invalid msg_type -test_dhcpv6 2 $src_mac $src_lla 10 0 0 1 1 +test_dhcpv6 2 $src_mac $src_lla 16 0 "" "" 1 1 # NXT_RESUMEs should be 2. OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) @@ -7305,9 +7304,9 @@ AT_CHECK([cat 1.packets], [0], [expout]) # There should be no DHCPv6 reply from ovn-controller and the request packet # should be received by ls2-lp2. -src_mac=f00000000003 -src_lla=fe80000000000000f20000fffe000003 -test_dhcpv6 3 $src_mac $src_lla 01 0 0 4 +src_mac="f0:00:00:00:00:03" +src_lla="fe80::f200:00ff:fe00:0003" +test_dhcpv6 3 $src_mac $src_lla 1 0 "" "" 4 # NXT_RESUMEs should be 2 only. OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) @@ -7323,10 +7322,10 @@ AT_CHECK([cat 4.packets], [0], [expout]) # Send DHCPv6 packet on ls1-lp3. native DHCPv6 works as stateless mode for this port. # The DHCPv6 reply shouldn't contain offer_ip. -src_mac=f00000000022 -src_lla=fe80000000000000f20000fffe000022 +src_mac="f0:00:00:00:00:22" +src_lla="fe80::f200:00ff:fe00:0022" reset_pcap_file hv1-vif5 hv1/vif5 -test_dhcpv6 5 $src_mac $src_lla 01 1 0 5 +test_dhcpv6 5 $src_mac $src_lla 1 1 "" "" 5 # NXT_RESUMEs should be 3. OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) @@ -7338,11 +7337,11 @@ AT_CHECK([cat 5.packets | cut -c 1-120,125- ], [0], [expout]) # Send DHCPv6 information request (code 11) on ls1-lp3. The DHCPv6 reply # shouldn't contain offer_ip -src_mac=f00000000022 -src_lla=fe80000000000000f20000fffe000022 +src_mac="f0:00:00:00:00:22" +src_lla="fe80::f200:00ff:fe00:0022" reset_pcap_file hv1-vif5 hv1/vif5 rm -f 5.expected -test_dhcpv6 5 $src_mac $src_lla 0b 1 0 5 +test_dhcpv6 5 $src_mac $src_lla 11 1 "" "" 5 # NXT_RESUMEs should be 4. OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) @@ -7363,11 +7362,11 @@ ovn-nbctl lsp-set-dhcpv6-options ls1-lp2 ${d1} reset_pcap_file hv1-vif2 hv1/vif2 -src_mac=f00000000002 -src_lla=fe80000000000000f20000fffe000002 -offer_ip=ae700000000000000000000000000005 +src_mac="f0:00:00:00:00:02" +src_lla="fe80::f200:00ff:fe00:0002" +offer_ip="ae70::5" -test_dhcpv6 2 $src_mac $src_lla 01 $offer_ip 1 +test_dhcpv6 2 $src_mac $src_lla 1 $offer_ip "bootfile_name_alt" "" # NXT_RESUMEs should be 5. OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) @@ -7381,7 +7380,7 @@ AT_CHECK([cat 2.packets | cut -c 1-120,125- ], [0], [expout]) reset_pcap_file hv1-vif2 hv1/vif2 rm 2.packets 2.expected -test_dhcpv6 2 $src_mac $src_lla 01 $offer_ip 2 +test_dhcpv6 2 $src_mac $src_lla 1 $offer_ip "bootfile_name" "" # NXT_RESUMEs should be 6. OVS_WAIT_UNTIL([test 6 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) @@ -7389,6 +7388,24 @@ $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap | trim_zeros > 2.pa cat 2.expected | cut -c 1-120,125- > expout AT_CHECK([cat 2.packets | cut -c 1-120,125- ], [0], [expout]) +ovn-nbctl --all destroy dhcp-option +d1="$(ovn-nbctl create DHCP_Options cidr="ae70\:\:/64")" +ovn-nbctl dhcp-options-set-options $d1 \ + server_id=00:00:00:10:00:01 \ + fqdn=\"ovn.org\" +ovn-nbctl --wait=hv lsp-set-dhcpv6-options ls1-lp2 ${d1} + +reset_pcap_file hv1-vif2 hv1/vif2 +rm 2.packets 2.expected + +test_dhcpv6 2 $src_mac $src_lla 1 $offer_ip "" "ovn.org" +# NXT_RESUMEs should be 7. +OVS_WAIT_UNTIL([test 7 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) + +$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap | trim_zeros > 2.packets +cat 2.expected | cut -c 1-120,125- > expout +AT_CHECK([cat 2.packets | cut -c 1-120,125- ], [0], [expout]) + OVN_CLEANUP([hv1]) AT_CLEANUP diff --git a/tests/test-ovn.c b/tests/test-ovn.c index ce9213c1d..6c7754eac 100644 --- a/tests/test-ovn.c +++ b/tests/test-ovn.c @@ -207,6 +207,7 @@ create_gen_opts(struct hmap *dhcp_opts, struct hmap *dhcpv6_opts, dhcp_opt_add(dhcpv6_opts, "domain_search", 24, "str"); dhcp_opt_add(dhcpv6_opts, "bootfile_name", 59, "str"); dhcp_opt_add(dhcpv6_opts, "bootfile_name_alt", 254, "str"); + dhcp_opt_add(dhcpv6_opts, "fqdn", 39, "domain"); /* IPv6 ND RA options. */ hmap_init(nd_ra_opts);
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> --- v2: Rebase on top of current main. Address comments from Mark: - Properly propagate option flags. - Extract the fqdn encoding to common function. --- NEWS | 2 + controller/pinctrl.c | 69 +++++++++---------- lib/actions.c | 9 ++- lib/ovn-l7.h | 10 +++ lib/ovn-util.c | 27 ++++++++ lib/ovn-util.h | 5 ++ northd/ovn-northd.c | 3 +- ovn-nb.xml | 7 ++ ovn-sb.ovsschema | 6 +- tests/ovn.at | 155 ++++++++++++++++++++++++------------------- tests/test-ovn.c | 1 + 11 files changed, 181 insertions(+), 113 deletions(-)