diff mbox series

odhcpd: add option to use absolute timestamps

Message ID 20210130163240.28887-1-vincent@systemli.org
State Rejected
Headers show
Series odhcpd: add option to use absolute timestamps | expand

Commit Message

Nick Jan. 30, 2021, 4:32 p.m. UTC
Until now it is not possible to give absolute timestamps in odhcpd.
This means that on every new RA or request, the timestamp is renewed.
Further, the valid and preferred lifetimes are not synced between all
devices.

There are several usecases when it is needed to have absolute timestamp
that needed to be synced across all devices, e.g. your ISP delegates
you a prefix for some certain time, or you want to change to another
prefix.

The purpose of having this as a absolute timestamp is to make it easier
to track. An example configuration is

  option absolute_lifetime '1'
  option valid_lifetime '05 Jan 2021 23:00:00'
  option preferred_lifetime '05 Jan 2021 23:00:00'

If the valid_lifetime is in the past, the preferred lifetime and valid
lifetime are set to 1 minute.

Signed-off-by: Nick Hainke <vincent@systemli.org>
---
 README          |  8 ++++--
 src/config.c    | 69 ++++++++++++++++++++++++++++++++++---------------
 src/dhcpv6-ia.c | 10 +++++++
 src/odhcpd.h    |  1 +
 4 files changed, 65 insertions(+), 23 deletions(-)

Comments

Hans Dedecker Feb. 6, 2021, 8:26 p.m. UTC | #1
On Sat, Jan 30, 2021 at 5:33 PM Nick Hainke <vincent@systemli.org> wrote:
>
> Until now it is not possible to give absolute timestamps in odhcpd.
> This means that on every new RA or request, the timestamp is renewed.
> Further, the valid and preferred lifetimes are not synced between all
> devices.
>
> There are several usecases when it is needed to have absolute timestamp
> that needed to be synced across all devices, e.g. your ISP delegates
> you a prefix for some certain time, or you want to change to another
> prefix.
>
> The purpose of having this as a absolute timestamp is to make it easier
> to track. An example configuration is
>
>   option absolute_lifetime '1'
>   option valid_lifetime '05 Jan 2021 23:00:00'
>   option preferred_lifetime '05 Jan 2021 23:00:00'
>
> If the valid_lifetime is in the past, the preferred lifetime and valid
> lifetime are set to 1 minute.
I have my reservations about the patch as it requires knowledge of
absolute time on devices.
This is a problem as not all devices have a RTC; or the time needs to
be synchronized with a wan NTP server.
This is also the reason why netifd does not to support absolute
lifetimes for configured prefixes

Hans
>
> Signed-off-by: Nick Hainke <vincent@systemli.org>
> ---
>  README          |  8 ++++--
>  src/config.c    | 69 ++++++++++++++++++++++++++++++++++---------------
>  src/dhcpv6-ia.c | 10 +++++++
>  src/odhcpd.h    |  1 +
>  4 files changed, 65 insertions(+), 23 deletions(-)
>
> diff --git a/README b/README
> index f9cbb11..0af5c75 100644
> --- a/README
> +++ b/README
> @@ -107,11 +107,13 @@ dns_service               bool    1                       Announce the address of interface as DNS service
>                                                         if the list of dns is empty
>  domain                 list    <local search domain>   Search domains to announce
>
> -leasetime              string  12h                     DHCPv4 address leasetime
> +leasetime              string  12h                     DHCPv4 address leasetime. If absolute_lifetime is
> +                                                       set the value can be given as a date, e.g. "10 Jan 2020 00:00:00".
>  start                  integer 100                     DHCPv4 pool start
>  limit                  integer 150                     DHCPv4 pool size
>  preferred_lifetime     string  12h                     Value for the preferred lifetime
> -                                                       for a prefix
> +                                                       for a prefix. If absolute_lifetime is set the value can
> +                                                       be given as a date, e.g. "10 Jan 2020 00:00:00".
>  ra_default             integer 0                       Override default route
>                         0: default, 1: ignore no public address, 2: ignore all
>  ra_flags               list    other-config            List of RA flags to be
> @@ -145,6 +147,8 @@ ndproxy_slave               bool    0                       NDProxy external slave
>  prefix_filter          string  ::/0                    Only advertise on-link prefixes within
>                         [IPv6 prefix]                   the provided IPv6 prefix; others are
>                                                         filtered out.
> +absolute_lifetime              bool    0                       Interpret configured lifetime as
> +                                                       absolute timestamps. The format has to be "10 Jan 2020 00:00:00".
>
>
>  Sections of type host (static leases)
> diff --git a/src/config.c b/src/config.c
> index 78b5855..42f73a1 100644
> --- a/src/config.c
> +++ b/src/config.c
> @@ -8,6 +8,7 @@
>  #include <string.h>
>  #include <sys/stat.h>
>  #include <syslog.h>
> +#include <time.h>
>
>  #include <uci.h>
>  #include <uci_blob.h>
> @@ -83,6 +84,7 @@ enum {
>         IFACE_ATTR_NDPROXY_SLAVE,
>         IFACE_ATTR_PREFIX_FILTER,
>         IFACE_ATTR_PREFERRED_LIFETIME,
> +       IFACE_ATTR_ABSOLUTE_LIFETIME,
>         IFACE_ATTR_MAX
>  };
>
> @@ -132,6 +134,7 @@ static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = {
>         [IFACE_ATTR_NDPROXY_SLAVE] = { .name = "ndproxy_slave", .type = BLOBMSG_TYPE_BOOL },
>         [IFACE_ATTR_PREFIX_FILTER] = { .name = "prefix_filter", .type = BLOBMSG_TYPE_STRING },
>         [IFACE_ATTR_PREFERRED_LIFETIME] = { .name = "preferred_lifetime", .type = BLOBMSG_TYPE_STRING },
> +       [IFACE_ATTR_ABSOLUTE_LIFETIME] = { .name = "absolute_lifetime", .type = BLOBMSG_TYPE_BOOL },
>  };
>
>  static const struct uci_blob_param_info iface_attr_info[IFACE_ATTR_MAX] = {
> @@ -212,6 +215,7 @@ static void set_interface_defaults(struct interface *iface)
>         iface->ra_mininterval = iface->ra_maxinterval/3;
>         iface->ra_lifetime = -1;
>         iface->ra_dns = true;
> +       iface->absolute_lifetime = false;
>  }
>
>  static void clean_interface(struct interface *iface)
> @@ -321,29 +325,48 @@ static void set_config(struct uci_section *s)
>         }
>  }
>
> -static double parse_leasetime(struct blob_attr *c) {
> +static double parse_leasetime(struct blob_attr *c, bool absolute) {
>         char *val = blobmsg_get_string(c), *endptr = NULL;
> -       double time = strcmp(val, "infinite") ? strtod(val, &endptr) : UINT32_MAX;
> -
> -       if (time && endptr && endptr[0]) {
> -               if (endptr[0] == 's')
> -                       time *= 1;
> -               else if (endptr[0] == 'm')
> -                       time *= 60;
> -               else if (endptr[0] == 'h')
> -                       time *= 3600;
> -               else if (endptr[0] == 'd')
> -                       time *= 24 * 3600;
> -               else if (endptr[0] == 'w')
> -                       time *= 7 * 24 * 3600;
> -               else
> +       double ret_time = strcmp(val, "infinite") ? strtod(val, &endptr) : UINT32_MAX;
> +
> +       if (absolute)
> +       {
> +               // "10 Jan 2020 00:00:00"
> +               // Parse absolut time
> +               struct tm tm = {0};
> +               char *s = strptime(val, "%d %b %Y %H:%M:%S", &tm);
> +               if (s == NULL) {
> +                       syslog(LOG_ERR, "Failed to Parse Date: %s", val);
>                         goto err;
> +               }
> +
> +               time_t now = odhcpd_time();
> +               time_t wall_time = time(NULL);
> +               time_t t = mktime(&tm);
> +
> +               double diff = difftime(t,wall_time);
> +               ret_time += now + diff;
> +       } else {
> +               if (ret_time && endptr && endptr[0]) {
> +                       if (endptr[0] == 's')
> +                               ret_time *= 1;
> +                       else if (endptr[0] == 'm')
> +                               ret_time *= 60;
> +                       else if (endptr[0] == 'h')
> +                               ret_time *= 3600;
> +                       else if (endptr[0] == 'd')
> +                               ret_time *= 24 * 3600;
> +                       else if (endptr[0] == 'w')
> +                               ret_time *= 7 * 24 * 3600;
> +                       else
> +                               goto err;
> +               }
>         }
>
> -       if (time < 60)
> -               time = 60;
> +       if (ret_time < 60)
> +               ret_time = 60;
>
> -       return time;
> +       return ret_time;
>
>  err:
>         return -1;
> @@ -409,7 +432,7 @@ int set_lease_from_blobmsg(struct blob_attr *ba)
>         }
>
>         if ((c = tb[LEASE_ATTR_LEASETIME])) {
> -               double time = parse_leasetime(c);
> +               double time = parse_leasetime(c, false); // do not support absolute timestamps for now
>                 if (time < 0)
>                         goto err;
>
> @@ -520,8 +543,12 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr
>         if ((c = tb[IFACE_ATTR_DYNAMICDHCP]))
>                 iface->no_dynamic_dhcp = !blobmsg_get_bool(c);
>
> +
> +       if ((c = tb[IFACE_ATTR_ABSOLUTE_LIFETIME]))
> +               iface->absolute_lifetime = blobmsg_get_bool(c);
> +
>         if ((c = tb[IFACE_ATTR_LEASETIME])) {
> -               double time = parse_leasetime(c);
> +               double time = parse_leasetime(c, iface->absolute_lifetime);
>                 if (time < 0)
>                         goto err;
>
> @@ -529,7 +556,7 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr
>         }
>
>         if ((c = tb[IFACE_ATTR_PREFERRED_LIFETIME])) {
> -               double time = parse_leasetime(c);
> +               double time = parse_leasetime(c, iface->absolute_lifetime);
>                 if (time < 0)
>                         goto err;
>
> diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c
> index a59fc20..78be8b8 100644
> --- a/src/dhcpv6-ia.c
> +++ b/src/dhcpv6-ia.c
> @@ -865,6 +865,16 @@ static size_t build_ia(uint8_t *buf, size_t buflen, uint16_t status,
>                         if (prefix_valid > leasetime)
>                                 prefix_valid = leasetime;
>
> +                       if (iface->absolute_lifetime) {
> +                               if ((long int) iface->dhcp_leasetime > now) {
> +                                       prefix_valid = iface->dhcp_leasetime - now;
> +                                       prefix_pref = iface->preferred_lifetime - now;
> +                               } else { // if we have a timestamp in the past set pref and valid to 60s
> +                                       prefix_valid = 60;
> +                                       prefix_pref = 60;
> +                               }
> +                       }
> +
>                         if (a->flags & OAF_DHCPV6_PD) {
>                                 struct dhcpv6_ia_prefix o_ia_p = {
>                                         .type = htons(DHCPV6_OPT_IA_PREFIX),
> diff --git a/src/odhcpd.h b/src/odhcpd.h
> index 45b6784..98673a8 100644
> --- a/src/odhcpd.h
> +++ b/src/odhcpd.h
> @@ -288,6 +288,7 @@ struct interface {
>         uint32_t ra_hoplimit;
>         int ra_mtu;
>         uint32_t preferred_lifetime;
> +       bool absolute_lifetime;
>
>         // DHCP
>         uint32_t dhcp_leasetime;
> --
> 2.30.0
>
>
> _______________________________________________
> openwrt-devel mailing list
> openwrt-devel@lists.openwrt.org
> https://lists.openwrt.org/mailman/listinfo/openwrt-devel
Nick Feb. 6, 2021, 8:33 p.m. UTC | #2
I see that more as setting a preferred lifetime with a relative time-span.
E.g. I will get a a preferred_lft of 120 from now, and I would set it like:

   date '+%d %b %Y %T' --date="@$(($(date +%s)+120))"

So it does not need to be synced, since I'm only interested in the 
timespan from now + 120s.
Setting this as abs time-span will prevent a reload or restart of the 
daemon setting the value again to now + 120.

Bests,
Nick

On 2/6/21 9:14 PM, Hans Dedecker wrote:
>
>
> On Sat, Jan 30, 2021 at 5:33 PM Nick Hainke <vincent@systemli.org 
> <mailto:vincent@systemli.org>> wrote:
>
>     Until now it is not possible to give absolute timestamps in odhcpd.
>     This means that on every new RA or request, the timestamp is renewed.
>     Further, the valid and preferred lifetimes are not synced between all
>     devices.
>
>     There are several usecases when it is needed to have absolute
>     timestamp
>     that needed to be synced across all devices, e.g. your ISP delegates
>     you a prefix for some certain time, or you want to change to another
>     prefix.
>
>     The purpose of having this as a absolute timestamp is to make it
>     easier
>     to track. An example configuration is
>
>       option absolute_lifetime '1'
>       option valid_lifetime '05 Jan 2021 23:00:00'
>       option preferred_lifetime '05 Jan 2021 23:00:00'
>
>     If the valid_lifetime is in the past, the preferred lifetime and valid
>     lifetime are set to 1 minute.
>
> I have my reservations about the patch as it requires knowledge of 
> absolute time on devices.
> This is a problem as not all devices have a RTC; or the time needs to 
> be synchronized with a wan NTP server.
> This is also the reason why netifd does not to support absolute 
> lifetimes for configured prefixes
>
> Hans
>
>
>     Signed-off-by: Nick Hainke <vincent@systemli.org
>     <mailto:vincent@systemli.org>>
>     ---
>      README          |  8 ++++--
>      src/config.c    | 69
>     ++++++++++++++++++++++++++++++++++---------------
>      src/dhcpv6-ia.c | 10 +++++++
>      src/odhcpd.h    |  1 +
>      4 files changed, 65 insertions(+), 23 deletions(-)
>
>     diff --git a/README b/README
>     index f9cbb11..0af5c75 100644
>     --- a/README
>     +++ b/README
>     @@ -107,11 +107,13 @@ dns_service               bool    1        
>                Announce the address of interface as DNS service
>                                                             if the
>     list of dns is empty
>      domain                 list    <local search domain>  Search
>     domains to announce
>
>     -leasetime              string  12h  DHCPv4 address leasetime
>     +leasetime              string  12h  DHCPv4 address leasetime. If
>     absolute_lifetime is
>     +                                                       set the
>     value can be given as a date, e.g. "10 Jan 2020 00:00:00".
>      start                  integer 100  DHCPv4 pool start
>      limit                  integer 150  DHCPv4 pool size
>      preferred_lifetime     string  12h  Value for the preferred lifetime
>     -                                                       for a prefix
>     +                                                       for a
>     prefix. If absolute_lifetime is set the value can
>     +                                                       be given
>     as a date, e.g. "10 Jan 2020 00:00:00".
>      ra_default             integer 0  Override default route
>                             0: default, 1: ignore no public address,
>     2: ignore all
>      ra_flags               list    other-config            List of RA
>     flags to be
>     @@ -145,6 +147,8 @@ ndproxy_slave               bool    0        
>                NDProxy external slave
>      prefix_filter          string  ::/0                    Only
>     advertise on-link prefixes within
>                             [IPv6 prefix]                   the
>     provided IPv6 prefix; others are
>     filtered out.
>     +absolute_lifetime              bool    0    Interpret configured
>     lifetime as
>     +  absolute timestamps. The format has to be "10 Jan 2020 00:00:00".
>
>
>      Sections of type host (static leases)
>     diff --git a/src/config.c b/src/config.c
>     index 78b5855..42f73a1 100644
>     --- a/src/config.c
>     +++ b/src/config.c
>     @@ -8,6 +8,7 @@
>      #include <string.h>
>      #include <sys/stat.h>
>      #include <syslog.h>
>     +#include <time.h>
>
>      #include <uci.h>
>      #include <uci_blob.h>
>     @@ -83,6 +84,7 @@ enum {
>             IFACE_ATTR_NDPROXY_SLAVE,
>             IFACE_ATTR_PREFIX_FILTER,
>             IFACE_ATTR_PREFERRED_LIFETIME,
>     +       IFACE_ATTR_ABSOLUTE_LIFETIME,
>             IFACE_ATTR_MAX
>      };
>
>     @@ -132,6 +134,7 @@ static const struct blobmsg_policy
>     iface_attrs[IFACE_ATTR_MAX] = {
>             [IFACE_ATTR_NDPROXY_SLAVE] = { .name = "ndproxy_slave",
>     .type = BLOBMSG_TYPE_BOOL },
>             [IFACE_ATTR_PREFIX_FILTER] = { .name = "prefix_filter",
>     .type = BLOBMSG_TYPE_STRING },
>             [IFACE_ATTR_PREFERRED_LIFETIME] = { .name =
>     "preferred_lifetime", .type = BLOBMSG_TYPE_STRING },
>     +       [IFACE_ATTR_ABSOLUTE_LIFETIME] = { .name =
>     "absolute_lifetime", .type = BLOBMSG_TYPE_BOOL },
>      };
>
>      static const struct uci_blob_param_info
>     iface_attr_info[IFACE_ATTR_MAX] = {
>     @@ -212,6 +215,7 @@ static void set_interface_defaults(struct
>     interface *iface)
>             iface->ra_mininterval = iface->ra_maxinterval/3;
>             iface->ra_lifetime = -1;
>             iface->ra_dns = true;
>     +       iface->absolute_lifetime = false;
>      }
>
>      static void clean_interface(struct interface *iface)
>     @@ -321,29 +325,48 @@ static void set_config(struct uci_section *s)
>             }
>      }
>
>     -static double parse_leasetime(struct blob_attr *c) {
>     +static double parse_leasetime(struct blob_attr *c, bool absolute) {
>             char *val = blobmsg_get_string(c), *endptr = NULL;
>     -       double time = strcmp(val, "infinite") ? strtod(val,
>     &endptr) : UINT32_MAX;
>     -
>     -       if (time && endptr && endptr[0]) {
>     -               if (endptr[0] == 's')
>     -                       time *= 1;
>     -               else if (endptr[0] == 'm')
>     -                       time *= 60;
>     -               else if (endptr[0] == 'h')
>     -                       time *= 3600;
>     -               else if (endptr[0] == 'd')
>     -                       time *= 24 * 3600;
>     -               else if (endptr[0] == 'w')
>     -                       time *= 7 * 24 * 3600;
>     -               else
>     +       double ret_time = strcmp(val, "infinite") ? strtod(val,
>     &endptr) : UINT32_MAX;
>     +
>     +       if (absolute)
>     +       {
>     +               // "10 Jan 2020 00:00:00"
>     +               // Parse absolut time
>     +               struct tm tm = {0};
>     +               char *s = strptime(val, "%d %b %Y %H:%M:%S", &tm);
>     +               if (s == NULL) {
>     +                       syslog(LOG_ERR, "Failed to Parse Date:
>     %s", val);
>                             goto err;
>     +               }
>     +
>     +               time_t now = odhcpd_time();
>     +               time_t wall_time = time(NULL);
>     +               time_t t = mktime(&tm);
>     +
>     +               double diff = difftime(t,wall_time);
>     +               ret_time += now + diff;
>     +       } else {
>     +               if (ret_time && endptr && endptr[0]) {
>     +                       if (endptr[0] == 's')
>     +                               ret_time *= 1;
>     +                       else if (endptr[0] == 'm')
>     +                               ret_time *= 60;
>     +                       else if (endptr[0] == 'h')
>     +                               ret_time *= 3600;
>     +                       else if (endptr[0] == 'd')
>     +                               ret_time *= 24 * 3600;
>     +                       else if (endptr[0] == 'w')
>     +                               ret_time *= 7 * 24 * 3600;
>     +                       else
>     +                               goto err;
>     +               }
>             }
>
>     -       if (time < 60)
>     -               time = 60;
>     +       if (ret_time < 60)
>     +               ret_time = 60;
>
>     -       return time;
>     +       return ret_time;
>
>      err:
>             return -1;
>     @@ -409,7 +432,7 @@ int set_lease_from_blobmsg(struct blob_attr *ba)
>             }
>
>             if ((c = tb[LEASE_ATTR_LEASETIME])) {
>     -               double time = parse_leasetime(c);
>     +               double time = parse_leasetime(c, false); // do not
>     support absolute timestamps for now
>                     if (time < 0)
>                             goto err;
>
>     @@ -520,8 +543,12 @@ int config_parse_interface(void *data, size_t
>     len, const char *name, bool overwr
>             if ((c = tb[IFACE_ATTR_DYNAMICDHCP]))
>                     iface->no_dynamic_dhcp = !blobmsg_get_bool(c);
>
>     +
>     +       if ((c = tb[IFACE_ATTR_ABSOLUTE_LIFETIME]))
>     +               iface->absolute_lifetime = blobmsg_get_bool(c);
>     +
>             if ((c = tb[IFACE_ATTR_LEASETIME])) {
>     -               double time = parse_leasetime(c);
>     +               double time = parse_leasetime(c,
>     iface->absolute_lifetime);
>                     if (time < 0)
>                             goto err;
>
>     @@ -529,7 +556,7 @@ int config_parse_interface(void *data, size_t
>     len, const char *name, bool overwr
>             }
>
>             if ((c = tb[IFACE_ATTR_PREFERRED_LIFETIME])) {
>     -               double time = parse_leasetime(c);
>     +               double time = parse_leasetime(c,
>     iface->absolute_lifetime);
>                     if (time < 0)
>                             goto err;
>
>     diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c
>     index a59fc20..78be8b8 100644
>     --- a/src/dhcpv6-ia.c
>     +++ b/src/dhcpv6-ia.c
>     @@ -865,6 +865,16 @@ static size_t build_ia(uint8_t *buf, size_t
>     buflen, uint16_t status,
>                             if (prefix_valid > leasetime)
>                                     prefix_valid = leasetime;
>
>     +                       if (iface->absolute_lifetime) {
>     +                               if ((long int)
>     iface->dhcp_leasetime > now) {
>     +                                       prefix_valid =
>     iface->dhcp_leasetime - now;
>     +                                       prefix_pref =
>     iface->preferred_lifetime - now;
>     +                               } else { // if we have a timestamp
>     in the past set pref and valid to 60s
>     +                                       prefix_valid = 60;
>     +                                       prefix_pref = 60;
>     +                               }
>     +                       }
>     +
>                             if (a->flags & OAF_DHCPV6_PD) {
>                                     struct dhcpv6_ia_prefix o_ia_p = {
>                                             .type =
>     htons(DHCPV6_OPT_IA_PREFIX),
>     diff --git a/src/odhcpd.h b/src/odhcpd.h
>     index 45b6784..98673a8 100644
>     --- a/src/odhcpd.h
>     +++ b/src/odhcpd.h
>     @@ -288,6 +288,7 @@ struct interface {
>             uint32_t ra_hoplimit;
>             int ra_mtu;
>             uint32_t preferred_lifetime;
>     +       bool absolute_lifetime;
>
>             // DHCP
>             uint32_t dhcp_leasetime;
>     -- 
>     2.30.0
>
>
>     _______________________________________________
>     openwrt-devel mailing list
>     openwrt-devel@lists.openwrt.org
>     <mailto:openwrt-devel@lists.openwrt.org>
>     https://lists.openwrt.org/mailman/listinfo/openwrt-devel
>     <https://lists.openwrt.org/mailman/listinfo/openwrt-devel>
>
Nick Feb. 9, 2021, 9:57 a.m. UTC | #3
Maybe I will just inject via ubus a prefix, that is not reflected by the 
config, like the
"UBUS_METHOD("add_prefix", handle_add_prefix, prefix_attrs)"

At the end that gives me the flexibility I need without the need to 
insert absolute timestamps.

Best,
Nick

On 2/6/21 9:33 PM, Nick wrote:
> I see that more as setting a preferred lifetime with a relative 
> time-span.
> E.g. I will get a a preferred_lft of 120 from now, and I would set it 
> like:
>
>   date '+%d %b %Y %T' --date="@$(($(date +%s)+120))"
>
> So it does not need to be synced, since I'm only interested in the 
> timespan from now + 120s.
> Setting this as abs time-span will prevent a reload or restart of the 
> daemon setting the value again to now + 120.
>
> Bests,
> Nick
>
> On 2/6/21 9:14 PM, Hans Dedecker wrote:
>>
>>
>> On Sat, Jan 30, 2021 at 5:33 PM Nick Hainke <vincent@systemli.org 
>> <mailto:vincent@systemli.org>> wrote:
>>
>>     Until now it is not possible to give absolute timestamps in odhcpd.
>>     This means that on every new RA or request, the timestamp is 
>> renewed.
>>     Further, the valid and preferred lifetimes are not synced between 
>> all
>>     devices.
>>
>>     There are several usecases when it is needed to have absolute
>>     timestamp
>>     that needed to be synced across all devices, e.g. your ISP delegates
>>     you a prefix for some certain time, or you want to change to another
>>     prefix.
>>
>>     The purpose of having this as a absolute timestamp is to make it
>>     easier
>>     to track. An example configuration is
>>
>>       option absolute_lifetime '1'
>>       option valid_lifetime '05 Jan 2021 23:00:00'
>>       option preferred_lifetime '05 Jan 2021 23:00:00'
>>
>>     If the valid_lifetime is in the past, the preferred lifetime and 
>> valid
>>     lifetime are set to 1 minute.
>>
>> I have my reservations about the patch as it requires knowledge of 
>> absolute time on devices.
>> This is a problem as not all devices have a RTC; or the time needs to 
>> be synchronized with a wan NTP server.
>> This is also the reason why netifd does not to support absolute 
>> lifetimes for configured prefixes
>>
>> Hans
>>
>>
>>     Signed-off-by: Nick Hainke <vincent@systemli.org
>>     <mailto:vincent@systemli.org>>
>>     ---
>>      README          |  8 ++++--
>>      src/config.c    | 69
>>     ++++++++++++++++++++++++++++++++++---------------
>>      src/dhcpv6-ia.c | 10 +++++++
>>      src/odhcpd.h    |  1 +
>>      4 files changed, 65 insertions(+), 23 deletions(-)
>>
>>     diff --git a/README b/README
>>     index f9cbb11..0af5c75 100644
>>     --- a/README
>>     +++ b/README
>>     @@ -107,11 +107,13 @@ dns_service               bool    1
>>                Announce the address of interface as DNS service
>>                                                             if the
>>     list of dns is empty
>>      domain                 list    <local search domain>  Search
>>     domains to announce
>>
>>     -leasetime              string  12h  DHCPv4 address leasetime
>>     +leasetime              string  12h  DHCPv4 address leasetime. If
>>     absolute_lifetime is
>>     +                                                       set the
>>     value can be given as a date, e.g. "10 Jan 2020 00:00:00".
>>      start                  integer 100  DHCPv4 pool start
>>      limit                  integer 150  DHCPv4 pool size
>>      preferred_lifetime     string  12h  Value for the preferred 
>> lifetime
>>     -                                                       for a prefix
>>     +                                                       for a
>>     prefix. If absolute_lifetime is set the value can
>>     +                                                       be given
>>     as a date, e.g. "10 Jan 2020 00:00:00".
>>      ra_default             integer 0  Override default route
>>                             0: default, 1: ignore no public address,
>>     2: ignore all
>>      ra_flags               list    other-config            List of RA
>>     flags to be
>>     @@ -145,6 +147,8 @@ ndproxy_slave               bool    0
>>                NDProxy external slave
>>      prefix_filter          string  ::/0                    Only
>>     advertise on-link prefixes within
>>                             [IPv6 prefix]                   the
>>     provided IPv6 prefix; others are
>>     filtered out.
>>     +absolute_lifetime              bool    0    Interpret configured
>>     lifetime as
>>     +  absolute timestamps. The format has to be "10 Jan 2020 00:00:00".
>>
>>
>>      Sections of type host (static leases)
>>     diff --git a/src/config.c b/src/config.c
>>     index 78b5855..42f73a1 100644
>>     --- a/src/config.c
>>     +++ b/src/config.c
>>     @@ -8,6 +8,7 @@
>>      #include <string.h>
>>      #include <sys/stat.h>
>>      #include <syslog.h>
>>     +#include <time.h>
>>
>>      #include <uci.h>
>>      #include <uci_blob.h>
>>     @@ -83,6 +84,7 @@ enum {
>>             IFACE_ATTR_NDPROXY_SLAVE,
>>             IFACE_ATTR_PREFIX_FILTER,
>>             IFACE_ATTR_PREFERRED_LIFETIME,
>>     +       IFACE_ATTR_ABSOLUTE_LIFETIME,
>>             IFACE_ATTR_MAX
>>      };
>>
>>     @@ -132,6 +134,7 @@ static const struct blobmsg_policy
>>     iface_attrs[IFACE_ATTR_MAX] = {
>>             [IFACE_ATTR_NDPROXY_SLAVE] = { .name = "ndproxy_slave",
>>     .type = BLOBMSG_TYPE_BOOL },
>>             [IFACE_ATTR_PREFIX_FILTER] = { .name = "prefix_filter",
>>     .type = BLOBMSG_TYPE_STRING },
>>             [IFACE_ATTR_PREFERRED_LIFETIME] = { .name =
>>     "preferred_lifetime", .type = BLOBMSG_TYPE_STRING },
>>     +       [IFACE_ATTR_ABSOLUTE_LIFETIME] = { .name =
>>     "absolute_lifetime", .type = BLOBMSG_TYPE_BOOL },
>>      };
>>
>>      static const struct uci_blob_param_info
>>     iface_attr_info[IFACE_ATTR_MAX] = {
>>     @@ -212,6 +215,7 @@ static void set_interface_defaults(struct
>>     interface *iface)
>>             iface->ra_mininterval = iface->ra_maxinterval/3;
>>             iface->ra_lifetime = -1;
>>             iface->ra_dns = true;
>>     +       iface->absolute_lifetime = false;
>>      }
>>
>>      static void clean_interface(struct interface *iface)
>>     @@ -321,29 +325,48 @@ static void set_config(struct uci_section *s)
>>             }
>>      }
>>
>>     -static double parse_leasetime(struct blob_attr *c) {
>>     +static double parse_leasetime(struct blob_attr *c, bool absolute) {
>>             char *val = blobmsg_get_string(c), *endptr = NULL;
>>     -       double time = strcmp(val, "infinite") ? strtod(val,
>>     &endptr) : UINT32_MAX;
>>     -
>>     -       if (time && endptr && endptr[0]) {
>>     -               if (endptr[0] == 's')
>>     -                       time *= 1;
>>     -               else if (endptr[0] == 'm')
>>     -                       time *= 60;
>>     -               else if (endptr[0] == 'h')
>>     -                       time *= 3600;
>>     -               else if (endptr[0] == 'd')
>>     -                       time *= 24 * 3600;
>>     -               else if (endptr[0] == 'w')
>>     -                       time *= 7 * 24 * 3600;
>>     -               else
>>     +       double ret_time = strcmp(val, "infinite") ? strtod(val,
>>     &endptr) : UINT32_MAX;
>>     +
>>     +       if (absolute)
>>     +       {
>>     +               // "10 Jan 2020 00:00:00"
>>     +               // Parse absolut time
>>     +               struct tm tm = {0};
>>     +               char *s = strptime(val, "%d %b %Y %H:%M:%S", &tm);
>>     +               if (s == NULL) {
>>     +                       syslog(LOG_ERR, "Failed to Parse Date:
>>     %s", val);
>>                             goto err;
>>     +               }
>>     +
>>     +               time_t now = odhcpd_time();
>>     +               time_t wall_time = time(NULL);
>>     +               time_t t = mktime(&tm);
>>     +
>>     +               double diff = difftime(t,wall_time);
>>     +               ret_time += now + diff;
>>     +       } else {
>>     +               if (ret_time && endptr && endptr[0]) {
>>     +                       if (endptr[0] == 's')
>>     +                               ret_time *= 1;
>>     +                       else if (endptr[0] == 'm')
>>     +                               ret_time *= 60;
>>     +                       else if (endptr[0] == 'h')
>>     +                               ret_time *= 3600;
>>     +                       else if (endptr[0] == 'd')
>>     +                               ret_time *= 24 * 3600;
>>     +                       else if (endptr[0] == 'w')
>>     +                               ret_time *= 7 * 24 * 3600;
>>     +                       else
>>     +                               goto err;
>>     +               }
>>             }
>>
>>     -       if (time < 60)
>>     -               time = 60;
>>     +       if (ret_time < 60)
>>     +               ret_time = 60;
>>
>>     -       return time;
>>     +       return ret_time;
>>
>>      err:
>>             return -1;
>>     @@ -409,7 +432,7 @@ int set_lease_from_blobmsg(struct blob_attr *ba)
>>             }
>>
>>             if ((c = tb[LEASE_ATTR_LEASETIME])) {
>>     -               double time = parse_leasetime(c);
>>     +               double time = parse_leasetime(c, false); // do not
>>     support absolute timestamps for now
>>                     if (time < 0)
>>                             goto err;
>>
>>     @@ -520,8 +543,12 @@ int config_parse_interface(void *data, size_t
>>     len, const char *name, bool overwr
>>             if ((c = tb[IFACE_ATTR_DYNAMICDHCP]))
>>                     iface->no_dynamic_dhcp = !blobmsg_get_bool(c);
>>
>>     +
>>     +       if ((c = tb[IFACE_ATTR_ABSOLUTE_LIFETIME]))
>>     +               iface->absolute_lifetime = blobmsg_get_bool(c);
>>     +
>>             if ((c = tb[IFACE_ATTR_LEASETIME])) {
>>     -               double time = parse_leasetime(c);
>>     +               double time = parse_leasetime(c,
>>     iface->absolute_lifetime);
>>                     if (time < 0)
>>                             goto err;
>>
>>     @@ -529,7 +556,7 @@ int config_parse_interface(void *data, size_t
>>     len, const char *name, bool overwr
>>             }
>>
>>             if ((c = tb[IFACE_ATTR_PREFERRED_LIFETIME])) {
>>     -               double time = parse_leasetime(c);
>>     +               double time = parse_leasetime(c,
>>     iface->absolute_lifetime);
>>                     if (time < 0)
>>                             goto err;
>>
>>     diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c
>>     index a59fc20..78be8b8 100644
>>     --- a/src/dhcpv6-ia.c
>>     +++ b/src/dhcpv6-ia.c
>>     @@ -865,6 +865,16 @@ static size_t build_ia(uint8_t *buf, size_t
>>     buflen, uint16_t status,
>>                             if (prefix_valid > leasetime)
>>                                     prefix_valid = leasetime;
>>
>>     +                       if (iface->absolute_lifetime) {
>>     +                               if ((long int)
>>     iface->dhcp_leasetime > now) {
>>     +                                       prefix_valid =
>>     iface->dhcp_leasetime - now;
>>     +                                       prefix_pref =
>>     iface->preferred_lifetime - now;
>>     +                               } else { // if we have a timestamp
>>     in the past set pref and valid to 60s
>>     +                                       prefix_valid = 60;
>>     +                                       prefix_pref = 60;
>>     +                               }
>>     +                       }
>>     +
>>                             if (a->flags & OAF_DHCPV6_PD) {
>>                                     struct dhcpv6_ia_prefix o_ia_p = {
>>                                             .type =
>>     htons(DHCPV6_OPT_IA_PREFIX),
>>     diff --git a/src/odhcpd.h b/src/odhcpd.h
>>     index 45b6784..98673a8 100644
>>     --- a/src/odhcpd.h
>>     +++ b/src/odhcpd.h
>>     @@ -288,6 +288,7 @@ struct interface {
>>             uint32_t ra_hoplimit;
>>             int ra_mtu;
>>             uint32_t preferred_lifetime;
>>     +       bool absolute_lifetime;
>>
>>             // DHCP
>>             uint32_t dhcp_leasetime;
>>     --     2.30.0
>>
>>
>>     _______________________________________________
>>     openwrt-devel mailing list
>>     openwrt-devel@lists.openwrt.org
>>     <mailto:openwrt-devel@lists.openwrt.org>
>>     https://lists.openwrt.org/mailman/listinfo/openwrt-devel
>> <https://lists.openwrt.org/mailman/listinfo/openwrt-devel>
>>
diff mbox series

Patch

diff --git a/README b/README
index f9cbb11..0af5c75 100644
--- a/README
+++ b/README
@@ -107,11 +107,13 @@  dns_service		bool	1			Announce the address of interface as DNS service
 							if the list of dns is empty
 domain			list	<local search domain>	Search domains to announce
 
-leasetime		string	12h			DHCPv4 address leasetime
+leasetime		string	12h			DHCPv4 address leasetime. If absolute_lifetime is
+							set the value can be given as a date, e.g. "10 Jan 2020 00:00:00".
 start			integer	100			DHCPv4 pool start
 limit			integer	150			DHCPv4 pool size
 preferred_lifetime	string	12h			Value for the preferred lifetime
-							for a prefix
+							for a prefix. If absolute_lifetime is set the value can
+							be given as a date, e.g. "10 Jan 2020 00:00:00".
 ra_default		integer	0			Override default route
 			0: default, 1: ignore no public address, 2: ignore all
 ra_flags		list	other-config		List of RA flags to be
@@ -145,6 +147,8 @@  ndproxy_slave		bool	0			NDProxy external slave
 prefix_filter		string	::/0			Only advertise on-link prefixes within
 			[IPv6 prefix]			the provided IPv6 prefix; others are
 							filtered out.
+absolute_lifetime		bool	0			Interpret configured lifetime as
+							absolute timestamps. The format has to be "10 Jan 2020 00:00:00".
 
 
 Sections of type host (static leases)
diff --git a/src/config.c b/src/config.c
index 78b5855..42f73a1 100644
--- a/src/config.c
+++ b/src/config.c
@@ -8,6 +8,7 @@ 
 #include <string.h>
 #include <sys/stat.h>
 #include <syslog.h>
+#include <time.h>
 
 #include <uci.h>
 #include <uci_blob.h>
@@ -83,6 +84,7 @@  enum {
 	IFACE_ATTR_NDPROXY_SLAVE,
 	IFACE_ATTR_PREFIX_FILTER,
 	IFACE_ATTR_PREFERRED_LIFETIME,
+	IFACE_ATTR_ABSOLUTE_LIFETIME,
 	IFACE_ATTR_MAX
 };
 
@@ -132,6 +134,7 @@  static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = {
 	[IFACE_ATTR_NDPROXY_SLAVE] = { .name = "ndproxy_slave", .type = BLOBMSG_TYPE_BOOL },
 	[IFACE_ATTR_PREFIX_FILTER] = { .name = "prefix_filter", .type = BLOBMSG_TYPE_STRING },
 	[IFACE_ATTR_PREFERRED_LIFETIME] = { .name = "preferred_lifetime", .type = BLOBMSG_TYPE_STRING },
+	[IFACE_ATTR_ABSOLUTE_LIFETIME] = { .name = "absolute_lifetime", .type = BLOBMSG_TYPE_BOOL },
 };
 
 static const struct uci_blob_param_info iface_attr_info[IFACE_ATTR_MAX] = {
@@ -212,6 +215,7 @@  static void set_interface_defaults(struct interface *iface)
 	iface->ra_mininterval = iface->ra_maxinterval/3;
 	iface->ra_lifetime = -1;
 	iface->ra_dns = true;
+	iface->absolute_lifetime = false;
 }
 
 static void clean_interface(struct interface *iface)
@@ -321,29 +325,48 @@  static void set_config(struct uci_section *s)
 	}
 }
 
-static double parse_leasetime(struct blob_attr *c) {
+static double parse_leasetime(struct blob_attr *c, bool absolute) {
 	char *val = blobmsg_get_string(c), *endptr = NULL;
-	double time = strcmp(val, "infinite") ? strtod(val, &endptr) : UINT32_MAX;
-
-	if (time && endptr && endptr[0]) {
-		if (endptr[0] == 's')
-			time *= 1;
-		else if (endptr[0] == 'm')
-			time *= 60;
-		else if (endptr[0] == 'h')
-			time *= 3600;
-		else if (endptr[0] == 'd')
-			time *= 24 * 3600;
-		else if (endptr[0] == 'w')
-			time *= 7 * 24 * 3600;
-		else
+	double ret_time = strcmp(val, "infinite") ? strtod(val, &endptr) : UINT32_MAX;
+
+	if (absolute)
+	{
+		// "10 Jan 2020 00:00:00"
+		// Parse absolut time
+		struct tm tm = {0};
+		char *s = strptime(val, "%d %b %Y %H:%M:%S", &tm);
+		if (s == NULL) {
+			syslog(LOG_ERR, "Failed to Parse Date: %s", val);
 			goto err;
+		}
+
+		time_t now = odhcpd_time();
+		time_t wall_time = time(NULL);
+		time_t t = mktime(&tm);
+
+		double diff = difftime(t,wall_time);
+		ret_time += now + diff;
+	} else {
+		if (ret_time && endptr && endptr[0]) {
+			if (endptr[0] == 's')
+				ret_time *= 1;
+			else if (endptr[0] == 'm')
+				ret_time *= 60;
+			else if (endptr[0] == 'h')
+				ret_time *= 3600;
+			else if (endptr[0] == 'd')
+				ret_time *= 24 * 3600;
+			else if (endptr[0] == 'w')
+				ret_time *= 7 * 24 * 3600;
+			else
+				goto err;
+		}
 	}
 
-	if (time < 60)
-		time = 60;
+	if (ret_time < 60)
+		ret_time = 60;
 
-	return time;
+	return ret_time;
 
 err:
 	return -1;
@@ -409,7 +432,7 @@  int set_lease_from_blobmsg(struct blob_attr *ba)
 	}
 
 	if ((c = tb[LEASE_ATTR_LEASETIME])) {
-		double time = parse_leasetime(c);
+		double time = parse_leasetime(c, false); // do not support absolute timestamps for now
 		if (time < 0)
 			goto err;
 
@@ -520,8 +543,12 @@  int config_parse_interface(void *data, size_t len, const char *name, bool overwr
 	if ((c = tb[IFACE_ATTR_DYNAMICDHCP]))
 		iface->no_dynamic_dhcp = !blobmsg_get_bool(c);
 
+
+	if ((c = tb[IFACE_ATTR_ABSOLUTE_LIFETIME]))
+		iface->absolute_lifetime = blobmsg_get_bool(c);
+
 	if ((c = tb[IFACE_ATTR_LEASETIME])) {
-		double time = parse_leasetime(c);
+		double time = parse_leasetime(c, iface->absolute_lifetime);
 		if (time < 0)
 			goto err;
 
@@ -529,7 +556,7 @@  int config_parse_interface(void *data, size_t len, const char *name, bool overwr
 	}
 
 	if ((c = tb[IFACE_ATTR_PREFERRED_LIFETIME])) {
-		double time = parse_leasetime(c);
+		double time = parse_leasetime(c, iface->absolute_lifetime);
 		if (time < 0)
 			goto err;
 
diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c
index a59fc20..78be8b8 100644
--- a/src/dhcpv6-ia.c
+++ b/src/dhcpv6-ia.c
@@ -865,6 +865,16 @@  static size_t build_ia(uint8_t *buf, size_t buflen, uint16_t status,
 			if (prefix_valid > leasetime)
 				prefix_valid = leasetime;
 
+			if (iface->absolute_lifetime) {
+				if ((long int) iface->dhcp_leasetime > now) {
+					prefix_valid = iface->dhcp_leasetime - now;
+					prefix_pref = iface->preferred_lifetime - now;
+				} else { // if we have a timestamp in the past set pref and valid to 60s
+					prefix_valid = 60;
+					prefix_pref = 60;
+				}
+			}
+
 			if (a->flags & OAF_DHCPV6_PD) {
 				struct dhcpv6_ia_prefix o_ia_p = {
 					.type = htons(DHCPV6_OPT_IA_PREFIX),
diff --git a/src/odhcpd.h b/src/odhcpd.h
index 45b6784..98673a8 100644
--- a/src/odhcpd.h
+++ b/src/odhcpd.h
@@ -288,6 +288,7 @@  struct interface {
 	uint32_t ra_hoplimit;
 	int ra_mtu;
 	uint32_t preferred_lifetime;
+	bool absolute_lifetime;
 
 	// DHCP
 	uint32_t dhcp_leasetime;