On Fri, Jun 2, 2023 at 12:43 PM Ales Musil <[email protected]> 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 I have created BZ for this it should be added on merge or I'll add it if there is a need for v2. Signed-off-by: Ales Musil <[email protected]> > --- > NEWS | 2 + > controller/pinctrl.c | 27 +++++++- > lib/actions.c | 30 ++++++++- > lib/ovn-l7.h | 10 +++ > northd/ovn-northd.c | 3 +- > ovn-nb.xml | 7 ++ > ovn-sb.ovsschema | 6 +- > tests/ovn.at | 156 ++++++++++++++++++++++++------------------- > tests/test-ovn.c | 1 + > 9 files changed, 166 insertions(+), 76 deletions(-) > > diff --git a/NEWS b/NEWS > index 1532f635a..b4dd0da05 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 - xx xxx xxxx > -------------------------- > diff --git a/controller/pinctrl.c b/controller/pinctrl.c > index c396ad4c2..51dbadc99 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); > + /* If client requested N or S inform him that it > + * was overwritten by the server. */ > + if (fqdn_flags & DHCPV6_FQDN_FLAGS_N || > + 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; > } > diff --git a/lib/actions.c b/lib/actions.c > index ec27223f9..0ec69bec9 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,33 @@ 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")) { > + /* The DNS format is 2 bytes longer than the "domain". > + * It replaces every '.' with len of the next name. */ > + size_t domain_len = strlen(c->string); > + size = domain_len + 2; > + char *encoded = xzalloc(size); > + > + int8_t label_len = 0; > + for (size_t i = 0; i < domain_len; i++) { > + if (c->string[i] == '.') { > + encoded[i - label_len] = label_len; > + label_len = 0; > + } else { > + encoded[i + 1] = c->string[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; > + } > + > + 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/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 d8c694002..ab6052299 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=0x02, > 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,25 @@ $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 > +cat 2.packets > +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); > -- > 2.40.1 > > -- Ales Musil Senior Software Engineer - OVN Core Red Hat EMEA <https://www.redhat.com> [email protected] IM: amusil <https://red.ht/sig> _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
