diff --git a/doc/bird.sgml b/doc/bird.sgml index 1d5ae056..310aef37 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -3377,7 +3377,7 @@ Supported attributes are: Example diff --git a/filter/config.Y b/filter/config.Y index 8916ea97..8916d08d 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -178,6 +178,9 @@ f_generate_empty(struct f_dynamic_attr dyn) case EAF_TYPE_LC_SET: empty = f_const_empty_lclist; break; + case EAF_TYPE_CC_ALGO: + empty = f_const_empty_cc_algo; + break; default: cf_error("Can't empty that attribute"); } @@ -816,6 +819,7 @@ term: | '-' EMPTY '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_clist); } | '-' '-' EMPTY '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_eclist); } | '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_lclist); } + | '-' '-' '-' '-' EMPTY '-' '-' '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_cc_algo); } | PREPEND '(' term ',' term ')' { $$ = f_new_inst(FI_PATH_PREPEND, $3, $5); } | ADD '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD, $3, $5); } | DELETE '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_DEL, $3, $5); } diff --git a/filter/data.c b/filter/data.c index 56c1fb17..84e91d59 100644 --- a/filter/data.c +++ b/filter/data.c @@ -91,6 +91,9 @@ const struct f_val f_const_empty_path = { }, f_const_empty_lclist = { .type = T_LCLIST, .val.ad = &null_adata, +}, f_const_empty_cc_algo = { + .type = T_STRING, + .val.ad = &null_adata, }; static struct adata * diff --git a/filter/data.h b/filter/data.h index 4cb6b7a8..46216e92 100644 --- a/filter/data.h +++ b/filter/data.h @@ -262,6 +262,7 @@ trie_match_next_longest_ip6(net_addr_ip6 *n, ip6_addr *found) #define F_CMP_ERROR 999 +#define TCP_CA_NAME_MAX 16 const char *f_type_name(enum f_type t); @@ -297,7 +298,7 @@ undef_value(struct f_val v) (v.val.ad == &null_adata); } -extern const struct f_val f_const_empty_path, f_const_empty_clist, f_const_empty_eclist, f_const_empty_lclist; +extern const struct f_val f_const_empty_path, f_const_empty_clist, f_const_empty_eclist, f_const_empty_lclist, f_const_empty_cc_algo; enum filter_return f_eval(const struct f_line *expr, struct linpool *tmp_pool, struct f_val *pres); diff --git a/filter/f-inst.c b/filter/f-inst.c index 901d2939..735c643a 100644 --- a/filter/f-inst.c +++ b/filter/f-inst.c @@ -709,6 +709,9 @@ case EAF_TYPE_LC_SET: RESULT_(T_LCLIST, ad, e->u.ptr); break; + case EAF_TYPE_CC_ALGO: + RESULT_(T_STRING, s, (const char *) e->u.ptr->data); + break; case EAF_TYPE_UNDEF: RESULT_VOID; break; @@ -758,7 +761,16 @@ case EAF_TYPE_LC_SET: l->attrs[0].u.ptr = v1.val.ad; break; - + case EAF_TYPE_CC_ALGO: + if (v1.type != T_STRING) + runtime( "Setting cc_algo attribute to non-string value" ); + else if (strlen(v1.val.s) >= TCP_CA_NAME_MAX) + runtime( "Setting cc_algo attribute out of bounds (> 15 chars)" ); + /* Store cc_algo string in byte[], making sure to copy the null terminator */ + struct adata *d = lp_alloc_adata(fs->pool, strlen(v1.val.s) + 1); + memcpy(d->data, v1.val.s, d->length); + l->attrs[0].u.ptr = d; + break; case EAF_TYPE_BITFIELD: { /* First, we have to find the old value */ diff --git a/filter/f-util.c b/filter/f-util.c index 410999a6..3cf68270 100644 --- a/filter/f-util.c +++ b/filter/f-util.c @@ -121,6 +121,8 @@ ca_lookup(pool *p, const char *name, int f_type) break; case T_LCLIST: ea_type = EAF_TYPE_LC_SET; + case T_STRING: + ea_type = EAF_TYPE_CC_ALGO; break; default: cf_error("Custom route attribute of unsupported type"); diff --git a/nest/route.h b/nest/route.h index 7930058a..053d2f1e 100644 --- a/nest/route.h +++ b/nest/route.h @@ -587,6 +587,7 @@ const char *ea_custom_name(uint ea); #define EAF_TYPE_INT_SET 0x0a /* Set of u32's (e.g., a community list) */ #define EAF_TYPE_EC_SET 0x0e /* Set of pairs of u32's - ext. community list */ #define EAF_TYPE_LC_SET 0x12 /* Set of triplets of u32's - large community list */ +#define EAF_TYPE_CC_ALGO 0x18 /* String to specify congestion control algorithm */ #define EAF_TYPE_UNDEF 0x1f /* `force undefined' entry */ #define EAF_EMBEDDED 0x01 /* Data stored in eattr.u.data (part of type spec) */ #define EAF_VAR_LENGTH 0x02 /* Attribute length is variable (part of type spec) */ diff --git a/nest/rt-attr.c b/nest/rt-attr.c index c630aa95..b4fdfd85 100644 --- a/nest/rt-attr.c +++ b/nest/rt-attr.c @@ -969,6 +969,9 @@ ea_show(struct cli *c, const eattr *e) case EAF_TYPE_LC_SET: ea_show_lc_set(c, ad, pos, buf, end); return; + case EAF_TYPE_CC_ALGO: + bsnprintf(pos, ad->length, "%s", (char *)ad->data); + return; case EAF_TYPE_UNDEF: default: bsprintf(pos, "", e->type); diff --git a/sysdep/linux/krt-sys.h b/sysdep/linux/krt-sys.h index 8897f889..f265ceb7 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,7 +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_CC_ALGO EA_CODE(PROTOCOL_KERNEL, 0x30) struct krt_params { u32 table_id; /* Kernel table ID we sync with */ diff --git a/sysdep/linux/netlink.Y b/sysdep/linux/netlink.Y index 487ad1d8..b07fa842 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_CC_ALGO, 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_CC_ALGO { $$ = f_new_dynamic_attr(EAF_TYPE_CC_ALGO, T_STRING, EA_KRT_CC_ALGO); } ; /* Bits of EA_KRT_LOCK, based on RTAX_* constants */ diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index 29b744cb..4d4d704b 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -73,6 +73,10 @@ #define NETLINK_GET_STRICT_CHK 12 #endif +#ifndef TCP_CA_NAME_MAX +#define TCP_CA_NAME_MAX 16 +#endif + #define krt_ipv4(p) ((p)->af == AF_INET) #define krt_ecmp6(p) ((p)->af == AF_INET6) @@ -534,6 +538,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 char *rta_get_str(struct rtattr *a) +{ return (char *) RTA_DATA(a); } + static inline ip4_addr rta_get_ip4(struct rtattr *a) { return ip4_ntoh(*(ip4_addr *) RTA_DATA(a)); } @@ -624,6 +631,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, 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) { @@ -880,20 +893,25 @@ err: } static void -nl_add_metrics(struct nlmsghdr *h, uint bufsize, u32 *metrics, int max) +nl_add_metrics(struct nlmsghdr *h, uint bufsize, u32 *metrics, 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]); + for (t = 1; t < max; t++) { + if (metrics[0] & (1 << t)) { + if (EA_CODE(PROTOCOL_KERNEL, KRT_METRICS_OFFSET + t) == EA_KRT_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, char *cc_algo, int max) { struct rtattr *a = RTA_DATA(hdr); int len = RTA_PAYLOAD(hdr); @@ -911,7 +929,19 @@ nl_parse_metrics(struct rtattr *hdr, u32 *metrics, int max) return -1; metrics[0] |= 1 << a->rta_type; - metrics[a->rta_type] = rta_get_u32(a); + + if (EA_CODE(PROTOCOL_KERNEL, KRT_METRICS_OFFSET + a->rta_type) == EA_KRT_CC_ALGO) { + char *str = rta_get_str(a); + if (strlen(str) < TCP_CA_NAME_MAX) { + memcpy(cc_algo, str, strlen(str) + 1); + } else { + log(L_ERR "KRT: Received route with cc_algo attribute out of bounds (> 15 chars)"); + return -1; + } + metrics[a->rta_type] = 0; + } else { + metrics[a->rta_type] = rta_get_u32(a); + } } if (len > 0) @@ -1427,6 +1457,7 @@ nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh) u32 metrics[KRT_METRICS_MAX]; + char cc_algo[TCP_CA_NAME_MAX]; metrics[0] = 0; struct ea_walk_state ews = { .eattrs = eattrs }; @@ -1434,11 +1465,16 @@ nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh) { int id = ea->id - EA_KRT_METRICS; metrics[0] |= 1 << id; - metrics[id] = ea->u.data; + if(ea->id == EA_KRT_CC_ALGO) { + metrics[id] = 0; + memcpy(cc_algo, ea->u.ptr->data, ea->u.ptr->length); + } 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); dest: @@ -1907,10 +1943,12 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h) if (a[RTA_METRICS]) { u32 metrics[KRT_METRICS_MAX]; + char *cc_algo = lp_alloc(s->pool, TCP_CA_NAME_MAX); ea_list *ea = lp_alloc(s->pool, sizeof(ea_list) + KRT_METRICS_MAX * sizeof(eattr)); + struct adata *d = lp_alloc(s->pool, sizeof(struct adata) + TCP_CA_NAME_MAX); 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; @@ -1921,8 +1959,15 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h) { ea->attrs[n].id = EA_CODE(PROTOCOL_KERNEL, KRT_METRICS_OFFSET + t); ea->attrs[n].flags = 0; - ea->attrs[n].type = EAF_TYPE_INT; /* FIXME: Some are EAF_TYPE_BITFIELD */ - ea->attrs[n].u.data = metrics[t]; + if (ea->attrs[n].id == EA_KRT_CC_ALGO) { + ea->attrs[n].type = EAF_TYPE_CC_ALGO; + d->length = strlen(cc_algo) + 1; + memcpy(d->data, cc_algo, d->length); + ea->attrs[n].u.ptr = d; + } else { + ea->attrs[n].type = EAF_TYPE_INT; /* FIXME: Some are EAF_TYPE_BITFIELD */ + ea->attrs[n].u.data = metrics[t]; + } n++; } @@ -2225,7 +2270,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", + "cc_algo" }; static const char *krt_features_names[KRT_FEATURES_MAX] = {