From b5e76398de1d4468b4061d9ef57dd3154b2f745e Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Wed, 19 Aug 2015 11:16:23 +0200 Subject: [PATCH 01/18] OSPF: Fixes some issues with link detection Thanks to Bernardo Figueiredo and Israel G. Lugo for the bugreport. --- proto/ospf/iface.c | 7 +++++-- proto/ospf/packet.c | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index 9b0f7797..77ce839a 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -493,8 +493,11 @@ ospf_iface_add(struct object_lock *lock) ifa->flood_queue = mb_allocz(ifa->pool, ifa->flood_queue_size * sizeof(void *)); } - /* Do iface UP, unless there is no link and we use link detection */ - ospf_iface_sm(ifa, (ifa->check_link && !(ifa->iface->flags & IF_LINK_UP)) ? ISM_LOOP : ISM_UP); + /* Do iface UP, unless there is no link (then wait in LOOP state) */ + if (!ifa->check_link || (ifa->iface->flags & IF_LINK_UP)) + ospf_iface_sm(ifa, ISM_UP); + else + ospf_iface_chstate(ifa, OSPF_IS_LOOP); } static inline void diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c index fb63e61c..6b8fd7b5 100644 --- a/proto/ospf/packet.c +++ b/proto/ospf/packet.c @@ -231,6 +231,10 @@ ospf_rx_hook(sock *sk, int len) const char *err_dsc = NULL; uint err_val = 0; + /* Should not happen */ + if (ifa->state <= OSPF_IS_LOOP) + return 1; + int src_local, dst_local, dst_mcast; src_local = ipa_in_net(sk->faddr, ifa->addr->prefix, ifa->addr->pxlen); dst_local = ipa_equal(sk->laddr, ifa->addr->ip); From 8465dccb06afffed171dc1e224e4eb5f67cc3326 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Mon, 5 Oct 2015 12:14:50 +0200 Subject: [PATCH 02/18] Major RIP redesign The new RIP implementation fixes plenty of old bugs and also adds support for many new features: ECMP support, link state support, BFD support, configurable split horizon and more. Most options are now per-interface. --- client/commands.c | 2 +- doc/bird.sgml | 244 ++++-- doc/reply_codes | 2 + lib/ip.c | 4 +- lib/ip.h | 7 + nest/password.c | 15 + nest/password.h | 3 +- nest/route.h | 12 +- nest/rt-fib.c | 19 + nest/rt-table.c | 6 - proto/ospf/packet.c | 2 +- proto/rip/Doc | 2 +- proto/rip/Makefile | 2 +- proto/rip/auth.c | 168 ---- proto/rip/config.Y | 205 +++-- proto/rip/packets.c | 772 +++++++++++++++++ proto/rip/rip.c | 1953 ++++++++++++++++++++++++------------------- proto/rip/rip.h | 360 ++++---- 18 files changed, 2428 insertions(+), 1350 deletions(-) delete mode 100644 proto/rip/auth.c create mode 100644 proto/rip/packets.c diff --git a/client/commands.c b/client/commands.c index 50fcba40..226ae048 100644 --- a/client/commands.c +++ b/client/commands.c @@ -67,7 +67,7 @@ cmd_build_tree(void) new->plastson = &new->son; new->len = c-d; memcpy(new->token, d, c-d); - new->prio = (new->len == 3 && !memcmp(new->token, "roa", 3)) ? 0 : 1; /* Hack */ + new->prio = (new->len == 3 && (!memcmp(new->token, "roa", 3) || !memcmp(new->token, "rip", 3))) ? 0 : 1; /* Hack */ } old = new; while (isspace(*c)) diff --git a/doc/bird.sgml b/doc/bird.sgml index df83aacd..86df0456 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -2476,7 +2476,7 @@ protocol ospf <name> { This option specifies whether OSPF is allowed to generate ECMP (equal-cost multipath) routes. Such routes are used when there are several directions to the destination, each with the same (computed) - cost. This option also allows to specify a limit on maximal number of + cost. This option also allows to specify a limit on maximum number of nexthops in one route. By default, ECMP is disabled. If enabled, default value of the limit is 16. @@ -3244,16 +3244,14 @@ one). After some time, the distance reaches infinity (that's 15 in RIP) and all routers know that network is unreachable. RIP tries to minimize situations where counting to infinity is necessary, because it is slow. Due to infinity being 16, you can't use RIP on networks where maximal distance is higher than 15 -hosts. You can read more about RIP at -. Both IPv4 -(RFC 1723 ) and IPv6 -(RFC 2080 ) versions -of RIP are supported by BIRD, historical RIPv1 -(RFC 1058 ) is not -currently supported. RIPv4 MD5 authentication -(RFC 2082 ) is -supported. +hosts. + +

BIRD supports RIPv1 +(RFC 1058), +RIPv2 (RFC 2453), +RIPng (RFC 2080), +and RIP cryptographic authentication (SHA-1 not implemented) +(RFC 4822).

RIP is a very simple protocol, and it has a lot of shortcomings. Slow convergence, big network load and inability to handle larger networks makes it @@ -3261,39 +3259,156 @@ pretty much obsolete. It is still usable on very small networks. Configuration -

In addition to options common for all to other protocols, RIP supports the -following ones: +

RIP configuration consists mainly of common protocol options and interface +definitions, most RIP options are interface specific. + + +protocol rip [<name>] { + infinity <number>; + ecmp <switch> [limit <number>]; + interface <interface pattern> { + metric <number>; + mode multicast|broadcast; + passive <switch>; + address <ip>; + port <number>; + version 1|2; + split horizon <switch>; + poison reverse <switch>; + check zero <switch>; + update time <number>; + timeout time <number>; + garbage time <number>; + ecmp weight <number>; + ttl security <switch>; | tx only; + tx class|dscp <number>; + tx priority <number>; + rx buffer <number>; + tx length <number>; + check link <switch>; + authentication none|plaintext|cryptographic; + password "<text>"; + password "<text>" { + id <num>; + generate from "<date>"; + generate to "<date>"; + accept from "<date>"; + accept to "<date>"; + }; + }; +} + - authentication none|plaintext|md5 - Selects authentication method to be used. password - section. Default: none. + infinity number + Selects the distance of infinity. Bigger values will make + protocol convergence even slower. The default value is 16. - honor always|neighbor|never - Specifies when should requests for dumping routing table be honored. - (Always, when sent from a host on a directly connected network or - never.) Routing table updates are honored only from neighbors, that is - not configurable. Default: never. + ecmp switch [limit number] + This option specifies whether RIP is allowed to generate ECMP + (equal-cost multipath) routes. Such routes are used when there are + several directions to the destination, each with the same (computed) + cost. This option also allows to specify a limit on maximum number of + nexthops in one route. By default, ECMP is disabled. If enabled, + default value of the limit is 16. + + interface + Interface definitions specify a set of interfaces on which the + protocol is activated and contain interface specific options. + See common options for + detailed description. -

There are some options that can be specified per-interface: +

Interface specific options: metric - This option specifies the metric of the interface. Valid + This option specifies the metric of the interface. When a route is + received from the interface, its metric is increased by this value + before further processing. Valid values are 1-255, but values higher + than infinity has no further meaning. Default: 1. - mode multicast|broadcast|quiet|nolisten|version1 - This option selects the mode for RIP to use on the interface. If nothing - is specified, RIP runs in multicast mode. mode multicast|broadcast + This option selects the mode for RIP to use on the interface. The + default is multicast mode for RIPv2 and broadcast mode for RIPv1. + RIPng always uses the multicast mode. + + passive + Passive interfaces receive routing updates but do not transmit any + messages. Default: no. + + address + This option specifies a destination address used for multicast or + broadcast messages, the default is the official RIP (224.0.0.9) or RIPng + (ff02::9) multicast address, or an appropriate broadcast address in the + broadcast mode. + + port + This option selects an UDP port to operate on, the default is the + official RIP (520) or RIPng (521) port. + + version 1|2 + This option selects the version of RIP used on the interface. For RIPv1, + automatic subnet aggregation is not implemented, only classful network + routes and host routes are propagated. Note that BIRD allows RIPv1 to be + configured with features that are defined for RIPv2 only, like + authentication or using multicast sockets. The default is RIPv2 for IPv4 + RIP, the option is not supported for RIPng, as no further versions are + defined. + + split horizon + Split horizon is a scheme for preventing routing loops. When split + horizon is active, routes are not regularly propagated back to the + interface from which they were received. They are either not propagated + back at all (plain split horizon) or propagated back with an infinity + metric (split horizon with poisoned reverse). Therefore, other routers + on the interface will not consider the router as a part of an + independent path to the destination of the route. Default: yes. + + poison reverse + When split horizon is active, this option specifies whether the poisoned + reverse variant (propagating routes back with an infinity metric) is + used. The poisoned reverse has some advantages in faster convergence, + but uses more network traffic. Default: yes. + + check zero + Received RIPv1 packets with non-zero values in reserved fields should + be discarded. This option specifies whether the check is performed or + such packets are just processed as usual. Default: yes. + + update time + Specifies the number of seconds between periodic updates. A lower number + will mean faster convergence but bigger network load. Default: 30. + + timeout time + Specifies the time interval (in seconds) between the last received route + announcement and the route expiration. After that, the network is + considered unreachable, but still is propagated with infinity distance. + Default: 180. + + garbage time + Specifies the time interval (in seconds) between the route expiration + and the removal of the unreachable network entry. The garbage interval, + when a route with infinity metric is propagated, is used for both + internal (after expiration) and external (after withdrawal) routes. + Default: 120. + + ecmp weight + When ECMP (multipath) routes are allowed, this value specifies a + relative weight used for nexthops going through the iface. Valid + values are 1-256. Default value is 1. + + authentication none|plaintext|cryptographic + Selects authentication method to be used. password + section. Default: none. + + password " + Specifies a password used for authentication. See common option for detailed description. ttl security [ TTL security is a feature that protects routing protocols from remote @@ -3309,43 +3424,31 @@ following ones: compatibility with neighbors regardless of whether they use ttl security. - Note that for RIPng, TTL security is a standard behavior (required by - RFC 2080), but BIRD uses tx class|dscp|priority + tx class|dscp|priority These options specify the ToS/DiffServ/Traffic class/Priority of the outgoing RIP packets. See common option for detailed description. - -

The following options generally override behavior specified in RFC. If you -use any of these options, BIRD will no longer be RFC-compliant, which means it -will not be able to talk to anything other than equally configured BIRD. I have -warned you. + rx buffer + This option specifies the size of buffers used for packet processing. + The buffer size should be bigger than maximal size of received packets. + The default value is 532 for IPv4 RIP and interface MTU value for RIPng. - - port number - Selects IP port to operate on, default 520. (This is useful when testing - BIRD, if you set this to an address >1024, you will not need to run - bird with UID==0). + tx length + This option specifies the maximum length of generated RIP packets. To + avoid IP fragmentation, it should not exceed the interface MTU value. + The default value is 532 for IPv4 RIP and interface MTU value for RIPng. - infinity number - Selects the value of infinity, default is 16. Bigger values will make - protocol convergence even slower. - - period number - Specifies the number of seconds between periodic updates. Default is 30 - seconds. A lower number will mean faster convergence but bigger network - load. Do not use values lower than 12. - - timeout time number - Specifies how old route has to be to be considered unreachable. - Default is 4*garbage time number - Specifies how old route has to be to be discarded. Default is - 10*check link + If set, the hardware link state (as reported by OS) is taken into + consideration. When the link disappears (e.g. an ethernet cable is + unplugged), neighbors are immediately considered unreachable and all + routes received from them are withdrawn. It is possible that some + hardware drivers or platforms do not implement this feature. Default: + no. Attributes @@ -3356,27 +3459,26 @@ warned you. int RIP metric of the route (ranging from 0 to int RIP route tag: a 16-bit number which can be used to carry additional information with the route (for example, an originating AS number in - case of external routes). When importing a non-RIP route, the tag - defaults to 0. + case of external routes). When a non-RIP route is exported to RIP, the + default tag is 0. Example

-protocol rip MyRIP_test { +protocol rip { debug all; port 1520; period 12; garbage time 60; interface "eth0" { metric 3; mode multicast; }; interface "eth*" { metric 2; mode broadcast; }; - honor neighbor; authentication none; import filter { print "importing"; accept; }; export filter { print "exporting"; accept; }; diff --git a/doc/reply_codes b/doc/reply_codes index cd5f2620..79a7eb92 100644 --- a/doc/reply_codes +++ b/doc/reply_codes @@ -55,6 +55,8 @@ Reply codes of BIRD command-line interface 1018 Show memory 1019 Show ROA list 1020 Show BFD sessions +1021 Show RIP interface +1022 Show RIP neighbors 8000 Reply too long 8001 Route not found diff --git a/lib/ip.c b/lib/ip.c index e1bfba49..3dc8919a 100644 --- a/lib/ip.c +++ b/lib/ip.c @@ -364,7 +364,9 @@ ip4_class_mask(ip4_addr ad) { u32 m, a = _I(ad); - if (a < 0x80000000) + if (a == 0x00000000) + m = 0x00000000; + else if (a < 0x80000000) m = 0xff000000; else if (a < 0xc0000000) m = 0xffff0000; diff --git a/lib/ip.h b/lib/ip.h index 90bb7f8a..9ac5798a 100644 --- a/lib/ip.h +++ b/lib/ip.h @@ -15,8 +15,11 @@ #include "lib/unaligned.h" +#define IP4_ALL_NODES ipa_build4(224, 0, 0, 1) +#define IP4_ALL_ROUTERS ipa_build4(224, 0, 0, 2) #define IP4_OSPF_ALL_ROUTERS ipa_build4(224, 0, 0, 5) #define IP4_OSPF_DES_ROUTERS ipa_build4(224, 0, 0, 6) +#define IP4_RIP_ROUTERS ipa_build4(224, 0, 0, 9) #define IP6_ALL_NODES ipa_build6(0xFF020000, 0, 0, 1) #define IP6_ALL_ROUTERS ipa_build6(0xFF020000, 0, 0, 2) @@ -32,6 +35,10 @@ #define IP_PREC_INTERNET_CONTROL 0xc0 +#define IP4_HEADER_LENGTH 20 +#define IP6_HEADER_LENGTH 40 +#define UDP_HEADER_LENGTH 8 + #ifdef IPV6 #define MAX_PREFIX_LENGTH 128 diff --git a/nest/password.c b/nest/password.c index 21e42e0e..91aaa418 100644 --- a/nest/password.c +++ b/nest/password.c @@ -51,3 +51,18 @@ password_find_by_id(list *l, int id) return NULL; } +struct password_item * +password_find_by_value(list *l, char *pass, uint size) +{ + struct password_item *pi; + + if (!l) + return NULL; + + WALK_LIST(pi, *l) + if (password_verify(pi, pass, size) && (pi->accfrom <= now_real) && (now_real < pi->accto)) + return pi; + + return NULL; +} + diff --git a/nest/password.h b/nest/password.h index cd120d70..1d9de53c 100644 --- a/nest/password.h +++ b/nest/password.h @@ -11,8 +11,6 @@ #define PASSWORD_H #include "lib/timer.h" -#define MD5_AUTH_SIZE 16 - struct password_item { node n; char *password; @@ -24,6 +22,7 @@ extern struct password_item *last_password_item; struct password_item *password_find(list *l, int first_fit); struct password_item *password_find_by_id(list *l, int id); +struct password_item *password_find_by_value(list *l, char *pass, uint size); static inline int password_verify(struct password_item *p1, char *p2, uint size) { diff --git a/nest/route.h b/nest/route.h index 6067526d..285fb372 100644 --- a/nest/route.h +++ b/nest/route.h @@ -75,6 +75,8 @@ void fib_check(struct fib *); /* Consistency check for debugging */ void fit_init(struct fib_iterator *, struct fib *); /* Internal functions, don't call */ struct fib_node *fit_get(struct fib *, struct fib_iterator *); void fit_put(struct fib_iterator *, struct fib_node *); +void fit_put_next(struct fib *f, struct fib_iterator *i, struct fib_node *n, uint hpos); + #define FIB_WALK(fib, z) do { \ struct fib_node *z, **ff = (fib)->hash_table; \ @@ -103,6 +105,11 @@ void fit_put(struct fib_iterator *, struct fib_node *); #define FIB_ITERATE_PUT(it, z) fit_put(it, z) +#define FIB_ITERATE_PUT_NEXT(it, fib, z) fit_put_next(fib, it, z, hpos) + +#define FIB_ITERATE_UNLINK(it, fib) fit_get(fib, it) + + /* * Master Routing Tables. Generally speaking, each of them contains a FIB * with each entry pointing to a list of route entries representing routes @@ -196,10 +203,9 @@ typedef struct rte { union { /* Protocol-dependent data (metrics etc.) */ #ifdef CONFIG_RIP struct { - node garbage; /* List for garbage collection */ - byte metric; /* RIP metric */ + struct iface *from; /* Incoming iface */ + u8 metric; /* RIP metric */ u16 tag; /* External route tag */ - struct rip_entry *entry; } rip; #endif #ifdef CONFIG_OSPF diff --git a/nest/rt-fib.c b/nest/rt-fib.c index aa5e2357..a73de1fd 100644 --- a/nest/rt-fib.c +++ b/nest/rt-fib.c @@ -430,6 +430,25 @@ fit_put(struct fib_iterator *i, struct fib_node *n) i->prev = (struct fib_iterator *) n; } +void +fit_put_next(struct fib *f, struct fib_iterator *i, struct fib_node *n, uint hpos) +{ + if (n = n->next) + goto found; + + while (++hpos < f->hash_size) + if (n = f->hash_table[hpos]) + goto found; + + /* We are at the end */ + i->prev = i->next = NULL; + i->node = NULL; + return; + +found: + fit_put(i, n); +} + #ifdef DEBUGGING /** diff --git a/nest/rt-table.c b/nest/rt-table.c index 9e2c4e0d..b39f2a69 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -888,12 +888,6 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, struct rte_src *sr } rte_free_quick(new); -#ifdef CONFIG_RIP - /* lastmod is used internally by RIP as the last time - when the route was received. */ - if (src->proto->proto == &proto_rip) - old->lastmod = now; -#endif return; } *k = old->next; diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c index fb63e61c..65842037 100644 --- a/proto/ospf/packet.c +++ b/proto/ospf/packet.c @@ -223,7 +223,7 @@ ospf_rx_hook(sock *sk, int len) return 1; DBG("OSPF: RX hook called (iface %s, src %I, dst %I)\n", - sk->ifname, sk->faddr, sk->laddr); + sk->iface->name, sk->faddr, sk->laddr); /* Initially, the packet is associated with the 'master' iface */ struct ospf_iface *ifa = sk->data; diff --git a/proto/rip/Doc b/proto/rip/Doc index 2c7f4c8f..561b2153 100644 --- a/proto/rip/Doc +++ b/proto/rip/Doc @@ -1,2 +1,2 @@ S rip.c -S auth.c +S packets.c diff --git a/proto/rip/Makefile b/proto/rip/Makefile index d03e3a90..d2d3c987 100644 --- a/proto/rip/Makefile +++ b/proto/rip/Makefile @@ -1,4 +1,4 @@ -source=rip.c auth.c +source=rip.c packets.c root-rel=../../ dir-name=proto/rip diff --git a/proto/rip/auth.c b/proto/rip/auth.c deleted file mode 100644 index 5634547a..00000000 --- a/proto/rip/auth.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Rest in pieces - RIP protocol - * - * Copyright (c) 1999 Pavel Machek - * Copyright (c) 2004 Ondrej Filip - * - * Bug fixes by Eric Leblond , April 2003 - * - * Can be freely distributed and used under the terms of the GNU GPL. - */ - -#undef LOCAL_DEBUG - -#include "nest/bird.h" -#include "nest/iface.h" -#include "nest/protocol.h" -#include "nest/route.h" -#include "lib/socket.h" -#include "lib/resource.h" -#include "lib/lists.h" -#include "lib/timer.h" -#include "lib/md5.h" -#include "lib/string.h" - -#include "rip.h" - -#define P ((struct rip_proto *) p) -#define P_CF ((struct rip_proto_config *)p->cf) - -#define PACKETLEN(num) (num * sizeof(struct rip_block) + sizeof(struct rip_packet_heading)) - -/* - * rip_incoming_authentication - check authentication of incomming packet and return 1 if there's problem. - */ -int -rip_incoming_authentication( struct proto *p, struct rip_block_auth *block, struct rip_packet *packet, int num, ip_addr whotoldme ) -{ - DBG( "Incoming authentication: " ); - switch (ntohs(block->authtype)) { /* Authentication type */ - case AT_PLAINTEXT: - { - struct password_item *passwd = password_find(P_CF->passwords, 1); - DBG( "Plaintext passwd" ); - if (!passwd) { - log( L_AUTH "No passwords set and password authentication came" ); - return 1; - } - if (strncmp( (char *) (&block->packetlen), passwd->password, 16)) { - log( L_AUTH "Passwd authentication failed!" ); - DBG( "Expected %s, got %.16s\n", passwd->password, &block->packetlen ); - return 1; - } - } - break; - case AT_MD5: - DBG( "md5 password" ); - { - struct password_item *pass = NULL, *ptmp; - struct rip_md5_tail *tail; - struct MD5Context ctxt; - char md5sum_packet[16]; - char md5sum_computed[16]; - struct neighbor *neigh = neigh_find(p, &whotoldme, 0); - list *l = P_CF->passwords; - - if (ntohs(block->packetlen) != PACKETLEN(num) - sizeof(struct rip_md5_tail) ) { - log( L_ERR "Packet length in MD5 does not match computed value" ); - return 1; - } - - tail = (struct rip_md5_tail *) ((char *) packet + (ntohs(block->packetlen) )); - if ((tail->mustbeFFFF != 0xffff) || (tail->mustbe0001 != 0x0100)) { - log( L_ERR "MD5 tail signature is not there" ); - return 1; - } - - WALK_LIST(ptmp, *l) - { - if (block->keyid != ptmp->id) continue; - if ((ptmp->genfrom > now_real) || (ptmp->gento < now_real)) continue; - pass = ptmp; - break; - } - - if(!pass) return 1; - - if (!neigh) { - log( L_AUTH "Non-neighbour MD5 checksummed packet?" ); - } else { - if (neigh->aux > block->seq) { - log( L_AUTH "MD5 protected packet with lower numbers" ); - return 1; - } - neigh->aux = block->seq; - } - - memcpy(md5sum_packet, tail->md5, 16); - strncpy(tail->md5, pass->password, 16); - - MD5Init(&ctxt); - MD5Update(&ctxt, (char *) packet, ntohs(block->packetlen) + sizeof(struct rip_block_auth) ); - MD5Final(md5sum_computed, &ctxt); - if (memcmp(md5sum_packet, md5sum_computed, 16)) - return 1; - } - } - - return 0; -} - -/* - * rip_outgoing_authentication - append authentication information to the packet. - * %num: number of rip_blocks already in packets. This function returns size of packet to send. - */ -int -rip_outgoing_authentication( struct proto *p, struct rip_block_auth *block, struct rip_packet *packet, int num ) -{ - struct password_item *passwd = password_find(P_CF->passwords, 1); - - if (!P_CF->authtype) - return PACKETLEN(num); - - DBG( "Outgoing authentication: " ); - - if (!passwd) { - log( L_ERR "No suitable password found for authentication" ); - return PACKETLEN(num); - } - - block->authtype = htons(P_CF->authtype); - block->mustbeFFFF = 0xffff; - switch (P_CF->authtype) { - case AT_PLAINTEXT: - strncpy( (char *) (&block->packetlen), passwd->password, 16); - return PACKETLEN(num); - case AT_MD5: - { - struct rip_md5_tail *tail; - struct MD5Context ctxt; - static u32 sequence = 0; - - if (num > PACKET_MD5_MAX) - bug( "We can not add MD5 authentication to this long packet" ); - - /* need to preset the sequence number to a sane value */ - if (!sequence) - sequence = (u32) time(NULL); - - block->keyid = passwd->id; - block->authlen = sizeof(struct rip_block_auth); - block->seq = sequence++; - block->zero0 = 0; - block->zero1 = 0; - block->packetlen = htons(PACKETLEN(num)); - tail = (struct rip_md5_tail *) ((char *) packet + PACKETLEN(num) ); - tail->mustbeFFFF = 0xffff; - tail->mustbe0001 = 0x0100; - - strncpy(tail->md5, passwd->password, 16); - MD5Init(&ctxt); - MD5Update(&ctxt, (char *) packet, PACKETLEN(num) + sizeof(struct rip_md5_tail)); - MD5Final(tail->md5, &ctxt); - return PACKETLEN(num) + block->authlen; - } - default: - bug( "Unknown authtype in outgoing authentication?" ); - } -} diff --git a/proto/rip/config.Y b/proto/rip/config.Y index b2b99095..29ea7eb1 100644 --- a/proto/rip/config.Y +++ b/proto/rip/config.Y @@ -1,17 +1,14 @@ /* * BIRD -- RIP Configuration * + * (c) 1998--1999 Pavel Machek + * (c) 2004--2013 Ondrej Filip + * (c) 2009--2015 Ondrej Zajicek + * (c) 2009--2015 CZ.NIC z.s.p.o. + * * Can be freely distributed and used under the terms of the GNU GPL. */ -/* -To add: - -version1 switch - -*/ - - CF_HDR #include "proto/rip/rip.h" @@ -19,76 +16,141 @@ CF_HDR CF_DEFINES -#define RIP_CFG ((struct rip_proto_config *) this_proto) -#define RIP_IPATT ((struct rip_patt *) this_ipatt) +#define RIP_CFG ((struct rip_config *) this_proto) +#define RIP_IFACE ((struct rip_iface_config *) this_ipatt) + +static inline int rip_cfg_is_v2(void) { return RIP_CFG->rip2; } +static inline int rip_cfg_is_ng(void) { return ! RIP_CFG->rip2; } + +static inline void +rip_check_auth(void) +{ + if (rip_cfg_is_ng()) + cf_error("Authentication not supported in RIPng"); +} -#ifdef IPV6 -#define RIP_DEFAULT_TTL_SECURITY 2 -#else -#define RIP_DEFAULT_TTL_SECURITY 0 -#endif CF_DECLS -CF_KEYWORDS(RIP, INFINITY, METRIC, PORT, PERIOD, GARBAGE, TIMEOUT, - MODE, BROADCAST, MULTICAST, QUIET, NOLISTEN, VERSION1, - AUTHENTICATION, NONE, PLAINTEXT, MD5, TTL, SECURITY, - HONOR, NEVER, NEIGHBOR, ALWAYS, TX, PRIORITY, ONLY, - RIP_METRIC, RIP_TAG) +CF_KEYWORDS(RIP, ECMP, LIMIT, WEIGHT, INFINITY, METRIC, UPDATE, TIMEOUT, + GARBAGE, PORT, ADDRESS, MODE, BROADCAST, MULTICAST, PASSIVE, + VERSION, SPLIT, HORIZON, POISON, REVERSE, CHECK, ZERO, TIME, BFD, + AUTHENTICATION, NONE, PLAINTEXT, CRYPTOGRAPHIC, MD5, TTL, SECURITY, + RX, TX, BUFFER, LENGTH, PRIORITY, ONLY, LINK, RIP_METRIC, RIP_TAG) -%type rip_mode rip_auth +%type rip_auth CF_GRAMMAR -CF_ADDTO(proto, rip_cfg '}' { RIP_CFG->passwords = get_passwords(); } ) +CF_ADDTO(proto, rip_proto) -rip_cfg_start: proto_start RIP { - this_proto = proto_config_new(&proto_rip, $1); - rip_init_config(RIP_CFG); - } +rip_proto_start: proto_start RIP +{ + this_proto = proto_config_new(&proto_rip, $1); + init_list(&RIP_CFG->patt_list); + + RIP_CFG->rip2 = RIP_IS_V2; + RIP_CFG->infinity = RIP_DEFAULT_INFINITY; + + RIP_CFG->min_timeout_time = 60; + RIP_CFG->max_garbage_time = 60; +}; + +rip_proto_item: + proto_item + | ECMP bool { RIP_CFG->ecmp = $2 ? RIP_DEFAULT_ECMP_LIMIT : 0; } + | ECMP bool LIMIT expr { RIP_CFG->ecmp = $2 ? $4 : 0; if ($4 < 0) cf_error("ECMP limit cannot be negative"); } + | INFINITY expr { RIP_CFG->infinity = $2; } + | INTERFACE rip_iface ; -rip_cfg: - rip_cfg_start proto_name '{' - | rip_cfg proto_item ';' - | rip_cfg INFINITY expr ';' { RIP_CFG->infinity = $3; } - | rip_cfg PORT expr ';' { RIP_CFG->port = $3; } - | rip_cfg PERIOD expr ';' { RIP_CFG->period = $3; } - | rip_cfg GARBAGE TIME expr ';' { RIP_CFG->garbage_time = $4; } - | rip_cfg TIMEOUT TIME expr ';' { RIP_CFG->timeout_time = $4; } - | rip_cfg AUTHENTICATION rip_auth ';' {RIP_CFG->authtype = $3; } - | rip_cfg password_list ';' - | rip_cfg HONOR ALWAYS ';' { RIP_CFG->honor = HO_ALWAYS; } - | rip_cfg HONOR NEIGHBOR ';' { RIP_CFG->honor = HO_NEIGHBOR; } - | rip_cfg HONOR NEVER ';' { RIP_CFG->honor = HO_NEVER; } - | rip_cfg INTERFACE rip_iface ';' +rip_proto_opts: + /* empty */ + | rip_proto_opts rip_proto_item ';' ; -rip_auth: - PLAINTEXT { $$=AT_PLAINTEXT; } - | MD5 { $$=AT_MD5; } - | NONE { $$=AT_NONE; } - ; +rip_proto: + rip_proto_start proto_name '{' rip_proto_opts '}'; -rip_mode: - BROADCAST { $$=IM_BROADCAST; } - | MULTICAST { $$=0; } - | QUIET { $$=IM_QUIET; } - | NOLISTEN { $$=IM_NOLISTEN; } - | VERSION1 { $$=IM_VERSION1 | IM_BROADCAST; } - ; +rip_iface_start: +{ + this_ipatt = cfg_allocz(sizeof(struct rip_iface_config)); + add_tail(&RIP_CFG->patt_list, NODE this_ipatt); + init_list(&this_ipatt->ipn_list); + reset_passwords(); + + RIP_IFACE->metric = 1; + RIP_IFACE->port = rip_cfg_is_v2() ? RIP_PORT : RIP_NG_PORT; + RIP_IFACE->version = rip_cfg_is_v2() ? RIP_V2 : RIP_V1; + RIP_IFACE->split_horizon = 1; + RIP_IFACE->poison_reverse = 1; + RIP_IFACE->check_zero = 1; + RIP_IFACE->ttl_security = rip_cfg_is_v2() ? 0 : 1; + RIP_IFACE->rx_buffer = rip_cfg_is_v2() ? RIP_MAX_PKT_LENGTH : 0; + RIP_IFACE->tx_length = rip_cfg_is_v2() ? RIP_MAX_PKT_LENGTH : 0; + RIP_IFACE->tx_tos = IP_PREC_INTERNET_CONTROL; + RIP_IFACE->tx_priority = sk_priority_control; + RIP_IFACE->update_time = RIP_DEFAULT_UPDATE_TIME; + RIP_IFACE->timeout_time = RIP_DEFAULT_TIMEOUT_TIME; + RIP_IFACE->garbage_time = RIP_DEFAULT_GARBAGE_TIME; +}; + +rip_iface_finish: +{ + RIP_IFACE->passwords = get_passwords(); + + if (!RIP_IFACE->auth_type != !RIP_IFACE->passwords) + log(L_WARN "Authentication and password options should be used together"); + + /* Default mode is broadcast for RIPv1, multicast for RIPv2 and RIPng */ + if (!RIP_IFACE->mode) + RIP_IFACE->mode = (rip_cfg_is_v2() && (RIP_IFACE->version == RIP_V1)) ? + RIP_IM_BROADCAST : RIP_IM_MULTICAST; + + RIP_CFG->min_timeout_time = MIN_(RIP_CFG->min_timeout_time, RIP_IFACE->timeout_time); + RIP_CFG->max_garbage_time = MAX_(RIP_CFG->max_garbage_time, RIP_IFACE->garbage_time); +}; rip_iface_item: - | METRIC expr { RIP_IPATT->metric = $2; } - | MODE rip_mode { RIP_IPATT->mode |= $2; } - | TX tos { RIP_IPATT->tx_tos = $2; } - | TX PRIORITY expr { RIP_IPATT->tx_priority = $3; } - | TTL SECURITY bool { RIP_IPATT->ttl_security = $3; } - | TTL SECURITY TX ONLY { RIP_IPATT->ttl_security = 2; } + METRIC expr { RIP_IFACE->metric = $2; if (($2<1) || ($2>255)) cf_error("Metric must be in range 1-255"); } + | MODE MULTICAST { RIP_IFACE->mode = RIP_IM_MULTICAST; } + | MODE BROADCAST { RIP_IFACE->mode = RIP_IM_BROADCAST; if (rip_cfg_is_ng()) cf_error("Broadcast not supported in RIPng"); } + | PASSIVE bool { RIP_IFACE->passive = $2; } + | ADDRESS ipa { RIP_IFACE->address = $2; } + | PORT expr { RIP_IFACE->port = $2; if (($2<1) || ($2>65535)) cf_error("Invalid port number"); } + | VERSION expr { RIP_IFACE->version = $2; + if (rip_cfg_is_ng()) cf_error("Version not supported in RIPng"); + if (($2 != RIP_V1) && ($2 != RIP_V2)) cf_error("Unsupported version"); + } + | VERSION ONLY bool { RIP_IFACE->version_only = $3; } + | SPLIT HORIZON bool { RIP_IFACE->split_horizon = $3; } + | POISON REVERSE bool { RIP_IFACE->poison_reverse = $3; } + | CHECK ZERO bool { RIP_IFACE->check_zero = $3; } + | UPDATE TIME expr { RIP_IFACE->update_time = $3; if ($3<=0) cf_error("Update time must be positive"); } + | TIMEOUT TIME expr { RIP_IFACE->timeout_time = $3; if ($3<=0) cf_error("Timeout time must be positive"); } + | GARBAGE TIME expr { RIP_IFACE->garbage_time = $3; if ($3<=0) cf_error("Garbage time must be positive"); } + | ECMP WEIGHT expr { RIP_IFACE->ecmp_weight = $3 - 1; if (($3<1) || ($3>256)) cf_error("ECMP weight must be in range 1-256"); } + | RX BUFFER expr { RIP_IFACE->rx_buffer = $3; if (($3<256) || ($3>65535)) cf_error("TX length must be in range 256-65535"); } + | TX LENGTH expr { RIP_IFACE->tx_length = $3; if (($3<256) || ($3>65535)) cf_error("TX length must be in range 256-65535"); } + | TX tos { RIP_IFACE->tx_tos = $2; } + | TX PRIORITY expr { RIP_IFACE->tx_priority = $3; } + | TTL SECURITY bool { RIP_IFACE->ttl_security = $3; } + | TTL SECURITY TX ONLY { RIP_IFACE->ttl_security = 2; } + | CHECK LINK bool { RIP_IFACE->check_link = $3; } + | BFD bool { RIP_IFACE->bfd = $2; cf_check_bfd($2); } + | AUTHENTICATION rip_auth { RIP_IFACE->auth_type = $2; if ($2) rip_check_auth(); } + | password_list { rip_check_auth(); } +; + +rip_auth: + NONE { $$ = RIP_AUTH_NONE; } + | PLAINTEXT { $$ = RIP_AUTH_PLAIN; } + | CRYPTOGRAPHIC { $$ = RIP_AUTH_CRYPTO; } + | MD5 { $$ = RIP_AUTH_CRYPTO; } ; -rip_iface_opts: +rip_iface_opts: /* empty */ | rip_iface_opts rip_iface_item ';' ; @@ -98,25 +160,22 @@ rip_iface_opt_list: | '{' rip_iface_opts '}' ; -rip_iface_init: - /* EMPTY */ { - this_ipatt = cfg_allocz(sizeof(struct rip_patt)); - add_tail(&RIP_CFG->iface_list, NODE this_ipatt); - init_list(&this_ipatt->ipn_list); - RIP_IPATT->metric = 1; - RIP_IPATT->tx_tos = IP_PREC_INTERNET_CONTROL; - RIP_IPATT->tx_priority = sk_priority_control; - RIP_IPATT->ttl_security = RIP_DEFAULT_TTL_SECURITY; - } - ; +rip_iface: + rip_iface_start iface_patt_list_nopx rip_iface_opt_list rip_iface_finish; -rip_iface: /* TODO: switch to iface_patt_list_nopx */ - rip_iface_init iface_patt_list rip_iface_opt_list - ; CF_ADDTO(dynamic_attr, RIP_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_RIP_METRIC); }) CF_ADDTO(dynamic_attr, RIP_TAG { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_RIP_TAG); }) +CF_CLI_HELP(SHOW RIP, ..., [[Show information about RIP protocol]]); + +CF_CLI(SHOW RIP INTERFACES, optsym opttext, [] [\"\"], [[Show information about RIP interfaces]]) +{ rip_show_interfaces(proto_get_named($4, &proto_rip), $5); }; + +CF_CLI(SHOW RIP NEIGHBORS, optsym opttext, [] [\"\"], [[Show information about RIP neighbors]]) +{ rip_show_neighbors(proto_get_named($4, &proto_rip), $5); }; + + CF_CODE CF_END diff --git a/proto/rip/packets.c b/proto/rip/packets.c new file mode 100644 index 00000000..be20734f --- /dev/null +++ b/proto/rip/packets.c @@ -0,0 +1,772 @@ +/* + * BIRD -- Routing Information Protocol (RIP) + * + * (c) 1998--1999 Pavel Machek + * (c) 2004--2013 Ondrej Filip + * (c) 2009--2015 Ondrej Zajicek + * (c) 2009--2015 CZ.NIC z.s.p.o. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include "rip.h" +#include "lib/md5.h" + + +#define RIP_CMD_REQUEST 1 /* want info */ +#define RIP_CMD_RESPONSE 2 /* responding to request */ + +#define RIP_BLOCK_LENGTH 20 + +#define RIP_PASSWD_LENGTH 16 +#define RIP_MD5_LENGTH 16 + +#define RIP_AF_IPV4 2 +#define RIP_AF_AUTH 0xffff + + +/* RIP packet header */ +struct rip_packet +{ + u8 command; + u8 version; + u16 unused; +}; + +/* RTE block for RIPv2 */ +struct rip_block_v2 +{ + u16 family; + u16 tag; + ip4_addr network; + ip4_addr netmask; + ip4_addr next_hop; + u32 metric; +}; + +/* RTE block for RIPng */ +struct rip_block_ng +{ + ip6_addr prefix; + u16 tag; + u8 pxlen; + u8 metric; +}; + +/* Authentication block for RIPv2 */ +struct rip_block_auth +{ + u16 must_be_ffff; + u16 auth_type; + char password[0]; + u16 packet_len; + u8 key_id; + u8 auth_len; + u32 seq_num; + u32 unused1; + u32 unused2; +}; + +/* Authentication tail, RFC 4822 */ +struct rip_auth_tail +{ + u16 must_be_ffff; + u16 must_be_0001; + byte auth_data[]; +}; + +/* Internal representation of RTE block data */ +struct rip_block +{ + ip_addr prefix; + int pxlen; + u32 metric; + u16 tag; + u16 no_af; + ip_addr next_hop; +}; + + +#define DROP(DSC,VAL) do { err_dsc = DSC; err_val = VAL; goto drop; } while(0) +#define DROP1(DSC) do { err_dsc = DSC; goto drop; } while(0) +#define SKIP(DSC) do { err_dsc = DSC; goto skip; } while(0) + +#define LOG_PKT(msg, args...) \ + log_rl(&p->log_pkt_tbf, L_REMOTE "%s: " msg, p->p.name, args) + +#define LOG_PKT_AUTH(msg, args...) \ + log_rl(&p->log_pkt_tbf, L_AUTH "%s: " msg, p->p.name, args) + +#define LOG_RTE(msg, args...) \ + log_rl(&p->log_rte_tbf, L_REMOTE "%s: " msg, p->p.name, args) + + +static inline void * rip_tx_buffer(struct rip_iface *ifa) +{ return ifa->sk->tbuf; } + +static inline uint rip_pkt_hdrlen(struct rip_iface *ifa) +{ return sizeof(struct rip_packet) + (ifa->cf->auth_type ? RIP_BLOCK_LENGTH : 0); } + +static inline void +rip_put_block(struct rip_proto *p, byte *pos, struct rip_block *rte) +{ + if (rip_is_v2(p)) + { + struct rip_block_v2 *block = (void *) pos; + block->family = rte->no_af ? 0 : htons(RIP_AF_IPV4); + block->tag = htons(rte->tag); + block->network = ip4_hton(ipa_to_ip4(rte->prefix)); + block->netmask = ip4_hton(ip4_mkmask(rte->pxlen)); + block->next_hop = ip4_hton(ipa_to_ip4(rte->next_hop)); + block->metric = htonl(rte->metric); + } + else /* RIPng */ + { + struct rip_block_ng *block = (void *) pos; + block->prefix = ip6_hton(ipa_to_ip6(rte->prefix)); + block->tag = htons(rte->tag); + block->pxlen = rte->pxlen; + block->metric = rte->metric; + } +} + +static inline void +rip_put_next_hop(struct rip_proto *p, byte *pos, struct rip_block *rte) +{ + struct rip_block_ng *block = (void *) pos; + block->prefix = ip6_hton(ipa_to_ip6(rte->next_hop)); + block->tag = 0; + block->pxlen = 0; + block->metric = 0xff; +} + +static inline int +rip_get_block(struct rip_proto *p, byte *pos, struct rip_block *rte) +{ + if (rip_is_v2(p)) + { + struct rip_block_v2 *block = (void *) pos; + + /* Skip blocks with strange AF, including authentication blocks */ + if (block->family != (rte->no_af ? 0 : htons(RIP_AF_IPV4))) + return 0; + + rte->prefix = ipa_from_ip4(ip4_ntoh(block->network)); + rte->pxlen = ip4_masklen(ip4_ntoh(block->netmask)); + rte->metric = ntohl(block->metric); + rte->tag = ntohs(block->tag); + rte->next_hop = ipa_from_ip4(ip4_ntoh(block->next_hop)); + + return 1; + } + else /* RIPng */ + { + struct rip_block_ng *block = (void *) pos; + + /* Handle and skip next hop blocks */ + if (block->metric == 0xff) + { + rte->next_hop = ipa_from_ip6(ip6_ntoh(block->prefix)); + if (!ipa_is_link_local(rte->next_hop)) rte->next_hop = IPA_NONE; + return 0; + } + + rte->prefix = ipa_from_ip6(ip6_ntoh(block->prefix)); + rte->pxlen = block->pxlen; + rte->metric = block->metric; + rte->tag = ntohs(block->tag); + /* rte->next_hop is deliberately kept unmodified */; + + return 1; + } +} + +static inline void +rip_update_csn(struct rip_proto *p UNUSED, struct rip_iface *ifa) +{ + /* + * We update crypto sequence numbers at the beginning of update session to + * avoid issues with packet reordering, so packets inside one update session + * have the same CSN. We are using real time, but enforcing monotonicity. + */ + if (ifa->cf->auth_type == RIP_AUTH_CRYPTO) + ifa->csn = (ifa->csn < (u32) now_real) ? (u32) now_real : ifa->csn + 1; +} + +static void +rip_fill_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint *plen) +{ + struct rip_block_auth *auth = (void *) (pkt + 1); + struct password_item *pass = password_find(ifa->cf->passwords, 0); + + if (!pass) + { + /* FIXME: This should not happen */ + log(L_ERR "%s: No suitable password found for authentication", p->p.name); + memset(auth, 0, sizeof(struct rip_block_auth)); + return; + } + + switch (ifa->cf->auth_type) + { + case RIP_AUTH_PLAIN: + auth->must_be_ffff = htons(0xffff); + auth->auth_type = htons(RIP_AUTH_PLAIN); + strncpy(auth->password, pass->password, RIP_PASSWD_LENGTH); + return; + + case RIP_AUTH_CRYPTO: + auth->must_be_ffff = htons(0xffff); + auth->auth_type = htons(RIP_AUTH_CRYPTO); + auth->packet_len = htons(*plen); + auth->key_id = pass->id; + auth->auth_len = sizeof(struct rip_auth_tail) + RIP_MD5_LENGTH; + auth->seq_num = ifa->csn_ready ? htonl(ifa->csn) : 0; + auth->unused1 = 0; + auth->unused2 = 0; + ifa->csn_ready = 1; + + /* + * Note that RFC 4822 is unclear whether auth_len should cover whole + * authentication trailer or just auth_data length. + * + * Crypto sequence numbers are increased by sender in rip_update_csn(). + * First CSN should be zero, this is handled by csn_ready. + */ + + struct rip_auth_tail *tail = (void *) ((byte *) pkt + *plen); + tail->must_be_ffff = htons(0xffff); + tail->must_be_0001 = htons(0x0001); + strncpy(tail->auth_data, pass->password, RIP_MD5_LENGTH); + + *plen += sizeof(struct rip_auth_tail) + RIP_MD5_LENGTH; + + struct MD5Context ctxt; + MD5Init(&ctxt); + MD5Update(&ctxt, (byte *) pkt, *plen); + MD5Final(tail->auth_data, &ctxt); + return; + + default: + bug("Unknown authentication type"); + } +} + +static int +rip_check_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint *plen, struct rip_neighbor *n) +{ + struct rip_block_auth *auth = (void *) (pkt + 1); + struct password_item *pass = NULL; + const char *err_dsc = NULL; + uint err_val = 0; + uint auth_type = 0; + + /* Check for authentication entry */ + if ((*plen >= (sizeof(struct rip_packet) + sizeof(struct rip_block_auth))) && + (auth->must_be_ffff == htons(0xffff))) + auth_type = ntohs(auth->auth_type); + + if (auth_type != ifa->cf->auth_type) + DROP("authentication method mismatch", auth_type); + + switch (auth_type) + { + case RIP_AUTH_NONE: + return 1; + + case RIP_AUTH_PLAIN: + pass = password_find_by_value(ifa->cf->passwords, auth->password, RIP_PASSWD_LENGTH); + if (!pass) + DROP1("wrong password"); + + return 1; + + case RIP_AUTH_CRYPTO: + pass = password_find_by_id(ifa->cf->passwords, auth->key_id); + if (!pass) + DROP("no suitable password found", auth->key_id); + + uint data_len = ntohs(auth->packet_len); + uint auth_len = sizeof(struct rip_auth_tail) + RIP_MD5_LENGTH; + + if (data_len + auth_len != *plen) + DROP("packet length mismatch", data_len); + + if ((auth->auth_len != RIP_MD5_LENGTH) && (auth->auth_len != auth_len)) + DROP("authentication data length mismatch", auth->auth_len); + + struct rip_auth_tail *tail = (void *) ((byte *) pkt + data_len); + if ((tail->must_be_ffff != htons(0xffff)) || (tail->must_be_0001 != htons(0x0001))) + DROP1("authentication trailer is missing"); + + /* Accept higher sequence number, or zero if connectivity is lost */ + /* FIXME: sequence number must be password/SA specific */ + u32 rcv_csn = ntohl(auth->seq_num); + if ((rcv_csn < n->csn) && (rcv_csn || n->uc)) + { + /* We want to report both new and old CSN */ + LOG_PKT_AUTH("Authentication failed for %I on %s - " + "lower sequence number (rcv %u, old %u)", + n->nbr->addr, ifa->iface->name, rcv_csn, n->csn); + return 0; + } + + char received[RIP_MD5_LENGTH]; + char computed[RIP_MD5_LENGTH]; + + memcpy(received, tail->auth_data, RIP_MD5_LENGTH); + strncpy(tail->auth_data, pass->password, RIP_MD5_LENGTH); + + struct MD5Context ctxt; + MD5Init(&ctxt); + MD5Update(&ctxt, (byte *) pkt, *plen); + MD5Final(computed, &ctxt); + + if (memcmp(received, computed, RIP_MD5_LENGTH)) + DROP("wrong MD5 digest", pass->id); + + *plen = data_len; + n->csn = rcv_csn; + + return 1; + } + +drop: + LOG_PKT_AUTH("Authentication failed for %I on %s - %s (%u)", + n->nbr->addr, ifa->iface->name, err_dsc, err_val); + + return 0; +} + +static inline int +rip_send_to(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint plen, ip_addr dst) +{ + if (ifa->cf->auth_type) + rip_fill_authentication(p, ifa, pkt, &plen); + + return sk_send_to(ifa->sk, plen, dst, 0); +} + + +void +rip_send_request(struct rip_proto *p, struct rip_iface *ifa) +{ + byte *pos = rip_tx_buffer(ifa); + + struct rip_packet *pkt = (void *) pos; + pkt->command = RIP_CMD_REQUEST; + pkt->version = ifa->cf->version; + pkt->unused = 0; + pos += rip_pkt_hdrlen(ifa); + + struct rip_block b = { .no_af = 1, .metric = p->infinity }; + rip_put_block(p, pos, &b); + pos += RIP_BLOCK_LENGTH; + + rip_update_csn(p, ifa); + + TRACE(D_PACKETS, "Sending request via %s", ifa->iface->name); + rip_send_to(p, ifa, pkt, pos - (byte *) pkt, ifa->addr); +} + +static void +rip_receive_request(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint plen, struct rip_neighbor *from) +{ + TRACE(D_PACKETS, "Request received from %I on %s", from->nbr->addr, ifa->iface->name); + + byte *pos = (byte *) pkt + rip_pkt_hdrlen(ifa); + + /* We expect one regular block */ + if (plen != (rip_pkt_hdrlen(ifa) + RIP_BLOCK_LENGTH)) + return; + + struct rip_block b = { .no_af = 1 }; + + if (!rip_get_block(p, pos, &b)) + return; + + /* Special case - zero prefix, infinity metric */ + if (ipa_nonzero(b.prefix) || b.pxlen || (b.metric != p->infinity)) + return; + + /* We do nothing if TX is already active */ + if (ifa->tx_active) + { + TRACE(D_EVENTS, "Skipping request from %I on %s, TX is busy", from->nbr->addr, ifa->iface->name); + return; + } + + if (!ifa->cf->passive) + rip_send_table(p, ifa, from->nbr->addr, 0); +} + + +static int +rip_send_response(struct rip_proto *p, struct rip_iface *ifa) +{ + if (! ifa->tx_active) + return 0; + + byte *pos = rip_tx_buffer(ifa); + byte *max = rip_tx_buffer(ifa) + ifa->tx_plen - + (rip_is_v2(p) ? RIP_BLOCK_LENGTH : 2*RIP_BLOCK_LENGTH); + ip_addr last_next_hop = IPA_NONE; + int send = 0; + + struct rip_packet *pkt = (void *) pos; + pkt->command = RIP_CMD_RESPONSE; + pkt->version = ifa->cf->version; + pkt->unused = 0; + pos += rip_pkt_hdrlen(ifa); + + FIB_ITERATE_START(&p->rtable, &ifa->tx_fit, z) + { + struct rip_entry *en = (struct rip_entry *) z; + + /* Dummy entries */ + if (!en->valid) + goto next_entry; + + /* Stale entries that should be removed */ + if ((en->valid == RIP_ENTRY_STALE) && + ((en->changed + ifa->cf->garbage_time) <= now)) + goto next_entry; + + /* Triggered updates */ + if (en->changed < ifa->tx_changed) + goto next_entry; + + /* Not enough space for current entry */ + if (pos > max) + { + FIB_ITERATE_PUT(&ifa->tx_fit, z); + goto break_loop; + } + + struct rip_block rte = { + .prefix = en->n.prefix, + .pxlen = en->n.pxlen, + .metric = en->metric, + .tag = en->tag + }; + + if (en->iface == ifa->iface) + rte.next_hop = en->next_hop; + + if (rip_is_v2(p) && (ifa->cf->version == RIP_V1)) + { + /* Skipping subnets (i.e. not hosts, classful networks or default route) */ + if (ip4_masklen(ip4_class_mask(ipa_to_ip4(en->n.prefix))) != en->n.pxlen) + goto next_entry; + + rte.tag = 0; + rte.pxlen = 0; + rte.next_hop = IPA_NONE; + } + + /* Split horizon */ + if (en->from == ifa->iface && ifa->cf->split_horizon) + { + if (ifa->cf->poison_reverse) + { + rte.metric = p->infinity; + rte.next_hop = IPA_NONE; + } + else + goto next_entry; + } + + // TRACE(D_PACKETS, " %I/%d -> %I metric %d", rte.prefix, rte.pxlen, rte.next_hop, rte.metric); + + /* RIPng next hop entry */ + if (rip_is_ng(p) && !ipa_equal(rte.next_hop, last_next_hop)) + { + last_next_hop = rte.next_hop; + rip_put_next_hop(p, pos, &rte); + pos += RIP_BLOCK_LENGTH; + } + + rip_put_block(p, pos, &rte); + pos += RIP_BLOCK_LENGTH; + send = 1; + + next_entry: ; + } + FIB_ITERATE_END(z); + ifa->tx_active = 0; + + /* Do not send empty packet */ + if (!send) + return 0; + +break_loop: + TRACE(D_PACKETS, "Sending response via %s", ifa->iface->name); + return rip_send_to(p, ifa, pkt, pos - (byte *) pkt, ifa->tx_addr); +} + +/** + * rip_send_table - RIP interface timer hook + * @p: RIP instance + * @ifa: RIP interface + * @addr: destination IP address + * @changed: time limit for triggered updates + * + * The function activates an update session and starts sending routing update + * packets (using rip_send_response()). The session may be finished during the + * call or may continue in rip_tx_hook() until all appropriate routes are + * transmitted. Note that there may be at most one active update session per + * interface, the function will terminate the old active session before + * activating the new one. + */ +void +rip_send_table(struct rip_proto *p, struct rip_iface *ifa, ip_addr addr, bird_clock_t changed) +{ + DBG("RIP: Opening TX session to %I on %s\n", dst, ifa->iface->name); + + rip_reset_tx_session(p, ifa); + + ifa->tx_active = 1; + ifa->tx_addr = addr; + ifa->tx_changed = changed; + FIB_ITERATE_INIT(&ifa->tx_fit, &p->rtable); + + rip_update_csn(p, ifa); + + while (rip_send_response(p, ifa) > 0) + ; +} + +static void +rip_tx_hook(sock *sk) +{ + struct rip_iface *ifa = sk->data; + struct rip_proto *p = ifa->rip; + + DBG("RIP: TX hook called (iface %s, src %I, dst %I)\n", + sk->iface->name, sk->saddr, sk->daddr); + + while (rip_send_response(p, ifa) > 0) + ; +} + +static void +rip_err_hook(sock *sk, int err) +{ + struct rip_iface *ifa = sk->data; + struct rip_proto *p = ifa->rip; + + log(L_ERR "%s: Socket error on %s: %M", p->p.name, ifa->iface->name, err); + + rip_reset_tx_session(p, ifa); +} + +static void +rip_receive_response(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint plen, struct rip_neighbor *from) +{ + struct rip_block rte = {}; + const char *err_dsc = NULL; + + TRACE(D_PACKETS, "Response received from %I on %s", from->nbr->addr, ifa->iface->name); + + byte *pos = (byte *) pkt + sizeof(struct rip_packet); + byte *end = (byte *) pkt + plen; + + for (; pos < end; pos += RIP_BLOCK_LENGTH) + { + /* Find next regular RTE */ + if (!rip_get_block(p, pos, &rte)) + continue; + + int c = ipa_classify_net(rte.prefix); + if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK)) + SKIP("invalid prefix"); + + if (rip_is_v2(p) && (pkt->version == RIP_V1)) + { + if (ifa->cf->check_zero && (rte.tag || rte.pxlen || ipa_nonzero(rte.next_hop))) + SKIP("RIPv1 reserved field is nonzero"); + + rte.tag = 0; + rte.pxlen = ip4_masklen(ip4_class_mask(ipa_to_ip4(rte.prefix))); + rte.next_hop = IPA_NONE; + } + + if ((rte.pxlen < 0) || (rte.pxlen > MAX_PREFIX_LENGTH)) + SKIP("invalid prefix length"); + + if (rte.metric > p->infinity) + SKIP("invalid metric"); + + if (ipa_nonzero(rte.next_hop)) + { + neighbor *nbr = neigh_find2(&p->p, &rte.next_hop, ifa->iface, 0); + if (!nbr || (nbr->scope <= 0)) + rte.next_hop = IPA_NONE; + } + + // TRACE(D_PACKETS, " %I/%d -> %I metric %d", rte.prefix, rte.pxlen, rte.next_hop, rte.metric); + + rte.metric += ifa->cf->metric; + + if (rte.metric < p->infinity) + { + struct rip_rte new = { + .from = from, + .next_hop = ipa_nonzero(rte.next_hop) ? rte.next_hop : from->nbr->addr, + .metric = rte.metric, + .tag = rte.tag, + .expires = now + ifa->cf->timeout_time + }; + + rip_update_rte(p, &rte.prefix, rte.pxlen, &new); + } + else + rip_withdraw_rte(p, &rte.prefix, rte.pxlen, from); + + continue; + + skip: + LOG_RTE("Ignoring route %I/%d received from %I - %s", + rte.prefix, rte.pxlen, from->nbr->addr, err_dsc); + } +} + +static int +rip_rx_hook(sock *sk, int len) +{ + struct rip_iface *ifa = sk->data; + struct rip_proto *p = ifa->rip; + const char *err_dsc = NULL; + uint err_val = 0; + + if (sk->lifindex != sk->iface->index) + return 1; + + DBG("RIP: RX hook called (iface %s, src %I, dst %I)\n", + sk->iface->name, sk->faddr, sk->laddr); + + /* Silently ignore my own packets */ + /* FIXME: Better local address check */ + if (ipa_equal(ifa->iface->addr->ip, sk->faddr)) + return 1; + + if (rip_is_ng(p) && !ipa_is_link_local(sk->faddr)) + DROP1("wrong src address"); + + struct rip_neighbor *n = rip_get_neighbor(p, &sk->faddr, ifa); + + if (!n) + DROP1("not from neighbor"); + + if ((ifa->cf->ttl_security == 1) && (sk->rcv_ttl < 255)) + DROP("wrong TTL", sk->rcv_ttl); + + if (sk->fport != sk->dport) + DROP("wrong src port", sk->fport); + + if (len < sizeof(struct rip_packet)) + DROP("too short", len); + + if (sk->flags & SKF_TRUNCATED) + DROP("truncated", len); + + struct rip_packet *pkt = (struct rip_packet *) sk->rbuf; + uint plen = len; + + if (!pkt->version || (ifa->cf->version_only && (pkt->version != ifa->cf->version))) + DROP("wrong version", pkt->version); + + /* rip_check_authentication() has its own error logging */ + if (rip_is_v2(p) && !rip_check_authentication(p, ifa, pkt, &plen, n)) + return 1; + + if ((plen - sizeof(struct rip_packet)) % RIP_BLOCK_LENGTH) + DROP("invalid length", plen); + + n->last_seen = now; + rip_update_bfd(p, n); + + switch (pkt->command) + { + case RIP_CMD_REQUEST: + rip_receive_request(p, ifa, pkt, plen, n); + break; + + case RIP_CMD_RESPONSE: + rip_receive_response(p, ifa, pkt, plen, n); + break; + + default: + DROP("unknown command", pkt->command); + } + return 1; + +drop: + LOG_PKT("Bad packet from %I via %s - %s (%u)", + sk->faddr, sk->iface->name, err_dsc, err_val); + + return 1; +} + +int +rip_open_socket(struct rip_iface *ifa) +{ + struct rip_proto *p = ifa->rip; + + sock *sk = sk_new(p->p.pool); + sk->type = SK_UDP; + sk->sport = ifa->cf->port; + sk->dport = ifa->cf->port; + sk->iface = ifa->iface; + + /* + * For RIPv2, we explicitly choose a primary address, mainly to ensure that + * RIP and BFD uses the same one. For RIPng, we left it to kernel, which + * should choose some link-local address based on the same scope rule. + */ + if (rip_is_v2(p)) + sk->saddr = ifa->iface->addr->ip; + + sk->rx_hook = rip_rx_hook; + sk->tx_hook = rip_tx_hook; + sk->err_hook = rip_err_hook; + sk->data = ifa; + + sk->tos = ifa->cf->tx_tos; + sk->priority = ifa->cf->tx_priority; + sk->ttl = ifa->cf->ttl_security ? 255 : 1; + sk->flags = SKF_LADDR_RX | ((ifa->cf->ttl_security == 1) ? SKF_TTL_RX : 0); + + /* sk->rbsize and sk->tbsize are handled in rip_iface_update_buffers() */ + + if (sk_open(sk) < 0) + goto err; + + if (ifa->cf->mode == RIP_IM_MULTICAST) + { + if (sk_setup_multicast(sk) < 0) + goto err; + + if (sk_join_group(sk, ifa->addr) < 0) + goto err; + } + else /* Broadcast */ + { + if (sk_setup_broadcast(sk) < 0) + goto err; + + if (ipa_zero(ifa->addr)) + { + sk->err = "Missing broadcast address"; + goto err; + } + } + + ifa->sk = sk; + return 1; + +err: + sk_log_error(sk, p->p.name); + rfree(sk); + return 0; +} diff --git a/proto/rip/rip.c b/proto/rip/rip.c index b77cf409..c85fd69b 100644 --- a/proto/rip/rip.c +++ b/proto/rip/rip.c @@ -1,1047 +1,1275 @@ /* - * Rest in pieces - RIP protocol + * BIRD -- Routing Information Protocol (RIP) * - * Copyright (c) 1998, 1999 Pavel Machek - * 2004 Ondrej Filip + * (c) 1998--1999 Pavel Machek + * (c) 2004--2013 Ondrej Filip + * (c) 2009--2015 Ondrej Zajicek + * (c) 2009--2015 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. - * - FIXME: IPv6 support: packet size - FIXME: (nonurgent) IPv6 support: receive "route using" blocks - FIXME: (nonurgent) IPv6 support: generate "nexthop" blocks - next hops are only advisory, and they are pretty ugly in IPv6. - I suggest just forgetting about them. - - FIXME: (nonurgent): fold rip_connection into rip_interface? - - FIXME: propagation of metric=infinity into main routing table may or may not be good idea. */ /** - * DOC: Routing Information Protocol + * DOC: Routing Information Protocol (RIP) * - * RIP is a pretty simple protocol, so about a half of its code is interface - * with the core. + * The RIP protocol is implemented in two files: |rip.c| containing the protocol + * logic, route management and the protocol glue with BIRD core, and |packets.c| + * handling RIP packet processing, RX, TX and protocol sockets. * - * We maintain our own linked list of &rip_entry structures -- it serves - * as our small routing table. RIP never adds to this linked list upon - * packet reception; instead, it lets the core know about data from the packet - * and waits for the core to call rip_rt_notify(). + * Each instance of RIP is described by a structure &rip_proto, which contains + * an internal RIP routing table, a list of protocol interfaces and the main + * timer responsible for RIP routing table cleanup. * - * Within rip_tx(), the list is - * walked and a packet is generated using rip_tx_prepare(). This gets - * tricky because we may need to send more than one packet to one - * destination. Struct &rip_connection is used to hold context information such as how - * many of &rip_entry's we have already sent and it's also used to protect - * against two concurrent sends to one destination. Each &rip_interface has - * at most one &rip_connection. + * RIP internal routing table contains incoming and outgoing routes. For each + * network (represented by structure &rip_entry) there is one outgoing route + * stored directly in &rip_entry and an one-way linked list of incoming routes + * (structures &rip_rte). The list contains incoming routes from different RIP + * neighbors, but only routes with the lowest metric are stored (i.e., all + * stored incoming routes have the same metric). * - * We are not going to honor requests for sending part of - * routing table. That would need to turn split horizon off etc. + * Note that RIP itself does not select outgoing route, that is done by the core + * routing table. When a new incoming route is received, it is propagated to the + * RIP table by rip_update_rte() and possibly stored in the list of incoming + * routes. Then the change may be propagated to the core by rip_announce_rte(). + * The core selects the best route and propagate it to RIP by rip_rt_notify(), + * which updates outgoing route part of &rip_entry and possibly triggers route + * propagation by rip_trigger_update(). * - * About triggered updates, RFC says: when a triggered update was sent, - * don't send a new one for something between 1 and 5 seconds (and send one - * after that). We do something else: each 5 seconds, - * we look for any changed routes and broadcast them. + * RIP interfaces are represented by structures &rip_iface. A RIP interface + * contains a per-interface socket, a list of associated neighbors, interface + * configuration, and state information related to scheduled interface events + * and running update sessions. RIP interfaces are added and removed based on + * core interface notifications. + * + * There are two RIP interface events - regular updates and triggered updates. + * Both are managed from the RIP interface timer (rip_iface_timer()). Regular + * updates are called at fixed interval and propagate the whole routing table, + * while triggered updates are scheduled by rip_trigger_update() due to some + * routing table change and propagate only the routes modified since the time + * they were scheduled. There are also unicast-destined requested updates, but + * these are sent directly as a reaction to received RIP request message. The + * update session is started by rip_send_table(). There may be at most one + * active update session per interface, as the associated state (including the + * fib iterator) is stored directly in &rip_iface structure. + * + * RIP neighbors are represented by structures &rip_neighbor. Compared to + * neighbor handling in other routing protocols, RIP does not have explicit + * neighbor discovery and adjacency maintenance, which makes the &rip_neighbor + * related code a bit peculiar. RIP neighbors are interlinked with core neighbor + * structures (&neighbor) and use core neighbor notifications to ensure that RIP + * neighbors are timely removed. RIP neighbors are added based on received route + * notifications and removed based on core neighbor and RIP interface events. + * + * RIP neighbors are linked by RIP routes and use counter to track the number of + * associated routes, but when these RIP routes timeout, associated RIP neighbor + * is still alive (with zero counter). When RIP neighbor is removed but still + * has some associated routes, it is not freed, just changed to detached state + * (core neighbors and RIP ifaces are unlinked), then during the main timer + * cleanup phase the associated routes are removed and the &rip_neighbor + * structure is finally freed. + * + * Supported standards: + * - RFC 1058 - RIPv1 + * - RFC 2453 - RIPv2 + * - RFC 2080 - RIPng + * - RFC 4822 - RIP cryptographic authentication */ -#undef LOCAL_DEBUG -#define LOCAL_DEBUG 1 - -#include "nest/bird.h" -#include "nest/iface.h" -#include "nest/protocol.h" -#include "nest/route.h" -#include "lib/socket.h" -#include "lib/resource.h" -#include "lib/lists.h" -#include "lib/timer.h" -#include "lib/string.h" - +#include #include "rip.h" -#define P ((struct rip_proto *) p) -#define P_CF ((struct rip_proto_config *)p->cf) -#undef TRACE -#define TRACE(level, msg, args...) do { if (p->debug & level) { log(L_TRACE "%s: " msg, p->name , ## args); } } while(0) +static inline void rip_lock_neighbor(struct rip_neighbor *n); +static inline void rip_unlock_neighbor(struct rip_neighbor *n); +static inline int rip_iface_link_up(struct rip_iface *ifa); +static inline void rip_kick_timer(struct rip_proto *p); +static inline void rip_iface_kick_timer(struct rip_iface *ifa); +static void rip_iface_timer(timer *timer); +static void rip_trigger_update(struct rip_proto *p); -static struct rip_interface *new_iface(struct proto *p, struct iface *new, unsigned long flags, struct iface_patt *patt); /* - * Output processing - * - * This part is responsible for getting packets out to the network. + * RIP routes */ static void -rip_tx_err( sock *s, int err ) +rip_init_entry(struct fib_node *fn) { - struct rip_connection *c = ((struct rip_interface *)(s->data))->busy; - struct proto *p = c->proto; - log( L_ERR "%s: Unexpected error at rip transmit: %M", p->name, err ); + // struct rip_entry *en = (void) *fn; + + const uint offset = OFFSETOF(struct rip_entry, routes); + memset((byte *)fn + offset, 0, sizeof(struct rip_entry) - offset); } -/* - * rip_tx_prepare: - * @e: rip entry that needs to be translated to form suitable for network - * @b: block to be filled +static struct rip_rte * +rip_add_rte(struct rip_proto *p, struct rip_rte **rp, struct rip_rte *src) +{ + struct rip_rte *rt = sl_alloc(p->rte_slab); + + memcpy(rt, src, sizeof(struct rip_rte)); + rt->next = *rp; + *rp = rt; + + rip_lock_neighbor(rt->from); + + return rt; +} + +static inline void +rip_remove_rte(struct rip_proto *p, struct rip_rte **rp) +{ + struct rip_rte *rt = *rp; + + rip_unlock_neighbor(rt->from); + + *rp = rt->next; + sl_free(p->rte_slab, rt); +} + +static inline int rip_same_rte(struct rip_rte *a, struct rip_rte *b) +{ return a->metric == b->metric && a->tag == b->tag && ipa_equal(a->next_hop, b->next_hop); } + +static inline int rip_valid_rte(struct rip_rte *rt) +{ return rt->from->ifa != NULL; } + +/** + * rip_announce_rte - announce route from RIP routing table to the core + * @p: RIP instance + * @en: related network * - * Fill one rip block with info that needs to go to the network. Handle - * nexthop and split horizont correctly. (Next hop is ignored for IPv6, - * that could be fixed but it is not real problem). - */ -static int -rip_tx_prepare(struct proto *p, struct rip_block *b, struct rip_entry *e, struct rip_interface *rif, int pos ) -{ - int metric; - DBG( "." ); - b->tag = htons( e->tag ); - b->network = e->n.prefix; - metric = e->metric; - if (neigh_connected_to(p, &e->whotoldme, rif->iface)) { - DBG( "(split horizon)" ); - metric = P_CF->infinity; - } -#ifndef IPV6 - b->family = htons( 2 ); /* AF_INET */ - b->netmask = ipa_mkmask( e->n.pxlen ); - ipa_hton( b->netmask ); - - if (neigh_connected_to(p, &e->nexthop, rif->iface)) - b->nexthop = e->nexthop; - else - b->nexthop = IPA_NONE; - ipa_hton( b->nexthop ); - b->metric = htonl( metric ); -#else - b->pxlen = e->n.pxlen; - b->metric = metric; /* it is u8 */ -#endif - - ipa_hton( b->network ); - - return pos+1; -} - -/* - * rip_tx - send one rip packet to the network + * The function takes a list of incoming routes from @en, prepare appropriate + * &rte for the core and propagate it by rte_update(). */ static void -rip_tx( sock *s ) +rip_announce_rte(struct rip_proto *p, struct rip_entry *en) { - struct rip_interface *rif = s->data; - struct rip_connection *c = rif->busy; - struct proto *p = c->proto; - struct rip_packet *packet = (void *) s->tbuf; - int i, packetlen; - int maxi, nullupdate = 1; + struct rip_rte *rt = en->routes; - DBG( "Sending to %I\n", s->daddr ); - do { + /* Find first valid rte */ + while (rt && !rip_valid_rte(rt)) + rt = rt->next; - if (c->done) - goto done; + if (rt) + { + /* Update */ + net *n = net_get(p->p.table, en->n.prefix, en->n.pxlen); - DBG( "Preparing packet to send: " ); + rta a0 = { + .src = p->p.main_source, + .source = RTS_RIP, + .scope = SCOPE_UNIVERSE, + .cast = RTC_UNICAST + }; - packet->heading.command = RIPCMD_RESPONSE; -#ifndef IPV6 - packet->heading.version = RIP_V2; -#else - packet->heading.version = RIP_NG; -#endif - packet->heading.unused = 0; + u8 rt_metric = rt->metric; + u16 rt_tag = rt->tag; + struct rip_rte *rt2 = rt->next; - i = !!P_CF->authtype; -#ifndef IPV6 - maxi = ((P_CF->authtype == AT_MD5) ? PACKET_MD5_MAX : PACKET_MAX); -#else - maxi = 5; /* We need to have at least reserve of one at end of packet */ -#endif - - FIB_ITERATE_START(&P->rtable, &c->iter, z) { - struct rip_entry *e = (struct rip_entry *) z; + /* Find second valid rte */ + while (rt2 && !rip_valid_rte(rt2)) + rt2 = rt2->next; - if (!rif->triggered || (!(e->updated < now-2))) { /* FIXME: Should be probably 1 or some different algorithm */ - nullupdate = 0; - i = rip_tx_prepare( p, packet->block + i, e, rif, i ); - if (i >= maxi) { - FIB_ITERATE_PUT(&c->iter, z); - goto break_loop; - } + if (p->ecmp && rt2) + { + /* ECMP route */ + struct mpnh *nhs = NULL; + struct mpnh **nhp = &nhs; + int num = 0; + + for (rt = en->routes; rt && (num < p->ecmp); rt = rt->next) + { + if (!rip_valid_rte(rt)) + continue; + + struct mpnh *nh = alloca(sizeof(struct mpnh)); + nh->gw = rt->next_hop; + nh->iface = rt->from->nbr->iface; + nh->weight = rt->from->ifa->cf->ecmp_weight; + nh->next = NULL; + *nhp = nh; + nhp = &(nh->next); + num++; + + if (rt->tag != rt_tag) + rt_tag = 0; } - } FIB_ITERATE_END(z); - c->done = 1; - break_loop: - - packetlen = rip_outgoing_authentication(p, (void *) &packet->block[0], packet, i); - - DBG( ", sending %d blocks, ", i ); - if (nullupdate) { - DBG( "not sending NULL update\n" ); - c->done = 1; - goto done; + a0.dest = RTD_MULTIPATH; + a0.nexthops = nhs; } - if (ipa_nonzero(c->daddr)) - i = sk_send_to( s, packetlen, c->daddr, c->dport ); else - i = sk_send( s, packetlen ); + { + /* Unipath route */ + a0.dest = RTD_ROUTER; + a0.gw = rt->next_hop; + a0.iface = rt->from->nbr->iface; + a0.from = rt->from->nbr->addr; + } - DBG( "it wants more\n" ); - - } while (i>0); - - if (i<0) rip_tx_err( s, i ); - DBG( "blocked\n" ); - return; + rta *a = rta_lookup(&a0); + rte *e = rte_get_temp(a); -done: - DBG( "Looks like I'm" ); - c->rif->busy = NULL; - rem_node(NODE c); - mb_free(c); - DBG( " done\n" ); - return; + e->u.rip.from = a0.iface; + e->u.rip.metric = rt_metric; + e->u.rip.tag = rt_tag; + + e->net = n; + e->pflags = 0; + + rte_update(&p->p, n, e); + } + else + { + /* Withdraw */ + net *n = net_find(p->p.table, en->n.prefix, en->n.pxlen); + rte_update(&p->p, n, NULL); + } } -/* - * rip_sendto - send whole routing table to selected destination - * @rif: interface to use. Notice that we lock interface so that at - * most one send to one interface is done. +/** + * rip_update_rte - enter a route update to RIP routing table + * @p: RIP instance + * @prefix: network prefix + * @pxlen: network prefix length + * @new: a &rip_rte representing the new route + * + * The function is called by the RIP packet processing code whenever it receives + * a reachable route. The appropriate routing table entry is found and the list + * of incoming routes is updated. Eventually, the change is also propagated to + * the core by rip_announce_rte(). Note that for unreachable routes, + * rip_withdraw_rte() should be called instead of rip_update_rte(). + */ +void +rip_update_rte(struct rip_proto *p, ip_addr *prefix, int pxlen, struct rip_rte *new) +{ + struct rip_entry *en = fib_get(&p->rtable, prefix, pxlen); + struct rip_rte *rt, **rp; + int changed = 0; + + /* If the new route is better, remove all current routes */ + if (en->routes && new->metric < en->routes->metric) + while (en->routes) + rip_remove_rte(p, &en->routes); + + /* Find the old route (also set rp for later) */ + for (rp = &en->routes; rt = *rp; rp = &rt->next) + if (rt->from == new->from) + { + if (rip_same_rte(rt, new)) + { + rt->expires = new->expires; + return; + } + + /* Remove the old route */ + rip_remove_rte(p, rp); + changed = 1; + break; + } + + /* If the new route is optimal, add it to the list */ + if (!en->routes || new->metric == en->routes->metric) + { + rt = rip_add_rte(p, rp, new); + changed = 1; + } + + /* Announce change if on relevant position (the first or any for ECMP) */ + if (changed && (rp == &en->routes || p->ecmp)) + rip_announce_rte(p, en); +} + +/** + * rip_withdraw_rte - enter a route withdraw to RIP routing table + * @p: RIP instance + * @prefix: network prefix + * @pxlen: network prefix length + * @from: a &rip_neighbor propagating the withdraw + * + * The function is called by the RIP packet processing code whenever it receives + * an unreachable route. The incoming route for given network from nbr @from is + * removed. Eventually, the change is also propagated by rip_announce_rte(). + */ +void +rip_withdraw_rte(struct rip_proto *p, ip_addr *prefix, int pxlen, struct rip_neighbor *from) +{ + struct rip_entry *en = fib_find(&p->rtable, prefix, pxlen); + struct rip_rte *rt, **rp; + + if (!en) + return; + + /* Find the old route */ + for (rp = &en->routes; rt = *rp; rp = &rt->next) + if (rt->from == from) + break; + + if (!rt) + return; + + /* Remove the old route */ + rip_remove_rte(p, rp); + + /* Announce change if on relevant position */ + if (rp == &en->routes || p->ecmp) + rip_announce_rte(p, en); +} + +/* + * rip_rt_notify - core tells us about new route, so store + * it into our data structures. */ static void -rip_sendto( struct proto *p, ip_addr daddr, int dport, struct rip_interface *rif ) +rip_rt_notify(struct proto *P, struct rtable *table UNUSED, struct network *net, struct rte *new, + struct rte *old UNUSED, struct ea_list *attrs) { - struct iface *iface = rif->iface; - struct rip_connection *c; - static int num = 0; + struct rip_proto *p = (struct rip_proto *) P; + struct rip_entry *en; + int old_metric; - if (rif->busy) { - log (L_WARN "%s: Interface %s is much too slow, dropping request", p->name, iface->name); + if (new) + { + /* Update */ + u32 rt_metric = ea_get_int(attrs, EA_RIP_METRIC, 1); + u32 rt_tag = ea_get_int(attrs, EA_RIP_TAG, 0); + + if (rt_metric > p->infinity) + { + log(L_WARN "%s: Invalid rip_metric value %u for route %I/%d", + p->p.name, rt_metric, net->n.prefix, net->n.pxlen); + rt_metric = p->infinity; + } + + if (rt_tag > 0xffff) + { + log(L_WARN "%s: Invalid rip_tag value %u for route %I/%d", + p->p.name, rt_tag, net->n.prefix, net->n.pxlen); + rt_metric = p->infinity; + rt_tag = 0; + } + + /* + * Note that we accept exported routes with infinity metric (this could + * happen if rip_metric is modified in filters). Such entry has infinity + * metric but is RIP_ENTRY_VALID and therefore is not subject to garbage + * collection. + */ + + en = fib_get(&p->rtable, &net->n.prefix, net->n.pxlen); + + old_metric = en->valid ? en->metric : -1; + + en->valid = RIP_ENTRY_VALID; + en->metric = rt_metric; + en->tag = rt_tag; + en->from = (new->attrs->src->proto == P) ? new->u.rip.from : NULL; + en->iface = new->attrs->iface; + en->next_hop = new->attrs->gw; + } + else + { + /* Withdraw */ + en = fib_find(&p->rtable, &net->n.prefix, net->n.pxlen); + + if (!en || en->valid != RIP_ENTRY_VALID) + return; + + old_metric = en->metric; + + en->valid = RIP_ENTRY_STALE; + en->metric = p->infinity; + en->tag = 0; + en->from = NULL; + en->iface = NULL; + en->next_hop = IPA_NONE; + } + + /* Activate triggered updates */ + if (en->metric != old_metric) + { + en->changed = now; + rip_trigger_update(p); + } +} + + +/* + * RIP neighbors + */ + +struct rip_neighbor * +rip_get_neighbor(struct rip_proto *p, ip_addr *a, struct rip_iface *ifa) +{ + neighbor *nbr = neigh_find2(&p->p, a, ifa->iface, 0); + + if (!nbr || (nbr->scope == SCOPE_HOST) || !rip_iface_link_up(ifa)) + return NULL; + + if (nbr->data) + return nbr->data; + + TRACE(D_EVENTS, "New neighbor %I on %s", *a, ifa->iface->name); + + struct rip_neighbor *n = mb_allocz(p->p.pool, sizeof(struct rip_neighbor)); + n->ifa = ifa; + n->nbr = nbr; + nbr->data = n; + n->csn = nbr->aux; + + add_tail(&ifa->neigh_list, NODE n); + + return n; +} + +static void +rip_remove_neighbor(struct rip_proto *p, struct rip_neighbor *n) +{ + neighbor *nbr = n->nbr; + + TRACE(D_EVENTS, "Removing neighbor %I on %s", nbr->addr, nbr->iface->name); + + rem_node(NODE n); + n->ifa = NULL; + n->nbr = NULL; + nbr->data = NULL; + nbr->aux = n->csn; + + rfree(n->bfd_req); + n->bfd_req = NULL; + n->last_seen = 0; + + if (!n->uc) + mb_free(n); + + /* Related routes are removed in rip_timer() */ + rip_kick_timer(p); +} + +static inline void +rip_lock_neighbor(struct rip_neighbor *n) +{ + n->uc++; +} + +static inline void +rip_unlock_neighbor(struct rip_neighbor *n) +{ + n->uc--; + + if (!n->nbr && !n->uc) + mb_free(n); +} + +static void +rip_neigh_notify(struct neighbor *nbr) +{ + struct rip_proto *p = (struct rip_proto *) nbr->proto; + struct rip_neighbor *n = nbr->data; + + if (!n) + return; + + /* + * We assume that rip_neigh_notify() is called before rip_if_notify() for + * IF_CHANGE_DOWN and therefore n->ifa is still valid. We have no such + * ordering assumption for IF_CHANGE_LINK, so we test link state of the + * underlying iface instead of just rip_iface state. + */ + if ((nbr->scope <= 0) || !rip_iface_link_up(n->ifa)) + rip_remove_neighbor(p, n); +} + +static void +rip_bfd_notify(struct bfd_request *req) +{ + struct rip_neighbor *n = req->data; + struct rip_proto *p = n->ifa->rip; + + if (req->down) + { + TRACE(D_EVENTS, "BFD session down for nbr %I on %s", + n->nbr->addr, n->ifa->iface->name); + rip_remove_neighbor(p, n); + } +} + +void +rip_update_bfd(struct rip_proto *p, struct rip_neighbor *n) +{ + int use_bfd = n->ifa->cf->bfd && n->last_seen; + + if (use_bfd && !n->bfd_req) + { + /* + * For RIPv2, use the same address as rip_open_socket(). For RIPng, neighbor + * should contain an address from the same prefix, thus also link-local. It + * may cause problems if two link-local addresses are assigned to one iface. + */ + ip_addr saddr = rip_is_v2(p) ? n->ifa->sk->saddr : n->nbr->ifa->ip; + n->bfd_req = bfd_request_session(p->p.pool, n->nbr->addr, saddr, + n->nbr->iface, rip_bfd_notify, n); + } + + if (!use_bfd && n->bfd_req) + { + rfree(n->bfd_req); + n->bfd_req = NULL; + } +} + + +/* + * RIP interfaces + */ + +static void +rip_iface_start(struct rip_iface *ifa) +{ + struct rip_proto *p = ifa->rip; + + TRACE(D_EVENTS, "Starting interface %s", ifa->iface->name); + + ifa->next_regular = now + (random() % ifa->cf->update_time) + 1; + ifa->next_triggered = now; /* Available immediately */ + ifa->want_triggered = 1; /* All routes in triggered update */ + tm_start(ifa->timer, 1); /* Or 100 ms */ + ifa->up = 1; + + if (!ifa->cf->passive) + rip_send_request(ifa->rip, ifa); +} + +static void +rip_iface_stop(struct rip_iface *ifa) +{ + struct rip_proto *p = ifa->rip; + struct rip_neighbor *n; + + TRACE(D_EVENTS, "Stopping interface %s", ifa->iface->name); + + rip_reset_tx_session(p, ifa); + + WALK_LIST_FIRST(n, ifa->neigh_list) + rip_remove_neighbor(p, n); + + tm_stop(ifa->timer); + ifa->up = 0; +} + +static inline int +rip_iface_link_up(struct rip_iface *ifa) +{ + return !ifa->cf->check_link || (ifa->iface->flags & IF_LINK_UP); +} + +static void +rip_iface_update_state(struct rip_iface *ifa) +{ + int up = ifa->sk && rip_iface_link_up(ifa); + + if (up == ifa->up) + return; + + if (up) + rip_iface_start(ifa); + else + rip_iface_stop(ifa); +} + +static void +rip_iface_update_buffers(struct rip_iface *ifa) +{ + if (!ifa->sk) + return; + + uint rbsize = ifa->cf->rx_buffer ?: ifa->iface->mtu; + uint tbsize = ifa->cf->tx_length ?: ifa->iface->mtu; + rbsize = MAX(rbsize, tbsize); + + sk_set_rbsize(ifa->sk, rbsize); + sk_set_tbsize(ifa->sk, tbsize); + + uint headers = (rip_is_v2(ifa->rip) ? IP4_HEADER_LENGTH : IP6_HEADER_LENGTH) + UDP_HEADER_LENGTH; + ifa->tx_plen = tbsize - headers; + + if (ifa->cf->auth_type == RIP_AUTH_CRYPTO) + ifa->tx_plen -= RIP_AUTH_TAIL_LENGTH; +} + +static inline void +rip_iface_update_bfd(struct rip_iface *ifa) +{ + struct rip_proto *p = ifa->rip; + struct rip_neighbor *n; + + WALK_LIST(n, ifa->neigh_list) + rip_update_bfd(p, n); +} + + +static void +rip_iface_locked(struct object_lock *lock) +{ + struct rip_iface *ifa = lock->data; + struct rip_proto *p = ifa->rip; + + if (!rip_open_socket(ifa)) + { + log(L_ERR "%s: Cannot open socket for %s", p->p.name, ifa->iface->name); return; } - c = mb_alloc( p->pool, sizeof( struct rip_connection )); - rif->busy = c; - - c->addr = daddr; - c->proto = p; - c->num = num++; - c->rif = rif; - c->dport = dport; - c->daddr = daddr; - if (c->rif->sock->data != rif) - bug("not enough send magic"); - - c->done = 0; - FIB_ITERATE_INIT( &c->iter, &P->rtable ); - add_head( &P->connections, NODE c ); - if (ipa_nonzero(daddr)) - TRACE(D_PACKETS, "Sending my routing table to %I:%d on %s", daddr, dport, rif->iface->name ); - else - TRACE(D_PACKETS, "Broadcasting routing table to %s", rif->iface->name ); - - rip_tx(c->rif->sock); + rip_iface_update_buffers(ifa); + rip_iface_update_state(ifa); } -static struct rip_interface* -find_interface(struct proto *p, struct iface *what) -{ - struct rip_interface *i; - WALK_LIST (i, P->interfaces) - if (i->iface == what) - return i; +static struct rip_iface * +rip_find_iface(struct rip_proto *p, struct iface *what) +{ + struct rip_iface *ifa; + + WALK_LIST(ifa, p->iface_list) + if (ifa->iface == what) + return ifa; + return NULL; } -/* - * Input processing - * - * This part is responsible for any updates that come from network - */ - -static int rip_rte_better(struct rte *new, struct rte *old); - static void -rip_rte_update_if_better(rtable *tab, net *net, struct proto *p, rte *new) +rip_add_iface(struct rip_proto *p, struct iface *iface, struct rip_iface_config *ic) { - rte *old; + struct rip_iface *ifa; - old = rte_find(net, p->main_source); - if (!old || rip_rte_better(new, old) || - (ipa_equal(old->attrs->from, new->attrs->from) && - (old->u.rip.metric != new->u.rip.metric)) ) - rte_update(p, net, new); - else - rte_free(new); + TRACE(D_EVENTS, "Adding interface %s", iface->name); + + ifa = mb_allocz(p->p.pool, sizeof(struct rip_iface)); + ifa->rip = p; + ifa->iface = iface; + ifa->cf = ic; + + if (ipa_nonzero(ic->address)) + ifa->addr = ic->address; + else if (ic->mode == RIP_IM_MULTICAST) + ifa->addr = rip_is_v2(p) ? IP4_RIP_ROUTERS : IP6_RIP_ROUTERS; + else /* Broadcast */ + ifa->addr = iface->addr->brd; + + init_list(&ifa->neigh_list); + + add_tail(&p->iface_list, NODE ifa); + + ifa->timer = tm_new_set(p->p.pool, rip_iface_timer, ifa, 0, 0); + + struct object_lock *lock = olock_new(p->p.pool); + lock->type = OBJLOCK_UDP; + lock->port = ic->port; + lock->iface = iface; + lock->data = ifa; + lock->hook = rip_iface_locked; + ifa->lock = lock; + + olock_acquire(lock); } -/* - * advertise_entry - let main routing table know about our new entry - * @b: entry in network format - * - * This basically translates @b to format used by bird core and feeds - * bird core with this route. - */ static void -advertise_entry( struct proto *p, struct rip_block *b, ip_addr whotoldme, struct iface *iface ) +rip_remove_iface(struct rip_proto *p, struct rip_iface *ifa) { - rta *a, A; - rte *r; - net *n; - neighbor *neighbor; - struct rip_interface *rif; - int pxlen; + rip_iface_stop(ifa); - bzero(&A, sizeof(A)); - A.src= p->main_source; - A.source = RTS_RIP; - A.scope = SCOPE_UNIVERSE; - A.cast = RTC_UNICAST; - A.dest = RTD_ROUTER; - A.flags = 0; -#ifndef IPV6 - A.gw = ipa_nonzero(b->nexthop) ? b->nexthop : whotoldme; - pxlen = ipa_masklen(b->netmask); -#else - /* FIXME: next hop is in other packet for v6 */ - A.gw = whotoldme; - pxlen = b->pxlen; -#endif - A.from = whotoldme; + TRACE(D_EVENTS, "Removing interface %s", ifa->iface->name); - /* No need to look if destination looks valid - ie not net 0 or 127 -- core will do for us. */ + rem_node(NODE ifa); - neighbor = neigh_find2( p, &A.gw, iface, 0 ); - if (!neighbor) { - log( L_REMOTE "%s: %I asked me to route %I/%d using not-neighbor %I.", p->name, A.from, b->network, pxlen, A.gw ); - return; - } - if (neighbor->scope == SCOPE_HOST) { - DBG("Self-destined route, ignoring.\n"); - return; - } + rfree(ifa->sk); + rfree(ifa->lock); + rfree(ifa->timer); - A.iface = neighbor->iface; - if (!(rif = neighbor->data)) { - rif = neighbor->data = find_interface(p, A.iface); - } - if (!rif) - bug("Route packet using unknown interface? No."); - - /* set to: interface of nexthop */ - a = rta_lookup(&A); - if (pxlen==-1) { - log( L_REMOTE "%s: %I gave me invalid pxlen/netmask for %I.", p->name, A.from, b->network ); - return; - } - n = net_get( p->table, b->network, pxlen ); - r = rte_get_temp(a); -#ifndef IPV6 - r->u.rip.metric = ntohl(b->metric) + rif->metric; -#else - r->u.rip.metric = b->metric + rif->metric; -#endif - - r->u.rip.entry = NULL; - if (r->u.rip.metric > P_CF->infinity) r->u.rip.metric = P_CF->infinity; - r->u.rip.tag = ntohl(b->tag); - r->net = n; - r->pflags = 0; /* Here go my flags */ - rip_rte_update_if_better( p->table, n, p, r ); - DBG( "done\n" ); + mb_free(ifa); } -/* - * process_block - do some basic check and pass block to advertise_entry - */ -static void -process_block( struct proto *p, struct rip_block *block, ip_addr whotoldme, struct iface *iface ) -{ - int metric, pxlen; - -#ifndef IPV6 - metric = ntohl( block->metric ); - pxlen = ipa_masklen(block->netmask); -#else - metric = block->metric; - pxlen = block->pxlen; -#endif - ip_addr network = block->network; - - CHK_MAGIC; - - TRACE(D_ROUTES, "block: %I tells me: %I/%d available, metric %d... ", - whotoldme, network, pxlen, metric ); - - if ((!metric) || (metric > P_CF->infinity)) { -#ifdef IPV6 /* Someone is sending us nexthop and we are ignoring it */ - if (metric == 0xff) - { DBG( "IPv6 nexthop ignored" ); return; } -#endif - log( L_WARN "%s: Got metric %d from %I", p->name, metric, whotoldme ); - return; - } - - advertise_entry( p, block, whotoldme, iface ); -} - -#define BAD( x ) { log( L_REMOTE "%s: " x, p->name ); return 1; } - -/* - * rip_process_packet - this is main routine for incoming packets. - */ static int -rip_process_packet( struct proto *p, struct rip_packet *packet, int num, ip_addr whotoldme, int port, struct iface *iface ) +rip_reconfigure_iface(struct rip_proto *p, struct rip_iface *ifa, struct rip_iface_config *new) { - int i; - int authenticated = 0; - neighbor *neighbor; + struct rip_iface_config *old = ifa->cf; - switch( packet->heading.version ) { - case RIP_V1: DBG( "Rip1: " ); break; - case RIP_V2: DBG( "Rip2: " ); break; - default: BAD( "Unknown version" ); - } + /* Change of these options would require to reset the iface socket */ + if ((new->mode != old->mode) || + (new->port != old->port) || + (new->tx_tos != old->tx_tos) || + (new->tx_priority != old->tx_priority) || + (new->ttl_security != old->ttl_security)) + return 0; - switch( packet->heading.command ) { - case RIPCMD_REQUEST: DBG( "Asked to send my routing table\n" ); - if (P_CF->honor == HO_NEVER) - BAD( "They asked me to send routing table, but I was told not to do it" ); + TRACE(D_EVENTS, "Reconfiguring interface %s", ifa->iface->name); - if ((P_CF->honor == HO_NEIGHBOR) && (!neigh_find2( p, &whotoldme, iface, 0 ))) - BAD( "They asked me to send routing table, but he is not my neighbor" ); - rip_sendto( p, whotoldme, port, HEAD(P->interfaces) ); /* no broadcast */ - break; - case RIPCMD_RESPONSE: DBG( "*** Rtable from %I\n", whotoldme ); - if (port != P_CF->port) { - log( L_REMOTE "%s: %I send me routing info from port %d", p->name, whotoldme, port ); - return 1; - } + ifa->cf = new; - if (!(neighbor = neigh_find2( p, &whotoldme, iface, 0 )) || neighbor->scope == SCOPE_HOST) { - log( L_REMOTE "%s: %I send me routing info but he is not my neighbor", p->name, whotoldme ); - return 0; - } + if (ifa->next_regular > (now + new->update_time)) + ifa->next_regular = now + (random() % new->update_time) + 1; - for (i=0; iblock[i]; -#ifndef IPV6 - /* Authentication is not defined for v6 */ - if (block->family == 0xffff) { - if (i) - continue; /* md5 tail has this family */ - if (rip_incoming_authentication(p, (void *) block, packet, num, whotoldme)) - BAD( "Authentication failed" ); - authenticated = 1; - continue; - } -#endif - if ((!authenticated) && (P_CF->authtype != AT_NONE)) - BAD( "Packet is not authenticated and it should be" ); - ipa_ntoh( block->network ); -#ifndef IPV6 - ipa_ntoh( block->netmask ); - ipa_ntoh( block->nexthop ); - if (packet->heading.version == RIP_V1) /* FIXME (nonurgent): switch to disable this? */ - block->netmask = ip4_class_mask(ipa_to_ip4(block->network)); -#endif - process_block( p, block, whotoldme, iface ); - } - break; - case RIPCMD_TRACEON: - case RIPCMD_TRACEOFF: BAD( "I was asked for traceon/traceoff" ); - case 5: BAD( "Some Sun extension around here" ); - default: BAD( "Unknown command" ); - } + if ((new->tx_length != old->tx_length) || (new->rx_buffer != old->rx_buffer)) + rip_iface_update_buffers(ifa); - return 0; -} + if (new->check_link != old->check_link) + rip_iface_update_state(ifa); -/* - * rip_rx - Receive hook: do basic checks and pass packet to rip_process_packet - */ -static int -rip_rx(sock *s, int size) -{ - struct rip_interface *i = s->data; - struct proto *p = i->proto; - struct iface *iface = NULL; - int num; + if (new->bfd != old->bfd) + rip_iface_update_bfd(ifa); - /* In non-listening mode, just ignore packet */ - if (i->mode & IM_NOLISTEN) - return 1; + if (ifa->up) + rip_iface_kick_timer(ifa); -#ifdef IPV6 - if (! i->iface || s->lifindex != i->iface->index) - return 1; - - iface = i->iface; -#endif - - if (i->check_ttl && (s->rcv_ttl < 255)) - { - log( L_REMOTE "%s: Discarding packet with TTL %d (< 255) from %I on %s", - p->name, s->rcv_ttl, s->faddr, i->iface->name); - return 1; - } - - - CHK_MAGIC; - DBG( "RIP: message came: %d bytes from %I via %s\n", size, s->faddr, i->iface ? i->iface->name : "(dummy)" ); - size -= sizeof( struct rip_packet_heading ); - if (size < 0) BAD( "Too small packet" ); - if (size % sizeof( struct rip_block )) BAD( "Odd sized packet" ); - num = size / sizeof( struct rip_block ); - if (num>PACKET_MAX) BAD( "Too many blocks" ); - - if (ipa_equal(i->iface->addr->ip, s->faddr)) { - DBG("My own packet\n"); - return 1; - } - - rip_process_packet( p, (struct rip_packet *) s->rbuf, num, s->faddr, s->fport, iface ); return 1; } -/* - * Interface to BIRD core - */ - static void -rip_dump_entry( struct rip_entry *e ) +rip_reconfigure_ifaces(struct rip_proto *p, struct rip_config *cf) { - debug( "%I told me %d/%d ago: to %I/%d go via %I, metric %d ", - e->whotoldme, e->updated-now, e->changed-now, e->n.prefix, e->n.pxlen, e->nexthop, e->metric ); - debug( "\n" ); + struct iface *iface; + + WALK_LIST(iface, iface_list) + { + if (! (iface->flags & IF_UP)) + continue; + + struct rip_iface *ifa = rip_find_iface(p, iface); + struct rip_iface_config *ic = (void *) iface_patt_find(&cf->patt_list, iface, NULL); + + if (ifa && ic) + { + if (rip_reconfigure_iface(p, ifa, ic)) + continue; + + /* Hard restart */ + log(L_INFO "%s: Restarting interface %s", p->p.name, ifa->iface->name); + rip_remove_iface(p, ifa); + rip_add_iface(p, iface, ic); + } + + if (ifa && !ic) + rip_remove_iface(p, ifa); + + if (!ifa && ic) + rip_add_iface(p, iface, ic); + } } -/** - * rip_timer - * @t: timer - * - * Broadcast routing tables periodically (using rip_tx) and kill - * routes that are too old. RIP keeps a list of its own entries present - * in the core table by a linked list (functions rip_rte_insert() and - * rip_rte_delete() are responsible for that), it walks this list in the timer - * and in case an entry is too old, it is discarded. +static void +rip_if_notify(struct proto *P, unsigned flags, struct iface *iface) +{ + struct rip_proto *p = (void *) P; + struct rip_config *cf = (void *) P->cf; + + if (iface->flags & IF_IGNORE) + return; + + if (flags & IF_CHANGE_UP) + { + struct rip_iface_config *ic = (void *) iface_patt_find(&cf->patt_list, iface, NULL); + + if (ic) + rip_add_iface(p, iface, ic); + + return; + } + + struct rip_iface *ifa = rip_find_iface(p, iface); + + if (!ifa) + return; + + if (flags & IF_CHANGE_DOWN) + { + rip_remove_iface(p, ifa); + return; + } + + if (flags & IF_CHANGE_MTU) + rip_iface_update_buffers(ifa); + + if (flags & IF_CHANGE_LINK) + rip_iface_update_state(ifa); +} + + +/* + * RIP timer events */ +/** + * rip_timer - RIP main timer hook + * @t: timer + * + * The RIP main timer is responsible for routing table maintenance. Invalid or + * expired routes (&rip_rte) are removed and garbage collection of stale routing + * table entries (&rip_entry) is done. Changes are propagated to core tables, + * route reload is also done here. Note that garbage collection uses a maximal + * GC time, while interfaces maintain an illusion of per-interface GC times in + * rip_send_response(). + * + * Keeping incoming routes and the selected outgoing route are two independent + * functions, therefore after garbage collection some entries now considered + * invalid (RIP_ENTRY_DUMMY) still may have non-empty list of incoming routes, + * while some valid entries (representing an outgoing route) may have that list + * empty. + * + * The main timer is not scheduled periodically but it uses the time of the + * current next event and the minimal interval of any possible event to compute + * the time of the next run. + */ static void rip_timer(timer *t) { - struct proto *p = t->data; - struct fib_node *e, *et; + struct rip_proto *p = t->data; + struct rip_config *cf = (void *) (p->p.cf); + struct rip_iface *ifa; + struct rip_neighbor *n, *nn; + struct fib_iterator fit; + bird_clock_t next = now + MIN(cf->min_timeout_time, cf->max_garbage_time); + bird_clock_t expires = 0; - CHK_MAGIC; - DBG( "RIP: tick tock\n" ); - - WALK_LIST_DELSAFE( e, et, P->garbage ) { - rte *rte; - rte = SKIP_BACK( struct rte, u.rip.garbage, e ); + TRACE(D_EVENTS, "Main timer fired"); - CHK_MAGIC; + FIB_ITERATE_INIT(&fit, &p->rtable); - DBG( "Garbage: (%p)", rte ); rte_dump( rte ); - - if (now - rte->lastmod > P_CF->timeout_time) { - TRACE(D_EVENTS, "entry is too old: %I", rte->net->n.prefix ); - if (rte->u.rip.entry) { - rte->u.rip.entry->metric = P_CF->infinity; - rte->u.rip.metric = P_CF->infinity; - } - } - - if (now - rte->lastmod > P_CF->garbage_time) { - TRACE(D_EVENTS, "entry is much too old: %I", rte->net->n.prefix ); - rte_discard(p->table, rte); - } - } - - DBG( "RIP: Broadcasting routing tables\n" ); + loop: + FIB_ITERATE_START(&p->rtable, &fit, node) { - struct rip_interface *rif; + struct rip_entry *en = (struct rip_entry *) node; + struct rip_rte *rt, **rp; + int changed = 0; - if ( P_CF->period > 2 ) { /* Bring some randomness into sending times */ - if (! (P->tx_count % P_CF->period)) P->rnd_count = random_u32() % 2; - } else P->rnd_count = P->tx_count % P_CF->period; + /* Checking received routes for timeout and for dead neighbors */ + for (rp = &en->routes; rt = *rp; /* rp = &rt->next */) + { + if (!rip_valid_rte(rt) || (rt->expires <= now)) + { + rip_remove_rte(p, rp); + changed = 1; + continue; + } - WALK_LIST( rif, P->interfaces ) { - struct iface *iface = rif->iface; - - if (!iface) continue; - if (rif->mode & IM_QUIET) continue; - if (!(iface->flags & IF_UP)) continue; - rif->triggered = P->rnd_count; - - rip_sendto( p, IPA_NONE, 0, rif ); + next = MIN(next, rt->expires); + rp = &rt->next; + } + + /* Propagating eventual change */ + if (changed || p->rt_reload) + { + /* + * We have to restart the iteration because there may be a cascade of + * synchronous events rip_announce_rte() -> nest table change -> + * rip_rt_notify() -> p->rtable change, invalidating hidden variables. + */ + + FIB_ITERATE_PUT_NEXT(&fit, &p->rtable, node); + rip_announce_rte(p, en); + goto loop; + } + + /* Checking stale entries for garbage collection timeout */ + if (en->valid == RIP_ENTRY_STALE) + { + expires = en->changed + cf->max_garbage_time; + + if (expires <= now) + { + // TRACE(D_EVENTS, "entry is too old: %I/%d", en->n.prefix, en->n.pxlen); + en->valid = 0; + } + else + next = MIN(next, expires); + } + + /* Remove empty nodes */ + if (!en->valid && !en->routes) + { + FIB_ITERATE_PUT(&fit, node); + fib_delete(&p->rtable, node); + goto loop; } - P->tx_count++; - P->rnd_count--; } + FIB_ITERATE_END(node); - DBG( "RIP: tick tock done\n" ); + p->rt_reload = 0; + + /* Handling neighbor expiration */ + WALK_LIST(ifa, p->iface_list) + WALK_LIST_DELSAFE(n, nn, ifa->neigh_list) + if (n->last_seen) + { + expires = n->last_seen + n->ifa->cf->timeout_time; + + if (expires <= now) + rip_remove_neighbor(p, n); + else + next = MIN(next, expires); + } + + tm_start(p->timer, MAX(next - now, 1)); } -/* - * rip_start - initialize instance of rip - */ -static int -rip_start(struct proto *p) +static inline void +rip_kick_timer(struct rip_proto *p) { - struct rip_interface *rif; - DBG( "RIP: starting instance...\n" ); - - ASSERT(sizeof(struct rip_packet_heading) == 4); - ASSERT(sizeof(struct rip_block) == 20); - ASSERT(sizeof(struct rip_block_auth) == 20); - -#ifdef LOCAL_DEBUG - P->magic = RIP_MAGIC; -#endif - fib_init( &P->rtable, p->pool, sizeof( struct rip_entry ), 0, NULL ); - init_list( &P->connections ); - init_list( &P->garbage ); - init_list( &P->interfaces ); - P->timer = tm_new( p->pool ); - P->timer->data = p; - P->timer->recurrent = 1; - P->timer->hook = rip_timer; - tm_start( P->timer, 2 ); - rif = new_iface(p, NULL, 0, NULL); /* Initialize dummy interface */ - add_head( &P->interfaces, NODE rif ); - CHK_MAGIC; - - DBG( "RIP: ...done\n"); - return PS_UP; -} - -static void -rip_dump(struct proto *p) -{ - int i; - node *w; - struct rip_interface *rif; - - CHK_MAGIC; - WALK_LIST( w, P->connections ) { - struct rip_connection *n = (void *) w; - debug( "RIP: connection #%d: %I\n", n->num, n->addr ); - } - i = 0; - FIB_WALK( &P->rtable, e ) { - debug( "RIP: entry #%d: ", i++ ); - rip_dump_entry( (struct rip_entry *)e ); - } FIB_WALK_END; - i = 0; - WALK_LIST( rif, P->interfaces ) { - debug( "RIP: interface #%d: %s, %I, busy = %x\n", i++, rif->iface?rif->iface->name:"(dummy)", rif->sock->daddr, rif->busy ); - } -} - -static void -rip_get_route_info(rte *rte, byte *buf, ea_list *attrs) -{ - eattr *metric = ea_find(attrs, EA_RIP_METRIC); - eattr *tag = ea_find(attrs, EA_RIP_TAG); - - buf += bsprintf(buf, " (%d/%d)", rte->pref, metric ? metric->u.data : 0); - if (tag && tag->u.data) - bsprintf(buf, " t%04x", tag->u.data); -} - -static void -kill_iface(struct rip_interface *i) -{ - DBG( "RIP: Interface %s disappeared\n", i->iface->name); - rfree(i->sock); - mb_free(i); + if (p->timer->expires > (now + 1)) + tm_start(p->timer, 1); /* Or 100 ms */ } /** - * new_iface - * @p: myself - * @new: interface to be created or %NULL if we are creating a magic - * socket. The magic socket is used for listening and also for - * sending requested responses. - * @flags: interface flags - * @patt: pattern this interface matched, used for access to config options + * rip_iface_timer - RIP interface timer hook + * @t: timer * - * Create an interface structure and start listening on the interface. + * RIP interface timers are responsible for scheduling both regular and + * triggered updates. Fixed, delay-independent period is used for regular + * updates, while minimal separating interval is enforced for triggered updates. + * The function also ensures that a new update is not started when the old one + * is still running. */ -static struct rip_interface * -new_iface(struct proto *p, struct iface *new, unsigned long flags, struct iface_patt *patt ) -{ - struct rip_interface *rif; - struct rip_patt *PATT = (struct rip_patt *) patt; - - rif = mb_allocz(p->pool, sizeof( struct rip_interface )); - rif->iface = new; - rif->proto = p; - rif->busy = NULL; - if (PATT) { - rif->mode = PATT->mode; - rif->metric = PATT->metric; - rif->multicast = (!(PATT->mode & IM_BROADCAST)) && (flags & IF_MULTICAST); - rif->check_ttl = (PATT->ttl_security == 1); - } - /* lookup multicasts over unnumbered links - no: rip is not defined over unnumbered links */ - - if (rif->multicast) - DBG( "Doing multicasts!\n" ); - - rif->sock = sk_new( p->pool ); - rif->sock->type = SK_UDP; - rif->sock->sport = P_CF->port; - rif->sock->rx_hook = rip_rx; - rif->sock->data = rif; - rif->sock->rbsize = 10240; - rif->sock->iface = new; /* Automagically works for dummy interface */ - rif->sock->tbuf = mb_alloc( p->pool, sizeof( struct rip_packet )); - rif->sock->tx_hook = rip_tx; - rif->sock->err_hook = rip_tx_err; - rif->sock->daddr = IPA_NONE; - rif->sock->dport = P_CF->port; - if (new) - { - rif->sock->tos = PATT->tx_tos; - rif->sock->priority = PATT->tx_priority; - rif->sock->ttl = PATT->ttl_security ? 255 : 1; - rif->sock->flags = SKF_LADDR_RX | (rif->check_ttl ? SKF_TTL_RX : 0); - } - - if (new) { - if (new->addr->flags & IA_PEER) - log( L_WARN "%s: rip is not defined over unnumbered links", p->name ); - if (rif->multicast) { -#ifndef IPV6 - rif->sock->daddr = ipa_from_u32(0xe0000009); -#else - rif->sock->daddr = IP6_RIP_ROUTERS; -#endif - } else { - rif->sock->daddr = new->addr->brd; - } - } - - if (!ipa_nonzero(rif->sock->daddr)) { - if (rif->iface) - log( L_WARN "%s: interface %s is too strange for me", p->name, rif->iface->name ); - } else { - - if (sk_open(rif->sock) < 0) - goto err; - - if (rif->multicast) - { - if (sk_setup_multicast(rif->sock) < 0) - goto err; - if (sk_join_group(rif->sock, rif->sock->daddr) < 0) - goto err; - } - else - { - if (sk_setup_broadcast(rif->sock) < 0) - goto err; - } - } - - TRACE(D_EVENTS, "Listening on %s, port %d, mode %s (%I)", rif->iface ? rif->iface->name : "(dummy)", P_CF->port, rif->multicast ? "multicast" : "broadcast", rif->sock->daddr ); - - return rif; - - err: - sk_log_error(rif->sock, p->name); - log(L_ERR "%s: Cannot open socket for %s", p->name, rif->iface ? rif->iface->name : "(dummy)" ); - if (rif->iface) { - rfree(rif->sock); - mb_free(rif); - return NULL; - } - /* On dummy, we just return non-working socket, so that user gets error every time anyone requests table */ - return rif; -} - static void -rip_real_if_add(struct object_lock *lock) +rip_iface_timer(timer *t) { - struct iface *iface = lock->iface; - struct proto *p = lock->data; - struct rip_interface *rif; - struct iface_patt *k = iface_patt_find(&P_CF->iface_list, iface, iface->addr); + struct rip_iface *ifa = t->data; + struct rip_proto *p = ifa->rip; + bird_clock_t period = ifa->cf->update_time; - if (!k) - bug("This can not happen! It existed few seconds ago!" ); - DBG("adding interface %s\n", iface->name ); - rif = new_iface(p, iface, iface->flags, k); - if (rif) { - add_head( &P->interfaces, NODE rif ); - DBG("Adding object lock of %p for %p\n", lock, rif); - rif->lock = lock; - } else { rfree(lock); } -} - -static void -rip_if_notify(struct proto *p, unsigned c, struct iface *iface) -{ - DBG( "RIP: if notify\n" ); - if (iface->flags & IF_IGNORE) + if (ifa->cf->passive) return; - if (c & IF_CHANGE_DOWN) { - struct rip_interface *i; - i = find_interface(p, iface); - if (i) { - rem_node(NODE i); - rfree(i->lock); - kill_iface(i); - } - } - if (c & IF_CHANGE_UP) { - struct iface_patt *k = iface_patt_find(&P_CF->iface_list, iface, iface->addr); - struct object_lock *lock; - struct rip_patt *PATT = (struct rip_patt *) k; - if (!k) return; /* We are not interested in this interface */ + TRACE(D_EVENTS, "Interface timer fired for %s", ifa->iface->name); - lock = olock_new( p->pool ); - if (!(PATT->mode & IM_BROADCAST) && (iface->flags & IF_MULTICAST)) -#ifndef IPV6 - lock->addr = ipa_from_u32(0xe0000009); -#else - lock->addr = IP6_RIP_ROUTERS; -#endif - else - lock->addr = iface->addr->brd; - lock->port = P_CF->port; - lock->iface = iface; - lock->hook = rip_real_if_add; - lock->data = p; - lock->type = OBJLOCK_UDP; - olock_acquire(lock); + if (ifa->tx_active) + { + if (now < (ifa->next_regular + period)) + { tm_start(ifa->timer, 1); return; } + + /* We are too late, reset is done by rip_send_table() */ + log(L_WARN "%s: Too slow update on %s, resetting", p->p.name, ifa->iface->name); } + + if (now >= ifa->next_regular) + { + /* Send regular update, set timer for next period (or following one if necessay) */ + TRACE(D_EVENTS, "Sending regular updates for %s", ifa->iface->name); + rip_send_table(p, ifa, ifa->addr, 0); + ifa->next_regular += period * (1 + ((now - ifa->next_regular) / period)); + ifa->want_triggered = 0; + p->triggered = 0; + } + else if (ifa->want_triggered && (now >= ifa->next_triggered)) + { + /* Send triggered update, enforce interval between triggered updates */ + TRACE(D_EVENTS, "Sending triggered updates for %s", ifa->iface->name); + rip_send_table(p, ifa, ifa->addr, ifa->want_triggered); + ifa->next_triggered = now + MIN(5, period / 2 + 1); + ifa->want_triggered = 0; + p->triggered = 0; + } + + tm_start(ifa->timer, ifa->want_triggered ? 1 : (ifa->next_regular - now)); } +static inline void +rip_iface_kick_timer(struct rip_iface *ifa) +{ + if (ifa->timer->expires > (now + 1)) + tm_start(ifa->timer, 1); /* Or 100 ms */ +} + +static void +rip_trigger_update(struct rip_proto *p) +{ + if (p->triggered) + return; + + struct rip_iface *ifa; + WALK_LIST(ifa, p->iface_list) + { + /* Interface not active */ + if (! ifa->up) + continue; + + /* Already scheduled */ + if (ifa->want_triggered) + continue; + + TRACE(D_EVENTS, "Scheduling triggered updates for %s", ifa->iface->name); + ifa->want_triggered = now; + rip_iface_kick_timer(ifa); + } + + p->triggered = 1; +} + + +/* + * RIP protocol glue + */ + static struct ea_list * -rip_gen_attrs(struct linpool *pool, int metric, u16 tag) +rip_prepare_attrs(struct linpool *pool, ea_list *next, u8 metric, u16 tag) { - struct ea_list *l = lp_alloc(pool, sizeof(struct ea_list) + 2*sizeof(eattr)); + struct ea_list *l = lp_alloc(pool, sizeof(struct ea_list) + 2 * sizeof(eattr)); - l->next = NULL; + l->next = next; l->flags = EALF_SORTED; l->count = 2; - l->attrs[0].id = EA_RIP_TAG; + + l->attrs[0].id = EA_RIP_METRIC; l->attrs[0].flags = 0; l->attrs[0].type = EAF_TYPE_INT | EAF_TEMP; - l->attrs[0].u.data = tag; - l->attrs[1].id = EA_RIP_METRIC; + l->attrs[0].u.data = metric; + + l->attrs[1].id = EA_RIP_TAG; l->attrs[1].flags = 0; l->attrs[1].type = EAF_TYPE_INT | EAF_TEMP; - l->attrs[1].u.data = metric; + l->attrs[1].u.data = tag; + return l; } static int -rip_import_control(struct proto *p, struct rte **rt, struct ea_list **attrs, struct linpool *pool) +rip_import_control(struct proto *P, struct rte **rt, struct ea_list **attrs, struct linpool *pool) { - if ((*rt)->attrs->src->proto == p) /* My own must not be touched */ + /* Prepare attributes with initial values */ + if ((*rt)->attrs->source != RTS_RIP) + *attrs = rip_prepare_attrs(pool, *attrs, 1, 0); + + return 0; +} + +static int +rip_reload_routes(struct proto *P) +{ + struct rip_proto *p = (struct rip_proto *) P; + + if (p->rt_reload) return 1; - if ((*rt)->attrs->source != RTS_RIP) { - struct ea_list *new = rip_gen_attrs(pool, 1, 0); - new->next = *attrs; - *attrs = new; - } - return 0; + TRACE(D_EVENTS, "Scheduling route reload"); + p->rt_reload = 1; + rip_kick_timer(p); + + return 1; } static struct ea_list * rip_make_tmp_attrs(struct rte *rt, struct linpool *pool) { - return rip_gen_attrs(pool, rt->u.rip.metric, rt->u.rip.tag); + return rip_prepare_attrs(pool, NULL, rt->u.rip.metric, rt->u.rip.tag); } -static void +static void rip_store_tmp_attrs(struct rte *rt, struct ea_list *attrs) { - rt->u.rip.tag = ea_get_int(attrs, EA_RIP_TAG, 0); rt->u.rip.metric = ea_get_int(attrs, EA_RIP_METRIC, 1); + rt->u.rip.tag = ea_get_int(attrs, EA_RIP_TAG, 0); } -/* - * rip_rt_notify - core tells us about new route (possibly our - * own), so store it into our data structures. - */ -static void -rip_rt_notify(struct proto *p, struct rtable *table UNUSED, struct network *net, - struct rte *new, struct rte *old UNUSED, struct ea_list *attrs) +static int +rip_rte_better(struct rte *new, struct rte *old) { - CHK_MAGIC; - struct rip_entry *e; - - e = fib_find( &P->rtable, &net->n.prefix, net->n.pxlen ); - if (e) - fib_delete( &P->rtable, e ); - - if (new) { - e = fib_get( &P->rtable, &net->n.prefix, net->n.pxlen ); - - e->nexthop = new->attrs->gw; - e->metric = 0; - e->whotoldme = IPA_NONE; - new->u.rip.entry = e; - - e->tag = ea_get_int(attrs, EA_RIP_TAG, 0); - e->metric = ea_get_int(attrs, EA_RIP_METRIC, 1); - if (e->metric > P_CF->infinity) - e->metric = P_CF->infinity; - - if (new->attrs->src->proto == p) - e->whotoldme = new->attrs->from; - - if (!e->metric) /* That's okay: this way user can set his own value for external - routes in rip. */ - e->metric = 5; - e->updated = e->changed = now; - e->flags = 0; - } + return new->u.rip.metric < old->u.rip.metric; } static int rip_rte_same(struct rte *new, struct rte *old) { - /* new->attrs == old->attrs always */ - return new->u.rip.metric == old->u.rip.metric; + return ((new->u.rip.metric == old->u.rip.metric) && + (new->u.rip.tag == old->u.rip.tag) && + (new->u.rip.from == old->u.rip.from)); } -static int -rip_rte_better(struct rte *new, struct rte *old) -{ - struct proto *p = new->attrs->src->proto; - - if (ipa_equal(old->attrs->from, new->attrs->from)) - return 1; - - if (old->u.rip.metric < new->u.rip.metric) - return 0; - - if (old->u.rip.metric > new->u.rip.metric) - return 1; - - if (old->attrs->src->proto == new->attrs->src->proto) /* This does not make much sense for different protocols */ - if ((old->u.rip.metric == new->u.rip.metric) && - ((now - old->lastmod) > (P_CF->timeout_time / 2))) - return 1; - - return 0; -} - -/* - * rip_rte_insert - we maintain linked list of "our" entries in main - * routing table, so that we can timeout them correctly. rip_timer() - * walks the list. - */ -static void -rip_rte_insert(net *net UNUSED, rte *rte) -{ - struct proto *p = rte->attrs->src->proto; - CHK_MAGIC; - DBG( "rip_rte_insert: %p\n", rte ); - add_head( &P->garbage, &rte->u.rip.garbage ); -} - -/* - * rip_rte_remove - link list maintenance - */ -static void -rip_rte_remove(net *net UNUSED, rte *rte) -{ -#ifdef LOCAL_DEBUG - struct proto *p = rte->attrs->src->proto; - CHK_MAGIC; - DBG( "rip_rte_remove: %p\n", rte ); -#endif - rem_node( &rte->u.rip.garbage ); -} - static struct proto * rip_init(struct proto_config *cfg) { - struct proto *p = proto_new(cfg, sizeof(struct rip_proto)); + struct proto *P = proto_new(cfg, sizeof(struct rip_proto)); - p->accept_ra_types = RA_OPTIMAL; - p->if_notify = rip_if_notify; - p->rt_notify = rip_rt_notify; - p->import_control = rip_import_control; - p->make_tmp_attrs = rip_make_tmp_attrs; - p->store_tmp_attrs = rip_store_tmp_attrs; - p->rte_better = rip_rte_better; - p->rte_same = rip_rte_same; - p->rte_insert = rip_rte_insert; - p->rte_remove = rip_rte_remove; + P->accept_ra_types = RA_OPTIMAL; + P->if_notify = rip_if_notify; + P->rt_notify = rip_rt_notify; + P->neigh_notify = rip_neigh_notify; + P->import_control = rip_import_control; + P->reload_routes = rip_reload_routes; + P->make_tmp_attrs = rip_make_tmp_attrs; + P->store_tmp_attrs = rip_store_tmp_attrs; + P->rte_better = rip_rte_better; + P->rte_same = rip_rte_same; - return p; + return P; } -void -rip_init_config(struct rip_proto_config *c) +static int +rip_start(struct proto *P) { - init_list(&c->iface_list); - c->infinity = 16; - c->port = RIP_PORT; - c->period = 30; - c->garbage_time = 120+180; - c->timeout_time = 120; - c->passwords = NULL; - c->authtype = AT_NONE; + struct rip_proto *p = (void *) P; + struct rip_config *cf = (void *) (P->cf); + + init_list(&p->iface_list); + fib_init(&p->rtable, P->pool, sizeof(struct rip_entry), 0, rip_init_entry); + p->rte_slab = sl_new(P->pool, sizeof(struct rip_rte)); + p->timer = tm_new_set(P->pool, rip_timer, p, 0, 0); + + p->ecmp = cf->ecmp; + p->infinity = cf->infinity; + p->triggered = 0; + + p->log_pkt_tbf = (struct tbf){ .rate = 1, .burst = 5 }; + p->log_rte_tbf = (struct tbf){ .rate = 4, .burst = 20 }; + + tm_start(p->timer, MIN(cf->min_timeout_time, cf->max_garbage_time)); + + return PS_UP; +} + +static int +rip_reconfigure(struct proto *P, struct proto_config *c) +{ + struct rip_proto *p = (void *) P; + struct rip_config *new = (void *) c; + // struct rip_config *old = (void *) (P->cf); + + if (new->infinity != p->infinity) + return 0; + + TRACE(D_EVENTS, "Reconfiguring"); + + p->p.cf = c; + p->ecmp = new->ecmp; + rip_reconfigure_ifaces(p, new); + + p->rt_reload = 1; + rip_kick_timer(p); + + return 1; +} + +static void +rip_get_route_info(rte *rte, byte *buf, ea_list *attrs) +{ + buf += bsprintf(buf, " (%d/%d)", rte->pref, rte->u.rip.metric); + + if (rte->u.rip.tag) + bsprintf(buf, " [%04x]", rte->u.rip.tag); } static int rip_get_attr(eattr *a, byte *buf, int buflen UNUSED) { - switch (a->id) { - case EA_RIP_METRIC: bsprintf( buf, "metric: %d", a->u.data ); return GA_FULL; - case EA_RIP_TAG: bsprintf( buf, "tag: %d", a->u.data ); return GA_FULL; - default: return GA_UNKNOWN; + switch (a->id) + { + case EA_RIP_METRIC: + bsprintf(buf, "metric: %d", a->u.data); + return GA_FULL; + + case EA_RIP_TAG: + bsprintf(buf, "tag: %04x", a->u.data); + return GA_FULL; + + default: + return GA_UNKNOWN; } } -static int -rip_pat_compare(struct rip_patt *a, struct rip_patt *b) +void +rip_show_interfaces(struct proto *P, char *iff) { - return ((a->metric == b->metric) && - (a->mode == b->mode) && - (a->tx_tos == b->tx_tos) && - (a->tx_priority == b->tx_priority)); + struct rip_proto *p = (void *) P; + struct rip_iface *ifa = NULL; + struct rip_neighbor *n = NULL; + + if (p->p.proto_state != PS_UP) + { + cli_msg(-1021, "%s: is not up", p->p.name); + cli_msg(0, ""); + return; + } + + cli_msg(-1021, "%s:", p->p.name); + cli_msg(-1021, "%-10s %-6s %6s %6s %6s", + "Interface", "State", "Metric", "Nbrs", "Timer"); + + WALK_LIST(ifa, p->iface_list) + { + if (iff && !patmatch(iff, ifa->iface->name)) + continue; + + int nbrs = 0; + WALK_LIST(n, ifa->neigh_list) + if (n->last_seen) + nbrs++; + + int timer = MAX(ifa->next_regular - now, 0); + cli_msg(-1021, "%-10s %-6s %6u %6u %6u", + ifa->iface->name, (ifa->up ? "Up" : "Down"), ifa->cf->metric, nbrs, timer); + } + + cli_msg(0, ""); } -static int -rip_reconfigure(struct proto *p, struct proto_config *c) +void +rip_show_neighbors(struct proto *P, char *iff) { - struct rip_proto_config *new = (struct rip_proto_config *) c; - int generic = sizeof(struct proto_config) + sizeof(list) /* + sizeof(struct password_item *) */; + struct rip_proto *p = (void *) P; + struct rip_iface *ifa = NULL; + struct rip_neighbor *n = NULL; - if (!iface_patts_equal(&P_CF->iface_list, &new->iface_list, (void *) rip_pat_compare)) - return 0; - return !memcmp(((byte *) P_CF) + generic, - ((byte *) new) + generic, - sizeof(struct rip_proto_config) - generic); + if (p->p.proto_state != PS_UP) + { + cli_msg(-1022, "%s: is not up", p->p.name); + cli_msg(0, ""); + return; + } + + cli_msg(-1022, "%s:", p->p.name); + cli_msg(-1022, "%-25s %-10s %6s %6s %6s", + "IP address", "Interface", "Metric", "Routes", "Seen"); + + WALK_LIST(ifa, p->iface_list) + { + if (iff && !patmatch(iff, ifa->iface->name)) + continue; + + WALK_LIST(n, ifa->neigh_list) + { + if (!n->last_seen) + continue; + + int timer = now - n->last_seen; + cli_msg(-1022, "%-25I %-10s %6u %6u %6u", + n->nbr->addr, ifa->iface->name, ifa->cf->metric, n->uc, timer); + } + } + + cli_msg(0, ""); } static void -rip_copy_config(struct proto_config *dest, struct proto_config *src) +rip_dump(struct proto *P) { - /* Shallow copy of everything */ - proto_copy_rest(dest, src, sizeof(struct rip_proto_config)); + struct rip_proto *p = (struct rip_proto *) P; + struct rip_iface *ifa; + int i; - /* We clean up iface_list, ifaces are non-sharable */ - init_list(&((struct rip_proto_config *) dest)->iface_list); + i = 0; + FIB_WALK(&p->rtable, e) + { + struct rip_entry *en = (struct rip_entry *) e; + debug("RIP: entry #%d: %I/%d via %I dev %s valid %d metric %d age %d s\n", + i++, en->n.prefix, en->n.pxlen, en->next_hop, en->iface->name, + en->valid, en->metric, now - en->changed); + } + FIB_WALK_END; - /* Copy of passwords is OK, it just will be replaced in dest when used */ + i = 0; + WALK_LIST(ifa, p->iface_list) + { + debug("RIP: interface #%d: %s, %I, up = %d, busy = %d\n", + i++, ifa->iface->name, ifa->sk ? ifa->sk->daddr : IPA_NONE, + ifa->up, ifa->tx_active); + } } @@ -1050,12 +1278,11 @@ struct protocol proto_rip = { .template = "rip%d", .attr_class = EAP_RIP, .preference = DEF_PREF_RIP, - .config_size = sizeof(struct rip_proto_config), + .config_size = sizeof(struct rip_config), .init = rip_init, .dump = rip_dump, .start = rip_start, .reconfigure = rip_reconfigure, - .copy_config = rip_copy_config, .get_route_info = rip_get_route_info, .get_attr = rip_get_attr }; diff --git a/proto/rip/rip.h b/proto/rip/rip.h index 2a327260..f245e612 100644 --- a/proto/rip/rip.h +++ b/proto/rip/rip.h @@ -1,185 +1,227 @@ /* - * Structures for RIP protocol + * BIRD -- Routing Information Protocol (RIP) * - FIXME: in V6, they insert additional entry whenever next hop differs. Such entry is identified by 0xff in metric. + * (c) 1998--1999 Pavel Machek + * (c) 2004--2013 Ondrej Filip + * (c) 2009--2015 Ondrej Zajicek + * (c) 2009--2015 CZ.NIC z.s.p.o. + * + * Can be freely distributed and used under the terms of the GNU GPL. */ +#ifndef _BIRD_RIP_H_ +#define _BIRD_RIP_H_ + +#include "nest/bird.h" +#include "nest/cli.h" +#include "nest/iface.h" +#include "nest/protocol.h" #include "nest/route.h" #include "nest/password.h" #include "nest/locks.h" - -#define EA_RIP_TAG EA_CODE(EAP_RIP, 0) -#define EA_RIP_METRIC EA_CODE(EAP_RIP, 1) - -#define PACKET_MAX 25 -#define PACKET_MD5_MAX 18 /* FIXME */ +#include "nest/bfd.h" +#include "lib/lists.h" +#include "lib/resource.h" +#include "lib/socket.h" +#include "lib/string.h" +#include "lib/timer.h" -#define RIP_V1 1 -#define RIP_V2 2 -#define RIP_NG 1 /* A new version numbering */ - -#ifndef IPV6 -#define RIP_PORT 520 /* RIP for IPv4 */ +#ifdef IPV6 +#define RIP_IS_V2 0 #else -#define RIP_PORT 521 /* RIPng */ +#define RIP_IS_V2 1 #endif -struct rip_connection { - node n; - - int num; - struct proto *proto; - ip_addr addr; - sock *send; - struct rip_interface *rif; - struct fib_iterator iter; - - ip_addr daddr; - int dport; - int done; -}; - -struct rip_packet_heading { /* 4 bytes */ - u8 command; -#define RIPCMD_REQUEST 1 /* want info */ -#define RIPCMD_RESPONSE 2 /* responding to request */ -#define RIPCMD_TRACEON 3 /* turn tracing on */ -#define RIPCMD_TRACEOFF 4 /* turn it off */ -#define RIPCMD_MAX 5 - u8 version; #define RIP_V1 1 #define RIP_V2 2 -#define RIP_NG 1 /* this is verion 1 of RIPng */ - u16 unused; + +#define RIP_PORT 520 /* RIP for IPv4 */ +#define RIP_NG_PORT 521 /* RIPng */ + +#define RIP_MAX_PKT_LENGTH 532 /* 512 + IP4_HEADER_LENGTH */ +#define RIP_AUTH_TAIL_LENGTH 20 /* 4 + MD5 length */ + +#define RIP_DEFAULT_ECMP_LIMIT 16 +#define RIP_DEFAULT_INFINITY 16 +#define RIP_DEFAULT_UPDATE_TIME 30 +#define RIP_DEFAULT_TIMEOUT_TIME 180 +#define RIP_DEFAULT_GARBAGE_TIME 120 + + +struct rip_config +{ + struct proto_config c; + list patt_list; /* List of iface configs (struct rip_iface_config) */ + + u8 rip2; /* RIPv2 (IPv4) or RIPng (IPv6) */ + u8 ecmp; /* Maximum number of nexthops in ECMP route, or 0 */ + u8 infinity; /* Maximum metric value, representing infinity */ + + u32 min_timeout_time; /* Minimum of interface timeout_time */ + u32 max_garbage_time; /* Maximum of interface garbage_time */ }; -#ifndef IPV6 -struct rip_block { /* 20 bytes */ - u16 family; /* 0xffff on first message means this is authentication */ - u16 tag; - ip_addr network; - ip_addr netmask; - ip_addr nexthop; - u32 metric; -}; -#else -struct rip_block { /* IPv6 version!, 20 bytes, too */ - ip_addr network; - u16 tag; - u8 pxlen; - u8 metric; -}; -#endif - -struct rip_block_auth { /* 20 bytes */ - u16 mustbeFFFF; - u16 authtype; - u16 packetlen; - u8 keyid; - u8 authlen; - u32 seq; - u32 zero0; - u32 zero1; -}; - -struct rip_md5_tail { /* 20 bytes */ - u16 mustbeFFFF; - u16 mustbe0001; - char md5[16]; -}; - -struct rip_entry { - struct fib_node n; - - ip_addr whotoldme; - ip_addr nexthop; - int metric; - u16 tag; - - bird_clock_t updated, changed; - int flags; -}; - -struct rip_packet { - struct rip_packet_heading heading; - struct rip_block block[PACKET_MAX]; -}; - -struct rip_interface { - node n; - struct proto *proto; - struct iface *iface; - sock *sock; - struct rip_connection *busy; - int metric; /* You don't want to put struct rip_patt *patt here -- think about reconfigure */ - int mode; - int check_ttl; /* Check incoming packets for TTL 255 */ - int triggered; - struct object_lock *lock; - int multicast; -}; - -struct rip_patt { +struct rip_iface_config +{ struct iface_patt i; - - int metric; /* If you add entries here, don't forget to modify patt_compare! */ - int mode; -#define IM_BROADCAST 2 -#define IM_QUIET 4 -#define IM_NOLISTEN 8 -#define IM_VERSION1 16 + ip_addr address; /* Configured dst address */ + u16 port; /* Src+dst port */ + u8 metric; /* Incoming metric */ + u8 mode; /* Interface mode (RIP_IM_*) */ + u8 passive; /* Passive iface - no packets are sent */ + u8 version; /* RIP version used for outgoing packets */ + u8 version_only; /* FIXXX */ + u8 split_horizon; /* Split horizon is used in route updates */ + u8 poison_reverse; /* Poisoned reverse is used in route updates */ + u8 check_zero; /* Validation of RIPv1 reserved fields */ + u8 ecmp_weight; /* Weight for ECMP routes*/ + u8 auth_type; /* Authentication type (RIP_AUTH_*) */ + u8 ttl_security; /* bool + 2 for TX only (send, but do not check on RX) */ + u8 check_link; /* Whether iface link change is used */ + u8 bfd; /* Use BFD on iface */ + u16 rx_buffer; /* RX buffer size, 0 for MTU */ + u16 tx_length; /* TX packet length limit (including headers), 0 for MTU */ int tx_tos; int tx_priority; - int ttl_security; /* bool + 2 for TX only (send, but do not check on RX) */ + u32 update_time; /* Periodic update interval */ + u32 timeout_time; /* Route expiration timeout */ + u32 garbage_time; /* Unreachable entry GC timeout */ + list *passwords; /* Passwords for authentication */ }; -struct rip_proto_config { - struct proto_config c; - list iface_list; /* Patterns configured -- keep it first; see rip_reconfigure why */ - list *passwords; /* Passwords, keep second */ +struct rip_proto +{ + struct proto p; + struct fib rtable; /* Internal routing table */ + list iface_list; /* List of interfaces (struct rip_iface) */ + slab *rte_slab; /* Slab for internal routes (struct rip_rte) */ + timer *timer; /* Main protocol timer */ - int infinity; /* User configurable data; must be comparable with memcmp */ - int port; - int period; - int garbage_time; - int timeout_time; + u8 ecmp; /* Maximum number of nexthops in ECMP route, or 0 */ + u8 infinity; /* Maximum metric value, representing infinity */ + u8 triggered; /* Logical AND of interface want_triggered values */ + u8 rt_reload; /* Route reload is scheduled */ - int authtype; -#define AT_NONE 0 -#define AT_PLAINTEXT 2 -#define AT_MD5 3 - int honor; -#define HO_NEVER 0 -#define HO_NEIGHBOR 1 -#define HO_ALWAYS 2 + struct tbf log_pkt_tbf; /* TBF for packet messages */ + struct tbf log_rte_tbf; /* TBF for RTE messages */ }; -struct rip_proto { - struct proto inherited; - timer *timer; - list connections; - struct fib rtable; - list garbage; - list interfaces; /* Interfaces we really know about */ -#ifdef LOCAL_DEBUG - int magic; +struct rip_iface +{ + node n; + struct rip_proto *rip; + struct iface *iface; /* Underyling core interface */ + struct rip_iface_config *cf; /* Related config, must be updated in reconfigure */ + struct object_lock *lock; /* Interface lock */ + timer *timer; /* Interface timer */ + sock *sk; /* UDP socket */ + + u8 up; /* Interface is active */ + u8 csn_ready; /* Nonzero CSN can be used */ + u16 tx_plen; /* Max TX packet data length */ + u32 csn; /* Last used crypto sequence number */ + ip_addr addr; /* Destination multicast/broadcast address */ + list neigh_list; /* List of iface neighbors (struct rip_neighbor) */ + + /* Update scheduling */ + bird_clock_t next_regular; /* Next time when regular update should be called */ + bird_clock_t next_triggered; /* Next time when triggerd update may be called */ + bird_clock_t want_triggered; /* Nonzero if triggered update is scheduled */ + + /* Active update */ + int tx_active; /* Update session is active */ + ip_addr tx_addr; /* Update session destination address */ + bird_clock_t tx_changed; /* Minimal changed time for triggered update */ + struct fib_iterator tx_fit; /* FIB iterator in RIP routing table (p.rtable) */ +}; + +struct rip_neighbor +{ + node n; + struct rip_iface *ifa; /* Associated interface, may be NULL if stale */ + struct neighbor *nbr; /* Associaded core neighbor, may be NULL if stale */ + struct bfd_request *bfd_req; /* BFD request, if BFD is used */ + bird_clock_t last_seen; /* Time of last received and accepted message */ + u32 uc; /* Use count, number of routes linking the neighbor */ + u32 csn; /* Last received crypto sequence number */ +}; + +struct rip_entry +{ + struct fib_node n; + struct rip_rte *routes; /* List of incoming routes */ + + u8 valid; /* Entry validity state (RIP_ENTRY_*) */ + u8 metric; /* Outgoing route metric */ + u16 tag; /* Outgoing route tag */ + struct iface *from; /* Outgoing route from, NULL if from proto */ + struct iface *iface; /* Outgoing route iface (for next hop) */ + ip_addr next_hop; /* Outgoing route next hop */ + + bird_clock_t changed; /* Last time when the outgoing route metric changed */ +}; + +struct rip_rte +{ + struct rip_rte *next; + + struct rip_neighbor *from; /* Advertising router */ + ip_addr next_hop; /* Route next hop (iface is from->nbr->iface) */ + u16 metric; /* Route metric (after increase) */ + u16 tag; /* Route tag */ + + bird_clock_t expires; /* Time of route expiration */ +}; + + +#define RIP_AUTH_NONE 0 +#define RIP_AUTH_PLAIN 2 +#define RIP_AUTH_CRYPTO 3 + +#define RIP_IM_MULTICAST 1 +#define RIP_IM_BROADCAST 2 + +#define RIP_ENTRY_DUMMY 0 /* Only used to store list of incoming routes */ +#define RIP_ENTRY_VALID 1 /* Valid outgoing route */ +#define RIP_ENTRY_STALE 2 /* Stale outgoing route, waiting for GC */ + +#define EA_RIP_METRIC EA_CODE(EAP_RIP, 0) +#define EA_RIP_TAG EA_CODE(EAP_RIP, 1) + +#define rip_is_v2(X) RIP_IS_V2 +#define rip_is_ng(X) (!RIP_IS_V2) + +/* +static inline int rip_is_v2(struct rip_proto *p) +{ return p->rip2; } + +static inline int rip_is_ng(struct rip_proto *p) +{ return ! p->rip2; } +*/ + +static inline void +rip_reset_tx_session(struct rip_proto *p, struct rip_iface *ifa) +{ + if (ifa->tx_active) + { + FIB_ITERATE_UNLINK(&ifa->tx_fit, &p->rtable); + ifa->tx_active = 0; + } +} + +/* rip.c */ +void rip_update_rte(struct rip_proto *p, ip_addr *prefix, int pxlen, struct rip_rte *new); +void rip_withdraw_rte(struct rip_proto *p, ip_addr *prefix, int pxlen, struct rip_neighbor *from); +struct rip_neighbor * rip_get_neighbor(struct rip_proto *p, ip_addr *a, struct rip_iface *ifa); +void rip_update_bfd(struct rip_proto *p, struct rip_neighbor *n); +void rip_show_interfaces(struct proto *P, char *iff); +void rip_show_neighbors(struct proto *P, char *iff); + +/* packets.c */ +void rip_send_request(struct rip_proto *p, struct rip_iface *ifa); +void rip_send_table(struct rip_proto *p, struct rip_iface *ifa, ip_addr addr, bird_clock_t changed); +int rip_open_socket(struct rip_iface *ifa); + + #endif - int tx_count; /* Do one regular update once in a while */ - int rnd_count; /* Randomize sending time */ -}; - -#ifdef LOCAL_DEBUG -#define RIP_MAGIC 81861253 -#define CHK_MAGIC do { if (P->magic != RIP_MAGIC) bug( "Not enough magic" ); } while (0) -#else -#define CHK_MAGIC do { } while (0) -#endif - - -void rip_init_config(struct rip_proto_config *c); - -/* Authentication functions */ - -int rip_incoming_authentication( struct proto *p, struct rip_block_auth *block, struct rip_packet *packet, int num, ip_addr whotoldme ); -int rip_outgoing_authentication( struct proto *p, struct rip_block_auth *block, struct rip_packet *packet, int num ); From acb04cfdc550697a7171a86ca559fd8c52841acb Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Sat, 17 Oct 2015 14:36:53 +0200 Subject: [PATCH 03/18] Minor changes --- nest/route.h | 34 ++++++++++++++++++++-------------- sysdep/linux/netlink.c | 23 ++++++++++++++++------- 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/nest/route.h b/nest/route.h index 6067526d..d0f8c688 100644 --- a/nest/route.h +++ b/nest/route.h @@ -174,7 +174,7 @@ struct hostentry { ip_addr addr; /* IP address of host, part of key */ ip_addr link; /* (link-local) IP address of host, used as gw if host is directly attached */ - struct rtable *tab; /* Dependent table, part of key*/ + struct rtable *tab; /* Dependent table, part of key */ struct hostentry *next; /* Next in hash chain */ unsigned hash_key; /* Hash key */ unsigned uc; /* Use count */ @@ -507,19 +507,25 @@ void rta_show(struct cli *, rta *, ea_list *); void rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr *gw, ip_addr *ll); /* - * rta_set_recursive_next_hop() acquires hostentry from hostcache and - * fills rta->hostentry field. New hostentry has zero use - * count. Cached rta locks its hostentry (increases its use count), - * uncached rta does not lock it. Hostentry with zero use count is - * removed asynchronously during host cache update, therefore it is - * safe to hold such hostentry temorarily. Hostentry holds a lock for - * a 'source' rta, mainly to share multipath nexthops. There is no - * need to hold a lock for hostentry->dep table, because that table - * contains routes responsible for that hostentry, and therefore is - * non-empty if given hostentry has non-zero use count. The protocol - * responsible for routes with recursive next hops should also hold a - * lock for a table governing that routes (argument tab to - * rta_set_recursive_next_hop()). + * rta_set_recursive_next_hop() acquires hostentry from hostcache and fills + * rta->hostentry field. New hostentry has zero use count. Cached rta locks its + * hostentry (increases its use count), uncached rta does not lock it. Hostentry + * with zero use count is removed asynchronously during host cache update, + * therefore it is safe to hold such hostentry temorarily. Hostentry holds a + * lock for a 'source' rta, mainly to share multipath nexthops. + * + * There is no need to hold a lock for hostentry->dep table, because that table + * contains routes responsible for that hostentry, and therefore is non-empty if + * given hostentry has non-zero use count. If the hostentry has zero use count, + * the entry is removed before dep is referenced. + * + * The protocol responsible for routes with recursive next hops should hold a + * lock for a 'source' table governing that routes (argument tab to + * rta_set_recursive_next_hop()), because its routes reference hostentries + * (through rta) related to the governing table. When all such routes are + * removed, rtas are immediately removed achieving zero uc. Then the 'source' + * table lock could be immediately released, although hostentries may still + * exist - they will be freed together with the 'source' table. */ static inline void rt_lock_hostentry(struct hostentry *he) { if (he) he->uc++; } diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index 9c9449e2..674d338b 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -239,6 +239,16 @@ nl_parse_attrs(struct rtattr *a, struct rtattr **k, int ksize) return 1; } +static inline ip4_addr rta_get_u32(struct rtattr *a) +{ return *(u32 *) RTA_DATA(a); } + +static inline ip4_addr rta_get_ip4(struct rtattr *a) +{ return ip4_ntoh(*(ip4_addr *) RTA_DATA(a)); } + +static inline ip6_addr rta_get_ip6(struct rtattr *a) +{ return ip6_ntoh(*(ip6_addr *) RTA_DATA(a)); } + + struct rtattr * nl_add_attr(struct nlmsghdr *h, uint bufsize, uint code, const void *data, uint dlen) { @@ -420,7 +430,7 @@ nl_parse_metrics(struct rtattr *hdr, u32 *metrics, int max) return -1; metrics[0] |= 1 << a->rta_type; - metrics[a->rta_type] = *(u32 *)RTA_DATA(a); + metrics[a->rta_type] = rta_get_u32(a); } if (len > 0) @@ -456,7 +466,7 @@ nl_parse_link(struct nlmsghdr *h, int scan) return; } name = RTA_DATA(a[IFLA_IFNAME]); - memcpy(&mtu, RTA_DATA(a[IFLA_MTU]), sizeof(u32)); + mtu = rta_get_u32(a[IFLA_MTU]); ifi = if_find_by_index(i->ifi_index); if (!new) @@ -831,7 +841,7 @@ nl_parse_route(struct nlmsghdr *h, int scan) } if (a[RTA_OIF]) - memcpy(&oif, RTA_DATA(a[RTA_OIF]), sizeof(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)"); @@ -965,11 +975,10 @@ nl_parse_route(struct nlmsghdr *h, int scan) e->u.krt.src = src; e->u.krt.proto = i->rtm_protocol; e->u.krt.type = i->rtm_type; + e->u.krt.metric = 0; if (a[RTA_PRIORITY]) - memcpy(&e->u.krt.metric, RTA_DATA(a[RTA_PRIORITY]), sizeof(e->u.krt.metric)); - else - e->u.krt.metric = 0; + e->u.krt.metric = rta_get_u32(a[RTA_PRIORITY]); if (a[RTA_PREFSRC]) { @@ -1000,7 +1009,7 @@ nl_parse_route(struct nlmsghdr *h, int scan) ea->attrs[0].id = EA_KRT_REALM; ea->attrs[0].flags = 0; ea->attrs[0].type = EAF_TYPE_INT; - memcpy(&ea->attrs[0].u.data, RTA_DATA(a[RTA_FLOW]), 4); + ea->attrs[0].u.data = rta_get_u32(a[RTA_FLOW]); } if (a[RTA_METRICS]) From 338f85ca7721fac16394ccabd561ddb5ccaacb36 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Tue, 3 Nov 2015 11:08:57 +0100 Subject: [PATCH 04/18] IO: Handle fd values too big for select() If the number of sockets is too much for select(), we should at least handle it with proper error messages and reject new sockets instead of breaking the event loop. Thanks to Alexander V. Chernikov for the patch. --- sysdep/unix/io.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 0724667d..726f1e49 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -1309,6 +1309,16 @@ sk_passive_connected(sock *s, int type) return 0; } + if (fd >= FD_SETSIZE) + { + /* FIXME: Call err_hook instead ? */ + log(L_ERR "SOCK: Incoming connection from %I%J (port %d) %s", + t->daddr, ipa_is_link_local(t->daddr) ? t->iface : NULL, + t->dport, "rejected due to FD_SETSIZE limit"); + close(fd); + return 1; + } + sock *t = sk_new(s->pool); t->type = type; t->fd = fd; @@ -1404,6 +1414,9 @@ sk_open(sock *s) if (fd < 0) ERR("socket"); + if (fd >= FD_SETSIZE) + ERR2("FD_SETSIZE limit reached"); + s->af = af; s->fd = fd; From 3aed0a6ff7b2b811a535202fd787281d2ac33409 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Tue, 3 Nov 2015 11:27:27 +0100 Subject: [PATCH 05/18] IO: Fix the previous bugfix I should check it after making some trivial changes. The original patch from Alexander has it right. --- sysdep/unix/io.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 726f1e49..b636e799 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -1309,16 +1309,6 @@ sk_passive_connected(sock *s, int type) return 0; } - if (fd >= FD_SETSIZE) - { - /* FIXME: Call err_hook instead ? */ - log(L_ERR "SOCK: Incoming connection from %I%J (port %d) %s", - t->daddr, ipa_is_link_local(t->daddr) ? t->iface : NULL, - t->dport, "rejected due to FD_SETSIZE limit"); - close(fd); - return 1; - } - sock *t = sk_new(s->pool); t->type = type; t->fd = fd; @@ -1338,6 +1328,18 @@ sk_passive_connected(sock *s, int type) log(L_WARN "SOCK: Cannot get remote IP address for TCP<"); } + if (fd >= FD_SETSIZE) + { + /* FIXME: Call err_hook instead ? */ + log(L_ERR "SOCK: Incoming connection from %I%J (port %d) %s", + t->daddr, ipa_is_link_local(t->daddr) ? t->iface : NULL, + t->dport, "rejected due to FD_SETSIZE limit"); + close(fd); + t->fd = -1; + rfree(t); + return 1; + } + if (sk_setup(t) < 0) { /* FIXME: Call err_hook instead ? */ From 9b9a7143c43d01f0459d40363d56e9c7690c596f Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Mon, 9 Nov 2015 00:42:02 +0100 Subject: [PATCH 06/18] Conf: Fixes bug in symbol lookup during reconfiguration Symbol lookup by cf_find_symbol() not only did the lookup but also added new void symbols allocated from cfg_mem linpool, which gets broken when lookups are done outside of config parsing, which may lead to crashes during reconfiguration. The patch separates lookup-only cf_find_symbol() and config-modifying cf_get_symbol(), while the later is called only during parsing. Also new_config and cfg_mem global variables are NULLed outside of parsing. --- conf/cf-lex.l | 64 ++++++++++++++++++++++++++++++--------------- conf/conf.c | 65 +++++++++++++++++++++++++++------------------- conf/conf.h | 4 ++- nest/proto.c | 2 +- nest/rt-roa.c | 2 +- nest/rt-table.c | 4 +-- sysdep/unix/main.c | 2 +- 7 files changed, 90 insertions(+), 53 deletions(-) diff --git a/conf/cf-lex.l b/conf/cf-lex.l index 61e04075..5a2a4d6b 100644 --- a/conf/cf-lex.l +++ b/conf/cf-lex.l @@ -70,7 +70,7 @@ struct sym_scope { static struct sym_scope *conf_this_scope; static int cf_hash(byte *c); -static struct symbol *cf_find_sym(byte *c, unsigned int h0); +static inline struct symbol * cf_get_sym(byte *c, uint h0); linpool *cfg_mem; @@ -194,7 +194,7 @@ else: { } k=k->next; } - cf_lval.s = cf_find_sym(yytext, h); + cf_lval.s = cf_get_sym(yytext, h); return SYM; } @@ -426,8 +426,9 @@ check_eof(void) } static struct symbol * -cf_new_sym(byte *c, unsigned int h) +cf_new_sym(byte *c, uint h0) { + uint h = h0 & (SYM_HASH_SIZE-1); struct symbol *s, **ht; int l; @@ -449,56 +450,77 @@ cf_new_sym(byte *c, unsigned int h) } static struct symbol * -cf_find_sym(byte *c, unsigned int h0) +cf_find_sym(struct config *cfg, byte *c, uint h0) { - unsigned int h = h0 & (SYM_HASH_SIZE-1); + uint h = h0 & (SYM_HASH_SIZE-1); struct symbol *s, **ht; - if (ht = new_config->sym_hash) + if (ht = cfg->sym_hash) { for(s = ht[h]; s; s=s->next) if (!strcmp(s->name, c) && s->scope->active) return s; } - if (new_config->sym_fallback) + if (ht = cfg->sym_fallback) { /* We know only top-level scope is active */ - for(s = new_config->sym_fallback[h]; s; s=s->next) + for(s = ht[h]; s; s=s->next) if (!strcmp(s->name, c) && s->scope->active) return s; } - return cf_new_sym(c, h); + + return NULL; +} + +static inline struct symbol * +cf_get_sym(byte *c, uint h0) +{ + return cf_find_sym(new_config, c, h0) ?: cf_new_sym(c, h0); } /** * cf_find_symbol - find a symbol by name + * @cfg: specificed config * @c: symbol name * - * This functions searches the symbol table for a symbol of given - * name. First it examines the current scope, then the second recent - * one and so on until it either finds the symbol and returns a pointer - * to its &symbol structure or reaches the end of the scope chain - * and returns %NULL to signify no match. + * This functions searches the symbol table in the config @cfg for a symbol of + * given name. First it examines the current scope, then the second recent one + * and so on until it either finds the symbol and returns a pointer to its + * &symbol structure or reaches the end of the scope chain and returns %NULL to + * signify no match. */ struct symbol * -cf_find_symbol(byte *c) +cf_find_symbol(struct config *cfg, byte *c) { - return cf_find_sym(c, cf_hash(c)); + return cf_find_sym(cfg, c, cf_hash(c)); +} + +/** + * cf_get_symbol - get a symbol by name + * @c: symbol name + * + * This functions searches the symbol table of the currently parsed config + * (@new_config) for a symbol of given name. It returns either the already + * existing symbol or a newly allocated undefined (%SYM_VOID) symbol if no + * existing symbol is found. + */ +struct symbol * +cf_get_symbol(byte *c) +{ + return cf_get_sym(c, cf_hash(c)); } struct symbol * cf_default_name(char *template, int *counter) { - char buf[32]; + char buf[SYM_MAX_LEN]; struct symbol *s; char *perc = strchr(template, '%'); for(;;) { bsprintf(buf, template, ++(*counter)); - s = cf_find_sym(buf, cf_hash(buf)); - if (!s) - break; + s = cf_get_sym(buf, cf_hash(buf)); if (s->class == SYM_VOID) return s; if (!perc) @@ -529,7 +551,7 @@ cf_define_symbol(struct symbol *sym, int type, void *def) { if (sym->scope == conf_this_scope) cf_error("Symbol already defined"); - sym = cf_new_sym(sym->name, cf_hash(sym->name) & (SYM_HASH_SIZE-1)); + sym = cf_new_sym(sym->name, cf_hash(sym->name)); } sym->class = type; sym->def = def; diff --git a/conf/conf.c b/conf/conf.c index a907402d..825a8e9f 100644 --- a/conf/conf.c +++ b/conf/conf.c @@ -20,19 +20,19 @@ * * There can exist up to four different configurations at one time: an active * one (pointed to by @config), configuration we are just switching from - * (@old_config), one queued for the next reconfiguration (@future_config; - * if there is one and the user wants to reconfigure once again, we just - * free the previous queued config and replace it with the new one) and - * finally a config being parsed (@new_config). The stored @old_config - * is also used for undo reconfiguration, which works in a similar way. - * Reconfiguration could also have timeout (using @config_timer) and undo - * is automatically called if the new configuration is not confirmed later. + * (@old_config), one queued for the next reconfiguration (@future_config; if + * there is one and the user wants to reconfigure once again, we just free the + * previous queued config and replace it with the new one) and finally a config + * being parsed (@new_config). The stored @old_config is also used for undo + * reconfiguration, which works in a similar way. Reconfiguration could also + * have timeout (using @config_timer) and undo is automatically called if the + * new configuration is not confirmed later. The new config (@new_config) and + * associated linear pool (@cfg_mem) is non-NULL only during parsing. * - * Loading of new configuration is very simple: just call config_alloc() - * to get a new &config structure, then use config_parse() to parse a - * configuration file and fill all fields of the structure - * and finally ask the config manager to switch to the new - * config by calling config_commit(). + * Loading of new configuration is very simple: just call config_alloc() to get + * a new &config structure, then use config_parse() to parse a configuration + * file and fill all fields of the structure and finally ask the config manager + * to switch to the new config by calling config_commit(). * * CLI commands are parsed in a very similar way -- there is also a stripped-down * &config structure associated with them and they are lex-ed and parsed by the @@ -91,10 +91,15 @@ config_alloc(byte *name) linpool *l = lp_new(p, 4080); struct config *c = lp_allocz(l, sizeof(struct config)); + /* Duplication of name string in local linear pool */ + uint nlen = strlen(name) + 1; + char *ndup = lp_allocu(l, nlen); + memcpy(ndup, name, nlen); + c->mrtdump_file = -1; /* Hack, this should be sysdep-specific */ c->pool = p; - cfg_mem = c->mem = l; - c->file_name = cfg_strdup(name); + c->mem = l; + c->file_name = ndup; c->load_time = now; c->tf_route = c->tf_proto = (struct timeformat){"%T", "%F", 20*3600}; c->tf_base = c->tf_log = (struct timeformat){"%F %T", NULL, 0}; @@ -119,11 +124,13 @@ config_alloc(byte *name) int config_parse(struct config *c) { + int done = 0; DBG("Parsing configuration file `%s'\n", c->file_name); new_config = c; cfg_mem = c->mem; if (setjmp(conf_jmpbuf)) - return 0; + goto cleanup; + cf_lex_init(0, c); sysdep_preconfig(c); protos_preconfig(c); @@ -137,7 +144,12 @@ config_parse(struct config *c) if (!c->router_id) cf_error("Router ID must be configured manually on IPv6 routers"); #endif - return 1; + done = 1; + +cleanup: + new_config = NULL; + cfg_mem = NULL; + return done; } /** @@ -150,14 +162,22 @@ config_parse(struct config *c) int cli_parse(struct config *c) { - new_config = c; + int done = 0; c->sym_fallback = config->sym_hash; + new_config = c; cfg_mem = c->mem; if (setjmp(conf_jmpbuf)) - return 0; + goto cleanup; + cf_lex_init(1, c); cf_parse(); - return 1; + done = 1; + +cleanup: + c->sym_fallback = NULL; + new_config = NULL; + cfg_mem = NULL; + return done; } /** @@ -237,10 +257,6 @@ config_do_commit(struct config *c, int type) if (old_config && !config->shutdown) log(L_INFO "Reconfiguring"); - /* This should not be necessary, but it seems there are some - functions that access new_config instead of config */ - new_config = config; - if (old_config) old_config->obstacle_count++; @@ -254,9 +270,6 @@ config_do_commit(struct config *c, int type) DBG("protos_commit\n"); protos_commit(c, old_config, force_restart, type); - /* Just to be sure nobody uses that now */ - new_config = NULL; - int obs = 0; if (old_config) obs = --old_config->obstacle_count; diff --git a/conf/conf.h b/conf/conf.h index 515efbb3..89a2c5b7 100644 --- a/conf/conf.h +++ b/conf/conf.h @@ -147,7 +147,9 @@ int cf_lex(void); void cf_lex_init(int is_cli, struct config *c); void cf_lex_unwind(void); -struct symbol *cf_find_symbol(byte *c); +struct symbol *cf_find_symbol(struct config *cfg, byte *c); + +struct symbol *cf_get_symbol(byte *c); struct symbol *cf_default_name(char *template, int *counter); struct symbol *cf_define_symbol(struct symbol *symbol, int type, void *def); void cf_push_scope(struct symbol *); diff --git a/nest/proto.c b/nest/proto.c index 6531083c..d04da333 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -521,7 +521,7 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty WALK_LIST(oc, old->protos) { p = oc->proto; - sym = cf_find_symbol(oc->name); + sym = cf_find_symbol(new, oc->name); if (sym && sym->class == SYM_PROTO && !new->shutdown) { /* Found match, let's check if we can smoothly switch to new configuration */ diff --git a/nest/rt-roa.c b/nest/rt-roa.c index aa453f16..0fd89667 100644 --- a/nest/rt-roa.c +++ b/nest/rt-roa.c @@ -311,7 +311,7 @@ roa_commit(struct config *new, struct config *old) if (old) WALK_LIST(t, roa_table_list) { - struct symbol *sym = cf_find_symbol(t->name); + struct symbol *sym = cf_find_symbol(new, t->name); if (sym && sym->class == SYM_ROA) { /* Found old table in new config */ diff --git a/nest/rt-table.c b/nest/rt-table.c index 9e2c4e0d..2ddff12e 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -1663,7 +1663,7 @@ rt_prune_loop(void) void rt_preconfig(struct config *c) { - struct symbol *s = cf_find_symbol("master"); + struct symbol *s = cf_get_symbol("master"); init_list(&c->tables); c->master_rtc = rt_new_table(s); @@ -1903,7 +1903,7 @@ rt_commit(struct config *new, struct config *old) rtable *ot = o->table; if (!ot->deleted) { - struct symbol *sym = cf_find_symbol(o->name); + struct symbol *sym = cf_find_symbol(new, o->name); if (sym && sym->class == SYM_TABLE && !new->shutdown) { DBG("\t%s: same\n", o->name); diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index e31471da..24d34f60 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -96,7 +96,7 @@ drop_gid(gid_t gid) static inline void add_num_const(char *name, int val) { - struct symbol *s = cf_find_symbol(name); + struct symbol *s = cf_get_symbol(name); s->class = SYM_CONSTANT | T_INT; s->def = cfg_allocz(sizeof(struct f_val)); SYM_TYPE(s) = T_INT; From 86b4e17001fe4cca6dde7ff523346121c0ae68fe Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Mon, 9 Nov 2015 01:01:12 +0100 Subject: [PATCH 07/18] Nest: Fixes bug in missing cleanup during table removal When a table is removed during reconfiguration, a reference was not cleared in the old configuration, which breaks undo. --- nest/rt-table.c | 1 + 1 file changed, 1 insertion(+) diff --git a/nest/rt-table.c b/nest/rt-table.c index 2ddff12e..10ce400a 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -1868,6 +1868,7 @@ rt_unlock_table(rtable *r) { struct config *conf = r->deleted; DBG("Deleting routing table %s\n", r->name); + r->config->table = NULL; if (r->hostcache) rt_free_hostcache(r); rem_node(&r->n); From 9ddbfbddf87462bbf50437bdc1d44499a5c223e7 Mon Sep 17 00:00:00 2001 From: Jan Moskyto Matejka Date: Tue, 3 Nov 2015 14:42:41 +0100 Subject: [PATCH 08/18] 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/bsd/krt-sock.c | 7 +++- sysdep/bsd/krt-sys.h | 1 + sysdep/linux/krt-sys.h | 8 ++-- sysdep/linux/netlink.Y | 2 - sysdep/linux/netlink.c | 90 ++++++++++++++++++++++++++++-------------- sysdep/unix/krt.c | 11 +++++- sysdep/unix/krt.h | 3 +- 7 files changed, 82 insertions(+), 40 deletions(-) diff --git a/sysdep/bsd/krt-sock.c b/sysdep/bsd/krt-sock.c index 064bae18..29203d1b 100644 --- a/sysdep/bsd/krt-sock.c +++ b/sysdep/bsd/krt-sock.c @@ -970,13 +970,15 @@ krt_sock_close_shared(void) } } -void +int krt_sys_start(struct krt_proto *p) { krt_table_map[KRT_CF->sys.table_id] = p; krt_sock_open_shared(); p->sys.sk = krt_sock; + + return 1; } void @@ -992,10 +994,11 @@ krt_sys_shutdown(struct krt_proto *p) #else -void +int krt_sys_start(struct krt_proto *p) { p->sys.sk = krt_sock_open(p->p.pool, p, KRT_CF->sys.table_id); + return 1; } void diff --git a/sysdep/bsd/krt-sys.h b/sysdep/bsd/krt-sys.h index 2c6e35c5..a63f8caf 100644 --- a/sysdep/bsd/krt-sys.h +++ b/sysdep/bsd/krt-sys.h @@ -42,6 +42,7 @@ struct krt_state { }; +static inline void krt_sys_io_init(void) { } static inline void krt_sys_init(struct krt_proto *p UNUSED) { } static inline int krt_sys_get_attr(eattr *a UNUSED, byte *buf UNUSED, int buflen UNUSED) { } 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) { diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index 2eab5cb2..49bf9519 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -77,6 +77,7 @@ krt_io_init(void) krt_pool = rp_new(&root_pool, "Kernel Syncer"); krt_filter_lp = lp_new(krt_pool, 4080); init_list(&krt_proto_list); + krt_sys_io_init(); } /* @@ -1126,7 +1127,11 @@ krt_start(struct proto *P) krt_learn_init(p); #endif - krt_sys_start(p); + if (!krt_sys_start(p)) + { + rem_node(&p->krt_node); + return PS_START; + } krt_scan_timer_start(p); @@ -1150,8 +1155,10 @@ krt_shutdown(struct proto *P) p->ready = 0; p->initialized = 0; - krt_sys_shutdown(p); + if (p->p.proto_state == PS_START) + return PS_DOWN; + krt_sys_shutdown(p); rem_node(&p->krt_node); return PS_DOWN; diff --git a/sysdep/unix/krt.h b/sysdep/unix/krt.h index 9d5d4e8c..aea20102 100644 --- a/sysdep/unix/krt.h +++ b/sysdep/unix/krt.h @@ -119,8 +119,9 @@ struct proto_config * krt_init_config(int class); /* krt sysdep */ +void krt_sys_io_init(void); void krt_sys_init(struct krt_proto *); -void krt_sys_start(struct krt_proto *); +int krt_sys_start(struct krt_proto *); void krt_sys_shutdown(struct krt_proto *); int krt_sys_reconfigure(struct krt_proto *p UNUSED, struct krt_config *n, struct krt_config *o); From fce764f90e8331d1adb6a85ec00136dfeae1a398 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Tvrd=C3=ADk?= Date: Mon, 9 Nov 2015 09:14:26 +0100 Subject: [PATCH 09/18] Fix compiling with --enable-debug option --- sysdep/linux/netlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) From 75ff08022ea58fe3efa96639f080ce375e997675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Tvrd=C3=ADk?= Date: Fri, 13 Nov 2015 16:10:33 +0100 Subject: [PATCH 10/18] Add get_u64() and put_u64() into lib/unaligned.h --- lib/unaligned.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/unaligned.h b/lib/unaligned.h index af655204..a2dbae4f 100644 --- a/lib/unaligned.h +++ b/lib/unaligned.h @@ -35,6 +35,15 @@ get_u32(void *p) return ntohl(x); } +static inline u64 +get_u64(const void *p) +{ + u32 xh, xl; + memcpy(&xh, p, 4); + memcpy(&xl, p+4, 4); + return (((u64) ntohl(xh)) << 32) | ntohl(xl); +} + static inline void put_u16(void *p, u16 x) { @@ -49,4 +58,14 @@ put_u32(void *p, u32 x) memcpy(p, &x, 4); } +static inline void +put_u64(void *p, u64 x) +{ + u32 xh, xl; + xh = htonl(x >> 32); + xl = htonl((u32) x); + memcpy(p, &xh, 4); + memcpy(p+4, &xl, 4); +} + #endif From 5d0c36f1da83b2a2a07e043247410948d90c600e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Tvrd=C3=ADk?= Date: Fri, 13 Nov 2015 16:11:51 +0100 Subject: [PATCH 11/18] Add SHA1 and SHA1-HMAC crypto hash --- lib/Modules | 2 + lib/sha1.c | 342 ++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/sha1.h | 86 +++++++++++++ 3 files changed, 430 insertions(+) create mode 100644 lib/sha1.c create mode 100644 lib/sha1.h diff --git a/lib/Modules b/lib/Modules index 7254df2d..a227f9a1 100644 --- a/lib/Modules +++ b/lib/Modules @@ -1,3 +1,5 @@ +sha1.c +sha1.h birdlib.h bitops.c bitops.h diff --git a/lib/sha1.c b/lib/sha1.c new file mode 100644 index 00000000..dd29b5fd --- /dev/null +++ b/lib/sha1.c @@ -0,0 +1,342 @@ +/* + * BIRD Library -- SHA-1 Hash Function (FIPS 180-1, RFC 3174) and HMAC-SHA-1 + * + * (c) 2015 CZ.NIC z.s.p.o. + * + * Based on the code from libucw-6.4 + * (c) 2008--2009 Martin Mares + * + * Based on the code from libgcrypt-1.2.3, which is + * (c) 1998, 2001, 2002, 2003 Free Software Foundation, Inc. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include "lib/sha1.h" +#include "lib/unaligned.h" + +void +sha1_init(struct sha1_context *hd) +{ + hd->h0 = 0x67452301; + hd->h1 = 0xefcdab89; + hd->h2 = 0x98badcfe; + hd->h3 = 0x10325476; + hd->h4 = 0xc3d2e1f0; + hd->nblocks = 0; + hd->count = 0; +} + +/* + * Transform the message X which consists of 16 32-bit-words + */ +static void +sha1_transform(struct sha1_context *hd, const byte *data) +{ + u32 a,b,c,d,e,tm; + u32 x[16]; + + /* Get values from the chaining vars. */ + a = hd->h0; + b = hd->h1; + c = hd->h2; + d = hd->h3; + e = hd->h4; + +#ifdef CPU_BIG_ENDIAN + memcpy(x, data, 64); +#else + int i; + for (i = 0; i < 16; i++) + x[i] = get_u32(data+4*i); +#endif + +#define K1 0x5A827999L +#define K2 0x6ED9EBA1L +#define K3 0x8F1BBCDCL +#define K4 0xCA62C1D6L +#define F1(x,y,z) ( z ^ ( x & ( y ^ z ) ) ) +#define F2(x,y,z) ( x ^ y ^ z ) +#define F3(x,y,z) ( ( x & y ) | ( z & ( x | y ) ) ) +#define F4(x,y,z) ( x ^ y ^ z ) + +#define M(i) (tm = x[i&0x0f] ^ x[(i-14)&0x0f] ^ x[(i-8)&0x0f] ^ x[(i-3)&0x0f], (x[i&0x0f] = ROL(tm, 1))) + +/* Bitwise rotation of an unsigned int to the left **/ +#define ROL(x, bits) (((x) << (bits)) | ((uint)(x) >> (sizeof(uint)*8 - (bits)))) + + #define R(a, b, c, d, e, f, k, m) \ + do \ + { \ + e += ROL(a, 5) + f(b, c, d) + k + m; \ + b = ROL( b, 30 ); \ + } while(0) + + R( a, b, c, d, e, F1, K1, x[ 0] ); + R( e, a, b, c, d, F1, K1, x[ 1] ); + R( d, e, a, b, c, F1, K1, x[ 2] ); + R( c, d, e, a, b, F1, K1, x[ 3] ); + R( b, c, d, e, a, F1, K1, x[ 4] ); + R( a, b, c, d, e, F1, K1, x[ 5] ); + R( e, a, b, c, d, F1, K1, x[ 6] ); + R( d, e, a, b, c, F1, K1, x[ 7] ); + R( c, d, e, a, b, F1, K1, x[ 8] ); + R( b, c, d, e, a, F1, K1, x[ 9] ); + R( a, b, c, d, e, F1, K1, x[10] ); + R( e, a, b, c, d, F1, K1, x[11] ); + R( d, e, a, b, c, F1, K1, x[12] ); + R( c, d, e, a, b, F1, K1, x[13] ); + R( b, c, d, e, a, F1, K1, x[14] ); + R( a, b, c, d, e, F1, K1, x[15] ); + R( e, a, b, c, d, F1, K1, M(16) ); + R( d, e, a, b, c, F1, K1, M(17) ); + R( c, d, e, a, b, F1, K1, M(18) ); + R( b, c, d, e, a, F1, K1, M(19) ); + R( a, b, c, d, e, F2, K2, M(20) ); + R( e, a, b, c, d, F2, K2, M(21) ); + R( d, e, a, b, c, F2, K2, M(22) ); + R( c, d, e, a, b, F2, K2, M(23) ); + R( b, c, d, e, a, F2, K2, M(24) ); + R( a, b, c, d, e, F2, K2, M(25) ); + R( e, a, b, c, d, F2, K2, M(26) ); + R( d, e, a, b, c, F2, K2, M(27) ); + R( c, d, e, a, b, F2, K2, M(28) ); + R( b, c, d, e, a, F2, K2, M(29) ); + R( a, b, c, d, e, F2, K2, M(30) ); + R( e, a, b, c, d, F2, K2, M(31) ); + R( d, e, a, b, c, F2, K2, M(32) ); + R( c, d, e, a, b, F2, K2, M(33) ); + R( b, c, d, e, a, F2, K2, M(34) ); + R( a, b, c, d, e, F2, K2, M(35) ); + R( e, a, b, c, d, F2, K2, M(36) ); + R( d, e, a, b, c, F2, K2, M(37) ); + R( c, d, e, a, b, F2, K2, M(38) ); + R( b, c, d, e, a, F2, K2, M(39) ); + R( a, b, c, d, e, F3, K3, M(40) ); + R( e, a, b, c, d, F3, K3, M(41) ); + R( d, e, a, b, c, F3, K3, M(42) ); + R( c, d, e, a, b, F3, K3, M(43) ); + R( b, c, d, e, a, F3, K3, M(44) ); + R( a, b, c, d, e, F3, K3, M(45) ); + R( e, a, b, c, d, F3, K3, M(46) ); + R( d, e, a, b, c, F3, K3, M(47) ); + R( c, d, e, a, b, F3, K3, M(48) ); + R( b, c, d, e, a, F3, K3, M(49) ); + R( a, b, c, d, e, F3, K3, M(50) ); + R( e, a, b, c, d, F3, K3, M(51) ); + R( d, e, a, b, c, F3, K3, M(52) ); + R( c, d, e, a, b, F3, K3, M(53) ); + R( b, c, d, e, a, F3, K3, M(54) ); + R( a, b, c, d, e, F3, K3, M(55) ); + R( e, a, b, c, d, F3, K3, M(56) ); + R( d, e, a, b, c, F3, K3, M(57) ); + R( c, d, e, a, b, F3, K3, M(58) ); + R( b, c, d, e, a, F3, K3, M(59) ); + R( a, b, c, d, e, F4, K4, M(60) ); + R( e, a, b, c, d, F4, K4, M(61) ); + R( d, e, a, b, c, F4, K4, M(62) ); + R( c, d, e, a, b, F4, K4, M(63) ); + R( b, c, d, e, a, F4, K4, M(64) ); + R( a, b, c, d, e, F4, K4, M(65) ); + R( e, a, b, c, d, F4, K4, M(66) ); + R( d, e, a, b, c, F4, K4, M(67) ); + R( c, d, e, a, b, F4, K4, M(68) ); + R( b, c, d, e, a, F4, K4, M(69) ); + R( a, b, c, d, e, F4, K4, M(70) ); + R( e, a, b, c, d, F4, K4, M(71) ); + R( d, e, a, b, c, F4, K4, M(72) ); + R( c, d, e, a, b, F4, K4, M(73) ); + R( b, c, d, e, a, F4, K4, M(74) ); + R( a, b, c, d, e, F4, K4, M(75) ); + R( e, a, b, c, d, F4, K4, M(76) ); + R( d, e, a, b, c, F4, K4, M(77) ); + R( c, d, e, a, b, F4, K4, M(78) ); + R( b, c, d, e, a, F4, K4, M(79) ); + + /* Update chaining vars. */ + hd->h0 += a; + hd->h1 += b; + hd->h2 += c; + hd->h3 += d; + hd->h4 += e; +} + +/* + * Update the message digest with the contents + * of INBUF with length INLEN. + */ +void +sha1_update(struct sha1_context *hd, const byte *inbuf, uint inlen) +{ + if (hd->count == 64) /* flush the buffer */ + { + sha1_transform(hd, hd->buf); + hd->count = 0; + hd->nblocks++; + } + if (!inbuf) + return; + + if (hd->count) + { + for (; inlen && hd->count < 64; inlen--) + hd->buf[hd->count++] = *inbuf++; + sha1_update( hd, NULL, 0 ); + if(!inlen) + return; + } + + while (inlen >= 64) + { + sha1_transform(hd, inbuf); + hd->count = 0; + hd->nblocks++; + inlen -= 64; + inbuf += 64; + } + for (; inlen && hd->count < 64; inlen--) + hd->buf[hd->count++] = *inbuf++; +} + +/* + * The routine final terminates the computation and + * returns the digest. + * The handle is prepared for a new cycle, but adding bytes to the + * handle will the destroy the returned buffer. + * Returns: 20 bytes representing the digest. + */ +byte * +sha1_final(struct sha1_context *hd) +{ + u32 t, msb, lsb; + u32 *p; + + sha1_update(hd, NULL, 0); /* flush */; + + t = hd->nblocks; + /* multiply by 64 to make a byte count */ + lsb = t << 6; + msb = t >> 26; + /* add the count */ + t = lsb; + if ((lsb += hd->count) < t) + msb++; + /* multiply by 8 to make a bit count */ + t = lsb; + lsb <<= 3; + msb <<= 3; + msb |= t >> 29; + + if (hd->count < 56) /* enough room */ + { + hd->buf[hd->count++] = 0x80; /* pad */ + while (hd->count < 56) + hd->buf[hd->count++] = 0; /* pad */ + } + else /* need one extra block */ + { + hd->buf[hd->count++] = 0x80; /* pad character */ + while (hd->count < 64) + hd->buf[hd->count++] = 0; + sha1_update(hd, NULL, 0); /* flush */; + memset(hd->buf, 0, 56 ); /* fill next block with zeroes */ + } + /* append the 64 bit count */ + hd->buf[56] = msb >> 24; + hd->buf[57] = msb >> 16; + hd->buf[58] = msb >> 8; + hd->buf[59] = msb ; + hd->buf[60] = lsb >> 24; + hd->buf[61] = lsb >> 16; + hd->buf[62] = lsb >> 8; + hd->buf[63] = lsb ; + sha1_transform(hd, hd->buf); + + p = (u32*) hd->buf; +#define X(a) do { put_u32(p, hd->h##a); p++; } while(0) + X(0); + X(1); + X(2); + X(3); + X(4); +#undef X + + return hd->buf; +} + + +/* + * SHA1-HMAC + */ + +/* + * Shortcut function which puts the hash value of the supplied buffer + * into outbuf which must have a size of 20 bytes. + */ +void +sha1_hash_buffer(byte *outbuf, const byte *buffer, uint length) +{ + struct sha1_context ctx; + + sha1_init(&ctx); + sha1_update(&ctx, buffer, length); + memcpy(outbuf, sha1_final(&ctx), SHA1_SIZE); +} + +void +sha1_hmac_init(struct sha1_hmac_context *ctx, const byte *key, uint keylen) +{ + byte keybuf[SHA1_BLOCK_SIZE], buf[SHA1_BLOCK_SIZE]; + + /* Hash the key if necessary */ + if (keylen <= SHA1_BLOCK_SIZE) + { + memcpy(keybuf, key, keylen); + bzero(keybuf + keylen, SHA1_BLOCK_SIZE - keylen); + } + else + { + sha1_hash_buffer(keybuf, key, keylen); + bzero(keybuf + SHA1_SIZE, SHA1_BLOCK_SIZE - SHA1_SIZE); + } + + /* Initialize the inner digest */ + sha1_init(&ctx->ictx); + int i; + for (i = 0; i < SHA1_BLOCK_SIZE; i++) + buf[i] = keybuf[i] ^ 0x36; + sha1_update(&ctx->ictx, buf, SHA1_BLOCK_SIZE); + + /* Initialize the outer digest */ + sha1_init(&ctx->octx); + for (i = 0; i < SHA1_BLOCK_SIZE; i++) + buf[i] = keybuf[i] ^ 0x5c; + sha1_update(&ctx->octx, buf, SHA1_BLOCK_SIZE); +} + +void +sha1_hmac_update(struct sha1_hmac_context *ctx, const byte *data, uint datalen) +{ + /* Just update the inner digest */ + sha1_update(&ctx->ictx, data, datalen); +} + +byte *sha1_hmac_final(struct sha1_hmac_context *ctx) +{ + /* Finish the inner digest */ + byte *isha = sha1_final(&ctx->ictx); + + /* Finish the outer digest */ + sha1_update(&ctx->octx, isha, SHA1_SIZE); + return sha1_final(&ctx->octx); +} + +void +sha1_hmac(byte *outbuf, const byte *key, uint keylen, const byte *data, uint datalen) +{ + struct sha1_hmac_context hd; + sha1_hmac_init(&hd, key, keylen); + sha1_hmac_update(&hd, data, datalen); + byte *osha = sha1_hmac_final(&hd); + memcpy(outbuf, osha, SHA1_SIZE); +} diff --git a/lib/sha1.h b/lib/sha1.h new file mode 100644 index 00000000..425160a0 --- /dev/null +++ b/lib/sha1.h @@ -0,0 +1,86 @@ +/* + * BIRD Library -- SHA-1 Hash Function (FIPS 180-1, RFC 3174) and HMAC-SHA-1 + * + * (c) 2015 CZ.NIC z.s.p.o. + * + * Based on the code from libucw-6.4 + * (c) 2008--2009 Martin Mares + * + * Based on the code from libgcrypt-1.2.3, which is + * (c) 1998, 2001, 2002, 2003 Free Software Foundation, Inc. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#ifndef _BIRD_SHA1_H_ +#define _BIRD_SHA1_H_ + +#include "nest/bird.h" + +/* + * Internal SHA1 state. + * You should use it just as an opaque handle only. + */ +struct sha1_context { + u32 h0,h1,h2,h3,h4; + u32 nblocks; + byte buf[64]; + int count; +} ; + +void sha1_init(struct sha1_context *hd); /* Initialize new algorithm run in the @hd context. **/ +/* + * Push another @inlen bytes of data pointed to by @inbuf onto the + * SHA1 hash currently in @hd. You can call this any times you want on + * the same hash (and you do not need to reinitialize it by + * @sha1_init()). It has the same effect as concatenating all the data + * together and passing them at once. + */ +void sha1_update(struct sha1_context *hd, const byte *inbuf, uint inlen); +/* + * No more @sha1_update() calls will be done. This terminates the hash + * and returns a pointer to it. + * + * Note that the pointer points into data in the @hd context. If it ceases + * to exist, the pointer becomes invalid. + * + * To convert the hash to its usual hexadecimal representation, see + * <>. + */ +byte *sha1_final(struct sha1_context *hd); + +/* + * A convenience one-shot function for SHA1 hash. + * It is equivalent to this snippet of code: + * + * sha1_context hd; + * sha1_init(&hd); + * sha1_update(&hd, buffer, length); + * memcpy(outbuf, sha1_final(&hd), SHA1_SIZE); + */ +void sha1_hash_buffer(byte *outbuf, const byte *buffer, uint length); + +/* + * SHA1 HMAC message authentication. If you provide @key and @data, + * the result will be stored in @outbuf. + */ +void sha1_hmac(byte *outbuf, const byte *key, uint keylen, const byte *data, uint datalen); + +/* + * The HMAC also exists in a stream version in a way analogous to the + * plain SHA1. Pass this as a context. + */ +struct sha1_hmac_context { + struct sha1_context ictx; + struct sha1_context octx; +}; + +void sha1_hmac_init(struct sha1_hmac_context *hd, const byte *key, uint keylen); /* Initialize HMAC with context @hd and the given key. See sha1_init(). */ +void sha1_hmac_update(struct sha1_hmac_context *hd, const byte *data, uint datalen); /* Hash another @datalen bytes of data. See sha1_update(). */ +byte *sha1_hmac_final(struct sha1_hmac_context *hd); /* Terminate the HMAC and return a pointer to the allocated hash. See sha1_final(). */ + +#define SHA1_SIZE 20 /* Size of the SHA1 hash in its binary representation **/ +#define SHA1_HEX_SIZE 41 /* Buffer length for a string containing SHA1 in hexadecimal format. **/ +#define SHA1_BLOCK_SIZE 64 /* SHA1 splits input to blocks of this size. **/ + +#endif /* _BIRD_SHA1_H_ */ From 4035e0e79c609bde30a2f755eec864771ea08e21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Tvrd=C3=ADk?= Date: Fri, 13 Nov 2015 16:13:15 +0100 Subject: [PATCH 12/18] Add SHA-224/256 and HMAC-SHA-224/256 crypto hash --- lib/Modules | 2 + lib/sha256.c | 467 +++++++++++++++++++++++++++++++++++++++++++++++++++ lib/sha256.h | 71 ++++++++ 3 files changed, 540 insertions(+) create mode 100644 lib/sha256.c create mode 100644 lib/sha256.h diff --git a/lib/Modules b/lib/Modules index a227f9a1..b3daf25d 100644 --- a/lib/Modules +++ b/lib/Modules @@ -1,3 +1,5 @@ +sha256.c +sha256.h sha1.c sha1.h birdlib.h diff --git a/lib/sha256.c b/lib/sha256.c new file mode 100644 index 00000000..2d979f90 --- /dev/null +++ b/lib/sha256.c @@ -0,0 +1,467 @@ +/* + * BIRD Library -- SHA-256 and SHA-224 Hash Functions, + * HMAC-SHA-256 and HMAC-SHA-224 Functions + * + * (c) 2015 CZ.NIC z.s.p.o. + * + * Based on the code from libgcrypt-1.6.0, which is + * (c) 2003, 2006, 2008, 2009 Free Software Foundation, Inc. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include "lib/sha256.h" +#include "lib/unaligned.h" + +static uint sha256_transform(void *ctx, const byte *data, size_t nblks); + +void +sha256_init(struct sha256_context *ctx) +{ + ctx->h0 = 0x6a09e667; + ctx->h1 = 0xbb67ae85; + ctx->h2 = 0x3c6ef372; + ctx->h3 = 0xa54ff53a; + ctx->h4 = 0x510e527f; + ctx->h5 = 0x9b05688c; + ctx->h6 = 0x1f83d9ab; + ctx->h7 = 0x5be0cd19; + + ctx->nblocks = 0; + ctx->nblocks_high = 0; + ctx->count = 0; + ctx->blocksize = 64; + ctx->transform = sha256_transform; +} + +void +sha224_init(struct sha224_context *ctx) +{ + ctx->h0 = 0xc1059ed8; + ctx->h1 = 0x367cd507; + ctx->h2 = 0x3070dd17; + ctx->h3 = 0xf70e5939; + ctx->h4 = 0xffc00b31; + ctx->h5 = 0x68581511; + ctx->h6 = 0x64f98fa7; + ctx->h7 = 0xbefa4fa4; + + ctx->nblocks = 0; + ctx->nblocks_high = 0; + ctx->count = 0; + ctx->blocksize = 64; + ctx->transform = sha256_transform; +} + +/* (4.2) same as SHA-1's F1. */ +static inline u32 +f1(u32 x, u32 y, u32 z) +{ + return (z ^ (x & (y ^ z))); +} + +/* (4.3) same as SHA-1's F3 */ +static inline u32 +f3(u32 x, u32 y, u32 z) +{ + return ((x & y) | (z & (x|y))); +} + +/* Bitwise rotation of an uint to the right */ +static inline u32 ror(u32 x, int n) +{ + return ( (x >> (n&(32-1))) | (x << ((32-n)&(32-1))) ); +} + +/* (4.4) */ +static inline u32 +sum0(u32 x) +{ + return (ror(x, 2) ^ ror(x, 13) ^ ror(x, 22)); +} + +/* (4.5) */ +static inline u32 +sum1(u32 x) +{ + return (ror(x, 6) ^ ror(x, 11) ^ ror(x, 25)); +} + +/* + Transform the message X which consists of 16 32-bit-words. See FIPS + 180-2 for details. */ +#define S0(x) (ror((x), 7) ^ ror((x), 18) ^ ((x) >> 3)) /* (4.6) */ +#define S1(x) (ror((x), 17) ^ ror((x), 19) ^ ((x) >> 10)) /* (4.7) */ +#define R(a,b,c,d,e,f,g,h,k,w) \ + do \ + { \ + t1 = (h) + sum1((e)) + f1((e),(f),(g)) + (k) + (w); \ + t2 = sum0((a)) + f3((a),(b),(c)); \ + h = g; \ + g = f; \ + f = e; \ + e = d + t1; \ + d = c; \ + c = b; \ + b = a; \ + a = t1 + t2; \ + } while (0) + +/* + The SHA-256 core: Transform the message X which consists of 16 + 32-bit-words. See FIPS 180-2 for details. + */ +static uint +sha256_transform_block(struct sha256_context *ctx, const byte *data) +{ + static const u32 K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + }; + + u32 a,b,c,d,e,f,g,h,t1,t2; + u32 w[64]; + int i; + + a = ctx->h0; + b = ctx->h1; + c = ctx->h2; + d = ctx->h3; + e = ctx->h4; + f = ctx->h5; + g = ctx->h6; + h = ctx->h7; + + for (i = 0; i < 16; i++) + w[i] = get_u32(data + i * 4); + for (; i < 64; i++) + w[i] = S1(w[i-2]) + w[i-7] + S0(w[i-15]) + w[i-16]; + + for (i = 0; i < 64;) + { + t1 = h + sum1(e) + f1(e, f, g) + K[i] + w[i]; + t2 = sum0 (a) + f3(a, b, c); + d += t1; + h = t1 + t2; + + t1 = g + sum1(d) + f1(d, e, f) + K[i+1] + w[i+1]; + t2 = sum0 (h) + f3(h, a, b); + c += t1; + g = t1 + t2; + + t1 = f + sum1(c) + f1(c, d, e) + K[i+2] + w[i+2]; + t2 = sum0 (g) + f3(g, h, a); + b += t1; + f = t1 + t2; + + t1 = e + sum1(b) + f1(b, c, d) + K[i+3] + w[i+3]; + t2 = sum0 (f) + f3(f, g, h); + a += t1; + e = t1 + t2; + + t1 = d + sum1(a) + f1(a, b, c) + K[i+4] + w[i+4]; + t2 = sum0 (e) + f3(e, f, g); + h += t1; + d = t1 + t2; + + t1 = c + sum1(h) + f1(h, a, b) + K[i+5] + w[i+5]; + t2 = sum0 (d) + f3(d, e, f); + g += t1; + c = t1 + t2; + + t1 = b + sum1(g) + f1(g, h, a) + K[i+6] + w[i+6]; + t2 = sum0 (c) + f3(c, d, e); + f += t1; + b = t1 + t2; + + t1 = a + sum1(f) + f1(f, g, h) + K[i+7] + w[i+7]; + t2 = sum0 (b) + f3(b, c, d); + e += t1; + a = t1 + t2; + + i += 8; + } + + ctx->h0 += a; + ctx->h1 += b; + ctx->h2 += c; + ctx->h3 += d; + ctx->h4 += e; + ctx->h5 += f; + ctx->h6 += g; + ctx->h7 += h; + + return /*burn_stack*/ 74*4+32; +} +#undef S0 +#undef S1 +#undef R + +static uint +sha256_transform(void *ctx, const byte *data, size_t nblks) +{ + struct sha256_context *hd = ctx; + uint burn; + + do + { + burn = sha256_transform_block(hd, data); + data += 64; + } + while (--nblks); + + return burn; +} + +/* Common function to write a chunk of data to the transform function + of a hash algorithm. Note that the use of the term "block" does + not imply a fixed size block. Note that we explicitly allow to use + this function after the context has been finalized; the result does + not have any meaning but writing after finalize is sometimes + helpful to mitigate timing attacks. */ +void +sha256_update(struct sha256_context *ctx, const byte *in_buf, size_t in_len) +{ + const uint blocksize = ctx->blocksize; + size_t inblocks; + + if (sizeof(ctx->buf) < blocksize) + debug("BUG: in file %s at line %d", __FILE__ , __LINE__); + + if (ctx->count == blocksize) /* Flush the buffer. */ + { + ctx->transform(ctx, ctx->buf, 1); + ctx->count = 0; + if (!++ctx->nblocks) + ctx->nblocks_high++; + } + if (!in_buf) + return; + + if (ctx->count) + { + for (; in_len && ctx->count < blocksize; in_len--) + ctx->buf[ctx->count++] = *in_buf++; + sha256_update(ctx, NULL, 0); + if (!in_len) + return; + } + + if (in_len >= blocksize) + { + inblocks = in_len / blocksize; + ctx->transform(ctx, in_buf, inblocks); + ctx->count = 0; + ctx->nblocks_high += (ctx->nblocks + inblocks < inblocks); + ctx->nblocks += inblocks; + in_len -= inblocks * blocksize; + in_buf += inblocks * blocksize; + } + for (; in_len && ctx->count < blocksize; in_len--) + ctx->buf[ctx->count++] = *in_buf++; +} + +/* + The routine finally terminates the computation and returns the + digest. The handle is prepared for a new cycle, but adding bytes + to the handle will the destroy the returned buffer. Returns: 32 + bytes with the message the digest. */ +byte* +sha256_final(struct sha256_context *ctx) +{ + u32 t, th, msb, lsb; + byte *p; + + sha256_update(ctx, NULL, 0); /* flush */; + + t = ctx->nblocks; + if (sizeof t == sizeof ctx->nblocks) + th = ctx->nblocks_high; + else + th = 0; + + /* multiply by 64 to make a byte count */ + lsb = t << 6; + msb = (th << 6) | (t >> 26); + /* add the count */ + t = lsb; + if ((lsb += ctx->count) < t) + msb++; + /* multiply by 8 to make a bit count */ + t = lsb; + lsb <<= 3; + msb <<= 3; + msb |= t >> 29; + + if (ctx->count < 56) + { /* enough room */ + ctx->buf[ctx->count++] = 0x80; /* pad */ + while (ctx->count < 56) + ctx->buf[ctx->count++] = 0; /* pad */ + } + else + { /* need one extra block */ + ctx->buf[ctx->count++] = 0x80; /* pad character */ + while (ctx->count < 64) + ctx->buf[ctx->count++] = 0; + sha256_update(ctx, NULL, 0); /* flush */; + memset (ctx->buf, 0, 56 ); /* fill next block with zeroes */ + } + /* append the 64 bit count */ + put_u32(ctx->buf + 56, msb); + put_u32(ctx->buf + 60, lsb); + sha256_transform(ctx, ctx->buf, 1); + + p = ctx->buf; + +#define X(a) do { put_u32(p, ctx->h##a); p += 4; } while(0) + X(0); + X(1); + X(2); + X(3); + X(4); + X(5); + X(6); + X(7); +#undef X + + return ctx->buf; +} + + +/* + * SHA256-HMAC + */ + +static void +sha256_hash_buffer(byte *outbuf, const byte *buffer, size_t length) +{ + struct sha256_context hd_tmp; + + sha256_init(&hd_tmp); + sha256_update(&hd_tmp, buffer, length); + memcpy(outbuf, sha256_final(&hd_tmp), SHA256_SIZE); +} + +void +sha256_hmac_init(struct sha256_hmac_context *ctx, const byte *key, size_t keylen) +{ + byte keybuf[SHA256_BLOCK_SIZE], buf[SHA256_BLOCK_SIZE]; + + /* Hash the key if necessary */ + if (keylen <= SHA256_BLOCK_SIZE) + { + memcpy(keybuf, key, keylen); + bzero(keybuf + keylen, SHA256_BLOCK_SIZE - keylen); + } + else + { + sha256_hash_buffer(keybuf, key, keylen); + bzero(keybuf + SHA256_SIZE, SHA256_BLOCK_SIZE - SHA256_SIZE); + } + + /* Initialize the inner digest */ + sha256_init(&ctx->ictx); + int i; + for (i = 0; i < SHA256_BLOCK_SIZE; i++) + buf[i] = keybuf[i] ^ 0x36; + sha256_update(&ctx->ictx, buf, SHA256_BLOCK_SIZE); + + /* Initialize the outer digest */ + sha256_init(&ctx->octx); + for (i = 0; i < SHA256_BLOCK_SIZE; i++) + buf[i] = keybuf[i] ^ 0x5c; + sha256_update(&ctx->octx, buf, SHA256_BLOCK_SIZE); +} + +void sha256_hmac_update(struct sha256_hmac_context *ctx, const byte *buf, size_t buflen) +{ + /* Just update the inner digest */ + sha256_update(&ctx->ictx, buf, buflen); +} + +byte *sha256_hmac_final(struct sha256_hmac_context *ctx) +{ + /* Finish the inner digest */ + byte *isha = sha256_final(&ctx->ictx); + + /* Finish the outer digest */ + sha256_update(&ctx->octx, isha, SHA256_SIZE); + return sha256_final(&ctx->octx); +} + + +/* + * SHA224-HMAC + */ + +static void +sha224_hash_buffer(byte *outbuf, const byte *buffer, size_t length) +{ + struct sha224_context hd_tmp; + + sha224_init(&hd_tmp); + sha224_update(&hd_tmp, buffer, length); + memcpy(outbuf, sha224_final(&hd_tmp), SHA224_SIZE); +} + +void +sha224_hmac_init(struct sha224_hmac_context *ctx, const byte *key, size_t keylen) +{ + byte keybuf[SHA224_BLOCK_SIZE], buf[SHA224_BLOCK_SIZE]; + + /* Hash the key if necessary */ + if (keylen <= SHA224_BLOCK_SIZE) + { + memcpy(keybuf, key, keylen); + bzero(keybuf + keylen, SHA224_BLOCK_SIZE - keylen); + } + else + { + sha224_hash_buffer(keybuf, key, keylen); + bzero(keybuf + SHA224_SIZE, SHA224_BLOCK_SIZE - SHA224_SIZE); + } + + /* Initialize the inner digest */ + sha224_init(&ctx->ictx); + int i; + for (i = 0; i < SHA224_BLOCK_SIZE; i++) + buf[i] = keybuf[i] ^ 0x36; + sha224_update(&ctx->ictx, buf, SHA224_BLOCK_SIZE); + + /* Initialize the outer digest */ + sha224_init(&ctx->octx); + for (i = 0; i < SHA224_BLOCK_SIZE; i++) + buf[i] = keybuf[i] ^ 0x5c; + sha224_update(&ctx->octx, buf, SHA224_BLOCK_SIZE); +} + +void sha224_hmac_update(struct sha224_hmac_context *ctx, const byte *buf, size_t buflen) +{ + /* Just update the inner digest */ + sha256_update(&ctx->ictx, buf, buflen); +} + +byte *sha224_hmac_final(struct sha224_hmac_context *ctx) +{ + /* Finish the inner digest */ + byte *isha = sha224_final(&ctx->ictx); + + /* Finish the outer digest */ + sha224_update(&ctx->octx, isha, SHA224_SIZE); + return sha224_final(&ctx->octx); +} diff --git a/lib/sha256.h b/lib/sha256.h new file mode 100644 index 00000000..848d2176 --- /dev/null +++ b/lib/sha256.h @@ -0,0 +1,71 @@ +/* + * BIRD Library -- SHA-256 and SHA-224 Hash Functions, + * HMAC-SHA-256 and HMAC-SHA-224 Functions + * + * (c) 2015 CZ.NIC z.s.p.o. + * + * Based on the code from libgcrypt-1.6.0, which is + * (c) 2003, 2006, 2008, 2009 Free Software Foundation, Inc. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#ifndef _BIRD_SHA256_H_ +#define _BIRD_SHA256_H_ + +#include "nest/bird.h" + +#define SHA224_SIZE 28 +#define SHA224_HEX_SIZE 57 +#define SHA224_BLOCK_SIZE 64 + +#define SHA256_SIZE 32 +#define SHA256_HEX_SIZE 65 +#define SHA256_BLOCK_SIZE 64 + +struct sha256_context { + u32 h0,h1,h2,h3,h4,h5,h6,h7; + byte buf[128]; /* 128 is for SHA384 and SHA512 support, otherwise for SHA224 and SHA256 is 64 enough */ + u32 nblocks; + u32 nblocks_high; + int count; + u32 blocksize; + uint (*transform)(void *c, const byte *blks, size_t nblks); +}; +#define sha224_context sha256_context /* aliasing 'struct sha224_context' to 'struct sha256_context' */ + +void sha256_init(struct sha256_context *ctx); +void sha224_init(struct sha224_context *ctx); + +void sha256_update(struct sha256_context *ctx, const byte *in_buf, size_t in_len); +static inline void sha224_update(struct sha224_context *ctx, const byte *in_buf, size_t in_len) +{ + sha256_update(ctx, in_buf, in_len); +} + +byte* sha256_final(struct sha256_context *ctx); +static inline byte* sha224_final(struct sha224_context *ctx) +{ + return sha256_final(ctx); +} + +/* + * HMAC-SHA256, HMAC-SHA224 + */ +struct sha256_hmac_context +{ + struct sha256_context ictx; + struct sha256_context octx; +}; +#define sha224_hmac_context sha256_hmac_context /* aliasing 'struct sha224_hmac_context' to 'struct sha256_hmac_context' */ + +void sha256_hmac_init(struct sha256_hmac_context *ctx, const byte *key, size_t keylen); +void sha224_hmac_init(struct sha224_hmac_context *ctx, const byte *key, size_t keylen); + +void sha256_hmac_update(struct sha256_hmac_context *ctx, const byte *buf, size_t buflen); +void sha224_hmac_update(struct sha224_hmac_context *ctx, const byte *buf, size_t buflen); + +byte *sha256_hmac_final(struct sha256_hmac_context *ctx); +byte *sha224_hmac_final(struct sha224_hmac_context *ctx); + +#endif /* _BIRD_SHA256_H_ */ From f312a837e919c660884ceb9c50c106df1e4c0658 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Tvrd=C3=ADk?= Date: Fri, 13 Nov 2015 16:14:36 +0100 Subject: [PATCH 13/18] Add SHA-384/512 and HMAC-SHA-384/512 crypto hash --- lib/Modules | 2 + lib/sha512.c | 614 +++++++++++++++++++++++++++++++++++++++++++++++++++ lib/sha512.h | 73 ++++++ 3 files changed, 689 insertions(+) create mode 100644 lib/sha512.c create mode 100644 lib/sha512.h diff --git a/lib/Modules b/lib/Modules index b3daf25d..745306d9 100644 --- a/lib/Modules +++ b/lib/Modules @@ -1,5 +1,7 @@ sha256.c sha256.h +sha512.c +sha512.h sha1.c sha1.h birdlib.h diff --git a/lib/sha512.c b/lib/sha512.c new file mode 100644 index 00000000..e46e4c98 --- /dev/null +++ b/lib/sha512.c @@ -0,0 +1,614 @@ +/* + * BIRD Library -- SHA-512 and SHA-384 Hash Functions, + * HMAC-SHA-512 and HMAC-SHA-384 Functions + * + * (c) 2015 CZ.NIC z.s.p.o. + * + * Based on the code from libgcrypt-1.6.0, which is + * (c) 2003, 2006, 2008, 2009 Free Software Foundation, Inc. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include "lib/sha256.h" +#include "lib/sha512.h" +#include "lib/unaligned.h" + +static uint sha512_transform(void *context, const byte *data, size_t nblks); + +void +sha512_init(struct sha512_context *ctx) +{ + struct sha512_state *hd = &ctx->state; + + hd->h0 = UINT64_C(0x6a09e667f3bcc908); + hd->h1 = UINT64_C(0xbb67ae8584caa73b); + hd->h2 = UINT64_C(0x3c6ef372fe94f82b); + hd->h3 = UINT64_C(0xa54ff53a5f1d36f1); + hd->h4 = UINT64_C(0x510e527fade682d1); + hd->h5 = UINT64_C(0x9b05688c2b3e6c1f); + hd->h6 = UINT64_C(0x1f83d9abfb41bd6b); + hd->h7 = UINT64_C(0x5be0cd19137e2179); + + ctx->bctx.nblocks = 0; + ctx->bctx.nblocks_high = 0; + ctx->bctx.count = 0; + ctx->bctx.blocksize = 128; + ctx->bctx.transform = sha512_transform; +} + +void +sha384_init(struct sha384_context *ctx) +{ + struct sha512_state *hd = &ctx->state; + + hd->h0 = UINT64_C(0xcbbb9d5dc1059ed8); + hd->h1 = UINT64_C(0x629a292a367cd507); + hd->h2 = UINT64_C(0x9159015a3070dd17); + hd->h3 = UINT64_C(0x152fecd8f70e5939); + hd->h4 = UINT64_C(0x67332667ffc00b31); + hd->h5 = UINT64_C(0x8eb44a8768581511); + hd->h6 = UINT64_C(0xdb0c2e0d64f98fa7); + hd->h7 = UINT64_C(0x47b5481dbefa4fa4); + + ctx->bctx.nblocks = 0; + ctx->bctx.nblocks_high = 0; + ctx->bctx.count = 0; + ctx->bctx.blocksize = 128; + ctx->bctx.transform = sha512_transform; +} + +void sha512_update(struct sha512_context *ctx, const byte *in_buf, size_t in_len) +{ + sha256_update(&ctx->bctx, in_buf, in_len); +} + +static inline u64 +ROTR(u64 x, u64 n) +{ + return ((x >> n) | (x << (64 - n))); +} + +static inline u64 +Ch(u64 x, u64 y, u64 z) +{ + return ((x & y) ^ ( ~x & z)); +} + +static inline u64 +Maj(u64 x, u64 y, u64 z) +{ + return ((x & y) ^ (x & z) ^ (y & z)); +} + +static inline u64 +Sum0(u64 x) +{ + return (ROTR (x, 28) ^ ROTR (x, 34) ^ ROTR (x, 39)); +} + +static inline u64 +Sum1 (u64 x) +{ + return (ROTR (x, 14) ^ ROTR (x, 18) ^ ROTR (x, 41)); +} + +static const u64 k[] = +{ + UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd), + UINT64_C(0xb5c0fbcfec4d3b2f), UINT64_C(0xe9b5dba58189dbbc), + UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019), + UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118), + UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe), + UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2), + UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1), + UINT64_C(0x9bdc06a725c71235), UINT64_C(0xc19bf174cf692694), + UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3), + UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65), + UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483), + UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5), + UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210), + UINT64_C(0xb00327c898fb213f), UINT64_C(0xbf597fc7beef0ee4), + UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725), + UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70), + UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926), + UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df), + UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8), + UINT64_C(0x81c2c92e47edaee6), UINT64_C(0x92722c851482353b), + UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001), + UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30), + UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910), + UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8), + UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53), + UINT64_C(0x2748774cdf8eeb99), UINT64_C(0x34b0bcb5e19b48a8), + UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb), + UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3), + UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60), + UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec), + UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9), + UINT64_C(0xbef9a3f7b2c67915), UINT64_C(0xc67178f2e372532b), + UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207), + UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178), + UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6), + UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b), + UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493), + UINT64_C(0x3c9ebe0a15c9bebc), UINT64_C(0x431d67c49c100d4c), + UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a), + UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817) +}; + +/* + * Transform the message W which consists of 16 64-bit-words + */ +static uint +sha512_transform_block(struct sha512_state *hd, const byte *data) +{ + u64 a, b, c, d, e, f, g, h; + u64 w[16]; + int t; + + /* get values from the chaining vars */ + a = hd->h0; + b = hd->h1; + c = hd->h2; + d = hd->h3; + e = hd->h4; + f = hd->h5; + g = hd->h6; + h = hd->h7; + + for ( t = 0; t < 16; t++ ) + w[t] = get_u64(data + t * 8); + +#define S0(x) (ROTR((x),1) ^ ROTR((x),8) ^ ((x)>>7)) +#define S1(x) (ROTR((x),19) ^ ROTR((x),61) ^ ((x)>>6)) + + for (t = 0; t < 80 - 16; ) + { + u64 t1, t2; + + /* Performance on a AMD Athlon(tm) Dual Core Processor 4050e + with gcc 4.3.3 using gcry_md_hash_buffer of each 10000 bytes + initialized to 0,1,2,3...255,0,... and 1000 iterations: + + Not unrolled with macros: 440ms + Unrolled with macros: 350ms + Unrolled with inline: 330ms + */ +#if 0 /* Not unrolled. */ + t1 = h + Sum1 (e) + Ch(e, f, g) + k[t] + w[t%16]; + w[t%16] += S1 (w[(t - 2)%16]) + w[(t - 7)%16] + S0 (w[(t - 15)%16]); + t2 = Sum0 (a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; +#else /* Unrolled to interweave the chain variables. */ + t1 = h + Sum1 (e) + Ch(e, f, g) + k[t] + w[0]; + w[0] += S1 (w[14]) + w[9] + S0 (w[1]); + t2 = Sum0 (a) + Maj(a, b, c); + d += t1; + h = t1 + t2; + + t1 = g + Sum1 (d) + Ch(d, e, f) + k[t+1] + w[1]; + w[1] += S1 (w[15]) + w[10] + S0 (w[2]); + t2 = Sum0 (h) + Maj(h, a, b); + c += t1; + g = t1 + t2; + + t1 = f + Sum1 (c) + Ch(c, d, e) + k[t+2] + w[2]; + w[2] += S1 (w[0]) + w[11] + S0 (w[3]); + t2 = Sum0 (g) + Maj(g, h, a); + b += t1; + f = t1 + t2; + + t1 = e + Sum1 (b) + Ch(b, c, d) + k[t+3] + w[3]; + w[3] += S1 (w[1]) + w[12] + S0 (w[4]); + t2 = Sum0 (f) + Maj(f, g, h); + a += t1; + e = t1 + t2; + + t1 = d + Sum1 (a) + Ch(a, b, c) + k[t+4] + w[4]; + w[4] += S1 (w[2]) + w[13] + S0 (w[5]); + t2 = Sum0 (e) + Maj(e, f, g); + h += t1; + d = t1 + t2; + + t1 = c + Sum1 (h) + Ch(h, a, b) + k[t+5] + w[5]; + w[5] += S1 (w[3]) + w[14] + S0 (w[6]); + t2 = Sum0 (d) + Maj(d, e, f); + g += t1; + c = t1 + t2; + + t1 = b + Sum1 (g) + Ch(g, h, a) + k[t+6] + w[6]; + w[6] += S1 (w[4]) + w[15] + S0 (w[7]); + t2 = Sum0 (c) + Maj(c, d, e); + f += t1; + b = t1 + t2; + + t1 = a + Sum1 (f) + Ch(f, g, h) + k[t+7] + w[7]; + w[7] += S1 (w[5]) + w[0] + S0 (w[8]); + t2 = Sum0 (b) + Maj(b, c, d); + e += t1; + a = t1 + t2; + + t1 = h + Sum1 (e) + Ch(e, f, g) + k[t+8] + w[8]; + w[8] += S1 (w[6]) + w[1] + S0 (w[9]); + t2 = Sum0 (a) + Maj(a, b, c); + d += t1; + h = t1 + t2; + + t1 = g + Sum1 (d) + Ch(d, e, f) + k[t+9] + w[9]; + w[9] += S1 (w[7]) + w[2] + S0 (w[10]); + t2 = Sum0 (h) + Maj(h, a, b); + c += t1; + g = t1 + t2; + + t1 = f + Sum1 (c) + Ch(c, d, e) + k[t+10] + w[10]; + w[10] += S1 (w[8]) + w[3] + S0 (w[11]); + t2 = Sum0 (g) + Maj(g, h, a); + b += t1; + f = t1 + t2; + + t1 = e + Sum1 (b) + Ch(b, c, d) + k[t+11] + w[11]; + w[11] += S1 (w[9]) + w[4] + S0 (w[12]); + t2 = Sum0 (f) + Maj(f, g, h); + a += t1; + e = t1 + t2; + + t1 = d + Sum1 (a) + Ch(a, b, c) + k[t+12] + w[12]; + w[12] += S1 (w[10]) + w[5] + S0 (w[13]); + t2 = Sum0 (e) + Maj(e, f, g); + h += t1; + d = t1 + t2; + + t1 = c + Sum1 (h) + Ch(h, a, b) + k[t+13] + w[13]; + w[13] += S1 (w[11]) + w[6] + S0 (w[14]); + t2 = Sum0 (d) + Maj(d, e, f); + g += t1; + c = t1 + t2; + + t1 = b + Sum1 (g) + Ch(g, h, a) + k[t+14] + w[14]; + w[14] += S1 (w[12]) + w[7] + S0 (w[15]); + t2 = Sum0 (c) + Maj(c, d, e); + f += t1; + b = t1 + t2; + + t1 = a + Sum1 (f) + Ch(f, g, h) + k[t+15] + w[15]; + w[15] += S1 (w[13]) + w[8] + S0 (w[0]); + t2 = Sum0 (b) + Maj(b, c, d); + e += t1; + a = t1 + t2; + + t += 16; +#endif + } + + for (; t < 80; ) + { + u64 t1, t2; + +#if 0 /* Not unrolled. */ + t1 = h + Sum1 (e) + Ch(e, f, g) + k[t] + w[t%16]; + t2 = Sum0 (a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; +#else /* Unrolled to interweave the chain variables. */ + t1 = h + Sum1 (e) + Ch(e, f, g) + k[t] + w[0]; + t2 = Sum0 (a) + Maj(a, b, c); + d += t1; + h = t1 + t2; + + t1 = g + Sum1 (d) + Ch(d, e, f) + k[t+1] + w[1]; + t2 = Sum0 (h) + Maj(h, a, b); + c += t1; + g = t1 + t2; + + t1 = f + Sum1 (c) + Ch(c, d, e) + k[t+2] + w[2]; + t2 = Sum0 (g) + Maj(g, h, a); + b += t1; + f = t1 + t2; + + t1 = e + Sum1 (b) + Ch(b, c, d) + k[t+3] + w[3]; + t2 = Sum0 (f) + Maj(f, g, h); + a += t1; + e = t1 + t2; + + t1 = d + Sum1 (a) + Ch(a, b, c) + k[t+4] + w[4]; + t2 = Sum0 (e) + Maj(e, f, g); + h += t1; + d = t1 + t2; + + t1 = c + Sum1 (h) + Ch(h, a, b) + k[t+5] + w[5]; + t2 = Sum0 (d) + Maj(d, e, f); + g += t1; + c = t1 + t2; + + t1 = b + Sum1 (g) + Ch(g, h, a) + k[t+6] + w[6]; + t2 = Sum0 (c) + Maj(c, d, e); + f += t1; + b = t1 + t2; + + t1 = a + Sum1 (f) + Ch(f, g, h) + k[t+7] + w[7]; + t2 = Sum0 (b) + Maj(b, c, d); + e += t1; + a = t1 + t2; + + t1 = h + Sum1 (e) + Ch(e, f, g) + k[t+8] + w[8]; + t2 = Sum0 (a) + Maj(a, b, c); + d += t1; + h = t1 + t2; + + t1 = g + Sum1 (d) + Ch(d, e, f) + k[t+9] + w[9]; + t2 = Sum0 (h) + Maj(h, a, b); + c += t1; + g = t1 + t2; + + t1 = f + Sum1 (c) + Ch(c, d, e) + k[t+10] + w[10]; + t2 = Sum0 (g) + Maj(g, h, a); + b += t1; + f = t1 + t2; + + t1 = e + Sum1 (b) + Ch(b, c, d) + k[t+11] + w[11]; + t2 = Sum0 (f) + Maj(f, g, h); + a += t1; + e = t1 + t2; + + t1 = d + Sum1 (a) + Ch(a, b, c) + k[t+12] + w[12]; + t2 = Sum0 (e) + Maj(e, f, g); + h += t1; + d = t1 + t2; + + t1 = c + Sum1 (h) + Ch(h, a, b) + k[t+13] + w[13]; + t2 = Sum0 (d) + Maj(d, e, f); + g += t1; + c = t1 + t2; + + t1 = b + Sum1 (g) + Ch(g, h, a) + k[t+14] + w[14]; + t2 = Sum0 (c) + Maj(c, d, e); + f += t1; + b = t1 + t2; + + t1 = a + Sum1 (f) + Ch(f, g, h) + k[t+15] + w[15]; + t2 = Sum0 (b) + Maj(b, c, d); + e += t1; + a = t1 + t2; + + t += 16; +#endif + } + + /* Update chaining vars. */ + hd->h0 += a; + hd->h1 += b; + hd->h2 += c; + hd->h3 += d; + hd->h4 += e; + hd->h5 += f; + hd->h6 += g; + hd->h7 += h; + + return /* burn_stack */ (8 + 16) * sizeof(u64) + sizeof(u32) + 3 * sizeof(void*); +} + +static uint +sha512_transform(void *context, const byte *data, size_t nblks) +{ + struct sha512_context *ctx = context; + uint burn; + + do + { + burn = sha512_transform_block(&ctx->state, data) + 3 * sizeof(void*); + data += 128; + } + while(--nblks); + + return burn; +} + +/* The routine final terminates the computation and + * returns the digest. + * The handle is prepared for a new cycle, but adding bytes to the + * handle will the destroy the returned buffer. + * Returns: 64 bytes representing the digest. When used for sha384, + * we take the leftmost 48 of those bytes. + */ +byte * +sha512_final(struct sha512_context *ctx) +{ + u64 t, th, msb, lsb; + byte *p; + + sha256_update(&ctx->bctx, NULL, 0); /* flush */ ; + + t = ctx->bctx.nblocks; + /* if (sizeof t == sizeof ctx->bctx.nblocks) */ + th = ctx->bctx.nblocks_high; + /* else */ + /* th = ctx->bctx.nblocks >> 64; In case we ever use u128 */ + + /* multiply by 128 to make a byte count */ + lsb = t << 7; + msb = (th << 7) | (t >> 57); + /* add the count */ + t = lsb; + if ((lsb += ctx->bctx.count) < t) + msb++; + /* multiply by 8 to make a bit count */ + t = lsb; + lsb <<= 3; + msb <<= 3; + msb |= t >> 61; + + if (ctx->bctx.count < 112) + { /* enough room */ + ctx->bctx.buf[ctx->bctx.count++] = 0x80; /* pad */ + while(ctx->bctx.count < 112) + ctx->bctx.buf[ctx->bctx.count++] = 0; /* pad */ + } + else + { /* need one extra block */ + ctx->bctx.buf[ctx->bctx.count++] = 0x80; /* pad character */ + while(ctx->bctx.count < 128) + ctx->bctx.buf[ctx->bctx.count++] = 0; + sha256_update(&ctx->bctx, NULL, 0); /* flush */ ; + memset(ctx->bctx.buf, 0, 112); /* fill next block with zeroes */ + } + /* append the 128 bit count */ + put_u64(ctx->bctx.buf + 112, msb); + put_u64(ctx->bctx.buf + 120, lsb); + sha512_transform(ctx, ctx->bctx.buf, 1); + + p = ctx->bctx.buf; +#define X(a) do { put_u64(p, ctx->state.h##a); p += 8; } while(0) + X (0); + X (1); + X (2); + X (3); + X (4); + X (5); + /* Note that these last two chunks are included even for SHA384. + We just ignore them. */ + X (6); + X (7); +#undef X + + return ctx->bctx.buf; +} + + +/* + * SHA512-HMAC + */ + +static void +sha512_hash_buffer(byte *outbuf, const byte *buffer, size_t length) +{ + struct sha512_context hd_tmp; + + sha512_init(&hd_tmp); + sha512_update(&hd_tmp, buffer, length); + memcpy(outbuf, sha512_final(&hd_tmp), SHA512_SIZE); +} + +void +sha512_hmac_init(struct sha512_hmac_context *ctx, const byte *key, size_t keylen) +{ + byte keybuf[SHA512_BLOCK_SIZE], buf[SHA512_BLOCK_SIZE]; + + /* Hash the key if necessary */ + if (keylen <= SHA512_BLOCK_SIZE) + { + memcpy(keybuf, key, keylen); + bzero(keybuf + keylen, SHA512_BLOCK_SIZE - keylen); + } + else + { + sha512_hash_buffer(keybuf, key, keylen); + bzero(keybuf + SHA512_SIZE, SHA512_BLOCK_SIZE - SHA512_SIZE); + } + + /* Initialize the inner digest */ + sha512_init(&ctx->ictx); + int i; + for (i = 0; i < SHA512_BLOCK_SIZE; i++) + buf[i] = keybuf[i] ^ 0x36; + sha512_update(&ctx->ictx, buf, SHA512_BLOCK_SIZE); + + /* Initialize the outer digest */ + sha512_init(&ctx->octx); + for (i = 0; i < SHA512_BLOCK_SIZE; i++) + buf[i] = keybuf[i] ^ 0x5c; + sha512_update(&ctx->octx, buf, SHA512_BLOCK_SIZE); +} + +void sha512_hmac_update(struct sha512_hmac_context *ctx, const byte *buf, size_t buflen) +{ + /* Just update the inner digest */ + sha512_update(&ctx->ictx, buf, buflen); +} + +byte *sha512_hmac_final(struct sha512_hmac_context *ctx) +{ + /* Finish the inner digest */ + byte *isha = sha512_final(&ctx->ictx); + + /* Finish the outer digest */ + sha512_update(&ctx->octx, isha, SHA512_SIZE); + return sha512_final(&ctx->octx); +} + + +/* + * SHA384-HMAC + */ + +static void +sha384_hash_buffer(byte *outbuf, const byte *buffer, size_t length) +{ + struct sha384_context hd_tmp; + + sha384_init(&hd_tmp); + sha384_update(&hd_tmp, buffer, length); + memcpy(outbuf, sha384_final(&hd_tmp), SHA384_SIZE); +} + +void +sha384_hmac_init(struct sha384_hmac_context *ctx, const byte *key, size_t keylen) +{ + byte keybuf[SHA384_BLOCK_SIZE], buf[SHA384_BLOCK_SIZE]; + + /* Hash the key if necessary */ + if (keylen <= SHA384_BLOCK_SIZE) + { + memcpy(keybuf, key, keylen); + bzero(keybuf + keylen, SHA384_BLOCK_SIZE - keylen); + } + else + { + sha384_hash_buffer(keybuf, key, keylen); + bzero(keybuf + SHA384_SIZE, SHA384_BLOCK_SIZE - SHA384_SIZE); + } + + /* Initialize the inner digest */ + sha384_init(&ctx->ictx); + int i; + for (i = 0; i < SHA384_BLOCK_SIZE; i++) + buf[i] = keybuf[i] ^ 0x36; + sha384_update(&ctx->ictx, buf, SHA384_BLOCK_SIZE); + + /* Initialize the outer digest */ + sha384_init(&ctx->octx); + for (i = 0; i < SHA384_BLOCK_SIZE; i++) + buf[i] = keybuf[i] ^ 0x5c; + sha384_update(&ctx->octx, buf, SHA384_BLOCK_SIZE); +} + +void sha384_hmac_update(struct sha384_hmac_context *ctx, const byte *buf, size_t buflen) +{ + /* Just update the inner digest */ + sha384_update(&ctx->ictx, buf, buflen); +} + +byte *sha384_hmac_final(struct sha384_hmac_context *ctx) +{ + /* Finish the inner digest */ + byte *isha = sha384_final(&ctx->ictx); + + /* Finish the outer digest */ + sha384_update(&ctx->octx, isha, SHA384_SIZE); + return sha384_final(&ctx->octx); +} diff --git a/lib/sha512.h b/lib/sha512.h new file mode 100644 index 00000000..bd998152 --- /dev/null +++ b/lib/sha512.h @@ -0,0 +1,73 @@ +/* + * BIRD Library -- SHA-512 and SHA-384 Hash Functions, + * HMAC-SHA-512 and HMAC-SHA-384 Functions + * + * (c) 2015 CZ.NIC z.s.p.o. + * + * Based on the code from libgcrypt-1.6.0, which is + * (c) 2003, 2006, 2008, 2009 Free Software Foundation, Inc. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#ifndef _BIRD_SHA512_H_ +#define _BIRD_SHA512_H_ + +#include "lib/sha256.h" + +#define SHA384_SIZE 48 +#define SHA384_HEX_SIZE 97 +#define SHA384_BLOCK_SIZE 128 + +#define SHA512_SIZE 64 +#define SHA512_HEX_SIZE 129 +#define SHA512_BLOCK_SIZE 128 + +struct sha512_state +{ + u64 h0, h1, h2, h3, h4, h5, h6, h7; +}; + +struct sha512_context +{ + struct sha256_context bctx; + struct sha512_state state; +}; +#define sha384_context sha512_context /* aliasing 'struct sha384_context' to 'struct sha512_context' */ + + +void sha512_init(struct sha512_context *ctx); +void sha384_init(struct sha384_context *ctx); + +void sha512_update(struct sha512_context *ctx, const byte *in_buf, size_t in_len); +static inline void sha384_update(struct sha384_context *ctx, const byte *in_buf, size_t in_len) +{ + sha512_update(ctx, in_buf, in_len); +} + +byte* sha512_final(struct sha512_context *ctx); +static inline byte* sha384_final(struct sha384_context *ctx) +{ + return sha512_final(ctx); +} + +/* + * HMAC-SHA512, HMAC-SHA384 + */ +struct sha512_hmac_context +{ + struct sha512_context ictx; + struct sha512_context octx; +} ; +#define sha384_hmac_context sha512_hmac_context /* aliasing 'struct sha384_hmac_context' to 'struct sha384_hmac_context' */ + +void sha512_hmac_init(struct sha512_hmac_context *ctx, const byte *key, size_t keylen); +void sha384_hmac_init(struct sha384_hmac_context *ctx, const byte *key, size_t keylen); + +void sha512_hmac_update(struct sha512_hmac_context *ctx, const byte *buf, size_t buflen); +void sha384_hmac_update(struct sha384_hmac_context *ctx, const byte *buf, size_t buflen); + +byte *sha512_hmac_final(struct sha512_hmac_context *ctx); +byte *sha384_hmac_final(struct sha384_hmac_context *ctx); + +#endif /* _BIRD_SHA512_H_ */ From 1e4891e48e7b6f022564e7409d15c3fdb65ec2ad Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Mon, 23 Nov 2015 11:13:40 +0100 Subject: [PATCH 14/18] 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(-) 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); From 5126380beace4e39578f005fe115917b8e8b8ff3 Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Tue, 24 Nov 2015 13:47:28 +0100 Subject: [PATCH 15/18] Minor changes to SHA hash functions --- lib/birdlib.h | 1 + lib/sha1.c | 176 +++++++++-------- lib/sha1.h | 72 +++---- lib/sha256.c | 180 ++++++++--------- lib/sha256.h | 42 ++-- lib/sha512.c | 526 +++++++++++++++++++++++++------------------------- lib/sha512.h | 42 ++-- 7 files changed, 518 insertions(+), 521 deletions(-) diff --git a/lib/birdlib.h b/lib/birdlib.h index ad41dca3..16f437ef 100644 --- a/lib/birdlib.h +++ b/lib/birdlib.h @@ -30,6 +30,7 @@ #define MAX(a,b) MAX_(a,b) #endif +#define U64(c) UINT64_C(c) #define ABS(a) ((a)>=0 ? (a) : -(a)) #define DELTA(a,b) (((a)>=(b))?(a)-(b):(b)-(a)) #define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a))) diff --git a/lib/sha1.c b/lib/sha1.c index dd29b5fd..73b4b280 100644 --- a/lib/sha1.c +++ b/lib/sha1.c @@ -15,33 +15,35 @@ #include "lib/sha1.h" #include "lib/unaligned.h" + void -sha1_init(struct sha1_context *hd) +sha1_init(struct sha1_context *ctx) { - hd->h0 = 0x67452301; - hd->h1 = 0xefcdab89; - hd->h2 = 0x98badcfe; - hd->h3 = 0x10325476; - hd->h4 = 0xc3d2e1f0; - hd->nblocks = 0; - hd->count = 0; + ctx->h0 = 0x67452301; + ctx->h1 = 0xefcdab89; + ctx->h2 = 0x98badcfe; + ctx->h3 = 0x10325476; + ctx->h4 = 0xc3d2e1f0; + + ctx->nblocks = 0; + ctx->count = 0; } /* * Transform the message X which consists of 16 32-bit-words */ static void -sha1_transform(struct sha1_context *hd, const byte *data) +sha1_transform(struct sha1_context *ctx, const byte *data) { u32 a,b,c,d,e,tm; u32 x[16]; /* Get values from the chaining vars. */ - a = hd->h0; - b = hd->h1; - c = hd->h2; - d = hd->h3; - e = hd->h4; + a = ctx->h0; + b = ctx->h1; + c = ctx->h2; + d = ctx->h3; + e = ctx->h4; #ifdef CPU_BIG_ENDIAN memcpy(x, data, 64); @@ -69,7 +71,7 @@ sha1_transform(struct sha1_context *hd, const byte *data) do \ { \ e += ROL(a, 5) + f(b, c, d) + k + m; \ - b = ROL( b, 30 ); \ + b = ROL(b, 30); \ } while(0) R( a, b, c, d, e, F1, K1, x[ 0] ); @@ -154,72 +156,72 @@ sha1_transform(struct sha1_context *hd, const byte *data) R( b, c, d, e, a, F4, K4, M(79) ); /* Update chaining vars. */ - hd->h0 += a; - hd->h1 += b; - hd->h2 += c; - hd->h3 += d; - hd->h4 += e; + ctx->h0 += a; + ctx->h1 += b; + ctx->h2 += c; + ctx->h3 += d; + ctx->h4 += e; } /* - * Update the message digest with the contents - * of INBUF with length INLEN. + * Update the message digest with the contents of BUF with length LEN. */ void -sha1_update(struct sha1_context *hd, const byte *inbuf, uint inlen) +sha1_update(struct sha1_context *ctx, const byte *buf, uint len) { - if (hd->count == 64) /* flush the buffer */ + if (ctx->count) { - sha1_transform(hd, hd->buf); - hd->count = 0; - hd->nblocks++; + /* Fill rest of internal buffer */ + for (; len && ctx->count < SHA1_BLOCK_SIZE; len--) + ctx->buf[ctx->count++] = *buf++; + + if (ctx->count < SHA1_BLOCK_SIZE) + return; + + /* Process data from internal buffer */ + sha1_transform(ctx, ctx->buf); + ctx->nblocks++; + ctx->count = 0; } - if (!inbuf) + + if (!len) return; - if (hd->count) + /* Process data from input buffer */ + while (len >= SHA1_BLOCK_SIZE) { - for (; inlen && hd->count < 64; inlen--) - hd->buf[hd->count++] = *inbuf++; - sha1_update( hd, NULL, 0 ); - if(!inlen) - return; + sha1_transform(ctx, buf); + ctx->nblocks++; + buf += SHA1_BLOCK_SIZE; + len -= SHA1_BLOCK_SIZE; } - while (inlen >= 64) - { - sha1_transform(hd, inbuf); - hd->count = 0; - hd->nblocks++; - inlen -= 64; - inbuf += 64; - } - for (; inlen && hd->count < 64; inlen--) - hd->buf[hd->count++] = *inbuf++; + /* Copy remaining data to internal buffer */ + memcpy(ctx->buf, buf, len); + ctx->count = len; } /* - * The routine final terminates the computation and - * returns the digest. - * The handle is prepared for a new cycle, but adding bytes to the - * handle will the destroy the returned buffer. + * The routine final terminates the computation and returns the digest. The + * handle is prepared for a new cycle, but adding bytes to the handle will the + * destroy the returned buffer. + * * Returns: 20 bytes representing the digest. */ byte * -sha1_final(struct sha1_context *hd) +sha1_final(struct sha1_context *ctx) { u32 t, msb, lsb; - u32 *p; - sha1_update(hd, NULL, 0); /* flush */; + sha1_update(ctx, NULL, 0); /* flush */ - t = hd->nblocks; + t = ctx->nblocks; /* multiply by 64 to make a byte count */ lsb = t << 6; msb = t >> 26; /* add the count */ t = lsb; - if ((lsb += hd->count) < t) + if ((lsb += ctx->count) < t) msb++; /* multiply by 8 to make a bit count */ t = lsb; @@ -227,33 +229,36 @@ sha1_final(struct sha1_context *hd) msb <<= 3; msb |= t >> 29; - if (hd->count < 56) /* enough room */ + if (ctx->count < 56) { - hd->buf[hd->count++] = 0x80; /* pad */ - while (hd->count < 56) - hd->buf[hd->count++] = 0; /* pad */ + /* enough room */ + ctx->buf[ctx->count++] = 0x80; /* pad */ + while (ctx->count < 56) + ctx->buf[ctx->count++] = 0; /* pad */ } - else /* need one extra block */ + else { - hd->buf[hd->count++] = 0x80; /* pad character */ - while (hd->count < 64) - hd->buf[hd->count++] = 0; - sha1_update(hd, NULL, 0); /* flush */; - memset(hd->buf, 0, 56 ); /* fill next block with zeroes */ + /* need one extra block */ + ctx->buf[ctx->count++] = 0x80; /* pad character */ + while (ctx->count < 64) + ctx->buf[ctx->count++] = 0; + sha1_update(ctx, NULL, 0); /* flush */ + memset(ctx->buf, 0, 56); /* fill next block with zeroes */ } - /* append the 64 bit count */ - hd->buf[56] = msb >> 24; - hd->buf[57] = msb >> 16; - hd->buf[58] = msb >> 8; - hd->buf[59] = msb ; - hd->buf[60] = lsb >> 24; - hd->buf[61] = lsb >> 16; - hd->buf[62] = lsb >> 8; - hd->buf[63] = lsb ; - sha1_transform(hd, hd->buf); - p = (u32*) hd->buf; -#define X(a) do { put_u32(p, hd->h##a); p++; } while(0) + /* append the 64 bit count */ + ctx->buf[56] = msb >> 24; + ctx->buf[57] = msb >> 16; + ctx->buf[58] = msb >> 8; + ctx->buf[59] = msb; + ctx->buf[60] = lsb >> 24; + ctx->buf[61] = lsb >> 16; + ctx->buf[62] = lsb >> 8; + ctx->buf[63] = lsb; + sha1_transform(ctx, ctx->buf); + + byte *p = ctx->buf; +#define X(a) do { put_u32(p, ctx->h##a); p += 4; } while(0) X(0); X(1); X(2); @@ -261,12 +266,12 @@ sha1_final(struct sha1_context *hd) X(4); #undef X - return hd->buf; + return ctx->buf; } /* - * SHA1-HMAC + * SHA1-HMAC */ /* @@ -292,12 +297,12 @@ sha1_hmac_init(struct sha1_hmac_context *ctx, const byte *key, uint keylen) if (keylen <= SHA1_BLOCK_SIZE) { memcpy(keybuf, key, keylen); - bzero(keybuf + keylen, SHA1_BLOCK_SIZE - keylen); + memset(keybuf + keylen, 0, SHA1_BLOCK_SIZE - keylen); } else { sha1_hash_buffer(keybuf, key, keylen); - bzero(keybuf + SHA1_SIZE, SHA1_BLOCK_SIZE - SHA1_SIZE); + memset(keybuf + SHA1_SIZE, 0, SHA1_BLOCK_SIZE - SHA1_SIZE); } /* Initialize the inner digest */ @@ -321,7 +326,8 @@ sha1_hmac_update(struct sha1_hmac_context *ctx, const byte *data, uint datalen) sha1_update(&ctx->ictx, data, datalen); } -byte *sha1_hmac_final(struct sha1_hmac_context *ctx) +byte * +sha1_hmac_final(struct sha1_hmac_context *ctx) { /* Finish the inner digest */ byte *isha = sha1_final(&ctx->ictx); @@ -334,9 +340,9 @@ byte *sha1_hmac_final(struct sha1_hmac_context *ctx) void sha1_hmac(byte *outbuf, const byte *key, uint keylen, const byte *data, uint datalen) { - struct sha1_hmac_context hd; - sha1_hmac_init(&hd, key, keylen); - sha1_hmac_update(&hd, data, datalen); - byte *osha = sha1_hmac_final(&hd); - memcpy(outbuf, osha, SHA1_SIZE); + struct sha1_hmac_context ctx; + + sha1_hmac_init(&ctx, key, keylen); + sha1_hmac_update(&ctx, data, datalen); + memcpy(outbuf, sha1_hmac_final(&ctx), SHA1_SIZE); } diff --git a/lib/sha1.h b/lib/sha1.h index 425160a0..c019bb49 100644 --- a/lib/sha1.h +++ b/lib/sha1.h @@ -17,70 +17,70 @@ #include "nest/bird.h" + +#define SHA1_SIZE 20 /* Size of the SHA1 hash in its binary representation */ +#define SHA1_HEX_SIZE 41 /* Buffer length for a string containing SHA1 in hexadecimal format. */ +#define SHA1_BLOCK_SIZE 64 /* SHA1 splits input to blocks of this size. */ + + /* * Internal SHA1 state. * You should use it just as an opaque handle only. */ struct sha1_context { - u32 h0,h1,h2,h3,h4; - u32 nblocks; - byte buf[64]; - int count; -} ; + u32 h0, h1, h2, h3, h4; + byte buf[SHA1_BLOCK_SIZE]; + uint nblocks; + uint count; +}; -void sha1_init(struct sha1_context *hd); /* Initialize new algorithm run in the @hd context. **/ + +void sha1_init(struct sha1_context *ctx); /* Initialize new algorithm run in the @ctx context. **/ /* - * Push another @inlen bytes of data pointed to by @inbuf onto the - * SHA1 hash currently in @hd. You can call this any times you want on - * the same hash (and you do not need to reinitialize it by - * @sha1_init()). It has the same effect as concatenating all the data - * together and passing them at once. + * Push another @len bytes of data pointed to by @buf onto the SHA1 hash + * currently in @ctx. You can call this any times you want on the same hash (and + * you do not need to reinitialize it by @sha1_init()). It has the same effect + * as concatenating all the data together and passing them at once. */ -void sha1_update(struct sha1_context *hd, const byte *inbuf, uint inlen); +void sha1_update(struct sha1_context *ctx, const byte *buf, uint len); /* - * No more @sha1_update() calls will be done. This terminates the hash - * and returns a pointer to it. + * No more @sha1_update() calls will be done. This terminates the hash and + * returns a pointer to it. * - * Note that the pointer points into data in the @hd context. If it ceases - * to exist, the pointer becomes invalid. - * - * To convert the hash to its usual hexadecimal representation, see - * <>. + * Note that the pointer points into data in the @ctx context. If it ceases to + * exist, the pointer becomes invalid. */ -byte *sha1_final(struct sha1_context *hd); +byte *sha1_final(struct sha1_context *ctx); /* - * A convenience one-shot function for SHA1 hash. - * It is equivalent to this snippet of code: + * A convenience one-shot function for SHA1 hash. It is equivalent to this + * snippet of code: * - * sha1_context hd; - * sha1_init(&hd); - * sha1_update(&hd, buffer, length); - * memcpy(outbuf, sha1_final(&hd), SHA1_SIZE); + * sha1_context ctx; + * sha1_init(&ctx); + * sha1_update(&ctx, buffer, length); + * memcpy(outbuf, sha1_final(&ctx), SHA1_SIZE); */ void sha1_hash_buffer(byte *outbuf, const byte *buffer, uint length); /* - * SHA1 HMAC message authentication. If you provide @key and @data, - * the result will be stored in @outbuf. + * SHA1 HMAC message authentication. If you provide @key and @data, the result + * will be stored in @outbuf. */ void sha1_hmac(byte *outbuf, const byte *key, uint keylen, const byte *data, uint datalen); /* - * The HMAC also exists in a stream version in a way analogous to the - * plain SHA1. Pass this as a context. + * The HMAC also exists in a stream version in a way analogous to the plain + * SHA1. Pass this as a context. */ struct sha1_hmac_context { struct sha1_context ictx; struct sha1_context octx; }; -void sha1_hmac_init(struct sha1_hmac_context *hd, const byte *key, uint keylen); /* Initialize HMAC with context @hd and the given key. See sha1_init(). */ -void sha1_hmac_update(struct sha1_hmac_context *hd, const byte *data, uint datalen); /* Hash another @datalen bytes of data. See sha1_update(). */ -byte *sha1_hmac_final(struct sha1_hmac_context *hd); /* Terminate the HMAC and return a pointer to the allocated hash. See sha1_final(). */ +void sha1_hmac_init(struct sha1_hmac_context *ctx, const byte *key, uint keylen); /* Initialize HMAC with context @ctx and the given key. See sha1_init(). */ +void sha1_hmac_update(struct sha1_hmac_context *ctx, const byte *data, uint datalen); /* Hash another @datalen bytes of data. See sha1_update(). */ +byte *sha1_hmac_final(struct sha1_hmac_context *ctx); /* Terminate the HMAC and return a pointer to the allocated hash. See sha1_final(). */ -#define SHA1_SIZE 20 /* Size of the SHA1 hash in its binary representation **/ -#define SHA1_HEX_SIZE 41 /* Buffer length for a string containing SHA1 in hexadecimal format. **/ -#define SHA1_BLOCK_SIZE 64 /* SHA1 splits input to blocks of this size. **/ #endif /* _BIRD_SHA1_H_ */ diff --git a/lib/sha256.c b/lib/sha256.c index 2d979f90..440245d5 100644 --- a/lib/sha256.c +++ b/lib/sha256.c @@ -13,7 +13,8 @@ #include "lib/sha256.h" #include "lib/unaligned.h" -static uint sha256_transform(void *ctx, const byte *data, size_t nblks); + +// #define SHA256_UNROLLED void sha256_init(struct sha256_context *ctx) @@ -28,10 +29,7 @@ sha256_init(struct sha256_context *ctx) ctx->h7 = 0x5be0cd19; ctx->nblocks = 0; - ctx->nblocks_high = 0; ctx->count = 0; - ctx->blocksize = 64; - ctx->transform = sha256_transform; } void @@ -47,10 +45,7 @@ sha224_init(struct sha224_context *ctx) ctx->h7 = 0xbefa4fa4; ctx->nblocks = 0; - ctx->nblocks_high = 0; ctx->count = 0; - ctx->blocksize = 64; - ctx->transform = sha256_transform; } /* (4.2) same as SHA-1's F1. */ @@ -70,7 +65,7 @@ f3(u32 x, u32 y, u32 z) /* Bitwise rotation of an uint to the right */ static inline u32 ror(u32 x, int n) { - return ( (x >> (n&(32-1))) | (x << ((32-n)&(32-1))) ); + return ((x >> (n&(32-1))) | (x << ((32-n)&(32-1)))); } /* (4.4) */ @@ -112,7 +107,7 @@ sum1(u32 x) 32-bit-words. See FIPS 180-2 for details. */ static uint -sha256_transform_block(struct sha256_context *ctx, const byte *data) +sha256_transform(struct sha256_context *ctx, const byte *data) { static const u32 K[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, @@ -148,52 +143,58 @@ sha256_transform_block(struct sha256_context *ctx, const byte *data) for (i = 0; i < 16; i++) w[i] = get_u32(data + i * 4); + for (; i < 64; i++) w[i] = S1(w[i-2]) + w[i-7] + S0(w[i-15]) + w[i-16]; for (i = 0; i < 64;) { +#ifndef SHA256_UNROLLED + R(a,b,c,d,e,f,g,h,K[i],w[i]); + i++; +#else /* Unrolled */ t1 = h + sum1(e) + f1(e, f, g) + K[i] + w[i]; - t2 = sum0 (a) + f3(a, b, c); + t2 = sum0(a) + f3(a, b, c); d += t1; h = t1 + t2; t1 = g + sum1(d) + f1(d, e, f) + K[i+1] + w[i+1]; - t2 = sum0 (h) + f3(h, a, b); + t2 = sum0(h) + f3(h, a, b); c += t1; g = t1 + t2; t1 = f + sum1(c) + f1(c, d, e) + K[i+2] + w[i+2]; - t2 = sum0 (g) + f3(g, h, a); + t2 = sum0(g) + f3(g, h, a); b += t1; f = t1 + t2; t1 = e + sum1(b) + f1(b, c, d) + K[i+3] + w[i+3]; - t2 = sum0 (f) + f3(f, g, h); + t2 = sum0(f) + f3(f, g, h); a += t1; e = t1 + t2; t1 = d + sum1(a) + f1(a, b, c) + K[i+4] + w[i+4]; - t2 = sum0 (e) + f3(e, f, g); + t2 = sum0(e) + f3(e, f, g); h += t1; d = t1 + t2; t1 = c + sum1(h) + f1(h, a, b) + K[i+5] + w[i+5]; - t2 = sum0 (d) + f3(d, e, f); + t2 = sum0(d) + f3(d, e, f); g += t1; c = t1 + t2; t1 = b + sum1(g) + f1(g, h, a) + K[i+6] + w[i+6]; - t2 = sum0 (c) + f3(c, d, e); + t2 = sum0(c) + f3(c, d, e); f += t1; b = t1 + t2; t1 = a + sum1(f) + f1(f, g, h) + K[i+7] + w[i+7]; - t2 = sum0 (b) + f3(b, c, d); + t2 = sum0(b) + f3(b, c, d); e += t1; a = t1 + t2; i += 8; +#endif } ctx->h0 += a; @@ -211,22 +212,6 @@ sha256_transform_block(struct sha256_context *ctx, const byte *data) #undef S1 #undef R -static uint -sha256_transform(void *ctx, const byte *data, size_t nblks) -{ - struct sha256_context *hd = ctx; - uint burn; - - do - { - burn = sha256_transform_block(hd, data); - data += 64; - } - while (--nblks); - - return burn; -} - /* Common function to write a chunk of data to the transform function of a hash algorithm. Note that the use of the term "block" does not imply a fixed size block. Note that we explicitly allow to use @@ -234,65 +219,56 @@ sha256_transform(void *ctx, const byte *data, size_t nblks) not have any meaning but writing after finalize is sometimes helpful to mitigate timing attacks. */ void -sha256_update(struct sha256_context *ctx, const byte *in_buf, size_t in_len) +sha256_update(struct sha256_context *ctx, const byte *buf, size_t len) { - const uint blocksize = ctx->blocksize; - size_t inblocks; - - if (sizeof(ctx->buf) < blocksize) - debug("BUG: in file %s at line %d", __FILE__ , __LINE__); - - if (ctx->count == blocksize) /* Flush the buffer. */ - { - ctx->transform(ctx, ctx->buf, 1); - ctx->count = 0; - if (!++ctx->nblocks) - ctx->nblocks_high++; - } - if (!in_buf) - return; - if (ctx->count) { - for (; in_len && ctx->count < blocksize; in_len--) - ctx->buf[ctx->count++] = *in_buf++; - sha256_update(ctx, NULL, 0); - if (!in_len) + /* Fill rest of internal buffer */ + for (; len && ctx->count < SHA256_BLOCK_SIZE; len--) + ctx->buf[ctx->count++] = *buf++; + + if (ctx->count < SHA256_BLOCK_SIZE) return; + + /* Process data from internal buffer */ + sha256_transform(ctx, ctx->buf); + ctx->nblocks++; + ctx->count = 0; } - if (in_len >= blocksize) + if (!len) + return; + + /* Process data from input buffer */ + while (len >= SHA256_BLOCK_SIZE) { - inblocks = in_len / blocksize; - ctx->transform(ctx, in_buf, inblocks); - ctx->count = 0; - ctx->nblocks_high += (ctx->nblocks + inblocks < inblocks); - ctx->nblocks += inblocks; - in_len -= inblocks * blocksize; - in_buf += inblocks * blocksize; + sha256_transform(ctx, buf); + ctx->nblocks++; + buf += SHA256_BLOCK_SIZE; + len -= SHA256_BLOCK_SIZE; } - for (; in_len && ctx->count < blocksize; in_len--) - ctx->buf[ctx->count++] = *in_buf++; + + /* Copy remaining data to internal buffer */ + memcpy(ctx->buf, buf, len); + ctx->count = len; } /* - The routine finally terminates the computation and returns the - digest. The handle is prepared for a new cycle, but adding bytes - to the handle will the destroy the returned buffer. Returns: 32 - bytes with the message the digest. */ -byte* + * The routine finally terminates the computation and returns the digest. The + * handle is prepared for a new cycle, but adding bytes to the handle will the + * destroy the returned buffer. + * + * Returns: 32 bytes with the message the digest. 28 bytes for SHA-224. + */ +byte * sha256_final(struct sha256_context *ctx) { u32 t, th, msb, lsb; - byte *p; - sha256_update(ctx, NULL, 0); /* flush */; + sha256_update(ctx, NULL, 0); /* flush */ t = ctx->nblocks; - if (sizeof t == sizeof ctx->nblocks) - th = ctx->nblocks_high; - else - th = 0; + th = 0; /* multiply by 64 to make a byte count */ lsb = t << 6; @@ -308,26 +284,28 @@ sha256_final(struct sha256_context *ctx) msb |= t >> 29; if (ctx->count < 56) - { /* enough room */ + { + /* enough room */ ctx->buf[ctx->count++] = 0x80; /* pad */ while (ctx->count < 56) ctx->buf[ctx->count++] = 0; /* pad */ } else - { /* need one extra block */ + { + /* need one extra block */ ctx->buf[ctx->count++] = 0x80; /* pad character */ while (ctx->count < 64) ctx->buf[ctx->count++] = 0; sha256_update(ctx, NULL, 0); /* flush */; - memset (ctx->buf, 0, 56 ); /* fill next block with zeroes */ + memset(ctx->buf, 0, 56 ); /* fill next block with zeroes */ } + /* append the 64 bit count */ put_u32(ctx->buf + 56, msb); put_u32(ctx->buf + 60, lsb); - sha256_transform(ctx, ctx->buf, 1); - - p = ctx->buf; + sha256_transform(ctx, ctx->buf); + byte *p = ctx->buf; #define X(a) do { put_u32(p, ctx->h##a); p += 4; } while(0) X(0); X(1); @@ -344,17 +322,17 @@ sha256_final(struct sha256_context *ctx) /* - * SHA256-HMAC + * SHA256-HMAC */ static void sha256_hash_buffer(byte *outbuf, const byte *buffer, size_t length) { - struct sha256_context hd_tmp; + struct sha256_context ctx; - sha256_init(&hd_tmp); - sha256_update(&hd_tmp, buffer, length); - memcpy(outbuf, sha256_final(&hd_tmp), SHA256_SIZE); + sha256_init(&ctx); + sha256_update(&ctx, buffer, length); + memcpy(outbuf, sha256_final(&ctx), SHA256_SIZE); } void @@ -366,12 +344,12 @@ sha256_hmac_init(struct sha256_hmac_context *ctx, const byte *key, size_t keylen if (keylen <= SHA256_BLOCK_SIZE) { memcpy(keybuf, key, keylen); - bzero(keybuf + keylen, SHA256_BLOCK_SIZE - keylen); + memset(keybuf + keylen, 0, SHA256_BLOCK_SIZE - keylen); } else { sha256_hash_buffer(keybuf, key, keylen); - bzero(keybuf + SHA256_SIZE, SHA256_BLOCK_SIZE - SHA256_SIZE); + memset(keybuf + SHA256_SIZE, 0, SHA256_BLOCK_SIZE - SHA256_SIZE); } /* Initialize the inner digest */ @@ -388,13 +366,15 @@ sha256_hmac_init(struct sha256_hmac_context *ctx, const byte *key, size_t keylen sha256_update(&ctx->octx, buf, SHA256_BLOCK_SIZE); } -void sha256_hmac_update(struct sha256_hmac_context *ctx, const byte *buf, size_t buflen) +void +sha256_hmac_update(struct sha256_hmac_context *ctx, const byte *buf, size_t buflen) { /* Just update the inner digest */ sha256_update(&ctx->ictx, buf, buflen); } -byte *sha256_hmac_final(struct sha256_hmac_context *ctx) +byte * +sha256_hmac_final(struct sha256_hmac_context *ctx) { /* Finish the inner digest */ byte *isha = sha256_final(&ctx->ictx); @@ -406,17 +386,17 @@ byte *sha256_hmac_final(struct sha256_hmac_context *ctx) /* - * SHA224-HMAC + * SHA224-HMAC */ static void sha224_hash_buffer(byte *outbuf, const byte *buffer, size_t length) { - struct sha224_context hd_tmp; + struct sha224_context ctx; - sha224_init(&hd_tmp); - sha224_update(&hd_tmp, buffer, length); - memcpy(outbuf, sha224_final(&hd_tmp), SHA224_SIZE); + sha224_init(&ctx); + sha224_update(&ctx, buffer, length); + memcpy(outbuf, sha224_final(&ctx), SHA224_SIZE); } void @@ -428,12 +408,12 @@ sha224_hmac_init(struct sha224_hmac_context *ctx, const byte *key, size_t keylen if (keylen <= SHA224_BLOCK_SIZE) { memcpy(keybuf, key, keylen); - bzero(keybuf + keylen, SHA224_BLOCK_SIZE - keylen); + memset(keybuf + keylen, 0, SHA224_BLOCK_SIZE - keylen); } else { sha224_hash_buffer(keybuf, key, keylen); - bzero(keybuf + SHA224_SIZE, SHA224_BLOCK_SIZE - SHA224_SIZE); + memset(keybuf + SHA224_SIZE, 0, SHA224_BLOCK_SIZE - SHA224_SIZE); } /* Initialize the inner digest */ @@ -450,13 +430,15 @@ sha224_hmac_init(struct sha224_hmac_context *ctx, const byte *key, size_t keylen sha224_update(&ctx->octx, buf, SHA224_BLOCK_SIZE); } -void sha224_hmac_update(struct sha224_hmac_context *ctx, const byte *buf, size_t buflen) +void +sha224_hmac_update(struct sha224_hmac_context *ctx, const byte *buf, size_t buflen) { /* Just update the inner digest */ sha256_update(&ctx->ictx, buf, buflen); } -byte *sha224_hmac_final(struct sha224_hmac_context *ctx) +byte * +sha224_hmac_final(struct sha224_hmac_context *ctx) { /* Finish the inner digest */ byte *isha = sha224_final(&ctx->ictx); diff --git a/lib/sha256.h b/lib/sha256.h index 848d2176..381200a9 100644 --- a/lib/sha256.h +++ b/lib/sha256.h @@ -15,6 +15,7 @@ #include "nest/bird.h" + #define SHA224_SIZE 28 #define SHA224_HEX_SIZE 57 #define SHA224_BLOCK_SIZE 64 @@ -23,44 +24,44 @@ #define SHA256_HEX_SIZE 65 #define SHA256_BLOCK_SIZE 64 + struct sha256_context { - u32 h0,h1,h2,h3,h4,h5,h6,h7; - byte buf[128]; /* 128 is for SHA384 and SHA512 support, otherwise for SHA224 and SHA256 is 64 enough */ - u32 nblocks; - u32 nblocks_high; - int count; - u32 blocksize; - uint (*transform)(void *c, const byte *blks, size_t nblks); + u32 h0, h1, h2, h3, h4, h5, h6, h7; + byte buf[SHA256_BLOCK_SIZE]; + uint nblocks; + uint count; }; -#define sha224_context sha256_context /* aliasing 'struct sha224_context' to 'struct sha256_context' */ + +#define sha224_context sha256_context + void sha256_init(struct sha256_context *ctx); void sha224_init(struct sha224_context *ctx); -void sha256_update(struct sha256_context *ctx, const byte *in_buf, size_t in_len); -static inline void sha224_update(struct sha224_context *ctx, const byte *in_buf, size_t in_len) -{ - sha256_update(ctx, in_buf, in_len); -} +void sha256_update(struct sha256_context *ctx, const byte *buf, size_t len); +static inline void sha224_update(struct sha224_context *ctx, const byte *buf, size_t len) +{ sha256_update(ctx, buf, len); } + +byte *sha256_final(struct sha256_context *ctx); +static inline byte *sha224_final(struct sha224_context *ctx) +{ return sha256_final(ctx); } -byte* sha256_final(struct sha256_context *ctx); -static inline byte* sha224_final(struct sha224_context *ctx) -{ - return sha256_final(ctx); -} /* * HMAC-SHA256, HMAC-SHA224 */ + struct sha256_hmac_context { struct sha256_context ictx; struct sha256_context octx; }; -#define sha224_hmac_context sha256_hmac_context /* aliasing 'struct sha224_hmac_context' to 'struct sha256_hmac_context' */ + +#define sha224_hmac_context sha256_hmac_context + void sha256_hmac_init(struct sha256_hmac_context *ctx, const byte *key, size_t keylen); -void sha224_hmac_init(struct sha224_hmac_context *ctx, const byte *key, size_t keylen); +void sha224_hmac_init(struct sha224_hmac_context *ctx, const byte *key, size_t keylen); void sha256_hmac_update(struct sha256_hmac_context *ctx, const byte *buf, size_t buflen); void sha224_hmac_update(struct sha224_hmac_context *ctx, const byte *buf, size_t buflen); @@ -68,4 +69,5 @@ void sha224_hmac_update(struct sha224_hmac_context *ctx, const byte *buf, size_t byte *sha256_hmac_final(struct sha256_hmac_context *ctx); byte *sha224_hmac_final(struct sha224_hmac_context *ctx); + #endif /* _BIRD_SHA256_H_ */ diff --git a/lib/sha512.c b/lib/sha512.c index e46e4c98..37e660f7 100644 --- a/lib/sha512.c +++ b/lib/sha512.c @@ -10,57 +10,42 @@ * Can be freely distributed and used under the terms of the GNU GPL. */ -#include "lib/sha256.h" #include "lib/sha512.h" #include "lib/unaligned.h" -static uint sha512_transform(void *context, const byte *data, size_t nblks); + +// #define SHA512_UNROLLED void sha512_init(struct sha512_context *ctx) { - struct sha512_state *hd = &ctx->state; + ctx->h0 = U64(0x6a09e667f3bcc908); + ctx->h1 = U64(0xbb67ae8584caa73b); + ctx->h2 = U64(0x3c6ef372fe94f82b); + ctx->h3 = U64(0xa54ff53a5f1d36f1); + ctx->h4 = U64(0x510e527fade682d1); + ctx->h5 = U64(0x9b05688c2b3e6c1f); + ctx->h6 = U64(0x1f83d9abfb41bd6b); + ctx->h7 = U64(0x5be0cd19137e2179); - hd->h0 = UINT64_C(0x6a09e667f3bcc908); - hd->h1 = UINT64_C(0xbb67ae8584caa73b); - hd->h2 = UINT64_C(0x3c6ef372fe94f82b); - hd->h3 = UINT64_C(0xa54ff53a5f1d36f1); - hd->h4 = UINT64_C(0x510e527fade682d1); - hd->h5 = UINT64_C(0x9b05688c2b3e6c1f); - hd->h6 = UINT64_C(0x1f83d9abfb41bd6b); - hd->h7 = UINT64_C(0x5be0cd19137e2179); - - ctx->bctx.nblocks = 0; - ctx->bctx.nblocks_high = 0; - ctx->bctx.count = 0; - ctx->bctx.blocksize = 128; - ctx->bctx.transform = sha512_transform; + ctx->nblocks = 0; + ctx->count = 0; } void sha384_init(struct sha384_context *ctx) { - struct sha512_state *hd = &ctx->state; + ctx->h0 = U64(0xcbbb9d5dc1059ed8); + ctx->h1 = U64(0x629a292a367cd507); + ctx->h2 = U64(0x9159015a3070dd17); + ctx->h3 = U64(0x152fecd8f70e5939); + ctx->h4 = U64(0x67332667ffc00b31); + ctx->h5 = U64(0x8eb44a8768581511); + ctx->h6 = U64(0xdb0c2e0d64f98fa7); + ctx->h7 = U64(0x47b5481dbefa4fa4); - hd->h0 = UINT64_C(0xcbbb9d5dc1059ed8); - hd->h1 = UINT64_C(0x629a292a367cd507); - hd->h2 = UINT64_C(0x9159015a3070dd17); - hd->h3 = UINT64_C(0x152fecd8f70e5939); - hd->h4 = UINT64_C(0x67332667ffc00b31); - hd->h5 = UINT64_C(0x8eb44a8768581511); - hd->h6 = UINT64_C(0xdb0c2e0d64f98fa7); - hd->h7 = UINT64_C(0x47b5481dbefa4fa4); - - ctx->bctx.nblocks = 0; - ctx->bctx.nblocks_high = 0; - ctx->bctx.count = 0; - ctx->bctx.blocksize = 128; - ctx->bctx.transform = sha512_transform; -} - -void sha512_update(struct sha512_context *ctx, const byte *in_buf, size_t in_len) -{ - sha256_update(&ctx->bctx, in_buf, in_len); + ctx->nblocks = 0; + ctx->count = 0; } static inline u64 @@ -82,82 +67,82 @@ Maj(u64 x, u64 y, u64 z) } static inline u64 -Sum0(u64 x) +sum0(u64 x) { - return (ROTR (x, 28) ^ ROTR (x, 34) ^ ROTR (x, 39)); + return (ROTR(x, 28) ^ ROTR(x, 34) ^ ROTR(x, 39)); } static inline u64 -Sum1 (u64 x) +sum1(u64 x) { - return (ROTR (x, 14) ^ ROTR (x, 18) ^ ROTR (x, 41)); + return (ROTR(x, 14) ^ ROTR(x, 18) ^ ROTR(x, 41)); } static const u64 k[] = { - UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd), - UINT64_C(0xb5c0fbcfec4d3b2f), UINT64_C(0xe9b5dba58189dbbc), - UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019), - UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118), - UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe), - UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2), - UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1), - UINT64_C(0x9bdc06a725c71235), UINT64_C(0xc19bf174cf692694), - UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3), - UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65), - UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483), - UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5), - UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210), - UINT64_C(0xb00327c898fb213f), UINT64_C(0xbf597fc7beef0ee4), - UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725), - UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70), - UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926), - UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df), - UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8), - UINT64_C(0x81c2c92e47edaee6), UINT64_C(0x92722c851482353b), - UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001), - UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30), - UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910), - UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8), - UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53), - UINT64_C(0x2748774cdf8eeb99), UINT64_C(0x34b0bcb5e19b48a8), - UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb), - UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3), - UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60), - UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec), - UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9), - UINT64_C(0xbef9a3f7b2c67915), UINT64_C(0xc67178f2e372532b), - UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207), - UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178), - UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6), - UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b), - UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493), - UINT64_C(0x3c9ebe0a15c9bebc), UINT64_C(0x431d67c49c100d4c), - UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a), - UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817) + U64(0x428a2f98d728ae22), U64(0x7137449123ef65cd), + U64(0xb5c0fbcfec4d3b2f), U64(0xe9b5dba58189dbbc), + U64(0x3956c25bf348b538), U64(0x59f111f1b605d019), + U64(0x923f82a4af194f9b), U64(0xab1c5ed5da6d8118), + U64(0xd807aa98a3030242), U64(0x12835b0145706fbe), + U64(0x243185be4ee4b28c), U64(0x550c7dc3d5ffb4e2), + U64(0x72be5d74f27b896f), U64(0x80deb1fe3b1696b1), + U64(0x9bdc06a725c71235), U64(0xc19bf174cf692694), + U64(0xe49b69c19ef14ad2), U64(0xefbe4786384f25e3), + U64(0x0fc19dc68b8cd5b5), U64(0x240ca1cc77ac9c65), + U64(0x2de92c6f592b0275), U64(0x4a7484aa6ea6e483), + U64(0x5cb0a9dcbd41fbd4), U64(0x76f988da831153b5), + U64(0x983e5152ee66dfab), U64(0xa831c66d2db43210), + U64(0xb00327c898fb213f), U64(0xbf597fc7beef0ee4), + U64(0xc6e00bf33da88fc2), U64(0xd5a79147930aa725), + U64(0x06ca6351e003826f), U64(0x142929670a0e6e70), + U64(0x27b70a8546d22ffc), U64(0x2e1b21385c26c926), + U64(0x4d2c6dfc5ac42aed), U64(0x53380d139d95b3df), + U64(0x650a73548baf63de), U64(0x766a0abb3c77b2a8), + U64(0x81c2c92e47edaee6), U64(0x92722c851482353b), + U64(0xa2bfe8a14cf10364), U64(0xa81a664bbc423001), + U64(0xc24b8b70d0f89791), U64(0xc76c51a30654be30), + U64(0xd192e819d6ef5218), U64(0xd69906245565a910), + U64(0xf40e35855771202a), U64(0x106aa07032bbd1b8), + U64(0x19a4c116b8d2d0c8), U64(0x1e376c085141ab53), + U64(0x2748774cdf8eeb99), U64(0x34b0bcb5e19b48a8), + U64(0x391c0cb3c5c95a63), U64(0x4ed8aa4ae3418acb), + U64(0x5b9cca4f7763e373), U64(0x682e6ff3d6b2b8a3), + U64(0x748f82ee5defb2fc), U64(0x78a5636f43172f60), + U64(0x84c87814a1f0ab72), U64(0x8cc702081a6439ec), + U64(0x90befffa23631e28), U64(0xa4506cebde82bde9), + U64(0xbef9a3f7b2c67915), U64(0xc67178f2e372532b), + U64(0xca273eceea26619c), U64(0xd186b8c721c0c207), + U64(0xeada7dd6cde0eb1e), U64(0xf57d4f7fee6ed178), + U64(0x06f067aa72176fba), U64(0x0a637dc5a2c898a6), + U64(0x113f9804bef90dae), U64(0x1b710b35131c471b), + U64(0x28db77f523047d84), U64(0x32caab7b40c72493), + U64(0x3c9ebe0a15c9bebc), U64(0x431d67c49c100d4c), + U64(0x4cc5d4becb3e42b6), U64(0x597f299cfc657e2a), + U64(0x5fcb6fab3ad6faec), U64(0x6c44198c4a475817) }; /* * Transform the message W which consists of 16 64-bit-words */ static uint -sha512_transform_block(struct sha512_state *hd, const byte *data) +sha512_transform(struct sha512_context *ctx, const byte *data) { u64 a, b, c, d, e, f, g, h; u64 w[16]; - int t; + uint t; /* get values from the chaining vars */ - a = hd->h0; - b = hd->h1; - c = hd->h2; - d = hd->h3; - e = hd->h4; - f = hd->h5; - g = hd->h6; - h = hd->h7; + a = ctx->h0; + b = ctx->h1; + c = ctx->h2; + d = ctx->h3; + e = ctx->h4; + f = ctx->h5; + g = ctx->h6; + h = ctx->h7; - for ( t = 0; t < 16; t++ ) + for (t = 0; t < 16; t++) w[t] = get_u64(data + t * 8); #define S0(x) (ROTR((x),1) ^ ROTR((x),8) ^ ((x)>>7)) @@ -175,10 +160,10 @@ sha512_transform_block(struct sha512_state *hd, const byte *data) Unrolled with macros: 350ms Unrolled with inline: 330ms */ -#if 0 /* Not unrolled. */ - t1 = h + Sum1 (e) + Ch(e, f, g) + k[t] + w[t%16]; - w[t%16] += S1 (w[(t - 2)%16]) + w[(t - 7)%16] + S0 (w[(t - 15)%16]); - t2 = Sum0 (a) + Maj(a, b, c); +#ifndef SHA512_UNROLLED + t1 = h + sum1(e) + Ch(e, f, g) + k[t] + w[t%16]; + w[t%16] += S1(w[(t - 2)%16]) + w[(t - 7)%16] + S0(w[(t - 15)%16]); + t2 = sum0(a) + Maj(a, b, c); h = g; g = f; f = e; @@ -188,100 +173,100 @@ sha512_transform_block(struct sha512_state *hd, const byte *data) b = a; a = t1 + t2; t++; -#else /* Unrolled to interweave the chain variables. */ - t1 = h + Sum1 (e) + Ch(e, f, g) + k[t] + w[0]; - w[0] += S1 (w[14]) + w[9] + S0 (w[1]); - t2 = Sum0 (a) + Maj(a, b, c); +#else /* Unrolled */ + t1 = h + sum1(e) + Ch(e, f, g) + k[t] + w[0]; + w[0] += S1(w[14]) + w[9] + S0(w[1]); + t2 = sum0(a) + Maj(a, b, c); d += t1; h = t1 + t2; - t1 = g + Sum1 (d) + Ch(d, e, f) + k[t+1] + w[1]; - w[1] += S1 (w[15]) + w[10] + S0 (w[2]); - t2 = Sum0 (h) + Maj(h, a, b); + t1 = g + sum1(d) + Ch(d, e, f) + k[t+1] + w[1]; + w[1] += S1(w[15]) + w[10] + S0(w[2]); + t2 = sum0(h) + Maj(h, a, b); c += t1; g = t1 + t2; - t1 = f + Sum1 (c) + Ch(c, d, e) + k[t+2] + w[2]; - w[2] += S1 (w[0]) + w[11] + S0 (w[3]); - t2 = Sum0 (g) + Maj(g, h, a); + t1 = f + sum1(c) + Ch(c, d, e) + k[t+2] + w[2]; + w[2] += S1(w[0]) + w[11] + S0(w[3]); + t2 = sum0(g) + Maj(g, h, a); b += t1; f = t1 + t2; - t1 = e + Sum1 (b) + Ch(b, c, d) + k[t+3] + w[3]; - w[3] += S1 (w[1]) + w[12] + S0 (w[4]); - t2 = Sum0 (f) + Maj(f, g, h); + t1 = e + sum1(b) + Ch(b, c, d) + k[t+3] + w[3]; + w[3] += S1(w[1]) + w[12] + S0(w[4]); + t2 = sum0(f) + Maj(f, g, h); a += t1; e = t1 + t2; - t1 = d + Sum1 (a) + Ch(a, b, c) + k[t+4] + w[4]; - w[4] += S1 (w[2]) + w[13] + S0 (w[5]); - t2 = Sum0 (e) + Maj(e, f, g); + t1 = d + sum1(a) + Ch(a, b, c) + k[t+4] + w[4]; + w[4] += S1(w[2]) + w[13] + S0(w[5]); + t2 = sum0(e) + Maj(e, f, g); h += t1; d = t1 + t2; - t1 = c + Sum1 (h) + Ch(h, a, b) + k[t+5] + w[5]; - w[5] += S1 (w[3]) + w[14] + S0 (w[6]); - t2 = Sum0 (d) + Maj(d, e, f); + t1 = c + sum1(h) + Ch(h, a, b) + k[t+5] + w[5]; + w[5] += S1(w[3]) + w[14] + S0(w[6]); + t2 = sum0(d) + Maj(d, e, f); g += t1; c = t1 + t2; - t1 = b + Sum1 (g) + Ch(g, h, a) + k[t+6] + w[6]; - w[6] += S1 (w[4]) + w[15] + S0 (w[7]); - t2 = Sum0 (c) + Maj(c, d, e); + t1 = b + sum1(g) + Ch(g, h, a) + k[t+6] + w[6]; + w[6] += S1(w[4]) + w[15] + S0(w[7]); + t2 = sum0(c) + Maj(c, d, e); f += t1; b = t1 + t2; - t1 = a + Sum1 (f) + Ch(f, g, h) + k[t+7] + w[7]; - w[7] += S1 (w[5]) + w[0] + S0 (w[8]); - t2 = Sum0 (b) + Maj(b, c, d); + t1 = a + sum1(f) + Ch(f, g, h) + k[t+7] + w[7]; + w[7] += S1(w[5]) + w[0] + S0(w[8]); + t2 = sum0(b) + Maj(b, c, d); e += t1; a = t1 + t2; - t1 = h + Sum1 (e) + Ch(e, f, g) + k[t+8] + w[8]; - w[8] += S1 (w[6]) + w[1] + S0 (w[9]); - t2 = Sum0 (a) + Maj(a, b, c); + t1 = h + sum1(e) + Ch(e, f, g) + k[t+8] + w[8]; + w[8] += S1(w[6]) + w[1] + S0(w[9]); + t2 = sum0(a) + Maj(a, b, c); d += t1; h = t1 + t2; - t1 = g + Sum1 (d) + Ch(d, e, f) + k[t+9] + w[9]; - w[9] += S1 (w[7]) + w[2] + S0 (w[10]); - t2 = Sum0 (h) + Maj(h, a, b); + t1 = g + sum1(d) + Ch(d, e, f) + k[t+9] + w[9]; + w[9] += S1(w[7]) + w[2] + S0(w[10]); + t2 = sum0(h) + Maj(h, a, b); c += t1; g = t1 + t2; - t1 = f + Sum1 (c) + Ch(c, d, e) + k[t+10] + w[10]; - w[10] += S1 (w[8]) + w[3] + S0 (w[11]); - t2 = Sum0 (g) + Maj(g, h, a); + t1 = f + sum1(c) + Ch(c, d, e) + k[t+10] + w[10]; + w[10] += S1(w[8]) + w[3] + S0(w[11]); + t2 = sum0(g) + Maj(g, h, a); b += t1; f = t1 + t2; - t1 = e + Sum1 (b) + Ch(b, c, d) + k[t+11] + w[11]; - w[11] += S1 (w[9]) + w[4] + S0 (w[12]); - t2 = Sum0 (f) + Maj(f, g, h); + t1 = e + sum1(b) + Ch(b, c, d) + k[t+11] + w[11]; + w[11] += S1(w[9]) + w[4] + S0(w[12]); + t2 = sum0(f) + Maj(f, g, h); a += t1; e = t1 + t2; - t1 = d + Sum1 (a) + Ch(a, b, c) + k[t+12] + w[12]; - w[12] += S1 (w[10]) + w[5] + S0 (w[13]); - t2 = Sum0 (e) + Maj(e, f, g); + t1 = d + sum1(a) + Ch(a, b, c) + k[t+12] + w[12]; + w[12] += S1(w[10]) + w[5] + S0(w[13]); + t2 = sum0(e) + Maj(e, f, g); h += t1; d = t1 + t2; - t1 = c + Sum1 (h) + Ch(h, a, b) + k[t+13] + w[13]; - w[13] += S1 (w[11]) + w[6] + S0 (w[14]); - t2 = Sum0 (d) + Maj(d, e, f); + t1 = c + sum1(h) + Ch(h, a, b) + k[t+13] + w[13]; + w[13] += S1(w[11]) + w[6] + S0(w[14]); + t2 = sum0(d) + Maj(d, e, f); g += t1; c = t1 + t2; - t1 = b + Sum1 (g) + Ch(g, h, a) + k[t+14] + w[14]; - w[14] += S1 (w[12]) + w[7] + S0 (w[15]); - t2 = Sum0 (c) + Maj(c, d, e); + t1 = b + sum1(g) + Ch(g, h, a) + k[t+14] + w[14]; + w[14] += S1(w[12]) + w[7] + S0(w[15]); + t2 = sum0(c) + Maj(c, d, e); f += t1; b = t1 + t2; - t1 = a + Sum1 (f) + Ch(f, g, h) + k[t+15] + w[15]; - w[15] += S1 (w[13]) + w[8] + S0 (w[0]); - t2 = Sum0 (b) + Maj(b, c, d); + t1 = a + sum1(f) + Ch(f, g, h) + k[t+15] + w[15]; + w[15] += S1(w[13]) + w[8] + S0(w[0]); + t2 = sum0(b) + Maj(b, c, d); e += t1; a = t1 + t2; @@ -293,9 +278,9 @@ sha512_transform_block(struct sha512_state *hd, const byte *data) { u64 t1, t2; -#if 0 /* Not unrolled. */ - t1 = h + Sum1 (e) + Ch(e, f, g) + k[t] + w[t%16]; - t2 = Sum0 (a) + Maj(a, b, c); +#ifndef SHA512_UNROLLED + t1 = h + sum1(e) + Ch(e, f, g) + k[t] + w[t%16]; + t2 = sum0(a) + Maj(a, b, c); h = g; g = f; f = e; @@ -305,84 +290,84 @@ sha512_transform_block(struct sha512_state *hd, const byte *data) b = a; a = t1 + t2; t++; -#else /* Unrolled to interweave the chain variables. */ - t1 = h + Sum1 (e) + Ch(e, f, g) + k[t] + w[0]; - t2 = Sum0 (a) + Maj(a, b, c); +#else /* Unrolled */ + t1 = h + sum1(e) + Ch(e, f, g) + k[t] + w[0]; + t2 = sum0(a) + Maj(a, b, c); d += t1; h = t1 + t2; - t1 = g + Sum1 (d) + Ch(d, e, f) + k[t+1] + w[1]; - t2 = Sum0 (h) + Maj(h, a, b); + t1 = g + sum1(d) + Ch(d, e, f) + k[t+1] + w[1]; + t2 = sum0(h) + Maj(h, a, b); c += t1; g = t1 + t2; - t1 = f + Sum1 (c) + Ch(c, d, e) + k[t+2] + w[2]; - t2 = Sum0 (g) + Maj(g, h, a); + t1 = f + sum1(c) + Ch(c, d, e) + k[t+2] + w[2]; + t2 = sum0(g) + Maj(g, h, a); b += t1; f = t1 + t2; - t1 = e + Sum1 (b) + Ch(b, c, d) + k[t+3] + w[3]; - t2 = Sum0 (f) + Maj(f, g, h); + t1 = e + sum1(b) + Ch(b, c, d) + k[t+3] + w[3]; + t2 = sum0(f) + Maj(f, g, h); a += t1; e = t1 + t2; - t1 = d + Sum1 (a) + Ch(a, b, c) + k[t+4] + w[4]; - t2 = Sum0 (e) + Maj(e, f, g); + t1 = d + sum1(a) + Ch(a, b, c) + k[t+4] + w[4]; + t2 = sum0(e) + Maj(e, f, g); h += t1; d = t1 + t2; - t1 = c + Sum1 (h) + Ch(h, a, b) + k[t+5] + w[5]; - t2 = Sum0 (d) + Maj(d, e, f); + t1 = c + sum1(h) + Ch(h, a, b) + k[t+5] + w[5]; + t2 = sum0(d) + Maj(d, e, f); g += t1; c = t1 + t2; - t1 = b + Sum1 (g) + Ch(g, h, a) + k[t+6] + w[6]; - t2 = Sum0 (c) + Maj(c, d, e); + t1 = b + sum1(g) + Ch(g, h, a) + k[t+6] + w[6]; + t2 = sum0(c) + Maj(c, d, e); f += t1; b = t1 + t2; - t1 = a + Sum1 (f) + Ch(f, g, h) + k[t+7] + w[7]; - t2 = Sum0 (b) + Maj(b, c, d); + t1 = a + sum1(f) + Ch(f, g, h) + k[t+7] + w[7]; + t2 = sum0(b) + Maj(b, c, d); e += t1; a = t1 + t2; - t1 = h + Sum1 (e) + Ch(e, f, g) + k[t+8] + w[8]; - t2 = Sum0 (a) + Maj(a, b, c); + t1 = h + sum1(e) + Ch(e, f, g) + k[t+8] + w[8]; + t2 = sum0(a) + Maj(a, b, c); d += t1; h = t1 + t2; - t1 = g + Sum1 (d) + Ch(d, e, f) + k[t+9] + w[9]; - t2 = Sum0 (h) + Maj(h, a, b); + t1 = g + sum1(d) + Ch(d, e, f) + k[t+9] + w[9]; + t2 = sum0(h) + Maj(h, a, b); c += t1; g = t1 + t2; - t1 = f + Sum1 (c) + Ch(c, d, e) + k[t+10] + w[10]; - t2 = Sum0 (g) + Maj(g, h, a); + t1 = f + sum1(c) + Ch(c, d, e) + k[t+10] + w[10]; + t2 = sum0(g) + Maj(g, h, a); b += t1; f = t1 + t2; - t1 = e + Sum1 (b) + Ch(b, c, d) + k[t+11] + w[11]; - t2 = Sum0 (f) + Maj(f, g, h); + t1 = e + sum1(b) + Ch(b, c, d) + k[t+11] + w[11]; + t2 = sum0(f) + Maj(f, g, h); a += t1; e = t1 + t2; - t1 = d + Sum1 (a) + Ch(a, b, c) + k[t+12] + w[12]; - t2 = Sum0 (e) + Maj(e, f, g); + t1 = d + sum1(a) + Ch(a, b, c) + k[t+12] + w[12]; + t2 = sum0(e) + Maj(e, f, g); h += t1; d = t1 + t2; - t1 = c + Sum1 (h) + Ch(h, a, b) + k[t+13] + w[13]; - t2 = Sum0 (d) + Maj(d, e, f); + t1 = c + sum1(h) + Ch(h, a, b) + k[t+13] + w[13]; + t2 = sum0(d) + Maj(d, e, f); g += t1; c = t1 + t2; - t1 = b + Sum1 (g) + Ch(g, h, a) + k[t+14] + w[14]; - t2 = Sum0 (c) + Maj(c, d, e); + t1 = b + sum1(g) + Ch(g, h, a) + k[t+14] + w[14]; + t2 = sum0(c) + Maj(c, d, e); f += t1; b = t1 + t2; - t1 = a + Sum1 (f) + Ch(f, g, h) + k[t+15] + w[15]; - t2 = Sum0 (b) + Maj(b, c, d); + t1 = a + sum1(f) + Ch(f, g, h) + k[t+15] + w[15]; + t2 = sum0(b) + Maj(b, c, d); e += t1; a = t1 + t2; @@ -391,61 +376,77 @@ sha512_transform_block(struct sha512_state *hd, const byte *data) } /* Update chaining vars. */ - hd->h0 += a; - hd->h1 += b; - hd->h2 += c; - hd->h3 += d; - hd->h4 += e; - hd->h5 += f; - hd->h6 += g; - hd->h7 += h; + ctx->h0 += a; + ctx->h1 += b; + ctx->h2 += c; + ctx->h3 += d; + ctx->h4 += e; + ctx->h5 += f; + ctx->h6 += g; + ctx->h7 += h; return /* burn_stack */ (8 + 16) * sizeof(u64) + sizeof(u32) + 3 * sizeof(void*); } -static uint -sha512_transform(void *context, const byte *data, size_t nblks) +void +sha512_update(struct sha512_context *ctx, const byte *buf, size_t len) { - struct sha512_context *ctx = context; - uint burn; - - do + if (ctx->count) { - burn = sha512_transform_block(&ctx->state, data) + 3 * sizeof(void*); - data += 128; - } - while(--nblks); + /* Fill rest of internal buffer */ + for (; len && ctx->count < SHA512_BLOCK_SIZE; len--) + ctx->buf[ctx->count++] = *buf++; - return burn; + if (ctx->count < SHA512_BLOCK_SIZE) + return; + + /* Process data from internal buffer */ + sha512_transform(ctx, ctx->buf); + ctx->nblocks++; + ctx->count = 0; + } + + if (!len) + return; + + /* Process data from input buffer */ + while (len >= SHA512_BLOCK_SIZE) + { + sha512_transform(ctx, buf); + ctx->nblocks++; + buf += SHA512_BLOCK_SIZE; + len -= SHA512_BLOCK_SIZE; + } + + /* Copy remaining data to internal buffer */ + memcpy(ctx->buf, buf, len); + ctx->count = len; } -/* The routine final terminates the computation and - * returns the digest. - * The handle is prepared for a new cycle, but adding bytes to the - * handle will the destroy the returned buffer. - * Returns: 64 bytes representing the digest. When used for sha384, - * we take the leftmost 48 of those bytes. +/* + * The routine final terminates the computation and returns the digest. The + * handle is prepared for a new cycle, but adding bytes to the handle will the + * destroy the returned buffer. + * + * Returns: 64 bytes representing the digest. When used for sha384, we take the + * first 48 of those bytes. */ byte * sha512_final(struct sha512_context *ctx) { u64 t, th, msb, lsb; - byte *p; - sha256_update(&ctx->bctx, NULL, 0); /* flush */ ; + sha512_update(ctx, NULL, 0); /* flush */ - t = ctx->bctx.nblocks; - /* if (sizeof t == sizeof ctx->bctx.nblocks) */ - th = ctx->bctx.nblocks_high; - /* else */ - /* th = ctx->bctx.nblocks >> 64; In case we ever use u128 */ + t = ctx->nblocks; + th = 0; /* multiply by 128 to make a byte count */ lsb = t << 7; msb = (th << 7) | (t >> 57); /* add the count */ t = lsb; - if ((lsb += ctx->bctx.count) < t) + if ((lsb += ctx->count) < t) msb++; /* multiply by 8 to make a bit count */ t = lsb; @@ -453,55 +454,56 @@ sha512_final(struct sha512_context *ctx) msb <<= 3; msb |= t >> 61; - if (ctx->bctx.count < 112) - { /* enough room */ - ctx->bctx.buf[ctx->bctx.count++] = 0x80; /* pad */ - while(ctx->bctx.count < 112) - ctx->bctx.buf[ctx->bctx.count++] = 0; /* pad */ + if (ctx->count < 112) + { + /* enough room */ + ctx->buf[ctx->count++] = 0x80; /* pad */ + while(ctx->count < 112) + ctx->buf[ctx->count++] = 0; /* pad */ } else - { /* need one extra block */ - ctx->bctx.buf[ctx->bctx.count++] = 0x80; /* pad character */ - while(ctx->bctx.count < 128) - ctx->bctx.buf[ctx->bctx.count++] = 0; - sha256_update(&ctx->bctx, NULL, 0); /* flush */ ; - memset(ctx->bctx.buf, 0, 112); /* fill next block with zeroes */ + { + /* need one extra block */ + ctx->buf[ctx->count++] = 0x80; /* pad character */ + while(ctx->count < 128) + ctx->buf[ctx->count++] = 0; + sha512_update(ctx, NULL, 0); /* flush */ + memset(ctx->buf, 0, 112); /* fill next block with zeroes */ } - /* append the 128 bit count */ - put_u64(ctx->bctx.buf + 112, msb); - put_u64(ctx->bctx.buf + 120, lsb); - sha512_transform(ctx, ctx->bctx.buf, 1); - p = ctx->bctx.buf; -#define X(a) do { put_u64(p, ctx->state.h##a); p += 8; } while(0) - X (0); - X (1); - X (2); - X (3); - X (4); - X (5); - /* Note that these last two chunks are included even for SHA384. - We just ignore them. */ - X (6); - X (7); + /* append the 128 bit count */ + put_u64(ctx->buf + 112, msb); + put_u64(ctx->buf + 120, lsb); + sha512_transform(ctx, ctx->buf); + + byte *p = ctx->buf; +#define X(a) do { put_u64(p, ctx->h##a); p += 8; } while(0) + X(0); + X(1); + X(2); + X(3); + X(4); + X(5); + X(6); + X(7); #undef X - return ctx->bctx.buf; + return ctx->buf; } /* - * SHA512-HMAC + * SHA512-HMAC */ static void sha512_hash_buffer(byte *outbuf, const byte *buffer, size_t length) { - struct sha512_context hd_tmp; + struct sha512_context ctx; - sha512_init(&hd_tmp); - sha512_update(&hd_tmp, buffer, length); - memcpy(outbuf, sha512_final(&hd_tmp), SHA512_SIZE); + sha512_init(&ctx); + sha512_update(&ctx, buffer, length); + memcpy(outbuf, sha512_final(&ctx), SHA512_SIZE); } void @@ -513,12 +515,12 @@ sha512_hmac_init(struct sha512_hmac_context *ctx, const byte *key, size_t keylen if (keylen <= SHA512_BLOCK_SIZE) { memcpy(keybuf, key, keylen); - bzero(keybuf + keylen, SHA512_BLOCK_SIZE - keylen); + memset(keybuf + keylen, 0, SHA512_BLOCK_SIZE - keylen); } else { sha512_hash_buffer(keybuf, key, keylen); - bzero(keybuf + SHA512_SIZE, SHA512_BLOCK_SIZE - SHA512_SIZE); + memset(keybuf + SHA512_SIZE, 0, SHA512_BLOCK_SIZE - SHA512_SIZE); } /* Initialize the inner digest */ @@ -535,13 +537,15 @@ sha512_hmac_init(struct sha512_hmac_context *ctx, const byte *key, size_t keylen sha512_update(&ctx->octx, buf, SHA512_BLOCK_SIZE); } -void sha512_hmac_update(struct sha512_hmac_context *ctx, const byte *buf, size_t buflen) +void +sha512_hmac_update(struct sha512_hmac_context *ctx, const byte *buf, size_t buflen) { /* Just update the inner digest */ sha512_update(&ctx->ictx, buf, buflen); } -byte *sha512_hmac_final(struct sha512_hmac_context *ctx) +byte * +sha512_hmac_final(struct sha512_hmac_context *ctx) { /* Finish the inner digest */ byte *isha = sha512_final(&ctx->ictx); @@ -553,17 +557,17 @@ byte *sha512_hmac_final(struct sha512_hmac_context *ctx) /* - * SHA384-HMAC + * SHA384-HMAC */ static void sha384_hash_buffer(byte *outbuf, const byte *buffer, size_t length) { - struct sha384_context hd_tmp; + struct sha384_context ctx; - sha384_init(&hd_tmp); - sha384_update(&hd_tmp, buffer, length); - memcpy(outbuf, sha384_final(&hd_tmp), SHA384_SIZE); + sha384_init(&ctx); + sha384_update(&ctx, buffer, length); + memcpy(outbuf, sha384_final(&ctx), SHA384_SIZE); } void @@ -575,12 +579,12 @@ sha384_hmac_init(struct sha384_hmac_context *ctx, const byte *key, size_t keylen if (keylen <= SHA384_BLOCK_SIZE) { memcpy(keybuf, key, keylen); - bzero(keybuf + keylen, SHA384_BLOCK_SIZE - keylen); + memset(keybuf + keylen, 0, SHA384_BLOCK_SIZE - keylen); } else { sha384_hash_buffer(keybuf, key, keylen); - bzero(keybuf + SHA384_SIZE, SHA384_BLOCK_SIZE - SHA384_SIZE); + memset(keybuf + SHA384_SIZE, 0, SHA384_BLOCK_SIZE - SHA384_SIZE); } /* Initialize the inner digest */ @@ -597,13 +601,15 @@ sha384_hmac_init(struct sha384_hmac_context *ctx, const byte *key, size_t keylen sha384_update(&ctx->octx, buf, SHA384_BLOCK_SIZE); } -void sha384_hmac_update(struct sha384_hmac_context *ctx, const byte *buf, size_t buflen) +void +sha384_hmac_update(struct sha384_hmac_context *ctx, const byte *buf, size_t buflen) { /* Just update the inner digest */ sha384_update(&ctx->ictx, buf, buflen); } -byte *sha384_hmac_final(struct sha384_hmac_context *ctx) +byte * +sha384_hmac_final(struct sha384_hmac_context *ctx) { /* Finish the inner digest */ byte *isha = sha384_final(&ctx->ictx); diff --git a/lib/sha512.h b/lib/sha512.h index bd998152..1614a3ac 100644 --- a/lib/sha512.h +++ b/lib/sha512.h @@ -13,7 +13,8 @@ #ifndef _BIRD_SHA512_H_ #define _BIRD_SHA512_H_ -#include "lib/sha256.h" +#include "nest/bird.h" + #define SHA384_SIZE 48 #define SHA384_HEX_SIZE 97 @@ -23,43 +24,41 @@ #define SHA512_HEX_SIZE 129 #define SHA512_BLOCK_SIZE 128 -struct sha512_state -{ + +struct sha512_context { u64 h0, h1, h2, h3, h4, h5, h6, h7; + byte buf[SHA512_BLOCK_SIZE]; + uint nblocks; + uint count; }; -struct sha512_context -{ - struct sha256_context bctx; - struct sha512_state state; -}; -#define sha384_context sha512_context /* aliasing 'struct sha384_context' to 'struct sha512_context' */ +#define sha384_context sha512_context void sha512_init(struct sha512_context *ctx); void sha384_init(struct sha384_context *ctx); -void sha512_update(struct sha512_context *ctx, const byte *in_buf, size_t in_len); -static inline void sha384_update(struct sha384_context *ctx, const byte *in_buf, size_t in_len) -{ - sha512_update(ctx, in_buf, in_len); -} +void sha512_update(struct sha512_context *ctx, const byte *buf, size_t len); +static inline void sha384_update(struct sha384_context *ctx, const byte *buf, size_t len) +{ sha512_update(ctx, buf, len); } + +byte *sha512_final(struct sha512_context *ctx); +static inline byte *sha384_final(struct sha384_context *ctx) +{ return sha512_final(ctx); } -byte* sha512_final(struct sha512_context *ctx); -static inline byte* sha384_final(struct sha384_context *ctx) -{ - return sha512_final(ctx); -} /* * HMAC-SHA512, HMAC-SHA384 */ + struct sha512_hmac_context { struct sha512_context ictx; struct sha512_context octx; -} ; -#define sha384_hmac_context sha512_hmac_context /* aliasing 'struct sha384_hmac_context' to 'struct sha384_hmac_context' */ +}; + +#define sha384_hmac_context sha512_hmac_context + void sha512_hmac_init(struct sha512_hmac_context *ctx, const byte *key, size_t keylen); void sha384_hmac_init(struct sha384_hmac_context *ctx, const byte *key, size_t keylen); @@ -70,4 +69,5 @@ void sha384_hmac_update(struct sha384_hmac_context *ctx, const byte *buf, size_t byte *sha512_hmac_final(struct sha512_hmac_context *ctx); byte *sha384_hmac_final(struct sha384_hmac_context *ctx); + #endif /* _BIRD_SHA512_H_ */ From e422ca0f292d08a873deacdbffbb6c6cbd79f88a Mon Sep 17 00:00:00 2001 From: "Ondrej Zajicek (work)" Date: Tue, 24 Nov 2015 13:52:26 +0100 Subject: [PATCH 16/18] Some consts for function arguments Patch from Pavel Tvrdik --- lib/ip.c | 6 +++--- lib/ip.h | 4 ++-- lib/patmatch.c | 2 +- lib/string.h | 2 +- lib/unaligned.h | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/ip.c b/lib/ip.c index e1bfba49..2061a1f7 100644 --- a/lib/ip.c +++ b/lib/ip.c @@ -233,7 +233,7 @@ ip6_ntop(ip6_addr a, char *b) } int -ip4_pton(char *a, ip4_addr *o) +ip4_pton(const char *a, ip4_addr *o) { int i; unsigned long int l; @@ -258,11 +258,11 @@ ip4_pton(char *a, ip4_addr *o) } int -ip6_pton(char *a, ip6_addr *o) +ip6_pton(const char *a, ip6_addr *o) { u16 words[8]; int i, j, k, l, hfil; - char *start; + const char *start; if (a[0] == ':') /* Leading :: */ { diff --git a/lib/ip.h b/lib/ip.h index 90bb7f8a..e33adc6e 100644 --- a/lib/ip.h +++ b/lib/ip.h @@ -446,8 +446,8 @@ static inline char * ip4_ntox(ip4_addr a, char *b) static inline char * ip6_ntox(ip6_addr a, char *b) { return b + bsprintf(b, "%08x.%08x.%08x.%08x", _I0(a), _I1(a), _I2(a), _I3(a)); } -int ip4_pton(char *a, ip4_addr *o); -int ip6_pton(char *a, ip6_addr *o); +int ip4_pton(const char *a, ip4_addr *o); +int ip6_pton(const char *a, ip6_addr *o); // XXXX these functions must be redesigned or removed #ifdef IPV6 diff --git a/lib/patmatch.c b/lib/patmatch.c index 923e8f86..d2e1e313 100644 --- a/lib/patmatch.c +++ b/lib/patmatch.c @@ -16,7 +16,7 @@ #endif int -MATCH_FUNC_NAME(byte *p, byte *s) +MATCH_FUNC_NAME(const byte *p, const byte *s) { while (*p) { diff --git a/lib/string.h b/lib/string.h index 528a1a19..218f7b1c 100644 --- a/lib/string.h +++ b/lib/string.h @@ -22,6 +22,6 @@ int buffer_vprint(buffer *buf, const char *fmt, va_list args); int buffer_print(buffer *buf, const char *fmt, ...); void buffer_puts(buffer *buf, const char *str); -int patmatch(byte *pat, byte *str); +int patmatch(const byte *pat, const byte *str); #endif diff --git a/lib/unaligned.h b/lib/unaligned.h index a2dbae4f..dc777fbf 100644 --- a/lib/unaligned.h +++ b/lib/unaligned.h @@ -20,7 +20,7 @@ #include "lib/string.h" static inline u16 -get_u16(void *p) +get_u16(const void *p) { u16 x; memcpy(&x, p, 2); @@ -28,7 +28,7 @@ get_u16(void *p) } static inline u32 -get_u32(void *p) +get_u32(const void *p) { u32 x; memcpy(&x, p, 4); From ad27615760e2795da3efe5e97c0e888281d5ca59 Mon Sep 17 00:00:00 2001 From: Jan Moskyto Matejka Date: Tue, 10 Nov 2015 14:59:41 +0100 Subject: [PATCH 17/18] 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 | 191 ++++++++++++++++++++++++++++++----------- 1 file changed, 143 insertions(+), 48 deletions(-) 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) -#else - || !a[IFA_LOCAL] || RTA_PAYLOAD(a[IFA_LOCAL]) != sizeof(ip_addr) - || (a[IFA_BROADCAST] && RTA_PAYLOAD(a[IFA_BROADCAST]) != sizeof(ip_addr)) -#endif - ) + + switch (i->ifa_family) { - log(L_ERR "nl_parse_addr: Malformed message received"); +#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 + 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 "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]); From 33b4f40acce02c90b4b7766c5c94ebf2d22765c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Tvrd=C3=ADk?= Date: Fri, 13 Nov 2015 16:08:28 +0100 Subject: [PATCH 18/18] MD5: Mormalize naming style --- lib/md5.c | 418 ++++++++++++++++++++++++++------------------ lib/md5.h | 55 ++++-- proto/ospf/packet.c | 31 ++-- proto/rip/packets.c | 18 +- 4 files changed, 310 insertions(+), 212 deletions(-) diff --git a/lib/md5.c b/lib/md5.c index ad284f07..8efa62d6 100644 --- a/lib/md5.c +++ b/lib/md5.c @@ -1,154 +1,159 @@ /* - * This code implements the MD5 message-digest algorithm. - * The algorithm is due to Ron Rivest. This code was - * written by Colin Plumb in 1993, no copyright is claimed. - * This code is in the public domain; do with it what you wish. + * BIRD Library -- MD5 Hash Function and HMAC-MD5 Function * - * Equivalent code is available from RSA Data Security, Inc. - * This code has been tested against that, and is equivalent, - * except that you don't need to include two pages of legalese - * with every copy. + * (c) 2015 CZ.NIC z.s.p.o. * - * To compute the message digest of a chunk of bytes, declare an - * MD5Context structure, pass it to MD5Init, call MD5Update as - * needed on buffers full of bytes, and then call MD5Final, which - * will fill a supplied 16-byte array with the digest. + * The code was written by Colin Plumb in 1993, no copyright is claimed. + * + * Adapted for BIRD by Martin Mares + * + * Can be freely distributed and used under the terms of the GNU GPL. */ -/* - * Adapted for BIRD by Martin Mares - */ - -#include "nest/bird.h" -#include "lib/string.h" -#include "md5.h" +#include "lib/md5.h" #ifdef CPU_LITTLE_ENDIAN #define byteReverse(buf, len) /* Nothing */ #else -void byteReverse(unsigned char *buf, unsigned longs); +void byteReverse(byte *buf, uint longs); /* * Note: this code is harmless on little-endian machines. */ -void byteReverse(unsigned char *buf, unsigned longs) +void byteReverse(byte *buf, uint longs) { - u32 t; - do { - t = (u32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | - ((unsigned) buf[1] << 8 | buf[0]); - *(u32 *) buf = t; - buf += 4; - } while (--longs); + u32 t; + do { + t = (u32) ((uint) buf[3] << 8 | buf[2]) << 16 | + ((uint) buf[1] << 8 | buf[0]); + *(u32 *) buf = t; + buf += 4; + } while (--longs); } #endif +static void md5_transform(u32 buf[4], u32 const in[16]); + /* * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. */ -void MD5Init(struct MD5Context *ctx) +void +md5_init(struct md5_context *ctx) { - ctx->buf[0] = 0x67452301; - ctx->buf[1] = 0xefcdab89; - ctx->buf[2] = 0x98badcfe; - ctx->buf[3] = 0x10325476; + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; - ctx->bits[0] = 0; - ctx->bits[1] = 0; + ctx->bits[0] = 0; + ctx->bits[1] = 0; } /* * Update context to reflect the concatenation of another buffer full * of bytes. */ -void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) +void +md5_update(struct md5_context *ctx, const byte *buf, uint len) { - u32 t; + u32 t; - /* Update bitcount */ + /* Update bitcount */ - t = ctx->bits[0]; - if ((ctx->bits[0] = t + ((u32) len << 3)) < t) - ctx->bits[1]++; /* Carry from low to high */ - ctx->bits[1] += len >> 29; + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((u32) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; - t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ - /* Handle any leading odd-sized chunks */ + /* Handle any leading odd-sized chunks */ + if (t) + { + byte *p = (byte *) ctx->in + t; - if (t) { - unsigned char *p = (unsigned char *) ctx->in + t; - - t = 64 - t; - if (len < t) { - memcpy(p, buf, len); - return; - } - memcpy(p, buf, t); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (u32 *) ctx->in); - buf += t; - len -= t; + t = 64 - t; + if (len < t) + { + memcpy(p, buf, len); + return; } - /* Process data in 64-byte chunks */ + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + md5_transform(ctx->buf, (u32 *) ctx->in); + buf += t; + len -= t; + } - while (len >= 64) { - memcpy(ctx->in, buf, 64); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (u32 *) ctx->in); - buf += 64; - len -= 64; - } + /* Process data in 64-byte chunks */ + while (len >= 64) + { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + md5_transform(ctx->buf, (u32 *) ctx->in); + buf += 64; + len -= 64; + } - /* Handle any remaining bytes of data. */ - - memcpy(ctx->in, buf, len); + /* Handle any remaining bytes of data. */ + memcpy(ctx->in, buf, len); } /* - * Final wrapup - pad to 64-byte boundary with the bit pattern + * Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) */ -void MD5Final(unsigned char digest[16], struct MD5Context *ctx) +byte * +md5_final(struct md5_context *ctx) { - unsigned count; - unsigned char *p; + uint count; + byte *p; - /* Compute number of bytes mod 64 */ - count = (ctx->bits[0] >> 3) & 0x3F; + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; - /* Set the first char of padding to 0x80. This is safe since there is + /* Set the first char of padding to 0x80. This is safe since there is always at least one byte free */ - p = ctx->in + count; - *p++ = 0x80; + p = ctx->in + count; + *p++ = 0x80; - /* Bytes of padding needed to make 64 bytes */ - count = 64 - 1 - count; + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; - /* Pad out to 56 mod 64 */ - if (count < 8) { - /* Two lots of padding: Pad the first block to 64 bytes */ - memset(p, 0, count); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (u32 *) ctx->in); + /* Pad out to 56 mod 64 */ + if (count < 8) + { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + md5_transform(ctx->buf, (u32 *) ctx->in); - /* Now fill the next block with 56 bytes */ - memset(ctx->in, 0, 56); - } else { - /* Pad block to 56 bytes */ - memset(p, 0, count - 8); - } - byteReverse(ctx->in, 14); + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } + else + { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); - /* Append length in bits and transform */ - ((u32 *) ctx->in)[14] = ctx->bits[0]; - ((u32 *) ctx->in)[15] = ctx->bits[1]; + /* Append length in bits and transform */ + ((u32 *) ctx->in)[14] = ctx->bits[0]; + ((u32 *) ctx->in)[15] = ctx->bits[1]; - MD5Transform(ctx->buf, (u32 *) ctx->in); - byteReverse((unsigned char *) ctx->buf, 4); - memcpy(digest, ctx->buf, 16); - memset((char *) ctx, 0, sizeof(ctx)); /* In case it's sensitive */ + md5_transform(ctx->buf, (u32 *) ctx->in); + byteReverse((byte *) ctx->buf, 4); + + return (byte*) ctx->buf; +} + +/* I am a hard paranoid */ +void +md5_erase_ctx(struct md5_context *ctx) +{ + memset((char *) ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ } /* The four core functions - F1 is optimized somewhat */ @@ -161,92 +166,157 @@ void MD5Final(unsigned char digest[16], struct MD5Context *ctx) /* This is the central step in the MD5 algorithm. */ #define MD5STEP(f, w, x, y, z, data, s) \ - ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) /* * The core of the MD5 algorithm, this alters an existing MD5 hash to * reflect the addition of 16 longwords of new data. MD5Update blocks * the data and converts bytes into longwords for this routine. */ -void MD5Transform(u32 buf[4], u32 const in[16]) +void +md5_transform(u32 buf[4], u32 const in[16]) { - register u32 a, b, c, d; + register u32 a, b, c, d; - a = buf[0]; - b = buf[1]; - c = buf[2]; - d = buf[3]; + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; - MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); - MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); - MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); - MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); - MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); - MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); - MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); - MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); - MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); - MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); - MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); - MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); - MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); - MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); - MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); - MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); - MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); - MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); - MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); - MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); - MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); - MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); - MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); - MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); - MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); - MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); - MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); - MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); - MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); - MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); - MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); - MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); - MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); - MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); - MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); - MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); - MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); - MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); - MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); - MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); - MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); - MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); - MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); - MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); - MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); - MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); - MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); - MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); - MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); - MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); - MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); - MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); - MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); - MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); - MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); - MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); - MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); - MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); - MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); - MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); - MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); - MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); - MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); - MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); - buf[0] += a; - buf[1] += b; - buf[2] += c; - buf[3] += d; + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + + +/* + * MD5-HMAC + */ + +static void +md5_hash_buffer(byte *outbuf, const byte *buffer, size_t length) +{ + struct md5_context hd_tmp; + + md5_init(&hd_tmp); + md5_update(&hd_tmp, buffer, length); + memcpy(outbuf, md5_final(&hd_tmp), MD5_SIZE); +} + +void +md5_hmac_init(struct md5_hmac_context *ctx, const byte *key, size_t keylen) +{ + byte keybuf[MD5_BLOCK_SIZE], buf[MD5_BLOCK_SIZE]; + + /* Hash the key if necessary */ + if (keylen <= MD5_BLOCK_SIZE) + { + memcpy(keybuf, key, keylen); + bzero(keybuf + keylen, MD5_BLOCK_SIZE - keylen); + } + else + { + md5_hash_buffer(keybuf, key, keylen); + bzero(keybuf + MD5_SIZE, MD5_BLOCK_SIZE - MD5_SIZE); + } + + /* Initialize the inner digest */ + md5_init(&ctx->ictx); + int i; + for (i = 0; i < MD5_BLOCK_SIZE; i++) + buf[i] = keybuf[i] ^ 0x36; + md5_update(&ctx->ictx, buf, MD5_BLOCK_SIZE); + + /* Initialize the outer digest */ + md5_init(&ctx->octx); + for (i = 0; i < MD5_BLOCK_SIZE; i++) + buf[i] = keybuf[i] ^ 0x5c; + md5_update(&ctx->octx, buf, MD5_BLOCK_SIZE); +} + +void +md5_hmac_update(struct md5_hmac_context *ctx, const byte *buf, size_t buflen) +{ + /* Just update the inner digest */ + md5_update(&ctx->ictx, buf, buflen); +} + +byte * +md5_hmac_final(struct md5_hmac_context *ctx) +{ + /* Finish the inner digest */ + byte *isha = md5_final(&ctx->ictx); + + /* Finish the outer digest */ + md5_update(&ctx->octx, isha, MD5_SIZE); + return md5_final(&ctx->octx); } diff --git a/lib/md5.h b/lib/md5.h index 12586357..034d764c 100644 --- a/lib/md5.h +++ b/lib/md5.h @@ -1,16 +1,47 @@ -#ifndef MD5_H -#define MD5_H +/* + * BIRD Library -- MD5 Hash Function and HMAC-MD5 Function + * + * (c) 2015 CZ.NIC z.s.p.o. + * + * Adapted for BIRD by Martin Mares + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ -struct MD5Context { - u32 buf[4]; - u32 bits[2]; - unsigned char in[64]; +#ifndef _BIRD_MD5_H_ +#define _BIRD_MD5_H_ + +#include "nest/bird.h" + + +#define MD5_SIZE 16 +#define MD5_HEX_SIZE 33 +#define MD5_BLOCK_SIZE 64 + + +struct md5_context { + u32 buf[4]; + u32 bits[2]; + byte in[64]; }; -void MD5Init(struct MD5Context *context); -void MD5Update(struct MD5Context *context, unsigned char const *buf, - unsigned len); -void MD5Final(unsigned char digest[16], struct MD5Context *context); -void MD5Transform(u32 buf[4], u32 const in[16]); +void md5_init(struct md5_context *ctx); +void md5_update(struct md5_context *ctx, const byte *buf, uint len); +byte *md5_final(struct md5_context *ctx); -#endif /* !MD5_H */ + +/* + * HMAC-MD5 + */ + +struct md5_hmac_context { + struct md5_context ictx; + struct md5_context octx; +}; + +void md5_hmac_init(struct md5_hmac_context *ctx, const byte *key, size_t keylen); +void md5_hmac_update(struct md5_hmac_context *ctx, const byte *buf, size_t buflen); +byte *md5_hmac_final(struct md5_hmac_context *ctx); + + +#endif /* _BIRD_MD5_H_ */ diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c index 5af9e875..faa33664 100644 --- a/proto/ospf/packet.c +++ b/proto/ospf/packet.c @@ -11,6 +11,7 @@ #include "ospf.h" #include "nest/password.h" #include "lib/md5.h" +#include "lib/socket.h" void ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type) @@ -108,11 +109,11 @@ ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt) char password[OSPF_AUTH_CRYPT_SIZE]; strncpy(password, passwd->password, sizeof(password)); - struct MD5Context ctxt; - MD5Init(&ctxt); - MD5Update(&ctxt, (char *) pkt, plen); - MD5Update(&ctxt, password, OSPF_AUTH_CRYPT_SIZE); - MD5Final(tail, &ctxt); + struct md5_context ctx; + md5_init(&ctx); + md5_update(&ctx, (char *) pkt, plen); + md5_update(&ctx, password, OSPF_AUTH_CRYPT_SIZE); + memcpy((byte *) tail, md5_final(&ctx), MD5_SIZE); break; default: @@ -174,19 +175,17 @@ ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_ if (!pass) DROP("no suitable password found", auth->md5.keyid); - void *tail = ((void *) pkt) + plen; - char passwd[OSPF_AUTH_CRYPT_SIZE]; - char md5sum[OSPF_AUTH_CRYPT_SIZE]; + byte *tail = ((byte *) pkt) + plen; + char received[OSPF_AUTH_CRYPT_SIZE]; + memcpy(received, tail, OSPF_AUTH_CRYPT_SIZE); + strncpy(tail, pass->password, OSPF_AUTH_CRYPT_SIZE); - strncpy(passwd, pass->password, OSPF_AUTH_CRYPT_SIZE); + struct md5_context ctx; + md5_init(&ctx); + md5_update(&ctx, (byte *) pkt, plen + OSPF_AUTH_CRYPT_SIZE); + char *computed = md5_final(&ctx); - struct MD5Context ctxt; - MD5Init(&ctxt); - MD5Update(&ctxt, (char *) pkt, plen); - MD5Update(&ctxt, passwd, OSPF_AUTH_CRYPT_SIZE); - MD5Final(md5sum, &ctxt); - - if (memcmp(md5sum, tail, OSPF_AUTH_CRYPT_SIZE)) + if (memcmp(received, computed, OSPF_AUTH_CRYPT_SIZE)) DROP("wrong MD5 digest", pass->id); if (n) diff --git a/proto/rip/packets.c b/proto/rip/packets.c index be20734f..9f10fd67 100644 --- a/proto/rip/packets.c +++ b/proto/rip/packets.c @@ -241,10 +241,10 @@ rip_fill_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_p *plen += sizeof(struct rip_auth_tail) + RIP_MD5_LENGTH; - struct MD5Context ctxt; - MD5Init(&ctxt); - MD5Update(&ctxt, (byte *) pkt, *plen); - MD5Final(tail->auth_data, &ctxt); + struct md5_context ctx; + md5_init(&ctx); + md5_update(&ctx, (byte *) pkt, *plen); + memcpy(tail->auth_data, md5_final(&ctx), RIP_MD5_LENGTH); return; default: @@ -312,15 +312,13 @@ rip_check_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_ } char received[RIP_MD5_LENGTH]; - char computed[RIP_MD5_LENGTH]; - memcpy(received, tail->auth_data, RIP_MD5_LENGTH); strncpy(tail->auth_data, pass->password, RIP_MD5_LENGTH); - struct MD5Context ctxt; - MD5Init(&ctxt); - MD5Update(&ctxt, (byte *) pkt, *plen); - MD5Final(computed, &ctxt); + struct md5_context ctx; + md5_init(&ctx); + md5_update(&ctx, (byte *) pkt, *plen); + char *computed = md5_final(&ctx); if (memcmp(received, computed, RIP_MD5_LENGTH)) DROP("wrong MD5 digest", pass->id);