From 9ddbfbddf87462bbf50437bdc1d44499a5c223e7 Mon Sep 17 00:00:00 2001 From: Jan Moskyto Matejka Date: Tue, 3 Nov 2015 14:42:41 +0100 Subject: Netlink: Allow more than 256 routing tables. Since 2.6.19, the netlink API defines RTA_TABLE routing attribute to allow 32-bit routing table IDs. Using this attribute to index routing tables at Linux, instead of 8-bit rtm_table field. --- sysdep/linux/krt-sys.h | 8 ++--- sysdep/linux/netlink.Y | 2 -- sysdep/linux/netlink.c | 90 ++++++++++++++++++++++++++++++++++---------------- 3 files changed, 65 insertions(+), 35 deletions(-) (limited to 'sysdep/linux') diff --git a/sysdep/linux/krt-sys.h b/sysdep/linux/krt-sys.h index e32e4fe1..7fd5f139 100644 --- a/sysdep/linux/krt-sys.h +++ b/sysdep/linux/krt-sys.h @@ -84,18 +84,18 @@ static inline struct ifa * kif_get_primary_ip(struct iface *i) { return NULL; } #define EA_KRT_FEATURE_ALLFRAG EA_KRT_FEATURES | EA_BIT(0x3) - -#define NL_NUM_TABLES 256 - struct krt_params { - int table_id; /* Kernel table ID we sync with */ + u32 table_id; /* Kernel table ID we sync with */ }; struct krt_state { + struct krt_proto *hash_next; }; static inline void krt_sys_init(struct krt_proto *p UNUSED) { } +static inline void krt_sys_preconfig(struct config *c UNUSED) { } +static inline void krt_sys_postconfig(struct krt_config *x UNUSED) { } #endif diff --git a/sysdep/linux/netlink.Y b/sysdep/linux/netlink.Y index f8137e23..e9c225a2 100644 --- a/sysdep/linux/netlink.Y +++ b/sysdep/linux/netlink.Y @@ -23,8 +23,6 @@ CF_ADDTO(kern_proto, kern_proto kern_sys_item ';') kern_sys_item: KERNEL TABLE expr { - if ($3 <= 0 || $3 >= NL_NUM_TABLES) - cf_error("Kernel routing table number out of range"); THIS_KRT->sys.table_id = $3; } ; diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index 674d338b..db998926 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -25,6 +25,7 @@ #include "lib/krt.h" #include "lib/socket.h" #include "lib/string.h" +#include "lib/hash.h" #include "conf/conf.h" #include @@ -32,6 +33,7 @@ #include #include + #ifndef MSG_TRUNC /* Hack: Several versions of glibc miss this one :( */ #define MSG_TRUNC 0x20 #endif @@ -40,6 +42,11 @@ #define IFF_LOWER_UP 0x10000 #endif +#ifndef RTA_TABLE +#define RTA_TABLE 15 +#endif + + /* * Synchronous Netlink interface */ @@ -650,7 +657,23 @@ kif_do_scan(struct kif_proto *p UNUSED) * Routes */ -static struct krt_proto *nl_table_map[NL_NUM_TABLES]; +static inline u32 +krt_table_id(struct krt_proto *p) +{ + return KRT_CF->sys.table_id; +} + +static HASH(struct krt_proto) nl_table_map; + +#define RTH_FN(k) u32_hash(k) +#define RTH_EQ(k1,k2) k1 == k2 +#define RTH_KEY(p) krt_table_id(p) +#define RTH_NEXT(p) p->sys.hash_next + +#define RTH_REHASH rth_rehash +#define RTH_PARAMS /8, *2, 2, 2, 6, 20 + +HASH_DEFINE_REHASH_FN(RTH, struct krt_proto) int krt_capable(rte *e) @@ -708,12 +731,15 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new) r.r.rtm_family = BIRD_AF; r.r.rtm_dst_len = net->n.pxlen; - r.r.rtm_tos = 0; - r.r.rtm_table = KRT_CF->sys.table_id; r.r.rtm_protocol = RTPROT_BIRD; r.r.rtm_scope = RT_SCOPE_UNIVERSE; nl_add_attr_ipa(&r.h, sizeof(r), RTA_DST, net->n.prefix); + if (krt_table_id(p) < 256) + r.r.rtm_table = krt_table_id(p); + else + nl_add_attr_u32(&r.h, sizeof(r), RTA_TABLE, krt_table_id(p)); + /* For route delete, we do not specify route attributes */ if (!new) return nl_exchange(&r.h); @@ -809,11 +835,12 @@ nl_parse_route(struct nlmsghdr *h, int scan) { struct krt_proto *p; struct rtmsg *i; - struct rtattr *a[RTA_CACHEINFO+1]; + struct rtattr *a[RTA_TABLE+1]; int new = h->nlmsg_type == RTM_NEWROUTE; ip_addr dst = IPA_NONE; u32 oif = ~0; + u32 table; int src; if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(RTM_RTA(i), a, sizeof(a))) @@ -825,6 +852,7 @@ nl_parse_route(struct nlmsghdr *h, int scan) (a[RTA_IIF] && RTA_PAYLOAD(a[RTA_IIF]) != 4) || #endif (a[RTA_OIF] && RTA_PAYLOAD(a[RTA_OIF]) != 4) || + (a[RTA_TABLE] && RTA_PAYLOAD(a[RTA_TABLE]) != 4) || (a[RTA_GATEWAY] && RTA_PAYLOAD(a[RTA_GATEWAY]) != sizeof(ip_addr)) || (a[RTA_PRIORITY] && RTA_PAYLOAD(a[RTA_PRIORITY]) != 4) || (a[RTA_PREFSRC] && RTA_PAYLOAD(a[RTA_PREFSRC]) != sizeof(ip_addr)) || @@ -843,10 +871,15 @@ nl_parse_route(struct nlmsghdr *h, int scan) if (a[RTA_OIF]) oif = rta_get_u32(a[RTA_OIF]); - p = nl_table_map[i->rtm_table]; /* Do we know this table? */ - DBG("KRT: Got %I/%d, type=%d, oif=%d, table=%d, prid=%d, proto=%s\n", dst, i->rtm_dst_len, i->rtm_type, oif, i->rtm_table, i->rtm_protocol, p ? p->p.name : "(none)"); + if (a[RTA_TABLE]) + table = rta_get_u32(a[RTA_TABLE]); + else + table = i->rtm_table; + + p = HASH_FIND(nl_table_map, RTH, table); /* Do we know this table? */ + DBG("KRT: Got %I/%d, type=%d, oif=%d, table=%d, prid=%d, proto=%s\n", dst, i->rtm_dst_len, i->rtm_type, oif, table, i->rtm_protocol, p ? p->p.name : "(none)"); if (!p) - SKIP("unknown table %d\n", i->rtm_table); + SKIP("unknown table %d\n", table); #ifdef IPV6 @@ -1186,25 +1219,41 @@ nl_open_async(void) bug("Netlink: sk_open failed"); } + /* * Interface to the UNIX krt module */ -static u8 nl_cf_table[(NL_NUM_TABLES+7) / 8]; - void +krt_sys_io_init(void) +{ + HASH_INIT(nl_table_map, krt_pool, 6); +} + +int krt_sys_start(struct krt_proto *p) { - nl_table_map[KRT_CF->sys.table_id] = p; + struct krt_proto *old = HASH_FIND(nl_table_map, RTH, krt_table_id(p)); + + if (old) + { + log(L_ERR "%s: Kernel table %u already registered by %s", + p->p.name, krt_table_id(p), old->p.name); + return 0; + } + + HASH_INSERT2(nl_table_map, RTH, krt_pool, p); nl_open(); nl_open_async(); + + return 1; } void -krt_sys_shutdown(struct krt_proto *p UNUSED) +krt_sys_shutdown(struct krt_proto *p) { - nl_table_map[KRT_CF->sys.table_id] = NULL; + HASH_REMOVE2(nl_table_map, RTH, krt_pool, p); } int @@ -1213,23 +1262,6 @@ krt_sys_reconfigure(struct krt_proto *p UNUSED, struct krt_config *n, struct krt return n->sys.table_id == o->sys.table_id; } - -void -krt_sys_preconfig(struct config *c UNUSED) -{ - bzero(&nl_cf_table, sizeof(nl_cf_table)); -} - -void -krt_sys_postconfig(struct krt_config *x) -{ - int id = x->sys.table_id; - - if (nl_cf_table[id/8] & (1 << (id%8))) - cf_error("Multiple kernel syncers defined for table #%d", id); - nl_cf_table[id/8] |= (1 << (id%8)); -} - void krt_sys_init_config(struct krt_config *cf) { -- cgit v1.2.3 From fce764f90e8331d1adb6a85ec00136dfeae1a398 Mon Sep 17 00:00:00 2001 From: Pavel TvrdĂ­k Date: Mon, 9 Nov 2015 09:14:26 +0100 Subject: Fix compiling with --enable-debug option --- sysdep/linux/netlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sysdep/linux') diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index db998926..f2f60100 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -246,7 +246,7 @@ nl_parse_attrs(struct rtattr *a, struct rtattr **k, int ksize) return 1; } -static inline ip4_addr rta_get_u32(struct rtattr *a) +static inline u32 rta_get_u32(struct rtattr *a) { return *(u32 *) RTA_DATA(a); } static inline ip4_addr rta_get_ip4(struct rtattr *a) -- cgit v1.2.3 From 1e4891e48e7b6f022564e7409d15c3fdb65ec2ad Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Mon, 23 Nov 2015 11:13:40 +0100 Subject: Nest: Fix bug in device proto If an interface address notification is received during device protocol shutdown/restart, BIRD crashed. Thanks to Wei Huang for the bugreport. --- sysdep/linux/netlink.c | 6 ++++-- sysdep/unix/krt.c | 2 +- sysdep/unix/krt.h | 2 ++ 3 files changed, 7 insertions(+), 3 deletions(-) (limited to 'sysdep/linux') diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index f2f60100..efbf41a6 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -1116,12 +1116,14 @@ nl_async_msg(struct nlmsghdr *h) case RTM_NEWLINK: case RTM_DELLINK: DBG("KRT: Received async link notification (%d)\n", h->nlmsg_type); - nl_parse_link(h, 0); + if (kif_proto) + nl_parse_link(h, 0); break; case RTM_NEWADDR: case RTM_DELADDR: DBG("KRT: Received async address notification (%d)\n", h->nlmsg_type); - nl_parse_addr(h, 0); + if (kif_proto) + nl_parse_addr(h, 0); break; default: DBG("KRT: Received unknown async notification (%d)\n", h->nlmsg_type); diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index 49bf9519..5e78586b 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -84,8 +84,8 @@ krt_io_init(void) * Interfaces */ +struct kif_proto *kif_proto; static struct kif_config *kif_cf; -static struct kif_proto *kif_proto; static timer *kif_scan_timer; static bird_clock_t kif_last_shot; diff --git a/sysdep/unix/krt.h b/sysdep/unix/krt.h index aea20102..d4a8717e 100644 --- a/sysdep/unix/krt.h +++ b/sysdep/unix/krt.h @@ -112,6 +112,8 @@ struct kif_proto { struct kif_state sys; /* Sysdep state */ }; +struct kif_proto *kif_proto; + #define KIF_CF ((struct kif_config *)p->p.cf) struct proto_config * krt_init_config(int class); -- cgit v1.2.3 From ad27615760e2795da3efe5e97c0e888281d5ca59 Mon Sep 17 00:00:00 2001 From: Jan Moskyto Matejka Date: Tue, 10 Nov 2015 14:59:41 +0100 Subject: Netlink: attribute validation before parsing Wanted netlink attributes are defined in a table, specifying their size and neediness. Removing the long conditions that did the validation before. Also parsing IPv4 and IPv6 versions regardless on the IPV6 macro. --- sysdep/linux/netlink.c | 187 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 141 insertions(+), 46 deletions(-) (limited to 'sysdep/linux') diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index efbf41a6..640d1877 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -226,24 +226,98 @@ nl_checkin(struct nlmsghdr *h, int lsize) return NLMSG_DATA(h); } +struct nl_want_attrs { + u8 defined:1; + u8 checksize:1; + u8 size; +}; + + +#define BIRD_IFLA_MAX (IFLA_WIRELESS+1) + +static struct nl_want_attrs ifla_attr_want[BIRD_IFLA_MAX] = { + [IFLA_IFNAME] = { 1, 0, 0 }, + [IFLA_MTU] = { 1, 1, sizeof(u32) }, + [IFLA_WIRELESS] = { 1, 0, 0 }, +}; + + +#define BIRD_IFA_MAX (IFA_ANYCAST+1) + +#ifndef IPV6 +static struct nl_want_attrs ifa_attr_want4[BIRD_IFA_MAX] = { + [IFA_ADDRESS] = { 1, 1, sizeof(ip4_addr) }, + [IFA_LOCAL] = { 1, 1, sizeof(ip4_addr) }, + [IFA_BROADCAST] = { 1, 1, sizeof(ip4_addr) }, +}; +#else +static struct nl_want_attrs ifa_attr_want6[BIRD_IFA_MAX] = { + [IFA_ADDRESS] = { 1, 1, sizeof(ip6_addr) }, + [IFA_LOCAL] = { 1, 1, sizeof(ip6_addr) }, +}; +#endif + + +#define BIRD_RTA_MAX (RTA_TABLE+1) + +static struct nl_want_attrs mpnh_attr_want4[BIRD_RTA_MAX] = { + [RTA_GATEWAY] = { 1, 1, sizeof(ip4_addr) }, +}; + +#ifndef IPV6 +static struct nl_want_attrs rtm_attr_want4[BIRD_RTA_MAX] = { + [RTA_DST] = { 1, 1, sizeof(ip4_addr) }, + [RTA_OIF] = { 1, 1, sizeof(u32) }, + [RTA_GATEWAY] = { 1, 1, sizeof(ip4_addr) }, + [RTA_PRIORITY] = { 1, 1, sizeof(u32) }, + [RTA_PREFSRC] = { 1, 1, sizeof(ip4_addr) }, + [RTA_METRICS] = { 1, 0, 0 }, + [RTA_MULTIPATH] = { 1, 0, 0 }, + [RTA_FLOW] = { 1, 1, sizeof(u32) }, + [RTA_TABLE] = { 1, 1, sizeof(u32) }, +}; +#else +static struct nl_want_attrs rtm_attr_want6[BIRD_RTA_MAX] = { + [RTA_DST] = { 1, 1, sizeof(ip6_addr) }, + [RTA_IIF] = { 1, 1, sizeof(u32) }, + [RTA_OIF] = { 1, 1, sizeof(u32) }, + [RTA_GATEWAY] = { 1, 1, sizeof(ip6_addr) }, + [RTA_PRIORITY] = { 1, 1, sizeof(u32) }, + [RTA_PREFSRC] = { 1, 1, sizeof(ip6_addr) }, + [RTA_METRICS] = { 1, 0, 0 }, + [RTA_FLOW] = { 1, 1, sizeof(u32) }, + [RTA_TABLE] = { 1, 1, sizeof(u32) }, +}; +#endif + + static int -nl_parse_attrs(struct rtattr *a, struct rtattr **k, int ksize) +nl_parse_attrs(struct rtattr *a, struct nl_want_attrs *want, struct rtattr **k, int ksize) { int max = ksize / sizeof(struct rtattr *); bzero(k, ksize); - while (RTA_OK(a, nl_attr_len)) + + for ( ; RTA_OK(a, nl_attr_len); a = RTA_NEXT(a, nl_attr_len)) { - if (a->rta_type < max) - k[a->rta_type] = a; - a = RTA_NEXT(a, nl_attr_len); + if ((a->rta_type >= max) || !want[a->rta_type].defined) + continue; + + if (want[a->rta_type].checksize && (RTA_PAYLOAD(a) != want[a->rta_type].size)) + { + log(L_ERR "nl_parse_attrs: Malformed message received"); + return 0; + } + + k[a->rta_type] = a; } + if (nl_attr_len) { log(L_ERR "nl_parse_attrs: remnant of size %d", nl_attr_len); return 0; } - else - return 1; + + return 1; } static inline u32 rta_get_u32(struct rtattr *a) @@ -350,7 +424,7 @@ nl_parse_multipath(struct krt_proto *p, struct rtattr *ra) static int nh_buf_size; /* in number of structures */ static int nh_buf_used; - struct rtattr *a[RTA_CACHEINFO+1]; + struct rtattr *a[BIRD_RTA_MAX]; struct rtnexthop *nh = RTA_DATA(ra); struct mpnh *rv, *first, **last; int len = RTA_PAYLOAD(ra); @@ -381,12 +455,9 @@ nl_parse_multipath(struct krt_proto *p, struct rtattr *ra) /* Nonexistent RTNH_PAYLOAD ?? */ nl_attr_len = nh->rtnh_len - RTNH_LENGTH(0); - nl_parse_attrs(RTNH_DATA(nh), a, sizeof(a)); + nl_parse_attrs(RTNH_DATA(nh), mpnh_attr_want4, a, sizeof(a)); if (a[RTA_GATEWAY]) { - if (RTA_PAYLOAD(a[RTA_GATEWAY]) != sizeof(ip_addr)) - return NULL; - memcpy(&rv->gw, RTA_DATA(a[RTA_GATEWAY]), sizeof(ip_addr)); ipa_ntoh(rv->gw); @@ -455,7 +526,7 @@ static void nl_parse_link(struct nlmsghdr *h, int scan) { struct ifinfomsg *i; - struct rtattr *a[IFLA_WIRELESS+1]; + struct rtattr *a[BIRD_IFLA_MAX]; int new = h->nlmsg_type == RTM_NEWLINK; struct iface f = {}; struct iface *ifi; @@ -463,15 +534,23 @@ nl_parse_link(struct nlmsghdr *h, int scan) u32 mtu; uint fl; - if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(IFLA_RTA(i), a, sizeof(a))) + if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(IFLA_RTA(i), ifla_attr_want, a, sizeof(a))) return; - if (!a[IFLA_IFNAME] || RTA_PAYLOAD(a[IFLA_IFNAME]) < 2 || - !a[IFLA_MTU] || RTA_PAYLOAD(a[IFLA_MTU]) != 4) + if (!a[IFLA_IFNAME] || (RTA_PAYLOAD(a[IFLA_IFNAME]) < 2) || !a[IFLA_MTU]) { - if (scan || !a[IFLA_WIRELESS]) - log(L_ERR "nl_parse_link: Malformed message received"); + /* + * IFLA_IFNAME and IFLA_MTU are required, in fact, but there may also come + * a message with IFLA_WIRELESS set, where (e.g.) no IFLA_IFNAME exists. + * We simply ignore all such messages with IFLA_WIRELESS without notice. + */ + + if (a[IFLA_WIRELESS]) + return; + + log(L_ERR "KIF: Malformed message received"); return; } + name = RTA_DATA(a[IFLA_IFNAME]); mtu = rta_get_u32(a[IFLA_MTU]); @@ -522,26 +601,40 @@ static void nl_parse_addr(struct nlmsghdr *h, int scan) { struct ifaddrmsg *i; - struct rtattr *a[IFA_ANYCAST+1]; + struct rtattr *a[BIRD_IFA_MAX]; int new = h->nlmsg_type == RTM_NEWADDR; struct ifa ifa; struct iface *ifi; int scope; - if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(IFA_RTA(i), a, sizeof(a))) + if (!(i = nl_checkin(h, sizeof(*i)))) return; - if (i->ifa_family != BIRD_AF) - return; - if (!a[IFA_ADDRESS] || RTA_PAYLOAD(a[IFA_ADDRESS]) != sizeof(ip_addr) -#ifdef IPV6 - || a[IFA_LOCAL] && RTA_PAYLOAD(a[IFA_LOCAL]) != sizeof(ip_addr) + + switch (i->ifa_family) + { +#ifndef IPV6 + case AF_INET: + if (!nl_parse_attrs(IFA_RTA(i), ifa_attr_want4, a, sizeof(a))) + return; + if (!a[IFA_LOCAL]) + { + log(L_ERR "KIF: Malformed message received (missing IFA_LOCAL)"); + return; + } + break; #else - || !a[IFA_LOCAL] || RTA_PAYLOAD(a[IFA_LOCAL]) != sizeof(ip_addr) - || (a[IFA_BROADCAST] && RTA_PAYLOAD(a[IFA_BROADCAST]) != sizeof(ip_addr)) + case AF_INET6: + if (!nl_parse_attrs(IFA_RTA(i), ifa_attr_want6, a, sizeof(a))) + return; + break; #endif - ) + default: + return; + } + + if (!a[IFA_ADDRESS]) { - log(L_ERR "nl_parse_addr: Malformed message received"); + log(L_ERR "KIF: Malformed message received (missing IFA_ADDRESS)"); return; } @@ -835,7 +928,7 @@ nl_parse_route(struct nlmsghdr *h, int scan) { struct krt_proto *p; struct rtmsg *i; - struct rtattr *a[RTA_TABLE+1]; + struct rtattr *a[BIRD_RTA_MAX]; int new = h->nlmsg_type == RTM_NEWROUTE; ip_addr dst = IPA_NONE; @@ -843,25 +936,27 @@ nl_parse_route(struct nlmsghdr *h, int scan) u32 table; int src; - if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(RTM_RTA(i), a, sizeof(a))) + if (!(i = nl_checkin(h, sizeof(*i)))) return; - if (i->rtm_family != BIRD_AF) - return; - if ((a[RTA_DST] && RTA_PAYLOAD(a[RTA_DST]) != sizeof(ip_addr)) || -#ifdef IPV6 - (a[RTA_IIF] && RTA_PAYLOAD(a[RTA_IIF]) != 4) || -#endif - (a[RTA_OIF] && RTA_PAYLOAD(a[RTA_OIF]) != 4) || - (a[RTA_TABLE] && RTA_PAYLOAD(a[RTA_TABLE]) != 4) || - (a[RTA_GATEWAY] && RTA_PAYLOAD(a[RTA_GATEWAY]) != sizeof(ip_addr)) || - (a[RTA_PRIORITY] && RTA_PAYLOAD(a[RTA_PRIORITY]) != 4) || - (a[RTA_PREFSRC] && RTA_PAYLOAD(a[RTA_PREFSRC]) != sizeof(ip_addr)) || - (a[RTA_FLOW] && RTA_PAYLOAD(a[RTA_FLOW]) != 4)) + + switch (i->rtm_family) { - log(L_ERR "KRT: Malformed message received"); - return; +#ifndef IPV6 + case AF_INET: + if (!nl_parse_attrs(RTM_RTA(i), rtm_attr_want4, a, sizeof(a))) + return; + break; +#else + case AF_INET6: + if (!nl_parse_attrs(RTM_RTA(i), rtm_attr_want6, a, sizeof(a))) + return; + break; +#endif + default: + return; } + if (a[RTA_DST]) { memcpy(&dst, RTA_DATA(a[RTA_DST]), sizeof(dst)); @@ -938,7 +1033,7 @@ nl_parse_route(struct nlmsghdr *h, int scan) { case RTN_UNICAST: - if (a[RTA_MULTIPATH]) + if (a[RTA_MULTIPATH] && (i->rtm_family == AF_INET)) { ra.dest = RTD_MULTIPATH; ra.nexthops = nl_parse_multipath(p, a[RTA_MULTIPATH]); -- cgit v1.2.3