|
1 | | -From 4c2bb6b71b0b7d4d9a2ca5796b320aaa4516c1bb Mon Sep 17 00:00:00 2001 |
| 1 | +From 8a8150f045ed73e15c77c38be4b90999024f6d17 Mon Sep 17 00:00:00 2001 |
| 2 | +From: skbeh <60107333+skbeh@users.noreply.github.com> |
2 | 3 | Date: Sat, 16 Sep 2023 15:04:12 +0000 |
3 | | -Subject: [PATCH] config: allow configuring limit of min and max value for |
4 | | - preferred and valid lifetime |
| 4 | +Subject: [PATCH] config: allow configuring max limit for preferred and valid |
| 5 | + lifetime |
5 | 6 |
|
6 | 7 | --- |
7 | | - src/config.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ |
8 | | - src/dhcpv6-ia.c | 16 ++++++++++++++++ |
9 | | - src/odhcpd.h | 4 ++++ |
10 | | - src/router.c | 16 ++++++++++++++++ |
11 | | - 4 files changed, 83 insertions(+) |
| 8 | + src/config.c | 32 ++++++++++++++++++++++++++++++++ |
| 9 | + src/dhcpv6-ia.c | 9 +++++++++ |
| 10 | + src/odhcpd.h | 6 ++++++ |
| 11 | + src/router.c | 35 +++++++++++++++++++++++++++-------- |
| 12 | + 4 files changed, 74 insertions(+), 8 deletions(-) |
12 | 13 |
|
13 | 14 | --- a/src/config.c |
14 | 15 | +++ b/src/config.c |
15 | | -@@ -92,6 +92,10 @@ enum { |
| 16 | +@@ -92,6 +92,8 @@ enum { |
16 | 17 | IFACE_ATTR_NDPROXY_SLAVE, |
17 | 18 | IFACE_ATTR_PREFIX_FILTER, |
18 | 19 | IFACE_ATTR_PREFERRED_LIFETIME, |
19 | | -+ IFACE_ATTR_MIN_PREFERRED_LIFETIME, |
20 | 20 | + IFACE_ATTR_MAX_PREFERRED_LIFETIME, |
21 | | -+ IFACE_ATTR_MIN_VALID_LIFETIME, |
22 | 21 | + IFACE_ATTR_MAX_VALID_LIFETIME, |
23 | 22 | IFACE_ATTR_NTP, |
24 | 23 | IFACE_ATTR_MAX |
25 | 24 | }; |
26 | | -@@ -145,6 +149,10 @@ static const struct blobmsg_policy iface |
| 25 | +@@ -145,6 +147,8 @@ static const struct blobmsg_policy iface |
27 | 26 | [IFACE_ATTR_NDPROXY_SLAVE] = { .name = "ndproxy_slave", .type = BLOBMSG_TYPE_BOOL }, |
28 | 27 | [IFACE_ATTR_PREFIX_FILTER] = { .name = "prefix_filter", .type = BLOBMSG_TYPE_STRING }, |
29 | 28 | [IFACE_ATTR_PREFERRED_LIFETIME] = { .name = "preferred_lifetime", .type = BLOBMSG_TYPE_STRING }, |
30 | | -+ [IFACE_ATTR_MIN_PREFERRED_LIFETIME] = { .name = "min_preferred_lifetime", .type = BLOBMSG_TYPE_STRING }, |
31 | 29 | + [IFACE_ATTR_MAX_PREFERRED_LIFETIME] = { .name = "max_preferred_lifetime", .type = BLOBMSG_TYPE_STRING }, |
32 | | -+ [IFACE_ATTR_MIN_VALID_LIFETIME] = { .name = "min_valid_lifetime", .type = BLOBMSG_TYPE_STRING }, |
33 | 30 | + [IFACE_ATTR_MAX_VALID_LIFETIME] = { .name = "max_valid_lifetime", .type = BLOBMSG_TYPE_STRING }, |
34 | 31 | [IFACE_ATTR_NTP] = { .name = "ntp", .type = BLOBMSG_TYPE_ARRAY }, |
35 | 32 | }; |
36 | 33 |
|
37 | | -@@ -648,6 +656,45 @@ int config_parse_interface(void *data, s |
| 34 | +@@ -648,6 +652,34 @@ int config_parse_interface(void *data, s |
38 | 35 |
|
39 | 36 | } |
40 | 37 |
|
41 | | -+ if ((c = tb[IFACE_ATTR_MIN_PREFERRED_LIFETIME])) { |
42 | | -+ double time = parse_leasetime(c); |
43 | | -+ |
44 | | -+ if (time >= 0) |
45 | | -+ iface->min_preferred_lifetime = time; |
46 | | -+ else |
47 | | -+ syslog(LOG_ERR, "Invalid %s value configured for interface '%s'", |
48 | | -+ iface_attrs[IFACE_ATTR_MIN_PREFERRED_LIFETIME].name, iface->name); |
49 | | -+ } |
50 | | -+ |
51 | 38 | + if ((c = tb[IFACE_ATTR_MAX_PREFERRED_LIFETIME])) { |
52 | 39 | + double time = parse_leasetime(c); |
53 | 40 | + |
54 | | -+ if (time >= 0) |
| 41 | ++ if (time >= 0) { |
55 | 42 | + iface->max_preferred_lifetime = time; |
56 | | -+ else |
| 43 | ++ } else { |
| 44 | ++ iface->max_preferred_lifetime = ND_PREFERRED_LIMIT; |
57 | 45 | + syslog(LOG_ERR, "Invalid %s value configured for interface '%s'", |
58 | 46 | + iface_attrs[IFACE_ATTR_MAX_PREFERRED_LIFETIME].name, iface->name); |
59 | | -+ } |
60 | | -+ if ((c = tb[IFACE_ATTR_MIN_VALID_LIFETIME])) { |
61 | | -+ double time = parse_leasetime(c); |
62 | | -+ |
63 | | -+ if (time >= 0) |
64 | | -+ iface->min_valid_lifetime = time; |
65 | | -+ else |
66 | | -+ syslog(LOG_ERR, "Invalid %s value configured for interface '%s'", |
67 | | -+ iface_attrs[IFACE_ATTR_MIN_VALID_LIFETIME].name, iface->name); |
| 47 | ++ } |
| 48 | ++ } else { |
| 49 | ++ iface->max_preferred_lifetime = ND_PREFERRED_LIMIT; |
68 | 50 | + } |
69 | 51 | + |
70 | 52 | + if ((c = tb[IFACE_ATTR_MAX_VALID_LIFETIME])) { |
71 | 53 | + double time = parse_leasetime(c); |
72 | 54 | + |
73 | | -+ if (time >= 0) |
| 55 | ++ if (time >= 0) { |
74 | 56 | + iface->max_valid_lifetime = time; |
75 | | -+ else |
| 57 | ++ } else { |
| 58 | ++ iface->max_valid_lifetime = ND_VALID_LIMIT; |
76 | 59 | + syslog(LOG_ERR, "Invalid %s value configured for interface '%s'", |
77 | 60 | + iface_attrs[IFACE_ATTR_MAX_VALID_LIFETIME].name, iface->name); |
| 61 | ++ } |
| 62 | ++ } else { |
| 63 | ++ iface->max_valid_lifetime = ND_VALID_LIMIT; |
78 | 64 | + } |
79 | 65 | + |
80 | 66 | if ((c = tb[IFACE_ATTR_START])) { |
81 | 67 | iface->dhcpv4_start.s_addr = htonl(blobmsg_get_u32(c)); |
82 | 68 | iface->dhcpv4_end.s_addr = htonl(ntohl(iface->dhcpv4_start.s_addr) + |
83 | 69 | --- a/src/dhcpv6-ia.c |
84 | 70 | +++ b/src/dhcpv6-ia.c |
85 | | -@@ -1027,6 +1027,22 @@ static size_t build_ia(uint8_t *buf, siz |
| 71 | +@@ -1027,6 +1027,15 @@ static size_t build_ia(uint8_t *buf, siz |
86 | 72 | } |
87 | 73 | } |
88 | 74 |
|
89 | 75 | + if (pref) { |
90 | | -+ if (iface->min_preferred_lifetime) |
91 | | -+ pref = max(pref, iface->min_preferred_lifetime); |
92 | 76 | + if (iface->max_preferred_lifetime) |
93 | 77 | + pref = min(pref, iface->max_preferred_lifetime); |
94 | 78 | + } |
95 | 79 | + if (valid) { |
96 | | -+ if (iface->min_valid_lifetime) |
97 | | -+ valid = max(valid, iface->min_valid_lifetime); |
98 | 80 | + if (iface->max_valid_lifetime) |
99 | 81 | + valid = min(valid, iface->max_valid_lifetime); |
100 | | -+ |
101 | | -+ if (!pref) |
102 | | -+ pref = valid; |
103 | 82 | + } |
104 | 83 | + |
105 | 84 | if (!INFINITE_VALID(a->valid_until)) |
106 | 85 | /* UINT32_MAX is considered as infinite leasetime */ |
107 | 86 | a->valid_until = (valid == UINT32_MAX) ? 0 : valid + now; |
108 | 87 | --- a/src/odhcpd.h |
109 | 88 | +++ b/src/odhcpd.h |
110 | | -@@ -319,6 +319,10 @@ struct interface { |
| 89 | +@@ -37,6 +37,10 @@ |
| 90 | + // RFC 8781 defines PREF64 option |
| 91 | + #define ND_OPT_PREF64 38 |
| 92 | + |
| 93 | ++// RFC9096 defines recommended option lifetimes configuration values |
| 94 | ++#define ND_PREFERRED_LIMIT 2700 |
| 95 | ++#define ND_VALID_LIMIT 5400 |
| 96 | ++ |
| 97 | + #define INFINITE_VALID(x) ((x) == 0) |
| 98 | + |
| 99 | + #define _unused __attribute__((unused)) |
| 100 | +@@ -319,6 +323,8 @@ struct interface { |
111 | 101 | uint32_t ra_hoplimit; |
112 | 102 | int ra_mtu; |
113 | 103 | uint32_t preferred_lifetime; |
114 | | -+ uint32_t min_preferred_lifetime; |
115 | 104 | + uint32_t max_preferred_lifetime; |
116 | | -+ uint32_t min_valid_lifetime; |
117 | 105 | + uint32_t max_valid_lifetime; |
118 | 106 |
|
119 | 107 | // DHCP |
120 | 108 | uint32_t dhcp_leasetime; |
121 | 109 | --- a/src/router.c |
122 | 110 | +++ b/src/router.c |
123 | | -@@ -600,6 +600,22 @@ static int send_router_advert(struct int |
| 111 | +@@ -452,7 +452,8 @@ static int send_router_advert(struct int |
| 112 | + size_t dns_sz = 0, search_sz = 0, pref64_sz = 0; |
| 113 | + size_t pfxs_cnt = 0, routes_cnt = 0; |
| 114 | + ssize_t valid_addr_cnt = 0, invalid_addr_cnt = 0; |
| 115 | +- uint32_t minvalid = UINT32_MAX, maxival, lifetime; |
| 116 | ++ uint32_t minvalid = UINT32_MAX, maxival, lifetime, max_prefix_vlt = ND_VALID_LIMIT; |
| 117 | ++ uint32_t calculated_ra_lifetime; |
| 118 | + int msecs, mtu = iface->ra_mtu, hlim = iface->ra_hoplimit; |
| 119 | + bool default_route = false; |
| 120 | + bool valid_prefix = false; |
| 121 | +@@ -598,10 +599,22 @@ static int send_router_advert(struct int |
| 122 | + if (addr->valid > (uint32_t)now) { |
| 123 | + valid = TIME_LEFT(addr->valid, now); |
124 | 124 |
|
| 125 | ++ if (valid < max_prefix_vlt) |
| 126 | ++ max_prefix_vlt = valid; |
| 127 | ++ |
125 | 128 | if (iface->ra_useleasetime && valid > iface->dhcp_leasetime) |
126 | 129 | valid = iface->dhcp_leasetime; |
127 | | -+ |
128 | | -+ if (!preferred) |
129 | | -+ preferred = valid; |
130 | | -+ } |
131 | | -+ |
| 130 | + } |
| 131 | + |
132 | 132 | + if (preferred) { |
133 | | -+ if (iface->min_preferred_lifetime) |
134 | | -+ preferred = max(preferred, iface->min_preferred_lifetime); |
135 | 133 | + if (iface->max_preferred_lifetime) |
136 | 134 | + preferred = min(preferred, iface->max_preferred_lifetime); |
137 | 135 | + } |
138 | 136 | + if (valid) { |
139 | | -+ if (iface->min_valid_lifetime) |
140 | | -+ valid = max(valid, iface->min_valid_lifetime); |
141 | 137 | + if (iface->max_valid_lifetime) |
142 | 138 | + valid = min(valid, iface->max_valid_lifetime); |
| 139 | ++ } |
| 140 | ++ |
| 141 | + if (minvalid > valid) |
| 142 | + minvalid = valid; |
| 143 | + |
| 144 | +@@ -629,24 +642,30 @@ static int send_router_advert(struct int |
| 145 | + |
| 146 | + /* Calculate periodic transmit */ |
| 147 | + msecs = calc_adv_interval(iface, minvalid, &maxival); |
| 148 | +- lifetime = calc_ra_lifetime(iface, maxival); |
| 149 | ++ calculated_ra_lifetime = calc_ra_lifetime(iface, maxival); |
| 150 | ++ lifetime = min(calculated_ra_lifetime, max_prefix_vlt); |
| 151 | + |
| 152 | + if (!iface->have_link_local) { |
| 153 | + syslog(LOG_NOTICE, "Skip sending a RA on %s as no link local address is available", iface->name); |
| 154 | + goto out; |
| 155 | + } |
| 156 | + |
| 157 | +- if (default_route && valid_prefix) { |
| 158 | +- adv.h.nd_ra_router_lifetime = htons(lifetime < UINT16_MAX ? lifetime : UINT16_MAX); |
| 159 | +- } else { |
| 160 | ++ /* RFC9096: CE routers SHOULD set the "Router Lifetime" of Router Advertisement (RA) messages to ND_PREFERRED_LIMIT. */ |
| 161 | ++ adv.h.nd_ra_router_lifetime = ND_PREFERRED_LIMIT; |
| 162 | ++ if (!(default_route && valid_prefix)) { |
| 163 | + adv.h.nd_ra_router_lifetime = 0; |
| 164 | + |
| 165 | + if (default_route) { |
| 166 | + syslog(LOG_WARNING, "A default route is present but there is no public prefix " |
| 167 | +- "on %s thus we don't announce a default route by overriding ra_lifetime!", iface->name); |
| 168 | ++ "on %s thus we don't announce a default route by setting ra_lifetime to zero!", iface->name); |
| 169 | + } else { |
| 170 | +- syslog(LOG_WARNING, "No default route present, overriding ra_lifetime!"); |
| 171 | ++ syslog(LOG_WARNING, "No default route present, setting ra_lifetime to zero!"); |
143 | 172 | } |
| 173 | ++ } else if (iface->ra_lifetime >= 0) { |
| 174 | ++ adv.h.nd_ra_router_lifetime = calculated_ra_lifetime; |
| 175 | ++ if (calculated_ra_lifetime == 0) |
| 176 | ++ syslog(LOG_WARNING, "A default route is present and there is public prefix " |
| 177 | ++ "but ra_lifetime on iface was set to zero, setting ra_lifetime to zero!"); |
| 178 | + } |
| 179 | + |
| 180 | + syslog(LOG_DEBUG, "Using a RA lifetime of %d seconds on %s", ntohs(adv.h.nd_ra_router_lifetime), iface->name); |
| 181 | +@@ -710,7 +729,7 @@ static int send_router_advert(struct int |
| 182 | + |
| 183 | + if (iface->pref64_length) { |
| 184 | + /* RFC 8781 § 4.1 rounding up lifetime to multiply of 8 */ |
| 185 | +- uint16_t pref64_lifetime = lifetime < (UINT16_MAX - 7) ? lifetime + 7 : UINT16_MAX; |
| 186 | ++ uint16_t pref64_lifetime = lifetime < (UINT16_MAX - 7) ? lifetime + 7 : (UINT16_MAX - 7); |
| 187 | + uint8_t prefix_length_code; |
| 188 | + uint32_t mask_a1, mask_a2; |
144 | 189 |
|
145 | | - if (minvalid > valid) |
|
0 commit comments