Unify the handling of DHCPv6 options. This is addressing several
problems that were present in the DHCPv6 handling:
1) There were inconsistent length checks for the packet length. It
would be possible to craft a packet that had an option header
without any data which could lead to a crash because we would
attempt to deref data after the packet buffer.
2) We could end up reading data after the packet buffer. This could
happen when the option header would lie about the data length in
the option.
3) Unbounded strcmp for a string created by user.
4) The parsing was inconsistent and very hard to read.
Make sure the parsing is done using helpers that should prevent
the mentioned issues.
Fixes: e3a398e9146e ("controller: Add ipv6 prefix delegation state machine")
Fixes: 32fc42fdbb20 ("ovn-controller: Add 'put_dhcpv6_opts' action in
ovn-controller")
Fixes: c5fd51bd1541 ("Introduce IPv6 iPXE chainload support")
Fixes: b3ae86a15e81 ("northd, controller: Add support for DHCPv6 FQDN option")
Reported-by: Seiji Sakurai <[email protected]>
Co-authored-by: Seiji Sakurai <[email protected]>
Acked-by: Dumitru Ceara <[email protected]>
Signed-off-by: Seiji Sakurai <[email protected]>
Signed-off-by: Ales Musil <[email protected]>
---
controller/pinctrl.c | 303 ++++++++++++++++++++++++++-----------------
lib/ovn-l7.h | 10 ++
tests/system-ovn.at | 66 ++++++++++
3 files changed, 262 insertions(+), 117 deletions(-)
diff --git a/controller/pinctrl.c b/controller/pinctrl.c
index 682b88b1a..de52ac87f 100644
--- a/controller/pinctrl.c
+++ b/controller/pinctrl.c
@@ -822,32 +822,100 @@ pinctrl_find_prefixd_state(const struct flow *ip_flow,
unsigned aid)
return NULL;
}
+static const struct dhcpv6_opt_header *
+next_dhcpv6_opt(const uint8_t *data, size_t opts_len,
+ size_t *len, size_t *opt_len)
+{
+ size_t len_inner = *len + sizeof(struct dhcpv6_opt_header);
+ if (len_inner > opts_len) {
+ return NULL;
+ }
+
+ const struct dhcpv6_opt_header *hdr =
+ (const struct dhcpv6_opt_header *) (data + *len);
+ len_inner += ntohs(hdr->len);
+ if (len_inner > opts_len) {
+ return NULL;
+ }
+
+ *len = len_inner;
+ *opt_len = sizeof *hdr + ntohs(hdr->len);
+ return hdr;
+}
+
static void
-pinctrl_parse_dhcpv6_advt(struct rconn *swconn, const struct flow *ip_flow,
- struct dp_packet *pkt_in, const struct match *md)
+dhcpv6_opt_ia_na_parse_inner(const struct dhcpv6_opt_ia_na *ia_na,
+ size_t opts_len,
+ struct dhcpv6_opt_ia_prefix **ia_prefix,
+ struct dhcpv6_opt_status **status)
{
- struct udp_header *udp_in = dp_packet_l4(pkt_in);
- size_t dlen = MIN(ntohs(udp_in->udp_len), dp_packet_l4_size(pkt_in));
- unsigned char *in_dhcpv6_data = (unsigned char *)(udp_in + 1);
- uint8_t *data, *end = (uint8_t *)udp_in + dlen;
- int len = 0, aid = 0;
-
- data = xmalloc(dlen);
- /* skip DHCPv6 common header */
- in_dhcpv6_data += 4;
- while (in_dhcpv6_data < end) {
- struct dhcpv6_opt_header *in_opt =
- (struct dhcpv6_opt_header *)in_dhcpv6_data;
- int opt_len = sizeof *in_opt + ntohs(in_opt->len);
+ /* Check if there are at least some data. */
+ opts_len -= sizeof *ia_na;
+ if (!opts_len) {
+ return;
+ }
+
+ const uint8_t *opts_data = (uint8_t *) ia_na + sizeof *ia_na;
+ size_t len = 0, opt_len = 0;
- if (dlen < opt_len + len) {
- goto out;
+ const struct dhcpv6_opt_header *in_opt;
+ for (in_opt = next_dhcpv6_opt(opts_data, opts_len, &len, &opt_len);
+ in_opt;
+ in_opt = next_dhcpv6_opt(opts_data, opts_len,
+ &len, &opt_len)) {
+ switch (ntohs(in_opt->code)) {
+ case DHCPV6_OPT_IA_PREFIX: {
+ /* Consider only the first found option. */
+ if (*ia_prefix) {
+ break;
+ }
+
+ if (opt_len < sizeof(struct dhcpv6_opt_ia_prefix)) {
+ break;
+ }
+
+ *ia_prefix = (struct dhcpv6_opt_ia_prefix *) in_opt;
+ break;
}
+ case DHCPV6_OPT_STATUS_CODE: {
+ /* Consider only the first found option. */
+ if (*status) {
+ break;
+ }
+ if (opt_len < sizeof(struct dhcpv6_opt_status)) {
+ break;
+ }
+
+ *status = (struct dhcpv6_opt_status *) in_opt;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+static void
+pinctrl_parse_dhcpv6_advt(struct rconn *swconn, const struct flow *ip_flow,
+ const void *opts_data, size_t opts_len,
+ const struct match *md)
+{
+ size_t len = 0, opt_len = 0, data_len = 0;
+ int aid = 0;
+
+ uint8_t *data = xmalloc(opts_len);
+
+ const struct dhcpv6_opt_header *in_opt;
+ for (in_opt = next_dhcpv6_opt(opts_data, opts_len, &len, &opt_len);
+ in_opt;
+ in_opt = next_dhcpv6_opt(opts_data, opts_len, &len, &opt_len)) {
switch (ntohs(in_opt->code)) {
case DHCPV6_OPT_IA_PD: {
+ if (opt_len < sizeof(struct dhcpv6_opt_ia_na)) {
+ break;
+ }
struct dhcpv6_opt_ia_na *ia_na = (struct dhcpv6_opt_ia_na *)in_opt;
- int orig_len = len, hdr_len = 0, size = sizeof *in_opt + 12;
uint32_t t1 = ntohl(ia_na->t1), t2 = ntohl(ia_na->t2);
if (t1 > t2 && t2 > 0) {
@@ -855,55 +923,49 @@ pinctrl_parse_dhcpv6_advt(struct rconn *swconn, const
struct flow *ip_flow,
}
aid = ntohl(ia_na->iaid);
- memcpy(&data[len], in_opt, size);
- in_opt = (struct dhcpv6_opt_header *)(in_dhcpv6_data + size);
- len += size;
-
- while (size < opt_len) {
- int flen = sizeof *in_opt + ntohs(in_opt->len);
- if (dlen < flen + len) {
- goto out;
- }
+ memcpy(data + data_len, in_opt, sizeof *ia_na);
+ size_t ia_na_offset = data_len;
+ data_len += sizeof *ia_na;
- if (ntohs(in_opt->code) == DHCPV6_OPT_IA_PREFIX) {
- struct dhcpv6_opt_ia_prefix *ia_hdr =
- (struct dhcpv6_opt_ia_prefix *)in_opt;
- uint32_t plife_time = ntohl(ia_hdr->plife_time);
- uint32_t vlife_time = ntohl(ia_hdr->vlife_time);
+ struct dhcpv6_opt_ia_prefix *ia_prefix = NULL;
+ struct dhcpv6_opt_status *status = NULL;
+ dhcpv6_opt_ia_na_parse_inner(ia_na, opt_len, &ia_prefix, &status);
- if (plife_time > vlife_time) {
- goto out;
- }
+ if (ia_prefix) {
+ uint32_t plife_time = ntohl(ia_prefix->plife_time);
+ uint32_t vlife_time = ntohl(ia_prefix->vlife_time);
- memcpy(&data[len], in_opt, flen);
- hdr_len += flen;
- len += flen;
+ if (plife_time > vlife_time) {
+ goto out;
}
- if (ntohs(in_opt->code) == DHCPV6_OPT_STATUS_CODE) {
- struct dhcpv6_opt_status *status;
- status = (struct dhcpv6_opt_status *)in_opt;
- if (ntohs(status->status_code)) {
- goto out;
- }
+ memcpy(data + data_len, ia_prefix, sizeof *ia_prefix);
+ data_len += sizeof *ia_prefix;
+ }
+
+ if (status) {
+ if (ntohs(status->status_code)) {
+ goto out;
}
- size += flen;
- in_opt = (struct dhcpv6_opt_header *)(in_dhcpv6_data + size);
}
- in_opt = (struct dhcpv6_opt_header *)&data[orig_len];
- in_opt->len = htons(hdr_len + 12);
+
+ /* Adjust the copied IA NA option header len. */
+ struct dhcpv6_opt_header *copied_hdr =
+ (struct dhcpv6_opt_header *) (data + ia_na_offset);
+ copied_hdr->len = htons(sizeof *ia_na - sizeof *copied_hdr +
+ (ia_prefix ? sizeof *ia_prefix : 0));
+
break;
}
case DHCPV6_OPT_SERVER_ID_CODE:
case DHCPV6_OPT_CLIENT_ID_CODE:
- memcpy(&data[len], in_opt, opt_len);
- len += opt_len;
+ memcpy(data + data_len, in_opt, opt_len);
+ data_len += opt_len;
break;
default:
break;
}
- in_dhcpv6_data += opt_len;
}
struct ipv6_prefixd_state *pfd = pinctrl_find_prefixd_state(ip_flow, aid);
@@ -931,14 +993,14 @@ pinctrl_parse_dhcpv6_advt(struct rconn *swconn, const
struct flow *ip_flow,
&ip_flow->ipv6_dst,
&in6addr_all_dhcp_agents,
0, 0, 255,
- len + UDP_HEADER_LEN + 4);
- udp_h->udp_len = htons(len + UDP_HEADER_LEN + 4);
+ data_len + UDP_HEADER_LEN + 4);
+ udp_h->udp_len = htons(data_len + UDP_HEADER_LEN + 4);
udp_h->udp_csum = 0;
packet_set_udp_port(&packet, htons(546), htons(547));
unsigned char *dhcp_hdr = (unsigned char *)(udp_h + 1);
*dhcp_hdr = DHCPV6_MSG_TYPE_REQUEST;
- memcpy(dhcp_hdr + 4, data, len);
+ memcpy(dhcp_hdr + 4, data, data_len);
uint32_t csum = packet_csum_pseudoheader6(dp_packet_l3(&packet));
csum = csum_continue(csum, udp_h, dp_packet_size(&packet) -
@@ -1006,39 +1068,29 @@ pinctrl_prefixd_state_handler(const struct flow
*ip_flow,
}
static void
-pinctrl_parse_dhcpv6_reply(struct dp_packet *pkt_in,
+pinctrl_parse_dhcpv6_reply(const void *opts_data, size_t opts_len,
const struct flow *ip_flow)
OVS_REQUIRES(pinctrl_mutex)
{
- struct udp_header *udp_in = dp_packet_l4(pkt_in);
- unsigned char *in_dhcpv6_data = (unsigned char *)(udp_in + 1);
- size_t dlen = MIN(ntohs(udp_in->udp_len), dp_packet_l4_size(pkt_in));
unsigned t1 = 0, t2 = 0, vlife_time = 0, plife_time = 0;
- uint8_t *end = (uint8_t *) udp_in + dlen;
uint8_t prefix_len = 0, uuid_len = 0;
uint8_t uuid[DHCPV6_MAX_DUID_LEN];
struct in6_addr ipv6 = in6addr_any;
+ size_t len = 0, opt_len = 0;
bool status = false;
unsigned aid = 0;
- /* skip DHCPv6 common header */
- in_dhcpv6_data += 4;
-
- while (in_dhcpv6_data < end) {
- struct dhcpv6_opt_header *in_opt =
- (struct dhcpv6_opt_header *)in_dhcpv6_data;
- int opt_len = sizeof *in_opt + ntohs(in_opt->len);
-
- if (in_dhcpv6_data + opt_len > end) {
- break;
- }
-
+ const struct dhcpv6_opt_header *in_opt;
+ for (in_opt = next_dhcpv6_opt(opts_data, opts_len, &len, &opt_len);
+ in_opt;
+ in_opt = next_dhcpv6_opt(opts_data, opts_len, &len, &opt_len)) {
switch (ntohs(in_opt->code)) {
case DHCPV6_OPT_IA_PD: {
- int size = sizeof *in_opt + 12;
- in_opt = (struct dhcpv6_opt_header *)(in_dhcpv6_data + size);
+ if (opt_len < sizeof(struct dhcpv6_opt_ia_na)) {
+ break;
+ }
struct dhcpv6_opt_ia_na *ia_na =
- (struct dhcpv6_opt_ia_na *)in_dhcpv6_data;
+ (struct dhcpv6_opt_ia_na *) in_opt;
aid = ntohl(ia_na->iaid);
t1 = ntohl(ia_na->t1);
@@ -1047,30 +1099,25 @@ pinctrl_parse_dhcpv6_reply(struct dp_packet *pkt_in,
break;
}
- while (size < opt_len) {
- if (ntohs(in_opt->code) == DHCPV6_OPT_IA_PREFIX) {
- struct dhcpv6_opt_ia_prefix *ia_hdr =
- (struct dhcpv6_opt_ia_prefix *)(in_dhcpv6_data + size);
+ struct dhcpv6_opt_ia_prefix *ia_hdr = NULL;
+ struct dhcpv6_opt_status *status_hdr = NULL;
+ dhcpv6_opt_ia_na_parse_inner(ia_na, opt_len, &ia_hdr, &status_hdr);
- plife_time = ntohl(ia_hdr->plife_time);
- vlife_time = ntohl(ia_hdr->vlife_time);
- if (plife_time > vlife_time) {
- break;
- }
- prefix_len = ia_hdr->plen;
- memcpy(&ipv6, &ia_hdr->ipv6, sizeof (struct in6_addr));
- status = true;
+ if (ia_hdr) {
+ plife_time = ntohl(ia_hdr->plife_time);
+ vlife_time = ntohl(ia_hdr->vlife_time);
+ if (plife_time > vlife_time) {
+ break;
}
- if (ntohs(in_opt->code) == DHCPV6_OPT_STATUS_CODE) {
- struct dhcpv6_opt_status *status_hdr;
+ prefix_len = ia_hdr->plen;
+ memcpy(&ipv6, &ia_hdr->ipv6, sizeof (struct in6_addr));
+ status = true;
+ }
- status_hdr = (struct dhcpv6_opt_status *)in_opt;
- if (ntohs(status_hdr->status_code)) {
- status = false;
- }
+ if (status_hdr) {
+ if (ntohs(status_hdr->status_code)) {
+ status = false;
}
- size += sizeof *in_opt + ntohs(in_opt->len);
- in_opt = (struct dhcpv6_opt_header *)(in_dhcpv6_data + size);
}
break;
}
@@ -1081,7 +1128,6 @@ pinctrl_parse_dhcpv6_reply(struct dp_packet *pkt_in,
default:
break;
}
- in_dhcpv6_data += opt_len;
}
if (status) {
char prefix[INET6_ADDRSTRLEN + 1];
@@ -1108,14 +1154,20 @@ pinctrl_handle_dhcp6_server(struct rconn *swconn, const
struct flow *ip_flow,
}
struct udp_header *udp_in = dp_packet_l4(pkt_in);
- unsigned char *dhcp_hdr = (unsigned char *)(udp_in + 1);
+ size_t dlen = MIN(ntohs(udp_in->udp_len), dp_packet_l4_size(pkt_in));
+ if (dlen < UDP_HEADER_LEN + DHCPV6_HEADER_LEN) {
+ return;
+ }
- switch (*dhcp_hdr) {
+ size_t opts_len = dlen - UDP_HEADER_LEN - DHCPV6_HEADER_LEN;
+ const struct dhcpv6_header *hdr = dp_packet_get_udp_payload(pkt_in);
+ switch (hdr->msg_type) {
case DHCPV6_MSG_TYPE_ADVT:
- pinctrl_parse_dhcpv6_advt(swconn, ip_flow, pkt_in, md);
+ pinctrl_parse_dhcpv6_advt(swconn, ip_flow, DHCPV6_PAYLOAD(hdr),
+ opts_len, md);
break;
case DHCPV6_MSG_TYPE_REPLY:
- pinctrl_parse_dhcpv6_reply(pkt_in, ip_flow);
+ pinctrl_parse_dhcpv6_reply(DHCPV6_PAYLOAD(hdr), opts_len, ip_flow);
break;
default:
break;
@@ -3073,6 +3125,8 @@ compose_dhcpv6_status(struct ofpbuf *userdata, struct
ofpbuf *opts)
return true;
}
+#define DHCPV6_UC_PXE_OFFSET 2
+
/* Called with in the pinctrl_handler thread context. */
static void
pinctrl_handle_put_dhcpv6_opts(
@@ -3115,16 +3169,17 @@ pinctrl_handle_put_dhcpv6_opts(
}
struct udp_header *in_udp = dp_packet_l4(pkt_in);
- const uint8_t *in_dhcpv6_data = dp_packet_get_udp_payload(pkt_in);
- if (!in_udp || !in_dhcpv6_data) {
+ size_t dlen = MIN(ntohs(in_udp->udp_len), dp_packet_l4_size(pkt_in));
+ if (dlen < UDP_HEADER_LEN + DHCPV6_HEADER_LEN) {
VLOG_WARN_RL(&rl, "truncated dhcpv6 packet");
goto exit;
}
+ const struct dhcpv6_header *hdr = dp_packet_get_udp_payload(pkt_in);
+
uint8_t out_dhcpv6_msg_type;
- uint8_t in_dhcpv6_msg_type = *in_dhcpv6_data;
bool status_only = false;
- switch (in_dhcpv6_msg_type) {
+ switch (hdr->msg_type) {
case DHCPV6_MSG_TYPE_SOLICIT:
out_dhcpv6_msg_type = DHCPV6_MSG_TYPE_ADVT;
break;
@@ -3145,8 +3200,6 @@ pinctrl_handle_put_dhcpv6_opts(
/* Invalid or unsupported DHCPv6 message type */
goto exit;
}
- /* Skip 4 bytes (message type (1 byte) + transaction ID (3 bytes). */
- in_dhcpv6_data += 4;
/* We need to extract IAID from the IA-NA option of the client's DHCPv6
* solicit/request/confirm packet and copy the same IAID in the Server's
* response.
@@ -3155,17 +3208,24 @@ pinctrl_handle_put_dhcpv6_opts(
* */
ovs_be32 iaid = 0;
struct dhcpv6_opt_header const *in_opt_client_id = NULL;
- size_t udp_len = ntohs(in_udp->udp_len);
- 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;
- switch(ntohs(in_opt->code)) {
+ size_t len = 0, opt_len = 0;
+ size_t opts_len = dlen - UDP_HEADER_LEN - DHCPV6_HEADER_LEN;
+
+ const struct dhcpv6_opt_header *in_opt;
+ for (in_opt = next_dhcpv6_opt(DHCPV6_PAYLOAD(hdr), opts_len,
+ &len, &opt_len);
+ in_opt;
+ in_opt = next_dhcpv6_opt(DHCPV6_PAYLOAD(hdr), opts_len,
+ &len, &opt_len)) {
+ switch (ntohs(in_opt->code)) {
case DHCPV6_OPT_IA_NA_CODE:
{
+ if (opt_len < sizeof(struct dhcpv6_opt_ia_na)) {
+ break;
+ }
+
struct dhcpv6_opt_ia_na *opt_ia_na = (
struct dhcpv6_opt_ia_na *)in_opt;
iaid = opt_ia_na->iaid;
@@ -3177,21 +3237,30 @@ pinctrl_handle_put_dhcpv6_opts(
break;
case DHCPV6_OPT_USER_CLASS: {
- char *user_class = (char *)(in_opt + 1);
- if (!strcmp(user_class + 2, "iPXE")) {
+ if (opt_len <
+ sizeof(struct dhcpv6_opt_header) + DHCPV6_UC_PXE_OFFSET + 4) {
+ break;
+ }
+
+ const char *user_class = DHCPV6_OPT_PAYLOAD(in_opt);
+ if (!strncmp(user_class + DHCPV6_UC_PXE_OFFSET, "iPXE", 4)) {
ipxe_req = true;
}
break;
}
case DHCPV6_OPT_FQDN_CODE:
- fqdn_flags = *(in_dhcpv6_data + sizeof *in_opt);
+ if (opt_len <
+ sizeof(struct dhcpv6_opt_header) + sizeof(uint8_t)) {
+ break;
+ }
+
+ fqdn_flags = *(uint8_t *) DHCPV6_OPT_PAYLOAD(in_opt);
break;
default:
break;
}
- in_dhcpv6_data += sizeof *in_opt + ntohs(in_opt->len);
}
if (!in_opt_client_id) {
@@ -3200,7 +3269,7 @@ pinctrl_handle_put_dhcpv6_opts(
goto exit;
}
- if (!iaid && in_dhcpv6_msg_type != DHCPV6_MSG_TYPE_INFO_REQ) {
+ if (!iaid && hdr->msg_type != DHCPV6_MSG_TYPE_INFO_REQ) {
VLOG_WARN_RL(&rl, "DHCPv6 option - IA NA not present in the "
"DHCPv6 packet");
goto exit;
diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h
index 7f5673b12..5f52e1791 100644
--- a/lib/ovn-l7.h
+++ b/lib/ovn-l7.h
@@ -312,6 +312,16 @@ extern const struct in6_addr in6addr_all_dhcp_agents;
0x00,0x00,0x00,0x00,0x00,0x00, \
0x00,0x01,0x00,0x02 } } }
+#define DHCPV6_HEADER_LEN 4
+OVS_PACKED(
+struct dhcpv6_header {
+ uint8_t msg_type;
+ uint8_t transaction_id[3];
+});
+BUILD_ASSERT_DECL(DHCPV6_HEADER_LEN == sizeof(struct dhcpv6_header));
+
+#define DHCPV6_PAYLOAD(hdr) \
+ (const void *)((uint8_t *) (hdr) + sizeof(struct dhcpv6_header))
#define DHCP6_OPT_HEADER_LEN 4
OVS_PACKED(
diff --git a/tests/system-ovn.at b/tests/system-ovn.at
index 06c5c4b2c..582ed194b 100644
--- a/tests/system-ovn.at
+++ b/tests/system-ovn.at
@@ -21748,3 +21748,69 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port
patch-.*/d
AT_CLEANUP
])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([DHCPv6 - Options heap overread])
+AT_SKIP_IF([test $HAVE_SCAPY = no])
+
+ovn_start
+
+OVS_TRAFFIC_VSWITCHD_START()
+ADD_BR([br-int])
+
+# Set external-ids in br-int needed for ovn-controller.
+check ovs-vsctl \
+ -- set Open_vSwitch . external-ids:system-id=hv1 \
+ -- set Open_vSwitch .
external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
+ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
+ -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
+ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
+
+start_daemon ovn-controller
+
+check ovn-nbctl ls-add ls1
+check ovn-nbctl lsp-add ls1 ls1-lp1 \
+ -- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01 fd10::4"
+
+check_uuid ovn-nbctl -- --id=@opt create DHCP_Options cidr="fd10\:\:/64" \
+ options="\"server_id\"=\"00:00:00:10:00:01\"" \
+ -- set Logical_Switch_Port ls1-lp1 dhcpv6_options=[@opt]
+
+ADD_NAMESPACES(ls1-lp1)
+ADD_VETH(ls1-lp1, ls1-lp1, br-int, "fd10::4/96", "f0:00:00:00:00:01", \
+ "fd10::1", "nodad")
+
+NETNS_START_TCPDUMP([ls1-lp1], [-nnne -i ls1-lp1 udp port 546 and udp port
547], [ls1-lp1])
+
+OVN_POPULATE_ARP
+wait_for_ports_up
+check ovn-nbctl --wait=hv sync
+
+ip netns exec ls1-lp1 scapy -H <<-EOF
+p = Ether(dst='33:33:00:01:00:02', src='f0:00:00:00:00:01') / \
+ IPv6(dst='ff02::1:2', src='fe80::f200:ff:fe00:1') / \
+ UDP(sport=546, dport=547) / \
+ Raw(load=bytes.fromhex( \
+ '01' '010203' \
+ '0003' '000c' '01020304' '00000000' '00000000' \
+ '0001' '0200' \
+ '0003' '0001' \
+ 'f00000000001'
+ ))
+sendp (p, iface='ls1-lp1', loop = 0, verbose = 0, count = 1)
+EOF
+
+# ovn-contorller should report a warning that the packet didn't contain valid
Client ID.
+OVS_WAIT_UNTIL([grep -q "DHCPv6 option - Client id not present"
ovn-controller.log])
+AT_CHECK([grep -q "advertise" ls1-lp1.tcpdump], [1])
+
+OVN_CLEANUP_CONTROLLER([hv1])
+OVN_CLEANUP_NORTHD
+
+as
+OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
+/connection dropped.*/d
+/DHCPv6 option - Client id not present.*/d"])
+
+AT_CLEANUP
+])
--
2.53.0
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev