diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/Makefile.am | 4 | ||||
| -rw-r--r-- | lib/command.c | 2 | ||||
| -rw-r--r-- | lib/command.h | 2 | ||||
| -rw-r--r-- | lib/frr_pthread.c | 184 | ||||
| -rw-r--r-- | lib/frr_pthread.h | 145 | ||||
| -rw-r--r-- | lib/log.c | 2 | ||||
| -rw-r--r-- | lib/route_types.txt | 2 | ||||
| -rw-r--r-- | lib/sha256.c | 433 | ||||
| -rw-r--r-- | lib/sha256.h | 58 | ||||
| -rw-r--r-- | lib/thread.c | 411 | ||||
| -rw-r--r-- | lib/thread.h | 8 | ||||
| -rw-r--r-- | lib/vty.c | 2 |
12 files changed, 1129 insertions, 124 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index 75947e6146..6e3c6d680d 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -32,8 +32,10 @@ libfrr_la_SOURCES = \ libfrr.c \ strlcpy.c \ strlcat.c \ + sha256.c \ module.c \ hook.c \ + frr_pthread.c \ # end BUILT_SOURCES = route_types.h gitversion.h command_parse.h command_lex.h @@ -74,6 +76,8 @@ pkginclude_HEADERS = \ module.h \ hook.h \ libfrr.h \ + sha256.h \ + frr_pthread.h \ # end noinst_HEADERS = \ diff --git a/lib/command.c b/lib/command.c index af13542bc3..ba1e0bcec3 100644 --- a/lib/command.c +++ b/lib/command.c @@ -1401,6 +1401,7 @@ cmd_exit (struct vty *vty) case ZEBRA_NODE: case BGP_NODE: case RIP_NODE: + case EIGRP_NODE: case RIPNG_NODE: case OSPF_NODE: case OSPF6_NODE: @@ -1484,6 +1485,7 @@ DEFUN (config_end, case ZEBRA_NODE: case RIP_NODE: case RIPNG_NODE: + case EIGRP_NODE: case BGP_NODE: case BGP_ENCAP_NODE: case BGP_ENCAPV6_NODE: diff --git a/lib/command.h b/lib/command.h index a8256c0312..8f6abc85ba 100644 --- a/lib/command.h +++ b/lib/command.h @@ -91,6 +91,7 @@ enum node_type TABLE_NODE, /* rtm_table selection node. */ RIP_NODE, /* RIP protocol mode node. */ RIPNG_NODE, /* RIPng protocol mode node. */ + EIGRP_NODE, /* EIGRP protocol mode node. */ BGP_NODE, /* BGP protocol mode which includes BGP4+ */ BGP_VPNV4_NODE, /* BGP MPLS-VPN PE exchange. */ BGP_VPNV6_NODE, /* BGP MPLS-VPN PE exchange. */ @@ -358,6 +359,7 @@ struct cmd_element #define REDIST_STR "Redistribute information from another routing protocol\n" #define CLEAR_STR "Reset functions\n" #define RIP_STR "RIP information\n" +#define EIGRP_STR "EIGRP information\n" #define BGP_STR "BGP information\n" #define BGP_SOFT_STR "Soft reconfig inbound and outbound updates\n" #define BGP_SOFT_IN_STR "Send route-refresh unless using 'soft-reconfiguration inbound'\n" diff --git a/lib/frr_pthread.c b/lib/frr_pthread.c new file mode 100644 index 0000000000..0408bca096 --- /dev/null +++ b/lib/frr_pthread.c @@ -0,0 +1,184 @@ +/* + Utilities and interfaces for managing POSIX threads + Copyright (C) 2017 Cumulus Networks + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + */ + +#include <zebra.h> +#include <pthread.h> + +#include "frr_pthread.h" +#include "memory.h" +#include "hash.h" + +DEFINE_MTYPE_STATIC(LIB, FRR_PTHREAD, "FRR POSIX Thread"); + +static unsigned int next_id = 0; + +/* Hash table of all frr_pthreads along with synchronization primitive(s) and + * hash table callbacks. + * ------------------------------------------------------------------------ */ +static struct hash *pthread_table; +static pthread_mutex_t pthread_table_mtx = PTHREAD_MUTEX_INITIALIZER; + +/* pthread_table->hash_cmp */ +static int pthread_table_hash_cmp(const void *value1, const void *value2) +{ + const struct frr_pthread *tq1 = value1; + const struct frr_pthread *tq2 = value2; + + return (tq1->id == tq2->id); +} + +/* pthread_table->hash_key */ +static unsigned int pthread_table_hash_key(void *value) +{ + return ((struct frr_pthread *)value)->id; +} +/* ------------------------------------------------------------------------ */ + +void frr_pthread_init() +{ + pthread_mutex_lock(&pthread_table_mtx); + { + pthread_table = + hash_create(pthread_table_hash_key, pthread_table_hash_cmp); + } + pthread_mutex_unlock(&pthread_table_mtx); +} + +void frr_pthread_finish() +{ + pthread_mutex_lock(&pthread_table_mtx); + { + hash_clean(pthread_table, (void (*)(void *))frr_pthread_destroy); + hash_free(pthread_table); + } + pthread_mutex_unlock(&pthread_table_mtx); +} + +struct frr_pthread *frr_pthread_new(const char *name, unsigned int id, + void *(*start_routine) (void *), + int (*stop_routine) (void **, struct frr_pthread *)) +{ + static struct frr_pthread holder = { 0 }; + struct frr_pthread *fpt = NULL; + + pthread_mutex_lock(&pthread_table_mtx); + { + holder.id = id; + + if (!hash_lookup(pthread_table, &holder)) { + struct frr_pthread *fpt = + XCALLOC(MTYPE_FRR_PTHREAD, + sizeof(struct frr_pthread)); + fpt->id = id; + fpt->master = thread_master_create(); + fpt->start_routine = start_routine; + fpt->stop_routine = stop_routine; + fpt->name = XSTRDUP(MTYPE_FRR_PTHREAD, name); + + hash_get(pthread_table, fpt, hash_alloc_intern); + } + } + pthread_mutex_unlock(&pthread_table_mtx); + + return fpt; +} + +void frr_pthread_destroy(struct frr_pthread *fpt) +{ + thread_master_free(fpt->master); + XFREE(MTYPE_FRR_PTHREAD, fpt->name); + XFREE(MTYPE_FRR_PTHREAD, fpt); +} + +struct frr_pthread *frr_pthread_get(unsigned int id) +{ + static struct frr_pthread holder = { 0 }; + struct frr_pthread *fpt; + + pthread_mutex_lock(&pthread_table_mtx); + { + holder.id = id; + fpt = hash_lookup(pthread_table, &holder); + } + pthread_mutex_unlock(&pthread_table_mtx); + + return fpt; +} + +int frr_pthread_run(unsigned int id, const pthread_attr_t * attr, void *arg) +{ + struct frr_pthread *fpt = frr_pthread_get(id); + int ret; + + if (!fpt) + return -1; + + ret = pthread_create(&fpt->thread, attr, fpt->start_routine, arg); + + /* Per pthread_create(3), the contents of fpt->thread are undefined if + * pthread_create() did not succeed. Reset this value to zero. */ + if (ret < 0) + memset(&fpt->thread, 0x00, sizeof(fpt->thread)); + + return ret; +} + +/** + * Calls the stop routine for the frr_pthread and resets any relevant fields. + * + * @param fpt - the frr_pthread to stop + * @param result - pointer to result pointer + * @return the return code from the stop routine + */ +static int frr_pthread_stop_actual(struct frr_pthread *fpt, void **result) +{ + int ret = (*fpt->stop_routine) (result, fpt); + memset(&fpt->thread, 0x00, sizeof(fpt->thread)); + return ret; +} + +int frr_pthread_stop(unsigned int id, void **result) +{ + struct frr_pthread *fpt = frr_pthread_get(id); + return frr_pthread_stop_actual(fpt, result); +} + +/** + * Callback for hash_iterate to stop all frr_pthread's. + */ +static void frr_pthread_stop_all_iter(struct hash_backet *hb, void *arg) +{ + struct frr_pthread *fpt = hb->data; + frr_pthread_stop_actual(fpt, NULL); +} + +void frr_pthread_stop_all() +{ + pthread_mutex_lock(&pthread_table_mtx); + { + hash_iterate(pthread_table, frr_pthread_stop_all_iter, NULL); + } + pthread_mutex_unlock(&pthread_table_mtx); +} + +unsigned int frr_pthread_get_id() +{ + return next_id++; +} diff --git a/lib/frr_pthread.h b/lib/frr_pthread.h new file mode 100644 index 0000000000..b4954367f4 --- /dev/null +++ b/lib/frr_pthread.h @@ -0,0 +1,145 @@ +/* + Utilities and interfaces for managing POSIX threads + Copyright (C) 2017 Cumulus Networks + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + */ + +#ifndef _FRR_PTHREAD_H +#define _FRR_PTHREAD_H + +#include <pthread.h> +#include "thread.h" + +struct frr_pthread { + + /* pthread id */ + pthread_t thread; + + /* frr thread identifier */ + unsigned int id; + + /* thread master for this pthread's thread.c event loop */ + struct thread_master *master; + + /* start routine */ + void *(*start_routine) (void *); + + /* stop routine */ + int (*stop_routine) (void **, struct frr_pthread *); + + /* the (hopefully descriptive) name of this thread */ + char *name; +}; + +/* Initializes this module. + * + * Must be called before using any of the other functions. + */ +void frr_pthread_init(void); + +/* Uninitializes this module. + * + * Destroys all registered frr_pthread's and internal data structures. + * + * It is safe to call frr_pthread_init() after this function to reinitialize + * the module. + */ +void frr_pthread_finish(void); + +/* Creates a new frr_pthread. + * + * If the provided ID is already assigned to an existing frr_pthread, the + * return value will be NULL. + * + * @param name - the name of the thread. Doesn't have to be unique, but it + * probably should be. This value is copied and may be safely free'd upon + * return. + * + * @param id - the integral ID of the thread. MUST be unique. The caller may + * use this id to retrieve the thread. + * + * @param start_routine - start routine for the pthread, will be passed to + * pthread_create (see those docs for details) + * + * @param stop_routine - stop routine for the pthread, called to terminate the + * thread. This function should gracefully stop the pthread and clean up any + * thread-specific resources. The passed pointer is used to return a data + * result. + * + * @return the created frr_pthread upon success, or NULL upon failure + */ +struct frr_pthread *frr_pthread_new(const char *name, unsigned int id, + void *(*start_routine) (void *), + int (*stop_routine) (void **, struct frr_pthread *)); + +/* Destroys an frr_pthread. + * + * Assumes that the associated pthread, if any, has already terminated. + * + * @param fpt - the frr_pthread to destroy + */ +void frr_pthread_destroy(struct frr_pthread *fpt); + +/* Gets an existing frr_pthread by its id. + * + * @return frr_thread associated with the provided id, or NULL on error + */ +struct frr_pthread *frr_pthread_get(unsigned int id); + +/* Creates a new pthread and binds it to a frr_pthread. + * + * This function is a wrapper for pthread_create. The first parameter is the + * frr_pthread to bind the created pthread to. All subsequent arguments are + * passed unmodified to pthread_create(). + * + * This function returns the same code as pthread_create(). If the value is + * zero, the provided frr_pthread is bound to a running POSIX thread. If the + * value is less than zero, the provided frr_pthread is guaranteed to be a + * clean instance that may be susbsequently passed to frr_pthread_run(). + * + * @param id - frr_pthread to bind the created pthread to + * @param attr - see pthread_create(3) + * @param arg - see pthread_create(3) + * + * @return see pthread_create(3) + */ +int frr_pthread_run(unsigned int id, const pthread_attr_t * attr, void *arg); + +/* Stops an frr_pthread with a result. + * + * @param id - frr_pthread to stop + * @param result - where to store the thread's result, if any. May be NULL if a + * result is not needed. + */ +int frr_pthread_stop(unsigned int id, void **result); + +/* Stops all frr_pthread's. */ +void frr_pthread_stop_all(void); + +/* Returns a unique identifier for use with frr_pthread_new(). + * + * Internally, this is an integer that increments after each call to this + * function. Because the number of pthreads created should never exceed INT_MAX + * during the life of the program, there is no overflow protection. If by + * chance this function returns an ID which is already in use, + * frr_pthread_new() will fail when it is provided. + * + * @return unique identifier + */ +unsigned int frr_pthread_get_id(void); + +#endif /* _FRR_PTHREAD_H */ @@ -1058,6 +1058,8 @@ proto_redistnum(int afi, const char *s) return ZEBRA_ROUTE_STATIC; else if (strmatch (s, "rip")) return ZEBRA_ROUTE_RIP; + else if (strmatch (s, "eigrp")) + return ZEBRA_ROUTE_EIGRP; else if (strmatch (s, "ospf")) return ZEBRA_ROUTE_OSPF; else if (strmatch (s, "isis")) diff --git a/lib/route_types.txt b/lib/route_types.txt index 5cb06ffb7f..7625d1f690 100644 --- a/lib/route_types.txt +++ b/lib/route_types.txt @@ -52,6 +52,7 @@ ZEBRA_ROUTE_OSPF6, ospf6, ospf6d, 'O', 0, 1, "OSPFv3" ZEBRA_ROUTE_ISIS, isis, isisd, 'I', 1, 1, "IS-IS" ZEBRA_ROUTE_BGP, bgp, bgpd, 'B', 1, 1, "BGP" ZEBRA_ROUTE_PIM, pim, pimd, 'P', 1, 0, "PIM" +ZEBRA_ROUTE_EIGRP, eigrp, eigrpd, 'E', 1, 0, "EIGRP" ZEBRA_ROUTE_NHRP, nhrp, nhrpd, 'N', 1, 1, "NHRP" # HSLS and OLSR both are AFI independent (so: 1, 1), however # we want to disable for them for general Quagga distribution. @@ -86,6 +87,7 @@ ZEBRA_ROUTE_OSPF6, "Open Shortest Path First (IPv6) (OSPFv3)" ZEBRA_ROUTE_ISIS, "Intermediate System to Intermediate System (IS-IS)" ZEBRA_ROUTE_BGP, "Border Gateway Protocol (BGP)" ZEBRA_ROUTE_PIM, "Protocol Independent Multicast (PIM)" +ZEBRA_ROUTE_EIGRP, "Enhanced Interior Gateway Routing Protocol (EIGRP)" ZEBRA_ROUTE_NHRP, "Next Hop Resolution Protocol (NHRP)" ZEBRA_ROUTE_HSLS, "Hazy-Sighted Link State Protocol (HSLS)" ZEBRA_ROUTE_VNC, "Virtual Network Control (VNC)" diff --git a/lib/sha256.c b/lib/sha256.c new file mode 100644 index 0000000000..f98a758821 --- /dev/null +++ b/lib/sha256.c @@ -0,0 +1,433 @@ +/*- + * Copyright 2005,2007,2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <zebra.h> +#include "sha256.h" + +#if !HAVE_DECL_BE32DEC +static inline uint32_t +be32dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + + return ((uint32_t)(p[3]) + ((uint32_t)(p[2]) << 8) + + ((uint32_t)(p[1]) << 16) + ((uint32_t)(p[0]) << 24)); +} +#else +#include <sys/endian.h> +#endif + +#if !HAVE_DECL_BE32ENC +static inline void +be32enc(void *pp, uint32_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[3] = x & 0xff; + p[2] = (x >> 8) & 0xff; + p[1] = (x >> 16) & 0xff; + p[0] = (x >> 24) & 0xff; +} +#else +#include <sys/endian.h> +#endif + +/* + * Encode a length len/4 vector of (uint32_t) into a length len vector of + * (unsigned char) in big-endian form. Assumes len is a multiple of 4. + */ +static void +be32enc_vect(unsigned char *dst, const uint32_t *src, size_t len) +{ + size_t i; + + for (i = 0; i < len / 4; i++) + be32enc(dst + i * 4, src[i]); +} + +/* + * Decode a big-endian length len vector of (unsigned char) into a length + * len/4 vector of (uint32_t). Assumes len is a multiple of 4. + */ +static void +be32dec_vect(uint32_t *dst, const unsigned char *src, size_t len) +{ + size_t i; + + for (i = 0; i < len / 4; i++) + dst[i] = be32dec(src + i * 4); +} + +/* Elementary functions used by SHA256 */ +#define Ch(x, y, z) ((x & (y ^ z)) ^ z) +#define Maj(x, y, z) ((x & (y | z)) | (y & z)) +#define SHR(x, n) (x >> n) +#define ROTR(x, n) ((x >> n) | (x << (32 - n))) +#define S0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) +#define S1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) +#define s0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3)) +#define s1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10)) + +/* SHA256 round function */ +#define RND(a, b, c, d, e, f, g, h, k) \ + t0 = h + S1(e) + Ch(e, f, g) + k; \ + t1 = S0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + +/* Adjusted round function for rotating state */ +#define RNDr(S, W, i, k) \ + RND(S[(64 - i) % 8], S[(65 - i) % 8], \ + S[(66 - i) % 8], S[(67 - i) % 8], \ + S[(68 - i) % 8], S[(69 - i) % 8], \ + S[(70 - i) % 8], S[(71 - i) % 8], \ + W[i] + k) + +/* + * SHA256 block compression function. The 256-bit state is transformed via + * the 512-bit input block to produce a new state. + */ +static void +SHA256_Transform(uint32_t * state, const unsigned char block[64]) +{ + uint32_t W[64]; + uint32_t S[8]; + uint32_t t0, t1; + int i; + + /* 1. Prepare message schedule W. */ + be32dec_vect(W, block, 64); + for (i = 16; i < 64; i++) + W[i] = s1(W[i - 2]) + W[i - 7] + s0(W[i - 15]) + W[i - 16]; + + /* 2. Initialize working variables. */ + memcpy(S, state, 32); + + /* 3. Mix. */ + RNDr(S, W, 0, 0x428a2f98); + RNDr(S, W, 1, 0x71374491); + RNDr(S, W, 2, 0xb5c0fbcf); + RNDr(S, W, 3, 0xe9b5dba5); + RNDr(S, W, 4, 0x3956c25b); + RNDr(S, W, 5, 0x59f111f1); + RNDr(S, W, 6, 0x923f82a4); + RNDr(S, W, 7, 0xab1c5ed5); + RNDr(S, W, 8, 0xd807aa98); + RNDr(S, W, 9, 0x12835b01); + RNDr(S, W, 10, 0x243185be); + RNDr(S, W, 11, 0x550c7dc3); + RNDr(S, W, 12, 0x72be5d74); + RNDr(S, W, 13, 0x80deb1fe); + RNDr(S, W, 14, 0x9bdc06a7); + RNDr(S, W, 15, 0xc19bf174); + RNDr(S, W, 16, 0xe49b69c1); + RNDr(S, W, 17, 0xefbe4786); + RNDr(S, W, 18, 0x0fc19dc6); + RNDr(S, W, 19, 0x240ca1cc); + RNDr(S, W, 20, 0x2de92c6f); + RNDr(S, W, 21, 0x4a7484aa); + RNDr(S, W, 22, 0x5cb0a9dc); + RNDr(S, W, 23, 0x76f988da); + RNDr(S, W, 24, 0x983e5152); + RNDr(S, W, 25, 0xa831c66d); + RNDr(S, W, 26, 0xb00327c8); + RNDr(S, W, 27, 0xbf597fc7); + RNDr(S, W, 28, 0xc6e00bf3); + RNDr(S, W, 29, 0xd5a79147); + RNDr(S, W, 30, 0x06ca6351); + RNDr(S, W, 31, 0x14292967); + RNDr(S, W, 32, 0x27b70a85); + RNDr(S, W, 33, 0x2e1b2138); + RNDr(S, W, 34, 0x4d2c6dfc); + RNDr(S, W, 35, 0x53380d13); + RNDr(S, W, 36, 0x650a7354); + RNDr(S, W, 37, 0x766a0abb); + RNDr(S, W, 38, 0x81c2c92e); + RNDr(S, W, 39, 0x92722c85); + RNDr(S, W, 40, 0xa2bfe8a1); + RNDr(S, W, 41, 0xa81a664b); + RNDr(S, W, 42, 0xc24b8b70); + RNDr(S, W, 43, 0xc76c51a3); + RNDr(S, W, 44, 0xd192e819); + RNDr(S, W, 45, 0xd6990624); + RNDr(S, W, 46, 0xf40e3585); + RNDr(S, W, 47, 0x106aa070); + RNDr(S, W, 48, 0x19a4c116); + RNDr(S, W, 49, 0x1e376c08); + RNDr(S, W, 50, 0x2748774c); + RNDr(S, W, 51, 0x34b0bcb5); + RNDr(S, W, 52, 0x391c0cb3); + RNDr(S, W, 53, 0x4ed8aa4a); + RNDr(S, W, 54, 0x5b9cca4f); + RNDr(S, W, 55, 0x682e6ff3); + RNDr(S, W, 56, 0x748f82ee); + RNDr(S, W, 57, 0x78a5636f); + RNDr(S, W, 58, 0x84c87814); + RNDr(S, W, 59, 0x8cc70208); + RNDr(S, W, 60, 0x90befffa); + RNDr(S, W, 61, 0xa4506ceb); + RNDr(S, W, 62, 0xbef9a3f7); + RNDr(S, W, 63, 0xc67178f2); + + /* 4. Mix local working variables into global state */ + for (i = 0; i < 8; i++) + state[i] += S[i]; + + /* Clean the stack. */ + memset(W, 0, 256); + memset(S, 0, 32); + t0 = t1 = 0; +} + +static unsigned char PAD[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* Add padding and terminating bit-count. */ +static void +SHA256_Pad(SHA256_CTX * ctx) +{ + unsigned char len[8]; + uint32_t r, plen; + + /* + * Convert length to a vector of bytes -- we do this now rather + * than later because the length will change after we pad. + */ + be32enc_vect(len, ctx->count, 8); + + /* Add 1--64 bytes so that the resulting length is 56 mod 64 */ + r = (ctx->count[1] >> 3) & 0x3f; + plen = (r < 56) ? (56 - r) : (120 - r); + SHA256_Update(ctx, PAD, (size_t)plen); + + /* Add the terminating bit-count */ + SHA256_Update(ctx, len, 8); +} + +/* SHA-256 initialization. Begins a SHA-256 operation. */ +void +SHA256_Init(SHA256_CTX * ctx) +{ + + /* Zero bits processed so far */ + ctx->count[0] = ctx->count[1] = 0; + + /* Magic initialization constants */ + ctx->state[0] = 0x6A09E667; + ctx->state[1] = 0xBB67AE85; + ctx->state[2] = 0x3C6EF372; + ctx->state[3] = 0xA54FF53A; + ctx->state[4] = 0x510E527F; + ctx->state[5] = 0x9B05688C; + ctx->state[6] = 0x1F83D9AB; + ctx->state[7] = 0x5BE0CD19; +} + +/* Add bytes into the hash */ +void +SHA256_Update(SHA256_CTX * ctx, const void *in, size_t len) +{ + uint32_t bitlen[2]; + uint32_t r; + const unsigned char *src = in; + + /* Number of bytes left in the buffer from previous updates */ + r = (ctx->count[1] >> 3) & 0x3f; + + /* Convert the length into a number of bits */ + bitlen[1] = ((uint32_t)len) << 3; + bitlen[0] = (uint32_t)(len >> 29); + + /* Update number of bits */ + if ((ctx->count[1] += bitlen[1]) < bitlen[1]) + ctx->count[0]++; + ctx->count[0] += bitlen[0]; + + /* Handle the case where we don't need to perform any transforms */ + if (len < 64 - r) { + memcpy(&ctx->buf[r], src, len); + return; + } + + /* Finish the current block */ + memcpy(&ctx->buf[r], src, 64 - r); + SHA256_Transform(ctx->state, ctx->buf); + src += 64 - r; + len -= 64 - r; + + /* Perform complete blocks */ + while (len >= 64) { + SHA256_Transform(ctx->state, src); + src += 64; + len -= 64; + } + + /* Copy left over data into buffer */ + memcpy(ctx->buf, src, len); +} + +/* + * SHA-256 finalization. Pads the input data, exports the hash value, + * and clears the context state. + */ +void +SHA256_Final(unsigned char digest[32], SHA256_CTX * ctx) +{ + + /* Add padding */ + SHA256_Pad(ctx); + + /* Write the hash */ + be32enc_vect(digest, ctx->state, 32); + + /* Clear the context state */ + memset((void *)ctx, 0, sizeof(*ctx)); +} + +/* Initialize an HMAC-SHA256 operation with the given key. */ +void +HMAC__SHA256_Init(HMAC_SHA256_CTX * ctx, const void * _K, size_t Klen) +{ + unsigned char pad[64]; + unsigned char khash[32]; + const unsigned char * K = _K; + size_t i; + + /* If Klen > 64, the key is really SHA256(K). */ + if (Klen > 64) { + SHA256_Init(&ctx->ictx); + SHA256_Update(&ctx->ictx, K, Klen); + SHA256_Final(khash, &ctx->ictx); + K = khash; + Klen = 32; + } + + /* Inner SHA256 operation is SHA256(K xor [block of 0x36] || data). */ + SHA256_Init(&ctx->ictx); + memset(pad, 0x36, 64); + for (i = 0; i < Klen; i++) + pad[i] ^= K[i]; + SHA256_Update(&ctx->ictx, pad, 64); + + /* Outer SHA256 operation is SHA256(K xor [block of 0x5c] || hash). */ + SHA256_Init(&ctx->octx); + memset(pad, 0x5c, 64); + for (i = 0; i < Klen; i++) + pad[i] ^= K[i]; + SHA256_Update(&ctx->octx, pad, 64); + + /* Clean the stack. */ + memset(khash, 0, 32); +} + +/* Add bytes to the HMAC-SHA256 operation. */ +void +HMAC__SHA256_Update(HMAC_SHA256_CTX * ctx, const void *in, size_t len) +{ + + /* Feed data to the inner SHA256 operation. */ + SHA256_Update(&ctx->ictx, in, len); +} + +/* Finish an HMAC-SHA256 operation. */ +void +HMAC__SHA256_Final(unsigned char digest[32], HMAC_SHA256_CTX * ctx) +{ + unsigned char ihash[32]; + + /* Finish the inner SHA256 operation. */ + SHA256_Final(ihash, &ctx->ictx); + + /* Feed the inner hash to the outer SHA256 operation. */ + SHA256_Update(&ctx->octx, ihash, 32); + + /* Finish the outer SHA256 operation. */ + SHA256_Final(digest, &ctx->octx); + + /* Clean the stack. */ + memset(ihash, 0, 32); +} + +/** + * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen): + * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and + * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). + */ +void +PBKDF2_SHA256(const uint8_t * passwd, size_t passwdlen, const uint8_t * salt, + size_t saltlen, uint64_t c, uint8_t * buf, size_t dkLen) +{ + HMAC_SHA256_CTX PShctx, hctx; + size_t i; + uint8_t ivec[4]; + uint8_t U[32]; + uint8_t T[32]; + uint64_t j; + int k; + size_t clen; + + /* Compute HMAC state after processing P and S. */ + HMAC__SHA256_Init(&PShctx, passwd, passwdlen); + HMAC__SHA256_Update(&PShctx, salt, saltlen); + + /* Iterate through the blocks. */ + for (i = 0; i * 32 < dkLen; i++) { + /* Generate INT(i + 1). */ + be32enc(ivec, (uint32_t)(i + 1)); + + /* Compute U_1 = PRF(P, S || INT(i)). */ + memcpy(&hctx, &PShctx, sizeof(HMAC_SHA256_CTX)); + HMAC__SHA256_Update(&hctx, ivec, 4); + HMAC__SHA256_Final(U, &hctx); + + /* T_i = U_1 ... */ + memcpy(T, U, 32); + + for (j = 2; j <= c; j++) { + /* Compute U_j. */ + HMAC__SHA256_Init(&hctx, passwd, passwdlen); + HMAC__SHA256_Update(&hctx, U, 32); + HMAC__SHA256_Final(U, &hctx); + + /* ... xor U_j ... */ + for (k = 0; k < 32; k++) + T[k] ^= U[k]; + } + + /* Copy as many bytes as necessary into buf. */ + clen = dkLen - i * 32; + if (clen > 32) + clen = 32; + memcpy(&buf[i * 32], T, clen); + } + + /* Clean PShctx, since we never called _Final on it. */ + memset(&PShctx, 0, sizeof(HMAC_SHA256_CTX)); +} diff --git a/lib/sha256.h b/lib/sha256.h new file mode 100644 index 0000000000..502f3fc224 --- /dev/null +++ b/lib/sha256.h @@ -0,0 +1,58 @@ +/*- + * Copyright 2005,2007,2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/libmd/sha256.h,v 1.2 2006/01/17 15:35:56 phk Exp $ + */ + +#ifndef _SHA256_H_ +#define _SHA256_H_ + +typedef struct SHA256Context { + uint32_t state[8]; + uint32_t count[2]; + unsigned char buf[64]; +} SHA256_CTX; + +typedef struct HMAC_SHA256Context { + SHA256_CTX ictx; + SHA256_CTX octx; +} HMAC_SHA256_CTX; + +void SHA256_Init(SHA256_CTX *); +void SHA256_Update(SHA256_CTX *, const void *, size_t); +void SHA256_Final(unsigned char [32], SHA256_CTX *); +void HMAC__SHA256_Init(HMAC_SHA256_CTX *, const void *, size_t); +void HMAC__SHA256_Update(HMAC_SHA256_CTX *, const void *, size_t); +void HMAC__SHA256_Final(unsigned char [32], HMAC_SHA256_CTX *); + +/** + * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen): + * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and + * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). + */ +void PBKDF2_SHA256(const uint8_t *, size_t, const uint8_t *, size_t, + uint64_t, uint8_t *, size_t); + +#endif /* !_SHA256_H_ */ diff --git a/lib/thread.c b/lib/thread.c index e707fc584c..3fb28bce26 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -41,7 +41,7 @@ DEFINE_MTYPE_STATIC(LIB, THREAD_STATS, "Thread stats") #include <mach/mach_time.h> #endif -/* Relative time, since startup */ +static pthread_mutex_t cpu_record_mtx = PTHREAD_MUTEX_INITIALIZER; static struct hash *cpu_record = NULL; static unsigned long @@ -137,9 +137,14 @@ cpu_record_print(struct vty *vty, thread_type filter) vty_out(vty, "Active Runtime(ms) Invoked Avg uSec Max uSecs"); vty_out(vty, " Avg uSec Max uSecs"); vty_out(vty, " Type Thread%s", VTY_NEWLINE); - hash_iterate(cpu_record, - (void(*)(struct hash_backet*,void*))cpu_record_hash_print, - args); + + pthread_mutex_lock (&cpu_record_mtx); + { + hash_iterate(cpu_record, + (void(*)(struct hash_backet*,void*))cpu_record_hash_print, + args); + } + pthread_mutex_unlock (&cpu_record_mtx); if (tmp.total_calls > 0) vty_out_cpu_thread_history(vty, &tmp); @@ -216,16 +221,25 @@ cpu_record_hash_clear (struct hash_backet *bucket, if ( !(a->types & *filter) ) return; - hash_release (cpu_record, bucket->data); + pthread_mutex_lock (&cpu_record_mtx); + { + hash_release (cpu_record, bucket->data); + } + pthread_mutex_unlock (&cpu_record_mtx); } static void cpu_record_clear (thread_type filter) { thread_type *tmp = &filter; - hash_iterate (cpu_record, - (void (*) (struct hash_backet*,void*)) cpu_record_hash_clear, - tmp); + + pthread_mutex_lock (&cpu_record_mtx); + { + hash_iterate (cpu_record, + (void (*) (struct hash_backet*,void*)) cpu_record_hash_clear, + tmp); + } + pthread_mutex_unlock (&cpu_record_mtx); } DEFUN (clear_thread_cpu, @@ -326,16 +340,20 @@ thread_master_create (void) getrlimit(RLIMIT_NOFILE, &limit); - if (cpu_record == NULL) - cpu_record - = hash_create ((unsigned int (*) (void *))cpu_record_hash_key, - (int (*) (const void *, const void *))cpu_record_hash_cmp); + pthread_mutex_lock (&cpu_record_mtx); + { + if (cpu_record == NULL) + cpu_record = hash_create ((unsigned int (*) (void *))cpu_record_hash_key, + (int (*) (const void *, const void *)) + cpu_record_hash_cmp); + } + pthread_mutex_unlock (&cpu_record_mtx); rv = XCALLOC (MTYPE_THREAD_MASTER, sizeof (struct thread_master)); if (rv == NULL) - { - return NULL; - } + return NULL; + + pthread_mutex_init (&rv->mtx, NULL); rv->fd_limit = (int)limit.rlim_cur; rv->read = XCALLOC (MTYPE_THREAD, sizeof (struct thread *) * rv->fd_limit); @@ -358,8 +376,10 @@ thread_master_create (void) rv->background = pqueue_create(); rv->timer->cmp = rv->background->cmp = thread_timer_cmp; rv->timer->update = rv->background->update = thread_timer_update; + rv->spin = true; + rv->handle_signals = true; -#if defined(HAVE_POLL) +#if defined(HAVE_POLL_CALL) rv->handler.pfdsize = rv->fd_limit; rv->handler.pfdcount = 0; rv->handler.pfds = XCALLOC (MTYPE_THREAD_MASTER, @@ -498,11 +518,16 @@ thread_queue_free (struct thread_master *m, struct pqueue *queue) void thread_master_free_unused (struct thread_master *m) { - struct thread *t; - while ((t = thread_trim_head(&m->unuse)) != NULL) - { - XFREE(MTYPE_THREAD, t); - } + pthread_mutex_lock (&m->mtx); + { + struct thread *t; + while ((t = thread_trim_head(&m->unuse)) != NULL) + { + pthread_mutex_destroy (&t->mtx); + XFREE(MTYPE_THREAD, t); + } + } + pthread_mutex_unlock (&m->mtx); } /* Stop thread scheduler. */ @@ -516,25 +541,37 @@ thread_master_free (struct thread_master *m) thread_list_free (m, &m->ready); thread_list_free (m, &m->unuse); thread_queue_free (m, m->background); + pthread_mutex_destroy (&m->mtx); -#if defined(HAVE_POLL) +#if defined(HAVE_POLL_CALL) XFREE (MTYPE_THREAD_MASTER, m->handler.pfds); #endif XFREE (MTYPE_THREAD_MASTER, m); - if (cpu_record) - { - hash_clean (cpu_record, cpu_record_hash_free); - hash_free (cpu_record); - cpu_record = NULL; - } + pthread_mutex_lock (&cpu_record_mtx); + { + if (cpu_record) + { + hash_clean (cpu_record, cpu_record_hash_free); + hash_free (cpu_record); + cpu_record = NULL; + } + } + pthread_mutex_unlock (&cpu_record_mtx); } /* Return remain time in second. */ unsigned long thread_timer_remain_second (struct thread *thread) { - int64_t remain = monotime_until(&thread->u.sands, NULL) / 1000000LL; + int64_t remain; + + pthread_mutex_lock (&thread->mtx); + { + remain = monotime_until(&thread->u.sands, NULL) / 1000000LL; + } + pthread_mutex_unlock (&thread->mtx); + return remain < 0 ? 0 : remain; } @@ -545,7 +582,11 @@ struct timeval thread_timer_remain(struct thread *thread) { struct timeval remain; - monotime_until(&thread->u.sands, &remain); + pthread_mutex_lock (&thread->mtx); + { + monotime_until(&thread->u.sands, &remain); + } + pthread_mutex_unlock (&thread->mtx); return remain; } @@ -560,8 +601,11 @@ thread_get (struct thread_master *m, u_char type, if (! thread) { thread = XCALLOC (MTYPE_THREAD, sizeof (struct thread)); + /* mutex only needs to be initialized at struct creation. */ + pthread_mutex_init (&thread->mtx, NULL); m->alloc++; } + thread->type = type; thread->add_type = type; thread->master = m; @@ -584,8 +628,12 @@ thread_get (struct thread_master *m, u_char type, { tmp.func = func; tmp.funcname = funcname; - thread->hist = hash_get (cpu_record, &tmp, - (void * (*) (void *))cpu_record_hash_alloc); + pthread_mutex_lock (&cpu_record_mtx); + { + thread->hist = hash_get (cpu_record, &tmp, + (void * (*) (void *))cpu_record_hash_alloc); + } + pthread_mutex_unlock (&cpu_record_mtx); } thread->hist->total_active++; thread->func = func; @@ -596,7 +644,7 @@ thread_get (struct thread_master *m, u_char type, return thread; } -#if defined (HAVE_POLL) +#if defined (HAVE_POLL_CALL) #define fd_copy_fd_set(X) (X) @@ -650,15 +698,45 @@ static int fd_select (struct thread_master *m, int size, thread_fd_set *read, thread_fd_set *write, thread_fd_set *except, struct timeval *timer_wait) { int num; -#if defined(HAVE_POLL) - /* recalc timeout for poll. Attention NULL pointer is no timeout with - select, where with poll no timeount is -1 */ + + /* If timer_wait is null here, that means either select() or poll() should + * block indefinitely, unless the thread_master has overriden it. select() + * and poll() differ in the timeout values they interpret as an indefinite + * block; select() requires a null pointer, while poll takes a millisecond + * value of -1. + * + * The thread_master owner has the option of overriding the default behavior + * by setting ->selectpoll_timeout. If the value is positive, it specifies + * the maximum number of milliseconds to wait. If the timeout is -1, it + * specifies that we should never wait and always return immediately even if + * no event is detected. If the value is zero, the behavior is default. + */ + +#if defined(HAVE_POLL_CALL) int timeout = -1; - if (timer_wait != NULL) + + if (timer_wait != NULL && m->selectpoll_timeout == 0) // use the default value timeout = (timer_wait->tv_sec*1000) + (timer_wait->tv_usec/1000); + else if (m->selectpoll_timeout > 0) // use the user's timeout + timeout = m->selectpoll_timeout; + else if (m->selectpoll_timeout < 0) // effect a poll (return immediately) + timeout = 0; num = poll (m->handler.pfds, m->handler.pfdcount + m->handler.pfdcountsnmp, timeout); #else + struct timeval timeout; + if (m->selectpoll_timeout > 0) // use the user's timeout + { + timeout.tv_sec = m->selectpoll_timeout / 1000; + timeout.tv_usec = (m->selectpoll_timeout % 1000) * 1000; + timer_wait = &timeout; + } + else if (m->selectpoll_timeout < 0) // effect a poll (return immediately) + { + timeout.tv_sec = 0; + timeout.tv_usec = 0; + timer_wait = &timeout; + } num = select (size, read, write, except, timer_wait); #endif @@ -668,7 +746,7 @@ fd_select (struct thread_master *m, int size, thread_fd_set *read, thread_fd_set static int fd_is_set (struct thread *thread, thread_fd_set *fdset, int pos) { -#if defined(HAVE_POLL) +#if defined(HAVE_POLL_CALL) return 1; #else return FD_ISSET (THREAD_FD (thread), fdset); @@ -678,7 +756,7 @@ fd_is_set (struct thread *thread, thread_fd_set *fdset, int pos) static int fd_clear_read_write (struct thread *thread) { -#if !defined(HAVE_POLL) +#if !defined(HAVE_POLL_CALL) thread_fd_set *fdset = NULL; int fd = THREAD_FD (thread); @@ -703,36 +781,49 @@ funcname_thread_add_read_write (int dir, struct thread_master *m, { struct thread *thread = NULL; -#if !defined(HAVE_POLL) - thread_fd_set *fdset = NULL; - if (dir == THREAD_READ) - fdset = &m->handler.readfd; - else - fdset = &m->handler.writefd; -#endif - -#if defined (HAVE_POLL) - thread = generic_thread_add(m, func, arg, fd, dir, debugargpass); - - if (thread == NULL) - return NULL; + pthread_mutex_lock (&m->mtx); + { +#if defined (HAVE_POLL_CALL) + thread = generic_thread_add(m, func, arg, fd, dir, debugargpass); #else - if (FD_ISSET (fd, fdset)) - { - zlog_warn ("There is already %s fd [%d]", - (dir == THREAD_READ) ? "read" : "write", fd); - return NULL; - } + if (fd >= FD_SETSIZE) + { + zlog_err ("File descriptor %d is >= FD_SETSIZE (%d). Please recompile" + "with --enable-poll=yes", fd, FD_SETSIZE); + assert (fd < FD_SETSIZE && !"fd >= FD_SETSIZE"); + } + thread_fd_set *fdset = NULL; + if (dir == THREAD_READ) + fdset = &m->handler.readfd; + else + fdset = &m->handler.writefd; - FD_SET (fd, fdset); - thread = thread_get (m, dir, func, arg, debugargpass); + if (FD_ISSET (fd, fdset)) + { + zlog_warn ("There is already %s fd [%d]", + (dir == THREAD_READ) ? "read" : "write", fd); + } + else + { + FD_SET (fd, fdset); + thread = thread_get (m, dir, func, arg, debugargpass); + } #endif - thread->u.fd = fd; - if (dir == THREAD_READ) - thread_add_fd (m->read, thread); - else - thread_add_fd (m->write, thread); + if (thread) + { + pthread_mutex_lock (&thread->mtx); + { + thread->u.fd = fd; + if (dir == THREAD_READ) + thread_add_fd (m->read, thread); + else + thread_add_fd (m->write, thread); + } + pthread_mutex_unlock (&thread->mtx); + } + } + pthread_mutex_unlock (&m->mtx); return thread; } @@ -753,13 +844,21 @@ funcname_thread_add_timer_timeval (struct thread_master *m, assert (type == THREAD_TIMER || type == THREAD_BACKGROUND); assert (time_relative); - queue = ((type == THREAD_TIMER) ? m->timer : m->background); - thread = thread_get (m, type, func, arg, debugargpass); + pthread_mutex_lock (&m->mtx); + { + queue = ((type == THREAD_TIMER) ? m->timer : m->background); + thread = thread_get (m, type, func, arg, debugargpass); - monotime(&thread->u.sands); - timeradd(&thread->u.sands, time_relative, &thread->u.sands); + pthread_mutex_lock (&thread->mtx); + { + monotime(&thread->u.sands); + timeradd(&thread->u.sands, time_relative, &thread->u.sands); + pqueue_enqueue(thread, queue); + } + pthread_mutex_unlock (&thread->mtx); + } + pthread_mutex_unlock (&m->mtx); - pqueue_enqueue(thread, queue); return thread; } @@ -847,9 +946,17 @@ funcname_thread_add_event (struct thread_master *m, assert (m != NULL); - thread = thread_get (m, THREAD_EVENT, func, arg, debugargpass); - thread->u.val = val; - thread_list_add (&m->event, thread); + pthread_mutex_lock (&m->mtx); + { + thread = thread_get (m, THREAD_EVENT, func, arg, debugargpass); + pthread_mutex_lock (&thread->mtx); + { + thread->u.val = val; + thread_list_add (&m->event, thread); + } + pthread_mutex_unlock (&thread->mtx); + } + pthread_mutex_unlock (&m->mtx); return thread; } @@ -857,7 +964,7 @@ funcname_thread_add_event (struct thread_master *m, static void thread_cancel_read_or_write (struct thread *thread, short int state) { -#if defined(HAVE_POLL) +#if defined(HAVE_POLL_CALL) nfds_t i; for (i=0;i<thread->master->handler.pfdcount;++i) @@ -880,18 +987,26 @@ thread_cancel_read_or_write (struct thread *thread, short int state) fd_clear_read_write (thread); } -/* Cancel thread from scheduler. */ +/** + * Cancel thread from scheduler. + * + * This function is *NOT* MT-safe. DO NOT call it from any other pthread except + * the one which owns thread->master. + */ void thread_cancel (struct thread *thread) { struct thread_list *list = NULL; struct pqueue *queue = NULL; struct thread **thread_array = NULL; - + + pthread_mutex_lock (&thread->master->mtx); + pthread_mutex_lock (&thread->mtx); + switch (thread->type) { case THREAD_READ: -#if defined (HAVE_POLL) +#if defined (HAVE_POLL_CALL) thread_cancel_read_or_write (thread, POLLIN | POLLHUP); #else thread_cancel_read_or_write (thread, 0); @@ -899,7 +1014,7 @@ thread_cancel (struct thread *thread) thread_array = thread->master->read; break; case THREAD_WRITE: -#if defined (HAVE_POLL) +#if defined (HAVE_POLL_CALL) thread_cancel_read_or_write (thread, POLLOUT | POLLHUP); #else thread_cancel_read_or_write (thread, 0); @@ -919,15 +1034,14 @@ thread_cancel (struct thread *thread) queue = thread->master->background; break; default: - return; + goto done; break; } if (queue) { assert(thread->index >= 0); - assert(thread == queue->array[thread->index]); - pqueue_remove_at(thread->index, queue); + pqueue_remove (thread, queue); } else if (list) { @@ -943,6 +1057,10 @@ thread_cancel (struct thread *thread) } thread_add_unuse (thread->master, thread); + +done: + pthread_mutex_unlock (&thread->mtx); + pthread_mutex_unlock (&thread->master->mtx); } /* Delete all events which has argument value arg. */ @@ -951,39 +1069,48 @@ thread_cancel_event (struct thread_master *m, void *arg) { unsigned int ret = 0; struct thread *thread; + struct thread *t; - thread = m->event.head; - while (thread) - { - struct thread *t; - - t = thread; - thread = t->next; - - if (t->arg == arg) + pthread_mutex_lock (&m->mtx); + { + thread = m->event.head; + while (thread) + { + t = thread; + pthread_mutex_lock (&t->mtx); { - ret++; - thread_list_delete (&m->event, t); - thread_add_unuse (m, t); + thread = t->next; + + if (t->arg == arg) + { + ret++; + thread_list_delete (&m->event, t); + thread_add_unuse (m, t); + } } - } - - /* thread can be on the ready list too */ - thread = m->ready.head; - while (thread) - { - struct thread *t; - - t = thread; - thread = t->next; + pthread_mutex_unlock (&t->mtx); + } - if (t->arg == arg) + /* thread can be on the ready list too */ + thread = m->ready.head; + while (thread) + { + t = thread; + pthread_mutex_lock (&t->mtx); { - ret++; - thread_list_delete (&m->ready, t); - thread_add_unuse (m, t); + thread = t->next; + + if (t->arg == arg) + { + ret++; + thread_list_delete (&m->ready, t); + thread_add_unuse (m, t); + } } - } + pthread_mutex_unlock (&t->mtx); + } + } + pthread_mutex_unlock (&m->mtx); return ret; } @@ -1027,7 +1154,7 @@ thread_process_fds_helper (struct thread_master *m, struct thread *thread, threa thread_delete_fd (thread_array, thread); thread_list_add (&m->ready, thread); thread->type = THREAD_READY; -#if defined(HAVE_POLL) +#if defined(HAVE_POLL_CALL) thread->master->handler.pfds[pos].events &= ~(state); #endif return 1; @@ -1035,7 +1162,7 @@ thread_process_fds_helper (struct thread_master *m, struct thread *thread, threa return 0; } -#if defined(HAVE_POLL) +#if defined(HAVE_POLL_CALL) /* check poll events */ static void @@ -1076,7 +1203,7 @@ check_pollfds(struct thread_master *m, fd_set *readfd, int num) static void thread_process_fds (struct thread_master *m, thread_fd_set *rset, thread_fd_set *wset, int num) { -#if defined (HAVE_POLL) +#if defined (HAVE_POLL_CALL) check_pollfds (m, rset, num); #else int ready = 0, index; @@ -1143,18 +1270,24 @@ thread_fetch (struct thread_master *m, struct thread *fetch) struct timeval *timer_wait = &timer_val; struct timeval *timer_wait_bg; - while (1) + do { int num = 0; /* Signals pre-empt everything */ - quagga_sigevent_process (); + if (m->handle_signals) + quagga_sigevent_process (); + pthread_mutex_lock (&m->mtx); /* Drain the ready queue of already scheduled jobs, before scheduling * more. */ if ((thread = thread_trim_head (&m->ready)) != NULL) - return thread_run (m, thread, fetch); + { + fetch = thread_run (m, thread, fetch); + pthread_mutex_unlock (&m->mtx); + return fetch; + } /* To be fair to all kinds of threads, and avoid starvation, we * need to be careful to consider all thread types for scheduling @@ -1165,7 +1298,7 @@ thread_fetch (struct thread_master *m, struct thread *fetch) thread_process (&m->event); /* Structure copy. */ -#if !defined(HAVE_POLL) +#if !defined(HAVE_POLL_CALL) readfd = fd_copy_fd_set(m->handler.readfd); writefd = fd_copy_fd_set(m->handler.writefd); exceptfd = fd_copy_fd_set(m->handler.exceptfd); @@ -1194,8 +1327,12 @@ thread_fetch (struct thread_master *m, struct thread *fetch) if (num < 0) { if (errno == EINTR) - continue; /* signal received - process it */ + { + pthread_mutex_unlock (&m->mtx); + continue; /* signal received - process it */ + } zlog_warn ("select() error: %s", safe_strerror (errno)); + pthread_mutex_unlock (&m->mtx); return NULL; } @@ -1215,15 +1352,28 @@ thread_fetch (struct thread_master *m, struct thread *fetch) list at this time. If this is code is uncommented, then background timer threads will not run unless there is nothing else to do. */ if ((thread = thread_trim_head (&m->ready)) != NULL) - return thread_run (m, thread, fetch); + { + fetch = thread_run (m, thread, fetch); + pthread_mutex_unlock (&m->mtx); + return fetch; + } #endif /* Background timer/events, lowest priority */ thread_timer_process (m->background, &now); if ((thread = thread_trim_head (&m->ready)) != NULL) - return thread_run (m, thread, fetch); - } + { + fetch = thread_run (m, thread, fetch); + pthread_mutex_unlock (&m->mtx); + return fetch; + } + + pthread_mutex_unlock (&m->mtx); + + } while (m->spin); + + return NULL; } unsigned long @@ -1248,13 +1398,23 @@ thread_consumed_time (RUSAGE_T *now, RUSAGE_T *start, unsigned long *cputime) int thread_should_yield (struct thread *thread) { - return monotime_since(&thread->real, NULL) > (int64_t)thread->yield; + int result; + pthread_mutex_lock (&thread->mtx); + { + result = monotime_since(&thread->real, NULL) > (int64_t)thread->yield; + } + pthread_mutex_unlock (&thread->mtx); + return result; } void thread_set_yield_time (struct thread *thread, unsigned long yield_time) { - thread->yield = yield_time; + pthread_mutex_lock (&thread->mtx); + { + thread->yield = yield_time; + } + pthread_mutex_unlock (&thread->mtx); } void @@ -1324,6 +1484,7 @@ funcname_thread_execute (struct thread_master *m, memset (&dummy, 0, sizeof (struct thread)); + pthread_mutex_init (&dummy.mtx, NULL); dummy.type = THREAD_EVENT; dummy.add_type = THREAD_EXECUTE; dummy.master = NULL; @@ -1332,8 +1493,12 @@ funcname_thread_execute (struct thread_master *m, tmp.func = dummy.func = func; tmp.funcname = dummy.funcname = funcname; - dummy.hist = hash_get (cpu_record, &tmp, - (void * (*) (void *))cpu_record_hash_alloc); + pthread_mutex_lock (&cpu_record_mtx); + { + dummy.hist = hash_get (cpu_record, &tmp, + (void * (*) (void *))cpu_record_hash_alloc); + } + pthread_mutex_unlock (&cpu_record_mtx); dummy.schedfrom = schedfrom; dummy.schedfrom_line = fromln; diff --git a/lib/thread.h b/lib/thread.h index 34adcc4d09..6fb6ad7c9d 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -24,6 +24,7 @@ #include <zebra.h> #include "monotime.h" +#include <pthread.h> struct rusage_t { @@ -50,7 +51,7 @@ struct pqueue; */ typedef fd_set thread_fd_set; -#if defined(HAVE_POLL) +#if defined(HAVE_POLL_CALL) #include <poll.h> struct fd_handler { @@ -84,6 +85,10 @@ struct thread_master int fd_limit; struct fd_handler handler; unsigned long alloc; + long selectpoll_timeout; + bool spin; + bool handle_signals; + pthread_mutex_t mtx; }; typedef unsigned char thread_type; @@ -110,6 +115,7 @@ struct thread const char *funcname; const char *schedfrom; int schedfrom_line; + pthread_mutex_t mtx; }; struct cpu_thread_history @@ -734,6 +734,7 @@ vty_end_config (struct vty *vty) case ZEBRA_NODE: case RIP_NODE: case RIPNG_NODE: + case EIGRP_NODE: case BGP_NODE: case BGP_VPNV4_NODE: case BGP_VPNV6_NODE: @@ -1163,6 +1164,7 @@ vty_stop_input (struct vty *vty) case ZEBRA_NODE: case RIP_NODE: case RIPNG_NODE: + case EIGRP_NODE: case BGP_NODE: case RMAP_NODE: case OSPF_NODE: |
