From 225943eaea3cdd634dce8fd84547baf1bc363640 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Wed, 14 Feb 2024 14:01:04 +0100 Subject: [PATCH] Filter: Add support for setting TCP congestion control algorithm Allow to set TCP congestion control algorithm using krt_congctl route attribute. Based on patch from Trisha Biswas , thanks! --- doc/bird.sgml | 6 +-- sysdep/linux/krt-sys.h | 3 +- sysdep/linux/netlink.Y | 3 +- sysdep/linux/netlink.c | 84 +++++++++++++++++++++++++++++++++--------- 4 files changed, 73 insertions(+), 23 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 79d5873f..5d59ae4d 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -3962,15 +3962,15 @@ these attributes:

In Linux, there is also a plenty of obscure route attributes mostly focused on tuning TCP performance of local connections. BIRD supports most of these attributes, see Linux or iproute2 documentation for their meaning. Attributes -Example diff --git a/sysdep/linux/krt-sys.h b/sysdep/linux/krt-sys.h index 8897f889..7c20a09e 100644 --- a/sysdep/linux/krt-sys.h +++ b/sysdep/linux/krt-sys.h @@ -39,7 +39,7 @@ static inline struct ifa * kif_get_primary_ip(struct iface *i UNUSED) { return N #define EA_KRT_SCOPE EA_CODE(PROTOCOL_KERNEL, 0x12) -#define KRT_METRICS_MAX 0x10 /* RTAX_QUICKACK+1 */ +#define KRT_METRICS_MAX 0x11 /* RTAX_CC_ALGO+1 */ #define KRT_METRICS_OFFSET 0x20 /* Offset of EA_KRT_* vs RTAX_* */ #define KRT_FEATURES_MAX 4 @@ -64,6 +64,7 @@ static inline struct ifa * kif_get_primary_ip(struct iface *i UNUSED) { return N #define EA_KRT_RTO_MIN EA_CODE(PROTOCOL_KERNEL, 0x2d) #define EA_KRT_INITRWND EA_CODE(PROTOCOL_KERNEL, 0x2e) #define EA_KRT_QUICKACK EA_CODE(PROTOCOL_KERNEL, 0x2f) +#define EA_KRT_CONGCTL EA_CODE(PROTOCOL_KERNEL, 0x30) struct krt_params { diff --git a/sysdep/linux/netlink.Y b/sysdep/linux/netlink.Y index 487ad1d8..70b83daf 100644 --- a/sysdep/linux/netlink.Y +++ b/sysdep/linux/netlink.Y @@ -14,7 +14,7 @@ CF_KEYWORDS(KERNEL, TABLE, METRIC, NETLINK, RX, BUFFER, KRT_PREFSRC, KRT_REALM, KRT_SCOPE, KRT_MTU, KRT_WINDOW, KRT_RTT, KRT_RTTVAR, KRT_SSTRESH, KRT_CWND, KRT_ADVMSS, KRT_REORDERING, KRT_HOPLIMIT, KRT_INITCWND, KRT_RTO_MIN, KRT_INITRWND, KRT_QUICKACK, - KRT_LOCK_MTU, KRT_LOCK_WINDOW, KRT_LOCK_RTT, KRT_LOCK_RTTVAR, + KRT_CONGCTL, KRT_LOCK_MTU, KRT_LOCK_WINDOW, KRT_LOCK_RTT, KRT_LOCK_RTTVAR, KRT_LOCK_SSTRESH, KRT_LOCK_CWND, KRT_LOCK_ADVMSS, KRT_LOCK_REORDERING, KRT_LOCK_HOPLIMIT, KRT_LOCK_RTO_MIN, KRT_FEATURE_ECN, KRT_FEATURE_ALLFRAG) @@ -45,6 +45,7 @@ dynamic_attr: KRT_INITCWND { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT dynamic_attr: KRT_RTO_MIN { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_RTO_MIN); } ; dynamic_attr: KRT_INITRWND { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_INITRWND); } ; dynamic_attr: KRT_QUICKACK { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_QUICKACK); } ; +dynamic_attr: KRT_CONGCTL { $$ = f_new_dynamic_attr(EAF_TYPE_STRING, T_STRING, EA_KRT_CONGCTL); } ; /* Bits of EA_KRT_LOCK, based on RTAX_* constants */ diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index 29446cab..fb54de39 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -479,6 +479,9 @@ static inline u16 rta_get_u16(struct rtattr *a) static inline u32 rta_get_u32(struct rtattr *a) { return *(u32 *) RTA_DATA(a); } +static inline const char *rta_get_str(struct rtattr *a) +{ return RTA_DATA(a); } + static inline ip4_addr rta_get_ip4(struct rtattr *a) { return ip4_ntoh(*(ip4_addr *) RTA_DATA(a)); } @@ -569,6 +572,12 @@ nl_add_attr_u32(struct nlmsghdr *h, uint bufsize, int code, u32 data) nl_add_attr(h, bufsize, code, &data, 4); } +static inline void +nl_add_attr_str(struct nlmsghdr *h, unsigned bufsize, int code, const char *str) +{ + nl_add_attr(h, bufsize, code, str, strlen(str) + 1); +} + static inline void nl_add_attr_ip4(struct nlmsghdr *h, uint bufsize, int code, ip4_addr ip4) { @@ -822,21 +831,26 @@ err: return NULL; } +STATIC_ASSERT(EA_KRT_METRICS + RTAX_CC_ALGO == EA_KRT_CONGCTL); + static void -nl_add_metrics(struct nlmsghdr *h, uint bufsize, u32 *metrics, int max) +nl_add_metrics(struct nlmsghdr *h, uint bufsize, u32 *metrics, const char *cc_algo, int max) { struct rtattr *a = nl_open_attr(h, bufsize, RTA_METRICS); int t; for (t = 1; t < max; t++) if (metrics[0] & (1 << t)) - nl_add_attr_u32(h, bufsize, t, metrics[t]); + if (t == RTAX_CC_ALGO) + nl_add_attr_str(h, bufsize, t, cc_algo); + else + nl_add_attr_u32(h, bufsize, t, metrics[t]); nl_close_attr(h, a); } static int -nl_parse_metrics(struct rtattr *hdr, u32 *metrics, int max) +nl_parse_metrics(struct rtattr *hdr, u32 *metrics, const char **cc_algo, int max) { struct rtattr *a = RTA_DATA(hdr); int len = RTA_PAYLOAD(hdr); @@ -844,17 +858,31 @@ nl_parse_metrics(struct rtattr *hdr, u32 *metrics, int max) metrics[0] = 0; for (; RTA_OK(a, len); a = RTA_NEXT(a, len)) { - if (a->rta_type == RTA_UNSPEC) + if (a->rta_type == RTAX_UNSPEC) continue; if (a->rta_type >= max) continue; - if (RTA_PAYLOAD(a) != 4) - return -1; + if (a->rta_type == RTAX_CC_ALGO) + { + *cc_algo = rta_get_str(a); + int slen = RTA_PAYLOAD(a); - metrics[0] |= 1 << a->rta_type; - metrics[a->rta_type] = rta_get_u32(a); + if (!slen || ((*cc_algo)[slen - 1] != 0)) + return -1; + + metrics[0] |= 1 << a->rta_type; + metrics[a->rta_type] = 0; + } + else + { + if (RTA_PAYLOAD(a) != 4) + return -1; + + metrics[0] |= 1 << a->rta_type; + metrics[a->rta_type] = rta_get_u32(a); + } } if (len > 0) @@ -1382,6 +1410,7 @@ nl_send_route(struct krt_proto *p, rte *e, int op) u32 metrics[KRT_METRICS_MAX]; + const char *cc_algo = NULL; metrics[0] = 0; struct ea_walk_state ews = { .eattrs = eattrs }; @@ -1389,11 +1418,15 @@ nl_send_route(struct krt_proto *p, rte *e, int op) { int id = ea->id - EA_KRT_METRICS; metrics[0] |= 1 << id; - metrics[id] = ea->u.data; + + if (id == RTAX_CC_ALGO) + cc_algo = ea->u.ptr->data; + else + metrics[id] = ea->u.data; } if (metrics[0]) - nl_add_metrics(&r->h, rsize, metrics, KRT_METRICS_MAX); + nl_add_metrics(&r->h, rsize, metrics, cc_algo, KRT_METRICS_MAX); switch (a->dest) { @@ -1795,10 +1828,11 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h) if (a[RTA_METRICS]) { u32 metrics[KRT_METRICS_MAX]; + const char *cc_algo = NULL; ea_list *ea = lp_alloc(s->pool, sizeof(ea_list) + KRT_METRICS_MAX * sizeof(eattr)); int t, n = 0; - if (nl_parse_metrics(a[RTA_METRICS], metrics, ARRAY_SIZE(metrics)) < 0) + if (nl_parse_metrics(a[RTA_METRICS], metrics, &cc_algo, ARRAY_SIZE(metrics)) < 0) { log(L_ERR "KRT: Received route %N with strange RTA_METRICS attribute", net->n.addr); return; @@ -1806,12 +1840,25 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h) for (t = 1; t < KRT_METRICS_MAX; t++) if (metrics[0] & (1 << t)) - ea->attrs[n++] = (eattr) { - .id = EA_CODE(PROTOCOL_KERNEL, KRT_METRICS_OFFSET + t), - .flags = 0, - .type = EAF_TYPE_INT, /* FIXME: Some are EAF_TYPE_BITFIELD */ - .u.data = metrics[t], - }; + if (t == RTAX_CC_ALGO) + { + struct adata *ad = lp_alloc_adata(s->pool, strlen(cc_algo)); + memcpy(ad->data, cc_algo, ad->length); + + ea->attrs[n++] = (eattr) { + .id = EA_KRT_CONGCTL, + .type = EAF_TYPE_STRING, + .u.ptr = ad, + }; + } + else + { + ea->attrs[n++] = (eattr) { + .id = EA_KRT_METRICS + t, + .type = EAF_TYPE_INT, /* FIXME: Some are EAF_TYPE_BITFIELD */ + .u.data = metrics[t], + }; + } if (n > 0) { @@ -2094,7 +2141,8 @@ krt_sys_copy_config(struct krt_config *d, struct krt_config *s) static const char *krt_metrics_names[KRT_METRICS_MAX] = { NULL, "lock", "mtu", "window", "rtt", "rttvar", "sstresh", "cwnd", "advmss", - "reordering", "hoplimit", "initcwnd", "features", "rto_min", "initrwnd", "quickack" + "reordering", "hoplimit", "initcwnd", "features", "rto_min", "initrwnd", "quickack", + "congctl" }; static const char *krt_features_names[KRT_FEATURES_MAX] = {