diff mbox series

[ovs-dev] controller: IPv6 Prefix-Delegation: introduce RENEW/REBIND msg support

Message ID 7e3381c2f3fdb911a4527e4847ad9d7386edee1d.1602246055.git.lorenzo.bianconi@redhat.com
State New
Headers show
Series [ovs-dev] controller: IPv6 Prefix-Delegation: introduce RENEW/REBIND msg support | expand

Commit Message

Lorenzo Bianconi Oct. 9, 2020, 12:21 p.m. UTC
Introduce RENEW/REBIND message support to OVN IPv6 PD support
according to RFC-3633 [0] in order to renew IPv6 prefixes when
T1/T2 are elapsed

[0] https://tools.ietf.org/html/rfc3633

Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
---
 controller/pinctrl.c | 131 +++++++++++++++++++++++++++++++++----------
 lib/ovn-l7.h         |   3 +
 tests/system-ovn.at  |  18 ++++--
 3 files changed, 118 insertions(+), 34 deletions(-)

Comments

Mark Michelson Oct. 9, 2020, 6:51 p.m. UTC | #1
Acked-by: Mark Michelson <mmichels@redhat.com>

My ack is mainly focused on the soundness of the code itself. With 
regards to DHCP renew and rebind, I don't have a lot of knowledge on 
their intended mechanics other than a quick look at the referenced RFC.

On 10/9/20 8:21 AM, Lorenzo Bianconi wrote:
> Introduce RENEW/REBIND message support to OVN IPv6 PD support
> according to RFC-3633 [0] in order to renew IPv6 prefixes when
> T1/T2 are elapsed
> 
> [0] https://tools.ietf.org/html/rfc3633
> 
> Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
> ---
>   controller/pinctrl.c | 131 +++++++++++++++++++++++++++++++++----------
>   lib/ovn-l7.h         |   3 +
>   tests/system-ovn.at  |  18 ++++--
>   3 files changed, 118 insertions(+), 34 deletions(-)
> 
> diff --git a/controller/pinctrl.c b/controller/pinctrl.c
> index 0a7020533..633bf3535 100644
> --- a/controller/pinctrl.c
> +++ b/controller/pinctrl.c
> @@ -573,11 +573,22 @@ enum {
>       PREFIX_REQUEST,
>       PREFIX_PENDING,
>       PREFIX_DONE,
> +    PREFIX_RENEW,
> +    PREFIX_REBIND,
>   };
>   
>   struct ipv6_prefixd_state {
>       long long int next_announce;
> +    long long int last_complete;
>       long long int last_used;
> +    /* IPv6 PD server info */
> +    struct in6_addr server_addr;
> +    struct eth_addr sa;
> +    /* server_id_info */
> +    struct {
> +        uint8_t *data;
> +        uint8_t len;
> +    } uuid;
>       struct in6_addr ipv6_addr;
>       struct eth_addr ea;
>       struct eth_addr cmac;
> @@ -781,20 +792,26 @@ out:
>   static void
>   pinctrl_prefixd_state_handler(const struct flow *ip_flow,
>                                 struct in6_addr addr, unsigned aid,
> +                              struct eth_addr sa, struct in6_addr server_addr,
>                                 char prefix_len, unsigned t1, unsigned t2,
> -                              unsigned plife_time, unsigned vlife_time)
> +                              unsigned plife_time, unsigned vlife_time,
> +                              uint8_t *uuid, uint8_t uuid_len)
>   {
>       struct ipv6_prefixd_state *pfd;
>   
>       pfd = pinctrl_find_prefixd_state(ip_flow, aid);
>       if (pfd) {
>           pfd->state = PREFIX_PENDING;
> -        pfd->plife_time = plife_time;
> -        pfd->vlife_time = vlife_time;
> +        pfd->server_addr = server_addr;
> +        pfd->sa = sa;
> +        pfd->uuid.data = uuid;
> +        pfd->uuid.len = uuid_len;
> +        pfd->plife_time = plife_time * 1000;
> +        pfd->vlife_time = vlife_time * 1000;
>           pfd->plen = prefix_len;
>           pfd->prefix = addr;
> -        pfd->t1 = t1;
> -        pfd->t2 = t2;
> +        pfd->t1 = t1 * 1000;
> +        pfd->t2 = t2 * 1000;
>           notify_pinctrl_main();
>       }
>   }
> @@ -804,19 +821,21 @@ pinctrl_parse_dhcpv6_reply(struct dp_packet *pkt_in,
>                              const struct flow *ip_flow)
>       OVS_REQUIRES(pinctrl_mutex)
>   {
> +    struct eth_header *eth = dp_packet_eth(pkt_in);
> +    struct ip6_hdr *in_ip = dp_packet_l3(pkt_in);
>       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;
> -    struct in6_addr ipv6;
> +    uint8_t *end = (uint8_t *)udp_in + dlen, *uuid = NULL;
> +    uint8_t prefix_len = 0, uuid_len = 0;
> +    struct in6_addr ipv6 = {};
>       bool status = false;
>       unsigned aid = 0;
>   
> -    memset(&ipv6, 0, sizeof (struct in6_addr));
>       /* 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;
> @@ -867,14 +886,20 @@ pinctrl_parse_dhcpv6_reply(struct dp_packet *pkt_in,
>               }
>               break;
>           }
> +        case DHCPV6_OPT_SERVER_ID_CODE:
> +            uuid_len = ntohs(in_opt->len);
> +            uuid = xmalloc(uuid_len);
> +            memcpy(uuid, in_opt + 1, uuid_len);
> +            break;
>           default:
>               break;
>           }
>           in_dhcpv6_data += opt_len;
>       }
>       if (status) {
> -        pinctrl_prefixd_state_handler(ip_flow, ipv6, aid, prefix_len,
> -                                      t1, t2, plife_time, vlife_time);
> +        pinctrl_prefixd_state_handler(ip_flow, ipv6, aid, eth->eth_src,
> +                                      in_ip->ip6_src, prefix_len, t1, t2,
> +                                      plife_time, vlife_time, uuid, uuid_len);
>       }
>   }
>   
> @@ -904,27 +929,42 @@ pinctrl_handle_dhcp6_server(struct rconn *swconn, const struct flow *ip_flow,
>   }
>   
>   static void
> -compose_prefixd_solicit(struct dp_packet *b,
> -                        struct ipv6_prefixd_state *pfd,
> -                        const struct eth_addr eth_dst,
> -                        const struct in6_addr *ipv6_dst)
> +compose_prefixd_packet(struct dp_packet *b, struct ipv6_prefixd_state *pfd)
>   {
> -    eth_compose(b, eth_dst, pfd->ea, ETH_TYPE_IPV6, IPV6_HEADER_LEN);
> +    struct in6_addr ipv6_dst;
> +    struct eth_addr eth_dst;
>   
>       int payload = sizeof(struct dhcpv6_opt_server_id) +
>                     sizeof(struct dhcpv6_opt_ia_na);
> +    if (pfd->uuid.len) {
> +        payload += pfd->uuid.len + sizeof(struct dhcpv6_opt_header);
> +        ipv6_dst = pfd->server_addr;
> +        eth_dst = pfd->sa;
> +    } else {
> +        eth_dst = (struct eth_addr) ETH_ADDR_C(33,33,00,01,00,02);
> +        ipv6_parse("ff02::1:2", &ipv6_dst);
> +    }
>       if (ipv6_addr_is_set(&pfd->prefix)) {
>           payload += sizeof(struct dhcpv6_opt_ia_prefix);
>       }
> +
> +    eth_compose(b, eth_dst, pfd->ea, ETH_TYPE_IPV6, IPV6_HEADER_LEN);
> +
>       int len = UDP_HEADER_LEN + 4 + payload;
>       struct udp_header *udp_h = compose_ipv6(b, IPPROTO_UDP, &pfd->ipv6_addr,
> -                                            ipv6_dst, 0, 0, 255, len);
> +                                            &ipv6_dst, 0, 0, 255, len);
>       udp_h->udp_len = htons(len);
>       udp_h->udp_csum = 0;
>       packet_set_udp_port(b, htons(546), htons(547));
>   
>       unsigned char *dhcp_hdr = (unsigned char *)(udp_h + 1);
> -    *dhcp_hdr = DHCPV6_MSG_TYPE_SOLICIT;
> +    if (pfd->state == PREFIX_RENEW) {
> +        *dhcp_hdr = DHCPV6_MSG_TYPE_RENEW;
> +    } else if (pfd->state == PREFIX_REBIND) {
> +        *dhcp_hdr = DHCPV6_MSG_TYPE_REBIND;
> +    } else {
> +        *dhcp_hdr = DHCPV6_MSG_TYPE_SOLICIT;
> +    }
>   
>       struct dhcpv6_opt_server_id *opt_client_id =
>           (struct dhcpv6_opt_server_id *)(dhcp_hdr + 4);
> @@ -935,11 +975,21 @@ compose_prefixd_solicit(struct dp_packet *b,
>       opt_client_id->hw_type = htons(DHCPV6_HW_TYPE_ETH);
>       opt_client_id->mac = pfd->cmac;
>   
> +    unsigned char *ptr = (unsigned char *)(opt_client_id + 1);
> +    if (pfd->uuid.len) {
> +        struct dhcpv6_opt_header *in_opt = (struct dhcpv6_opt_header *)ptr;
> +        in_opt->code = htons(DHCPV6_OPT_SERVER_ID_CODE);
> +        in_opt->len = htons(pfd->uuid.len);
> +
> +        ptr += sizeof *in_opt;
> +        memcpy(ptr, pfd->uuid.data, pfd->uuid.len);
> +        ptr += pfd->uuid.len;
> +    }
> +
>       if (!ipv6_addr_is_set(&pfd->prefix)) {
>           pfd->aid = random_uint16();
>       }
> -    struct dhcpv6_opt_ia_na *ia_pd =
> -            (struct dhcpv6_opt_ia_na *)(opt_client_id + 1);
> +    struct dhcpv6_opt_ia_na *ia_pd = (struct dhcpv6_opt_ia_na *)ptr;
>       ia_pd->opt.code = htons(DHCPV6_OPT_IA_PD);
>       int opt_len = sizeof(struct dhcpv6_opt_ia_na) -
>                     sizeof(struct dhcpv6_opt_header);
> @@ -981,16 +1031,15 @@ ipv6_prefixd_send(struct rconn *swconn, struct ipv6_prefixd_state *pfd)
>           return pfd->next_announce;
>       }
>   
> +    if (pfd->state == PREFIX_DONE) {
> +        goto out;
> +    }
> +
>       uint64_t packet_stub[256 / 8];
>       struct dp_packet packet;
>   
> -    struct eth_addr eth_dst;
> -    eth_dst = (struct eth_addr) ETH_ADDR_C(33,33,00,01,00,02);
> -    struct in6_addr ipv6_dst;
> -    ipv6_parse("ff02::1:2", &ipv6_dst);
> -
>       dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
> -    compose_prefixd_solicit(&packet, pfd, eth_dst, &ipv6_dst);
> +    compose_prefixd_packet(&packet, pfd);
>   
>       uint64_t ofpacts_stub[4096 / 8];
>       struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
> @@ -1019,8 +1068,9 @@ ipv6_prefixd_send(struct rconn *swconn, struct ipv6_prefixd_state *pfd)
>       queue_msg(swconn, ofputil_encode_packet_out(&po, proto));
>       dp_packet_uninit(&packet);
>       ofpbuf_uninit(&ofpacts);
> +
> +out:
>       pfd->next_announce = cur_time + random_range(IPV6_PREFIXD_TIMEOUT);
> -    pfd->state = PREFIX_SOLICIT;
>   
>       return pfd->next_announce;
>   }
> @@ -1031,10 +1081,28 @@ static bool ipv6_prefixd_should_inject(void)
>   
>       SHASH_FOR_EACH (iter, &ipv6_prefixd) {
>           struct ipv6_prefixd_state *pfd = iter->data;
> +        long long int cur_time = time_msec();
> +
>           if (pfd->state == PREFIX_SOLICIT) {
>               return true;
>           }
> -        if (pfd->state && pfd->next_announce < time_msec()) {
> +        if (pfd->state == PREFIX_DONE &&
> +            cur_time > pfd->last_complete + pfd->t1) {
> +            pfd->state = PREFIX_RENEW;
> +            return true;
> +        }
> +        if (pfd->state == PREFIX_RENEW &&
> +            cur_time > pfd->last_complete + pfd->t2) {
> +            pfd->state = PREFIX_REBIND;
> +            if (pfd->uuid.len) {
> +                free(pfd->uuid.data);
> +                pfd->uuid.len = 0;
> +            }
> +            return true;
> +        }
> +        if (pfd->state == PREFIX_REBIND &&
> +            cur_time > pfd->last_complete + pfd->vlife_time) {
> +            pfd->state = PREFIX_SOLICIT;
>               return true;
>           }
>       }
> @@ -1120,7 +1188,8 @@ fill_ipv6_prefix_state(struct ovsdb_idl_txn *ovnsb_idl_txn,
>               struct smap options;
>   
>               pfd->state = PREFIX_DONE;
> -            pfd->next_announce = time_msec() + pfd->t1 * 1000;
> +            pfd->last_complete = time_msec();
> +            pfd->next_announce = pfd->last_complete + pfd->t1;
>               ipv6_string_mapped(prefix_str, &pfd->prefix);
>               smap_clone(&options, &pb->options);
>               smap_add_format(&options, "ipv6_ra_pd_list", "%d:%s/%d",
> @@ -1219,6 +1288,10 @@ prepare_ipv6_prefixd(struct ovsdb_idl_txn *ovnsb_idl_txn,
>       SHASH_FOR_EACH_SAFE (iter, next, &ipv6_prefixd) {
>           struct ipv6_prefixd_state *pfd = iter->data;
>           if (pfd->last_used + IPV6_PREFIXD_STALE_TIMEOUT < time_msec()) {
> +            if (pfd->uuid.len) {
> +                free(pfd->uuid.data);
> +                pfd->uuid.len = 0;
> +            }
>               free(pfd);
>               shash_delete(&ipv6_prefixd, iter);
>           }
> diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h
> index 18c59896d..30a795531 100644
> --- a/lib/ovn-l7.h
> +++ b/lib/ovn-l7.h
> @@ -189,6 +189,9 @@ struct dhcp_opt6_header {
>   #define DHCPV6_MSG_TYPE_ADVT        2
>   #define DHCPV6_MSG_TYPE_REQUEST     3
>   #define DHCPV6_MSG_TYPE_CONFIRM     4
> +#define DHCPV6_MSG_TYPE_RENEW       5
> +#define DHCPV6_MSG_TYPE_REBIND      6
> +
>   #define DHCPV6_MSG_TYPE_REPLY       7
>   #define DHCPV6_MSG_TYPE_DECLINE     9
>   #define DHCPV6_MSG_TYPE_INFO_REQ    11
> diff --git a/tests/system-ovn.at b/tests/system-ovn.at
> index 420610f89..60bd20fd4 100644
> --- a/tests/system-ovn.at
> +++ b/tests/system-ovn.at
> @@ -4768,19 +4768,28 @@ AT_CHECK([ovn-nbctl get logical_router_port rp-sw1 ipv6_prefix | cut -c3-16], [0
>   [2001:1db8:3333]
>   ])
>   
> -kill $(pidof dibbler-server)
> -
>   prefix=$(ovn-nbctl list logical_router_port rp-public | awk -F/ '/ipv6_prefix/{print substr($1,25,9)}' | sed 's/://g')
>   ovn-nbctl set logical_router_port rp-sw0 options:prefix=false
>   ovn-nbctl set logical_router_port rp-sw1 options:prefix=false
>   
> -NS_CHECK_EXEC([server], [tcpdump -c 1 -nni s1 ip6[[95:4]]=0x${prefix} > public.pcap &])
> +# Renew message
> +NS_CHECK_EXEC([server], [tcpdump -c 1 -nni s1 ip6[[48:1]]=0x05 and ip6[[113:4]]=0x${prefix} > renew.pcap &])
> +# Reply message with Status OK
> +NS_CHECK_EXEC([server], [tcpdump -c 1 -nni s1 ip6[[48:1]]=0x07 and ip6[[81:4]]=0x${prefix} and ip6[[98:1]]=0x0d and ip6[[101:2]]=0x0000 > reply.pcap &])
> +
> +OVS_WAIT_UNTIL([
> +    total_pkts=$(cat renew.pcap | wc -l)
> +    test "${total_pkts}" = "1"
> +])
>   
>   OVS_WAIT_UNTIL([
> -    total_pkts=$(cat public.pcap | wc -l)
> +    total_pkts=$(cat reply.pcap | wc -l)
>       test "${total_pkts}" = "1"
>   ])
>   
> +kill $(pidof dibbler-server)
> +kill $(pidof tcpdump)
> +
>   ovn-nbctl set logical_router_port rp-sw0 options:prefix=false
>   ovn-nbctl clear logical_router_port rp-sw0 ipv6_prefix
>   OVS_WAIT_WHILE([test "$(ovn-nbctl get logical_router_port rp-sw0 ipv6_prefix | cut -c3-16)" = "[2001:1db8:3333]"])
> @@ -4788,7 +4797,6 @@ AT_CHECK([ovn-nbctl get logical_router_port rp-sw0 ipv6_prefix | cut -c3-16], [0
>   []
>   ])
>   
> -kill $(pidof tcpdump)
>   kill $(pidof ovn-controller)
>   
>   as ovn-sb
>
diff mbox series

Patch

diff --git a/controller/pinctrl.c b/controller/pinctrl.c
index 0a7020533..633bf3535 100644
--- a/controller/pinctrl.c
+++ b/controller/pinctrl.c
@@ -573,11 +573,22 @@  enum {
     PREFIX_REQUEST,
     PREFIX_PENDING,
     PREFIX_DONE,
+    PREFIX_RENEW,
+    PREFIX_REBIND,
 };
 
 struct ipv6_prefixd_state {
     long long int next_announce;
+    long long int last_complete;
     long long int last_used;
+    /* IPv6 PD server info */
+    struct in6_addr server_addr;
+    struct eth_addr sa;
+    /* server_id_info */
+    struct {
+        uint8_t *data;
+        uint8_t len;
+    } uuid;
     struct in6_addr ipv6_addr;
     struct eth_addr ea;
     struct eth_addr cmac;
@@ -781,20 +792,26 @@  out:
 static void
 pinctrl_prefixd_state_handler(const struct flow *ip_flow,
                               struct in6_addr addr, unsigned aid,
+                              struct eth_addr sa, struct in6_addr server_addr,
                               char prefix_len, unsigned t1, unsigned t2,
-                              unsigned plife_time, unsigned vlife_time)
+                              unsigned plife_time, unsigned vlife_time,
+                              uint8_t *uuid, uint8_t uuid_len)
 {
     struct ipv6_prefixd_state *pfd;
 
     pfd = pinctrl_find_prefixd_state(ip_flow, aid);
     if (pfd) {
         pfd->state = PREFIX_PENDING;
-        pfd->plife_time = plife_time;
-        pfd->vlife_time = vlife_time;
+        pfd->server_addr = server_addr;
+        pfd->sa = sa;
+        pfd->uuid.data = uuid;
+        pfd->uuid.len = uuid_len;
+        pfd->plife_time = plife_time * 1000;
+        pfd->vlife_time = vlife_time * 1000;
         pfd->plen = prefix_len;
         pfd->prefix = addr;
-        pfd->t1 = t1;
-        pfd->t2 = t2;
+        pfd->t1 = t1 * 1000;
+        pfd->t2 = t2 * 1000;
         notify_pinctrl_main();
     }
 }
@@ -804,19 +821,21 @@  pinctrl_parse_dhcpv6_reply(struct dp_packet *pkt_in,
                            const struct flow *ip_flow)
     OVS_REQUIRES(pinctrl_mutex)
 {
+    struct eth_header *eth = dp_packet_eth(pkt_in);
+    struct ip6_hdr *in_ip = dp_packet_l3(pkt_in);
     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;
-    struct in6_addr ipv6;
+    uint8_t *end = (uint8_t *)udp_in + dlen, *uuid = NULL;
+    uint8_t prefix_len = 0, uuid_len = 0;
+    struct in6_addr ipv6 = {};
     bool status = false;
     unsigned aid = 0;
 
-    memset(&ipv6, 0, sizeof (struct in6_addr));
     /* 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;
@@ -867,14 +886,20 @@  pinctrl_parse_dhcpv6_reply(struct dp_packet *pkt_in,
             }
             break;
         }
+        case DHCPV6_OPT_SERVER_ID_CODE:
+            uuid_len = ntohs(in_opt->len);
+            uuid = xmalloc(uuid_len);
+            memcpy(uuid, in_opt + 1, uuid_len);
+            break;
         default:
             break;
         }
         in_dhcpv6_data += opt_len;
     }
     if (status) {
-        pinctrl_prefixd_state_handler(ip_flow, ipv6, aid, prefix_len,
-                                      t1, t2, plife_time, vlife_time);
+        pinctrl_prefixd_state_handler(ip_flow, ipv6, aid, eth->eth_src,
+                                      in_ip->ip6_src, prefix_len, t1, t2,
+                                      plife_time, vlife_time, uuid, uuid_len);
     }
 }
 
@@ -904,27 +929,42 @@  pinctrl_handle_dhcp6_server(struct rconn *swconn, const struct flow *ip_flow,
 }
 
 static void
-compose_prefixd_solicit(struct dp_packet *b,
-                        struct ipv6_prefixd_state *pfd,
-                        const struct eth_addr eth_dst,
-                        const struct in6_addr *ipv6_dst)
+compose_prefixd_packet(struct dp_packet *b, struct ipv6_prefixd_state *pfd)
 {
-    eth_compose(b, eth_dst, pfd->ea, ETH_TYPE_IPV6, IPV6_HEADER_LEN);
+    struct in6_addr ipv6_dst;
+    struct eth_addr eth_dst;
 
     int payload = sizeof(struct dhcpv6_opt_server_id) +
                   sizeof(struct dhcpv6_opt_ia_na);
+    if (pfd->uuid.len) {
+        payload += pfd->uuid.len + sizeof(struct dhcpv6_opt_header);
+        ipv6_dst = pfd->server_addr;
+        eth_dst = pfd->sa;
+    } else {
+        eth_dst = (struct eth_addr) ETH_ADDR_C(33,33,00,01,00,02);
+        ipv6_parse("ff02::1:2", &ipv6_dst);
+    }
     if (ipv6_addr_is_set(&pfd->prefix)) {
         payload += sizeof(struct dhcpv6_opt_ia_prefix);
     }
+
+    eth_compose(b, eth_dst, pfd->ea, ETH_TYPE_IPV6, IPV6_HEADER_LEN);
+
     int len = UDP_HEADER_LEN + 4 + payload;
     struct udp_header *udp_h = compose_ipv6(b, IPPROTO_UDP, &pfd->ipv6_addr,
-                                            ipv6_dst, 0, 0, 255, len);
+                                            &ipv6_dst, 0, 0, 255, len);
     udp_h->udp_len = htons(len);
     udp_h->udp_csum = 0;
     packet_set_udp_port(b, htons(546), htons(547));
 
     unsigned char *dhcp_hdr = (unsigned char *)(udp_h + 1);
-    *dhcp_hdr = DHCPV6_MSG_TYPE_SOLICIT;
+    if (pfd->state == PREFIX_RENEW) {
+        *dhcp_hdr = DHCPV6_MSG_TYPE_RENEW;
+    } else if (pfd->state == PREFIX_REBIND) {
+        *dhcp_hdr = DHCPV6_MSG_TYPE_REBIND;
+    } else {
+        *dhcp_hdr = DHCPV6_MSG_TYPE_SOLICIT;
+    }
 
     struct dhcpv6_opt_server_id *opt_client_id =
         (struct dhcpv6_opt_server_id *)(dhcp_hdr + 4);
@@ -935,11 +975,21 @@  compose_prefixd_solicit(struct dp_packet *b,
     opt_client_id->hw_type = htons(DHCPV6_HW_TYPE_ETH);
     opt_client_id->mac = pfd->cmac;
 
+    unsigned char *ptr = (unsigned char *)(opt_client_id + 1);
+    if (pfd->uuid.len) {
+        struct dhcpv6_opt_header *in_opt = (struct dhcpv6_opt_header *)ptr;
+        in_opt->code = htons(DHCPV6_OPT_SERVER_ID_CODE);
+        in_opt->len = htons(pfd->uuid.len);
+
+        ptr += sizeof *in_opt;
+        memcpy(ptr, pfd->uuid.data, pfd->uuid.len);
+        ptr += pfd->uuid.len;
+    }
+
     if (!ipv6_addr_is_set(&pfd->prefix)) {
         pfd->aid = random_uint16();
     }
-    struct dhcpv6_opt_ia_na *ia_pd =
-            (struct dhcpv6_opt_ia_na *)(opt_client_id + 1);
+    struct dhcpv6_opt_ia_na *ia_pd = (struct dhcpv6_opt_ia_na *)ptr;
     ia_pd->opt.code = htons(DHCPV6_OPT_IA_PD);
     int opt_len = sizeof(struct dhcpv6_opt_ia_na) -
                   sizeof(struct dhcpv6_opt_header);
@@ -981,16 +1031,15 @@  ipv6_prefixd_send(struct rconn *swconn, struct ipv6_prefixd_state *pfd)
         return pfd->next_announce;
     }
 
+    if (pfd->state == PREFIX_DONE) {
+        goto out;
+    }
+
     uint64_t packet_stub[256 / 8];
     struct dp_packet packet;
 
-    struct eth_addr eth_dst;
-    eth_dst = (struct eth_addr) ETH_ADDR_C(33,33,00,01,00,02);
-    struct in6_addr ipv6_dst;
-    ipv6_parse("ff02::1:2", &ipv6_dst);
-
     dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
-    compose_prefixd_solicit(&packet, pfd, eth_dst, &ipv6_dst);
+    compose_prefixd_packet(&packet, pfd);
 
     uint64_t ofpacts_stub[4096 / 8];
     struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
@@ -1019,8 +1068,9 @@  ipv6_prefixd_send(struct rconn *swconn, struct ipv6_prefixd_state *pfd)
     queue_msg(swconn, ofputil_encode_packet_out(&po, proto));
     dp_packet_uninit(&packet);
     ofpbuf_uninit(&ofpacts);
+
+out:
     pfd->next_announce = cur_time + random_range(IPV6_PREFIXD_TIMEOUT);
-    pfd->state = PREFIX_SOLICIT;
 
     return pfd->next_announce;
 }
@@ -1031,10 +1081,28 @@  static bool ipv6_prefixd_should_inject(void)
 
     SHASH_FOR_EACH (iter, &ipv6_prefixd) {
         struct ipv6_prefixd_state *pfd = iter->data;
+        long long int cur_time = time_msec();
+
         if (pfd->state == PREFIX_SOLICIT) {
             return true;
         }
-        if (pfd->state && pfd->next_announce < time_msec()) {
+        if (pfd->state == PREFIX_DONE &&
+            cur_time > pfd->last_complete + pfd->t1) {
+            pfd->state = PREFIX_RENEW;
+            return true;
+        }
+        if (pfd->state == PREFIX_RENEW &&
+            cur_time > pfd->last_complete + pfd->t2) {
+            pfd->state = PREFIX_REBIND;
+            if (pfd->uuid.len) {
+                free(pfd->uuid.data);
+                pfd->uuid.len = 0;
+            }
+            return true;
+        }
+        if (pfd->state == PREFIX_REBIND &&
+            cur_time > pfd->last_complete + pfd->vlife_time) {
+            pfd->state = PREFIX_SOLICIT;
             return true;
         }
     }
@@ -1120,7 +1188,8 @@  fill_ipv6_prefix_state(struct ovsdb_idl_txn *ovnsb_idl_txn,
             struct smap options;
 
             pfd->state = PREFIX_DONE;
-            pfd->next_announce = time_msec() + pfd->t1 * 1000;
+            pfd->last_complete = time_msec();
+            pfd->next_announce = pfd->last_complete + pfd->t1;
             ipv6_string_mapped(prefix_str, &pfd->prefix);
             smap_clone(&options, &pb->options);
             smap_add_format(&options, "ipv6_ra_pd_list", "%d:%s/%d",
@@ -1219,6 +1288,10 @@  prepare_ipv6_prefixd(struct ovsdb_idl_txn *ovnsb_idl_txn,
     SHASH_FOR_EACH_SAFE (iter, next, &ipv6_prefixd) {
         struct ipv6_prefixd_state *pfd = iter->data;
         if (pfd->last_used + IPV6_PREFIXD_STALE_TIMEOUT < time_msec()) {
+            if (pfd->uuid.len) {
+                free(pfd->uuid.data);
+                pfd->uuid.len = 0;
+            }
             free(pfd);
             shash_delete(&ipv6_prefixd, iter);
         }
diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h
index 18c59896d..30a795531 100644
--- a/lib/ovn-l7.h
+++ b/lib/ovn-l7.h
@@ -189,6 +189,9 @@  struct dhcp_opt6_header {
 #define DHCPV6_MSG_TYPE_ADVT        2
 #define DHCPV6_MSG_TYPE_REQUEST     3
 #define DHCPV6_MSG_TYPE_CONFIRM     4
+#define DHCPV6_MSG_TYPE_RENEW       5
+#define DHCPV6_MSG_TYPE_REBIND      6
+
 #define DHCPV6_MSG_TYPE_REPLY       7
 #define DHCPV6_MSG_TYPE_DECLINE     9
 #define DHCPV6_MSG_TYPE_INFO_REQ    11
diff --git a/tests/system-ovn.at b/tests/system-ovn.at
index 420610f89..60bd20fd4 100644
--- a/tests/system-ovn.at
+++ b/tests/system-ovn.at
@@ -4768,19 +4768,28 @@  AT_CHECK([ovn-nbctl get logical_router_port rp-sw1 ipv6_prefix | cut -c3-16], [0
 [2001:1db8:3333]
 ])
 
-kill $(pidof dibbler-server)
-
 prefix=$(ovn-nbctl list logical_router_port rp-public | awk -F/ '/ipv6_prefix/{print substr($1,25,9)}' | sed 's/://g')
 ovn-nbctl set logical_router_port rp-sw0 options:prefix=false
 ovn-nbctl set logical_router_port rp-sw1 options:prefix=false
 
-NS_CHECK_EXEC([server], [tcpdump -c 1 -nni s1 ip6[[95:4]]=0x${prefix} > public.pcap &])
+# Renew message
+NS_CHECK_EXEC([server], [tcpdump -c 1 -nni s1 ip6[[48:1]]=0x05 and ip6[[113:4]]=0x${prefix} > renew.pcap &])
+# Reply message with Status OK
+NS_CHECK_EXEC([server], [tcpdump -c 1 -nni s1 ip6[[48:1]]=0x07 and ip6[[81:4]]=0x${prefix} and ip6[[98:1]]=0x0d and ip6[[101:2]]=0x0000 > reply.pcap &])
+
+OVS_WAIT_UNTIL([
+    total_pkts=$(cat renew.pcap | wc -l)
+    test "${total_pkts}" = "1"
+])
 
 OVS_WAIT_UNTIL([
-    total_pkts=$(cat public.pcap | wc -l)
+    total_pkts=$(cat reply.pcap | wc -l)
     test "${total_pkts}" = "1"
 ])
 
+kill $(pidof dibbler-server)
+kill $(pidof tcpdump)
+
 ovn-nbctl set logical_router_port rp-sw0 options:prefix=false
 ovn-nbctl clear logical_router_port rp-sw0 ipv6_prefix
 OVS_WAIT_WHILE([test "$(ovn-nbctl get logical_router_port rp-sw0 ipv6_prefix | cut -c3-16)" = "[2001:1db8:3333]"])
@@ -4788,7 +4797,6 @@  AT_CHECK([ovn-nbctl get logical_router_port rp-sw0 ipv6_prefix | cut -c3-16], [0
 []
 ])
 
-kill $(pidof tcpdump)
 kill $(pidof ovn-controller)
 
 as ovn-sb