diff options
| author | paulzlabn <paulz@labn.net> | 2018-03-14 13:31:58 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-03-14 13:31:58 -0700 |
| commit | 3f1224cd1a9408bdad6aca8c0c205211cb548d5c (patch) | |
| tree | 87e6a52a3e7ad7b09caa3207f081fd92bc8fd018 /lib | |
| parent | fd9b55a2b77c187730600d429b3f290ab58fa035 (diff) | |
| parent | 6ca96cc6ada990d052fcfc48cffeef454ae64a10 (diff) | |
Merge branch 'master' into working/master/bgp-vpn-vrf-leaking
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/.gitignore | 1 | ||||
| -rw-r--r-- | lib/debug.h | 92 | ||||
| -rw-r--r-- | lib/frr_pthread.c | 44 | ||||
| -rw-r--r-- | lib/frr_pthread.h | 36 | ||||
| -rw-r--r-- | lib/ipaddr.h | 17 | ||||
| -rw-r--r-- | lib/log.c | 43 | ||||
| -rw-r--r-- | lib/nexthop.c | 51 | ||||
| -rw-r--r-- | lib/nexthop.h | 2 | ||||
| -rw-r--r-- | lib/nexthop_group.c | 313 | ||||
| -rw-r--r-- | lib/nexthop_group.h | 42 | ||||
| -rw-r--r-- | lib/strlcat.c | 14 | ||||
| -rw-r--r-- | lib/strlcpy.c | 29 | ||||
| -rw-r--r-- | lib/thread.c | 12 | ||||
| -rw-r--r-- | lib/thread.h | 1 | ||||
| -rw-r--r-- | lib/vrf.c | 20 | ||||
| -rw-r--r-- | lib/vrf.h | 3 | ||||
| -rw-r--r-- | lib/zclient.c | 12 | ||||
| -rw-r--r-- | lib/zclient.h | 65 | ||||
| -rw-r--r-- | lib/zebra.h | 6 |
19 files changed, 691 insertions, 112 deletions
diff --git a/lib/.gitignore b/lib/.gitignore index 94f401ebe6..072146dbd5 100644 --- a/lib/.gitignore +++ b/lib/.gitignore @@ -24,4 +24,3 @@ refix grammar_sandbox clippy defun_lex.c -plist_clippy.c diff --git a/lib/debug.h b/lib/debug.h index 3e6772aacf..d0fa27d3fe 100644 --- a/lib/debug.h +++ b/lib/debug.h @@ -104,76 +104,77 @@ struct debug_callbacks { * * MT-Safe */ -#define DEBUG_MODE_CHECK(name, type) \ - CHECK_FLAG_ATOMIC(&(name)->flags, (type)&DEBUG_MODE_ALL) +#define DEBUG_MODE_CHECK(name, mode) \ + CHECK_FLAG_ATOMIC(&(name)->flags, (mode)&DEBUG_MODE_ALL) /* * Check if an option bit is set for a debug. * * MT-Safe */ -#define DEBUG_OPT_CHECK(name, type) \ - CHECK_FLAG_ATOMIC(&(name)->flags, (type)&DEBUG_OPT_ALL) +#define DEBUG_OPT_CHECK(name, opt) \ + CHECK_FLAG_ATOMIC(&(name)->flags, (opt)&DEBUG_OPT_ALL) /* * Check if bits are set for a debug. * * MT-Safe */ -#define DEBUG_FLAGS_CHECK(name, type) CHECK_FLAG_ATOMIC(&(name)->flags, (type)) - -/* - * Check if any mode is on for a debug. - * - * MT-Safe - */ -#define DEBUG(name) DEBUG_MODE_CHECK((name), DEBUG_MODE_ALL) +#define DEBUG_FLAGS_CHECK(name, fl) CHECK_FLAG_ATOMIC(&(name)->flags, (fl)) /* * Set modes on a debug. * * MT-Safe */ -#define DEBUG_MODE_SET(name, type) \ - SET_FLAG_ATOMIC(&(name)->flags, (type)&DEBUG_MODE_ALL) +#define DEBUG_MODE_SET(name, mode, onoff) \ + do { \ + if (onoff) \ + SET_FLAG_ATOMIC(&(name)->flags, \ + (mode)&DEBUG_MODE_ALL); \ + else \ + UNSET_FLAG_ATOMIC(&(name)->flags, \ + (mode)&DEBUG_MODE_ALL); \ + } while (0) -/* - * Unset modes on a debug. - * - * MT-Safe - */ -#define DEBUG_MODE_UNSET(name, type) \ - UNSET_FLAG_ATOMIC(&(name)->flags, (type)&DEBUG_MODE_ALL) +/* Convenience macros for specific set operations. */ +#define DEBUG_MODE_ON(name, mode) DEBUG_MODE_SET(name, mode, true) +#define DEBUG_MODE_OFF(name, mode) DEBUG_MODE_SET(name, mode, false) /* * Set options on a debug. * * MT-Safe */ -#define DEBUG_OPT_SET(name, type) \ - SET_FLAG_ATOMIC(&(name)->flags, (type)&DEBUG_OPT_ALL) +#define DEBUG_OPT_SET(name, opt, onoff) \ + do { \ + if (onoff) \ + SET_FLAG_ATOMIC(&(name)->flags, (opt)&DEBUG_OPT_ALL); \ + else \ + UNSET_FLAG_ATOMIC(&(name)->flags, \ + (opt)&DEBUG_OPT_ALL); \ + } while (0) -/* - * Unset options on a debug. - * - * MT-Safe - */ -#define DEBUG_OPT_UNSET(name, type) \ - UNSET_FLAG_ATOMIC(&(name)->flags, (type)&DEBUG_OPT_ALL) +/* Convenience macros for specific set operations. */ +#define DEBUG_OPT_ON(name, opt) DEBUG_OPT_SET(name, opt, true) +#define DEBUG_OPT_OFF(name, opt) DEBUG_OPT_SET(name, opt, true) /* * Set bits on a debug. * * MT-Safe */ -#define DEBUG_FLAGS_SET(name, type) SET_FLAG_ATOMIC(&(name)->flags, (type)) +#define DEBUG_FLAGS_SET(name, fl, onoff) \ + do { \ + if (onoff) \ + SET_FLAG_ATOMIC(&(name)->flags, (fl)); \ + else \ + UNSET_FLAG_ATOMIC(&(name)->flags, (fl)); \ + } while (0) -/* - * Unset bits on a debug. - * - * MT-Safe - */ -#define DEBUG_FLAGS_UNSET(name, type) UNSET_FLAG_ATOMIC(&(name)->flags, (type)) +/* Convenience macros for specific set operations. */ +#define DEBUG_FLAGS_ON(name, fl) DEBUG_FLAGS_SET(&(name)->flags, (type), true) +#define DEBUG_FLAGS_OFF(name, fl) DEBUG_FLAGS_SET(&(name)->flags, (type), false) /* * Unset all modes and options on a debug. @@ -201,6 +202,23 @@ struct debug_callbacks { #define DEBUG_NODE2MODE(vtynode) \ (((vtynode) == CONFIG_NODE) ? DEBUG_MODE_ALL : DEBUG_MODE_TERM) +/* + * Debug at the given level to the default logging destination. + * + * MT-Safe + */ +#define DEBUG(level, name, fmt, ...) \ + do { \ + if (DEBUG_MODE_CHECK(name, DEBUG_MODE_ALL)) \ + zlog_##level(fmt, ##__VA_ARGS__); \ + } while (0) + +/* Convenience macros for the various levels. */ +#define DEBUGE(name, fmt, ...) DEBUG(err, name, fmt, ##__VA_ARGS__) +#define DEBUGW(name, fmt, ...) DEBUG(warn, name, fmt, ##__VA_ARGS__) +#define DEBUGI(name, fmt, ...) DEBUG(info, name, fmt, ##__VA_ARGS__) +#define DEBUGN(name, fmt, ...) DEBUG(notice, name, fmt, ##__VA_ARGS__) +#define DEBUGD(name, fmt, ...) DEBUG(debug, name, fmt, ##__VA_ARGS__) /* * Optional initializer for debugging. Highly recommended. diff --git a/lib/frr_pthread.c b/lib/frr_pthread.c index 72b47ae5c3..36a89168c2 100644 --- a/lib/frr_pthread.c +++ b/lib/frr_pthread.c @@ -1,6 +1,6 @@ /* * Utilities and interfaces for managing POSIX threads within FRR. - * Copyright (C) 2017 Cumulus Networks + * Copyright (C) 2017 Cumulus Networks, Inc. * * 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 @@ -29,7 +29,7 @@ DEFINE_MTYPE(LIB, FRR_PTHREAD, "FRR POSIX Thread"); DEFINE_MTYPE(LIB, PTHREAD_PRIM, "POSIX synchronization primitives"); /* id for next created pthread */ -static unsigned int next_id = 0; +static _Atomic uint32_t next_id = 0; /* default frr_pthread start/stop routine prototypes */ static void *fpt_run(void *arg); @@ -40,7 +40,6 @@ struct frr_pthread_attr frr_pthread_attr_default = { .id = 0, .start = fpt_run, .stop = fpt_halt, - .name = "Anonymous", }; /* hash table to keep track of all frr_pthreads */ @@ -85,9 +84,10 @@ void frr_pthread_finish() pthread_mutex_unlock(&frr_pthread_hash_mtx); } -struct frr_pthread *frr_pthread_new(struct frr_pthread_attr *attr) +struct frr_pthread *frr_pthread_new(struct frr_pthread_attr *attr, + const char *name) { - static struct frr_pthread holder = {0}; + static struct frr_pthread holder = {}; struct frr_pthread *fpt = NULL; attr = attr ? attr : &frr_pthread_attr_default; @@ -99,10 +99,14 @@ struct frr_pthread *frr_pthread_new(struct frr_pthread_attr *attr) if (!hash_lookup(frr_pthread_hash, &holder)) { fpt = XCALLOC(MTYPE_FRR_PTHREAD, sizeof(struct frr_pthread)); + /* initialize mutex */ + pthread_mutex_init(&fpt->mtx, NULL); /* create new thread master */ - fpt->master = thread_master_create(attr->name); + fpt->master = thread_master_create(name); /* set attributes */ fpt->attr = *attr; + name = (name ? name : "Anonymous thread"); + fpt->name = XSTRDUP(MTYPE_FRR_PTHREAD, name); if (attr == &frr_pthread_attr_default) fpt->attr.id = frr_pthread_get_id(); /* initialize startup synchronization primitives */ @@ -126,16 +130,31 @@ void frr_pthread_destroy(struct frr_pthread *fpt) { thread_master_free(fpt->master); + pthread_mutex_destroy(&fpt->mtx); pthread_mutex_destroy(fpt->running_cond_mtx); pthread_cond_destroy(fpt->running_cond); + if (fpt->name) + XFREE(MTYPE_FRR_PTHREAD, fpt->name); XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond_mtx); XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond); XFREE(MTYPE_FRR_PTHREAD, fpt); } -struct frr_pthread *frr_pthread_get(unsigned int id) +void frr_pthread_set_name(struct frr_pthread *fpt, const char *name) { - static struct frr_pthread holder = {0}; + pthread_mutex_lock(&fpt->mtx); + { + if (fpt->name) + XFREE(MTYPE_FRR_PTHREAD, fpt->name); + fpt->name = XSTRDUP(MTYPE_FRR_PTHREAD, name); + } + pthread_mutex_unlock(&fpt->mtx); + thread_master_set_name(fpt->master, name); +} + +struct frr_pthread *frr_pthread_get(uint32_t id) +{ + static struct frr_pthread holder = {}; struct frr_pthread *fpt; pthread_mutex_lock(&frr_pthread_hash_mtx); @@ -210,11 +229,13 @@ void frr_pthread_stop_all() pthread_mutex_unlock(&frr_pthread_hash_mtx); } -unsigned int frr_pthread_get_id() +uint32_t frr_pthread_get_id(void) { + _Atomic uint32_t nxid; + nxid = atomic_fetch_add_explicit(&next_id, 1, memory_order_seq_cst); /* just a sanity check, this should never happen */ - assert(next_id <= INT_MAX - 1); - return next_id++; + assert(nxid <= (UINT32_MAX - 1)); + return nxid; } void frr_pthread_yield(void) @@ -238,6 +259,7 @@ static int fpt_dummy(struct thread *thread) static int fpt_finish(struct thread *thread) { struct frr_pthread *fpt = THREAD_ARG(thread); + atomic_store_explicit(&fpt->running, false, memory_order_relaxed); return 0; } diff --git a/lib/frr_pthread.h b/lib/frr_pthread.h index 2cc50196a8..91002dd8ef 100644 --- a/lib/frr_pthread.h +++ b/lib/frr_pthread.h @@ -1,6 +1,6 @@ /* * Utilities and interfaces for managing POSIX threads within FRR. - * Copyright (C) 2017 Cumulus Networks + * Copyright (C) 2017 Cumulus Networks, Inc. * * 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 @@ -32,14 +32,19 @@ struct frr_pthread; struct frr_pthread_attr; struct frr_pthread_attr { - int id; + _Atomic uint32_t id; void *(*start)(void *); int (*stop)(struct frr_pthread *, void **); - const char *name; }; struct frr_pthread { + /* + * Mutex protecting this structure. Must be taken for reading some + * fields, denoted by a 'Requires: mtx'. + */ + pthread_mutex_t mtx; + /* pthread id */ pthread_t thread; @@ -73,8 +78,17 @@ struct frr_pthread { * Fake thread-specific storage. No constraints on usage. Helpful when * creating reentrant pthread implementations. Can be used to pass * argument to pthread entry function. + * + * Requires: mtx */ void *data; + + /* + * Human-readable thread name. + * + * Requires: mtx + */ + char *name; }; extern struct frr_pthread_attr frr_pthread_attr_default; @@ -107,9 +121,19 @@ void frr_pthread_finish(void); * frr_pthread will cause them to run on that pthread. * * @param attr - the thread attributes + * @param name - Human-readable name * @return the created frr_pthread upon success, or NULL upon failure */ -struct frr_pthread *frr_pthread_new(struct frr_pthread_attr *attr); +struct frr_pthread *frr_pthread_new(struct frr_pthread_attr *attr, + const char *name); + +/* + * Changes the name of the frr_pthread. + * + * @param fpt - the frr_pthread to operate on + * @param name - Human-readable name + */ +void frr_pthread_set_name(struct frr_pthread *fpt, const char *name); /* * Destroys an frr_pthread. @@ -125,7 +149,7 @@ void frr_pthread_destroy(struct frr_pthread *fpt); * * @return frr_thread associated with the provided id, or NULL on error */ -struct frr_pthread *frr_pthread_get(unsigned int id); +struct frr_pthread *frr_pthread_get(uint32_t id); /* * Creates a new pthread and binds it to a frr_pthread. @@ -198,6 +222,6 @@ void frr_pthread_yield(void); * * @return unique identifier */ -unsigned int frr_pthread_get_id(void); +uint32_t frr_pthread_get_id(void); #endif /* _FRR_PTHREAD_H */ diff --git a/lib/ipaddr.h b/lib/ipaddr.h index 98c28008dc..44bde45add 100644 --- a/lib/ipaddr.h +++ b/lib/ipaddr.h @@ -85,4 +85,21 @@ static inline char *ipaddr2str(struct ipaddr *ip, char *buf, int size) } return buf; } + +/* + * Convert IPv4 address to IPv4-mapped IPv6 address which is of the + * form ::FFFF:<IPv4 address> (RFC 4291). This IPv6 address can then + * be used to represent the IPv4 address, wherever only an IPv6 address + * is required. + */ +static inline void ipv4_to_ipv4_mapped_ipv6(struct in6_addr *in6, + struct in_addr in) +{ + u_int32_t addr_type = htonl(0xFFFF); + + memset(in6, 0, sizeof(struct in6_addr)); + memcpy((char *)in6 + 8, &addr_type, sizeof(addr_type)); + memcpy((char *)in6 + 12, &in, sizeof(struct in_addr)); +} + #endif /* __IPADDR_H__ */ @@ -1096,41 +1096,52 @@ void zlog_hexdump(const void *mem, unsigned int len) unsigned long i = 0; unsigned int j = 0; unsigned int columns = 8; - char buf[(len * 4) + ((len / 4) * 20) + 30]; + /* + * 19 bytes for 0xADDRESS: + * 24 bytes for data; 2 chars plus a space per data byte + * 1 byte for space + * 8 bytes for ASCII representation + * 1 byte for a newline + * ===================== + * 53 bytes per 8 bytes of data + * 1 byte for null term + */ + size_t bs = ((len / 8) + 1) * 53 + 1; + char buf[bs]; char *s = buf; + memset(buf, 0, sizeof(buf)); + for (i = 0; i < len + ((len % columns) ? (columns - len % columns) : 0); i++) { /* print offset */ if (i % columns == 0) - s += sprintf(s, "0x%016lx: ", (unsigned long)mem + i); + s += snprintf(s, bs - (s - buf), + "0x%016lx: ", (unsigned long)mem + i); /* print hex data */ if (i < len) - s += sprintf(s, "%02x ", 0xFF & ((const char *)mem)[i]); + s += snprintf(s, bs - (s - buf), "%02x ", + 0xFF & ((const char *)mem)[i]); /* end of block, just aligning for ASCII dump */ else - s += sprintf(s, " "); + s += snprintf(s, bs - (s - buf), " "); /* print ASCII dump */ if (i % columns == (columns - 1)) { for (j = i - (columns - 1); j <= i; j++) { - if (j >= len) /* end of block, not really - printing */ - s += sprintf(s, " "); - - else if (isprint((int)((const char *)mem) - [j])) /* printable char - */ - s += sprintf( - s, "%c", + /* end of block not really printing */ + if (j >= len) + s += snprintf(s, bs - (s - buf), " "); + else if (isprint((int)((const char *)mem)[j])) + s += snprintf( + s, bs - (s - buf), "%c", 0xFF & ((const char *)mem)[j]); - else /* other char */ - s += sprintf(s, "."); + s += snprintf(s, bs - (s - buf), "."); } - s += sprintf(s, "\n"); + s += snprintf(s, bs - (s - buf), "\n"); } } zlog_debug("\n%s", buf); diff --git a/lib/nexthop.c b/lib/nexthop.c index cee34e85c7..fb7ccc169e 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -163,6 +163,57 @@ void nexthops_free(struct nexthop *nexthop) } } +bool nexthop_same(const struct nexthop *nh1, const struct nexthop *nh2) +{ + if (nh1 && !nh2) + return false; + + if (!nh1 && nh2) + return false; + + if (nh1 == nh2) + return true; + + if (nh1->vrf_id != nh2->vrf_id) + return false; + + if (nh1->type != nh2->type) + return false; + + switch (nh1->type) { + case NEXTHOP_TYPE_IFINDEX: + if (nh1->ifindex != nh2->ifindex) + return false; + break; + case NEXTHOP_TYPE_IPV4: + if (nh1->gate.ipv4.s_addr != nh2->gate.ipv4.s_addr) + return false; + break; + case NEXTHOP_TYPE_IPV4_IFINDEX: + if (nh1->gate.ipv4.s_addr != nh2->gate.ipv4.s_addr) + return false; + if (nh1->ifindex != nh2->ifindex) + return false; + break; + case NEXTHOP_TYPE_IPV6: + if (memcmp(&nh1->gate.ipv6, &nh2->gate.ipv6, 16)) + return false; + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + if (memcmp(&nh1->gate.ipv6, &nh2->gate.ipv6, 16)) + return false; + if (nh1->ifindex != nh2->ifindex) + return false; + break; + case NEXTHOP_TYPE_BLACKHOLE: + if (nh1->bh_type != nh2->bh_type) + return false; + break; + } + + return true; +} + /* Update nexthop with label information. */ void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t type, u_int8_t num_labels, mpls_label_t *label) diff --git a/lib/nexthop.h b/lib/nexthop.h index 0ca8a0063a..568243d3a9 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -118,6 +118,8 @@ void nexthop_add_labels(struct nexthop *, enum lsp_types_t, u_int8_t, mpls_label_t *); void nexthop_del_labels(struct nexthop *); +extern bool nexthop_same(const struct nexthop *nh1, const struct nexthop *nh2); + extern const char *nexthop_type_to_str(enum nexthop_types_t nh_type); extern int nexthop_same_no_recurse(const struct nexthop *next1, const struct nexthop *next2); diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index e7f10487d1..e486247244 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -19,6 +19,7 @@ */ #include <zebra.h> +#include <vrf.h> #include <nexthop.h> #include <nexthop_group.h> #include <vty.h> @@ -28,6 +29,56 @@ #include "lib/nexthop_group_clippy.c" #endif +DEFINE_MTYPE_STATIC(LIB, NEXTHOP_GROUP, "Nexthop Group") + +struct nexthop_group_hooks { + void (*new)(const char *name); + void (*add_nexthop)(const struct nexthop_group_cmd *nhg, + const struct nexthop *nhop); + void (*del_nexthop)(const struct nexthop_group_cmd *nhg, + const struct nexthop *nhop); + void (*delete)(const char *name); +}; + +static struct nexthop_group_hooks nhg_hooks; + +static inline int +nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1, + const struct nexthop_group_cmd *nhgc2); +RB_GENERATE(nhgc_entry_head, nexthop_group_cmd, nhgc_entry, + nexthop_group_cmd_compare) + +struct nhgc_entry_head nhgc_entries; + +static inline int +nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1, + const struct nexthop_group_cmd *nhgc2) +{ + return strcmp(nhgc1->name, nhgc2->name); +} + +struct nexthop *nexthop_exists(struct nexthop_group *nhg, struct nexthop *nh) +{ + struct nexthop *nexthop; + + for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) { + if (nexthop_same(nh, nexthop)) + return nexthop; + } + + return NULL; +} + +struct nexthop_group *nexthop_group_new(void) +{ + return XCALLOC(MTYPE_NEXTHOP_GROUP, sizeof(struct nexthop_group)); +} + +void nexthop_group_delete(struct nexthop_group **nhg) +{ + XFREE(MTYPE_NEXTHOP_GROUP, *nhg); +} + /* Add nexthop to the end of a nexthop list. */ void nexthop_add(struct nexthop **target, struct nexthop *nexthop) { @@ -42,6 +93,27 @@ void nexthop_add(struct nexthop **target, struct nexthop *nexthop) nexthop->prev = last; } +/* Delete nexthop from a nexthop list. */ +void nexthop_del(struct nexthop_group *nhg, struct nexthop *nh) +{ + struct nexthop *nexthop; + + for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) { + if (nexthop_same(nh, nexthop)) + break; + } + + assert(nexthop); + + if (nexthop->prev) + nexthop->prev->next = nexthop->next; + else + nhg->nexthop = nexthop->next; + + if (nexthop->next) + nexthop->next->prev = nexthop->prev; +} + void copy_nexthops(struct nexthop **tnh, struct nexthop *nh, struct nexthop *rparent) { @@ -71,12 +143,169 @@ void copy_nexthops(struct nexthop **tnh, struct nexthop *nh, } } -DEFPY (nexthop_group, - nexthop_group_cmd, - "nexthop-group NAME", - "Enter into the nexthop-group submode\n" - "Specify the NAME of the nexthop-group\n") +static void nhgc_delete_nexthops(struct nexthop_group_cmd *nhgc) +{ + struct nexthop *nexthop; + + nexthop = nhgc->nhg.nexthop; + while (nexthop) { + struct nexthop *next = nexthop_next(nexthop); + + if (nhg_hooks.del_nexthop) + nhg_hooks.del_nexthop(nhgc, nexthop); + + nexthop_free(nexthop); + + nexthop = next; + } +} + +struct nexthop_group_cmd *nhgc_find(const char *name) +{ + struct nexthop_group_cmd find; + + strlcpy(find.name, name, sizeof(find.name)); + + return RB_FIND(nhgc_entry_head, &nhgc_entries, &find); +} + +static struct nexthop_group_cmd *nhgc_get(const char *name) { + struct nexthop_group_cmd *nhgc; + + nhgc = nhgc_find(name); + if (!nhgc) { + nhgc = XCALLOC(MTYPE_TMP, sizeof(*nhgc)); + strlcpy(nhgc->name, name, sizeof(nhgc->name)); + + QOBJ_REG(nhgc, nexthop_group_cmd); + RB_INSERT(nhgc_entry_head, &nhgc_entries, nhgc); + + if (nhg_hooks.new) + nhg_hooks.new(name); + } + + return nhgc; +} + +static void nhgc_delete(struct nexthop_group_cmd *nhgc) +{ + nhgc_delete_nexthops(nhgc); + + if (nhg_hooks.delete) + nhg_hooks.delete(nhgc->name); + + RB_REMOVE(nhgc_entry_head, &nhgc_entries, nhgc); +} + +DEFINE_QOBJ_TYPE(nexthop_group_cmd) + +DEFUN_NOSH(nexthop_group, nexthop_group_cmd, "nexthop-group NAME", + "Enter into the nexthop-group submode\n" + "Specify the NAME of the nexthop-group\n") +{ + const char *nhg_name = argv[1]->arg; + struct nexthop_group_cmd *nhgc = NULL; + + nhgc = nhgc_get(nhg_name); + VTY_PUSH_CONTEXT(NH_GROUP_NODE, nhgc); + + return CMD_SUCCESS; +} + +DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NAME", + NO_STR + "Delete the nexthop-group\n" + "Specify the NAME of the nexthop-group\n") +{ + const char *nhg_name = argv[2]->arg; + struct nexthop_group_cmd *nhgc = NULL; + + nhgc = nhgc_find(nhg_name); + if (nhgc) + nhgc_delete(nhgc); + + return CMD_SUCCESS; +} + +DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, + "[no] nexthop <A.B.C.D|X:X::X:X>$addr [INTERFACE]$intf [nexthop-vrf NAME$name]", + NO_STR + "Specify one of the nexthops in this ECMP group\n" + "v4 Address\n" + "v6 Address\n" + "Interface to use\n" + "If the nexthop is in a different vrf tell us\n" + "The nexthop-vrf Name\n") +{ + VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc); + struct vrf *vrf; + struct nexthop nhop; + struct nexthop *nh; + + if (name) + vrf = vrf_lookup_by_name(name); + else + vrf = vrf_lookup_by_id(VRF_DEFAULT); + + if (!vrf) { + vty_out(vty, "Specified: %s is non-existent\n", name); + return CMD_WARNING; + } + + memset(&nhop, 0, sizeof(nhop)); + nhop.vrf_id = vrf->vrf_id; + + if (addr->sa.sa_family == AF_INET) { + nhop.gate.ipv4.s_addr = addr->sin.sin_addr.s_addr; + if (intf) { + nhop.type = NEXTHOP_TYPE_IPV4_IFINDEX; + nhop.ifindex = ifname2ifindex(intf, vrf->vrf_id); + if (nhop.ifindex == IFINDEX_INTERNAL) { + vty_out(vty, + "Specified Intf %s does not exist in vrf: %s\n", + intf, vrf->name); + return CMD_WARNING; + } + } else + nhop.type = NEXTHOP_TYPE_IPV4; + } else { + memcpy(&nhop.gate.ipv6, &addr->sin6.sin6_addr, 16); + if (intf) { + nhop.type = NEXTHOP_TYPE_IPV6_IFINDEX; + nhop.ifindex = ifname2ifindex(intf, vrf->vrf_id); + if (nhop.ifindex == IFINDEX_INTERNAL) { + vty_out(vty, + "Specified Intf %s does not exist in vrf: %s\n", + intf, vrf->name); + return CMD_WARNING; + } + } else + nhop.type = NEXTHOP_TYPE_IPV6; + } + + nh = nexthop_exists(&nhgc->nhg, &nhop); + + if (no) { + if (nh) { + nexthop_del(&nhgc->nhg, nh); + + if (nhg_hooks.del_nexthop) + nhg_hooks.del_nexthop(nhgc, nh); + + nexthop_free(nh); + } + } else if (!nh) { + /* must be adding new nexthop since !no and !nexthop_exists */ + nh = nexthop_new(); + + memcpy(nh, &nhop, sizeof(nhop)); + nexthop_add(&nhgc->nhg.nexthop, nh); + + if (nhg_hooks.add_nexthop) + nhg_hooks.add_nexthop(nhgc, nh); + } + return CMD_SUCCESS; } @@ -86,15 +315,85 @@ struct cmd_node nexthop_group_node = { 1 }; +void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh) +{ + char buf[100]; + struct vrf *vrf; + + vty_out(vty, " nexthop "); + + switch (nh->type) { + case NEXTHOP_TYPE_IFINDEX: + vty_out(vty, "%s", ifindex2ifname(nh->ifindex, nh->vrf_id)); + break; + case NEXTHOP_TYPE_IPV4: + vty_out(vty, "%s", inet_ntoa(nh->gate.ipv4)); + break; + case NEXTHOP_TYPE_IPV4_IFINDEX: + vty_out(vty, "%s %s", inet_ntoa(nh->gate.ipv4), + ifindex2ifname(nh->ifindex, nh->vrf_id)); + break; + case NEXTHOP_TYPE_IPV6: + vty_out(vty, "%s", + inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf))); + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + vty_out(vty, "%s %s", + inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)), + ifindex2ifname(nh->ifindex, nh->vrf_id)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + break; + } + + if (nh->vrf_id != VRF_DEFAULT) { + vrf = vrf_lookup_by_id(nh->vrf_id); + vty_out(vty, " nexthop-vrf %s", vrf->name); + } + vty_out(vty, "\n"); +} + static int nexthop_group_write(struct vty *vty) { - vty_out(vty, "!\n"); + struct nexthop_group_cmd *nhgc; + struct nexthop *nh; + + RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) { + vty_out(vty, "nexthop-group %s\n", nhgc->name); + + for (nh = nhgc->nhg.nexthop; nh; nh = nh->next) + nexthop_group_write_nexthop(vty, nh); + + vty_out(vty, "!\n"); + } return 1; } -void nexthop_group_init(void) +void nexthop_group_init(void (*new)(const char *name), + void (*add_nexthop)(const struct nexthop_group_cmd *nhg, + const struct nexthop *nhop), + void (*del_nexthop)(const struct nexthop_group_cmd *nhg, + const struct nexthop *nhop), + void (*delete)(const char *name)) { + RB_INIT(nhgc_entry_head, &nhgc_entries); + install_node(&nexthop_group_node, nexthop_group_write); install_element(CONFIG_NODE, &nexthop_group_cmd); + install_element(CONFIG_NODE, &no_nexthop_group_cmd); + + install_default(NH_GROUP_NODE); + install_element(NH_GROUP_NODE, &ecmp_nexthops_cmd); + + memset(&nhg_hooks, 0, sizeof(nhg_hooks)); + + if (new) + nhg_hooks.new = new; + if (add_nexthop) + nhg_hooks.add_nexthop = add_nexthop; + if (del_nexthop) + nhg_hooks.del_nexthop = del_nexthop; + if (delete) + nhg_hooks.delete = delete; } diff --git a/lib/nexthop_group.h b/lib/nexthop_group.h index 561fe96425..c2e4c4d757 100644 --- a/lib/nexthop_group.h +++ b/lib/nexthop_group.h @@ -21,6 +21,8 @@ #ifndef __NEXTHOP_GROUP__ #define __NEXTHOP_GROUP__ +#include <vty.h> + /* * What is a nexthop group? * @@ -33,9 +35,11 @@ struct nexthop_group { struct nexthop *nexthop; }; -void nexthop_group_init(void); +struct nexthop_group *nexthop_group_new(void); +void nexthop_group_delete(struct nexthop_group **nhg); void nexthop_add(struct nexthop **target, struct nexthop *nexthop); +void nexthop_del(struct nexthop_group *nhg, struct nexthop *nexthop); void copy_nexthops(struct nexthop **tnh, struct nexthop *nh, struct nexthop *rparent); @@ -51,4 +55,40 @@ void copy_nexthops(struct nexthop **tnh, struct nexthop *nh, (nhop) = (head.nexthop); \ (nhop); \ (nhop) = nexthop_next(nhop) + +struct nexthop_group_cmd { + + RB_ENTRY(nexthop_group_cmd) nhgc_entry; + + char name[80]; + + struct nexthop_group nhg; + + QOBJ_FIELDS +}; +RB_HEAD(nhgc_entry_head, nexthp_group_cmd); +RB_PROTOTYPE(nhgc_entry_head, nexthop_group_cmd, nhgc_entry, + nexthop_group_cmd_compare) +DECLARE_QOBJ_TYPE(nexthop_group_cmd) + +/* + * Initialize nexthop_groups. If you are interested in when + * a nexthop_group is added/deleted/modified, then set the + * appropriate callback functions to handle it in your + * code + */ +void nexthop_group_init( + void (*new)(const char *name), + void (*add_nexthop)(const struct nexthop_group_cmd *nhgc, + const struct nexthop *nhop), + void (*del_nexthop)(const struct nexthop_group_cmd *nhgc, + const struct nexthop *nhop), + void (*delete)(const char *name)); + +extern struct nexthop *nexthop_exists(struct nexthop_group *nhg, + struct nexthop *nh); + +extern struct nexthop_group_cmd *nhgc_find(const char *name); + +extern void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh); #endif diff --git a/lib/strlcat.c b/lib/strlcat.c index 8186304437..be211f82a8 100644 --- a/lib/strlcat.c +++ b/lib/strlcat.c @@ -28,23 +28,25 @@ #ifndef HAVE_STRLCAT #undef strlcat -size_t strlcat(char *__restrict dest, const char *__restrict src, size_t size); +size_t strlcat(char *__restrict dest, + const char *__restrict src, size_t destsize); -size_t strlcat(char *__restrict dest, const char *__restrict src, size_t size) +size_t strlcat(char *__restrict dest, + const char *__restrict src, size_t destsize) { size_t src_length = strlen(src); /* Our implementation strlcat supports dest == NULL if size == 0 (for consistency with snprintf and strlcpy), but strnlen does not, so we have to cover this case explicitly. */ - if (size == 0) + if (destsize == 0) return src_length; - size_t dest_length = strnlen(dest, size); - if (dest_length != size) { + size_t dest_length = strnlen(dest, destsize); + if (dest_length != destsize) { /* Copy at most the remaining number of characters in the destination buffer. Leave for the NUL terminator. */ - size_t to_copy = size - dest_length - 1; + size_t to_copy = destsize - dest_length - 1; /* But not more than what is available in the source string. */ if (to_copy > src_length) to_copy = src_length; diff --git a/lib/strlcpy.c b/lib/strlcpy.c index b7681754aa..b0c33ca7f4 100644 --- a/lib/strlcpy.c +++ b/lib/strlcpy.c @@ -27,23 +27,26 @@ #ifndef HAVE_STRLCPY #undef strlcpy -size_t strlcpy(char *__restrict dest, const char *__restrict src, size_t size); +size_t strlcpy(char *__restrict dest, + const char *__restrict src, size_t destsize); -size_t strlcpy(char *__restrict dest, const char *__restrict src, size_t size) +size_t strlcpy(char *__restrict dest, + const char *__restrict src, size_t destsize) { size_t src_length = strlen(src); - if (__builtin_expect(src_length >= size, 0)) { - if (size > 0) { - /* Copy the leading portion of the string. The last - character is subsequently overwritten with the NUL - terminator, but the destination size is usually a - multiple of a small power of two, so writing it twice - should be more efficient than copying an odd number - of - bytes. */ - memcpy(dest, src, size); - dest[size - 1] = '\0'; + if (__builtin_expect(src_length >= destsize, 0)) { + if (destsize > 0) { + /* + * Copy the leading portion of the string. The last + * character is subsequently overwritten with the NUL + * terminator, but the destination destsize is usually + * a multiple of a small power of two, so writing it + * twice should be more efficient than copying an odd + * number of bytes. + */ + memcpy(dest, src, destsize); + dest[destsize - 1] = '\0'; } } else /* Copy the string and its terminating NUL character. */ diff --git a/lib/thread.c b/lib/thread.c index a221c77628..d26be1541f 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -343,7 +343,6 @@ static void initializer() pthread_key_create(&thread_current, NULL); } -/* Allocate new thread master. */ struct thread_master *thread_master_create(const char *name) { struct thread_master *rv; @@ -426,6 +425,17 @@ struct thread_master *thread_master_create(const char *name) return rv; } +void thread_master_set_name(struct thread_master *master, const char *name) +{ + pthread_mutex_lock(&master->mtx); + { + if (master->name) + XFREE(MTYPE_THREAD_MASTER, master->name); + master->name = XSTRDUP(MTYPE_THREAD_MASTER, name); + } + pthread_mutex_unlock(&master->mtx); +} + /* Add a new thread to the list. */ static void thread_list_add(struct thread_list *list, struct thread *thread) { diff --git a/lib/thread.h b/lib/thread.h index c830446e10..f7c110914d 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -174,6 +174,7 @@ struct cpu_thread_history { /* Prototypes. */ extern struct thread_master *thread_master_create(const char *); +void thread_master_set_name(struct thread_master *master, const char *name); extern void thread_master_free(struct thread_master *); extern void thread_master_free_unused(struct thread_master *); @@ -33,6 +33,7 @@ #include "memory.h" #include "command.h" #include "ns.h" +#include "privs.h" /* default VRF ID value used when VRF backend is not NETNS */ #define VRF_DEFAULT_INTERNAL 0 @@ -52,6 +53,7 @@ struct vrf_id_head vrfs_by_id = RB_INITIALIZER(&vrfs_by_id); struct vrf_name_head vrfs_by_name = RB_INITIALIZER(&vrfs_by_name); static int vrf_backend; +static struct zebra_privs_t *vrf_daemon_privs; /* * Turn on/off debug code @@ -690,14 +692,24 @@ DEFUN_NOSH (vrf_netns, "Attach VRF to a Namespace\n" "The file name in " NS_RUN_DIR ", or a full pathname\n") { - int idx_name = 1; + int idx_name = 1, ret; char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); VTY_DECLVAR_CONTEXT(vrf, vrf); if (!pathname) return CMD_WARNING_CONFIG_FAILED; - return vrf_netns_handler_create(vty, vrf, pathname, NS_UNKNOWN); + + if (vrf_daemon_privs && + vrf_daemon_privs->change(ZPRIVS_RAISE)) + zlog_err("%s: Can't raise privileges", __func__); + + ret = vrf_netns_handler_create(vty, vrf, pathname, NS_UNKNOWN); + + if (vrf_daemon_privs && + vrf_daemon_privs->change(ZPRIVS_LOWER)) + zlog_err("%s: Can't lower privileges", __func__); + return ret; } DEFUN (no_vrf_netns, @@ -779,7 +791,8 @@ void vrf_install_commands(void) install_element(ENABLE_NODE, &no_vrf_debug_cmd); } -void vrf_cmd_init(int (*writefunc)(struct vty *vty)) +void vrf_cmd_init(int (*writefunc)(struct vty *vty), + struct zebra_privs_t *daemon_privs) { install_element(CONFIG_NODE, &vrf_cmd); install_element(CONFIG_NODE, &no_vrf_cmd); @@ -787,6 +800,7 @@ void vrf_cmd_init(int (*writefunc)(struct vty *vty)) install_default(VRF_NODE); if (vrf_is_backend_netns() && ns_have_netns()) { /* Install NS commands. */ + vrf_daemon_privs = daemon_privs; install_element(VRF_NODE, &vrf_netns_cmd); install_element(VRF_NODE, &no_vrf_netns_cmd); } @@ -242,7 +242,8 @@ extern int vrf_switchback_to_initial(void); /* VRF vty command initialisation */ -extern void vrf_cmd_init(int (*writefunc)(struct vty *vty)); +extern void vrf_cmd_init(int (*writefunc)(struct vty *vty), + struct zebra_privs_t *daemon_priv); /* VRF vty debugging */ diff --git a/lib/zclient.c b/lib/zclient.c index 3ad4d9c6e7..777f6fcf9b 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -322,6 +322,18 @@ stream_failure: return 0; } +bool zapi_parse_header(struct stream *zmsg, struct zmsghdr *hdr) +{ + STREAM_GETW(zmsg, hdr->length); + STREAM_GETC(zmsg, hdr->marker); + STREAM_GETC(zmsg, hdr->version); + STREAM_GETL(zmsg, hdr->vrf_id); + STREAM_GETW(zmsg, hdr->command); + return true; +stream_failure: + return false; +} + /* Send simple Zebra message. */ static int zebra_message_send(struct zclient *zclient, int command, vrf_id_t vrf_id) diff --git a/lib/zclient.h b/lib/zclient.h index 1aa94b641c..8033488444 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -237,14 +237,13 @@ struct zclient { */ #define ZAPI_MESSAGE_TABLEID 0x80 +#define ZSERV_VERSION 5 /* Zserv protocol message header */ -struct zserv_header { +struct zmsghdr { uint16_t length; - uint8_t marker; /* corresponds to command field in old zserv - * always set to 255 in new zserv. - */ + /* Always set to 255 in new zserv */ + uint8_t marker; uint8_t version; -#define ZSERV_VERSION 5 vrf_id_t vrf_id; uint16_t command; }; @@ -380,9 +379,11 @@ struct zclient_options { /* Prototypes of zebra client service functions. */ extern struct zclient *zclient_new(struct thread_master *); +/* clang-format off */ #if CONFDATE > 20181101 CPP_NOTICE("zclient_new_notify can take over or zclient_new now"); #endif +/* clang-format on */ extern struct zclient_options zclient_options_default; @@ -449,9 +450,58 @@ extern int zclient_send_message(struct zclient *); /* create header for command, length to be filled in by user later */ extern void zclient_create_header(struct stream *, uint16_t, vrf_id_t); +/* + * Read sizeof(struct zmsghdr) bytes from the provided socket and parse the + * received data into the specified fields. If this is successful, read the + * rest of the packet into the provided stream. + * + * s + * The stream to read into + * + * sock + * The socket to read from + * + * size + * Parsed message size will be placed in the pointed-at integer + * + * marker + * Parsed marker will be placed in the pointed-at byte + * + * version + * Parsed version will be placed in the pointed-at byte + * + * vrf_id + * Parsed VRF ID will be placed in the pointed-at vrf_id_t + * + * cmd + * Parsed command number will be placed in the pointed-at integer + * + * Returns: + * -1 if: + * - insufficient data for header was read + * - a version mismatch was detected + * - a marker mismatch was detected + * - header size field specified more data than could be read + */ extern int zclient_read_header(struct stream *s, int sock, u_int16_t *size, u_char *marker, u_char *version, vrf_id_t *vrf_id, u_int16_t *cmd); +/* + * Parse header from ZAPI message stream into struct zmsghdr. + * This function assumes the stream getp points at the first byte of the header. + * If the function is successful then the stream getp will point to the byte + * immediately after the last byte of the header. + * + * zmsg + * The stream containing the header + * + * hdr + * The header struct to parse into. + * + * Returns: + * true if parsing succeeded, false otherwise + */ +extern bool zapi_parse_header(struct stream *zmsg, struct zmsghdr *hdr); extern void zclient_interface_set_master(struct zclient *client, struct interface *master, @@ -468,10 +518,11 @@ extern struct interface *zebra_interface_vrf_update_read(struct stream *s, extern void zebra_interface_if_set_value(struct stream *, struct interface *); extern void zebra_router_id_update_read(struct stream *s, struct prefix *rid); +/* clang-format off */ #if CONFDATE > 20180823 -CPP_NOTICE( - "zapi_ipv4_route, zapi_ipv6_route, zapi_ipv4_route_ipv6_nexthop as well as the zapi_ipv4 and zapi_ipv6 data structures should be removed now"); +CPP_NOTICE("zapi_ipv4_route, zapi_ipv6_route, zapi_ipv4_route_ipv6_nexthop as well as the zapi_ipv4 and zapi_ipv6 data structures should be removed now"); #endif +/* clang-format on */ extern int zapi_ipv4_route(u_char, struct zclient *, struct prefix_ipv4 *, struct zapi_ipv4 *) __attribute__((deprecated)); diff --git a/lib/zebra.h b/lib/zebra.h index 262ad2e43d..923f6f77c6 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -232,10 +232,12 @@ typedef unsigned char u_int8_t; #include "zassert.h" #ifndef HAVE_STRLCAT -size_t strlcat(char *__restrict dest, const char *__restrict src, size_t size); +size_t strlcat(char *__restrict dest, + const char *__restrict src, size_t destsize); #endif #ifndef HAVE_STRLCPY -size_t strlcpy(char *__restrict dest, const char *__restrict src, size_t size); +size_t strlcpy(char *__restrict dest, + const char *__restrict src, size_t destsize); #endif #ifdef HAVE_BROKEN_CMSG_FIRSTHDR |
