void route_metric_done(RouteMetric *metric) {
assert(metric);
+ free(metric->metrics);
+ free(metric->metrics_set);
free(metric->tcp_congestion_control_algo);
}
assert(src);
assert(dest);
- dest->quickack = src->quickack;
- dest->fast_open_no_cookie = src->fast_open_no_cookie;
- dest->mtu = src->mtu;
- dest->initcwnd = src->initcwnd;
- dest->initrwnd = src->initrwnd;
- dest->advmss = src->advmss;
- dest->hop_limit = src->hop_limit;
- dest->tcp_rto_usec = src->tcp_rto_usec;
+ dest->n_metrics = src->n_metrics;
+ if (src->n_metrics > 0) {
+ assert(src->n_metrics != 1);
+
+ dest->metrics = newdup(uint32_t, src->metrics, src->n_metrics);
+ if (!dest->metrics)
+ return -ENOMEM;
+ } else
+ dest->metrics = NULL;
+
+ dest->n_metrics_set = src->n_metrics_set;
+ if (src->n_metrics_set > 0) {
+ assert(src->n_metrics_set != 1);
+
+ dest->metrics_set = newdup(bool, src->metrics_set, src->n_metrics_set);
+ if (!dest->metrics_set)
+ return -ENOMEM;
+ } else
+ dest->metrics_set = NULL;
return free_and_strdup(&dest->tcp_congestion_control_algo, src->tcp_congestion_control_algo);
}
void route_metric_hash_func(const RouteMetric *metric, struct siphash *state) {
assert(metric);
- siphash24_compress_typesafe(metric->initcwnd, state);
- siphash24_compress_typesafe(metric->initrwnd, state);
- siphash24_compress_typesafe(metric->advmss, state);
+ siphash24_compress_typesafe(metric->n_metrics, state);
+ siphash24_compress_safe(metric->metrics, sizeof(uint32_t) * metric->n_metrics, state);
+ siphash24_compress_string(metric->tcp_congestion_control_algo, state);
}
int route_metric_compare_func(const RouteMetric *a, const RouteMetric *b) {
assert(a);
assert(b);
- r = CMP(a->initcwnd, b->initcwnd);
+ r = memcmp_nn(a->metrics, a->n_metrics * sizeof(uint32_t), b->metrics, b->n_metrics * sizeof(uint32_t));
if (r != 0)
return r;
- r = CMP(a->initrwnd, b->initrwnd);
- if (r != 0)
- return r;
+ return strcmp_ptr(a->tcp_congestion_control_algo, b->tcp_congestion_control_algo);
+}
+
+int route_metric_set_full(RouteMetric *metric, uint16_t attr, uint32_t value, bool force) {
+ assert(metric);
+
+ if (force) {
+ if (!GREEDY_REALLOC0(metric->metrics_set, attr + 1))
+ return -ENOMEM;
+
+ metric->metrics_set[attr] = true;
+ metric->n_metrics_set = MAX(metric->n_metrics_set, (size_t) (attr + 1));
+ } else {
+ /* Do not override the values specified in conf parsers. */
+ if (metric->n_metrics_set > attr && metric->metrics_set[attr])
+ return 0;
+ }
+
+ if (value != 0) {
+ if (!GREEDY_REALLOC0(metric->metrics, attr + 1))
+ return -ENOMEM;
+
+ metric->metrics[attr] = value;
+ metric->n_metrics = MAX(metric->n_metrics, (size_t) (attr + 1));
+ return 0;
+ }
+
+ if (metric->n_metrics <= attr)
+ return 0;
+
+ metric->metrics[attr] = 0;
+
+ for (size_t i = metric->n_metrics; i > 0; i--)
+ if (metric->metrics[i-1] != 0) {
+ metric->n_metrics = i;
+ return 0;
+ }
- return CMP(a->advmss, b->advmss);
+ metric->n_metrics = 0;
+ return 0;
+}
+
+static void route_metric_unset(RouteMetric *metric, uint16_t attr) {
+ assert(metric);
+
+ if (metric->n_metrics_set > attr)
+ metric->metrics_set[attr] = false;
+
+ assert_se(route_metric_set_full(metric, attr, 0, /* force = */ false) >= 0);
+}
+
+uint32_t route_metric_get(const RouteMetric *metric, uint16_t attr) {
+ assert(metric);
+
+ if (metric->n_metrics <= attr)
+ return 0;
+
+ return metric->metrics[attr];
}
int route_metric_set_netlink_message(const RouteMetric *metric, sd_netlink_message *m) {
assert(metric);
assert(m);
+ if (metric->n_metrics <= 0 && isempty(metric->tcp_congestion_control_algo))
+ return 0;
+
r = sd_netlink_message_open_container(m, RTA_METRICS);
if (r < 0)
return r;
- if (metric->mtu > 0) {
- r = sd_netlink_message_append_u32(m, RTAX_MTU, metric->mtu);
- if (r < 0)
- return r;
- }
-
- if (metric->initcwnd > 0) {
- r = sd_netlink_message_append_u32(m, RTAX_INITCWND, metric->initcwnd);
- if (r < 0)
- return r;
- }
-
- if (metric->initrwnd > 0) {
- r = sd_netlink_message_append_u32(m, RTAX_INITRWND, metric->initrwnd);
- if (r < 0)
- return r;
- }
+ for (size_t i = 0; i < metric->n_metrics; i++) {
+ if (i == RTAX_CC_ALGO)
+ continue;
- if (metric->quickack >= 0) {
- r = sd_netlink_message_append_u32(m, RTAX_QUICKACK, metric->quickack);
- if (r < 0)
- return r;
- }
+ if (metric->metrics[i] == 0)
+ continue;
- if (metric->fast_open_no_cookie >= 0) {
- r = sd_netlink_message_append_u32(m, RTAX_FASTOPEN_NO_COOKIE, metric->fast_open_no_cookie);
- if (r < 0)
- return r;
- }
-
- if (metric->advmss > 0) {
- r = sd_netlink_message_append_u32(m, RTAX_ADVMSS, metric->advmss);
+ r = sd_netlink_message_append_u32(m, i, metric->metrics[i]);
if (r < 0)
return r;
}
return r;
}
- if (metric->hop_limit > 0) {
- r = sd_netlink_message_append_u32(m, RTAX_HOPLIMIT, metric->hop_limit);
- if (r < 0)
- return r;
- }
-
- if (metric->tcp_rto_usec > 0) {
- r = sd_netlink_message_append_u32(m, RTAX_RTO_MIN, DIV_ROUND_UP(metric->tcp_rto_usec, USEC_PER_MSEC));
- if (r < 0)
- return r;
- }
-
r = sd_netlink_message_close_container(m);
if (r < 0)
return r;
}
int route_metric_read_netlink_message(RouteMetric *metric, sd_netlink_message *m) {
+ _cleanup_free_ void *data = NULL;
+ size_t len;
int r;
assert(metric);
assert(m);
- r = sd_netlink_message_enter_container(m, RTA_METRICS);
+ r = sd_netlink_message_read_data(m, RTA_METRICS, &len, &data);
if (r == -ENODATA)
return 0;
if (r < 0)
- return log_warning_errno(r, "rtnl: Could not enter RTA_METRICS container, ignoring: %m");
+ return log_warning_errno(r, "rtnl: Could not read RTA_METRICS attribute, ignoring: %m");
- r = sd_netlink_message_read_u32(m, RTAX_INITCWND, &metric->initcwnd);
- if (r < 0 && r != -ENODATA)
- return log_warning_errno(r, "rtnl: received route message with invalid initcwnd, ignoring: %m");
+ for (struct rtattr *rta = data; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
+ size_t rta_type = RTA_TYPE(rta);
- r = sd_netlink_message_read_u32(m, RTAX_INITRWND, &metric->initrwnd);
- if (r < 0 && r != -ENODATA)
- return log_warning_errno(r, "rtnl: received route message with invalid initrwnd, ignoring: %m");
+ if (rta_type == RTAX_CC_ALGO) {
+ char *p = memdup_suffix0(RTA_DATA(rta), RTA_PAYLOAD(rta));
+ if (!p)
+ return log_oom();
- r = sd_netlink_message_read_u32(m, RTAX_ADVMSS, &metric->advmss);
- if (r < 0 && r != -ENODATA)
- return log_warning_errno(r, "rtnl: received route message with invalid advmss, ignoring: %m");
+ free_and_replace(metric->tcp_congestion_control_algo, p);
- r = sd_netlink_message_exit_container(m);
- if (r < 0)
- return log_warning_errno(r, "rtnl: Could not exit from RTA_METRICS container, ignoring: %m");
+ } else {
+ if (RTA_PAYLOAD(rta) != sizeof(uint32_t))
+ continue;
+
+ r = route_metric_set(metric, rta_type, *(uint32_t*) RTA_DATA(rta));
+ if (r < 0)
+ return log_oom();
+ }
+ }
return 0;
}
return 0;
}
- r = config_parse_mtu(unit, filename, line, section, section_line, lvalue, AF_UNSPEC, rvalue, &route->metric.mtu, userdata);
+ uint32_t k;
+ r = config_parse_mtu(unit, filename, line, section, section_line, lvalue, AF_UNSPEC, rvalue, &k, userdata);
if (r <= 0)
return r;
+ if (route_metric_set_full(&route->metric, ltype, k, /* force = */ true) < 0)
+ return log_oom();
+
TAKE_PTR(route);
return 0;
}
}
if (isempty(rvalue)) {
- route->metric.advmss = 0;
+ route_metric_unset(&route->metric, ltype);
TAKE_PTR(route);
return 0;
}
return 0;
}
- route->metric.advmss = u;
+ if (route_metric_set_full(&route->metric, ltype, u, /* force = */ true) < 0)
+ return log_oom();
TAKE_PTR(route);
return 0;
}
if (isempty(rvalue)) {
- route->metric.hop_limit = 0;
+ route_metric_unset(&route->metric, ltype);
TAKE_PTR(route);
return 0;
}
return 0;
}
- route->metric.hop_limit = k;
+ if (route_metric_set_full(&route->metric, ltype, k, /* force = */ true) < 0)
+ return log_oom();
TAKE_PTR(route);
return 0;
}
*window = k;
- return 0;
+ return 1;
}
int config_parse_route_metric_tcp_window(
_cleanup_(route_free_or_set_invalidp) Route *route = NULL;
Network *network = userdata;
- uint32_t *d;
int r;
assert(filename);
return 0;
}
- if (streq(lvalue, "InitialCongestionWindow"))
- d = &route->metric.initcwnd;
- else if (streq(lvalue, "InitialAdvertisedReceiveWindow"))
- d = &route->metric.initrwnd;
- else
- assert_not_reached();
-
- r = config_parse_tcp_window(unit, filename, line, section, section_line, lvalue, ltype, rvalue, d, userdata);
- if (r < 0)
+ uint32_t k;
+ r = config_parse_tcp_window(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &k, userdata);
+ if (r <= 0)
return r;
+ if (route_metric_set_full(&route->metric, ltype, k, /* force = */ true) < 0)
+ return log_oom();
+
TAKE_PTR(route);
return 0;
}
return 0;
}
- route->metric.tcp_rto_usec = usec;
+ if (route_metric_set_full(&route->metric, ltype, DIV_ROUND_UP(usec, USEC_PER_MSEC), /* force = */ true) < 0)
+ return log_oom();
TAKE_PTR(route);
return 0;
return 0;
}
- if (streq(lvalue, "QuickAck"))
- route->metric.quickack = r;
- else if (streq(lvalue, "FastOpenNoCookie"))
- route->metric.fast_open_no_cookie = r;
- else
- assert_not_reached();
+ if (route_metric_set_full(&route->metric, ltype, r, /* force = */ true) < 0)
+ return log_oom();
TAKE_PTR(route);
return 0;